mirror of
https://github.com/ziglang/zig.git
synced 2026-01-21 06:45:24 +00:00
Merge pull request #19062 from mlugg/dbg-var-blocks
compiler: decide dbg_var scoping based on AIR blocks
This commit is contained in:
commit
8775d8bbce
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
270
src/Sema.zig
270
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;
|
||||
@ -5762,7 +5742,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 {
|
||||
@ -5950,13 +5930,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";
|
||||
@ -5978,6 +5976,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();
|
||||
@ -5992,25 +5991,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);
|
||||
@ -6388,24 +6419,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,
|
||||
@ -6437,6 +6450,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.
|
||||
@ -7590,7 +7612,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
|
||||
@ -11534,7 +11556,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 {
|
||||
@ -12156,7 +12178,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 {
|
||||
@ -13169,8 +13191,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,
|
||||
@ -13422,8 +13442,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,
|
||||
@ -13436,10 +13454,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;
|
||||
@ -13505,8 +13520,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,
|
||||
=> {},
|
||||
@ -19098,55 +19111,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;
|
||||
@ -19487,8 +19509,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", .{});
|
||||
|
||||
10
src/Zir.zig
10
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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.?;
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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).?;
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user