From 59447e53056d8fb6682b79accccffa176d0b44d1 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 24 Feb 2024 04:29:42 +0000 Subject: [PATCH] compiler: decide dbg_var scoping based on AIR blocks This commit eliminates the `dbg_block_{begin,end}` instructions from both ZIR and AIR. Instead, lexical scoping of `dbg_var_{ptr,val}` instructions is decided based on the AIR block they exist within. This is a much more robust system, and also results in a huge drop in ZIR bytes - around 7% for Sema.zig. This required some enhancements to Sema to prevent elision of blocks when they are required for debug variable scoping. This can be observed by looking at the AIR for the following simple test program with and without `-fstrip`: ```zig export fn f() void { { var a: u32 = 0; _ = &a; } { var a: u32 = 0; _ = &a; } } ``` When `-fstrip` is passed, no AIR blocks are generated. When `-fno-strip` is passed, the ZIR blocks are lowered to true AIR blocks to give correct lexical scoping to the debug vars. The changes here incidentally reolve #19060. A corresponding behavior test has been added. Resolves: #19060 --- src/Air.zig | 8 -- src/AstGen.zig | 51 ------- src/Liveness.zig | 4 - src/Liveness/Verify.zig | 2 - src/Sema.zig | 270 +++++++++++++++++++---------------- src/Zir.zig | 10 -- src/arch/aarch64/CodeGen.zig | 10 +- src/arch/arm/CodeGen.zig | 10 +- src/arch/riscv64/CodeGen.zig | 10 +- src/arch/sparc64/CodeGen.zig | 10 +- src/arch/wasm/CodeGen.zig | 2 - src/arch/x86_64/CodeGen.zig | 11 +- src/codegen/c.zig | 4 - src/codegen/llvm.zig | 44 +++--- src/codegen/spirv.zig | 2 - src/print_air.zig | 2 - src/print_zir.zig | 4 - test/behavior/eval.zig | 18 +++ 18 files changed, 186 insertions(+), 286 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index fbd958dece6a9e85af41363ec0c94ac3db10fb95..f316ed04e6946571862fea63f91e70461e4bf2d7 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -443,10 +443,6 @@ pub const Inst = struct { /// Result type is always void. /// Uses the `dbg_stmt` field. dbg_stmt, - /// Marks the beginning of a semantic scope for debug info variables. - dbg_block_begin, - /// Marks the end of a semantic scope for debug info variables. - dbg_block_end, /// Marks the start of an inline call. /// Uses the `ty_fn` field. dbg_inline_begin, @@ -1454,8 +1450,6 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .dbg_stmt, .dbg_inline_begin, .dbg_inline_end, - .dbg_block_begin, - .dbg_block_end, .dbg_var_ptr, .dbg_var_val, .store, @@ -1612,8 +1606,6 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { .@"try", .try_ptr, .dbg_stmt, - .dbg_block_begin, - .dbg_block_end, .dbg_inline_begin, .dbg_inline_end, .dbg_var_ptr, diff --git a/src/AstGen.zig b/src/AstGen.zig index 9555aec68197348b7dbde22dcc025cdcb9346cba..375dafe8801d3db76a5cafb4487a30b3dc4c68a5 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2445,8 +2445,6 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod if (statements.len == 0) return; - try gz.addDbgBlockBegin(); - var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa); defer block_arena.deinit(); const block_arena_allocator = block_arena.allocator(); @@ -2518,8 +2516,6 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod } } - try gz.addDbgBlockEnd(); - try genDefers(gz, parent_scope, scope, .normal_only); try checkUsed(gz, parent_scope, scope); } @@ -2804,8 +2800,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .dbg_stmt, .dbg_var_ptr, .dbg_var_val, - .dbg_block_begin, - .dbg_block_end, .ensure_result_used, .ensure_result_non_error, .ensure_err_union_payload_void, @@ -3026,7 +3020,6 @@ fn deferStmt( var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none; const have_err_code = scope_tag == .defer_error and payload_token != 0; const sub_scope = if (!have_err_code) &defer_gen.base else blk: { - try gz.addDbgBlockBegin(); const ident_name = try gz.astgen.identAsString(payload_token); const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); opt_remapped_err_code = remapped_err_code.toOptional(); @@ -3052,7 +3045,6 @@ fn deferStmt( }; _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node); try checkUsed(gz, scope, sub_scope); - if (have_err_code) try gz.addDbgBlockEnd(); _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value); // We must handle ref_table for remapped_err_code manually. @@ -6245,7 +6237,6 @@ fn ifExpr( var payload_val_scope: Scope.LocalVal = undefined; - try then_scope.addDbgBlockBegin(); const then_node = if_full.ast.then_expr; const then_sub_scope = s: { if (if_full.error_token != null) { @@ -6305,7 +6296,6 @@ fn ifExpr( const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node); try checkUsed(parent_gz, &then_scope.base, then_sub_scope); if (!then_scope.endsWithNoReturn()) { - try then_scope.addDbgBlockEnd(); _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node); } @@ -6319,7 +6309,6 @@ fn ifExpr( const else_node = if_full.ast.else_expr; if (else_node != 0) { - try else_scope.addDbgBlockBegin(); const sub_scope = s: { if (if_full.error_token) |error_token| { const tag: Zir.Inst.Tag = if (payload_is_ref) @@ -6347,7 +6336,6 @@ fn ifExpr( } }; const else_result = try expr(&else_scope, sub_scope, block_scope.break_result_info, else_node); - try else_scope.addDbgBlockEnd(); if (!else_scope.endsWithNoReturn()) { // As our last action before the break, "pop" the error trace if needed if (do_err_trace) @@ -6579,7 +6567,6 @@ fn whileExpr( // done adding instructions to loop_scope, can now stack then_scope then_scope.instructions_top = then_scope.instructions.items.len; - try then_scope.addDbgBlockBegin(); const then_node = while_full.ast.then_expr; if (opt_payload_inst.unwrap()) |payload_inst| { try then_scope.instructions.append(astgen.gpa, payload_inst); @@ -6593,7 +6580,6 @@ fn whileExpr( if (while_full.ast.cont_expr != 0) { _ = try unusedResultExpr(&then_scope, then_sub_scope, while_full.ast.cont_expr); } - try then_scope.addDbgBlockEnd(); continue_scope.instructions_top = continue_scope.instructions.items.len; _ = try unusedResultExpr(&continue_scope, &continue_scope.base, then_node); @@ -6610,7 +6596,6 @@ fn whileExpr( const else_node = while_full.ast.else_expr; if (else_node != 0) { - try else_scope.addDbgBlockBegin(); const sub_scope = s: { if (while_full.error_token) |error_token| { const tag: Zir.Inst.Tag = if (payload_is_ref) @@ -6647,7 +6632,6 @@ fn whileExpr( } try checkUsed(parent_gz, &else_scope.base, sub_scope); - try else_scope.addDbgBlockEnd(); if (!else_scope.endsWithNoReturn()) { _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); } @@ -6849,8 +6833,6 @@ fn forExpr( var then_scope = parent_gz.makeSubBlock(&cond_scope.base); defer then_scope.unstack(); - try then_scope.addDbgBlockBegin(); - const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len); defer gpa.free(capture_scopes); @@ -6916,7 +6898,6 @@ fn forExpr( _ = try addEnsureResult(&then_scope, then_result, then_node); try checkUsed(parent_gz, &then_scope.base, then_sub_scope); - try then_scope.addDbgBlockEnd(); const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; @@ -7149,8 +7130,6 @@ fn switchExprErrUnion( case_scope.instructions_top = parent_gz.instructions.items.len; defer case_scope.unstack(); - try case_scope.addDbgBlockBegin(); - const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref) .err_union_payload_unsafe_ptr else @@ -7173,7 +7152,6 @@ fn switchExprErrUnion( catch_or_if_node, ), }; - try case_scope.addDbgBlockEnd(); _ = try case_scope.addBreakWithSrcNode( .@"break", switch_block, @@ -7184,7 +7162,6 @@ fn switchExprErrUnion( .@"if" => { var payload_val_scope: Scope.LocalVal = undefined; - try case_scope.addDbgBlockBegin(); const then_node = if_full.ast.then_expr; const then_sub_scope = s: { assert(if_full.error_token != null); @@ -7228,7 +7205,6 @@ fn switchExprErrUnion( ); try checkUsed(parent_gz, &case_scope.base, then_sub_scope); if (!case_scope.endsWithNoReturn()) { - try case_scope.addDbgBlockEnd(); _ = try case_scope.addBreakWithSrcNode( .@"break", switch_block, @@ -7407,7 +7383,6 @@ fn switchExprErrUnion( if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node)) _ = try case_scope.addSaveErrRetIndex(.always); - try case_scope.addDbgBlockBegin(); if (dbg_var_name != .empty) { try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); } @@ -7421,7 +7396,6 @@ fn switchExprErrUnion( try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef()); any_uses_err_capture = true; } - try case_scope.addDbgBlockEnd(); if (!parent_gz.refIsNoReturn(case_result)) { if (do_err_trace) @@ -7868,7 +7842,6 @@ fn switchExpr( case_scope.instructions_top = parent_gz.instructions.items.len; defer case_scope.unstack(); - try case_scope.addDbgBlockBegin(); if (dbg_var_name != .empty) { try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); } @@ -7878,7 +7851,6 @@ fn switchExpr( const target_expr_node = case.ast.target_expr; const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node); try checkUsed(parent_gz, &case_scope.base, sub_scope); - try case_scope.addDbgBlockEnd(); if (!parent_gz.refIsNoReturn(case_result)) { _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); } @@ -13165,29 +13137,6 @@ const GenZir = struct { }, } }); } - - fn addDbgBlockBegin(gz: *GenZir) !void { - if (gz.is_comptime) return; - - _ = try gz.add(.{ .tag = .dbg_block_begin, .data = undefined }); - } - - fn addDbgBlockEnd(gz: *GenZir) !void { - if (gz.is_comptime) return; - const gpa = gz.astgen.gpa; - - const tags = gz.astgen.instructions.items(.tag); - const last_inst = gz.instructions.items[gz.instructions.items.len - 1]; - // remove dbg_block_begin immediately followed by dbg_block_end - if (tags[@intFromEnum(last_inst)] == .dbg_block_begin) { - _ = gz.instructions.pop(); - return; - } - - const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); - try gz.astgen.instructions.append(gpa, .{ .tag = .dbg_block_end, .data = undefined }); - try gz.instructions.append(gpa, new_index); - } }; /// This can only be for short-lived references; the memory becomes invalidated diff --git a/src/Liveness.zig b/src/Liveness.zig index d1283c502bc07c5a0875510a9e35255e7e21f225..3b29c20cebd1b298b82f925ca669975155151da4 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -329,8 +329,6 @@ pub fn categorizeOperand( .dbg_stmt, .dbg_inline_begin, .dbg_inline_end, - .dbg_block_begin, - .dbg_block_end, .unreach, .ret_addr, .frame_addr, @@ -967,8 +965,6 @@ fn analyzeInst( .dbg_stmt, .dbg_inline_begin, .dbg_inline_end, - .dbg_block_begin, - .dbg_block_end, .fence, .ret_addr, .frame_addr, diff --git a/src/Liveness/Verify.zig b/src/Liveness/Verify.zig index bb3d1c67f97789024ef7b7a4d5858e891f828300..de7981d18fa26459d89111a81a88f561d6724fb0 100644 --- a/src/Liveness/Verify.zig +++ b/src/Liveness/Verify.zig @@ -48,8 +48,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .dbg_stmt, .dbg_inline_begin, .dbg_inline_end, - .dbg_block_begin, - .dbg_block_end, .fence, .ret_addr, .frame_addr, diff --git a/src/Sema.zig b/src/Sema.zig index 741c4e2fbaa387b03f2a357d107768a0079e3851..b41f8867c80560f9c9c06f2f87e8a4ae7879bda3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -364,6 +364,12 @@ pub const Block = struct { c_import_buf: ?*std.ArrayList(u8) = null, + /// If not `null`, this boolean is set when a `dbg_var_ptr` or `dbg_var_val` + /// instruction is emitted. It signals that the innermost lexically + /// enclosing `block`/`block_inline` should be translated into a real AIR + /// `block` in order for codegen to match lexical scoping for debug vars. + need_debug_scope: ?*bool = null, + const ComptimeReason = union(enum) { c_import: struct { block: *Block, @@ -482,6 +488,7 @@ pub const Block = struct { .float_mode = parent.float_mode, .c_import_buf = parent.c_import_buf, .error_return_trace_index = parent.error_return_trace_index, + .need_debug_scope = parent.need_debug_scope, }; } @@ -986,8 +993,6 @@ fn analyzeBodyInner( crash_info.push(); defer crash_info.pop(); - var dbg_block_begins: u32 = 0; - // We use a while (true) loop here to avoid a redundant way of breaking out of // the loop. The only way to break out of the loop is with a `noreturn` // instruction. @@ -1332,18 +1337,6 @@ fn analyzeBodyInner( i += 1; continue; }, - .dbg_block_begin => { - dbg_block_begins += 1; - try zirDbgBlockBegin(block); - i += 1; - continue; - }, - .dbg_block_end => { - dbg_block_begins -= 1; - try zirDbgBlockEnd(block); - i += 1; - continue; - }, .ensure_err_union_payload_void => { try sema.zirEnsureErrUnionPayloadVoid(block, inst); i += 1; @@ -1641,10 +1634,12 @@ fn analyzeBodyInner( const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); const gpa = sema.gpa; - const opt_break_data = b: { + const opt_break_data, const need_debug_scope = b: { // Create a temporary child block so that this inline block is properly // labeled for any .restore_err_ret_index instructions var child_block = block.makeSubBlock(); + var need_debug_scope = false; + child_block.need_debug_scope = &need_debug_scope; // If this block contains a function prototype, we need to reset the // current list of parameters and restore it later. @@ -1665,7 +1660,11 @@ fn analyzeBodyInner( child_block.instructions = block.instructions; defer block.instructions = child_block.instructions; - break :b try sema.analyzeBodyBreak(&child_block, inline_body); + const result = try sema.analyzeBodyBreak(&child_block, inline_body); + if (need_debug_scope) { + _ = try sema.ensurePostHoc(block, inst); + } + break :b .{ result, need_debug_scope }; }; // A runtime conditional branch that needs a post-hoc block to be @@ -1686,28 +1685,22 @@ fn analyzeBodyInner( // since it crosses a runtime branch. // It may pass through our currently being analyzed block_inline or it // may point directly to it. In the latter case, this modifies the - // block that we are about to look up in the post_hoc_blocks map below. + // block that we looked up in the post_hoc_blocks map above. try sema.addRuntimeBreak(block, break_data); - } else { - // Here the comptime control flow ends with noreturn; however - // we have runtime control flow continuing after this block. - // This branch is therefore handled by the `i += 1; continue;` - // logic below. } try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]); block.instructions.items.len = block_index; - const block_result = try sema.analyzeBlockBody(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges); + const block_result = try sema.analyzeBlockBody(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges, need_debug_scope); { // Destroy the ad-hoc block entry so that it does not interfere with // the next iteration of comptime control flow, if any. labeled_block.destroy(gpa); assert(sema.post_hoc_blocks.remove(new_block_inst)); } - map.putAssumeCapacity(inst, block_result); - i += 1; - continue; + + break :blk block_result; } const break_data = opt_break_data orelse break always_noreturn; @@ -1860,19 +1853,6 @@ fn analyzeBodyInner( i += 1; }; - // balance out dbg_block_begins in case of early noreturn - if (!block.is_comptime and !block.ownerModule().strip) { - const noreturn_inst = block.instructions.popOrNull(); - while (dbg_block_begins > 0) { - dbg_block_begins -= 1; - _ = try block.addInst(.{ - .tag = .dbg_block_end, - .data = undefined, - }); - } - if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some); - } - // We may have overwritten the capture scope due to a `repeat` instruction where // the body had a capture; restore it now. block.wip_capture_scope = parent_capture_scope; @@ -5757,7 +5737,7 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError ); sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items)); } - return sema.analyzeBlockBody(parent_block, src, &child_block, merges); + return sema.analyzeBlockBody(parent_block, src, &child_block, merges, false); } fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -5945,13 +5925,31 @@ fn resolveBlockBody( if (child_block.is_comptime) { return sema.resolveBody(child_block, body, body_inst); } else { + var need_debug_scope = false; + child_block.need_debug_scope = &need_debug_scope; if (sema.analyzeBodyInner(child_block, body)) |_| { - return sema.analyzeBlockBody(parent_block, src, child_block, merges); + return sema.analyzeBlockBody(parent_block, src, child_block, merges, need_debug_scope); } else |err| switch (err) { error.ComptimeBreak => { // Comptime control flow is happening, however child_block may still contain // runtime instructions which need to be copied to the parent block. - try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items); + if (need_debug_scope and child_block.instructions.items.len > 0) { + // We need a runtime block for scoping reasons. + _ = try child_block.addBr(merges.block_inst, .void_value); + try parent_block.instructions.append(sema.gpa, merges.block_inst); + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).Struct.fields.len + + child_block.instructions.items.len); + sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ + .ty = .void_type, + .payload = sema.addExtraAssumeCapacity(Air.Block{ + .body_len = @intCast(child_block.instructions.items.len), + }), + } }; + sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); + } else { + // We can copy instructions directly to the parent block. + try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items); + } const break_inst = sema.comptime_break_inst; const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; @@ -5973,6 +5971,7 @@ fn analyzeBlockBody( src: LazySrcLoc, child_block: *Block, merges: *Block.Merges, + need_debug_scope: bool, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -5987,25 +5986,57 @@ fn analyzeBlockBody( if (merges.results.items.len == 0) { // No need for a block instruction. We can put the new instructions // directly into the parent block. + if (need_debug_scope) { + // The code following this block is unreachable, as the block has no + // merges, so we don't necessarily need to emit this as an AIR block. + // However, we need a block *somewhere* to make the scoping correct, + // so forward this request to the parent block. + if (parent_block.need_debug_scope) |ptr| ptr.* = true; + } try parent_block.instructions.appendSlice(gpa, child_block.instructions.items); return child_block.instructions.items[child_block.instructions.items.len - 1].toRef(); } if (merges.results.items.len == 1) { - const last_inst_index = child_block.instructions.items.len - 1; - const last_inst = child_block.instructions.items[last_inst_index]; - if (sema.getBreakBlock(last_inst)) |br_block| { - if (br_block == merges.block_inst) { - // No need for a block instruction. We can put the new instructions directly - // into the parent block. Here we omit the break instruction. - const without_break = child_block.instructions.items[0..last_inst_index]; - try parent_block.instructions.appendSlice(gpa, without_break); - return merges.results.items[0]; + // If the `break` is trailing, we may be able to elide the AIR block here + // by appending the new instructions directly to the parent block. + if (!need_debug_scope) { + const last_inst_index = child_block.instructions.items.len - 1; + const last_inst = child_block.instructions.items[last_inst_index]; + if (sema.getBreakBlock(last_inst)) |br_block| { + if (br_block == merges.block_inst) { + // Great, the last instruction is the break! Put the instructions + // directly into the parent block. + try parent_block.instructions.appendSlice(gpa, child_block.instructions.items[0..last_inst_index]); + return merges.results.items[0]; + } } } + // Okay, we need a runtime block. If the value is comptime-known, the + // block should just return void, and we return the merge result + // directly. Otherwise, we can defer to the logic below. + if (try sema.resolveValue(merges.results.items[0])) |result_val| { + // Create a block containing all instruction from the body. + try parent_block.instructions.append(gpa, merges.block_inst); + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + + child_block.instructions.items.len); + sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ + .ty = .void_type, + .payload = sema.addExtraAssumeCapacity(Air.Block{ + .body_len = @intCast(child_block.instructions.items.len), + }), + } }; + sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); + // Rewrite the break to just give value {}; the value is + // comptime-known and will be returned directly. + sema.air_instructions.items(.data)[@intFromEnum(merges.br_list.items[0])].br.operand = .void_value; + return Air.internedToRef(result_val.toIntern()); + } } // It is impossible to have the number of results be > 1 in a comptime scope. assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition. + // Note that we'll always create an AIR block here, so `need_debug_scope` is irrelevant. + // Need to set the type and emit the Block instruction. This allows machine code generation // to emit a jump instruction to after the block when it encounters the break. try parent_block.instructions.append(gpa, merges.block_inst); @@ -6383,24 +6414,6 @@ fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi }); } -fn zirDbgBlockBegin(block: *Block) CompileError!void { - if (block.is_comptime or block.ownerModule().strip) return; - - _ = try block.addInst(.{ - .tag = .dbg_block_begin, - .data = undefined, - }); -} - -fn zirDbgBlockEnd(block: *Block) CompileError!void { - if (block.is_comptime or block.ownerModule().strip) return; - - _ = try block.addInst(.{ - .tag = .dbg_block_end, - .data = undefined, - }); -} - fn zirDbgVar( sema: *Sema, block: *Block, @@ -6432,6 +6445,15 @@ fn addDbgVar( if (try sema.typeRequiresComptime(val_ty)) return; if (!(try sema.typeHasRuntimeBits(val_ty))) return; + // To ensure the lexical scoping is known to backends, this alloc must be + // within a real runtime block. We set a flag which communicates information + // to the closest lexically enclosing block: + // * If it is a `block_inline`, communicates to logic in `analyzeBodyInner` + // to create a post-hoc block. + // * Otherwise, communicates to logic in `resolveBlockBody` to create a + // real `block` instruction. + if (block.need_debug_scope) |ptr| ptr.* = true; + try sema.queueFullTypeResolution(operand_ty); // Add the name to the AIR. @@ -7585,7 +7607,7 @@ fn analyzeCall( error.ComptimeReturn => break :result inlining.comptime_result, else => |e| return e, }; - break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges); + break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges, false); }; if (!is_comptime_call and !block.is_typeof and @@ -11528,7 +11550,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions)); sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); - return sema.analyzeBlockBody(block, main_src, &child_block, merges); + return sema.analyzeBlockBody(block, main_src, &child_block, merges, false); } fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref { @@ -12150,7 +12172,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r false, ); - return sema.analyzeBlockBody(block, src, &child_block, merges); + return sema.analyzeBlockBody(block, src, &child_block, merges, false); } const SpecialProng = struct { @@ -13163,8 +13185,6 @@ fn validateErrSetSwitch( const tags = sema.code.instructions.items(.tag); const datas = sema.code.instructions.items(.data); for (else_case.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) { - .dbg_block_begin, - .dbg_block_end, .dbg_stmt, .dbg_var_val, .ret_type, @@ -13416,8 +13436,6 @@ fn maybeErrorUnwrap( .@"unreachable" => if (!block.wantSafety()) return false, .err_union_code => if (!allow_err_code_inst) return false, .save_err_ret_index, - .dbg_block_begin, - .dbg_block_end, .dbg_stmt, .str, .as_node, @@ -13430,10 +13448,7 @@ fn maybeErrorUnwrap( for (body) |inst| { const air_inst = switch (tags[@intFromEnum(inst)]) { - .dbg_block_begin, - .dbg_block_end, - .err_union_code, - => continue, + .err_union_code => continue, .dbg_stmt => { try sema.zirDbgStmt(block, inst); continue; @@ -13499,8 +13514,6 @@ fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.I const tags = sema.code.instructions.items(.tag); const inst = for (body) |inst| { switch (tags[@intFromEnum(inst)]) { - .dbg_block_begin, - .dbg_block_end, .dbg_stmt, .save_err_ret_index, => {}, @@ -19092,55 +19105,64 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr return try_inst; } +fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*LabeledBlock { + const gop = sema.inst_map.getOrPutAssumeCapacity(dest_block); + if (gop.found_existing) existing: { + // This may be a *result* from an earlier iteration of an inline loop. + // In this case, there will not be a post-hoc block entry, and we can + // continue with the logic below. + const new_block_inst = gop.value_ptr.*.toIndex() orelse break :existing; + return sema.post_hoc_blocks.get(new_block_inst) orelse break :existing; + } + + try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); + + const new_block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); + gop.value_ptr.* = new_block_inst.toRef(); + try sema.air_instructions.append(sema.gpa, .{ + .tag = .block, + .data = undefined, + }); + const labeled_block = try sema.gpa.create(LabeledBlock); + labeled_block.* = .{ + .label = .{ + .zir_block = dest_block, + .merges = .{ + .src_locs = .{}, + .results = .{}, + .br_list = .{}, + .block_inst = new_block_inst, + }, + }, + .block = .{ + .parent = block, + .sema = sema, + .src_decl = block.src_decl, + .namespace = block.namespace, + .wip_capture_scope = block.wip_capture_scope, + .instructions = .{}, + .label = &labeled_block.label, + .inlining = block.inlining, + .is_comptime = block.is_comptime, + }, + }; + sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); + return labeled_block; +} + // A `break` statement is inside a runtime condition, but trying to // break from an inline loop. In such case we must convert it to // a runtime break. fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void { - const gop = sema.inst_map.getOrPutAssumeCapacity(break_data.block_inst); - const labeled_block = if (!gop.found_existing) blk: { - try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); - - const new_block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); - gop.value_ptr.* = new_block_inst.toRef(); - try sema.air_instructions.append(sema.gpa, .{ - .tag = .block, - .data = undefined, - }); - const labeled_block = try sema.gpa.create(LabeledBlock); - labeled_block.* = .{ - .label = .{ - .zir_block = break_data.block_inst, - .merges = .{ - .src_locs = .{}, - .results = .{}, - .br_list = .{}, - .block_inst = new_block_inst, - }, - }, - .block = .{ - .parent = child_block, - .sema = sema, - .src_decl = child_block.src_decl, - .namespace = child_block.namespace, - .wip_capture_scope = child_block.wip_capture_scope, - .instructions = .{}, - .label = &labeled_block.label, - .inlining = child_block.inlining, - .is_comptime = child_block.is_comptime, - }, - }; - sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); - break :blk labeled_block; - } else blk: { - const new_block_inst = gop.value_ptr.*.toIndex().?; - const labeled_block = sema.post_hoc_blocks.get(new_block_inst).?; - break :blk labeled_block; - }; + const labeled_block = try sema.ensurePostHoc(child_block, break_data.block_inst); const operand = try sema.resolveInst(break_data.operand); const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); + try labeled_block.label.merges.results.append(sema.gpa, operand); try labeled_block.label.merges.br_list.append(sema.gpa, br_ref.toIndex().?); + try labeled_block.label.merges.src_locs.append(sema.gpa, null); + labeled_block.block.runtime_index.increment(); if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) { labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop; @@ -19481,8 +19503,10 @@ fn analyzeRet( return error.ComptimeReturn; } // We are inlining a function call; rewrite the `ret` as a `break`. + const br_inst = try block.addBr(inlining.merges.block_inst, operand); try inlining.merges.results.append(sema.gpa, operand); - _ = try block.addBr(inlining.merges.block_inst, operand); + try inlining.merges.br_list.append(sema.gpa, br_inst.toIndex().?); + try inlining.merges.src_locs.append(sema.gpa, operand_src); return always_noreturn; } else if (block.is_comptime) { return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{}); diff --git a/src/Zir.zig b/src/Zir.zig index 61d1301167c7943545dd6a88c245a211d8bd15cc..3e6ffcc3257d81c8b957d4ad7189f5e613f10e9e 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -392,10 +392,6 @@ pub const Inst = struct { /// Same as `dbg_var_ptr` but the local is always a const and the operand /// is the local's value. dbg_var_val, - /// Marks the beginning of a semantic scope for debug info variables. - dbg_block_begin, - /// Marks the end of a semantic scope for debug info variables. - dbg_block_end, /// Uses a name to identify a Decl and takes a pointer to it. /// Uses the `str_tok` union field. decl_ref, @@ -1107,8 +1103,6 @@ pub const Inst = struct { .dbg_stmt, .dbg_var_ptr, .dbg_var_val, - .dbg_block_begin, - .dbg_block_end, .decl_ref, .decl_val, .load, @@ -1335,8 +1329,6 @@ pub const Inst = struct { .dbg_stmt, .dbg_var_ptr, .dbg_var_val, - .dbg_block_begin, - .dbg_block_end, .ensure_result_used, .ensure_result_non_error, .ensure_err_union_payload_void, @@ -1663,8 +1655,6 @@ pub const Inst = struct { .dbg_stmt = .dbg_stmt, .dbg_var_ptr = .str_op, .dbg_var_val = .str_op, - .dbg_block_begin = .tok, - .dbg_block_end = .tok, .decl_ref = .str_tok, .decl_val = .str_tok, .load = .un_node, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 750edb45a89d33a84450f1457a730a1367fe5106..d85475f1fea4be7424f90f8078e6a74e91d4f988 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -813,10 +813,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_inline_end, => try self.airDbgInline(inst), - .dbg_block_begin, - .dbg_block_end, - => try self.airDbgBlock(inst), - .call => try self.airCall(inst, .auto), .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), @@ -4634,11 +4630,6 @@ fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ .none, .none, .none }); } -fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { - // TODO emit debug info lexical block - return self.finishAir(inst, .dead, .{ .none, .none, .none }); -} - fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const operand = pl_op.operand; @@ -5066,6 +5057,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Block, ty_pl.payload); const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); + // TODO emit debug info lexical block try self.genBody(body); // relocations for `br` instructions diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index ed3434106f6b2f2a8977e79bed411d7412433d39..c8011c90a707db5ba13015207c61c28d57a78bef 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -799,10 +799,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_inline_end, => try self.airDbgInline(inst), - .dbg_block_begin, - .dbg_block_end, - => try self.airDbgBlock(inst), - .call => try self.airCall(inst, .auto), .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), @@ -4587,11 +4583,6 @@ fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ .none, .none, .none }); } -fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { - // TODO emit debug info lexical block - return self.finishAir(inst, .dead, .{ .none, .none, .none }); -} - fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const operand = pl_op.operand; @@ -4997,6 +4988,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Block, ty_pl.payload); const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); + // TODO emit debug info lexical block try self.genBody(body); // relocations for `br` instructions diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index fe46d0ddb630ad09f6978c17de50dec93c534051..1c83b2efad4a502cae3b0fe821de326aaf00a1eb 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -632,10 +632,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_inline_end, => try self.airDbgInline(inst), - .dbg_block_begin, - .dbg_block_end, - => try self.airDbgBlock(inst), - .call => try self.airCall(inst, .auto), .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), @@ -1894,11 +1890,6 @@ fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ .none, .none, .none }); } -fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { - // TODO emit debug info lexical block - return self.finishAir(inst, .dead, .{ .none, .none, .none }); -} - fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const name = self.air.nullTerminatedString(pl_op.payload); @@ -2084,6 +2075,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Block, ty_pl.payload); const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); + // TODO emit debug info lexical block try self.genBody(body); for (self.blocks.getPtr(inst).?.relocs.items) |reloc| try self.performReloc(reloc); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 9d5f8158ef89ccd06fc709e4867831a12ede6457..85e8fbecb7e82807a4f7e95b230fcb5fe4e77495 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -645,10 +645,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_inline_end, => try self.airDbgInline(inst), - .dbg_block_begin, - .dbg_block_end, - => try self.airDbgBlock(inst), - .call => try self.airCall(inst, .auto), .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), @@ -1146,6 +1142,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Block, ty_pl.payload); const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); + // TODO emit debug info lexical block try self.genBody(body); // relocations for `bpcc` instructions @@ -1655,11 +1652,6 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { - // TODO emit debug info lexical block - return self.finishAir(inst, .dead, .{ .none, .none, .none }); -} - fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; const mod = self.bin_file.comp.module.?; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 781190e13bfba0aea38eed8629bd768c19eda2ce..c3a9bb8895588a117666e3eec612be06a083fffc 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1913,8 +1913,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { // TODO .dbg_inline_begin, .dbg_inline_end, - .dbg_block_begin, - .dbg_block_end, => func.finishAir(inst, .none, &.{}), .dbg_var_ptr => func.airDbgVar(inst, true), diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 4ca2ae44bb5c8f495f249a0a66c45b5f5f1dbe98..9c9c4a0064fab17a6d77abceae22f22394841875 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2106,10 +2106,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_inline_end, => try self.airDbgInline(inst), - .dbg_block_begin, - .dbg_block_end, - => try self.airDbgBlock(inst), - .call => try self.airCall(inst, .auto), .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), @@ -12976,12 +12972,6 @@ fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { self.finishAirBookkeeping(); } -fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - // TODO emit debug info lexical block - self.finishAirBookkeeping(); -} - fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const operand = pl_op.operand; @@ -13428,6 +13418,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Block, ty_pl.payload); const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); + // TODO emit debug info lexical block try self.genBody(body); var block_data = self.blocks.fetchRemove(inst).?; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 0977acf7fe025c741a195d92e6d72cdc667741db..d4747256bbe03e651ad68fa1694cb0e3edaa7847 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3268,10 +3268,6 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .dbg_inline_end, => try airDbgInline(f, inst), - .dbg_block_begin, - .dbg_block_end, - => .none, - .call => try airCall(f, inst, .auto), .call_always_tail => .none, .call_never_tail => try airCall(f, inst, .never_tail), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index bd0c33716910a9ca9328e4ef25bf8a2edad93e82..331af769ca21600b19b0902557c3fce82a1b5384 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4768,8 +4768,6 @@ pub const FuncGen = struct { scope: Builder.Metadata, }) = .{}, - scope_stack: std.ArrayListUnmanaged(Builder.Metadata) = .{}, - base_line: u32, prev_dbg_line: c_uint, prev_dbg_column: c_uint, @@ -4813,7 +4811,6 @@ pub const FuncGen = struct { fn deinit(self: *FuncGen) void { self.wip.deinit(); - self.scope_stack.deinit(self.gpa); self.inlined.deinit(self.gpa); self.func_inst_table.deinit(self.gpa); self.blocks.deinit(self.gpa); @@ -5112,8 +5109,6 @@ pub const FuncGen = struct { .dbg_stmt => try self.airDbgStmt(inst), .dbg_inline_begin => try self.airDbgInlineBegin(inst), .dbg_inline_end => try self.airDbgInlineEnd(inst), - .dbg_block_begin => try self.airDbgBlockBegin(), - .dbg_block_end => try self.airDbgBlockEnd(), .dbg_var_ptr => try self.airDbgVarPtr(inst), .dbg_var_val => try self.airDbgVarVal(inst), @@ -5131,6 +5126,19 @@ pub const FuncGen = struct { } } + fn genBodyDebugScope(self: *FuncGen, body: []const Air.Inst.Index) Error!void { + if (self.wip.strip) return self.genBody(body); + const old_scope = self.scope; + self.scope = try self.dg.object.builder.debugLexicalBlock( + old_scope, + self.file, + self.prev_dbg_line, + self.prev_dbg_column, + ); + try self.genBody(body); + self.scope = old_scope; + } + pub const CallAttr = enum { Auto, NeverTail, @@ -5820,7 +5828,7 @@ pub const FuncGen = struct { const inst_ty = self.typeOfIndex(inst); if (inst_ty.isNoReturn(mod)) { - try self.genBody(body); + try self.genBodyDebugScope(body); return .none; } @@ -5836,7 +5844,7 @@ pub const FuncGen = struct { }); defer assert(self.blocks.remove(inst)); - try self.genBody(body); + try self.genBodyDebugScope(body); self.wip.cursor = .{ .block = parent_bb }; @@ -6683,26 +6691,6 @@ pub const FuncGen = struct { return .none; } - fn airDbgBlockBegin(self: *FuncGen) Allocator.Error!Builder.Value { - const o = self.dg.object; - - try self.scope_stack.append(self.gpa, self.scope); - - const old = self.scope; - self.scope = try o.builder.debugLexicalBlock( - old, - self.file, - self.prev_dbg_line, - self.prev_dbg_column, - ); - return .none; - } - - fn airDbgBlockEnd(self: *FuncGen) !Builder.Value { - self.scope = self.scope_stack.pop(); - return .none; - } - fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.dg.object; const mod = o.module; @@ -7488,7 +7476,7 @@ pub const FuncGen = struct { for (body_tail[1..]) |body_inst| { switch (air_tags[@intFromEnum(body_inst)]) { .ret => return true, - .dbg_stmt, .dbg_block_end => continue, + .dbg_stmt => continue, else => return false, } } diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index dc3b646ab7331457c7b6876e36a7f358dec1e0a2..ca724c38799e1dc763885b9ca23ad73f32eb2e48 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -2322,8 +2322,6 @@ const DeclGen = struct { .dbg_inline_begin => return self.airDbgInlineBegin(inst), .dbg_inline_end => return self.airDbgInlineEnd(inst), .dbg_var_ptr, .dbg_var_val => return self.airDbgVar(inst), - .dbg_block_begin => return, - .dbg_block_end => return, .unwrap_errunion_err => try self.airErrUnionErr(inst), .unwrap_errunion_payload => try self.airErrUnionPayload(inst), diff --git a/src/print_air.zig b/src/print_air.zig index 6f9ac1f7711555082cb8b0f2d45e1157a354a106..e4f412129924a19a101fcfb27231fd692114b5d5 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -319,8 +319,6 @@ const Writer = struct { .cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst), .vector_store_elem => try w.writeVectorStoreElem(s, inst), - .dbg_block_begin, .dbg_block_end => {}, - .work_item_id, .work_group_size, .work_group_id, diff --git a/src/print_zir.zig b/src/print_zir.zig index 226b09a5c4a2a0d065173e3f27439b1d3d55f438..711dcaa2a3fc0ea45c475b14092a908956714717 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -510,10 +510,6 @@ const Writer = struct { .dbg_stmt => try self.writeDbgStmt(stream, inst), - .dbg_block_begin, - .dbg_block_end, - => try stream.writeAll(")"), - .closure_get => try self.writeInstNode(stream, inst), .@"defer" => try self.writeDefer(stream, inst), diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index e502bea85b5d60e29c18ceaa7b5ccfc0f4381b79..c755df7d8664202d64f90c1bdca83117ebdc846f 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1714,3 +1714,21 @@ test "const with specified type initialized with typed array is comptime-known" comptime assert(x[1] == 2); comptime assert(x[2] == 3); } + +test "block with comptime-known result but possible runtime exit is comptime-known" { + var t: bool = true; + _ = &t; + + const a: comptime_int = a: { + if (!t) return error.TestFailed; + break :a 123; + }; + + const b: comptime_int = b: { + if (t) break :b 456; + return error.TestFailed; + }; + + comptime assert(a == 123); + comptime assert(b == 456); +} -- 2.54.0