authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-24 20:23:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-24 22:28:37-07:00
logadb746a7017ba6f91974d5e940bc8a8f64bb45f5
tree66cf6d6a07cea65dfb63e4602919b64fb2648695
parent5d30e8016d2e29d84efb27ec2a4f7be8a63a4f49

stage2: improved handling of store_to_block_ptr

* AstGen: remove the setBlockBodyEliding function. This is no longer needed after 63788b2a511eb87974065a052e2436b0c6202544. * Sema: store_to_block_ptr instruction is handled as store_to_inferred_ptr or store, as necessary.

6 files changed, 90 insertions(+), 87 deletions(-)

src/AstGen.zig+1-31
...@@ -1964,11 +1964,7 @@ fn labeledBlockExpr(...@@ -1964,11 +1964,7 @@ fn labeledBlockExpr(
1964 },1964 },
1965 .break_operand => {1965 .break_operand => {
1966 // All break operands are values that did not use the result location pointer.1966 // All break operands are values that did not use the result location pointer.
1967 if (strat.elide_store_to_block_ptr_instructions) {1967 try block_scope.setBlockBody(block_inst);
1968 try block_scope.setBlockBodyEliding(block_inst);
1969 } else {
1970 try block_scope.setBlockBody(block_inst);
1971 }
1972 const block_ref = indexToRef(block_inst);1968 const block_ref = indexToRef(block_inst);
1973 switch (rl) {1969 switch (rl) {
1974 .ref => return block_ref,1970 .ref => return block_ref,
...@@ -9734,32 +9730,6 @@ const GenZir = struct {...@@ -9734,32 +9730,6 @@ const GenZir = struct {
9734 gz.unstack();9730 gz.unstack();
9735 }9731 }
97369732
9737 /// Same as `setBlockBody` except we don't copy instructions which are
9738 /// `store_to_block_ptr` instructions with lhs set to .none.
9739 /// Assumes nothing stacked on `gz`. Unstacks `gz`.
9740 fn setBlockBodyEliding(gz: *GenZir, inst: Zir.Inst.Index) !void {
9741 const gpa = gz.astgen.gpa;
9742 const body = gz.instructionsSlice();
9743 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Block).Struct.fields.len + body.len);
9744 const zir_datas = gz.astgen.instructions.items(.data);
9745 const zir_tags = gz.astgen.instructions.items(.tag);
9746 const block_pl_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Block{
9747 .body_len = @intCast(u32, body.len),
9748 });
9749 zir_datas[inst].pl_node.payload_index = block_pl_index;
9750 for (body) |sub_inst| {
9751 if (zir_tags[sub_inst] == .store_to_block_ptr and
9752 zir_datas[sub_inst].bin.lhs == .none)
9753 {
9754 // Decrement `body_len`.
9755 gz.astgen.extra.items[block_pl_index] -= 1;
9756 continue;
9757 }
9758 gz.astgen.extra.appendAssumeCapacity(sub_inst);
9759 }
9760 gz.unstack();
9761 }
9762
9763 /// Supports `body_gz` stacked on `ret_gz` stacked on `gz`. Unstacks `body_gz` and `ret_gz`.9733 /// Supports `body_gz` stacked on `ret_gz` stacked on `gz`. Unstacks `body_gz` and `ret_gz`.
9764 fn addFunc(gz: *GenZir, args: struct {9734 fn addFunc(gz: *GenZir, args: struct {
9765 src_node: Ast.Node.Index,9735 src_node: Ast.Node.Index,
src/Sema.zig+79-45
...@@ -3247,22 +3247,31 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -3247,22 +3247,31 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
3247 defer tracy.end();3247 defer tracy.end();
32483248
3249 const bin_inst = sema.code.instructions.items(.data)[inst].bin;3249 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
3250 const ptr = sema.inst_map.get(@enumToInt(bin_inst.lhs) - @as(u32, Zir.Inst.Ref.typed_value_map.len)) orelse {3250 const ptr = sema.inst_map.get(Zir.refToIndex(bin_inst.lhs).?) orelse {
3251 // This is an elided instruction, but AstGen was unable to omit it.3251 // This is an elided instruction, but AstGen was unable to omit it.
3252 return;3252 return;
3253 };3253 };
3254 const value = sema.resolveInst(bin_inst.rhs);3254 const operand = sema.resolveInst(bin_inst.rhs);
3255 const ptr_ty = try Type.ptr(sema.arena, .{3255 const src: LazySrcLoc = sema.src;
3256 .pointee_type = sema.typeOf(value),3256 blk: {
3257 // TODO figure out which address space is appropriate here3257 const ptr_inst = Air.refToIndex(ptr) orelse break :blk;
3258 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),3258 if (sema.air_instructions.items(.tag)[ptr_inst] != .constant) break :blk;
3259 });3259 const air_datas = sema.air_instructions.items(.data);
3260 // TODO detect when this store should be done at compile-time. For example,3260 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
3261 // if expressions should force it when the condition is compile-time known.3261 switch (ptr_val.tag()) {
3262 const src: LazySrcLoc = .unneeded;3262 .inferred_alloc_comptime => {
3263 try sema.requireRuntimeBlock(block, src);3263 const iac = ptr_val.castTag(.inferred_alloc_comptime).?;
3264 const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);3264 return sema.storeToInferredAllocComptime(block, src, operand, iac);
3265 return sema.storePtr(block, src, bitcasted_ptr, value);3265 },
3266 .inferred_alloc => {
3267 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
3268 return sema.storeToInferredAlloc(block, src, ptr, operand, inferred_alloc);
3269 },
3270 else => break :blk,
3271 }
3272 }
3273
3274 return sema.storePtr(block, src, ptr, operand);
3266}3275}
32673276
3268fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {3277fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
...@@ -3273,46 +3282,71 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi...@@ -3273,46 +3282,71 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
3273 const bin_inst = sema.code.instructions.items(.data)[inst].bin;3282 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
3274 const ptr = sema.resolveInst(bin_inst.lhs);3283 const ptr = sema.resolveInst(bin_inst.lhs);
3275 const operand = sema.resolveInst(bin_inst.rhs);3284 const operand = sema.resolveInst(bin_inst.rhs);
3276 const operand_ty = sema.typeOf(operand);
3277 const ptr_inst = Air.refToIndex(ptr).?;3285 const ptr_inst = Air.refToIndex(ptr).?;
3278 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);3286 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
3279 const air_datas = sema.air_instructions.items(.data);3287 const air_datas = sema.air_instructions.items(.data);
3280 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];3288 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
32813289
3282 if (ptr_val.castTag(.inferred_alloc_comptime)) |iac| {3290 switch (ptr_val.tag()) {
3283 // There will be only one store_to_inferred_ptr because we are running at comptime.3291 .inferred_alloc_comptime => {
3284 // The alloc will turn into a Decl.3292 const iac = ptr_val.castTag(.inferred_alloc_comptime).?;
3285 if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| {3293 return sema.storeToInferredAllocComptime(block, src, operand, iac);
3286 if (operand_val.tag() == .variable) {3294 },
3287 return sema.failWithNeededComptime(block, src);3295 .inferred_alloc => {
3288 }3296 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
3289 var anon_decl = try block.startAnonDecl(src);3297 return sema.storeToInferredAlloc(block, src, ptr, operand, inferred_alloc);
3290 defer anon_decl.deinit();3298 },
3291 iac.data.decl = try anon_decl.finish(3299 else => unreachable,
3292 try operand_ty.copy(anon_decl.arena()),
3293 try operand_val.copy(anon_decl.arena()),
3294 );
3295 // TODO set the alignment on the decl
3296 return;
3297 } else {
3298 return sema.failWithNeededComptime(block, src);
3299 }
3300 }3300 }
3301}
33013302
3302 if (ptr_val.castTag(.inferred_alloc)) |inferred_alloc| {3303fn storeToInferredAlloc(
3303 // Add the stored instruction to the set we will use to resolve peer types3304 sema: *Sema,
3304 // for the inferred allocation.3305 block: *Block,
3305 try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);3306 src: LazySrcLoc,
3306 // Create a runtime bitcast instruction with exactly the type the pointer wants.3307 ptr: Air.Inst.Ref,
3307 const ptr_ty = try Type.ptr(sema.arena, .{3308 operand: Air.Inst.Ref,
3308 .pointee_type = operand_ty,3309 inferred_alloc: *Value.Payload.InferredAlloc,
3309 .@"align" = inferred_alloc.data.alignment,3310) CompileError!void {
3310 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),3311 const operand_ty = sema.typeOf(operand);
3311 });3312 // Add the stored instruction to the set we will use to resolve peer types
3312 const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);3313 // for the inferred allocation.
3313 return sema.storePtr(block, src, bitcasted_ptr, operand);3314 try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);
3315 // Create a runtime bitcast instruction with exactly the type the pointer wants.
3316 const ptr_ty = try Type.ptr(sema.arena, .{
3317 .pointee_type = operand_ty,
3318 .@"align" = inferred_alloc.data.alignment,
3319 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
3320 });
3321 const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
3322 return sema.storePtr(block, src, bitcasted_ptr, operand);
3323}
3324
3325fn storeToInferredAllocComptime(
3326 sema: *Sema,
3327 block: *Block,
3328 src: LazySrcLoc,
3329 operand: Air.Inst.Ref,
3330 iac: *Value.Payload.InferredAllocComptime,
3331) CompileError!void {
3332 const operand_ty = sema.typeOf(operand);
3333 // There will be only one store_to_inferred_ptr because we are running at comptime.
3334 // The alloc will turn into a Decl.
3335 if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| {
3336 if (operand_val.tag() == .variable) {
3337 return sema.failWithNeededComptime(block, src);
3338 }
3339 var anon_decl = try block.startAnonDecl(src);
3340 defer anon_decl.deinit();
3341 iac.data.decl = try anon_decl.finish(
3342 try operand_ty.copy(anon_decl.arena()),
3343 try operand_val.copy(anon_decl.arena()),
3344 );
3345 // TODO set the alignment on the decl
3346 return;
3347 } else {
3348 return sema.failWithNeededComptime(block, src);
3314 }3349 }
3315 unreachable;
3316}3350}
33173351
3318fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {3352fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
src/Zir.zig+8-5
...@@ -518,11 +518,14 @@ pub const Inst = struct {...@@ -518,11 +518,14 @@ pub const Inst = struct {
518 /// Same as `store` except provides a source location.518 /// Same as `store` except provides a source location.
519 /// Uses the `pl_node` union field. Payload is `Bin`.519 /// Uses the `pl_node` union field. Payload is `Bin`.
520 store_node,520 store_node,
521 /// Same as `store` but the type of the value being stored will be used to infer521 /// This instruction is not really supposed to be emitted from AstGen; nevetheless it
522 /// the block type. The LHS is the pointer to store to.522 /// is sometimes emitted due to deficiencies in AstGen. When Sema sees this instruction,
523 /// Uses the `bin` union field.523 /// it must clean up after AstGen's mess by looking at various context clues and
524 /// If the pointer is none, it means this instruction has been elided in524 /// then treating it as one of the following:
525 /// AstGen, but AstGen was unable to actually omit it from the ZIR code.525 /// * no-op
526 /// * store_to_inferred_ptr
527 /// * store
528 /// Uses the `bin` union field with LHS as the pointer to store to.
526 store_to_block_ptr,529 store_to_block_ptr,
527 /// Same as `store` but the type of the value being stored will be used to infer530 /// Same as `store` but the type of the value being stored will be used to infer
528 /// the pointer type.531 /// the pointer type.
test/behavior.zig+1-1
...@@ -124,6 +124,7 @@ test {...@@ -124,6 +124,7 @@ test {
124 _ = @import("behavior/bugs/421.zig");124 _ = @import("behavior/bugs/421.zig");
125 _ = @import("behavior/bugs/726.zig");125 _ = @import("behavior/bugs/726.zig");
126 _ = @import("behavior/bugs/1421.zig");126 _ = @import("behavior/bugs/1421.zig");
127 _ = @import("behavior/bugs/1442.zig");
127 _ = @import("behavior/bugs/2114.zig");128 _ = @import("behavior/bugs/2114.zig");
128 _ = @import("behavior/bugs/3742.zig");129 _ = @import("behavior/bugs/3742.zig");
129 _ = @import("behavior/struct_contains_null_ptr_itself.zig");130 _ = @import("behavior/struct_contains_null_ptr_itself.zig");
...@@ -144,7 +145,6 @@ test {...@@ -144,7 +145,6 @@ test {
144 _ = @import("behavior/bugs/828.zig");145 _ = @import("behavior/bugs/828.zig");
145 _ = @import("behavior/bugs/920.zig");146 _ = @import("behavior/bugs/920.zig");
146 _ = @import("behavior/bugs/1120.zig");147 _ = @import("behavior/bugs/1120.zig");
147 _ = @import("behavior/bugs/1442.zig");
148 _ = @import("behavior/bugs/1607.zig");148 _ = @import("behavior/bugs/1607.zig");
149 _ = @import("behavior/bugs/1851.zig");149 _ = @import("behavior/bugs/1851.zig");
150 _ = @import("behavior/bugs/3384.zig");150 _ = @import("behavior/bugs/3384.zig");
test/behavior/bugs/1442.zig-2
...@@ -7,8 +7,6 @@ const Union = union(enum) {...@@ -7,8 +7,6 @@ const Union = union(enum) {
7};7};
88
9test "const error union field alignment" {9test "const error union field alignment" {
10 if (builtin.zig_backend != .stage1) return error.SkipZigTest;
11
12 var union_or_err: anyerror!Union = Union{ .Color = 1234 };10 var union_or_err: anyerror!Union = Union{ .Color = 1234 };
13 try std.testing.expect((union_or_err catch unreachable).Color == 1234);11 try std.testing.expect((union_or_err catch unreachable).Color == 1234);
14}12}
test/behavior/eval.zig+1-3
...@@ -495,7 +495,7 @@ test "@tagName of @typeInfo" {...@@ -495,7 +495,7 @@ test "@tagName of @typeInfo" {
495}495}
496496
497test "static eval list init" {497test "static eval list init" {
498 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO498 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
499499
500 try expect(static_vec3.data[2] == 1.0);500 try expect(static_vec3.data[2] == 1.0);
501 try expect(vec3(0.0, 0.0, 3.0).data[2] == 3.0);501 try expect(vec3(0.0, 0.0, 3.0).data[2] == 3.0);
...@@ -511,8 +511,6 @@ pub fn vec3(x: f32, y: f32, z: f32) Vec3 {...@@ -511,8 +511,6 @@ pub fn vec3(x: f32, y: f32, z: f32) Vec3 {
511}511}
512512
513test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" {513test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" {
514 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
515
516 var runtime = [1]i32{3};514 var runtime = [1]i32{3};
517 comptime var i: usize = 0;515 comptime var i: usize = 0;
518 inline while (i < 2) : (i += 1) {516 inline while (i < 2) : (i += 1) {