authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-17 17:24:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-17 17:24:35-07:00
log7233a3324aaa5b3995606f24b2b961149219986b
tree009a83fa10f489f0d6da4f77f07a4d0c8ca4e388
parent76e103057ea6037d3bb3e44cd33880a9a91609fb

stage2: implement `@reduce`

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 elements541 /// 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 },
698710
699 // Make sure we don't accidentally add a field to make this union711 // Make sure we don't accidentally add a field to make this union
700 // bigger than expected. Note that in Debug builds, Zig is allowed712 // 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 },
10291041
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),
10311045
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}
1397513975
13976fn resolveAtomicOrder(13976fn 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
13990fn 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}
1398813998
13989fn resolveAtomicRmwOp(13999fn 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}
1400114007
14002fn zirCmpxchg(14008fn 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
1411814124
14119fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {14125fn 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}
1412414194
14125fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {14195fn 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: on14500 // 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();
1924719317
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}
37293730
3731fn 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
3730fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {3737fn 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}
42064207
4208fn 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
4207fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {4214fn 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}
22152216
2217fn 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
2216fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {2223fn 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);
29902991
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);
30013001
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}
30063005
3006fn 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
3007fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3016fn 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 = {} };
30093018
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}
55695570
5571fn 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
5570fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {5577fn 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}
36273628
3629fn 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
3628fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {3644fn 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;
36303646
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 }
62836284
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;
62866331
src/codegen/llvm/bindings.zig+33
...@@ -853,6 +853,39 @@ pub const Builder = opaque {...@@ -853,6 +853,39 @@ pub const Builder = opaque {
853853
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};
857890
858pub const MDString = opaque {891pub 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),
268269
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 }
394395
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;
397405
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 }
18431843
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 }
20122022
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 }
29492969
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 }
29612981
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}
521521
522test "vector reduce operation" {522test "vector reduce operation" {
523 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO523 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;
528533
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 never542 // 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));
558565
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));
564571
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));
569576
570 // LLVM 11 ERROR: Cannot select type577 // LLVM 11 ERROR: Cannot select type
571 // https://github.com/ziglang/zig/issues/7138578 // 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 }
576583
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));
582589
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));
587594
588 // LLVM 11 ERROR: Cannot select type595 // LLVM 11 ERROR: Cannot select type
589 // https://github.com/ziglang/zig/issues/7138596 // 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 }
594601
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));
600607
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));
612619
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));
619626
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));
626633
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);
631638
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);
635642
636 // LLVM 11 ERROR: Cannot select type643 // LLVM 11 ERROR: Cannot select type
637 // https://github.com/ziglang/zig/issues/7138644 // 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);
642649
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 }
647654
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 };
653660