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...@@ -3866,8 +3866,8 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
3866 const dummy_ptr = try trash_block.addTy(.alloc, mut_final_ptr_ty);3866 const dummy_ptr = try trash_block.addTy(.alloc, mut_final_ptr_ty);
3867 const empty_trash_count = trash_block.instructions.items.len;3867 const empty_trash_count = trash_block.instructions.items.len;
38683868
3869 for (placeholders, 0..) |bitcast_inst, i| {3869 for (peer_inst_list, placeholders) |peer_inst, placeholder_inst| {
3870 const sub_ptr_ty = sema.typeOf(Air.indexToRef(bitcast_inst));3870 const sub_ptr_ty = sema.typeOf(Air.indexToRef(placeholder_inst));
38713871
3872 if (mut_final_ptr_ty.eql(sub_ptr_ty, sema.mod)) {3872 if (mut_final_ptr_ty.eql(sub_ptr_ty, sema.mod)) {
3873 // New result location type is the same as the old one; nothing3873 // 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...@@ -3875,39 +3875,54 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
3875 continue;3875 continue;
3876 }3876 }
38773877
3878 var bitcast_block = block.makeSubBlock();3878 var replacement_block = block.makeSubBlock();
3879 defer bitcast_block.instructions.deinit(gpa);3879 defer replacement_block.instructions.deinit(gpa);
38803880
3881 trash_block.instructions.shrinkRetainingCapacity(empty_trash_count);3881 const result = switch (sema.air_instructions.items(.tag)[placeholder_inst]) {
3882 const sub_ptr = try sema.coerceResultPtr(&bitcast_block, src, ptr, dummy_ptr, peer_inst_list[i], &trash_block);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);
3885 // If only one instruction is produced then we can replace the bitcast3897 // If only one instruction is produced then we can replace the bitcast
3886 // placeholder instruction with this instruction; no need for an entire block.3898 // placeholder instruction with this instruction; no need for an entire block.
3887 if (bitcast_block.instructions.items.len == 1) {3899 if (replacement_block.instructions.items.len == 1) {
3888 const only_inst = bitcast_block.instructions.items[0];3900 const only_inst = replacement_block.instructions.items[0];
3889 sema.air_instructions.set(bitcast_inst, sema.air_instructions.get(only_inst));3901 sema.air_instructions.set(placeholder_inst, sema.air_instructions.get(only_inst));
3890 continue;3902 continue;
3891 }3903 }
38923904
3893 // Here we replace the placeholder bitcast instruction with a block3905 // Here we replace the placeholder bitcast instruction with a block
3894 // that does the coerce_result_ptr logic.3906 // that does the coerce_result_ptr logic.
3895 _ = try bitcast_block.addBr(bitcast_inst, sub_ptr);3907 _ = try replacement_block.addBr(placeholder_inst, result);
3896 const ty_inst = sema.air_instructions.items(.data)[bitcast_inst].ty_op.ty;3908 const ty_inst = if (result == .void_value)
3909 .void_type
3910 else
3911 sema.air_instructions.items(.data)[placeholder_inst].ty_op.ty;
3897 try sema.air_extra.ensureUnusedCapacity(3912 try sema.air_extra.ensureUnusedCapacity(
3898 gpa,3913 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,
3900 );3915 );
3901 sema.air_instructions.set(bitcast_inst, .{3916 sema.air_instructions.set(placeholder_inst, .{
3902 .tag = .block,3917 .tag = .block,
3903 .data = .{ .ty_pl = .{3918 .data = .{ .ty_pl = .{
3904 .ty = ty_inst,3919 .ty = ty_inst,
3905 .payload = sema.addExtraAssumeCapacity(Air.Block{3920 .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),
3907 }),3922 }),
3908 } },3923 } },
3909 });3924 });
3910 sema.air_extra.appendSliceAssumeCapacity(bitcast_block.instructions.items);3925 sema.air_extra.appendSliceAssumeCapacity(replacement_block.instructions.items);
3911 }3926 }
3912 },3927 },
3913 else => unreachable,3928 else => unreachable,
...@@ -4916,7 +4931,7 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -4916,7 +4931,7 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
4916 },4931 },
4917 .inferred_alloc => {4932 .inferred_alloc => {
4918 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;4933 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);
4920 },4935 },
4921 else => break :blk,4936 else => break :blk,
4922 }4937 }
...@@ -4945,7 +4960,7 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi...@@ -4945,7 +4960,7 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
4945 },4960 },
4946 .inferred_alloc => {4961 .inferred_alloc => {
4947 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;4962 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);
4949 },4964 },
4950 else => unreachable,4965 else => unreachable,
4951 }4966 }
...@@ -4954,27 +4969,19 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi...@@ -4954,27 +4969,19 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
4954fn storeToInferredAlloc(4969fn storeToInferredAlloc(
4955 sema: *Sema,4970 sema: *Sema,
4956 block: *Block,4971 block: *Block,
4957 src: LazySrcLoc,
4958 ptr: Air.Inst.Ref,4972 ptr: Air.Inst.Ref,
4959 operand: Air.Inst.Ref,4973 operand: Air.Inst.Ref,
4960 inferred_alloc: *Value.Payload.InferredAlloc,4974 inferred_alloc: *Value.Payload.InferredAlloc,
4961) CompileError!void {4975) CompileError!void {
4962 const operand_ty = sema.typeOf(operand);4976 // Create a store instruction as a placeholder. This will be replaced by a
4963 // Create a runtime bitcast instruction with exactly the type the pointer wants.4977 // proper store sequence once we know the stored type.
4964 const target = sema.mod.getTarget();4978 const dummy_store = try block.addBinOp(.store, ptr, operand);
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);
4971 // Add the stored instruction to the set we will use to resolve peer types4979 // Add the stored instruction to the set we will use to resolve peer types
4972 // for the inferred allocation.4980 // for the inferred allocation.
4973 try inferred_alloc.data.prongs.append(sema.arena, .{4981 try inferred_alloc.data.prongs.append(sema.arena, .{
4974 .stored_inst = operand,4982 .stored_inst = operand,
4975 .placeholder = Air.refToIndex(bitcasted_ptr).?,4983 .placeholder = Air.refToIndex(dummy_store).?,
4976 });4984 });
4977 return sema.storePtr2(block, src, bitcasted_ptr, src, operand, src, .bitcast);
4978}4985}
49794986
4980fn storeToInferredAllocComptime(4987fn storeToInferredAllocComptime(
src/codegen/c.zig+1-7
...@@ -3597,10 +3597,6 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3597,10 +3597,6 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3597 const ptr_ty = f.air.typeOf(bin_op.lhs);3597 const ptr_ty = f.air.typeOf(bin_op.lhs);
3598 const ptr_scalar_ty = ptr_ty.scalarType();3598 const ptr_scalar_ty = ptr_ty.scalarType();
3599 const ptr_info = ptr_scalar_ty.ptrInfo().data;3599 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
3605 const ptr_val = try f.resolveInst(bin_op.lhs);3601 const ptr_val = try f.resolveInst(bin_op.lhs);
3606 const src_ty = f.air.typeOf(bin_op.rhs);3602 const src_ty = f.air.typeOf(bin_op.rhs);
...@@ -4461,9 +4457,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4461,9 +4457,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
4461fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {4457fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
4462 const ty_op = f.air.instructions.items(.data)[inst].ty_op;4458 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
4463 const dest_ty = f.air.typeOfIndex(inst);4459 const dest_ty = f.air.typeOfIndex(inst);
4464 // No IgnoreComptime until Sema stops giving us garbage Air.4460 if (f.liveness.isUnused(inst)) {
4465 // https://github.com/ziglang/zig/issues/13410
4466 if (f.liveness.isUnused(inst) or !dest_ty.hasRuntimeBits()) {
4467 try reap(f, inst, &.{ty_op.operand});4461 try reap(f, inst, &.{ty_op.operand});
4468 return .none;4462 return .none;
4469 }4463 }
src/codegen/llvm.zig-1
...@@ -8216,7 +8216,6 @@ pub const FuncGen = struct {...@@ -8216,7 +8216,6 @@ pub const FuncGen = struct {
8216 const dest_ptr = try self.resolveInst(bin_op.lhs);8216 const dest_ptr = try self.resolveInst(bin_op.lhs);
8217 const ptr_ty = self.air.typeOf(bin_op.lhs);8217 const ptr_ty = self.air.typeOf(bin_op.lhs);
8218 const operand_ty = ptr_ty.childType();8218 const operand_ty = ptr_ty.childType();
8219 if (!operand_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return null;
82208219
8221 // TODO Sema should emit a different instruction when the store should8220 // TODO Sema should emit a different instruction when the store should
8222 // possibly do the safety 0xaa bytes for undefined.8221 // 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...@@ -140,12 +140,6 @@ test "if-else expression with runtime condition result location is inferred opti
140}140}
141141
142test "result location with inferred type ends up being pointer to comptime_int" {142test "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
149 var a: ?u32 = 1234;143 var a: ?u32 = 1234;
150 var b: u32 = 2000;144 var b: u32 = 2000;
151 var c = if (a) |d| blk: {145 var c = if (a) |d| blk: {
test/behavior/union.zig+16
...@@ -1540,3 +1540,19 @@ test "access the tag of a global tagged union" {...@@ -1540,3 +1540,19 @@ test "access the tag of a global tagged union" {
1540 };1540 };
1541 try expect(U.u == .a);1541 try expect(U.u == .a);
1542}1542}
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}