diff options
| -rw-r--r-- | src/Sema.zig | 83 | ||||
| -rw-r--r-- | test/behavior.zig | 2 | ||||
| -rw-r--r-- | test/behavior/shuffle.zig | 59 |
3 files changed, 110 insertions, 34 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index f631bf36b5..fb45c9e9d3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13454,8 +13454,6 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 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 .elem_type = Type.@"i32", }); mask = try sema.coerce(block, mask_ty, mask, mask_src); + const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask); + return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(u32, mask_len)); +} + +fn analyzeShuffle( + sema: *Sema, + block: *Block, + src_node: i32, + elem_ty: Type, + a_arg: Air.Inst.Ref, + b_arg: Air.Inst.Ref, + mask: Value, + mask_len: u32, +) CompileError!Air.Inst.Ref { + const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node }; + const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node }; + const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node }; + var a = a_arg; + var b = b_arg; const res_ty = try Type.Tag.vector.create(sema.arena, .{ .len = mask_len, @@ -13485,7 +13502,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Undefined => null, else => return sema.fail(block, a_src, "expected vector or array with element type {}, found {}", .{ elem_ty, - sema.typeOf(mask), + sema.typeOf(a), }), }; 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 .Undefined => null, else => return sema.fail(block, b_src, "expected vector or array with element type {}, found {}", .{ elem_ty, - sema.typeOf(mask), + sema.typeOf(b), }), }; 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 .{ b_len, b_src, b_ty }, }; - const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask); var i: usize = 0; while (i < mask_len) : (i += 1) { var buf: Value.ElemValueBuffer = undefined; - const elem = mask_val.elemValueBuffer(i, &buf); + const elem = mask.elemValueBuffer(i, &buf); if (elem.isUndef()) continue; const int = elem.toSignedInt(); var unsigned: u32 = undefined; @@ -13555,14 +13571,61 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } } - // TODO at comptime + if (try sema.resolveMaybeUndefVal(block, a_src, a)) |a_val| { + if (try sema.resolveMaybeUndefVal(block, b_src, b)) |b_val| { + const values = try sema.arena.alloc(Value, mask_len); + + i = 0; + while (i < mask_len) : (i += 1) { + var buf: Value.ElemValueBuffer = undefined; + const mask_elem_val = mask.elemValueBuffer(i, &buf); + if (mask_elem_val.isUndef()) { + values[i] = Value.undef; + continue; + } + const int = mask_elem_val.toSignedInt(); + const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int); + if (int >= 0) { + values[i] = try a_val.elemValue(sema.arena, unsigned); + } else { + values[i] = try b_val.elemValue(sema.arena, unsigned); + } + } + const res_val = try Value.Tag.array.create(sema.arena, values); + return sema.addConstant(res_ty, res_val); + } + } + // All static analysis passed, and not comptime. + // For runtime codegen, vectors a and b must be the same length. Here we + // recursively @shuffle the smaller vector to append undefined elements + // to it up to the length of the longer vector. This recursion terminates + // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len. if (a_len != b_len) { - return sema.fail(block, mask_src, "TODO handle shuffle a_len != b_len", .{}); + const min_len = std.math.min(a_len, b_len); + const max_len = std.math.max(a_len, b_len); + + const expand_mask_values = try sema.arena.alloc(Value, max_len); + i = 0; + while (i < min_len) : (i += 1) { + expand_mask_values[i] = try Value.Tag.int_u64.create(sema.arena, i); + } + while (i < max_len) : (i += 1) { + expand_mask_values[i] = Value.negative_one; + } + const expand_mask = try Value.Tag.array.create(sema.arena, expand_mask_values); + + if (a_len < b_len) { + const undef = try sema.addConstUndef(a_ty); + a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask, @intCast(u32, max_len)); + } else { + const undef = try sema.addConstUndef(b_ty); + b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask, @intCast(u32, max_len)); + } } const mask_index = @intCast(u32, sema.air_values.items.len); - try sema.air_values.append(sema.gpa, mask_val); + try sema.air_values.append(sema.gpa, mask); return block.addInst(.{ .tag = .shuffle, .data = .{ .ty_pl = .{ @@ -13571,7 +13634,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .a = a, .b = b, .mask = mask_index, - .mask_len = @intCast(u32, mask_len), + .mask_len = mask_len, }), } }, }); diff --git a/test/behavior.zig b/test/behavior.zig index 871bd1c697..23a0134141 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -151,6 +151,7 @@ test { _ = @import("behavior/bugs/2114.zig"); _ = @import("behavior/bugs/3779.zig"); _ = @import("behavior/bugs/10147.zig"); + _ = @import("behavior/shuffle.zig"); _ = @import("behavior/union_with_members.zig"); if (builtin.zig_backend == .stage1) { @@ -169,7 +170,6 @@ test { _ = @import("behavior/bugs/7027.zig"); _ = @import("behavior/const_slice_child.zig"); _ = @import("behavior/select.zig"); - _ = @import("behavior/shuffle.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); _ = @import("behavior/typename.zig"); _ = @import("behavior/vector.zig"); diff --git a/test/behavior/shuffle.zig b/test/behavior/shuffle.zig index e0f973762e..ec9305c8b6 100644 --- a/test/behavior/shuffle.zig +++ b/test/behavior/shuffle.zig @@ -4,12 +4,12 @@ const mem = std.mem; const expect = std.testing.expect; const Vector = std.meta.Vector; -test "@shuffle" { +test "@shuffle int" { const S = struct { fn doTheTest() !void { var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; - const mask: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) }; + const mask = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) }; var res = @shuffle(i32, v, x, mask); try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 })); @@ -18,40 +18,53 @@ test "@shuffle" { try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 })); // Undefined - const mask2: Vector(4, i32) = [4]i32{ 3, 1, 2, 0 }; + const mask2 = [4]i32{ 3, 1, 2, 0 }; res = @shuffle(i32, v, undefined, mask2); try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 40, -2, 30, 2147483647 })); // Upcasting of b var v2: Vector(2, i32) = [2]i32{ 2147483647, undefined }; - const mask3: Vector(4, i32) = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 }; + const mask3 = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 }; res = @shuffle(i32, x, v2, mask3); try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 2147483647, 4 })); // Upcasting of a var v3: Vector(2, i32) = [2]i32{ 2147483647, -2 }; - const mask4: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) }; + const mask4 = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) }; res = @shuffle(i32, v3, x, mask4); try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, -2, 4 })); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} - // bool - { - var x2: Vector(4, bool) = [4]bool{ false, true, false, true }; - var v4: Vector(2, bool) = [2]bool{ true, false }; - const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; - var res2 = @shuffle(bool, x2, v4, mask5); - try expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false })); - } - - // TODO re-enable when LLVM codegen is fixed - // https://github.com/ziglang/zig/issues/3246 - if (false) { - var x2: Vector(3, bool) = [3]bool{ false, true, false }; - var v4: Vector(2, bool) = [2]bool{ true, false }; - const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; - var res2 = @shuffle(bool, x2, v4, mask5); - try expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false })); - } +test "@shuffle bool" { + const S = struct { + fn doTheTest() !void { + var x: Vector(4, bool) = [4]bool{ false, true, false, true }; + var v: Vector(2, bool) = [2]bool{ true, false }; + const mask = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; + var res = @shuffle(bool, x, v, mask); + try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false })); + } + }; + if (builtin.zig_backend == .stage1) try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "@shuffle bool" { + // TODO re-enable when LLVM codegen is fixed + // https://github.com/ziglang/zig/issues/3246 + if (true) return error.SkipZigTest; + + const S = struct { + fn doTheTest() !void { + var x: Vector(3, bool) = [3]bool{ false, true, false }; + var v: Vector(2, bool) = [2]bool{ true, false }; + const mask: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; + var res = @shuffle(bool, x, v, mask); + try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false })); } }; try S.doTheTest(); |
