authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-31 16:23:01-04:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-04-02 18:05:44+03:00
logf4b411314ccf8e852d3febddc8b31ce1f533938b
tree41ff16cd4d9bec89e5f57be34e790017be93a4d0
parent878163e58813aef968900aa7495dacc5220eb941

Sema: defer stores to inferred allocs

This lets us generate the store with knowledge of the type to be stored. Therefore, we can avoid generating garbage Air with stores through pointers to comptime-only types which backends cannot lower. Closes #13410 Closes #15122

5 files changed, 54 insertions(+), 44 deletions(-)

src/Sema.zig+37-30
......@@ -3866,8 +3866,8 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
38663866 const dummy_ptr = try trash_block.addTy(.alloc, mut_final_ptr_ty);
38673867 const empty_trash_count = trash_block.instructions.items.len;
38683868
3869 for (placeholders, 0..) |bitcast_inst, i| {
3870 const sub_ptr_ty = sema.typeOf(Air.indexToRef(bitcast_inst));
3869 for (peer_inst_list, placeholders) |peer_inst, placeholder_inst| {
3870 const sub_ptr_ty = sema.typeOf(Air.indexToRef(placeholder_inst));
38713871
38723872 if (mut_final_ptr_ty.eql(sub_ptr_ty, sema.mod)) {
38733873 // New result location type is the same as the old one; nothing
......@@ -3875,39 +3875,54 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
38753875 continue;
38763876 }
38773877
3878 var bitcast_block = block.makeSubBlock();
3879 defer bitcast_block.instructions.deinit(gpa);
3878 var replacement_block = block.makeSubBlock();
3879 defer replacement_block.instructions.deinit(gpa);
38803880
3881 trash_block.instructions.shrinkRetainingCapacity(empty_trash_count);
3882 const sub_ptr = try sema.coerceResultPtr(&bitcast_block, src, ptr, dummy_ptr, peer_inst_list[i], &trash_block);
3881 const result = switch (sema.air_instructions.items(.tag)[placeholder_inst]) {
3882 .bitcast => result: {
3883 trash_block.instructions.shrinkRetainingCapacity(empty_trash_count);
3884 const sub_ptr = try sema.coerceResultPtr(&replacement_block, src, ptr, dummy_ptr, peer_inst, &trash_block);
3885
3886 assert(replacement_block.instructions.items.len > 0);
3887 break :result sub_ptr;
3888 },
3889 .store => result: {
3890 const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op;
3891 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast);
3892 break :result .void_value;
3893 },
3894 else => unreachable,
3895 };
38833896
3884 assert(bitcast_block.instructions.items.len > 0);
38853897 // If only one instruction is produced then we can replace the bitcast
38863898 // placeholder instruction with this instruction; no need for an entire block.
3887 if (bitcast_block.instructions.items.len == 1) {
3888 const only_inst = bitcast_block.instructions.items[0];
3889 sema.air_instructions.set(bitcast_inst, sema.air_instructions.get(only_inst));
3899 if (replacement_block.instructions.items.len == 1) {
3900 const only_inst = replacement_block.instructions.items[0];
3901 sema.air_instructions.set(placeholder_inst, sema.air_instructions.get(only_inst));
38903902 continue;
38913903 }
38923904
38933905 // Here we replace the placeholder bitcast instruction with a block
38943906 // that does the coerce_result_ptr logic.
3895 _ = try bitcast_block.addBr(bitcast_inst, sub_ptr);
3896 const ty_inst = sema.air_instructions.items(.data)[bitcast_inst].ty_op.ty;
3907 _ = try replacement_block.addBr(placeholder_inst, result);
3908 const ty_inst = if (result == .void_value)
3909 .void_type
3910 else
3911 sema.air_instructions.items(.data)[placeholder_inst].ty_op.ty;
38973912 try sema.air_extra.ensureUnusedCapacity(
38983913 gpa,
3899 @typeInfo(Air.Block).Struct.fields.len + bitcast_block.instructions.items.len,
3914 @typeInfo(Air.Block).Struct.fields.len + replacement_block.instructions.items.len,
39003915 );
3901 sema.air_instructions.set(bitcast_inst, .{
3916 sema.air_instructions.set(placeholder_inst, .{
39023917 .tag = .block,
39033918 .data = .{ .ty_pl = .{
39043919 .ty = ty_inst,
39053920 .payload = sema.addExtraAssumeCapacity(Air.Block{
3906 .body_len = @intCast(u32, bitcast_block.instructions.items.len),
3921 .body_len = @intCast(u32, replacement_block.instructions.items.len),
39073922 }),
39083923 } },
39093924 });
3910 sema.air_extra.appendSliceAssumeCapacity(bitcast_block.instructions.items);
3925 sema.air_extra.appendSliceAssumeCapacity(replacement_block.instructions.items);
39113926 }
39123927 },
39133928 else => unreachable,
......@@ -4916,7 +4931,7 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
49164931 },
49174932 .inferred_alloc => {
49184933 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
4919 return sema.storeToInferredAlloc(block, src, ptr, operand, inferred_alloc);
4934 return sema.storeToInferredAlloc(block, ptr, operand, inferred_alloc);
49204935 },
49214936 else => break :blk,
49224937 }
......@@ -4945,7 +4960,7 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
49454960 },
49464961 .inferred_alloc => {
49474962 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
4948 return sema.storeToInferredAlloc(block, src, ptr, operand, inferred_alloc);
4963 return sema.storeToInferredAlloc(block, ptr, operand, inferred_alloc);
49494964 },
49504965 else => unreachable,
49514966 }
......@@ -4954,27 +4969,19 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
49544969fn storeToInferredAlloc(
49554970 sema: *Sema,
49564971 block: *Block,
4957 src: LazySrcLoc,
49584972 ptr: Air.Inst.Ref,
49594973 operand: Air.Inst.Ref,
49604974 inferred_alloc: *Value.Payload.InferredAlloc,
49614975) CompileError!void {
4962 const operand_ty = sema.typeOf(operand);
4963 // Create a runtime bitcast instruction with exactly the type the pointer wants.
4964 const target = sema.mod.getTarget();
4965 const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
4966 .pointee_type = operand_ty,
4967 .@"align" = inferred_alloc.data.alignment,
4968 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
4969 });
4970 const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
4976 // Create a store instruction as a placeholder. This will be replaced by a
4977 // proper store sequence once we know the stored type.
4978 const dummy_store = try block.addBinOp(.store, ptr, operand);
49714979 // Add the stored instruction to the set we will use to resolve peer types
49724980 // for the inferred allocation.
49734981 try inferred_alloc.data.prongs.append(sema.arena, .{
49744982 .stored_inst = operand,
4975 .placeholder = Air.refToIndex(bitcasted_ptr).?,
4983 .placeholder = Air.refToIndex(dummy_store).?,
49764984 });
4977 return sema.storePtr2(block, src, bitcasted_ptr, src, operand, src, .bitcast);
49784985}
49794986
49804987fn storeToInferredAllocComptime(
src/codegen/c.zig+1-7
......@@ -3597,10 +3597,6 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
35973597 const ptr_ty = f.air.typeOf(bin_op.lhs);
35983598 const ptr_scalar_ty = ptr_ty.scalarType();
35993599 const ptr_info = ptr_scalar_ty.ptrInfo().data;
3600 if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) {
3601 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3602 return .none;
3603 }
36043600
36053601 const ptr_val = try f.resolveInst(bin_op.lhs);
36063602 const src_ty = f.air.typeOf(bin_op.rhs);
......@@ -4461,9 +4457,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
44614457fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
44624458 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
44634459 const dest_ty = f.air.typeOfIndex(inst);
4464 // No IgnoreComptime until Sema stops giving us garbage Air.
4465 // https://github.com/ziglang/zig/issues/13410
4466 if (f.liveness.isUnused(inst) or !dest_ty.hasRuntimeBits()) {
4460 if (f.liveness.isUnused(inst)) {
44674461 try reap(f, inst, &.{ty_op.operand});
44684462 return .none;
44694463 }
src/codegen/llvm.zig-1
......@@ -8216,7 +8216,6 @@ pub const FuncGen = struct {
82168216 const dest_ptr = try self.resolveInst(bin_op.lhs);
82178217 const ptr_ty = self.air.typeOf(bin_op.lhs);
82188218 const operand_ty = ptr_ty.childType();
8219 if (!operand_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return null;
82208219
82218220 // TODO Sema should emit a different instruction when the store should
82228221 // possibly do the safety 0xaa bytes for undefined.
test/behavior/if.zig-6
......@@ -140,12 +140,6 @@ test "if-else expression with runtime condition result location is inferred opti
140140}
141141
142142test "result location with inferred type ends up being pointer to comptime_int" {
143 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
144 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
145 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
146 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
147 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
148
149143 var a: ?u32 = 1234;
150144 var b: u32 = 2000;
151145 var c = if (a) |d| blk: {
test/behavior/union.zig+16
......@@ -1540,3 +1540,19 @@ test "access the tag of a global tagged union" {
15401540 };
15411541 try expect(U.u == .a);
15421542}
1543
1544test "coerce enum literal to union in result loc" {
1545 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1546
1547 const U = union(enum) {
1548 a,
1549 b: u8,
1550
1551 fn doTest(c: bool) !void {
1552 var u = if (c) .a else @This(){ .b = 0 };
1553 try expect(u == .a);
1554 }
1555 };
1556 try U.doTest(true);
1557 comptime try U.doTest(true);
1558}