| author | |
| committer | |
| log | 7233a3324aaa5b3995606f24b2b961149219986b |
| tree | 009a83fa10f489f0d6da4f77f07a4d0c8ca4e388 |
| parent | 76e103057ea6037d3bb3e44cd33880a9a91609fb |
Notably, Value.eql and Value.hash are improved to treat NaN as equal to
itself, so that Type/Value can be hash map keys. Likewise float hashing
normalizes the float value before computing the hash.14 files changed, 362 insertions(+), 108 deletions(-)
src/Air.zig+14| ... | @@ -530,6 +530,14 @@ pub const Inst = struct { | ... | @@ -530,6 +530,14 @@ pub const Inst = struct { |
| 530 | /// Given an integer operand, return the float with the closest mathematical meaning. | 530 | /// Given an integer operand, return the float with the closest mathematical meaning. |
| 531 | /// Uses the `ty_op` field. | 531 | /// Uses the `ty_op` field. |
| 532 | int_to_float, | 532 | int_to_float, |
| 533 | |||
| 534 | /// Transforms a vector into a scalar value by performing a sequential | ||
| 535 | /// horizontal reduction of its elements using the specified operator. | ||
| 536 | /// The vector element type (and hence result type) will be: | ||
| 537 | /// * and, or, xor => integer or boolean | ||
| 538 | /// * min, max, add, mul => integer or float | ||
| 539 | /// Uses the `reduce` field. | ||
| 540 | reduce, | ||
| 533 | /// Given an integer, bool, float, or pointer operand, return a vector with all elements | 541 | /// Given an integer, bool, float, or pointer operand, return a vector with all elements |
| 534 | /// equal to the scalar value. | 542 | /// equal to the scalar value. |
| 535 | /// Uses the `ty_op` field. | 543 | /// Uses the `ty_op` field. |
| ... | @@ -695,6 +703,10 @@ pub const Inst = struct { | ... | @@ -695,6 +703,10 @@ pub const Inst = struct { |
| 695 | locality: u2, | 703 | locality: u2, |
| 696 | cache: std.builtin.PrefetchOptions.Cache, | 704 | cache: std.builtin.PrefetchOptions.Cache, |
| 697 | }, | 705 | }, |
| 706 | reduce: struct { | ||
| 707 | operand: Ref, | ||
| 708 | operation: std.builtin.ReduceOp, | ||
| 709 | }, | ||
| 698 | 710 | ||
| 699 | // Make sure we don't accidentally add a field to make this union | 711 | // Make sure we don't accidentally add a field to make this union |
| 700 | // bigger than expected. Note that in Debug builds, Zig is allowed | 712 | // bigger than expected. Note that in Debug builds, Zig is allowed |
| ... | @@ -1027,6 +1039,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -1027,6 +1039,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1027 | return ptr_ty.elemType(); | 1039 | return ptr_ty.elemType(); |
| 1028 | }, | 1040 | }, |
| 1029 | 1041 | ||
| 1042 | .reduce => return air.typeOf(datas[inst].reduce.operand).childType(), | ||
| 1043 | |||
| 1030 | .mul_add => return air.typeOf(datas[inst].pl_op.operand), | 1044 | .mul_add => return air.typeOf(datas[inst].pl_op.operand), |
| 1031 | 1045 | ||
| 1032 | .add_with_overflow, | 1046 | .add_with_overflow, |
src/Liveness.zig+4| ... | @@ -435,6 +435,10 @@ fn analyzeInst( | ... | @@ -435,6 +435,10 @@ fn analyzeInst( |
| 435 | const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data; | 435 | const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data; |
| 436 | return trackOperands(a, new_set, inst, main_tomb, .{ extra.a, extra.b, .none }); | 436 | return trackOperands(a, new_set, inst, main_tomb, .{ extra.a, extra.b, .none }); |
| 437 | }, | 437 | }, |
| 438 | .reduce => { | ||
| 439 | const reduce = inst_datas[inst].reduce; | ||
| 440 | return trackOperands(a, new_set, inst, main_tomb, .{ reduce.operand, .none, .none }); | ||
| 441 | }, | ||
| 438 | .aggregate_init => { | 442 | .aggregate_init => { |
| 439 | const ty_pl = inst_datas[inst].ty_pl; | 443 | const ty_pl = inst_datas[inst].ty_pl; |
| 440 | const aggregate_ty = a.air.getRefType(ty_pl.ty); | 444 | const aggregate_ty = a.air.getRefType(ty_pl.ty); |
src/Sema.zig+88-18| ... | @@ -13973,17 +13973,27 @@ fn resolveExportOptions( | ... | @@ -13973,17 +13973,27 @@ fn resolveExportOptions( |
| 13973 | }; | 13973 | }; |
| 13974 | } | 13974 | } |
| 13975 | 13975 | ||
| 13976 | fn resolveAtomicOrder( | 13976 | fn resolveBuiltinEnum( |
| 13977 | sema: *Sema, | 13977 | sema: *Sema, |
| 13978 | block: *Block, | 13978 | block: *Block, |
| 13979 | src: LazySrcLoc, | 13979 | src: LazySrcLoc, |
| 13980 | zir_ref: Zir.Inst.Ref, | 13980 | zir_ref: Zir.Inst.Ref, |
| 13981 | ) CompileError!std.builtin.AtomicOrder { | 13981 | comptime name: []const u8, |
| 13982 | const atomic_order_ty = try sema.getBuiltinType(block, src, "AtomicOrder"); | 13982 | ) CompileError!@field(std.builtin, name) { |
| 13983 | const ty = try sema.getBuiltinType(block, src, name); | ||
| 13983 | const air_ref = sema.resolveInst(zir_ref); | 13984 | const air_ref = sema.resolveInst(zir_ref); |
| 13984 | const coerced = try sema.coerce(block, atomic_order_ty, air_ref, src); | 13985 | const coerced = try sema.coerce(block, ty, air_ref, src); |
| 13985 | const val = try sema.resolveConstValue(block, src, coerced); | 13986 | const val = try sema.resolveConstValue(block, src, coerced); |
| 13986 | return val.toEnum(std.builtin.AtomicOrder); | 13987 | return val.toEnum(@field(std.builtin, name)); |
| 13988 | } | ||
| 13989 | |||
| 13990 | fn resolveAtomicOrder( | ||
| 13991 | sema: *Sema, | ||
| 13992 | block: *Block, | ||
| 13993 | src: LazySrcLoc, | ||
| 13994 | zir_ref: Zir.Inst.Ref, | ||
| 13995 | ) CompileError!std.builtin.AtomicOrder { | ||
| 13996 | return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicOrder"); | ||
| 13987 | } | 13997 | } |
| 13988 | 13998 | ||
| 13989 | fn resolveAtomicRmwOp( | 13999 | fn resolveAtomicRmwOp( |
| ... | @@ -13992,11 +14002,7 @@ fn resolveAtomicRmwOp( | ... | @@ -13992,11 +14002,7 @@ fn resolveAtomicRmwOp( |
| 13992 | src: LazySrcLoc, | 14002 | src: LazySrcLoc, |
| 13993 | zir_ref: Zir.Inst.Ref, | 14003 | zir_ref: Zir.Inst.Ref, |
| 13994 | ) CompileError!std.builtin.AtomicRmwOp { | 14004 | ) CompileError!std.builtin.AtomicRmwOp { |
| 13995 | const atomic_rmw_op_ty = try sema.getBuiltinType(block, src, "AtomicRmwOp"); | 14005 | return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicRmwOp"); |
| 13996 | const air_ref = sema.resolveInst(zir_ref); | ||
| 13997 | const coerced = try sema.coerce(block, atomic_rmw_op_ty, air_ref, src); | ||
| 13998 | const val = try sema.resolveConstValue(block, src, coerced); | ||
| 13999 | return val.toEnum(std.builtin.AtomicRmwOp); | ||
| 14000 | } | 14006 | } |
| 14001 | 14007 | ||
| 14002 | fn zirCmpxchg( | 14008 | fn zirCmpxchg( |
| ... | @@ -14118,8 +14124,72 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I | ... | @@ -14118,8 +14124,72 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 14118 | 14124 | ||
| 14119 | fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 14125 | fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14120 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 14126 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 14121 | const src = inst_data.src(); | 14127 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 14122 | return sema.fail(block, src, "TODO: Sema.zirReduce", .{}); | 14128 | const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 14129 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 14130 | const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp"); | ||
| 14131 | const operand = sema.resolveInst(extra.rhs); | ||
| 14132 | const operand_ty = sema.typeOf(operand); | ||
| 14133 | |||
| 14134 | if (operand_ty.zigTypeTag() != .Vector) { | ||
| 14135 | return sema.fail(block, operand_src, "expected vector, found {}", .{operand_ty}); | ||
| 14136 | } | ||
| 14137 | |||
| 14138 | const scalar_ty = operand_ty.childType(); | ||
| 14139 | |||
| 14140 | // Type-check depending on operation. | ||
| 14141 | switch (operation) { | ||
| 14142 | .And, .Or, .Xor => switch (scalar_ty.zigTypeTag()) { | ||
| 14143 | .Int, .Bool => {}, | ||
| 14144 | else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found {}", .{ | ||
| 14145 | @tagName(operation), operand_ty, | ||
| 14146 | }), | ||
| 14147 | }, | ||
| 14148 | .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag()) { | ||
| 14149 | .Int, .Float => {}, | ||
| 14150 | else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found {}", .{ | ||
| 14151 | @tagName(operation), operand_ty, | ||
| 14152 | }), | ||
| 14153 | }, | ||
| 14154 | } | ||
| 14155 | |||
| 14156 | const vec_len = operand_ty.vectorLen(); | ||
| 14157 | if (vec_len == 0) { | ||
| 14158 | // TODO re-evaluate if we should introduce a "neutral value" for some operations, | ||
| 14159 | // e.g. zero for add and one for mul. | ||
| 14160 | return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{}); | ||
| 14161 | } | ||
| 14162 | |||
| 14163 | if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { | ||
| 14164 | if (operand_val.isUndef()) return sema.addConstUndef(scalar_ty); | ||
| 14165 | |||
| 14166 | const target = sema.mod.getTarget(); | ||
| 14167 | var accum: Value = try operand_val.elemValue(sema.arena, 0); | ||
| 14168 | var elem_buf: Value.ElemValueBuffer = undefined; | ||
| 14169 | var i: u32 = 1; | ||
| 14170 | while (i < vec_len) : (i += 1) { | ||
| 14171 | const elem_val = operand_val.elemValueBuffer(i, &elem_buf); | ||
| 14172 | switch (operation) { | ||
| 14173 | .And => accum = try accum.bitwiseAnd(elem_val, sema.arena), | ||
| 14174 | .Or => accum = try accum.bitwiseOr(elem_val, sema.arena), | ||
| 14175 | .Xor => accum = try accum.bitwiseXor(elem_val, sema.arena), | ||
| 14176 | .Min => accum = accum.numberMin(elem_val), | ||
| 14177 | .Max => accum = accum.numberMax(elem_val), | ||
| 14178 | .Add => accum = try accum.numberAddWrap(elem_val, scalar_ty, sema.arena, target), | ||
| 14179 | .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, target), | ||
| 14180 | } | ||
| 14181 | } | ||
| 14182 | return sema.addConstant(scalar_ty, accum); | ||
| 14183 | } | ||
| 14184 | |||
| 14185 | try sema.requireRuntimeBlock(block, operand_src); | ||
| 14186 | return block.addInst(.{ | ||
| 14187 | .tag = .reduce, | ||
| 14188 | .data = .{ .reduce = .{ | ||
| 14189 | .operand = operand, | ||
| 14190 | .operation = operation, | ||
| 14191 | } }, | ||
| 14192 | }); | ||
| 14123 | } | 14193 | } |
| 14124 | 14194 | ||
| 14125 | fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 14195 | fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -14425,8 +14495,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -14425,8 +14495,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 14425 | .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), | 14495 | .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), |
| 14426 | .Or => try stored_val.bitwiseOr (operand_val, sema.arena), | 14496 | .Or => try stored_val.bitwiseOr (operand_val, sema.arena), |
| 14427 | .Xor => try stored_val.bitwiseXor (operand_val, sema.arena), | 14497 | .Xor => try stored_val.bitwiseXor (operand_val, sema.arena), |
| 14428 | .Max => try stored_val.numberMax (operand_val), | 14498 | .Max => stored_val.numberMax (operand_val), |
| 14429 | .Min => try stored_val.numberMin (operand_val), | 14499 | .Min => stored_val.numberMin (operand_val), |
| 14430 | // zig fmt: on | 14500 | // zig fmt: on |
| 14431 | }; | 14501 | }; |
| 14432 | try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); | 14502 | try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); |
| ... | @@ -14760,7 +14830,7 @@ fn analyzeMinMax( | ... | @@ -14760,7 +14830,7 @@ fn analyzeMinMax( |
| 14760 | else => unreachable, | 14830 | else => unreachable, |
| 14761 | }; | 14831 | }; |
| 14762 | const vec_len = simd_op.len orelse { | 14832 | const vec_len = simd_op.len orelse { |
| 14763 | const result_val = try opFunc(lhs_val, rhs_val); | 14833 | const result_val = opFunc(lhs_val, rhs_val); |
| 14764 | return sema.addConstant(simd_op.result_ty, result_val); | 14834 | return sema.addConstant(simd_op.result_ty, result_val); |
| 14765 | }; | 14835 | }; |
| 14766 | var lhs_buf: Value.ElemValueBuffer = undefined; | 14836 | var lhs_buf: Value.ElemValueBuffer = undefined; |
| ... | @@ -14769,7 +14839,7 @@ fn analyzeMinMax( | ... | @@ -14769,7 +14839,7 @@ fn analyzeMinMax( |
| 14769 | for (elems) |*elem, i| { | 14839 | for (elems) |*elem, i| { |
| 14770 | const lhs_elem_val = lhs_val.elemValueBuffer(i, &lhs_buf); | 14840 | const lhs_elem_val = lhs_val.elemValueBuffer(i, &lhs_buf); |
| 14771 | const rhs_elem_val = rhs_val.elemValueBuffer(i, &rhs_buf); | 14841 | const rhs_elem_val = rhs_val.elemValueBuffer(i, &rhs_buf); |
| 14772 | elem.* = try opFunc(lhs_elem_val, rhs_elem_val); | 14842 | elem.* = opFunc(lhs_elem_val, rhs_elem_val); |
| 14773 | } | 14843 | } |
| 14774 | return sema.addConstant( | 14844 | return sema.addConstant( |
| 14775 | simd_op.result_ty, | 14845 | simd_op.result_ty, |
| ... | @@ -19246,9 +19316,9 @@ fn cmpNumeric( | ... | @@ -19246,9 +19316,9 @@ fn cmpNumeric( |
| 19246 | const rhs_ty_tag = rhs_ty.zigTypeTag(); | 19316 | const rhs_ty_tag = rhs_ty.zigTypeTag(); |
| 19247 | 19317 | ||
| 19248 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | 19318 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { |
| 19249 | if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) { | 19319 | if (lhs_ty.vectorLen() != rhs_ty.vectorLen()) { |
| 19250 | return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{ | 19320 | return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{ |
| 19251 | lhs_ty.arrayLen(), rhs_ty.arrayLen(), | 19321 | lhs_ty.vectorLen(), rhs_ty.vectorLen(), |
| 19252 | }); | 19322 | }); |
| 19253 | } | 19323 | } |
| 19254 | return sema.fail(block, src, "TODO implement support for vectors in cmpNumeric", .{}); | 19324 | return sema.fail(block, src, "TODO implement support for vectors in cmpNumeric", .{}); |
src/arch/aarch64/CodeGen.zig+7| ... | @@ -640,6 +640,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -640,6 +640,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 640 | .error_name => try self.airErrorName(inst), | 640 | .error_name => try self.airErrorName(inst), |
| 641 | .splat => try self.airSplat(inst), | 641 | .splat => try self.airSplat(inst), |
| 642 | .shuffle => try self.airShuffle(inst), | 642 | .shuffle => try self.airShuffle(inst), |
| 643 | .reduce => try self.airReduce(inst), | ||
| 643 | .aggregate_init => try self.airAggregateInit(inst), | 644 | .aggregate_init => try self.airAggregateInit(inst), |
| 644 | .union_init => try self.airUnionInit(inst), | 645 | .union_init => try self.airUnionInit(inst), |
| 645 | .prefetch => try self.airPrefetch(inst), | 646 | .prefetch => try self.airPrefetch(inst), |
| ... | @@ -3727,6 +3728,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3727,6 +3728,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 3727 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 3728 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3728 | } | 3729 | } |
| 3729 | 3730 | ||
| 3731 | fn airReduce(self: *Self, inst: Air.Inst.Index) !void { | ||
| 3732 | const reduce = self.air.instructions.items(.data)[inst].reduce; | ||
| 3733 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{}); | ||
| 3734 | return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); | ||
| 3735 | } | ||
| 3736 | |||
| 3730 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { | 3737 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 3731 | const vector_ty = self.air.typeOfIndex(inst); | 3738 | const vector_ty = self.air.typeOfIndex(inst); |
| 3732 | const len = vector_ty.vectorLen(); | 3739 | const len = vector_ty.vectorLen(); |
src/arch/arm/CodeGen.zig+7| ... | @@ -637,6 +637,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -637,6 +637,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 637 | .error_name => try self.airErrorName(inst), | 637 | .error_name => try self.airErrorName(inst), |
| 638 | .splat => try self.airSplat(inst), | 638 | .splat => try self.airSplat(inst), |
| 639 | .shuffle => try self.airShuffle(inst), | 639 | .shuffle => try self.airShuffle(inst), |
| 640 | .reduce => try self.airReduce(inst), | ||
| 640 | .aggregate_init => try self.airAggregateInit(inst), | 641 | .aggregate_init => try self.airAggregateInit(inst), |
| 641 | .union_init => try self.airUnionInit(inst), | 642 | .union_init => try self.airUnionInit(inst), |
| 642 | .prefetch => try self.airPrefetch(inst), | 643 | .prefetch => try self.airPrefetch(inst), |
| ... | @@ -4204,6 +4205,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4204,6 +4205,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 4204 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 4205 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 4205 | } | 4206 | } |
| 4206 | 4207 | ||
| 4208 | fn airReduce(self: *Self, inst: Air.Inst.Index) !void { | ||
| 4209 | const reduce = self.air.instructions.items(.data)[inst].reduce; | ||
| 4210 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for arm", .{}); | ||
| 4211 | return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); | ||
| 4212 | } | ||
| 4213 | |||
| 4207 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { | 4214 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 4208 | const vector_ty = self.air.typeOfIndex(inst); | 4215 | const vector_ty = self.air.typeOfIndex(inst); |
| 4209 | const len = vector_ty.vectorLen(); | 4216 | const len = vector_ty.vectorLen(); |
src/arch/riscv64/CodeGen.zig+7| ... | @@ -604,6 +604,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -604,6 +604,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 604 | .error_name => try self.airErrorName(inst), | 604 | .error_name => try self.airErrorName(inst), |
| 605 | .splat => try self.airSplat(inst), | 605 | .splat => try self.airSplat(inst), |
| 606 | .shuffle => try self.airShuffle(inst), | 606 | .shuffle => try self.airShuffle(inst), |
| 607 | .reduce => try self.airReduce(inst), | ||
| 607 | .aggregate_init => try self.airAggregateInit(inst), | 608 | .aggregate_init => try self.airAggregateInit(inst), |
| 608 | .union_init => try self.airUnionInit(inst), | 609 | .union_init => try self.airUnionInit(inst), |
| 609 | .prefetch => try self.airPrefetch(inst), | 610 | .prefetch => try self.airPrefetch(inst), |
| ... | @@ -2213,6 +2214,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2213,6 +2214,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 2213 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 2214 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2214 | } | 2215 | } |
| 2215 | 2216 | ||
| 2217 | fn airReduce(self: *Self, inst: Air.Inst.Index) !void { | ||
| 2218 | const reduce = self.air.instructions.items(.data)[inst].reduce; | ||
| 2219 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for riscv64", .{}); | ||
| 2220 | return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); | ||
| 2221 | } | ||
| 2222 | |||
| 2216 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { | 2223 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 2217 | const vector_ty = self.air.typeOfIndex(inst); | 2224 | const vector_ty = self.air.typeOfIndex(inst); |
| 2218 | const len = vector_ty.vectorLen(); | 2225 | const len = vector_ty.vectorLen(); |
src/arch/wasm/CodeGen.zig+11-2| ... | @@ -1263,6 +1263,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1263,6 +1263,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1263 | .ret_load => self.airRetLoad(inst), | 1263 | .ret_load => self.airRetLoad(inst), |
| 1264 | .splat => self.airSplat(inst), | 1264 | .splat => self.airSplat(inst), |
| 1265 | .shuffle => self.airShuffle(inst), | 1265 | .shuffle => self.airShuffle(inst), |
| 1266 | .reduce => self.airReduce(inst), | ||
| 1266 | .aggregate_init => self.airAggregateInit(inst), | 1267 | .aggregate_init => self.airAggregateInit(inst), |
| 1267 | .union_init => self.airUnionInit(inst), | 1268 | .union_init => self.airUnionInit(inst), |
| 1268 | .prefetch => self.airPrefetch(inst), | 1269 | .prefetch => self.airPrefetch(inst), |
| ... | @@ -2988,7 +2989,6 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2988,7 +2989,6 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2988 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2989 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2989 | const operand = try self.resolveInst(ty_op.operand); | 2990 | const operand = try self.resolveInst(ty_op.operand); |
| 2990 | 2991 | ||
| 2991 | _ = ty_op; | ||
| 2992 | _ = operand; | 2992 | _ = operand; |
| 2993 | return self.fail("TODO: Implement wasm airSplat", .{}); | 2993 | return self.fail("TODO: Implement wasm airSplat", .{}); |
| 2994 | } | 2994 | } |
| ... | @@ -2999,11 +2999,20 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2999,11 +2999,20 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2999 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2999 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3000 | const operand = try self.resolveInst(ty_op.operand); | 3000 | const operand = try self.resolveInst(ty_op.operand); |
| 3001 | 3001 | ||
| 3002 | _ = ty_op; | ||
| 3003 | _ = operand; | 3002 | _ = operand; |
| 3004 | return self.fail("TODO: Implement wasm airShuffle", .{}); | 3003 | return self.fail("TODO: Implement wasm airShuffle", .{}); |
| 3005 | } | 3004 | } |
| 3006 | 3005 | ||
| 3006 | fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ||
| 3007 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; | ||
| 3008 | |||
| 3009 | const reduce = self.air.instructions.items(.data)[inst].reduce; | ||
| 3010 | const operand = try self.resolveInst(reduce.operand); | ||
| 3011 | |||
| 3012 | _ = operand; | ||
| 3013 | return self.fail("TODO: Implement wasm airReduce", .{}); | ||
| 3014 | } | ||
| 3015 | |||
| 3007 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3016 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3008 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; | 3017 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 3009 | 3018 |
src/arch/x86_64/CodeGen.zig+7| ... | @@ -721,6 +721,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -721,6 +721,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 721 | .error_name => try self.airErrorName(inst), | 721 | .error_name => try self.airErrorName(inst), |
| 722 | .splat => try self.airSplat(inst), | 722 | .splat => try self.airSplat(inst), |
| 723 | .shuffle => try self.airShuffle(inst), | 723 | .shuffle => try self.airShuffle(inst), |
| 724 | .reduce => try self.airReduce(inst), | ||
| 724 | .aggregate_init => try self.airAggregateInit(inst), | 725 | .aggregate_init => try self.airAggregateInit(inst), |
| 725 | .union_init => try self.airUnionInit(inst), | 726 | .union_init => try self.airUnionInit(inst), |
| 726 | .prefetch => try self.airPrefetch(inst), | 727 | .prefetch => try self.airPrefetch(inst), |
| ... | @@ -5567,6 +5568,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5567,6 +5568,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 5567 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 5568 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 5568 | } | 5569 | } |
| 5569 | 5570 | ||
| 5571 | fn airReduce(self: *Self, inst: Air.Inst.Index) !void { | ||
| 5572 | const reduce = self.air.instructions.items(.data)[inst].reduce; | ||
| 5573 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for x86_64", .{}); | ||
| 5574 | return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); | ||
| 5575 | } | ||
| 5576 | |||
| 5570 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { | 5577 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 5571 | const vector_ty = self.air.typeOfIndex(inst); | 5578 | const vector_ty = self.air.typeOfIndex(inst); |
| 5572 | const len = vector_ty.vectorLen(); | 5579 | const len = vector_ty.vectorLen(); |
src/codegen/c.zig+16| ... | @@ -1731,6 +1731,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1731,6 +1731,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1731 | .error_name => try airErrorName(f, inst), | 1731 | .error_name => try airErrorName(f, inst), |
| 1732 | .splat => try airSplat(f, inst), | 1732 | .splat => try airSplat(f, inst), |
| 1733 | .shuffle => try airShuffle(f, inst), | 1733 | .shuffle => try airShuffle(f, inst), |
| 1734 | .reduce => try airReduce(f, inst), | ||
| 1734 | .aggregate_init => try airAggregateInit(f, inst), | 1735 | .aggregate_init => try airAggregateInit(f, inst), |
| 1735 | .union_init => try airUnionInit(f, inst), | 1736 | .union_init => try airUnionInit(f, inst), |
| 1736 | .prefetch => try airPrefetch(f, inst), | 1737 | .prefetch => try airPrefetch(f, inst), |
| ... | @@ -3625,6 +3626,21 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3625,6 +3626,21 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3625 | return f.fail("TODO: C backend: implement airShuffle", .{}); | 3626 | return f.fail("TODO: C backend: implement airShuffle", .{}); |
| 3626 | } | 3627 | } |
| 3627 | 3628 | ||
| 3629 | fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 3630 | if (f.liveness.isUnused(inst)) return CValue.none; | ||
| 3631 | |||
| 3632 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 3633 | const reduce = f.air.instructions.items(.data)[inst].reduce; | ||
| 3634 | const operand = try f.resolveInst(reduce.operand); | ||
| 3635 | const writer = f.object.writer(); | ||
| 3636 | const local = try f.allocLocal(inst_ty, .Const); | ||
| 3637 | try writer.writeAll(" = "); | ||
| 3638 | |||
| 3639 | _ = operand; | ||
| 3640 | _ = local; | ||
| 3641 | return f.fail("TODO: C backend: implement airReduce", .{}); | ||
| 3642 | } | ||
| 3643 | |||
| 3628 | fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | 3644 | fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3629 | if (f.liveness.isUnused(inst)) return CValue.none; | 3645 | if (f.liveness.isUnused(inst)) return CValue.none; |
| 3630 | 3646 |
src/codegen/llvm.zig+45| ... | @@ -3426,6 +3426,7 @@ pub const FuncGen = struct { | ... | @@ -3426,6 +3426,7 @@ pub const FuncGen = struct { |
| 3426 | .error_name => try self.airErrorName(inst), | 3426 | .error_name => try self.airErrorName(inst), |
| 3427 | .splat => try self.airSplat(inst), | 3427 | .splat => try self.airSplat(inst), |
| 3428 | .shuffle => try self.airShuffle(inst), | 3428 | .shuffle => try self.airShuffle(inst), |
| 3429 | .reduce => try self.airReduce(inst), | ||
| 3429 | .aggregate_init => try self.airAggregateInit(inst), | 3430 | .aggregate_init => try self.airAggregateInit(inst), |
| 3430 | .union_init => try self.airUnionInit(inst), | 3431 | .union_init => try self.airUnionInit(inst), |
| 3431 | .prefetch => try self.airPrefetch(inst), | 3432 | .prefetch => try self.airPrefetch(inst), |
| ... | @@ -6281,6 +6282,50 @@ pub const FuncGen = struct { | ... | @@ -6281,6 +6282,50 @@ pub const FuncGen = struct { |
| 6281 | return self.builder.buildShuffleVector(a, b, llvm_mask_value, ""); | 6282 | return self.builder.buildShuffleVector(a, b, llvm_mask_value, ""); |
| 6282 | } | 6283 | } |
| 6283 | 6284 | ||
| 6285 | fn airReduce(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | ||
| 6286 | if (self.liveness.isUnused(inst)) return null; | ||
| 6287 | |||
| 6288 | const reduce = self.air.instructions.items(.data)[inst].reduce; | ||
| 6289 | const operand = try self.resolveInst(reduce.operand); | ||
| 6290 | const scalar_ty = self.air.typeOfIndex(inst); | ||
| 6291 | |||
| 6292 | // TODO handle the fast math setting | ||
| 6293 | |||
| 6294 | switch (reduce.operation) { | ||
| 6295 | .And => return self.builder.buildAndReduce(operand), | ||
| 6296 | .Or => return self.builder.buildOrReduce(operand), | ||
| 6297 | .Xor => return self.builder.buildXorReduce(operand), | ||
| 6298 | .Min => switch (scalar_ty.zigTypeTag()) { | ||
| 6299 | .Int => return self.builder.buildIntMinReduce(operand, scalar_ty.isSignedInt()), | ||
| 6300 | .Float => return self.builder.buildFPMinReduce(operand), | ||
| 6301 | else => unreachable, | ||
| 6302 | }, | ||
| 6303 | .Max => switch (scalar_ty.zigTypeTag()) { | ||
| 6304 | .Int => return self.builder.buildIntMaxReduce(operand, scalar_ty.isSignedInt()), | ||
| 6305 | .Float => return self.builder.buildFPMaxReduce(operand), | ||
| 6306 | else => unreachable, | ||
| 6307 | }, | ||
| 6308 | .Add => switch (scalar_ty.zigTypeTag()) { | ||
| 6309 | .Int => return self.builder.buildAddReduce(operand), | ||
| 6310 | .Float => { | ||
| 6311 | const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); | ||
| 6312 | const neutral_value = scalar_llvm_ty.constReal(-0.0); | ||
| 6313 | return self.builder.buildFPAddReduce(neutral_value, operand); | ||
| 6314 | }, | ||
| 6315 | else => unreachable, | ||
| 6316 | }, | ||
| 6317 | .Mul => switch (scalar_ty.zigTypeTag()) { | ||
| 6318 | .Int => return self.builder.buildMulReduce(operand), | ||
| 6319 | .Float => { | ||
| 6320 | const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); | ||
| 6321 | const neutral_value = scalar_llvm_ty.constReal(1.0); | ||
| 6322 | return self.builder.buildFPMulReduce(neutral_value, operand); | ||
| 6323 | }, | ||
| 6324 | else => unreachable, | ||
| 6325 | }, | ||
| 6326 | } | ||
| 6327 | } | ||
| 6328 | |||
| 6284 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 6329 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 6285 | if (self.liveness.isUnused(inst)) return null; | 6330 | if (self.liveness.isUnused(inst)) return null; |
| 6286 | 6331 |
src/codegen/llvm/bindings.zig+33| ... | @@ -853,6 +853,39 @@ pub const Builder = opaque { | ... | @@ -853,6 +853,39 @@ pub const Builder = opaque { |
| 853 | 853 | ||
| 854 | pub const buildShuffleVector = LLVMBuildShuffleVector; | 854 | pub const buildShuffleVector = LLVMBuildShuffleVector; |
| 855 | extern fn LLVMBuildShuffleVector(*const Builder, V1: *const Value, V2: *const Value, Mask: *const Value, Name: [*:0]const u8) *const Value; | 855 | extern fn LLVMBuildShuffleVector(*const Builder, V1: *const Value, V2: *const Value, Mask: *const Value, Name: [*:0]const u8) *const Value; |
| 856 | |||
| 857 | pub const buildAndReduce = ZigLLVMBuildAndReduce; | ||
| 858 | extern fn ZigLLVMBuildAndReduce(B: *const Builder, Val: *const Value) *const Value; | ||
| 859 | |||
| 860 | pub const buildOrReduce = ZigLLVMBuildOrReduce; | ||
| 861 | extern fn ZigLLVMBuildOrReduce(B: *const Builder, Val: *const Value) *const Value; | ||
| 862 | |||
| 863 | pub const buildXorReduce = ZigLLVMBuildXorReduce; | ||
| 864 | extern fn ZigLLVMBuildXorReduce(B: *const Builder, Val: *const Value) *const Value; | ||
| 865 | |||
| 866 | pub const buildIntMaxReduce = ZigLLVMBuildIntMaxReduce; | ||
| 867 | extern fn ZigLLVMBuildIntMaxReduce(B: *const Builder, Val: *const Value, is_signed: bool) *const Value; | ||
| 868 | |||
| 869 | pub const buildIntMinReduce = ZigLLVMBuildIntMinReduce; | ||
| 870 | extern fn ZigLLVMBuildIntMinReduce(B: *const Builder, Val: *const Value, is_signed: bool) *const Value; | ||
| 871 | |||
| 872 | pub const buildFPMaxReduce = ZigLLVMBuildFPMaxReduce; | ||
| 873 | extern fn ZigLLVMBuildFPMaxReduce(B: *const Builder, Val: *const Value) *const Value; | ||
| 874 | |||
| 875 | pub const buildFPMinReduce = ZigLLVMBuildFPMinReduce; | ||
| 876 | extern fn ZigLLVMBuildFPMinReduce(B: *const Builder, Val: *const Value) *const Value; | ||
| 877 | |||
| 878 | pub const buildAddReduce = ZigLLVMBuildAddReduce; | ||
| 879 | extern fn ZigLLVMBuildAddReduce(B: *const Builder, Val: *const Value) *const Value; | ||
| 880 | |||
| 881 | pub const buildMulReduce = ZigLLVMBuildMulReduce; | ||
| 882 | extern fn ZigLLVMBuildMulReduce(B: *const Builder, Val: *const Value) *const Value; | ||
| 883 | |||
| 884 | pub const buildFPAddReduce = ZigLLVMBuildFPAddReduce; | ||
| 885 | extern fn ZigLLVMBuildFPAddReduce(B: *const Builder, Acc: *const Value, Val: *const Value) *const Value; | ||
| 886 | |||
| 887 | pub const buildFPMulReduce = ZigLLVMBuildFPMulReduce; | ||
| 888 | extern fn ZigLLVMBuildFPMulReduce(B: *const Builder, Acc: *const Value, Val: *const Value) *const Value; | ||
| 856 | }; | 889 | }; |
| 857 | 890 | ||
| 858 | pub const MDString = opaque { | 891 | pub const MDString = opaque { |
src/print_air.zig+8| ... | @@ -265,6 +265,7 @@ const Writer = struct { | ... | @@ -265,6 +265,7 @@ const Writer = struct { |
| 265 | .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), | 265 | .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), |
| 266 | .mul_add => try w.writeMulAdd(s, inst), | 266 | .mul_add => try w.writeMulAdd(s, inst), |
| 267 | .shuffle => try w.writeShuffle(s, inst), | 267 | .shuffle => try w.writeShuffle(s, inst), |
| 268 | .reduce => try w.writeReduce(s, inst), | ||
| 268 | 269 | ||
| 269 | .add_with_overflow, | 270 | .add_with_overflow, |
| 270 | .sub_with_overflow, | 271 | .sub_with_overflow, |
| ... | @@ -392,6 +393,13 @@ const Writer = struct { | ... | @@ -392,6 +393,13 @@ const Writer = struct { |
| 392 | try s.print(", mask {d}, len {d}", .{ extra.mask, extra.mask_len }); | 393 | try s.print(", mask {d}, len {d}", .{ extra.mask, extra.mask_len }); |
| 393 | } | 394 | } |
| 394 | 395 | ||
| 396 | fn writeReduce(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | ||
| 397 | const reduce = w.air.instructions.items(.data)[inst].reduce; | ||
| 398 | |||
| 399 | try w.writeOperand(s, inst, 0, reduce.operand); | ||
| 400 | try s.print(", {s}", .{@tagName(reduce.operation)}); | ||
| 401 | } | ||
| 402 | |||
| 395 | fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | 403 | fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 396 | const atomic_order = w.air.instructions.items(.data)[inst].fence; | 404 | const atomic_order = w.air.instructions.items(.data)[inst].fence; |
| 397 | 405 |
src/value.zig+25-5| ... | @@ -1841,6 +1841,8 @@ pub const Value = extern union { | ... | @@ -1841,6 +1841,8 @@ pub const Value = extern union { |
| 1841 | return orderAgainstZero(lhs).compare(op); | 1841 | return orderAgainstZero(lhs).compare(op); |
| 1842 | } | 1842 | } |
| 1843 | 1843 | ||
| 1844 | /// This function is used by hash maps and so treats floating-point NaNs as equal | ||
| 1845 | /// to each other, and not equal to other floating-point values. | ||
| 1844 | pub fn eql(a: Value, b: Value, ty: Type) bool { | 1846 | pub fn eql(a: Value, b: Value, ty: Type) bool { |
| 1845 | const a_tag = a.tag(); | 1847 | const a_tag = a.tag(); |
| 1846 | const b_tag = b.tag(); | 1848 | const b_tag = b.tag(); |
| ... | @@ -2006,10 +2008,20 @@ pub const Value = extern union { | ... | @@ -2006,10 +2008,20 @@ pub const Value = extern union { |
| 2006 | // end up here and the values are equal if the type has zero fields. | 2008 | // end up here and the values are equal if the type has zero fields. |
| 2007 | return ty.structFieldCount() != 0; | 2009 | return ty.structFieldCount() != 0; |
| 2008 | }, | 2010 | }, |
| 2011 | .Float => { | ||
| 2012 | const a_nan = a.isNan(); | ||
| 2013 | const b_nan = b.isNan(); | ||
| 2014 | if (a_nan or b_nan) { | ||
| 2015 | return a_nan and b_nan; | ||
| 2016 | } | ||
| 2017 | return order(a, b).compare(.eq); | ||
| 2018 | }, | ||
| 2009 | else => return order(a, b).compare(.eq), | 2019 | else => return order(a, b).compare(.eq), |
| 2010 | } | 2020 | } |
| 2011 | } | 2021 | } |
| 2012 | 2022 | ||
| 2023 | /// This function is used by hash maps and so treats floating-point NaNs as equal | ||
| 2024 | /// to each other, and not equal to other floating-point values. | ||
| 2013 | pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void { | 2025 | pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void { |
| 2014 | const zig_ty_tag = ty.zigTypeTag(); | 2026 | const zig_ty_tag = ty.zigTypeTag(); |
| 2015 | std.hash.autoHash(hasher, zig_ty_tag); | 2027 | std.hash.autoHash(hasher, zig_ty_tag); |
| ... | @@ -2030,10 +2042,18 @@ pub const Value = extern union { | ... | @@ -2030,10 +2042,18 @@ pub const Value = extern union { |
| 2030 | return val.toType(&buf).hashWithHasher(hasher); | 2042 | return val.toType(&buf).hashWithHasher(hasher); |
| 2031 | }, | 2043 | }, |
| 2032 | .Float, .ComptimeFloat => { | 2044 | .Float, .ComptimeFloat => { |
| 2033 | // TODO double check the lang spec. should we to bitwise hashing here, | 2045 | // Normalize the float here because this hash must match eql semantics. |
| 2034 | // or a hash that normalizes the float value? | 2046 | // These functions are used for hash maps so we want NaN to equal itself, |
| 2047 | // and -0.0 to equal +0.0. | ||
| 2035 | const float = val.toFloat(f128); | 2048 | const float = val.toFloat(f128); |
| 2036 | std.hash.autoHash(hasher, @bitCast(u128, float)); | 2049 | if (std.math.isNan(float)) { |
| 2050 | std.hash.autoHash(hasher, std.math.nan_u128); | ||
| 2051 | } else if (float == 0.0) { | ||
| 2052 | var normalized_zero: f128 = 0.0; | ||
| 2053 | std.hash.autoHash(hasher, @bitCast(u128, normalized_zero)); | ||
| 2054 | } else { | ||
| 2055 | std.hash.autoHash(hasher, @bitCast(u128, float)); | ||
| 2056 | } | ||
| 2037 | }, | 2057 | }, |
| 2038 | .Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) { | 2058 | .Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) { |
| 2039 | .slice => { | 2059 | .slice => { |
| ... | @@ -2948,7 +2968,7 @@ pub const Value = extern union { | ... | @@ -2948,7 +2968,7 @@ pub const Value = extern union { |
| 2948 | } | 2968 | } |
| 2949 | 2969 | ||
| 2950 | /// Supports both floats and ints; handles undefined. | 2970 | /// Supports both floats and ints; handles undefined. |
| 2951 | pub fn numberMax(lhs: Value, rhs: Value) !Value { | 2971 | pub fn numberMax(lhs: Value, rhs: Value) Value { |
| 2952 | if (lhs.isUndef() or rhs.isUndef()) return undef; | 2972 | if (lhs.isUndef() or rhs.isUndef()) return undef; |
| 2953 | if (lhs.isNan()) return rhs; | 2973 | if (lhs.isNan()) return rhs; |
| 2954 | if (rhs.isNan()) return lhs; | 2974 | if (rhs.isNan()) return lhs; |
| ... | @@ -2960,7 +2980,7 @@ pub const Value = extern union { | ... | @@ -2960,7 +2980,7 @@ pub const Value = extern union { |
| 2960 | } | 2980 | } |
| 2961 | 2981 | ||
| 2962 | /// Supports both floats and ints; handles undefined. | 2982 | /// Supports both floats and ints; handles undefined. |
| 2963 | pub fn numberMin(lhs: Value, rhs: Value) !Value { | 2983 | pub fn numberMin(lhs: Value, rhs: Value) Value { |
| 2964 | if (lhs.isUndef() or rhs.isUndef()) return undef; | 2984 | if (lhs.isUndef() or rhs.isUndef()) return undef; |
| 2965 | if (lhs.isNan()) return rhs; | 2985 | if (lhs.isNan()) return rhs; |
| 2966 | if (rhs.isNan()) return lhs; | 2986 | if (rhs.isNan()) return lhs; |
test/behavior/vector.zig+90-83| ... | @@ -520,15 +520,20 @@ test "vector shift operators" { | ... | @@ -520,15 +520,20 @@ test "vector shift operators" { |
| 520 | } | 520 | } |
| 521 | 521 | ||
| 522 | test "vector reduce operation" { | 522 | test "vector reduce operation" { |
| 523 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 523 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 524 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 525 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 526 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 527 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 528 | |||
| 524 | const S = struct { | 529 | const S = struct { |
| 525 | fn doTheTestReduce(comptime op: std.builtin.ReduceOp, x: anytype, expected: anytype) !void { | 530 | fn testReduce(comptime op: std.builtin.ReduceOp, x: anytype, expected: anytype) !void { |
| 526 | const N = @typeInfo(@TypeOf(x)).Array.len; | 531 | const N = @typeInfo(@TypeOf(x)).Array.len; |
| 527 | const TX = @typeInfo(@TypeOf(x)).Array.child; | 532 | const TX = @typeInfo(@TypeOf(x)).Array.child; |
| 528 | 533 | ||
| 529 | var r = @reduce(op, @as(Vector(N, TX), x)); | 534 | var r = @reduce(op, @as(@Vector(N, TX), x)); |
| 530 | switch (@typeInfo(TX)) { | 535 | switch (@typeInfo(TX)) { |
| 531 | .Int, .Bool => try expectEqual(expected, r), | 536 | .Int, .Bool => try expect(expected == r), |
| 532 | .Float => { | 537 | .Float => { |
| 533 | const expected_nan = math.isNan(expected); | 538 | const expected_nan = math.isNan(expected); |
| 534 | const got_nan = math.isNan(r); | 539 | const got_nan = math.isNan(r); |
| ... | @@ -537,117 +542,119 @@ test "vector reduce operation" { | ... | @@ -537,117 +542,119 @@ test "vector reduce operation" { |
| 537 | // Do this check explicitly as two NaN values are never | 542 | // Do this check explicitly as two NaN values are never |
| 538 | // equal. | 543 | // equal. |
| 539 | } else { | 544 | } else { |
| 540 | try expectApproxEqRel(expected, r, math.sqrt(math.epsilon(TX))); | 545 | const F = @TypeOf(expected); |
| 546 | const tolerance = @sqrt(math.epsilon(TX)); | ||
| 547 | try expect(std.math.approxEqRel(F, expected, r, tolerance)); | ||
| 541 | } | 548 | } |
| 542 | }, | 549 | }, |
| 543 | else => unreachable, | 550 | else => unreachable, |
| 544 | } | 551 | } |
| 545 | } | 552 | } |
| 546 | fn doTheTest() !void { | 553 | fn doTheTest() !void { |
| 547 | try doTheTestReduce(.Add, [4]i16{ -9, -99, -999, -9999 }, @as(i32, -11106)); | 554 | try testReduce(.Add, [4]i16{ -9, -99, -999, -9999 }, @as(i32, -11106)); |
| 548 | try doTheTestReduce(.Add, [4]u16{ 9, 99, 999, 9999 }, @as(u32, 11106)); | 555 | try testReduce(.Add, [4]u16{ 9, 99, 999, 9999 }, @as(u32, 11106)); |
| 549 | try doTheTestReduce(.Add, [4]i32{ -9, -99, -999, -9999 }, @as(i32, -11106)); | 556 | try testReduce(.Add, [4]i32{ -9, -99, -999, -9999 }, @as(i32, -11106)); |
| 550 | try doTheTestReduce(.Add, [4]u32{ 9, 99, 999, 9999 }, @as(u32, 11106)); | 557 | try testReduce(.Add, [4]u32{ 9, 99, 999, 9999 }, @as(u32, 11106)); |
| 551 | try doTheTestReduce(.Add, [4]i64{ -9, -99, -999, -9999 }, @as(i64, -11106)); | 558 | try testReduce(.Add, [4]i64{ -9, -99, -999, -9999 }, @as(i64, -11106)); |
| 552 | try doTheTestReduce(.Add, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 11106)); | 559 | try testReduce(.Add, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 11106)); |
| 553 | try doTheTestReduce(.Add, [4]i128{ -9, -99, -999, -9999 }, @as(i128, -11106)); | 560 | try testReduce(.Add, [4]i128{ -9, -99, -999, -9999 }, @as(i128, -11106)); |
| 554 | try doTheTestReduce(.Add, [4]u128{ 9, 99, 999, 9999 }, @as(u128, 11106)); | 561 | try testReduce(.Add, [4]u128{ 9, 99, 999, 9999 }, @as(u128, 11106)); |
| 555 | try doTheTestReduce(.Add, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 42.9)); | 562 | try testReduce(.Add, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 42.9)); |
| 556 | try doTheTestReduce(.Add, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 42.9)); | 563 | try testReduce(.Add, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 42.9)); |
| 557 | try doTheTestReduce(.Add, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 42.9)); | 564 | try testReduce(.Add, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 42.9)); |
| 558 | 565 | ||
| 559 | try doTheTestReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); | 566 | try testReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); |
| 560 | try doTheTestReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0)); | 567 | try testReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0)); |
| 561 | try doTheTestReduce(.And, [4]u16{ 0xffff, 0xff55, 0xaaff, 0x1010 }, @as(u16, 0x10)); | 568 | try testReduce(.And, [4]u16{ 0xffff, 0xff55, 0xaaff, 0x1010 }, @as(u16, 0x10)); |
| 562 | try doTheTestReduce(.And, [4]u32{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u32, 0x1010)); | 569 | try testReduce(.And, [4]u32{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u32, 0x1010)); |
| 563 | try doTheTestReduce(.And, [4]u64{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u64, 0x1010)); | 570 | try testReduce(.And, [4]u64{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u64, 0x1010)); |
| 564 | 571 | ||
| 565 | try doTheTestReduce(.Min, [4]i16{ -1, 2, 3, 4 }, @as(i16, -1)); | 572 | try testReduce(.Min, [4]i16{ -1, 2, 3, 4 }, @as(i16, -1)); |
| 566 | try doTheTestReduce(.Min, [4]u16{ 1, 2, 3, 4 }, @as(u16, 1)); | 573 | try testReduce(.Min, [4]u16{ 1, 2, 3, 4 }, @as(u16, 1)); |
| 567 | try doTheTestReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386)); | 574 | try testReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386)); |
| 568 | try doTheTestReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9)); | 575 | try testReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9)); |
| 569 | 576 | ||
| 570 | // LLVM 11 ERROR: Cannot select type | 577 | // LLVM 11 ERROR: Cannot select type |
| 571 | // https://github.com/ziglang/zig/issues/7138 | 578 | // https://github.com/ziglang/zig/issues/7138 |
| 572 | if (builtin.target.cpu.arch != .aarch64) { | 579 | if (builtin.target.cpu.arch != .aarch64) { |
| 573 | try doTheTestReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386)); | 580 | try testReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386)); |
| 574 | try doTheTestReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9)); | 581 | try testReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9)); |
| 575 | } | 582 | } |
| 576 | 583 | ||
| 577 | try doTheTestReduce(.Min, [4]i128{ 1234567, -386, 0, 3 }, @as(i128, -386)); | 584 | try testReduce(.Min, [4]i128{ 1234567, -386, 0, 3 }, @as(i128, -386)); |
| 578 | try doTheTestReduce(.Min, [4]u128{ 99, 9999, 9, 99999 }, @as(u128, 9)); | 585 | try testReduce(.Min, [4]u128{ 99, 9999, 9, 99999 }, @as(u128, 9)); |
| 579 | try doTheTestReduce(.Min, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, -100.0)); | 586 | try testReduce(.Min, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, -100.0)); |
| 580 | try doTheTestReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0)); | 587 | try testReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0)); |
| 581 | try doTheTestReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0)); | 588 | try testReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0)); |
| 582 | 589 | ||
| 583 | try doTheTestReduce(.Max, [4]i16{ -1, 2, 3, 4 }, @as(i16, 4)); | 590 | try testReduce(.Max, [4]i16{ -1, 2, 3, 4 }, @as(i16, 4)); |
| 584 | try doTheTestReduce(.Max, [4]u16{ 1, 2, 3, 4 }, @as(u16, 4)); | 591 | try testReduce(.Max, [4]u16{ 1, 2, 3, 4 }, @as(u16, 4)); |
| 585 | try doTheTestReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567)); | 592 | try testReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567)); |
| 586 | try doTheTestReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999)); | 593 | try testReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999)); |
| 587 | 594 | ||
| 588 | // LLVM 11 ERROR: Cannot select type | 595 | // LLVM 11 ERROR: Cannot select type |
| 589 | // https://github.com/ziglang/zig/issues/7138 | 596 | // https://github.com/ziglang/zig/issues/7138 |
| 590 | if (builtin.target.cpu.arch != .aarch64) { | 597 | if (builtin.target.cpu.arch != .aarch64) { |
| 591 | try doTheTestReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567)); | 598 | try testReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567)); |
| 592 | try doTheTestReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999)); | 599 | try testReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999)); |
| 593 | } | 600 | } |
| 594 | 601 | ||
| 595 | try doTheTestReduce(.Max, [4]i128{ 1234567, -386, 0, 3 }, @as(i128, 1234567)); | 602 | try testReduce(.Max, [4]i128{ 1234567, -386, 0, 3 }, @as(i128, 1234567)); |
| 596 | try doTheTestReduce(.Max, [4]u128{ 99, 9999, 9, 99999 }, @as(u128, 99999)); | 603 | try testReduce(.Max, [4]u128{ 99, 9999, 9, 99999 }, @as(u128, 99999)); |
| 597 | try doTheTestReduce(.Max, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, 10.0e9)); | 604 | try testReduce(.Max, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, 10.0e9)); |
| 598 | try doTheTestReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9)); | 605 | try testReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9)); |
| 599 | try doTheTestReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9)); | 606 | try testReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9)); |
| 600 | 607 | ||
| 601 | try doTheTestReduce(.Mul, [4]i16{ -1, 2, 3, 4 }, @as(i16, -24)); | 608 | try testReduce(.Mul, [4]i16{ -1, 2, 3, 4 }, @as(i16, -24)); |
| 602 | try doTheTestReduce(.Mul, [4]u16{ 1, 2, 3, 4 }, @as(u16, 24)); | 609 | try testReduce(.Mul, [4]u16{ 1, 2, 3, 4 }, @as(u16, 24)); |
| 603 | try doTheTestReduce(.Mul, [4]i32{ -9, -99, -999, 999 }, @as(i32, -889218891)); | 610 | try testReduce(.Mul, [4]i32{ -9, -99, -999, 999 }, @as(i32, -889218891)); |
| 604 | try doTheTestReduce(.Mul, [4]u32{ 1, 2, 3, 4 }, @as(u32, 24)); | 611 | try testReduce(.Mul, [4]u32{ 1, 2, 3, 4 }, @as(u32, 24)); |
| 605 | try doTheTestReduce(.Mul, [4]i64{ 9, 99, 999, 9999 }, @as(i64, 8900199891)); | 612 | try testReduce(.Mul, [4]i64{ 9, 99, 999, 9999 }, @as(i64, 8900199891)); |
| 606 | try doTheTestReduce(.Mul, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 8900199891)); | 613 | try testReduce(.Mul, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 8900199891)); |
| 607 | try doTheTestReduce(.Mul, [4]i128{ -9, -99, -999, 9999 }, @as(i128, -8900199891)); | 614 | try testReduce(.Mul, [4]i128{ -9, -99, -999, 9999 }, @as(i128, -8900199891)); |
| 608 | try doTheTestReduce(.Mul, [4]u128{ 9, 99, 999, 9999 }, @as(u128, 8900199891)); | 615 | try testReduce(.Mul, [4]u128{ 9, 99, 999, 9999 }, @as(u128, 8900199891)); |
| 609 | try doTheTestReduce(.Mul, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 58430.7)); | 616 | try testReduce(.Mul, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 58430.7)); |
| 610 | try doTheTestReduce(.Mul, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 58430.7)); | 617 | try testReduce(.Mul, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 58430.7)); |
| 611 | try doTheTestReduce(.Mul, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 58430.7)); | 618 | try testReduce(.Mul, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 58430.7)); |
| 612 | 619 | ||
| 613 | try doTheTestReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true)); | 620 | try testReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true)); |
| 614 | try doTheTestReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1)); | 621 | try testReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1)); |
| 615 | try doTheTestReduce(.Or, [4]u16{ 0xff00, 0xff00, 0xf0, 0xf }, ~@as(u16, 0)); | 622 | try testReduce(.Or, [4]u16{ 0xff00, 0xff00, 0xf0, 0xf }, ~@as(u16, 0)); |
| 616 | try doTheTestReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0)); | 623 | try testReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0)); |
| 617 | try doTheTestReduce(.Or, [4]u64{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u64, 0xffffffff)); | 624 | try testReduce(.Or, [4]u64{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u64, 0xffffffff)); |
| 618 | try doTheTestReduce(.Or, [4]u128{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u128, 0xffffffff)); | 625 | try testReduce(.Or, [4]u128{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u128, 0xffffffff)); |
| 619 | 626 | ||
| 620 | try doTheTestReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true)); | 627 | try testReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true)); |
| 621 | try doTheTestReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1)); | 628 | try testReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1)); |
| 622 | try doTheTestReduce(.Xor, [4]u16{ 0x0000, 0x3333, 0x8888, 0x4444 }, ~@as(u16, 0)); | 629 | try testReduce(.Xor, [4]u16{ 0x0000, 0x3333, 0x8888, 0x4444 }, ~@as(u16, 0)); |
| 623 | try doTheTestReduce(.Xor, [4]u32{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, ~@as(u32, 0)); | 630 | try testReduce(.Xor, [4]u32{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, ~@as(u32, 0)); |
| 624 | try doTheTestReduce(.Xor, [4]u64{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u64, 0xffffffff)); | 631 | try testReduce(.Xor, [4]u64{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u64, 0xffffffff)); |
| 625 | try doTheTestReduce(.Xor, [4]u128{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u128, 0xffffffff)); | 632 | try testReduce(.Xor, [4]u128{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u128, 0xffffffff)); |
| 626 | 633 | ||
| 627 | // Test the reduction on vectors containing NaNs. | 634 | // Test the reduction on vectors containing NaNs. |
| 628 | const f16_nan = math.nan(f16); | 635 | const f16_nan = math.nan(f16); |
| 629 | const f32_nan = math.nan(f32); | 636 | const f32_nan = math.nan(f32); |
| 630 | const f64_nan = math.nan(f64); | 637 | const f64_nan = math.nan(f64); |
| 631 | 638 | ||
| 632 | try doTheTestReduce(.Add, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | 639 | try testReduce(.Add, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); |
| 633 | try doTheTestReduce(.Add, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | 640 | try testReduce(.Add, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); |
| 634 | try doTheTestReduce(.Add, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | 641 | try testReduce(.Add, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); |
| 635 | 642 | ||
| 636 | // LLVM 11 ERROR: Cannot select type | 643 | // LLVM 11 ERROR: Cannot select type |
| 637 | // https://github.com/ziglang/zig/issues/7138 | 644 | // https://github.com/ziglang/zig/issues/7138 |
| 638 | if (false) { | 645 | if (false) { |
| 639 | try doTheTestReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | 646 | try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); |
| 640 | try doTheTestReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | 647 | try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); |
| 641 | try doTheTestReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | 648 | try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); |
| 642 | 649 | ||
| 643 | try doTheTestReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | 650 | try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); |
| 644 | try doTheTestReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | 651 | try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); |
| 645 | try doTheTestReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | 652 | try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); |
| 646 | } | 653 | } |
| 647 | 654 | ||
| 648 | try doTheTestReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | 655 | try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); |
| 649 | try doTheTestReduce(.Mul, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | 656 | try testReduce(.Mul, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); |
| 650 | try doTheTestReduce(.Mul, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | 657 | try testReduce(.Mul, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); |
| 651 | } | 658 | } |
| 652 | }; | 659 | }; |
| 653 | 660 |