authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-10-08 23:37:16+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-10-10 11:22:49+01:00
logc96f9a017a863e1f8cb610b7caba60ce93ab5616
tree501d159d47c55437222a0c771563fb6a0bbdf784
parent072e062443e441f63dc1694766dd716c15a287c1

Sema: implement @splat for arrays

Resolves: #20433

7 files changed, 138 insertions(+), 32 deletions(-)

lib/std/zig/AstGen.zig+2-2
...@@ -2716,7 +2716,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2716,7 +2716,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2716 .array_type_sentinel,2716 .array_type_sentinel,
2717 .elem_type,2717 .elem_type,
2718 .indexable_ptr_elem_type,2718 .indexable_ptr_elem_type,
2719 .vector_elem_type,2719 .vec_arr_elem_type,
2720 .vector_type,2720 .vector_type,
2721 .indexable_ptr_len,2721 .indexable_ptr_len,
2722 .anyframe_type,2722 .anyframe_type,
...@@ -9529,7 +9529,7 @@ fn builtinCall(...@@ -9529,7 +9529,7 @@ fn builtinCall(
95299529
9530 .splat => {9530 .splat => {
9531 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);9531 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
9532 const elem_type = try gz.addUnNode(.vector_elem_type, result_type, node);9532 const elem_type = try gz.addUnNode(.vec_arr_elem_type, result_type, node);
9533 const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]);9533 const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]);
9534 const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{9534 const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
9535 .lhs = result_type,9535 .lhs = result_type,
lib/std/zig/Zir.zig+6-6
...@@ -247,9 +247,9 @@ pub const Inst = struct {...@@ -247,9 +247,9 @@ pub const Inst = struct {
247 /// element type. Emits a compile error if the type is not an indexable pointer.247 /// element type. Emits a compile error if the type is not an indexable pointer.
248 /// Uses the `un_node` field.248 /// Uses the `un_node` field.
249 indexable_ptr_elem_type,249 indexable_ptr_elem_type,
250 /// Given a vector type, returns its element type.250 /// Given a vector or array type, returns its element type.
251 /// Uses the `un_node` field.251 /// Uses the `un_node` field.
252 vector_elem_type,252 vec_arr_elem_type,
253 /// Given a pointer to an indexable object, returns the len property. This is253 /// Given a pointer to an indexable object, returns the len property. This is
254 /// used by for loops. This instruction also emits a for-loop specific compile254 /// used by for loops. This instruction also emits a for-loop specific compile
255 /// error if the indexable object is not indexable.255 /// error if the indexable object is not indexable.
...@@ -1065,7 +1065,7 @@ pub const Inst = struct {...@@ -1065,7 +1065,7 @@ pub const Inst = struct {
1065 .vector_type,1065 .vector_type,
1066 .elem_type,1066 .elem_type,
1067 .indexable_ptr_elem_type,1067 .indexable_ptr_elem_type,
1068 .vector_elem_type,1068 .vec_arr_elem_type,
1069 .indexable_ptr_len,1069 .indexable_ptr_len,
1070 .anyframe_type,1070 .anyframe_type,
1071 .as_node,1071 .as_node,
...@@ -1375,7 +1375,7 @@ pub const Inst = struct {...@@ -1375,7 +1375,7 @@ pub const Inst = struct {
1375 .vector_type,1375 .vector_type,
1376 .elem_type,1376 .elem_type,
1377 .indexable_ptr_elem_type,1377 .indexable_ptr_elem_type,
1378 .vector_elem_type,1378 .vec_arr_elem_type,
1379 .indexable_ptr_len,1379 .indexable_ptr_len,
1380 .anyframe_type,1380 .anyframe_type,
1381 .as_node,1381 .as_node,
...@@ -1607,7 +1607,7 @@ pub const Inst = struct {...@@ -1607,7 +1607,7 @@ pub const Inst = struct {
1607 .vector_type = .pl_node,1607 .vector_type = .pl_node,
1608 .elem_type = .un_node,1608 .elem_type = .un_node,
1609 .indexable_ptr_elem_type = .un_node,1609 .indexable_ptr_elem_type = .un_node,
1610 .vector_elem_type = .un_node,1610 .vec_arr_elem_type = .un_node,
1611 .indexable_ptr_len = .un_node,1611 .indexable_ptr_len = .un_node,
1612 .anyframe_type = .un_node,1612 .anyframe_type = .un_node,
1613 .as_node = .pl_node,1613 .as_node = .pl_node,
...@@ -3781,7 +3781,7 @@ fn findDeclsInner(...@@ -3781,7 +3781,7 @@ fn findDeclsInner(
3781 .vector_type,3781 .vector_type,
3782 .elem_type,3782 .elem_type,
3783 .indexable_ptr_elem_type,3783 .indexable_ptr_elem_type,
3784 .vector_elem_type,3784 .vec_arr_elem_type,
3785 .indexable_ptr_len,3785 .indexable_ptr_len,
3786 .anyframe_type,3786 .anyframe_type,
3787 .as_node,3787 .as_node,
src/Sema.zig+55-14
...@@ -1087,7 +1087,7 @@ fn analyzeBodyInner(...@@ -1087,7 +1087,7 @@ fn analyzeBodyInner(
1087 .elem_val_imm => try sema.zirElemValImm(block, inst),1087 .elem_val_imm => try sema.zirElemValImm(block, inst),
1088 .elem_type => try sema.zirElemType(block, inst),1088 .elem_type => try sema.zirElemType(block, inst),
1089 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),1089 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
1090 .vector_elem_type => try sema.zirVectorElemType(block, inst),1090 .vec_arr_elem_type => try sema.zirVecArrElemType(block, inst),
1091 .enum_literal => try sema.zirEnumLiteral(block, inst),1091 .enum_literal => try sema.zirEnumLiteral(block, inst),
1092 .decl_literal => try sema.zirDeclLiteral(block, inst, true),1092 .decl_literal => try sema.zirDeclLiteral(block, inst, true),
1093 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false),1093 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false),
...@@ -2046,7 +2046,7 @@ fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoi...@@ -2046,7 +2046,7 @@ fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoi
2046 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;2046 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
2047 cur = bin.lhs;2047 cur = bin.lhs;
2048 },2048 },
2049 .indexable_ptr_elem_type, .vector_elem_type => {2049 .indexable_ptr_elem_type, .vec_arr_elem_type => {
2050 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;2050 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
2051 cur = un_node.operand;2051 cur = un_node.operand;
2052 },2052 },
...@@ -8603,7 +8603,7 @@ fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -8603,7 +8603,7 @@ fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
8603 return Air.internedToRef(elem_ty.toIntern());8603 return Air.internedToRef(elem_ty.toIntern());
8604}8604}
86058605
8606fn zirVectorElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8606fn zirVecArrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8607 const pt = sema.pt;8607 const pt = sema.pt;
8608 const zcu = pt.zcu;8608 const zcu = pt.zcu;
8609 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;8609 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
...@@ -8615,8 +8615,9 @@ fn zirVectorElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -8615,8 +8615,9 @@ fn zirVectorElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
8615 error.GenericPoison => return .generic_poison_type,8615 error.GenericPoison => return .generic_poison_type,
8616 else => |e| return e,8616 else => |e| return e,
8617 };8617 };
8618 if (!vec_ty.isVector(zcu)) {8618 switch (vec_ty.zigTypeTag(zcu)) {
8619 return sema.fail(block, block.nodeOffset(un_node.src_node), "expected vector type, found '{}'", .{vec_ty.fmt(pt)});8619 .array, .vector => {},
8620 else => return sema.fail(block, block.nodeOffset(un_node.src_node), "expected array or vector type, found '{}'", .{vec_ty.fmt(pt)}),
8620 }8621 }
8621 return Air.internedToRef(vec_ty.childType(zcu).toIntern());8622 return Air.internedToRef(vec_ty.childType(zcu).toIntern());
8622}8623}
...@@ -24804,26 +24805,66 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -24804,26 +24805,66 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
24804 const scalar_src = block.builtinCallArgSrc(inst_data.src_node, 0);24805 const scalar_src = block.builtinCallArgSrc(inst_data.src_node, 0);
24805 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat");24806 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat");
2480624807
24807 if (!dest_ty.isVector(zcu)) return sema.fail(block, src, "expected vector type, found '{}'", .{dest_ty.fmt(pt)});24808 switch (dest_ty.zigTypeTag(zcu)) {
24809 .array, .vector => {},
24810 else => return sema.fail(block, src, "expected array or vector type, found '{}'", .{dest_ty.fmt(pt)}),
24811 }
2480824812
24809 if (!dest_ty.hasRuntimeBits(zcu)) {24813 const operand = try sema.resolveInst(extra.rhs);
24814 const scalar_ty = dest_ty.childType(zcu);
24815 const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src);
24816
24817 const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu));
24818
24819 // `len == 0` because `[0:s]T` always has a comptime-known splat.
24820 if (!dest_ty.hasRuntimeBits(zcu) or len == 0) {
24810 const empty_aggregate = try pt.intern(.{ .aggregate = .{24821 const empty_aggregate = try pt.intern(.{ .aggregate = .{
24811 .ty = dest_ty.toIntern(),24822 .ty = dest_ty.toIntern(),
24812 .storage = .{ .elems = &[_]InternPool.Index{} },24823 .storage = .{ .elems = &.{} },
24813 } });24824 } });
24814 return Air.internedToRef(empty_aggregate);24825 return Air.internedToRef(empty_aggregate);
24815 }24826 }
2481624827
24817 const operand = try sema.resolveInst(extra.rhs);24828 const maybe_sentinel = dest_ty.sentinel(zcu);
24818 const scalar_ty = dest_ty.childType(zcu);24829
24819 const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src);
24820 if (try sema.resolveValue(scalar)) |scalar_val| {24830 if (try sema.resolveValue(scalar)) |scalar_val| {
24821 if (scalar_val.isUndef(zcu)) return pt.undefRef(dest_ty);24831 if (scalar_val.isUndef(zcu) and maybe_sentinel == null) {
24822 return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern());24832 return pt.undefRef(dest_ty);
24833 }
24834 // TODO: I didn't want to put `.aggregate` on a separate line here; `zig fmt` bugs have forced my hand
24835 return Air.internedToRef(try pt.intern(.{
24836 .aggregate = .{
24837 .ty = dest_ty.toIntern(),
24838 .storage = s: {
24839 full: {
24840 if (dest_ty.zigTypeTag(zcu) == .vector) break :full;
24841 const sentinel = maybe_sentinel orelse break :full;
24842 if (sentinel.toIntern() == scalar_val.toIntern()) break :full;
24843 // This is a array with non-zero length and a sentinel which does not match the element.
24844 // We have to use the full `elems` representation.
24845 const elems = try sema.arena.alloc(InternPool.Index, len + 1);
24846 @memset(elems[0..len], scalar_val.toIntern());
24847 elems[len] = sentinel.toIntern();
24848 break :s .{ .elems = elems };
24849 }
24850 break :s .{ .repeated_elem = scalar_val.toIntern() };
24851 },
24852 },
24853 }));
24823 }24854 }
2482424855
24825 try sema.requireRuntimeBlock(block, src, scalar_src);24856 try sema.requireRuntimeBlock(block, src, scalar_src);
24826 return block.addTyOp(.splat, dest_ty, scalar);24857
24858 switch (dest_ty.zigTypeTag(zcu)) {
24859 .array => {
24860 const elems = try sema.arena.alloc(Air.Inst.Ref, len + @intFromBool(maybe_sentinel != null));
24861 @memset(elems[0..len], scalar);
24862 if (maybe_sentinel) |s| elems[len] = Air.internedToRef(s.toIntern());
24863 return block.addAggregateInit(dest_ty, elems);
24864 },
24865 .vector => return block.addTyOp(.splat, dest_ty, scalar),
24866 else => unreachable,
24867 }
24827}24868}
2482824869
24829fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {24870fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/print_zir.zig+1-1
...@@ -203,7 +203,7 @@ const Writer = struct {...@@ -203,7 +203,7 @@ const Writer = struct {
203 .alloc_comptime_mut,203 .alloc_comptime_mut,
204 .elem_type,204 .elem_type,
205 .indexable_ptr_elem_type,205 .indexable_ptr_elem_type,
206 .vector_elem_type,206 .vec_arr_elem_type,
207 .indexable_ptr_len,207 .indexable_ptr_len,
208 .anyframe_type,208 .anyframe_type,
209 .bit_not,209 .bit_not,
test/behavior/array.zig+67
...@@ -1021,3 +1021,70 @@ test "runtime index of array of zero-bit values" {...@@ -1021,3 +1021,70 @@ test "runtime index of array of zero-bit values" {
1021 try std.testing.expect(result.index == 0);1021 try std.testing.expect(result.index == 0);
1022 try std.testing.expect(result.value == {});1022 try std.testing.expect(result.value == {});
1023}1023}
1024
1025test "@splat array" {
1026 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1027 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1028 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1029 const S = struct {
1030 fn doTheTest(comptime T: type, x: T) !void {
1031 const arr: [10]T = @splat(x);
1032 for (arr) |elem| {
1033 try expectEqual(x, elem);
1034 }
1035 }
1036 };
1037
1038 try S.doTheTest(u32, 123);
1039 try comptime S.doTheTest(u32, 123);
1040
1041 const Foo = struct { x: u8 };
1042 try S.doTheTest(Foo, .{ .x = 10 });
1043 try comptime S.doTheTest(Foo, .{ .x = 10 });
1044}
1045
1046test "@splat array with sentinel" {
1047 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1048 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1049 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1050 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1051 const S = struct {
1052 fn doTheTest(comptime T: type, x: T, comptime s: T) !void {
1053 const arr: [10:s]T = @splat(x);
1054 for (arr) |elem| {
1055 try expectEqual(x, elem);
1056 }
1057 const ptr: [*]const T = &arr;
1058 try expectEqual(s, ptr[10]); // sentinel correct
1059 }
1060 };
1061
1062 try S.doTheTest(u32, 100, 42);
1063 try comptime S.doTheTest(u32, 100, 42);
1064
1065 try S.doTheTest(?*anyopaque, @ptrFromInt(0x1000), null);
1066 try comptime S.doTheTest(?*anyopaque, @ptrFromInt(0x1000), null);
1067}
1068
1069test "@splat zero-length array" {
1070 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1071 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1072 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1073 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1074 const S = struct {
1075 fn doTheTest(comptime T: type, comptime s: T) !void {
1076 var runtime_undef: T = undefined;
1077 runtime_undef = undefined;
1078 // The array should be comptime-known despite the `@splat` operand being runtime-known.
1079 const arr: [0:s]T = @splat(runtime_undef);
1080 const ptr: [*]const T = &arr;
1081 comptime assert(ptr[0] == s);
1082 }
1083 };
1084
1085 try S.doTheTest(u32, 42);
1086 try comptime S.doTheTest(u32, 42);
1087
1088 try S.doTheTest(?*anyopaque, null);
1089 try comptime S.doTheTest(?*anyopaque, null);
1090}
test/cases/compile_errors/splat_bad_result_type.zig created+7
...@@ -0,0 +1,7 @@
1export fn f() void {
2 _ = @as(u32, @splat(5));
3}
4
5// error
6//
7// :2:18: error: expected array or vector type, found 'u32'
test/cases/compile_errors/splat_result_type_non_vector.zig deleted-9
...@@ -1,9 +0,0 @@
1export fn f() void {
2 _ = @as(u32, @splat(5));
3}
4
5// error
6// backend=stage2
7// target=native
8//
9// :2:18: error: expected vector type, found 'u32'