authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-02-24 23:45:49+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-24 18:32:08-05:00
log63788b2a511eb87974065a052e2436b0c6202544
treeef11ed08dc3b7328ba5de9c778a1afe9e0d48744
parent52a2aa11e2768a5d7881377ddb7a053d81e4cff4

stage2: change how stale `store_to_block_ptr`s are detected

Instead of explicitly setting lhs to .none, check if the lhs instruction was analyzed. This simpler approach also handles stores from nested blocks correctly.

4 files changed, 19 insertions(+), 32 deletions(-)

src/AstGen.zig-27
......@@ -1760,19 +1760,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
17601760 .block_ptr => {
17611761 const br = try parent_gz.addBreak(break_tag, block_inst, operand);
17621762 try block_gz.labeled_breaks.append(astgen.gpa, br);
1763
1764 // if list grew as much as rvalue_rl_count, then a break
1765 // inside operand already saved the store_to_block_ptr
1766 const have_store_to_block = block_gz.rvalue_rl_count >
1767 block_gz.labeled_store_to_block_ptr_list.items.len;
1768 if (have_store_to_block) {
1769 const zir_tags = parent_gz.astgen.instructions.items(.tag);
1770 const zir_datas = parent_gz.astgen.instructions.items(.data);
1771 const store_inst = @intCast(u32, zir_tags.len - 2);
1772 assert(zir_tags[store_inst] == .store_to_block_ptr);
1773 assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr);
1774 try block_gz.labeled_store_to_block_ptr_list.append(astgen.gpa, store_inst);
1775 }
17761763 },
17771764 .ptr => {
17781765 // In this case we don't have any mechanism to intercept it;
......@@ -1955,7 +1942,6 @@ fn labeledBlockExpr(
19551942 block_scope.setBreakResultLoc(rl);
19561943 defer block_scope.unstack();
19571944 defer block_scope.labeled_breaks.deinit(astgen.gpa);
1958 defer block_scope.labeled_store_to_block_ptr_list.deinit(astgen.gpa);
19591945
19601946 try blockExprStmts(&block_scope, &block_scope.base, statements);
19611947
......@@ -1963,9 +1949,7 @@ fn labeledBlockExpr(
19631949 try astgen.appendErrorTok(label_token, "unused block label", .{});
19641950 }
19651951
1966 const zir_tags = gz.astgen.instructions.items(.tag);
19671952 const zir_datas = gz.astgen.instructions.items(.data);
1968
19691953 const strat = rl.strategy(&block_scope);
19701954 switch (strat.tag) {
19711955 .break_void => {
......@@ -1981,11 +1965,6 @@ fn labeledBlockExpr(
19811965 .break_operand => {
19821966 // All break operands are values that did not use the result location pointer.
19831967 if (strat.elide_store_to_block_ptr_instructions) {
1984 for (block_scope.labeled_store_to_block_ptr_list.items) |inst| {
1985 // Mark as elided for removal below.
1986 assert(zir_tags[inst] == .store_to_block_ptr);
1987 zir_datas[inst].bin.lhs = .none;
1988 }
19891968 try block_scope.setBlockBodyEliding(block_inst);
19901969 } else {
19911970 try block_scope.setBlockBody(block_inst);
......@@ -5322,7 +5301,6 @@ fn whileExpr(
53225301 loop_scope.setBreakResultLoc(rl);
53235302 defer loop_scope.unstack();
53245303 defer loop_scope.labeled_breaks.deinit(astgen.gpa);
5325 defer loop_scope.labeled_store_to_block_ptr_list.deinit(astgen.gpa);
53265304
53275305 var continue_scope = parent_gz.makeSubBlock(&loop_scope.base);
53285306 defer continue_scope.unstack();
......@@ -5573,7 +5551,6 @@ fn forExpr(
55735551 loop_scope.setBreakResultLoc(rl);
55745552 defer loop_scope.unstack();
55755553 defer loop_scope.labeled_breaks.deinit(astgen.gpa);
5576 defer loop_scope.labeled_store_to_block_ptr_list.deinit(astgen.gpa);
55775554
55785555 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
55795556 defer cond_scope.unstack();
......@@ -9553,10 +9530,6 @@ const GenZir = struct {
95539530 /// Tracks `break :foo bar` instructions so they can possibly be elided later if
95549531 /// the labeled block ends up not needing a result location pointer.
95559532 labeled_breaks: ArrayListUnmanaged(Zir.Inst.Index) = .{},
9556 /// Tracks `store_to_block_ptr` instructions that correspond to break instructions
9557 /// so they can possibly be elided later if the labeled block ends up not needing
9558 /// a result location pointer.
9559 labeled_store_to_block_ptr_list: ArrayListUnmanaged(Zir.Inst.Index) = .{},
95609533
95619534 suspend_node: Ast.Node.Index = 0,
95629535 nosuspend_node: Ast.Node.Index = 0,
src/Sema.zig+3-5
......@@ -3247,12 +3247,10 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
32473247 defer tracy.end();
32483248
32493249 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
3250 if (bin_inst.lhs == .none) {
3251 // This is an elided instruction, but AstGen was not smart enough
3252 // to omit it.
3250 const ptr = sema.inst_map.get(@enumToInt(bin_inst.lhs) - @as(u32, Zir.Inst.Ref.typed_value_map.len)) orelse {
3251 // This is an elided instruction, but AstGen was unable to omit it.
32533252 return;
3254 }
3255 const ptr = sema.resolveInst(bin_inst.lhs);
3253 };
32563254 const value = sema.resolveInst(bin_inst.rhs);
32573255 const ptr_ty = try Type.ptr(sema.arena, .{
32583256 .pointee_type = sema.typeOf(value),
test/behavior.zig+1
......@@ -103,6 +103,7 @@ test {
103103 {
104104 // Tests that pass for stage1, llvm backend, C backend
105105 _ = @import("behavior/bugs/9584.zig");
106 _ = @import("behavior/bugs/10970.zig");
106107 _ = @import("behavior/cast_int.zig");
107108 _ = @import("behavior/eval.zig");
108109 _ = @import("behavior/int128.zig");
test/behavior/bugs/10970.zig created+15
......@@ -0,0 +1,15 @@
1const builtin = @import("builtin");
2
3fn retOpt() ?u32 {
4 return null;
5}
6test {
7 var cond = true;
8 const opt = while (cond) {
9 if (retOpt()) |opt| {
10 break opt;
11 }
12 break 1;
13 } else 2;
14 _ = opt;
15}