| 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 | 530 | /// Given an integer operand, return the float with the closest mathematical meaning. |
| 531 | 531 | /// Uses the `ty_op` field. |
| 532 | 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 | 541 | /// Given an integer, bool, float, or pointer operand, return a vector with all elements |
| 534 | 542 | /// equal to the scalar value. |
| 535 | 543 | /// Uses the `ty_op` field. |
| ... | ... | @@ -695,6 +703,10 @@ pub const Inst = struct { |
| 695 | 703 | locality: u2, |
| 696 | 704 | cache: std.builtin.PrefetchOptions.Cache, |
| 697 | 705 | }, |
| 706 | reduce: struct { | |
| 707 | operand: Ref, | |
| 708 | operation: std.builtin.ReduceOp, | |
| 709 | }, | |
| 698 | 710 | |
| 699 | 711 | // Make sure we don't accidentally add a field to make this union |
| 700 | 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 | 1039 | return ptr_ty.elemType(); |
| 1028 | 1040 | }, |
| 1029 | 1041 | |
| 1042 | .reduce => return air.typeOf(datas[inst].reduce.operand).childType(), | |
| 1043 | ||
| 1030 | 1044 | .mul_add => return air.typeOf(datas[inst].pl_op.operand), |
| 1031 | 1045 | |
| 1032 | 1046 | .add_with_overflow, |
src/Liveness.zig+4| ... | ... | @@ -435,6 +435,10 @@ fn analyzeInst( |
| 435 | 435 | const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data; |
| 436 | 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 | 442 | .aggregate_init => { |
| 439 | 443 | const ty_pl = inst_datas[inst].ty_pl; |
| 440 | 444 | const aggregate_ty = a.air.getRefType(ty_pl.ty); |
src/Sema.zig+88-18| ... | ... | @@ -13973,17 +13973,27 @@ fn resolveExportOptions( |
| 13973 | 13973 | }; |
| 13974 | 13974 | } |
| 13975 | 13975 | |
| 13976 | fn resolveAtomicOrder( | |
| 13976 | fn resolveBuiltinEnum( | |
| 13977 | 13977 | sema: *Sema, |
| 13978 | 13978 | block: *Block, |
| 13979 | 13979 | src: LazySrcLoc, |
| 13980 | 13980 | zir_ref: Zir.Inst.Ref, |
| 13981 | ) CompileError!std.builtin.AtomicOrder { | |
| 13982 | const atomic_order_ty = try sema.getBuiltinType(block, src, "AtomicOrder"); | |
| 13981 | comptime name: []const u8, | |
| 13982 | ) CompileError!@field(std.builtin, name) { | |
| 13983 | const ty = try sema.getBuiltinType(block, src, name); | |
| 13983 | 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 | 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 | 13999 | fn resolveAtomicRmwOp( |
| ... | ... | @@ -13992,11 +14002,7 @@ fn resolveAtomicRmwOp( |
| 13992 | 14002 | src: LazySrcLoc, |
| 13993 | 14003 | zir_ref: Zir.Inst.Ref, |
| 13994 | 14004 | ) CompileError!std.builtin.AtomicRmwOp { |
| 13995 | const atomic_rmw_op_ty = try sema.getBuiltinType(block, src, "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); | |
| 14005 | return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicRmwOp"); | |
| 14000 | 14006 | } |
| 14001 | 14007 | |
| 14002 | 14008 | fn zirCmpxchg( |
| ... | ... | @@ -14118,8 +14124,72 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 14118 | 14124 | |
| 14119 | 14125 | fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14120 | 14126 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 14121 | const src = inst_data.src(); | |
| 14122 | return sema.fail(block, src, "TODO: Sema.zirReduce", .{}); | |
| 14127 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | |
| 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 | 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 | 14495 | .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), |
| 14426 | 14496 | .Or => try stored_val.bitwiseOr (operand_val, sema.arena), |
| 14427 | 14497 | .Xor => try stored_val.bitwiseXor (operand_val, sema.arena), |
| 14428 | .Max => try stored_val.numberMax (operand_val), | |
| 14429 | .Min => try stored_val.numberMin (operand_val), | |
| 14498 | .Max => stored_val.numberMax (operand_val), | |
| 14499 | .Min => stored_val.numberMin (operand_val), | |
| 14430 | 14500 | // zig fmt: on |
| 14431 | 14501 | }; |
| 14432 | 14502 | try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); |
| ... | ... | @@ -14760,7 +14830,7 @@ fn analyzeMinMax( |
| 14760 | 14830 | else => unreachable, |
| 14761 | 14831 | }; |
| 14762 | 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 | 14834 | return sema.addConstant(simd_op.result_ty, result_val); |
| 14765 | 14835 | }; |
| 14766 | 14836 | var lhs_buf: Value.ElemValueBuffer = undefined; |
| ... | ... | @@ -14769,7 +14839,7 @@ fn analyzeMinMax( |
| 14769 | 14839 | for (elems) |*elem, i| { |
| 14770 | 14840 | const lhs_elem_val = lhs_val.elemValueBuffer(i, &lhs_buf); |
| 14771 | 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 | 14844 | return sema.addConstant( |
| 14775 | 14845 | simd_op.result_ty, |
| ... | ... | @@ -19246,9 +19316,9 @@ fn cmpNumeric( |
| 19246 | 19316 | const rhs_ty_tag = rhs_ty.zigTypeTag(); |
| 19247 | 19317 | |
| 19248 | 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 | 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 | 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 | 640 | .error_name => try self.airErrorName(inst), |
| 641 | 641 | .splat => try self.airSplat(inst), |
| 642 | 642 | .shuffle => try self.airShuffle(inst), |
| 643 | .reduce => try self.airReduce(inst), | |
| 643 | 644 | .aggregate_init => try self.airAggregateInit(inst), |
| 644 | 645 | .union_init => try self.airUnionInit(inst), |
| 645 | 646 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -3727,6 +3728,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 3727 | 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 | 3737 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 3731 | 3738 | const vector_ty = self.air.typeOfIndex(inst); |
| 3732 | 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 | 637 | .error_name => try self.airErrorName(inst), |
| 638 | 638 | .splat => try self.airSplat(inst), |
| 639 | 639 | .shuffle => try self.airShuffle(inst), |
| 640 | .reduce => try self.airReduce(inst), | |
| 640 | 641 | .aggregate_init => try self.airAggregateInit(inst), |
| 641 | 642 | .union_init => try self.airUnionInit(inst), |
| 642 | 643 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -4204,6 +4205,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 4204 | 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 | 4214 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 4208 | 4215 | const vector_ty = self.air.typeOfIndex(inst); |
| 4209 | 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 | 604 | .error_name => try self.airErrorName(inst), |
| 605 | 605 | .splat => try self.airSplat(inst), |
| 606 | 606 | .shuffle => try self.airShuffle(inst), |
| 607 | .reduce => try self.airReduce(inst), | |
| 607 | 608 | .aggregate_init => try self.airAggregateInit(inst), |
| 608 | 609 | .union_init => try self.airUnionInit(inst), |
| 609 | 610 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -2213,6 +2214,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 2213 | 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 | 2223 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 2217 | 2224 | const vector_ty = self.air.typeOfIndex(inst); |
| 2218 | 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 | 1263 | .ret_load => self.airRetLoad(inst), |
| 1264 | 1264 | .splat => self.airSplat(inst), |
| 1265 | 1265 | .shuffle => self.airShuffle(inst), |
| 1266 | .reduce => self.airReduce(inst), | |
| 1266 | 1267 | .aggregate_init => self.airAggregateInit(inst), |
| 1267 | 1268 | .union_init => self.airUnionInit(inst), |
| 1268 | 1269 | .prefetch => self.airPrefetch(inst), |
| ... | ... | @@ -2988,7 +2989,6 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2988 | 2989 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2989 | 2990 | const operand = try self.resolveInst(ty_op.operand); |
| 2990 | 2991 | |
| 2991 | _ = ty_op; | |
| 2992 | 2992 | _ = operand; |
| 2993 | 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 | 2999 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3000 | 3000 | const operand = try self.resolveInst(ty_op.operand); |
| 3001 | 3001 | |
| 3002 | _ = ty_op; | |
| 3003 | 3002 | _ = operand; |
| 3004 | 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 | 3016 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3008 | 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 | 721 | .error_name => try self.airErrorName(inst), |
| 722 | 722 | .splat => try self.airSplat(inst), |
| 723 | 723 | .shuffle => try self.airShuffle(inst), |
| 724 | .reduce => try self.airReduce(inst), | |
| 724 | 725 | .aggregate_init => try self.airAggregateInit(inst), |
| 725 | 726 | .union_init => try self.airUnionInit(inst), |
| 726 | 727 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -5567,6 +5568,12 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 5567 | 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 | 5577 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 5571 | 5578 | const vector_ty = self.air.typeOfIndex(inst); |
| 5572 | 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 | 1731 | .error_name => try airErrorName(f, inst), |
| 1732 | 1732 | .splat => try airSplat(f, inst), |
| 1733 | 1733 | .shuffle => try airShuffle(f, inst), |
| 1734 | .reduce => try airReduce(f, inst), | |
| 1734 | 1735 | .aggregate_init => try airAggregateInit(f, inst), |
| 1735 | 1736 | .union_init => try airUnionInit(f, inst), |
| 1736 | 1737 | .prefetch => try airPrefetch(f, inst), |
| ... | ... | @@ -3625,6 +3626,21 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3625 | 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 | 3644 | fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3629 | 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 | 3426 | .error_name => try self.airErrorName(inst), |
| 3427 | 3427 | .splat => try self.airSplat(inst), |
| 3428 | 3428 | .shuffle => try self.airShuffle(inst), |
| 3429 | .reduce => try self.airReduce(inst), | |
| 3429 | 3430 | .aggregate_init => try self.airAggregateInit(inst), |
| 3430 | 3431 | .union_init => try self.airUnionInit(inst), |
| 3431 | 3432 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -6281,6 +6282,50 @@ pub const FuncGen = struct { |
| 6281 | 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 | 6329 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 6285 | 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 | 853 | |
| 854 | 854 | pub const buildShuffleVector = LLVMBuildShuffleVector; |
| 855 | 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 | 891 | pub const MDString = opaque { |
src/print_air.zig+8| ... | ... | @@ -265,6 +265,7 @@ const Writer = struct { |
| 265 | 265 | .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), |
| 266 | 266 | .mul_add => try w.writeMulAdd(s, inst), |
| 267 | 267 | .shuffle => try w.writeShuffle(s, inst), |
| 268 | .reduce => try w.writeReduce(s, inst), | |
| 268 | 269 | |
| 269 | 270 | .add_with_overflow, |
| 270 | 271 | .sub_with_overflow, |
| ... | ... | @@ -392,6 +393,13 @@ const Writer = struct { |
| 392 | 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 | 403 | fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 396 | 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 | 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 | 1846 | pub fn eql(a: Value, b: Value, ty: Type) bool { |
| 1845 | 1847 | const a_tag = a.tag(); |
| 1846 | 1848 | const b_tag = b.tag(); |
| ... | ... | @@ -2006,10 +2008,20 @@ pub const Value = extern union { |
| 2006 | 2008 | // end up here and the values are equal if the type has zero fields. |
| 2007 | 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 | 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 | 2025 | pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void { |
| 2014 | 2026 | const zig_ty_tag = ty.zigTypeTag(); |
| 2015 | 2027 | std.hash.autoHash(hasher, zig_ty_tag); |
| ... | ... | @@ -2030,10 +2042,18 @@ pub const Value = extern union { |
| 2030 | 2042 | return val.toType(&buf).hashWithHasher(hasher); |
| 2031 | 2043 | }, |
| 2032 | 2044 | .Float, .ComptimeFloat => { |
| 2033 | // TODO double check the lang spec. should we to bitwise hashing here, | |
| 2034 | // or a hash that normalizes the float value? | |
| 2045 | // Normalize the float here because this hash must match eql semantics. | |
| 2046 | // These functions are used for hash maps so we want NaN to equal itself, | |
| 2047 | // and -0.0 to equal +0.0. | |
| 2035 | 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 | 2058 | .Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) { |
| 2039 | 2059 | .slice => { |
| ... | ... | @@ -2948,7 +2968,7 @@ pub const Value = extern union { |
| 2948 | 2968 | } |
| 2949 | 2969 | |
| 2950 | 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 | 2972 | if (lhs.isUndef() or rhs.isUndef()) return undef; |
| 2953 | 2973 | if (lhs.isNan()) return rhs; |
| 2954 | 2974 | if (rhs.isNan()) return lhs; |
| ... | ... | @@ -2960,7 +2980,7 @@ pub const Value = extern union { |
| 2960 | 2980 | } |
| 2961 | 2981 | |
| 2962 | 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 | 2984 | if (lhs.isUndef() or rhs.isUndef()) return undef; |
| 2965 | 2985 | if (lhs.isNan()) return rhs; |
| 2966 | 2986 | if (rhs.isNan()) return lhs; |
test/behavior/vector.zig+90-83| ... | ... | @@ -520,15 +520,20 @@ test "vector shift operators" { |
| 520 | 520 | } |
| 521 | 521 | |
| 522 | 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 | 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 | 531 | const N = @typeInfo(@TypeOf(x)).Array.len; |
| 527 | 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 | 535 | switch (@typeInfo(TX)) { |
| 531 | .Int, .Bool => try expectEqual(expected, r), | |
| 536 | .Int, .Bool => try expect(expected == r), | |
| 532 | 537 | .Float => { |
| 533 | 538 | const expected_nan = math.isNan(expected); |
| 534 | 539 | const got_nan = math.isNan(r); |
| ... | ... | @@ -537,117 +542,119 @@ test "vector reduce operation" { |
| 537 | 542 | // Do this check explicitly as two NaN values are never |
| 538 | 543 | // equal. |
| 539 | 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 | 550 | else => unreachable, |
| 544 | 551 | } |
| 545 | 552 | } |
| 546 | 553 | fn doTheTest() !void { |
| 547 | try doTheTestReduce(.Add, [4]i16{ -9, -99, -999, -9999 }, @as(i32, -11106)); | |
| 548 | try doTheTestReduce(.Add, [4]u16{ 9, 99, 999, 9999 }, @as(u32, 11106)); | |
| 549 | try doTheTestReduce(.Add, [4]i32{ -9, -99, -999, -9999 }, @as(i32, -11106)); | |
| 550 | try doTheTestReduce(.Add, [4]u32{ 9, 99, 999, 9999 }, @as(u32, 11106)); | |
| 551 | try doTheTestReduce(.Add, [4]i64{ -9, -99, -999, -9999 }, @as(i64, -11106)); | |
| 552 | try doTheTestReduce(.Add, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 11106)); | |
| 553 | try doTheTestReduce(.Add, [4]i128{ -9, -99, -999, -9999 }, @as(i128, -11106)); | |
| 554 | try doTheTestReduce(.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)); | |
| 556 | try doTheTestReduce(.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)); | |
| 558 | ||
| 559 | try doTheTestReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); | |
| 560 | try doTheTestReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0)); | |
| 561 | try doTheTestReduce(.And, [4]u16{ 0xffff, 0xff55, 0xaaff, 0x1010 }, @as(u16, 0x10)); | |
| 562 | try doTheTestReduce(.And, [4]u32{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u32, 0x1010)); | |
| 563 | try doTheTestReduce(.And, [4]u64{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u64, 0x1010)); | |
| 564 | ||
| 565 | try doTheTestReduce(.Min, [4]i16{ -1, 2, 3, 4 }, @as(i16, -1)); | |
| 566 | try doTheTestReduce(.Min, [4]u16{ 1, 2, 3, 4 }, @as(u16, 1)); | |
| 567 | try doTheTestReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386)); | |
| 568 | try doTheTestReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9)); | |
| 554 | try testReduce(.Add, [4]i16{ -9, -99, -999, -9999 }, @as(i32, -11106)); | |
| 555 | try testReduce(.Add, [4]u16{ 9, 99, 999, 9999 }, @as(u32, 11106)); | |
| 556 | try testReduce(.Add, [4]i32{ -9, -99, -999, -9999 }, @as(i32, -11106)); | |
| 557 | try testReduce(.Add, [4]u32{ 9, 99, 999, 9999 }, @as(u32, 11106)); | |
| 558 | try testReduce(.Add, [4]i64{ -9, -99, -999, -9999 }, @as(i64, -11106)); | |
| 559 | try testReduce(.Add, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 11106)); | |
| 560 | try testReduce(.Add, [4]i128{ -9, -99, -999, -9999 }, @as(i128, -11106)); | |
| 561 | try testReduce(.Add, [4]u128{ 9, 99, 999, 9999 }, @as(u128, 11106)); | |
| 562 | try testReduce(.Add, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 42.9)); | |
| 563 | try testReduce(.Add, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 42.9)); | |
| 564 | try testReduce(.Add, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 42.9)); | |
| 565 | ||
| 566 | try testReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); | |
| 567 | try testReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0)); | |
| 568 | try testReduce(.And, [4]u16{ 0xffff, 0xff55, 0xaaff, 0x1010 }, @as(u16, 0x10)); | |
| 569 | try testReduce(.And, [4]u32{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u32, 0x1010)); | |
| 570 | try testReduce(.And, [4]u64{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u64, 0x1010)); | |
| 571 | ||
| 572 | try testReduce(.Min, [4]i16{ -1, 2, 3, 4 }, @as(i16, -1)); | |
| 573 | try testReduce(.Min, [4]u16{ 1, 2, 3, 4 }, @as(u16, 1)); | |
| 574 | try testReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386)); | |
| 575 | try testReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9)); | |
| 569 | 576 | |
| 570 | 577 | // LLVM 11 ERROR: Cannot select type |
| 571 | 578 | // https://github.com/ziglang/zig/issues/7138 |
| 572 | 579 | if (builtin.target.cpu.arch != .aarch64) { |
| 573 | try doTheTestReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386)); | |
| 574 | try doTheTestReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9)); | |
| 580 | try testReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386)); | |
| 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)); | |
| 578 | try doTheTestReduce(.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)); | |
| 580 | try doTheTestReduce(.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)); | |
| 584 | try testReduce(.Min, [4]i128{ 1234567, -386, 0, 3 }, @as(i128, -386)); | |
| 585 | try testReduce(.Min, [4]u128{ 99, 9999, 9, 99999 }, @as(u128, 9)); | |
| 586 | try testReduce(.Min, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, -100.0)); | |
| 587 | try testReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -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)); | |
| 584 | try doTheTestReduce(.Max, [4]u16{ 1, 2, 3, 4 }, @as(u16, 4)); | |
| 585 | try doTheTestReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567)); | |
| 586 | try doTheTestReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999)); | |
| 590 | try testReduce(.Max, [4]i16{ -1, 2, 3, 4 }, @as(i16, 4)); | |
| 591 | try testReduce(.Max, [4]u16{ 1, 2, 3, 4 }, @as(u16, 4)); | |
| 592 | try testReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567)); | |
| 593 | try testReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999)); | |
| 587 | 594 | |
| 588 | 595 | // LLVM 11 ERROR: Cannot select type |
| 589 | 596 | // https://github.com/ziglang/zig/issues/7138 |
| 590 | 597 | if (builtin.target.cpu.arch != .aarch64) { |
| 591 | try doTheTestReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567)); | |
| 592 | try doTheTestReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999)); | |
| 598 | try testReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567)); | |
| 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)); | |
| 596 | try doTheTestReduce(.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)); | |
| 598 | try doTheTestReduce(.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)); | |
| 600 | ||
| 601 | try doTheTestReduce(.Mul, [4]i16{ -1, 2, 3, 4 }, @as(i16, -24)); | |
| 602 | try doTheTestReduce(.Mul, [4]u16{ 1, 2, 3, 4 }, @as(u16, 24)); | |
| 603 | try doTheTestReduce(.Mul, [4]i32{ -9, -99, -999, 999 }, @as(i32, -889218891)); | |
| 604 | try doTheTestReduce(.Mul, [4]u32{ 1, 2, 3, 4 }, @as(u32, 24)); | |
| 605 | try doTheTestReduce(.Mul, [4]i64{ 9, 99, 999, 9999 }, @as(i64, 8900199891)); | |
| 606 | try doTheTestReduce(.Mul, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 8900199891)); | |
| 607 | try doTheTestReduce(.Mul, [4]i128{ -9, -99, -999, 9999 }, @as(i128, -8900199891)); | |
| 608 | try doTheTestReduce(.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)); | |
| 610 | try doTheTestReduce(.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)); | |
| 612 | ||
| 613 | try doTheTestReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true)); | |
| 614 | try doTheTestReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1)); | |
| 615 | try doTheTestReduce(.Or, [4]u16{ 0xff00, 0xff00, 0xf0, 0xf }, ~@as(u16, 0)); | |
| 616 | try doTheTestReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0)); | |
| 617 | try doTheTestReduce(.Or, [4]u64{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u64, 0xffffffff)); | |
| 618 | try doTheTestReduce(.Or, [4]u128{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u128, 0xffffffff)); | |
| 619 | ||
| 620 | try doTheTestReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true)); | |
| 621 | try doTheTestReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1)); | |
| 622 | try doTheTestReduce(.Xor, [4]u16{ 0x0000, 0x3333, 0x8888, 0x4444 }, ~@as(u16, 0)); | |
| 623 | try doTheTestReduce(.Xor, [4]u32{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, ~@as(u32, 0)); | |
| 624 | try doTheTestReduce(.Xor, [4]u64{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u64, 0xffffffff)); | |
| 625 | try doTheTestReduce(.Xor, [4]u128{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u128, 0xffffffff)); | |
| 602 | try testReduce(.Max, [4]i128{ 1234567, -386, 0, 3 }, @as(i128, 1234567)); | |
| 603 | try testReduce(.Max, [4]u128{ 99, 9999, 9, 99999 }, @as(u128, 99999)); | |
| 604 | try testReduce(.Max, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, 10.0e9)); | |
| 605 | try testReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9)); | |
| 606 | try testReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9)); | |
| 607 | ||
| 608 | try testReduce(.Mul, [4]i16{ -1, 2, 3, 4 }, @as(i16, -24)); | |
| 609 | try testReduce(.Mul, [4]u16{ 1, 2, 3, 4 }, @as(u16, 24)); | |
| 610 | try testReduce(.Mul, [4]i32{ -9, -99, -999, 999 }, @as(i32, -889218891)); | |
| 611 | try testReduce(.Mul, [4]u32{ 1, 2, 3, 4 }, @as(u32, 24)); | |
| 612 | try testReduce(.Mul, [4]i64{ 9, 99, 999, 9999 }, @as(i64, 8900199891)); | |
| 613 | try testReduce(.Mul, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 8900199891)); | |
| 614 | try testReduce(.Mul, [4]i128{ -9, -99, -999, 9999 }, @as(i128, -8900199891)); | |
| 615 | try testReduce(.Mul, [4]u128{ 9, 99, 999, 9999 }, @as(u128, 8900199891)); | |
| 616 | try testReduce(.Mul, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 58430.7)); | |
| 617 | try testReduce(.Mul, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 58430.7)); | |
| 618 | try testReduce(.Mul, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 58430.7)); | |
| 619 | ||
| 620 | try testReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true)); | |
| 621 | try testReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1)); | |
| 622 | try testReduce(.Or, [4]u16{ 0xff00, 0xff00, 0xf0, 0xf }, ~@as(u16, 0)); | |
| 623 | try testReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0)); | |
| 624 | try testReduce(.Or, [4]u64{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u64, 0xffffffff)); | |
| 625 | try testReduce(.Or, [4]u128{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u128, 0xffffffff)); | |
| 626 | ||
| 627 | try testReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true)); | |
| 628 | try testReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1)); | |
| 629 | try testReduce(.Xor, [4]u16{ 0x0000, 0x3333, 0x8888, 0x4444 }, ~@as(u16, 0)); | |
| 630 | try testReduce(.Xor, [4]u32{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, ~@as(u32, 0)); | |
| 631 | try testReduce(.Xor, [4]u64{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u64, 0xffffffff)); | |
| 632 | try testReduce(.Xor, [4]u128{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u128, 0xffffffff)); | |
| 626 | 633 | |
| 627 | 634 | // Test the reduction on vectors containing NaNs. |
| 628 | 635 | const f16_nan = math.nan(f16); |
| 629 | 636 | const f32_nan = math.nan(f32); |
| 630 | 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); | |
| 633 | try doTheTestReduce(.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); | |
| 639 | try testReduce(.Add, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | |
| 640 | try testReduce(.Add, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | |
| 641 | try testReduce(.Add, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | |
| 635 | 642 | |
| 636 | 643 | // LLVM 11 ERROR: Cannot select type |
| 637 | 644 | // https://github.com/ziglang/zig/issues/7138 |
| 638 | 645 | if (false) { |
| 639 | try doTheTestReduce(.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); | |
| 641 | try doTheTestReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | |
| 646 | try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | |
| 647 | try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_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); | |
| 644 | try doTheTestReduce(.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); | |
| 650 | try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | |
| 651 | try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_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); | |
| 649 | try doTheTestReduce(.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); | |
| 655 | try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | |
| 656 | try testReduce(.Mul, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | |
| 657 | try testReduce(.Mul, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | |
| 651 | 658 | } |
| 652 | 659 | }; |
| 653 | 660 |