authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-14 21:17:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-14 21:17:30-07:00
log55eea3b045c86c78eb8d9cc862122d260352a631
treea8e234fa3a68c1c62233f047b2b1647be2e091dd
parent8b882747813878a40b63572636a6e86a59a8581e

stage2: implement `@minimum` and `@maximum`, including vectors

* std.os: take advantage of `@minimum`. It's probably time to deprecate `std.min` and `std.max`. * New AIR instructions: min and max * Introduce SIMD vector support to stage2 * Add `@Type` support for vectors * Sema: add `checkSimdBinOp` which can be re-used for other arithmatic operators that want to support vectors. * Implement coercion from vectors to arrays. - In backends this is handled with bitcast for vector to array, however maybe we want to reduce the amount of branching by introducing an explicit AIR instruction for it in the future. * LLVM backend: implement lowering vector types * Sema: Implement `slice.ptr` at comptime * Value: improve `numberMin` and `numberMax` to support floats in addition to integers, and make them behave properly in the presence of NaN.

14 files changed, 470 insertions(+), 125 deletions(-)

lib/std/os.zig+25-25
......@@ -1,18 +1,18 @@
1// This file contains thin wrappers around OS-specific APIs, with these
2// specific goals in mind:
3// * Convert "errno"-style error codes into Zig errors.
4// * When null-terminated byte buffers are required, provide APIs which accept
5// slices as well as APIs which accept null-terminated byte buffers. Same goes
6// for UTF-16LE encoding.
7// * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide
8// cross platform abstracting.
9// * When there exists a corresponding libc function and linking libc, the libc
10// implementation is used. Exceptions are made for known buggy areas of libc.
11// On Linux libc can be side-stepped by using `std.os.linux` directly.
12// * For Windows, this file represents the API that libc would provide for
13// Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.
14// Note: The Zig standard library does not support POSIX thread cancellation, and
15// in general EINTR is handled by trying again.
1//! This file contains thin wrappers around OS-specific APIs, with these
2//! specific goals in mind:
3//! * Convert "errno"-style error codes into Zig errors.
4//! * When null-terminated byte buffers are required, provide APIs which accept
5//! slices as well as APIs which accept null-terminated byte buffers. Same goes
6//! for UTF-16LE encoding.
7//! * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide
8//! cross platform abstracting.
9//! * When there exists a corresponding libc function and linking libc, the libc
10//! implementation is used. Exceptions are made for known buggy areas of libc.
11//! On Linux libc can be side-stepped by using `std.os.linux` directly.
12//! * For Windows, this file represents the API that libc would provide for
13//! Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.
14//! Note: The Zig standard library does not support POSIX thread cancellation, and
15//! in general EINTR is handled by trying again.
1616
1717const root = @import("root");
1818const std = @import("std.zig");
......@@ -492,7 +492,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
492492 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
493493 else => math.maxInt(isize),
494494 };
495 const adjusted_len = math.min(max_count, buf.len);
495 const adjusted_len = @minimum(max_count, buf.len);
496496
497497 while (true) {
498498 const rc = system.read(fd, buf.ptr, adjusted_len);
......@@ -621,7 +621,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
621621 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
622622 else => math.maxInt(isize),
623623 };
624 const adjusted_len = math.min(max_count, buf.len);
624 const adjusted_len = @minimum(max_count, buf.len);
625625
626626 const pread_sym = if (builtin.os.tag == .linux and builtin.link_libc)
627627 system.pread64
......@@ -873,7 +873,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
873873 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
874874 else => math.maxInt(isize),
875875 };
876 const adjusted_len = math.min(max_count, bytes.len);
876 const adjusted_len = @minimum(max_count, bytes.len);
877877
878878 while (true) {
879879 const rc = system.write(fd, bytes.ptr, adjusted_len);
......@@ -1029,7 +1029,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
10291029 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
10301030 else => math.maxInt(isize),
10311031 };
1032 const adjusted_len = math.min(max_count, bytes.len);
1032 const adjusted_len = @minimum(max_count, bytes.len);
10331033
10341034 const pwrite_sym = if (builtin.os.tag == .linux and builtin.link_libc)
10351035 system.pwrite64
......@@ -5439,7 +5439,7 @@ pub fn sendfile(
54395439 }
54405440
54415441 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
5442 const adjusted_count = if (in_len == 0) max_count else math.min(in_len, @as(size_t, max_count));
5442 const adjusted_count = if (in_len == 0) max_count else @minimum(in_len, @as(size_t, max_count));
54435443
54445444 const sendfile_sym = if (builtin.link_libc)
54455445 system.sendfile64
......@@ -5522,7 +5522,7 @@ pub fn sendfile(
55225522 hdtr = &hdtr_data;
55235523 }
55245524
5525 const adjusted_count = math.min(in_len, max_count);
5525 const adjusted_count = @minimum(in_len, max_count);
55265526
55275527 while (true) {
55285528 var sbytes: off_t = undefined;
......@@ -5601,7 +5601,7 @@ pub fn sendfile(
56015601 hdtr = &hdtr_data;
56025602 }
56035603
5604 const adjusted_count = math.min(in_len, @as(u63, max_count));
5604 const adjusted_count = @minimum(in_len, @as(u63, max_count));
56055605
56065606 while (true) {
56075607 var sbytes: off_t = adjusted_count;
......@@ -5655,7 +5655,7 @@ pub fn sendfile(
56555655 rw: {
56565656 var buf: [8 * 4096]u8 = undefined;
56575657 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
5658 const adjusted_count = if (in_len == 0) buf.len else math.min(buf.len, in_len);
5658 const adjusted_count = if (in_len == 0) buf.len else @minimum(buf.len, in_len);
56595659 const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
56605660 if (amt_read == 0) {
56615661 if (in_len == 0) {
......@@ -5756,7 +5756,7 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len
57565756 }
57575757
57585758 var buf: [8 * 4096]u8 = undefined;
5759 const adjusted_count = math.min(buf.len, len);
5759 const adjusted_count = @minimum(buf.len, len);
57605760 const amt_read = try pread(fd_in, buf[0..adjusted_count], off_in);
57615761 // TODO without @as the line below fails to compile for wasm32-wasi:
57625762 // error: integer value 0 cannot be coerced to type 'os.PWriteError!usize'
......@@ -5919,7 +5919,7 @@ pub fn dn_expand(
59195919 const end = msg.ptr + msg.len;
59205920 if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket;
59215921 var dest = exp_dn.ptr;
5922 const dend = dest + std.math.min(exp_dn.len, 254);
5922 const dend = dest + @minimum(exp_dn.len, 254);
59235923 // detect reference loop using an iteration counter
59245924 var i: usize = 0;
59255925 while (i < msg.len) : (i += 2) {
src/Air.zig+14
......@@ -107,6 +107,18 @@ pub const Inst = struct {
107107 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
108108 /// Uses the `bin_op` field.
109109 ptr_sub,
110 /// Given two operands which can be floats, integers, or vectors, returns the
111 /// greater of the operands. For vectors it operates element-wise.
112 /// Both operands are guaranteed to be the same type, and the result type
113 /// is the same as both operands.
114 /// Uses the `bin_op` field.
115 max,
116 /// Given two operands which can be floats, integers, or vectors, returns the
117 /// lesser of the operands. For vectors it operates element-wise.
118 /// Both operands are guaranteed to be the same type, and the result type
119 /// is the same as both operands.
120 /// Uses the `bin_op` field.
121 min,
110122 /// Allocates stack local memory.
111123 /// Uses the `ty` field.
112124 alloc,
......@@ -640,6 +652,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
640652 .shl,
641653 .shl_exact,
642654 .shl_sat,
655 .min,
656 .max,
643657 => return air.typeOf(datas[inst].bin_op.lhs),
644658
645659 .cmp_lt,
src/Liveness.zig+2
......@@ -264,6 +264,8 @@ fn analyzeInst(
264264 .atomic_store_release,
265265 .atomic_store_seq_cst,
266266 .set_union_tag,
267 .min,
268 .max,
267269 => {
268270 const o = inst_datas[inst].bin_op;
269271 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
src/Sema.zig+218-28
......@@ -614,8 +614,6 @@ pub fn analyzeBody(
614614 .builtin_call => try sema.zirBuiltinCall(block, inst),
615615 .field_ptr_type => try sema.zirFieldPtrType(block, inst),
616616 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
617 .maximum => try sema.zirMaximum(block, inst),
618 .minimum => try sema.zirMinimum(block, inst),
619617 .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
620618 .@"resume" => try sema.zirResume(block, inst),
621619 .@"await" => try sema.zirAwait(block, inst, false),
......@@ -654,6 +652,9 @@ pub fn analyzeBody(
654652 .subwrap => try sema.zirArithmetic(block, inst, .subwrap),
655653 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
656654
655 .maximum => try sema.zirMinMax(block, inst, .max),
656 .minimum => try sema.zirMinMax(block, inst, .min),
657
657658 .shl => try sema.zirShl(block, inst, .shl),
658659 .shl_exact => try sema.zirShl(block, inst, .shl_exact),
659660 .shl_sat => try sema.zirShl(block, inst, .shl_sat),
......@@ -9018,6 +9019,12 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
90189019 .Void => return Air.Inst.Ref.void_type,
90199020 .Bool => return Air.Inst.Ref.bool_type,
90209021 .NoReturn => return Air.Inst.Ref.noreturn_type,
9022 .ComptimeFloat => return Air.Inst.Ref.comptime_float_type,
9023 .ComptimeInt => return Air.Inst.Ref.comptime_int_type,
9024 .Undefined => return Air.Inst.Ref.undefined_type,
9025 .Null => return Air.Inst.Ref.null_type,
9026 .AnyFrame => return Air.Inst.Ref.anyframe_type,
9027 .EnumLiteral => return Air.Inst.Ref.enum_literal_type,
90219028 .Int => {
90229029 const struct_val = union_val.val.castTag(.@"struct").?.data;
90239030 // TODO use reflection instead of magic numbers here
......@@ -9032,14 +9039,23 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
90329039 };
90339040 return sema.addType(ty);
90349041 },
9042 .Vector => {
9043 const struct_val = union_val.val.castTag(.@"struct").?.data;
9044 // TODO use reflection instead of magic numbers here
9045 const len_val = struct_val[0];
9046 const child_val = struct_val[1];
9047
9048 const len = len_val.toUnsignedInt();
9049 var buffer: Value.ToTypeBuffer = undefined;
9050 const child_ty = child_val.toType(&buffer);
9051
9052 const ty = try Type.vector(sema.arena, len, child_ty);
9053 return sema.addType(ty);
9054 },
90359055 .Float => return sema.fail(block, src, "TODO: Sema.zirReify for Float", .{}),
90369056 .Pointer => return sema.fail(block, src, "TODO: Sema.zirReify for Pointer", .{}),
90379057 .Array => return sema.fail(block, src, "TODO: Sema.zirReify for Array", .{}),
90389058 .Struct => return sema.fail(block, src, "TODO: Sema.zirReify for Struct", .{}),
9039 .ComptimeFloat => return Air.Inst.Ref.comptime_float_type,
9040 .ComptimeInt => return Air.Inst.Ref.comptime_int_type,
9041 .Undefined => return Air.Inst.Ref.undefined_type,
9042 .Null => return Air.Inst.Ref.null_type,
90439059 .Optional => return sema.fail(block, src, "TODO: Sema.zirReify for Optional", .{}),
90449060 .ErrorUnion => return sema.fail(block, src, "TODO: Sema.zirReify for ErrorUnion", .{}),
90459061 .ErrorSet => return sema.fail(block, src, "TODO: Sema.zirReify for ErrorSet", .{}),
......@@ -9049,9 +9065,6 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
90499065 .BoundFn => @panic("TODO delete BoundFn from the language"),
90509066 .Opaque => return sema.fail(block, src, "TODO: Sema.zirReify for Opaque", .{}),
90519067 .Frame => return sema.fail(block, src, "TODO: Sema.zirReify for Frame", .{}),
9052 .AnyFrame => return Air.Inst.Ref.anyframe_type,
9053 .Vector => return sema.fail(block, src, "TODO: Sema.zirReify for Vector", .{}),
9054 .EnumLiteral => return Air.Inst.Ref.enum_literal_type,
90559068 }
90569069}
90579070
......@@ -9379,9 +9392,23 @@ fn checkFloatType(
93799392) CompileError!void {
93809393 switch (ty.zigTypeTag()) {
93819394 .ComptimeFloat, .Float => {},
9382 else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{
9383 ty,
9384 }),
9395 else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty}),
9396 }
9397}
9398
9399fn checkNumericType(
9400 sema: *Sema,
9401 block: *Block,
9402 ty_src: LazySrcLoc,
9403 ty: Type,
9404) CompileError!void {
9405 switch (ty.zigTypeTag()) {
9406 .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
9407 .Vector => switch (ty.childType().zigTypeTag()) {
9408 .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
9409 else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}),
9410 },
9411 else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty}),
93859412 }
93869413}
93879414
......@@ -9474,6 +9501,82 @@ fn checkComptimeVarStore(
94749501 }
94759502}
94769503
9504const SimdBinOp = struct {
9505 len: ?u64,
9506 /// Coerced to `result_ty`.
9507 lhs: Air.Inst.Ref,
9508 /// Coerced to `result_ty`.
9509 rhs: Air.Inst.Ref,
9510 lhs_val: ?Value,
9511 rhs_val: ?Value,
9512 /// Only different than `scalar_ty` when it is a vector operation.
9513 result_ty: Type,
9514 scalar_ty: Type,
9515};
9516
9517fn checkSimdBinOp(
9518 sema: *Sema,
9519 block: *Block,
9520 src: LazySrcLoc,
9521 uncasted_lhs: Air.Inst.Ref,
9522 uncasted_rhs: Air.Inst.Ref,
9523 lhs_src: LazySrcLoc,
9524 rhs_src: LazySrcLoc,
9525) CompileError!SimdBinOp {
9526 const lhs_ty = sema.typeOf(uncasted_lhs);
9527 const rhs_ty = sema.typeOf(uncasted_rhs);
9528 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
9529 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
9530
9531 var vec_len: ?u64 = null;
9532 if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) {
9533 const lhs_len = lhs_ty.arrayLen();
9534 const rhs_len = rhs_ty.arrayLen();
9535 if (lhs_len != rhs_len) {
9536 const msg = msg: {
9537 const msg = try sema.errMsg(block, src, "vector length mismatch", .{});
9538 errdefer msg.destroy(sema.gpa);
9539 try sema.errNote(block, lhs_src, msg, "length {d} here", .{lhs_len});
9540 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len});
9541 break :msg msg;
9542 };
9543 return sema.failWithOwnedErrorMsg(msg);
9544 }
9545 vec_len = lhs_len;
9546 } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) {
9547 const msg = msg: {
9548 const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: {} and {}", .{
9549 lhs_ty, rhs_ty,
9550 });
9551 errdefer msg.destroy(sema.gpa);
9552 if (lhs_zig_ty_tag == .Vector) {
9553 try sema.errNote(block, lhs_src, msg, "vector here", .{});
9554 try sema.errNote(block, rhs_src, msg, "scalar here", .{});
9555 } else {
9556 try sema.errNote(block, lhs_src, msg, "scalar here", .{});
9557 try sema.errNote(block, rhs_src, msg, "vector here", .{});
9558 }
9559 break :msg msg;
9560 };
9561 return sema.failWithOwnedErrorMsg(msg);
9562 }
9563 const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{
9564 .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
9565 });
9566 const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src);
9567 const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src);
9568
9569 return SimdBinOp{
9570 .len = vec_len,
9571 .lhs = lhs,
9572 .rhs = rhs,
9573 .lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs),
9574 .rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs),
9575 .result_ty = result_ty,
9576 .scalar_ty = result_ty.scalarType(),
9577 };
9578}
9579
94779580fn resolveExportOptions(
94789581 sema: *Sema,
94799582 block: *Block,
......@@ -9744,8 +9847,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
97449847 .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target),
97459848 .Or => try stored_val.bitwiseOr (operand_val, sema.arena),
97469849 .Xor => try stored_val.bitwiseXor (operand_val, sema.arena),
9747 .Max => try stored_val.numberMax (operand_val, sema.arena),
9748 .Min => try stored_val.numberMin (operand_val, sema.arena),
9850 .Max => try stored_val.numberMax (operand_val),
9851 .Min => try stored_val.numberMin (operand_val),
97499852 // zig fmt: on
97509853 };
97519854 try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty);
......@@ -9826,10 +9929,62 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
98269929 return sema.fail(block, src, "TODO: Sema.zirFieldParentPtr", .{});
98279930}
98289931
9829fn zirMaximum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9932fn zirMinMax(
9933 sema: *Sema,
9934 block: *Block,
9935 inst: Zir.Inst.Index,
9936 air_tag: Air.Inst.Tag,
9937) CompileError!Air.Inst.Ref {
98309938 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
9939 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
98319940 const src = inst_data.src();
9832 return sema.fail(block, src, "TODO: Sema.zirMaximum", .{});
9941 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
9942 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
9943 const lhs = sema.resolveInst(extra.lhs);
9944 const rhs = sema.resolveInst(extra.rhs);
9945 try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs));
9946 try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs));
9947 const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src);
9948
9949 // TODO @maximum(max_int, undefined) should return max_int
9950
9951 const runtime_src = if (simd_op.lhs_val) |lhs_val| rs: {
9952 if (lhs_val.isUndef()) return sema.addConstUndef(simd_op.result_ty);
9953
9954 const rhs_val = simd_op.rhs_val orelse break :rs rhs_src;
9955
9956 if (rhs_val.isUndef()) return sema.addConstUndef(simd_op.result_ty);
9957
9958 const opFunc = switch (air_tag) {
9959 .min => Value.numberMin,
9960 .max => Value.numberMax,
9961 else => unreachable,
9962 };
9963 const vec_len = simd_op.len orelse {
9964 const result_val = try opFunc(lhs_val, rhs_val);
9965 return sema.addConstant(simd_op.result_ty, result_val);
9966 };
9967 var lhs_buf: Value.ElemValueBuffer = undefined;
9968 var rhs_buf: Value.ElemValueBuffer = undefined;
9969 const elems = try sema.arena.alloc(Value, vec_len);
9970 for (elems) |*elem, i| {
9971 const lhs_elem_val = lhs_val.elemValueBuffer(i, &lhs_buf);
9972 const rhs_elem_val = rhs_val.elemValueBuffer(i, &rhs_buf);
9973 elem.* = try opFunc(lhs_elem_val, rhs_elem_val);
9974 }
9975 return sema.addConstant(
9976 simd_op.result_ty,
9977 try Value.Tag.array.create(sema.arena, elems),
9978 );
9979 } else rs: {
9980 if (simd_op.rhs_val) |rhs_val| {
9981 if (rhs_val.isUndef()) return sema.addConstUndef(simd_op.result_ty);
9982 }
9983 break :rs lhs_src;
9984 };
9985
9986 try sema.requireRuntimeBlock(block, runtime_src);
9987 return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs);
98339988}
98349989
98359990fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
......@@ -9943,12 +10098,6 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
994310098 });
994410099}
994510100
9946fn zirMinimum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9947 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
9948 const src = inst_data.src();
9949 return sema.fail(block, src, "TODO: Sema.zirMinimum", .{});
9950}
9951
995210101fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
995310102 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
995410103 const src = inst_data.src();
......@@ -10453,12 +10602,7 @@ fn fieldVal(
1045310602 const result_ty = object_ty.slicePtrFieldType(buf);
1045410603 if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
1045510604 if (val.isUndef()) return sema.addConstUndef(result_ty);
10456 return sema.fail(
10457 block,
10458 field_name_src,
10459 "TODO implement comptime slice ptr",
10460 .{},
10461 );
10605 return sema.addConstant(result_ty, val.slicePtr());
1046210606 }
1046310607 try sema.requireRuntimeBlock(block, src);
1046410608 return block.addTyOp(.slice_ptr, result_ty, object);
......@@ -11464,6 +11608,10 @@ fn coerce(
1146411608 .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
1146511609 else => {},
1146611610 },
11611 .Array => switch (inst_ty.zigTypeTag()) {
11612 .Vector => return sema.coerceVectorToArray(block, dest_ty, dest_ty_src, inst, inst_src),
11613 else => {},
11614 },
1146711615 else => {},
1146811616 }
1146911617
......@@ -12045,6 +12193,48 @@ fn coerceEnumToUnion(
1204512193 return sema.failWithOwnedErrorMsg(msg);
1204612194}
1204712195
12196fn coerceVectorToArray(
12197 sema: *Sema,
12198 block: *Block,
12199 array_ty: Type,
12200 array_ty_src: LazySrcLoc,
12201 vector: Air.Inst.Ref,
12202 vector_src: LazySrcLoc,
12203) !Air.Inst.Ref {
12204 const vector_ty = sema.typeOf(vector);
12205 const array_len = array_ty.arrayLen();
12206 const vector_len = vector_ty.arrayLen();
12207 if (array_len != vector_len) {
12208 const msg = msg: {
12209 const msg = try sema.errMsg(block, vector_src, "expected {}, found {}", .{
12210 array_ty, vector_ty,
12211 });
12212 errdefer msg.destroy(sema.gpa);
12213 try sema.errNote(block, array_ty_src, msg, "array has length {d}", .{array_len});
12214 try sema.errNote(block, vector_src, msg, "vector has length {d}", .{vector_len});
12215 break :msg msg;
12216 };
12217 return sema.failWithOwnedErrorMsg(msg);
12218 }
12219
12220 const target = sema.mod.getTarget();
12221 const array_elem_ty = array_ty.childType();
12222 const vector_elem_ty = vector_ty.childType();
12223 const in_memory_result = coerceInMemoryAllowed(array_elem_ty, vector_elem_ty, false, target);
12224 if (in_memory_result != .ok) {
12225 // TODO recursive error notes for coerceInMemoryAllowed failure
12226 return sema.fail(block, vector_src, "expected {}, found {}", .{ array_ty, vector_ty });
12227 }
12228
12229 if (try sema.resolveMaybeUndefVal(block, vector_src, vector)) |vector_val| {
12230 // These types share the same comptime value representation.
12231 return sema.addConstant(array_ty, vector_val);
12232 }
12233
12234 try sema.requireRuntimeBlock(block, vector_src);
12235 return block.addTyOp(.bitcast, array_ty, vector);
12236}
12237
1204812238fn analyzeDeclVal(
1204912239 sema: *Sema,
1205012240 block: *Block,
src/Zir.zig+3-3
......@@ -906,9 +906,6 @@ pub const Inst = struct {
906906 /// Implements the `@fieldParentPtr` builtin.
907907 /// Uses the `pl_node` union field with payload `FieldParentPtr`.
908908 field_parent_ptr,
909 /// Implements the `@maximum` builtin.
910 /// Uses the `pl_node` union field with payload `Bin`
911 maximum,
912909 /// Implements the `@memcpy` builtin.
913910 /// Uses the `pl_node` union field with payload `Memcpy`.
914911 memcpy,
......@@ -918,6 +915,9 @@ pub const Inst = struct {
918915 /// Implements the `@minimum` builtin.
919916 /// Uses the `pl_node` union field with payload `Bin`
920917 minimum,
918 /// Implements the `@maximum` builtin.
919 /// Uses the `pl_node` union field with payload `Bin`
920 maximum,
921921 /// Implements the `@asyncCall` builtin.
922922 /// Uses the `pl_node` union field with payload `AsyncCall`.
923923 builtin_async_call,
src/codegen.zig+18
......@@ -839,6 +839,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
839839 .mod => try self.airMod(inst),
840840 .shl, .shl_exact => try self.airShl(inst),
841841 .shl_sat => try self.airShlSat(inst),
842 .min => try self.airMin(inst),
843 .max => try self.airMax(inst),
842844
843845 .cmp_lt => try self.airCmp(inst, .lt),
844846 .cmp_lte => try self.airCmp(inst, .lte),
......@@ -1299,6 +1301,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12991301 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
13001302 }
13011303
1304 fn airMin(self: *Self, inst: Air.Inst.Index) !void {
1305 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1306 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1307 else => return self.fail("TODO implement min for {}", .{self.target.cpu.arch}),
1308 };
1309 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1310 }
1311
1312 fn airMax(self: *Self, inst: Air.Inst.Index) !void {
1313 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1314 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1315 else => return self.fail("TODO implement max for {}", .{self.target.cpu.arch}),
1316 };
1317 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1318 }
1319
13021320 fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
13031321 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
13041322 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
src/codegen/c.zig+28
......@@ -989,6 +989,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
989989 .mul_sat => try airSatOp(f, inst, "muls_"),
990990 .shl_sat => try airSatOp(f, inst, "shls_"),
991991
992 .min => try airMinMax(f, inst, "<"),
993 .max => try airMinMax(f, inst, ">"),
994
992995 .cmp_eq => try airBinOp(f, inst, " == "),
993996 .cmp_gt => try airBinOp(f, inst, " > "),
994997 .cmp_gte => try airBinOp(f, inst, " >= "),
......@@ -1595,6 +1598,31 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue
15951598 return local;
15961599}
15971600
1601fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1602 if (f.liveness.isUnused(inst)) return CValue.none;
1603
1604 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1605 const lhs = try f.resolveInst(bin_op.lhs);
1606 const rhs = try f.resolveInst(bin_op.rhs);
1607
1608 const writer = f.object.writer();
1609 const inst_ty = f.air.typeOfIndex(inst);
1610 const local = try f.allocLocal(inst_ty, .Const);
1611
1612 // (lhs <> rhs) ? lhs : rhs
1613 try writer.writeAll(" = (");
1614 try f.writeCValue(writer, lhs);
1615 try writer.print("{s}", .{operator});
1616 try f.writeCValue(writer, rhs);
1617 try writer.writeAll(") ");
1618 try f.writeCValue(writer, lhs);
1619 try writer.writeAll(" : ");
1620 try f.writeCValue(writer, rhs);
1621 try writer.writeAll(";\n");
1622
1623 return local;
1624}
1625
15981626fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
15991627 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
16001628 const extra = f.air.extraData(Air.Call, pl_op.payload);
src/codegen/llvm.zig+74-10
......@@ -754,7 +754,7 @@ pub const DeclGen = struct {
754754
755755 const fields: [2]*const llvm.Type = .{
756756 try dg.llvmType(ptr_type),
757 try dg.llvmType(Type.initTag(.usize)),
757 try dg.llvmType(Type.usize),
758758 };
759759 return dg.context.structType(&fields, fields.len, .False);
760760 } else {
......@@ -780,10 +780,14 @@ pub const DeclGen = struct {
780780 return llvm_struct_ty;
781781 },
782782 .Array => {
783 const elem_type = try dg.llvmType(t.elemType());
783 const elem_type = try dg.llvmType(t.childType());
784784 const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null);
785785 return elem_type.arrayType(@intCast(c_uint, total_len));
786786 },
787 .Vector => {
788 const elem_type = try dg.llvmType(t.childType());
789 return elem_type.vectorType(@intCast(c_uint, t.arrayLen()));
790 },
787791 .Optional => {
788792 var buf: Type.Payload.ElemType = undefined;
789793 const child_type = t.optionalChild(&buf);
......@@ -966,7 +970,6 @@ pub const DeclGen = struct {
966970
967971 .Frame,
968972 .AnyFrame,
969 .Vector,
970973 => return dg.todo("implement llvmType for type '{}'", .{t}),
971974 }
972975 }
......@@ -1062,7 +1065,7 @@ pub const DeclGen = struct {
10621065 return self.context.constStruct(&fields, fields.len, .False);
10631066 },
10641067 .int_u64 => {
1065 const llvm_usize = try self.llvmType(Type.initTag(.usize));
1068 const llvm_usize = try self.llvmType(Type.usize);
10661069 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
10671070 return llvm_int.constIntToPtr(try self.llvmType(tv.ty));
10681071 },
......@@ -1295,7 +1298,7 @@ pub const DeclGen = struct {
12951298 .val = tv.val,
12961299 }),
12971300 try self.genTypedValue(.{
1298 .ty = Type.initTag(.usize),
1301 .ty = Type.usize,
12991302 .val = Value.initPayload(&slice_len.base),
13001303 }),
13011304 };
......@@ -1470,6 +1473,8 @@ pub const FuncGen = struct {
14701473 .shl => try self.airShl(inst),
14711474 .shl_sat => try self.airShlSat(inst),
14721475 .shl_exact => try self.airShlExact(inst),
1476 .min => try self.airMin(inst),
1477 .max => try self.airMax(inst),
14731478
14741479 .bit_and, .bool_and => try self.airAnd(inst),
14751480 .bit_or, .bool_or => try self.airOr(inst),
......@@ -2356,6 +2361,32 @@ pub const FuncGen = struct {
23562361 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
23572362 }
23582363
2364 fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2365 if (self.liveness.isUnused(inst)) return null;
2366
2367 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2368 const lhs = try self.resolveInst(bin_op.lhs);
2369 const rhs = try self.resolveInst(bin_op.rhs);
2370 const scalar_ty = self.air.typeOfIndex(inst).scalarType();
2371
2372 if (scalar_ty.isAnyFloat()) return self.builder.buildMinNum(lhs, rhs, "");
2373 if (scalar_ty.isSignedInt()) return self.builder.buildSMin(lhs, rhs, "");
2374 return self.builder.buildUMin(lhs, rhs, "");
2375 }
2376
2377 fn airMax(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2378 if (self.liveness.isUnused(inst)) return null;
2379
2380 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2381 const lhs = try self.resolveInst(bin_op.lhs);
2382 const rhs = try self.resolveInst(bin_op.rhs);
2383 const scalar_ty = self.air.typeOfIndex(inst).scalarType();
2384
2385 if (scalar_ty.isAnyFloat()) return self.builder.buildMaxNum(lhs, rhs, "");
2386 if (scalar_ty.isSignedInt()) return self.builder.buildSMax(lhs, rhs, "");
2387 return self.builder.buildUMax(lhs, rhs, "");
2388 }
2389
23592390 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
23602391 if (self.liveness.isUnused(inst)) return null;
23612392
......@@ -2705,15 +2736,48 @@ pub const FuncGen = struct {
27052736 }
27062737
27072738 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2708 if (self.liveness.isUnused(inst))
2709 return null;
2739 if (self.liveness.isUnused(inst)) return null;
27102740
27112741 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
27122742 const operand = try self.resolveInst(ty_op.operand);
2743 const operand_ty = self.air.typeOf(ty_op.operand);
27132744 const inst_ty = self.air.typeOfIndex(inst);
2714 const dest_type = try self.dg.llvmType(inst_ty);
2745 const llvm_dest_ty = try self.dg.llvmType(inst_ty);
2746
2747 // TODO look into pulling this logic out into a different AIR instruction than bitcast
2748 if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) {
2749 const target = self.dg.module.getTarget();
2750 const elem_ty = operand_ty.childType();
2751 if (!isByRef(inst_ty)) {
2752 return self.dg.todo("implement bitcast vector to non-ref array", .{});
2753 }
2754 const array_ptr = self.buildAlloca(llvm_dest_ty);
2755 const bitcast_ok = elem_ty.bitSize(target) == elem_ty.abiSize(target) * 8;
2756 if (bitcast_ok) {
2757 const llvm_vector_ty = try self.dg.llvmType(operand_ty);
2758 const casted_ptr = self.builder.buildBitCast(array_ptr, llvm_vector_ty.pointerType(0), "");
2759 _ = self.builder.buildStore(operand, casted_ptr);
2760 } else {
2761 // If the ABI size of the element type is not evenly divisible by size in bits;
2762 // a simple bitcast will not work, and we fall back to extractelement.
2763 const llvm_usize = try self.dg.llvmType(Type.usize);
2764 const llvm_u32 = self.context.intType(32);
2765 const zero = llvm_usize.constNull();
2766 const vector_len = operand_ty.arrayLen();
2767 var i: u64 = 0;
2768 while (i < vector_len) : (i += 1) {
2769 const index_usize = llvm_usize.constInt(i, .False);
2770 const index_u32 = llvm_u32.constInt(i, .False);
2771 const indexes: [2]*const llvm.Value = .{ zero, index_usize };
2772 const elem_ptr = self.builder.buildInBoundsGEP(array_ptr, &indexes, indexes.len, "");
2773 const elem = self.builder.buildExtractElement(operand, index_u32, "");
2774 _ = self.builder.buildStore(elem, elem_ptr);
2775 }
2776 }
2777 return array_ptr;
2778 }
27152779
2716 return self.builder.buildBitCast(operand, dest_type, "");
2780 return self.builder.buildBitCast(operand, llvm_dest_ty, "");
27172781 }
27182782
27192783 fn airBoolToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -2906,7 +2970,7 @@ pub const FuncGen = struct {
29062970 }
29072971
29082972 // It's a pointer but we need to treat it as an int.
2909 const usize_llvm_ty = try self.dg.llvmType(Type.initTag(.usize));
2973 const usize_llvm_ty = try self.dg.llvmType(Type.usize);
29102974 const casted_ptr = self.builder.buildBitCast(ptr, usize_llvm_ty.pointerType(0), "");
29112975 const casted_operand = self.builder.buildPtrToInt(operand, usize_llvm_ty, "");
29122976 const uncasted_result = self.builder.buildAtomicRmw(
src/codegen/llvm/bindings.zig+29
......@@ -212,6 +212,9 @@ pub const Type = opaque {
212212 pub const arrayType = LLVMArrayType;
213213 extern fn LLVMArrayType(ElementType: *const Type, ElementCount: c_uint) *const Type;
214214
215 pub const vectorType = LLVMVectorType;
216 extern fn LLVMVectorType(ElementType: *const Type, ElementCount: c_uint) *const Type;
217
215218 pub const structSetBody = LLVMStructSetBody;
216219 extern fn LLVMStructSetBody(
217220 StructTy: *const Type,
......@@ -553,6 +556,14 @@ pub const Builder = opaque {
553556 Name: [*:0]const u8,
554557 ) *const Value;
555558
559 pub const buildExtractElement = LLVMBuildExtractElement;
560 extern fn LLVMBuildExtractElement(
561 *const Builder,
562 VecVal: *const Value,
563 Index: *const Value,
564 Name: [*:0]const u8,
565 ) *const Value;
566
556567 pub const buildPtrToInt = LLVMBuildPtrToInt;
557568 extern fn LLVMBuildPtrToInt(
558569 *const Builder,
......@@ -700,6 +711,24 @@ pub const Builder = opaque {
700711 Size: *const Value,
701712 is_volatile: bool,
702713 ) *const Value;
714
715 pub const buildMaxNum = ZigLLVMBuildMaxNum;
716 extern fn ZigLLVMBuildMaxNum(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value;
717
718 pub const buildMinNum = ZigLLVMBuildMinNum;
719 extern fn ZigLLVMBuildMinNum(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value;
720
721 pub const buildUMax = ZigLLVMBuildUMax;
722 extern fn ZigLLVMBuildUMax(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value;
723
724 pub const buildUMin = ZigLLVMBuildUMin;
725 extern fn ZigLLVMBuildUMin(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value;
726
727 pub const buildSMax = ZigLLVMBuildSMax;
728 extern fn ZigLLVMBuildSMax(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value;
729
730 pub const buildSMin = ZigLLVMBuildSMin;
731 extern fn ZigLLVMBuildSMin(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value;
703732};
704733
705734pub const IntPredicate = enum(c_uint) {
src/print_air.zig+2
......@@ -138,6 +138,8 @@ const Writer = struct {
138138 .shl_sat,
139139 .shr,
140140 .set_union_tag,
141 .min,
142 .max,
141143 => try w.writeBinOp(s, inst),
142144
143145 .is_null,
src/type.zig+15
......@@ -2517,6 +2517,14 @@ pub const Type = extern union {
25172517 };
25182518 }
25192519
2520 /// For vectors, returns the element type. Otherwise returns self.
2521 pub fn scalarType(ty: Type) Type {
2522 return switch (ty.zigTypeTag()) {
2523 .Vector => ty.childType(),
2524 else => ty,
2525 };
2526 }
2527
25202528 /// Asserts that the type is an optional.
25212529 /// Resulting `Type` will have inner memory referencing `buf`.
25222530 pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {
......@@ -4017,6 +4025,13 @@ pub const Type = extern union {
40174025 });
40184026 }
40194027
4028 pub fn vector(arena: *Allocator, len: u64, elem_type: Type) Allocator.Error!Type {
4029 return Tag.vector.create(arena, .{
4030 .len = len,
4031 .elem_type = elem_type,
4032 });
4033 }
4034
40204035 pub fn smallestUnsignedBits(max: u64) u16 {
40214036 if (max == 0) return 0;
40224037 const base = std.math.log2(max);
src/value.zig+37-54
......@@ -1626,6 +1626,14 @@ pub const Value = extern union {
16261626 };
16271627 }
16281628
1629 pub fn slicePtr(val: Value) Value {
1630 return switch (val.tag()) {
1631 .slice => val.castTag(.slice).?.data.ptr,
1632 .decl_ref, .decl_ref_mut => val,
1633 else => unreachable,
1634 };
1635 }
1636
16291637 pub fn sliceLen(val: Value) u64 {
16301638 return switch (val.tag()) {
16311639 .slice => val.castTag(.slice).?.data.len.toUnsignedInt(),
......@@ -2042,63 +2050,27 @@ pub const Value = extern union {
20422050 }
20432051
20442052 /// Supports both floats and ints; handles undefined.
2045 pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {
2046 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2047
2048 // TODO is this a performance issue? maybe we should try the operation without
2049 // resorting to BigInt first.
2050 var lhs_space: Value.BigIntSpace = undefined;
2051 var rhs_space: Value.BigIntSpace = undefined;
2052 const lhs_bigint = lhs.toBigInt(&lhs_space);
2053 const rhs_bigint = rhs.toBigInt(&rhs_space);
2054 const limbs = try arena.alloc(
2055 std.math.big.Limb,
2056 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2057 );
2058 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2059
2060 switch (lhs_bigint.order(rhs_bigint)) {
2061 .lt => result_bigint.copy(rhs_bigint),
2062 .gt, .eq => result_bigint.copy(lhs_bigint),
2063 }
2064
2065 const result_limbs = result_bigint.limbs[0..result_bigint.len];
2066
2067 if (result_bigint.positive) {
2068 return Value.Tag.int_big_positive.create(arena, result_limbs);
2069 } else {
2070 return Value.Tag.int_big_negative.create(arena, result_limbs);
2071 }
2053 pub fn numberMax(lhs: Value, rhs: Value) !Value {
2054 if (lhs.isUndef() or rhs.isUndef()) return undef;
2055 if (lhs.isNan()) return rhs;
2056 if (rhs.isNan()) return lhs;
2057
2058 return switch (order(lhs, rhs)) {
2059 .lt => rhs,
2060 .gt, .eq => lhs,
2061 };
20722062 }
20732063
20742064 /// Supports both floats and ints; handles undefined.
2075 pub fn numberMin(lhs: Value, rhs: Value, arena: *Allocator) !Value {
2076 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2077
2078 // TODO is this a performance issue? maybe we should try the operation without
2079 // resorting to BigInt first.
2080 var lhs_space: Value.BigIntSpace = undefined;
2081 var rhs_space: Value.BigIntSpace = undefined;
2082 const lhs_bigint = lhs.toBigInt(&lhs_space);
2083 const rhs_bigint = rhs.toBigInt(&rhs_space);
2084 const limbs = try arena.alloc(
2085 std.math.big.Limb,
2086 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2087 );
2088 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2089
2090 switch (lhs_bigint.order(rhs_bigint)) {
2091 .lt => result_bigint.copy(lhs_bigint),
2092 .gt, .eq => result_bigint.copy(rhs_bigint),
2093 }
2094
2095 const result_limbs = result_bigint.limbs[0..result_bigint.len];
2096
2097 if (result_bigint.positive) {
2098 return Value.Tag.int_big_positive.create(arena, result_limbs);
2099 } else {
2100 return Value.Tag.int_big_negative.create(arena, result_limbs);
2101 }
2065 pub fn numberMin(lhs: Value, rhs: Value) !Value {
2066 if (lhs.isUndef() or rhs.isUndef()) return undef;
2067 if (lhs.isNan()) return rhs;
2068 if (rhs.isNan()) return lhs;
2069
2070 return switch (order(lhs, rhs)) {
2071 .lt => lhs,
2072 .gt, .eq => rhs,
2073 };
21022074 }
21032075
21042076 /// operands must be integers; handles undefined.
......@@ -2327,6 +2299,17 @@ pub const Value = extern union {
23272299 }
23282300 }
23292301
2302 /// Returns true if the value is a floating point type and is NaN. Returns false otherwise.
2303 pub fn isNan(val: Value) bool {
2304 return switch (val.tag()) {
2305 .float_16 => std.math.isNan(val.castTag(.float_16).?.data),
2306 .float_32 => std.math.isNan(val.castTag(.float_32).?.data),
2307 .float_64 => std.math.isNan(val.castTag(.float_64).?.data),
2308 .float_128 => std.math.isNan(val.castTag(.float_128).?.data),
2309 else => false,
2310 };
2311 }
2312
23302313 pub fn floatRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
23312314 _ = lhs;
23322315 _ = rhs;
test/behavior.zig+1-1
......@@ -24,6 +24,7 @@ test {
2424 _ = @import("behavior/generics.zig");
2525 _ = @import("behavior/if.zig");
2626 _ = @import("behavior/math.zig");
27 _ = @import("behavior/maximum_minimum.zig");
2728 _ = @import("behavior/member_func.zig");
2829 _ = @import("behavior/optional.zig");
2930 _ = @import("behavior/pointers.zig");
......@@ -130,7 +131,6 @@ test {
130131 _ = @import("behavior/inttoptr.zig");
131132 _ = @import("behavior/ir_block_deps.zig");
132133 _ = @import("behavior/math_stage1.zig");
133 _ = @import("behavior/maximum_minimum.zig");
134134 _ = @import("behavior/merge_error_sets.zig");
135135 _ = @import("behavior/misc.zig");
136136 _ = @import("behavior/muladd.zig");
test/behavior/maximum_minimum.zig+4-4
......@@ -8,8 +8,8 @@ const Vector = std.meta.Vector;
88test "@maximum" {
99 const S = struct {
1010 fn doTheTest() !void {
11 try expectEqual(@as(i32, 10), @maximum(@as(i32, -3), @as(i32, 10)));
12 try expectEqual(@as(f32, 3.2), @maximum(@as(f32, 3.2), @as(f32, 0.68)));
11 try expect(@as(i32, 10) == @maximum(@as(i32, -3), @as(i32, 10)));
12 try expect(@as(f32, 3.2) == @maximum(@as(f32, 3.2), @as(f32, 0.68)));
1313
1414 var a: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
1515 var b: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
......@@ -34,8 +34,8 @@ test "@maximum" {
3434test "@minimum" {
3535 const S = struct {
3636 fn doTheTest() !void {
37 try expectEqual(@as(i32, -3), @minimum(@as(i32, -3), @as(i32, 10)));
38 try expectEqual(@as(f32, 0.68), @minimum(@as(f32, 3.2), @as(f32, 0.68)));
37 try expect(@as(i32, -3) == @minimum(@as(i32, -3), @as(i32, 10)));
38 try expect(@as(f32, 0.68) == @minimum(@as(f32, 3.2), @as(f32, 0.68)));
3939
4040 var a: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
4141 var b: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };