authorgravatar for john.schmidt.h@gmail.comJohn Schmidt <john.schmidt.h@gmail.com> 2022-03-23 21:41:35+01:00
committergravatar for john.schmidt.h@gmail.comJohn Schmidt <john.schmidt.h@gmail.com> 2022-03-25 16:13:54+01:00
log12d5efcbe621b98b5a99c5f84a9d5b605e4acc40
tree9188b875170a4c23511213f8002fe48908ea3729
parent1c33ea2c35e9260babedb116ad527256e0a4ef5e

stage2: implement `@select`


12 files changed, 222 insertions(+), 20 deletions(-)

src/Air.zig+11-1
......@@ -344,7 +344,7 @@ pub const Inst = struct {
344344 /// to the storage for the variable. The local may be a const or a var.
345345 /// Result type is always void.
346346 /// Uses `pl_op`. The payload index is the variable name. It points to the extra
347 /// array, reinterpreting the bytes there as a null-terminated string.
347 /// array, reinterpreting the bytes there as a null-terminated string.
348348 dbg_var_ptr,
349349 /// Same as `dbg_var_ptr` except the local is a const, not a var, and the
350350 /// operand is the local's value.
......@@ -553,6 +553,9 @@ pub const Inst = struct {
553553 /// Constructs a vector by selecting elements from `a` and `b` based on `mask`.
554554 /// Uses the `ty_pl` field with payload `Shuffle`.
555555 shuffle,
556 /// Constructs a vector element-wise from `a` or `b` based on `pred`.
557 /// Uses the `ty_pl` field with payload `Select`.
558 select,
556559
557560 /// Given dest ptr, value, and len, set all elements at dest to value.
558561 /// Result type is always void.
......@@ -785,6 +788,12 @@ pub const Shuffle = struct {
785788 mask_len: u32,
786789};
787790
791pub const Select = struct {
792 pred: Inst.Ref,
793 a: Inst.Ref,
794 b: Inst.Ref,
795};
796
788797pub const VectorCmp = struct {
789798 lhs: Inst.Ref,
790799 rhs: Inst.Ref,
......@@ -956,6 +965,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
956965 .cmpxchg_weak,
957966 .cmpxchg_strong,
958967 .slice,
968 .select,
959969 .shuffle,
960970 .aggregate_init,
961971 .union_init,
src/Liveness.zig+4
......@@ -433,6 +433,10 @@ fn analyzeInst(
433433 }
434434 return extra_tombs.finish();
435435 },
436 .select => {
437 const extra = a.air.extraData(Air.Select, inst_datas[inst].ty_pl.payload).data;
438 return trackOperands(a, new_set, inst, main_tomb, .{ extra.pred, extra.a, extra.b });
439 },
436440 .shuffle => {
437441 const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data;
438442 return trackOperands(a, new_set, inst, main_tomb, .{ extra.a, extra.b, .none });
src/Sema.zig+99-2
......@@ -14804,8 +14804,105 @@ fn analyzeShuffle(
1480414804
1480514805fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1480614806 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
14807 const src = inst_data.src();
14808 return sema.fail(block, src, "TODO: Sema.zirSelect", .{});
14807 const extra = sema.code.extraData(Zir.Inst.Select, inst_data.payload_index).data;
14808
14809 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
14810 const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
14811 const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
14812 const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
14813
14814 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
14815 try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
14816 const pred = sema.resolveInst(extra.pred);
14817 const a = sema.resolveInst(extra.a);
14818 const b = sema.resolveInst(extra.b);
14819 const target = sema.mod.getTarget();
14820
14821 const pred_ty = sema.typeOf(pred);
14822 switch (try pred_ty.zigTypeTagOrPoison()) {
14823 .Vector => {
14824 const scalar_ty = pred_ty.childType();
14825 if (!scalar_ty.eql(Type.bool, target)) {
14826 const bool_vec_ty = try Type.vector(sema.arena, pred_ty.vectorLen(), Type.bool);
14827 return sema.fail(block, pred_src, "Expected '{}', found '{}'", .{ bool_vec_ty.fmt(target), pred_ty.fmt(target) });
14828 }
14829 },
14830 else => return sema.fail(block, pred_src, "Expected vector type, found '{}'", .{pred_ty.fmt(target)}),
14831 }
14832
14833 const vec_len = pred_ty.vectorLen();
14834 const vec_ty = try Type.vector(sema.arena, vec_len, elem_ty);
14835
14836 const a_ty = sema.typeOf(a);
14837 if (!a_ty.eql(vec_ty, target)) {
14838 return sema.fail(block, a_src, "Expected '{}', found '{}'", .{ vec_ty.fmt(target), a_ty.fmt(target) });
14839 }
14840
14841 const b_ty = sema.typeOf(b);
14842 if (!b_ty.eql(vec_ty, target)) {
14843 return sema.fail(block, b_src, "Expected '{}', found '{}'", .{ vec_ty.fmt(target), b_ty.fmt(target) });
14844 }
14845
14846 const maybe_pred = try sema.resolveMaybeUndefVal(block, pred_src, pred);
14847 const maybe_a = try sema.resolveMaybeUndefVal(block, a_src, a);
14848 const maybe_b = try sema.resolveMaybeUndefVal(block, b_src, b);
14849
14850 const runtime_src = if (maybe_pred) |pred_val| rs: {
14851 if (pred_val.isUndef()) return sema.addConstUndef(vec_ty);
14852
14853 if (maybe_a) |a_val| {
14854 if (a_val.isUndef()) return sema.addConstUndef(vec_ty);
14855
14856 if (maybe_b) |b_val| {
14857 if (b_val.isUndef()) return sema.addConstUndef(vec_ty);
14858
14859 var buf: Value.ElemValueBuffer = undefined;
14860 const elems = try sema.gpa.alloc(Value, vec_len);
14861 for (elems) |*elem, i| {
14862 const pred_elem_val = pred_val.elemValueBuffer(i, &buf);
14863 const should_choose_a = pred_elem_val.toBool();
14864 if (should_choose_a) {
14865 elem.* = a_val.elemValueBuffer(i, &buf);
14866 } else {
14867 elem.* = b_val.elemValueBuffer(i, &buf);
14868 }
14869 }
14870
14871 return sema.addConstant(
14872 vec_ty,
14873 try Value.Tag.aggregate.create(sema.arena, elems),
14874 );
14875 } else {
14876 break :rs b_src;
14877 }
14878 } else {
14879 if (maybe_b) |b_val| {
14880 if (b_val.isUndef()) return sema.addConstUndef(vec_ty);
14881 }
14882 break :rs a_src;
14883 }
14884 } else rs: {
14885 if (maybe_a) |a_val| {
14886 if (a_val.isUndef()) return sema.addConstUndef(vec_ty);
14887 }
14888 if (maybe_b) |b_val| {
14889 if (b_val.isUndef()) return sema.addConstUndef(vec_ty);
14890 }
14891 break :rs pred_src;
14892 };
14893
14894 try sema.requireRuntimeBlock(block, runtime_src);
14895 return block.addInst(.{
14896 .tag = .select,
14897 .data = .{ .ty_pl = .{
14898 .ty = try block.sema.addType(vec_ty),
14899 .payload = try block.sema.addExtra(Air.Select{
14900 .pred = pred,
14901 .a = a,
14902 .b = b,
14903 }),
14904 } },
14905 });
1480914906}
1481014907
1481114908fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/arch/aarch64/CodeGen.zig+8
......@@ -640,6 +640,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
640640 .tag_name => try self.airTagName(inst),
641641 .error_name => try self.airErrorName(inst),
642642 .splat => try self.airSplat(inst),
643 .select => try self.airSelect(inst),
643644 .shuffle => try self.airShuffle(inst),
644645 .reduce => try self.airReduce(inst),
645646 .aggregate_init => try self.airAggregateInit(inst),
......@@ -3746,6 +3747,13 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
37463747 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
37473748}
37483749
3750fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
3751 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3752 const extra = self.air.extraData(Air.Select, ty_pl.payload).data;
3753 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch});
3754 return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b });
3755}
3756
37493757fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
37503758 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
37513759 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch});
src/arch/arm/CodeGen.zig+8
......@@ -630,6 +630,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
630630 .tag_name => try self.airTagName(inst),
631631 .error_name => try self.airErrorName(inst),
632632 .splat => try self.airSplat(inst),
633 .select => try self.airSelect(inst),
633634 .shuffle => try self.airShuffle(inst),
634635 .reduce => try self.airReduce(inst),
635636 .aggregate_init => try self.airAggregateInit(inst),
......@@ -4323,6 +4324,13 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
43234324 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
43244325}
43254326
4327fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
4328 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4329 const extra = self.air.extraData(Air.Select, ty_pl.payload).data;
4330 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for arm", .{});
4331 return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b });
4332}
4333
43264334fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
43274335 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
43284336 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for arm", .{});
src/arch/riscv64/CodeGen.zig+8
......@@ -600,6 +600,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
600600 .tag_name => try self.airTagName(inst),
601601 .error_name => try self.airErrorName(inst),
602602 .splat => try self.airSplat(inst),
603 .select => try self.airSelect(inst),
603604 .shuffle => try self.airShuffle(inst),
604605 .reduce => try self.airReduce(inst),
605606 .aggregate_init => try self.airAggregateInit(inst),
......@@ -2396,6 +2397,13 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
23962397 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
23972398}
23982399
2400fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
2401 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2402 const extra = self.air.extraData(Air.Select, ty_pl.payload).data;
2403 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for riscv64", .{});
2404 return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b });
2405}
2406
23992407fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
24002408 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
24012409 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for riscv64", .{});
src/arch/wasm/CodeGen.zig+11
......@@ -1371,6 +1371,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
13711371 .ret_ptr => self.airRetPtr(inst),
13721372 .ret_load => self.airRetLoad(inst),
13731373 .splat => self.airSplat(inst),
1374 .select => self.airSelect(inst),
13741375 .shuffle => self.airShuffle(inst),
13751376 .reduce => self.airReduce(inst),
13761377 .aggregate_init => self.airAggregateInit(inst),
......@@ -3265,6 +3266,16 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32653266 return self.fail("TODO: Implement wasm airSplat", .{});
32663267}
32673268
3269fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3270 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3271
3272 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3273 const ty = try self.resolveInst(ty_pl.ty);
3274
3275 _ = ty;
3276 return self.fail("TODO: Implement wasm airSelect", .{});
3277}
3278
32683279fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32693280 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
32703281
src/arch/x86_64/CodeGen.zig+8
......@@ -721,6 +721,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
721721 .tag_name => try self.airTagName(inst),
722722 .error_name => try self.airErrorName(inst),
723723 .splat => try self.airSplat(inst),
724 .select => try self.airSelect(inst),
724725 .shuffle => try self.airShuffle(inst),
725726 .reduce => try self.airReduce(inst),
726727 .aggregate_init => try self.airAggregateInit(inst),
......@@ -5678,6 +5679,13 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
56785679 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
56795680}
56805681
5682fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
5683 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5684 const extra = self.air.extraData(Air.Select, ty_pl.payload).data;
5685 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for x86_64", .{});
5686 return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b });
5687}
5688
56815689fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
56825690 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
56835691 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for x86_64", .{});
src/codegen/c.zig+16
......@@ -1825,6 +1825,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
18251825 .tag_name => try airTagName(f, inst),
18261826 .error_name => try airErrorName(f, inst),
18271827 .splat => try airSplat(f, inst),
1828 .select => try airSelect(f, inst),
18281829 .shuffle => try airShuffle(f, inst),
18291830 .reduce => try airReduce(f, inst),
18301831 .aggregate_init => try airAggregateInit(f, inst),
......@@ -3794,6 +3795,21 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
37943795 return f.fail("TODO: C backend: implement airSplat", .{});
37953796}
37963797
3798fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {
3799 if (f.liveness.isUnused(inst)) return CValue.none;
3800
3801 const inst_ty = f.air.typeOfIndex(inst);
3802 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
3803
3804 const writer = f.object.writer();
3805 const local = try f.allocLocal(inst_ty, .Const);
3806 try writer.writeAll(" = ");
3807
3808 _ = local;
3809 _ = ty_pl;
3810 return f.fail("TODO: C backend: implement airSelect", .{});
3811}
3812
37973813fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {
37983814 if (f.liveness.isUnused(inst)) return CValue.none;
37993815
src/codegen/llvm.zig+13
......@@ -3444,6 +3444,7 @@ pub const FuncGen = struct {
34443444 .tag_name => try self.airTagName(inst),
34453445 .error_name => try self.airErrorName(inst),
34463446 .splat => try self.airSplat(inst),
3447 .select => try self.airSelect(inst),
34473448 .shuffle => try self.airShuffle(inst),
34483449 .reduce => try self.airReduce(inst),
34493450 .aggregate_init => try self.airAggregateInit(inst),
......@@ -6355,6 +6356,18 @@ pub const FuncGen = struct {
63556356 return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), "");
63566357 }
63576358
6359 fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
6360 if (self.liveness.isUnused(inst)) return null;
6361
6362 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
6363 const extra = self.air.extraData(Air.Select, ty_pl.payload).data;
6364 const pred = try self.resolveInst(extra.pred);
6365 const a = try self.resolveInst(extra.a);
6366 const b = try self.resolveInst(extra.b);
6367
6368 return self.builder.buildSelect(pred, a, b, "");
6369 }
6370
63586371 fn airShuffle(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
63596372 if (self.liveness.isUnused(inst)) return null;
63606373
src/print_air.zig+13
......@@ -264,6 +264,7 @@ const Writer = struct {
264264 .wasm_memory_size => try w.writeWasmMemorySize(s, inst),
265265 .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),
266266 .mul_add => try w.writeMulAdd(s, inst),
267 .select => try w.writeSelect(s, inst),
267268 .shuffle => try w.writeShuffle(s, inst),
268269 .reduce => try w.writeReduce(s, inst),
269270 .cmp_vector => try w.writeCmpVector(s, inst),
......@@ -396,6 +397,18 @@ const Writer = struct {
396397 try s.print(", mask {d}, len {d}", .{ extra.mask, extra.mask_len });
397398 }
398399
400 fn writeSelect(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
401 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
402 const extra = w.air.extraData(Air.Select, ty_pl.payload).data;
403
404 try s.print("{}, ", .{w.air.getRefType(ty_pl.ty).fmtDebug()});
405 try w.writeOperand(s, inst, 0, extra.pred);
406 try s.writeAll(", ");
407 try w.writeOperand(s, inst, 1, extra.a);
408 try s.writeAll(", ");
409 try w.writeOperand(s, inst, 2, extra.b);
410 }
411
399412 fn writeReduce(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
400413 const reduce = w.air.instructions.items(.data)[inst].reduce;
401414
test/behavior/select.zig+23-17
......@@ -4,23 +4,29 @@ const mem = std.mem;
44const expect = std.testing.expect;
55
66test "@select" {
7 if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
812
9 const S = struct {
10 fn doTheTest() !void {
11 var a: @Vector(4, bool) = [4]bool{ true, false, true, false };
12 var b: @Vector(4, i32) = [4]i32{ -1, 4, 999, -31 };
13 var c: @Vector(4, i32) = [4]i32{ -5, 1, 0, 1234 };
14 var abc = @select(i32, a, b, c);
15 try expect(mem.eql(i32, &@as([4]i32, abc), &[4]i32{ -1, 1, 999, 1234 }));
13 try doTheTest();
14 comptime try doTheTest();
15}
16
17fn doTheTest() !void {
18 var a = @Vector(4, bool){ true, false, true, false };
19 var b = @Vector(4, i32){ -1, 4, 999, -31 };
20 var c = @Vector(4, i32){ -5, 1, 0, 1234 };
21 var abc = @select(i32, a, b, c);
22 try expect(abc[0] == -1);
23 try expect(abc[1] == 1);
24 try expect(abc[2] == 999);
25 try expect(abc[3] == 1234);
1626
17 var x: @Vector(4, bool) = [4]bool{ false, false, false, true };
18 var y: @Vector(4, f32) = [4]f32{ 0.001, 33.4, 836, -3381.233 };
19 var z: @Vector(4, f32) = [4]f32{ 0.0, 312.1, -145.9, 9993.55 };
20 var xyz = @select(f32, x, y, z);
21 try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 }));
22 }
23 };
24 try S.doTheTest();
25 comptime try S.doTheTest();
27 var x = @Vector(4, bool){ false, false, false, true };
28 var y = @Vector(4, f32){ 0.001, 33.4, 836, -3381.233 };
29 var z = @Vector(4, f32){ 0.0, 312.1, -145.9, 9993.55 };
30 var xyz = @select(f32, x, y, z);
31 try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 }));
2632}