authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-03-11 14:25:15+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-03-11 14:25:15+02:00
log01cd4119b032f13899e9b7b30c7e093620058dfd
treebfafdc11324d3b77ff5af8b59b5ff324db66335b
parentcba68090a60c3de8eadbf8eb53e37620a1d66683

Sema: implement `@shuffle` at comptime and for differing lengths


3 files changed, 110 insertions(+), 34 deletions(-)

src/Sema.zig+73-10
...@@ -13454,8 +13454,6 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13454,8 +13454,6 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13454 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;13454 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
13455 const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;13455 const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
13456 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };13456 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
13457 const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
13458 const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
13459 const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };13457 const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
1346013458
13461 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);13459 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
...@@ -13474,6 +13472,25 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13474,6 +13472,25 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13474 .elem_type = Type.@"i32",13472 .elem_type = Type.@"i32",
13475 });13473 });
13476 mask = try sema.coerce(block, mask_ty, mask, mask_src);13474 mask = try sema.coerce(block, mask_ty, mask, mask_src);
13475 const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask);
13476 return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(u32, mask_len));
13477}
13478
13479fn analyzeShuffle(
13480 sema: *Sema,
13481 block: *Block,
13482 src_node: i32,
13483 elem_ty: Type,
13484 a_arg: Air.Inst.Ref,
13485 b_arg: Air.Inst.Ref,
13486 mask: Value,
13487 mask_len: u32,
13488) CompileError!Air.Inst.Ref {
13489 const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node };
13490 const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node };
13491 const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node };
13492 var a = a_arg;
13493 var b = b_arg;
1347713494
13478 const res_ty = try Type.Tag.vector.create(sema.arena, .{13495 const res_ty = try Type.Tag.vector.create(sema.arena, .{
13479 .len = mask_len,13496 .len = mask_len,
...@@ -13485,7 +13502,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13485,7 +13502,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13485 .Undefined => null,13502 .Undefined => null,
13486 else => return sema.fail(block, a_src, "expected vector or array with element type {}, found {}", .{13503 else => return sema.fail(block, a_src, "expected vector or array with element type {}, found {}", .{
13487 elem_ty,13504 elem_ty,
13488 sema.typeOf(mask),13505 sema.typeOf(a),
13489 }),13506 }),
13490 };13507 };
13491 var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) {13508 var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) {
...@@ -13493,7 +13510,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13493,7 +13510,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13493 .Undefined => null,13510 .Undefined => null,
13494 else => return sema.fail(block, b_src, "expected vector or array with element type {}, found {}", .{13511 else => return sema.fail(block, b_src, "expected vector or array with element type {}, found {}", .{
13495 elem_ty,13512 elem_ty,
13496 sema.typeOf(mask),13513 sema.typeOf(b),
13497 }),13514 }),
13498 };13515 };
13499 if (maybe_a_len == null and maybe_b_len == null) {13516 if (maybe_a_len == null and maybe_b_len == null) {
...@@ -13519,11 +13536,10 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13519,11 +13536,10 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13519 .{ b_len, b_src, b_ty },13536 .{ b_len, b_src, b_ty },
13520 };13537 };
1352113538
13522 const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask);
13523 var i: usize = 0;13539 var i: usize = 0;
13524 while (i < mask_len) : (i += 1) {13540 while (i < mask_len) : (i += 1) {
13525 var buf: Value.ElemValueBuffer = undefined;13541 var buf: Value.ElemValueBuffer = undefined;
13526 const elem = mask_val.elemValueBuffer(i, &buf);13542 const elem = mask.elemValueBuffer(i, &buf);
13527 if (elem.isUndef()) continue;13543 if (elem.isUndef()) continue;
13528 const int = elem.toSignedInt();13544 const int = elem.toSignedInt();
13529 var unsigned: u32 = undefined;13545 var unsigned: u32 = undefined;
...@@ -13555,14 +13571,61 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13555,14 +13571,61 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13555 }13571 }
13556 }13572 }
1355713573
13558 // TODO at comptime13574 if (try sema.resolveMaybeUndefVal(block, a_src, a)) |a_val| {
13575 if (try sema.resolveMaybeUndefVal(block, b_src, b)) |b_val| {
13576 const values = try sema.arena.alloc(Value, mask_len);
13577
13578 i = 0;
13579 while (i < mask_len) : (i += 1) {
13580 var buf: Value.ElemValueBuffer = undefined;
13581 const mask_elem_val = mask.elemValueBuffer(i, &buf);
13582 if (mask_elem_val.isUndef()) {
13583 values[i] = Value.undef;
13584 continue;
13585 }
13586 const int = mask_elem_val.toSignedInt();
13587 const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int);
13588 if (int >= 0) {
13589 values[i] = try a_val.elemValue(sema.arena, unsigned);
13590 } else {
13591 values[i] = try b_val.elemValue(sema.arena, unsigned);
13592 }
13593 }
13594 const res_val = try Value.Tag.array.create(sema.arena, values);
13595 return sema.addConstant(res_ty, res_val);
13596 }
13597 }
1355913598
13599 // All static analysis passed, and not comptime.
13600 // For runtime codegen, vectors a and b must be the same length. Here we
13601 // recursively @shuffle the smaller vector to append undefined elements
13602 // to it up to the length of the longer vector. This recursion terminates
13603 // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len.
13560 if (a_len != b_len) {13604 if (a_len != b_len) {
13561 return sema.fail(block, mask_src, "TODO handle shuffle a_len != b_len", .{});13605 const min_len = std.math.min(a_len, b_len);
13606 const max_len = std.math.max(a_len, b_len);
13607
13608 const expand_mask_values = try sema.arena.alloc(Value, max_len);
13609 i = 0;
13610 while (i < min_len) : (i += 1) {
13611 expand_mask_values[i] = try Value.Tag.int_u64.create(sema.arena, i);
13612 }
13613 while (i < max_len) : (i += 1) {
13614 expand_mask_values[i] = Value.negative_one;
13615 }
13616 const expand_mask = try Value.Tag.array.create(sema.arena, expand_mask_values);
13617
13618 if (a_len < b_len) {
13619 const undef = try sema.addConstUndef(a_ty);
13620 a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask, @intCast(u32, max_len));
13621 } else {
13622 const undef = try sema.addConstUndef(b_ty);
13623 b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask, @intCast(u32, max_len));
13624 }
13562 }13625 }
1356313626
13564 const mask_index = @intCast(u32, sema.air_values.items.len);13627 const mask_index = @intCast(u32, sema.air_values.items.len);
13565 try sema.air_values.append(sema.gpa, mask_val);13628 try sema.air_values.append(sema.gpa, mask);
13566 return block.addInst(.{13629 return block.addInst(.{
13567 .tag = .shuffle,13630 .tag = .shuffle,
13568 .data = .{ .ty_pl = .{13631 .data = .{ .ty_pl = .{
...@@ -13571,7 +13634,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13571,7 +13634,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13571 .a = a,13634 .a = a,
13572 .b = b,13635 .b = b,
13573 .mask = mask_index,13636 .mask = mask_index,
13574 .mask_len = @intCast(u32, mask_len),13637 .mask_len = mask_len,
13575 }),13638 }),
13576 } },13639 } },
13577 });13640 });
test/behavior.zig+1-1
...@@ -151,6 +151,7 @@ test {...@@ -151,6 +151,7 @@ test {
151 _ = @import("behavior/bugs/2114.zig");151 _ = @import("behavior/bugs/2114.zig");
152 _ = @import("behavior/bugs/3779.zig");152 _ = @import("behavior/bugs/3779.zig");
153 _ = @import("behavior/bugs/10147.zig");153 _ = @import("behavior/bugs/10147.zig");
154 _ = @import("behavior/shuffle.zig");
154 _ = @import("behavior/union_with_members.zig");155 _ = @import("behavior/union_with_members.zig");
155156
156 if (builtin.zig_backend == .stage1) {157 if (builtin.zig_backend == .stage1) {
...@@ -169,7 +170,6 @@ test {...@@ -169,7 +170,6 @@ test {
169 _ = @import("behavior/bugs/7027.zig");170 _ = @import("behavior/bugs/7027.zig");
170 _ = @import("behavior/const_slice_child.zig");171 _ = @import("behavior/const_slice_child.zig");
171 _ = @import("behavior/select.zig");172 _ = @import("behavior/select.zig");
172 _ = @import("behavior/shuffle.zig");
173 _ = @import("behavior/struct_contains_slice_of_itself.zig");173 _ = @import("behavior/struct_contains_slice_of_itself.zig");
174 _ = @import("behavior/typename.zig");174 _ = @import("behavior/typename.zig");
175 _ = @import("behavior/vector.zig");175 _ = @import("behavior/vector.zig");
test/behavior/shuffle.zig+36-23
...@@ -4,12 +4,12 @@ const mem = std.mem;...@@ -4,12 +4,12 @@ const mem = std.mem;
4const expect = std.testing.expect;4const expect = std.testing.expect;
5const Vector = std.meta.Vector;5const Vector = std.meta.Vector;
66
7test "@shuffle" {7test "@shuffle int" {
8 const S = struct {8 const S = struct {
9 fn doTheTest() !void {9 fn doTheTest() !void {
10 var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };10 var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
11 var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };11 var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
12 const mask: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) };12 const mask = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) };
13 var res = @shuffle(i32, v, x, mask);13 var res = @shuffle(i32, v, x, mask);
14 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 }));14 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 }));
1515
...@@ -18,40 +18,53 @@ test "@shuffle" {...@@ -18,40 +18,53 @@ test "@shuffle" {
18 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 }));18 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 }));
1919
20 // Undefined20 // Undefined
21 const mask2: Vector(4, i32) = [4]i32{ 3, 1, 2, 0 };21 const mask2 = [4]i32{ 3, 1, 2, 0 };
22 res = @shuffle(i32, v, undefined, mask2);22 res = @shuffle(i32, v, undefined, mask2);
23 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 40, -2, 30, 2147483647 }));23 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 40, -2, 30, 2147483647 }));
2424
25 // Upcasting of b25 // Upcasting of b
26 var v2: Vector(2, i32) = [2]i32{ 2147483647, undefined };26 var v2: Vector(2, i32) = [2]i32{ 2147483647, undefined };
27 const mask3: Vector(4, i32) = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 };27 const mask3 = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 };
28 res = @shuffle(i32, x, v2, mask3);28 res = @shuffle(i32, x, v2, mask3);
29 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 2147483647, 4 }));29 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 2147483647, 4 }));
3030
31 // Upcasting of a31 // Upcasting of a
32 var v3: Vector(2, i32) = [2]i32{ 2147483647, -2 };32 var v3: Vector(2, i32) = [2]i32{ 2147483647, -2 };
33 const mask4: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) };33 const mask4 = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) };
34 res = @shuffle(i32, v3, x, mask4);34 res = @shuffle(i32, v3, x, mask4);
35 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, -2, 4 }));35 try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, -2, 4 }));
36 }
37 };
38 try S.doTheTest();
39 comptime try S.doTheTest();
40}
3641
37 // bool42test "@shuffle bool" {
38 {43 const S = struct {
39 var x2: Vector(4, bool) = [4]bool{ false, true, false, true };44 fn doTheTest() !void {
40 var v4: Vector(2, bool) = [2]bool{ true, false };45 var x: Vector(4, bool) = [4]bool{ false, true, false, true };
41 const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 };46 var v: Vector(2, bool) = [2]bool{ true, false };
42 var res2 = @shuffle(bool, x2, v4, mask5);47 const mask = [4]i32{ 0, ~@as(i32, 1), 1, 2 };
43 try expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false }));48 var res = @shuffle(bool, x, v, mask);
44 }49 try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false }));
4550 }
46 // TODO re-enable when LLVM codegen is fixed51 };
47 // https://github.com/ziglang/zig/issues/324652 if (builtin.zig_backend == .stage1) try S.doTheTest();
48 if (false) {53 comptime try S.doTheTest();
49 var x2: Vector(3, bool) = [3]bool{ false, true, false };54}
50 var v4: Vector(2, bool) = [2]bool{ true, false };55
51 const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 };56test "@shuffle bool" {
52 var res2 = @shuffle(bool, x2, v4, mask5);57 // TODO re-enable when LLVM codegen is fixed
53 try expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false }));58 // https://github.com/ziglang/zig/issues/3246
54 }59 if (true) return error.SkipZigTest;
60
61 const S = struct {
62 fn doTheTest() !void {
63 var x: Vector(3, bool) = [3]bool{ false, true, false };
64 var v: Vector(2, bool) = [2]bool{ true, false };
65 const mask: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 };
66 var res = @shuffle(bool, x, v, mask);
67 try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false }));
55 }68 }
56 };69 };
57 try S.doTheTest();70 try S.doTheTest();