From aa688567f556f9d24cae25f087adf90d96f6906f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 2 Mar 2024 19:49:20 +0100 Subject: [PATCH] Air: replace `.dbg_inline_*` with `.dbg_inline_block` This prevents the possibility of not emitting a `.dbg_inline_end` instruction and reduces the allocation requirements of the backends. Closes #19093 --- src/Air.zig | 25 ++--- src/Liveness.zig | 39 ++++--- src/Liveness/Verify.zig | 21 ++-- src/Sema.zig | 153 +++++++++++++++------------ src/arch/aarch64/CodeGen.zig | 25 ++--- src/arch/arm/CodeGen.zig | 25 ++--- src/arch/riscv64/CodeGen.zig | 26 ++--- src/arch/sparc64/CodeGen.zig | 25 ++--- src/arch/wasm/CodeGen.zig | 54 +++++----- src/arch/x86_64/CodeGen.zig | 36 ++++--- src/codegen/c.zig | 37 ++++--- src/codegen/llvm.zig | 194 +++++++++++++++++------------------ src/codegen/spirv.zig | 43 ++++---- src/print_air.zig | 37 ++++--- test/behavior/switch.zig | 17 +++ 15 files changed, 413 insertions(+), 344 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index faf239c7bf..e014e225e0 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -443,12 +443,9 @@ pub const Inst = struct { /// Result type is always void. /// Uses the `dbg_stmt` field. dbg_stmt, - /// Marks the start of an inline call. - /// Uses the `ty_fn` field. - dbg_inline_begin, - /// Marks the end of an inline call. - /// Uses the `ty_fn` field. - dbg_inline_end, + /// A block that represents an inlined function call. + /// Uses the `ty_pl` field. Payload is `DbgInlineBlock`. + dbg_inline_block, /// Marks the beginning of a local variable. The operand is a pointer pointing /// to the storage for the variable. The local may be a const or a var. /// Result type is always void. @@ -1051,10 +1048,6 @@ pub const Inst = struct { // Index into a different array. payload: u32, }, - ty_fn: struct { - ty: Ref, - func: InternPool.Index, - }, br: struct { block_inst: Index, operand: Ref, @@ -1117,6 +1110,12 @@ pub const Block = struct { body_len: u32, }; +/// Trailing is a list of instruction indexes for every `body_len`. +pub const DbgInlineBlock = struct { + func: InternPool.Index, + body_len: u32, +}; + /// Trailing is a list of `Inst.Ref` for every `args_len`. pub const Call = struct { args_len: u32, @@ -1371,6 +1370,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .assembly, .block, + .dbg_inline_block, .struct_field_ptr, .struct_field_val, .slice_elem_ptr, @@ -1448,8 +1448,6 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .breakpoint, .dbg_stmt, - .dbg_inline_begin, - .dbg_inline_end, .dbg_var_ptr, .dbg_var_val, .store, @@ -1606,8 +1604,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { .@"try", .try_ptr, .dbg_stmt, - .dbg_inline_begin, - .dbg_inline_end, + .dbg_inline_block, .dbg_var_ptr, .dbg_var_val, .ret, diff --git a/src/Liveness.zig b/src/Liveness.zig index 3b29c20ceb..3d19948b68 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -327,8 +327,6 @@ pub fn categorizeOperand( .trap, .breakpoint, .dbg_stmt, - .dbg_inline_begin, - .dbg_inline_end, .unreach, .ret_addr, .frame_addr, @@ -604,9 +602,19 @@ pub fn categorizeOperand( .assembly => { return .complex; }, - .block => { - const extra = air.extraData(Air.Block, air_datas[@intFromEnum(inst)].ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(air.extra[extra.end..][0..extra.data.body_len]); + .block, .dbg_inline_block => |tag| { + const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; + const body: []const Air.Inst.Index = @ptrCast(switch (tag) { + inline .block, .dbg_inline_block => |comptime_tag| body: { + const extra = air.extraData(switch (comptime_tag) { + .block => Air.Block, + .dbg_inline_block => Air.DbgInlineBlock, + else => unreachable, + }, ty_pl.payload); + break :body air.extra[extra.end..][0..extra.data.body_len]; + }, + else => unreachable, + }); if (body.len == 1 and air_tags[@intFromEnum(body[0])] == .cond_br) { // Peephole optimization for "panic-like" conditionals, which have @@ -963,8 +971,6 @@ fn analyzeInst( .ret_ptr, .breakpoint, .dbg_stmt, - .dbg_inline_begin, - .dbg_inline_end, .fence, .ret_addr, .frame_addr, @@ -1235,7 +1241,15 @@ fn analyzeInst( return big.finish(); }, - .block => return analyzeInstBlock(a, pass, data, inst), + inline .block, .dbg_inline_block => |comptime_tag| { + const ty_pl = inst_datas[@intFromEnum(inst)].ty_pl; + const extra = a.air.extraData(switch (comptime_tag) { + .block => Air.Block, + .dbg_inline_block => Air.DbgInlineBlock, + else => unreachable, + }, ty_pl.payload); + return analyzeInstBlock(a, pass, data, inst, ty_pl.ty, @ptrCast(a.air.extra[extra.end..][0..extra.data.body_len])); + }, .loop => return analyzeInstLoop(a, pass, data, inst), .@"try" => return analyzeInstCondBr(a, pass, data, inst, .@"try"), @@ -1369,12 +1383,9 @@ fn analyzeInstBlock( comptime pass: LivenessPass, data: *LivenessPassData(pass), inst: Air.Inst.Index, + ty: Air.Inst.Ref, + body: []const Air.Inst.Index, ) !void { - const inst_datas = a.air.instructions.items(.data); - const ty_pl = inst_datas[@intFromEnum(inst)].ty_pl; - const extra = a.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(a.air.extra[extra.end..][0..extra.data.body_len]); - const gpa = a.gpa; // We actually want to do `analyzeOperands` *first*, since our result logically doesn't @@ -1403,7 +1414,7 @@ fn analyzeInstBlock( // If the block is noreturn, block deaths not only aren't useful, they're impossible to // find: there could be more stuff alive after the block than before it! - if (!a.intern_pool.isNoReturn(ty_pl.ty.toType().ip_index)) { + if (!a.intern_pool.isNoReturn(ty.toType().toIntern())) { // The block kills the difference in the live sets const block_scope = data.block_scopes.get(inst).?; const num_deaths = data.live_set.count() - block_scope.live_set.count(); diff --git a/src/Liveness/Verify.zig b/src/Liveness/Verify.zig index de7981d18f..4392f25e10 100644 --- a/src/Liveness/Verify.zig +++ b/src/Liveness/Verify.zig @@ -29,7 +29,7 @@ const LiveMap = std.AutoHashMapUnmanaged(Air.Inst.Index, void); fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { const ip = self.intern_pool; - const tag = self.air.instructions.items(.tag); + const tags = self.air.instructions.items(.tag); const data = self.air.instructions.items(.data); for (body) |inst| { if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) { @@ -37,7 +37,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { continue; } - switch (tag[@intFromEnum(inst)]) { + switch (tags[@intFromEnum(inst)]) { // no operands .arg, .alloc, @@ -46,8 +46,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .ret_ptr, .breakpoint, .dbg_stmt, - .dbg_inline_begin, - .dbg_inline_end, .fence, .ret_addr, .frame_addr, @@ -431,11 +429,20 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { } try self.verifyInst(inst); }, - .block => { + .block, .dbg_inline_block => |tag| { const ty_pl = data[@intFromEnum(inst)].ty_pl; const block_ty = ty_pl.ty.toType(); - const extra = self.air.extraData(Air.Block, ty_pl.payload); - const block_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); + const block_body: []const Air.Inst.Index = @ptrCast(switch (tag) { + inline .block, .dbg_inline_block => |comptime_tag| body: { + const extra = self.air.extraData(switch (comptime_tag) { + .block => Air.Block, + .dbg_inline_block => Air.DbgInlineBlock, + else => unreachable, + }, ty_pl.payload); + break :body self.air.extra[extra.end..][0..extra.data.body_len]; + }, + else => unreachable, + }); const block_liveness = self.liveness.getBlock(inst); var orig_live = try self.live.clone(self.gpa); diff --git a/src/Sema.zig b/src/Sema.zig index a0c66f440c..fc5a17acce 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5939,6 +5939,7 @@ fn resolveBlockBody( if (child_block.is_comptime) { return sema.resolveInlineBody(child_block, body, body_inst); } else { + assert(sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)] == .block); var need_debug_scope = false; child_block.need_debug_scope = &need_debug_scope; if (sema.analyzeBodyInner(child_block, body)) |_| { @@ -6002,18 +6003,44 @@ fn resolveAnalyzedBlock( assert(child_block.instructions.items.len != 0); assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(mod)); + const block_tag = sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)]; + switch (block_tag) { + .block => {}, + .dbg_inline_block => assert(need_debug_scope), + else => unreachable, + } 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; + switch (block_tag) { + .block => { + // 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(); + }, + .dbg_inline_block => { + // 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.DbgInlineBlock).Struct.fields.len + + child_block.instructions.items.len); + sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ + .ty = .noreturn_type, + .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ + .func = child_block.inlining.?.func, + .body_len = @intCast(child_block.instructions.items.len), + }), + } }; + sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); + return merges.block_inst.toRef(); + }, + else => unreachable, } - 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) { // If the `break` is trailing, we may be able to elide the AIR block here @@ -6036,14 +6063,30 @@ fn resolveAnalyzedBlock( 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), - }), - } }; + switch (block_tag) { + .block => { + 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), + }), + } }; + }, + .dbg_inline_block => { + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).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.DbgInlineBlock{ + .func = child_block.inlining.?.func, + .body_len = @intCast(child_block.instructions.items.len), + }), + } }; + }, + else => unreachable, + } 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. @@ -6079,14 +6122,30 @@ fn resolveAnalyzedBlock( return sema.failWithOwnedErrorMsg(child_block, msg); } const ty_inst = Air.internedToRef(resolved_ty.toIntern()); - 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 = ty_inst, - .payload = sema.addExtraAssumeCapacity(Air.Block{ - .body_len = @intCast(child_block.instructions.items.len), - }), - } }; + switch (block_tag) { + .block => { + 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 = ty_inst, + .payload = sema.addExtraAssumeCapacity(Air.Block{ + .body_len = @intCast(child_block.instructions.items.len), + }), + } }; + }, + .dbg_inline_block => { + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).Struct.fields.len + + child_block.instructions.items.len); + sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ + .ty = ty_inst, + .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ + .func = child_block.inlining.?.func, + .body_len = @intCast(child_block.instructions.items.len), + }), + } }; + }, + else => unreachable, + } sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); // Now that the block has its type resolved, we need to go back into all the break // instructions, and insert type coercion on the operands. @@ -7379,7 +7438,6 @@ fn analyzeCall( .needed_comptime_reason = "function being called at comptime must be comptime-known", .block_comptime_reason = comptime_reason, }); - const prev_fn_index = sema.func_index; const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { .extern_func => return sema.fail(block, call_src, "{s} call of extern function", .{ @as([]const u8, if (is_comptime_call) "comptime" else "inline"), @@ -7415,9 +7473,10 @@ fn analyzeCall( // set to in the `Block`. // This block instruction will be used to capture the return value from the // inlined function. + const need_debug_scope = !is_comptime_call and !block.is_typeof and !block.ownerModule().strip; const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); try sema.air_instructions.append(gpa, .{ - .tag = .block, + .tag = if (need_debug_scope) .dbg_inline_block else .block, .data = undefined, }); // This one is shared among sub-blocks within the same callee, but not @@ -7584,10 +7643,7 @@ fn analyzeCall( } new_fn_info.return_type = sema.fn_ret_ty.toIntern(); - const new_func_resolved_ty = try mod.funcType(new_fn_info); if (!is_comptime_call and !block.is_typeof) { - try emitDbgInline(block, prev_fn_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin); - const zir_tags = sema.code.instructions.items(.tag); for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) { .param, .param_comptime => { @@ -7626,21 +7682,9 @@ fn analyzeCall( error.ComptimeReturn => break :result inlining.comptime_result, else => |e| return e, }; - break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, false); + break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, need_debug_scope); }; - if (!is_comptime_call and !block.is_typeof and - sema.typeOf(result).zigTypeTag(mod) != .NoReturn) - { - try emitDbgInline( - block, - module_fn_index, - prev_fn_index, - mod.funcOwnerDeclPtr(sema.func_index).ty, - .dbg_inline_end, - ); - } - if (should_memoize and is_comptime_call) { const result_val = try sema.resolveConstValue(block, .unneeded, result, undefined); const result_interned = try result_val.intern2(sema.fn_ret_ty, mod); @@ -8207,27 +8251,6 @@ fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) } } -fn emitDbgInline( - block: *Block, - old_func: InternPool.Index, - new_func: InternPool.Index, - new_func_ty: Type, - tag: Air.Inst.Tag, -) CompileError!void { - if (block.ownerModule().strip) return; - - // Recursive inline call; no dbg_inline needed. - if (old_func == new_func) return; - - _ = try block.addInst(.{ - .tag = tag, - .data = .{ .ty_fn = .{ - .ty = Air.internedToRef(new_func_ty.toIntern()), - .func = new_func, - } }, - }); -} - fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type; diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index d85475f1fe..4cd065d61a 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -747,7 +747,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .frame_addr => try self.airFrameAddress(inst), .fence => try self.airFence(), .cond_br => try self.airCondBr(inst), - .dbg_stmt => try self.airDbgStmt(inst), .fptrunc => try self.airFptrunc(inst), .fpext => try self.airFpext(inst), .intcast => try self.airIntCast(inst), @@ -805,14 +804,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .@"try" => try self.airTry(inst), .try_ptr => try self.airTryPtr(inst), + .dbg_stmt => try self.airDbgStmt(inst), + .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), - .dbg_inline_begin, - .dbg_inline_end, - => try self.airDbgInline(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), @@ -4621,13 +4618,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { return self.finishAirBookkeeping(); } -fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { - const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; +fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.comp.module.?; - const func = mod.funcInfo(ty_fn.func); + const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + const func = mod.funcInfo(extra.data.func); // TODO emit debug info for function change _ = func; - return self.finishAir(inst, .dead, .{ .none, .none, .none }); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); } fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { @@ -5042,6 +5040,12 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void { } 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); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); +} + +fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { try self.blocks.putNoClobber(self.gpa, inst, .{ // A block is a setup to be able to jump to the end. .relocs = .{}, @@ -5054,9 +5058,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { }); defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); - 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); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index c8011c90a7..638a8543fb 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -733,7 +733,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .frame_addr => try self.airFrameAddress(inst), .fence => try self.airFence(), .cond_br => try self.airCondBr(inst), - .dbg_stmt => try self.airDbgStmt(inst), .fptrunc => try self.airFptrunc(inst), .fpext => try self.airFpext(inst), .intcast => try self.airIntCast(inst), @@ -791,14 +790,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .@"try" => try self.airTry(inst), .try_ptr => try self.airTryPtr(inst), + .dbg_stmt => try self.airDbgStmt(inst), + .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), - .dbg_inline_begin, - .dbg_inline_end, - => try self.airDbgInline(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), @@ -4574,13 +4571,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { return self.finishAirBookkeeping(); } -fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { - const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; +fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.comp.module.?; - const func = mod.funcInfo(ty_fn.func); + const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + const func = mod.funcInfo(extra.data.func); // TODO emit debug info for function change _ = func; - return self.finishAir(inst, .dead, .{ .none, .none, .none }); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); } fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { @@ -4973,6 +4971,12 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void { } 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); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); +} + +fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { try self.blocks.putNoClobber(self.gpa, inst, .{ // A block is a setup to be able to jump to the end. .relocs = .{}, @@ -4985,9 +4989,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { }); defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); - 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); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 1c83b2efad..78a3638e19 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -566,7 +566,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .frame_addr => try self.airFrameAddress(inst), .fence => try self.airFence(), .cond_br => try self.airCondBr(inst), - .dbg_stmt => try self.airDbgStmt(inst), .fptrunc => try self.airFptrunc(inst), .fpext => try self.airFpext(inst), .intcast => try self.airIntCast(inst), @@ -624,14 +623,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .@"try" => @panic("TODO"), .try_ptr => @panic("TODO"), + .dbg_stmt => try self.airDbgStmt(inst), + .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), - .dbg_inline_begin, - .dbg_inline_end, - => try self.airDbgInline(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), @@ -1881,13 +1878,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { return self.finishAirBookkeeping(); } -fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { - const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; +fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.comp.module.?; - const func = mod.funcInfo(ty_fn.func); + const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + const func = mod.funcInfo(extra.data.func); // TODO emit debug info for function change _ = func; - return self.finishAir(inst, .dead, .{ .none, .none, .none }); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); } fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { @@ -2060,6 +2058,12 @@ fn jump(self: *Self, index: usize) !void { } 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); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); +} + +fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { try self.blocks.putNoClobber(self.gpa, inst, .{ // A block is a setup to be able to jump to the end. .relocs = .{}, @@ -2071,10 +2075,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { .mcv = MCValue{ .none = {} }, }); defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); - - 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); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 85e8fbecb7..0ab82abdd6 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -579,7 +579,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), .fence => try self.airFence(inst), .cond_br => try self.airCondBr(inst), - .dbg_stmt => try self.airDbgStmt(inst), .fptrunc => @panic("TODO try self.airFptrunc(inst)"), .fpext => @panic("TODO try self.airFpext(inst)"), .intcast => try self.airIntCast(inst), @@ -637,14 +636,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .@"try" => try self.airTry(inst), .try_ptr => @panic("TODO try self.airTryPtr(inst)"), + .dbg_stmt => try self.airDbgStmt(inst), + .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), - .dbg_inline_begin, - .dbg_inline_end, - => try self.airDbgInline(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), @@ -1127,6 +1124,12 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { } 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); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); +} + +fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { try self.blocks.putNoClobber(self.gpa, inst, .{ // A block is a setup to be able to jump to the end. .relocs = .{}, @@ -1139,9 +1142,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { }); defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); - 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); @@ -1652,13 +1652,14 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { - const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; +fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.comp.module.?; - const func = mod.funcInfo(ty_fn.func); + const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + const func = mod.funcInfo(extra.data.func); // TODO emit debug info for function change _ = func; - return self.finishAir(inst, .dead, .{ .none, .none, .none }); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); } fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 5439cc5c10..a77d00873d 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1910,16 +1910,11 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .@"try" => func.airTry(inst), .try_ptr => func.airTryPtr(inst), - // TODO - .dbg_inline_begin, - .dbg_inline_end, - => func.finishAir(inst, .none, &.{}), - + .dbg_stmt => func.airDbgStmt(inst), + .dbg_inline_block => func.airDbgInlineBlock(inst), .dbg_var_ptr => func.airDbgVar(inst, true), .dbg_var_val => func.airDbgVar(inst, false), - .dbg_stmt => func.airDbgStmt(inst), - .call => func.airCall(inst, .auto), .call_always_tail => func.airCall(inst, .always_tail), .call_never_tail => func.airCall(inst, .never_tail), @@ -3476,12 +3471,14 @@ fn intStorageAsI32(storage: InternPool.Key.Int.Storage, mod: *Module) i32 { } fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const block_ty = ty_pl.ty.toType(); - const wasm_block_ty = genBlockType(block_ty, mod); const extra = func.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]); + try func.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len])); +} + +fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void { + const mod = func.bin_file.base.comp.module.?; + const wasm_block_ty = genBlockType(block_ty, mod); // if wasm_block_ty is non-empty, we create a register to store the temporary value const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: { @@ -6472,7 +6469,27 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { func.finishAir(inst, result, &.{ty_op.operand}); } -fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void { +fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { + if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); + + const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; + try func.addInst(.{ .tag = .dbg_line, .data = .{ + .payload = try func.addExtra(Mir.DbgLineColumn{ + .line = dbg_stmt.line, + .column = dbg_stmt.column, + }), + } }); + func.finishAir(inst, .none, &.{}); +} + +fn airDbgInlineBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { + const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = func.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + // TODO + try func.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len])); +} + +fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) InnerError!void { if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); const mod = func.bin_file.base.comp.module.?; @@ -6497,19 +6514,6 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void { func.finishAir(inst, .none, &.{}); } -fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) !void { - if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); - - const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; - try func.addInst(.{ .tag = .dbg_line, .data = .{ - .payload = try func.addExtra(Mir.DbgLineColumn{ - .line = dbg_stmt.line, - .column = dbg_stmt.column, - }), - } }); - func.finishAir(inst, .none, &.{}); -} - fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const err_union = try func.resolveInst(pl_op.operand); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9c9c4a0064..65610e39ec 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -58,6 +58,7 @@ bin_file: *link.File, debug_output: DebugInfoOutput, target: *const std.Target, owner: Owner, +inline_func: InternPool.Index, mod: *Package.Module, err_msg: ?*ErrorMsg, args: []MCValue, @@ -820,6 +821,7 @@ pub fn generate( .bin_file = bin_file, .debug_output = debug_output, .owner = .{ .func_index = func_index }, + .inline_func = func_index, .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` .va_info = undefined, // populated after `resolveCallingConventionValues` @@ -987,6 +989,7 @@ pub fn generateLazy( .bin_file = bin_file, .debug_output = debug_output, .owner = .{ .lazy_sym = lazy_sym }, + .inline_func = undefined, .err_msg = null, .args = undefined, .va_info = undefined, @@ -2042,7 +2045,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .frame_addr => try self.airFrameAddress(inst), .fence => try self.airFence(inst), .cond_br => try self.airCondBr(inst), - .dbg_stmt => try self.airDbgStmt(inst), .fptrunc => try self.airFptrunc(inst), .fpext => try self.airFpext(inst), .intcast => try self.airIntCast(inst), @@ -2098,14 +2100,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .@"try" => try self.airTry(inst), .try_ptr => try self.airTryPtr(inst), + .dbg_stmt => try self.airDbgStmt(inst), + .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), - .dbg_inline_begin, - .dbg_inline_end, - => try self.airDbgInline(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), @@ -12962,14 +12962,23 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { self.finishAirBookkeeping(); } -fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { - const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; +fn airDbgInlineBlock(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.DbgInlineBlock, ty_pl.payload); + const old_inline_func = self.inline_func; + defer self.inline_func = old_inline_func; + self.inline_func = extra.data.func; _ = try self.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_inline_func, - .data = .{ .func = ty_fn.func }, + .data = .{ .func = extra.data.func }, + }); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); + _ = try self.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_dbg_inline_func, + .data = .{ .func = old_inline_func }, }); - self.finishAirBookkeeping(); } fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { @@ -13407,6 +13416,12 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { } 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); + try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); +} + +fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { // A block is a setup to be able to jump to the end. const inst_tracking_i = self.inst_tracking.count(); self.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(.unreach)); @@ -13415,9 +13430,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { try self.blocks.putNoClobber(self.gpa, inst, .{ .state = self.initRetroactiveState() }); const liveness = self.liveness.getBlock(inst); - 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); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 92dc904c1a..60769dc7e5 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2554,9 +2554,9 @@ pub fn genTypeDecl( .suffix, .{}, ); - try writer.writeAll("; // "); + try writer.writeAll("; /* "); try mod.declPtr(owner_decl).renderFullyQualifiedName(mod, writer); - try writer.writeByte('\n'); + try writer.writeAll(" */\n"); }, .anon_struct, @@ -3215,7 +3215,6 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .assembly => try airAsm(f, inst), .block => try airBlock(f, inst), .bitcast => try airBitcast(f, inst), - .dbg_stmt => try airDbgStmt(f, inst), .intcast => try airIntCast(f, inst), .trunc => try airTrunc(f, inst), .int_from_bool => try airIntFromBool(f, inst), @@ -3259,13 +3258,9 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .@"try" => try airTry(f, inst), .try_ptr => try airTryPtr(f, inst), - .dbg_var_ptr, - .dbg_var_val, - => try airDbgVar(f, inst), - - .dbg_inline_begin, - .dbg_inline_end, - => try airDbgInline(f, inst), + .dbg_stmt => try airDbgStmt(f, inst), + .dbg_inline_block => try airDbgInlineBlock(f, inst), + .dbg_var_ptr, .dbg_var_val => try airDbgVar(f, inst), .call => try airCall(f, inst, .auto), .call_always_tail => .none, @@ -4497,15 +4492,16 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { return .none; } -fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { - const ty_fn = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; +fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue { const mod = f.object.dg.module; + const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + const owner_decl = mod.funcOwnerDeclPtr(extra.data.func); const writer = f.object.writer(); - const owner_decl = mod.funcOwnerDeclPtr(ty_fn.func); - try writer.print("/* dbg func:{s} */\n", .{ - mod.intern_pool.stringToSlice(owner_decl.name), - }); - return .none; + try writer.writeAll("/* "); + try owner_decl.renderFullyQualifiedName(mod, writer); + try writer.writeAll(" */ "); + return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len])); } fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4522,10 +4518,13 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { } fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { - const mod = f.object.dg.module; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = f.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len]); + return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len])); +} + +fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) !CValue { + const mod = f.object.dg.module; const liveness_block = f.liveness.getBlock(inst); const block_id: usize = f.next_block_index; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 82b204cacb..3e55fe5e9d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4762,11 +4762,7 @@ pub const FuncGen = struct { file: Builder.Metadata, scope: Builder.Metadata, - inlined: std.ArrayListUnmanaged(struct { - base_line: u32, - location: Builder.DebugLocation, - scope: Builder.Metadata, - }) = .{}, + inlined: Builder.DebugLocation = .no_location, base_line: u32, prev_dbg_line: c_uint, @@ -4811,7 +4807,6 @@ pub const FuncGen = struct { fn deinit(self: *FuncGen) void { self.wip.deinit(); - self.inlined.deinit(self.gpa); self.func_inst_table.deinit(self.gpa); self.blocks.deinit(self.gpa); } @@ -5107,8 +5102,7 @@ pub const FuncGen = struct { .unreach => try self.airUnreach(inst), .dbg_stmt => try self.airDbgStmt(inst), - .dbg_inline_begin => try self.airDbgInlineBegin(inst), - .dbg_inline_end => try self.airDbgInlineEnd(inst), + .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr => try self.airDbgVarPtr(inst), .dbg_var_val => try self.airDbgVarVal(inst), @@ -5126,17 +5120,81 @@ pub const FuncGen = struct { } } - fn genBodyDebugScope(self: *FuncGen, body: []const Air.Inst.Index) Error!void { + fn genBodyDebugScope(self: *FuncGen, maybe_inline_func: ?InternPool.Index, body: []const Air.Inst.Index) Error!void { if (self.wip.strip) return self.genBody(body); + + const old_file = self.file; + const old_inlined = self.inlined; + const old_base_line = self.base_line; const old_scope = self.scope; + defer if (maybe_inline_func) |_| { + self.wip.debug_location = self.inlined; + self.file = old_file; + self.inlined = old_inlined; + self.base_line = old_base_line; + }; + defer self.scope = old_scope; + + if (maybe_inline_func) |inline_func| { + const o = self.dg.object; + const zcu = o.module; + + const func = zcu.funcInfo(inline_func); + const decl_index = func.owner_decl; + const decl = zcu.declPtr(decl_index); + const namespace = zcu.namespacePtr(decl.src_namespace); + const owner_mod = namespace.file_scope.mod; + + self.file = try o.getDebugFile(namespace.file_scope); + + const line_number = decl.src_line + 1; + self.inlined = self.wip.debug_location; + + const fqn = try decl.fullyQualifiedName(zcu); + + const is_internal_linkage = !zcu.decl_exports.contains(decl_index); + const fn_ty = try zcu.funcType(.{ + .param_types = &.{}, + .return_type = .void_type, + }); + + self.scope = try o.builder.debugSubprogram( + self.file, + try o.builder.metadataString(zcu.intern_pool.stringToSlice(decl.name)), + try o.builder.metadataString(zcu.intern_pool.stringToSlice(fqn)), + line_number, + line_number + func.lbrace_line, + try o.lowerDebugType(fn_ty), + .{ + .di_flags = .{ .StaticMember = true }, + .sp_flags = .{ + .Optimized = owner_mod.optimize_mode != .Debug, + .Definition = true, + .LocalToUnit = is_internal_linkage, + }, + }, + o.debug_compile_unit, + ); + + self.base_line = decl.src_line; + const inlined_at_location = try self.wip.debug_location.toMetadata(&o.builder); + self.wip.debug_location = .{ + .location = .{ + .line = line_number, + .column = 0, + .scope = self.scope, + .inlined_at = inlined_at_location, + }, + }; + } + self.scope = try self.dg.object.builder.debugLexicalBlock( - old_scope, + self.scope, self.file, self.prev_dbg_line, self.prev_dbg_column, ); try self.genBody(body); - self.scope = old_scope; } pub const CallAttr = enum { @@ -5820,15 +5878,23 @@ pub const FuncGen = struct { } fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { - const o = self.dg.object; - const mod = o.module; 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]); + return self.lowerBlock(inst, null, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); + } + + fn lowerBlock( + self: *FuncGen, + inst: Air.Inst.Index, + maybe_inline_func: ?InternPool.Index, + body: []const Air.Inst.Index, + ) !Builder.Value { + const o = self.dg.object; + const mod = o.module; const inst_ty = self.typeOfIndex(inst); if (inst_ty.isNoReturn(mod)) { - try self.genBodyDebugScope(body); + try self.genBodyDebugScope(maybe_inline_func, body); return .none; } @@ -5844,7 +5910,7 @@ pub const FuncGen = struct { }); defer assert(self.blocks.remove(inst)); - try self.genBodyDebugScope(body); + try self.genBodyDebugScope(maybe_inline_func, body); self.wip.cursor = .{ .block = parent_bb }; @@ -5903,10 +5969,10 @@ pub const FuncGen = struct { _ = try self.wip.brCond(cond, then_block, else_block); self.wip.cursor = .{ .block = then_block }; - try self.genBodyDebugScope(then_body); + try self.genBodyDebugScope(null, then_body); self.wip.cursor = .{ .block = else_block }; - try self.genBodyDebugScope(else_body); + try self.genBodyDebugScope(null, else_body); // No need to reset the insert cursor since this instruction is noreturn. return .none; @@ -5987,7 +6053,7 @@ pub const FuncGen = struct { _ = try fg.wip.brCond(is_err, return_block, continue_block); fg.wip.cursor = .{ .block = return_block }; - try fg.genBodyDebugScope(body); + try fg.genBodyDebugScope(null, body); fg.wip.cursor = .{ .block = continue_block }; } @@ -6060,13 +6126,13 @@ pub const FuncGen = struct { } self.wip.cursor = .{ .block = case_block }; - try self.genBodyDebugScope(case_body); + try self.genBodyDebugScope(null, case_body); } self.wip.cursor = .{ .block = else_block }; const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]); if (else_body.len != 0) { - try self.genBodyDebugScope(else_body); + try self.genBodyDebugScope(null, else_body); } else { _ = try self.wip.@"unreachable"(); } @@ -6085,7 +6151,7 @@ pub const FuncGen = struct { _ = try self.wip.br(loop_block); self.wip.cursor = .{ .block = loop_block }; - try self.genBodyDebugScope(body); + try self.genBodyDebugScope(null, body); // TODO instead of this logic, change AIR to have the property that // every block is guaranteed to end with a noreturn instruction. @@ -6592,96 +6658,22 @@ pub const FuncGen = struct { self.prev_dbg_line = @intCast(self.base_line + dbg_stmt.line + 1); self.prev_dbg_column = @intCast(dbg_stmt.column + 1); - const inlined_at_location = if (self.inlined.getLastOrNull()) |inlined| - try inlined.location.toMetadata(self.wip.builder) - else - .none; - self.wip.debug_location = .{ .location = .{ .line = self.prev_dbg_line, .column = self.prev_dbg_column, .scope = self.scope, - .inlined_at = inlined_at_location, + .inlined_at = try self.inlined.toMetadata(self.wip.builder), }, }; return .none; } - fn airDbgInlineBegin(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { - const o = self.dg.object; - const zcu = o.module; - - const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const func = zcu.funcInfo(ty_fn.func); - const decl_index = func.owner_decl; - const decl = zcu.declPtr(decl_index); - const namespace = zcu.namespacePtr(decl.src_namespace); - const owner_mod = namespace.file_scope.mod; - - self.file = try o.getDebugFile(namespace.file_scope); - - const line_number = decl.src_line + 1; - try self.inlined.append(self.gpa, .{ - .location = self.wip.debug_location, - .scope = self.scope, - .base_line = self.base_line, - }); - - const fqn = try decl.fullyQualifiedName(zcu); - - const is_internal_linkage = !zcu.decl_exports.contains(decl_index); - const fn_ty = try zcu.funcType(.{ - .param_types = &.{}, - .return_type = .void_type, - }); - - self.scope = try o.builder.debugSubprogram( - self.file, - try o.builder.metadataString(zcu.intern_pool.stringToSlice(decl.name)), - try o.builder.metadataString(zcu.intern_pool.stringToSlice(fqn)), - line_number, - line_number + func.lbrace_line, - try o.lowerDebugType(fn_ty), - .{ - .di_flags = .{ .StaticMember = true }, - .sp_flags = .{ - .Optimized = owner_mod.optimize_mode != .Debug, - .Definition = true, - .LocalToUnit = is_internal_linkage, - }, - }, - o.debug_compile_unit, - ); - - self.base_line = decl.src_line; - const inlined_at_location = try self.wip.debug_location.toMetadata(&o.builder); - self.wip.debug_location = .{ - .location = .{ - .line = line_number, - .column = 0, - .scope = self.scope, - .inlined_at = inlined_at_location, - }, - }; - return .none; - } - - fn airDbgInlineEnd(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { - const o = self.dg.object; - - const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - - const mod = o.module; - const decl = mod.funcOwnerDeclPtr(ty_fn.func); - self.file = try o.getDebugFile(mod.namespacePtr(decl.src_namespace).file_scope); - - const old = self.inlined.pop(); - self.scope = old.scope; - self.base_line = old.base_line; - self.wip.debug_location = old.location; - return .none; + fn airDbgInlineBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { + const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + return self.lowerBlock(inst, extra.data.func, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); } fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index fb8d1e1e8e..59cbb9d2d3 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -208,6 +208,7 @@ pub const Object = struct { false => .{ .unstructured = .{} }, }, .current_block_label = undefined, + .base_line = decl.src_line, }; defer decl_gen.deinit(); @@ -321,9 +322,8 @@ const DeclGen = struct { /// The code (prologue and body) for the function we are currently generating code for. func: SpvModule.Fn = .{}, - /// Stack of the base offsets of the current decl, which is what `dbg_stmt` is relative to. - /// This is a stack to keep track of inline functions. - base_line_stack: std.ArrayListUnmanaged(u32) = .{}, + /// The base offset of the current decl, which is what `dbg_stmt` is relative to. + base_line: u32, /// If `gen` returned `Error.CodegenFail`, this contains an explanatory message. /// Memory is owned by `module.gpa`. @@ -401,7 +401,6 @@ const DeclGen = struct { self.wip_pointers.deinit(self.gpa); self.control_flow.deinit(self.gpa); self.func.deinit(self.gpa); - self.base_line_stack.deinit(self.gpa); } /// Return the target which we are currently compiling for. @@ -1959,8 +1958,6 @@ const DeclGen = struct { const decl_id = self.spv.declPtr(spv_decl_index).result_id; - try self.base_line_stack.append(self.gpa, decl.src_line); - if (decl.val.getFunction(mod)) |_| { assert(decl.ty.zigTypeTag(mod) == .Fn); const fn_info = mod.typeToFunc(decl.ty).?; @@ -2317,8 +2314,7 @@ const DeclGen = struct { .unreach, .trap => return self.airUnreach(), .dbg_stmt => return self.airDbgStmt(inst), - .dbg_inline_begin => return self.airDbgInlineBegin(inst), - .dbg_inline_end => return self.airDbgInlineEnd(inst), + .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val => return self.airDbgVar(inst), .unwrap_errunion_err => try self.airErrUnionErr(inst), @@ -4311,6 +4307,12 @@ const DeclGen = struct { } fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { + const inst_datas = self.air.instructions.items(.data); + const extra = self.air.extraData(Air.Block, inst_datas[@intFromEnum(inst)].ty_pl.payload); + return self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); + } + + fn lowerBlock(self: *DeclGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) !?IdRef { // In AIR, a block doesn't really define an entry point like a block, but // more like a scope that breaks can jump out of and "return" a value from. // This cannot be directly modelled in SPIR-V, so in a block instruction, @@ -4320,10 +4322,6 @@ const DeclGen = struct { const mod = self.module; const ty = self.typeOfIndex(inst); - const inst_datas = self.air.instructions.items(.data); - const extra = self.air.extraData(Air.Block, inst_datas[@intFromEnum(inst)].ty_pl.payload); - const body: []const Air.Inst.Index = - @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); const have_block_result = ty.isFnOrHasRuntimeBitsIgnoreComptime(mod); const cf = switch (self.control_flow) { @@ -5157,25 +5155,22 @@ const DeclGen = struct { const decl = mod.declPtr(self.decl_index); const path = decl.getFileScope(mod).sub_file_path; const src_fname_id = try self.spv.resolveSourceFileName(path); - const base_line = self.base_line_stack.getLast(); try self.func.body.emit(self.spv.gpa, .OpLine, .{ .file = src_fname_id, - .line = base_line + dbg_stmt.line + 1, + .line = self.base_line + dbg_stmt.line + 1, .column = dbg_stmt.column + 1, }); } - fn airDbgInlineBegin(self: *DeclGen, inst: Air.Inst.Index) !void { + fn airDbgInlineBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { const mod = self.module; - const fn_ty = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const decl_index = mod.funcInfo(fn_ty.func).owner_decl; - const decl = mod.declPtr(decl_index); - try self.base_line_stack.append(self.gpa, decl.src_line); - } - - fn airDbgInlineEnd(self: *DeclGen, inst: Air.Inst.Index) !void { - _ = inst; - _ = self.base_line_stack.pop(); + const inst_datas = self.air.instructions.items(.data); + const extra = self.air.extraData(Air.DbgInlineBlock, inst_datas[@intFromEnum(inst)].ty_pl.payload); + const decl = mod.funcOwnerDeclPtr(extra.data.func); + const old_base_line = self.base_line; + defer self.base_line = old_base_line; + self.base_line = decl.src_line; + return self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); } fn airDbgVar(self: *DeclGen, inst: Air.Inst.Index) !void { diff --git a/src/print_air.zig b/src/print_air.zig index e4f4121299..8af0301c1a 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -260,7 +260,7 @@ const Writer = struct { .c_va_copy, => try w.writeTyOp(s, inst), - .block => try w.writeBlock(s, inst), + .block, .dbg_inline_block => try w.writeBlock(s, tag, inst), .loop => try w.writeLoop(s, inst), @@ -292,7 +292,6 @@ const Writer = struct { .assembly => try w.writeAssembly(s, inst), .dbg_stmt => try w.writeDbgStmt(s, inst), - .dbg_inline_begin, .dbg_inline_end => try w.writeDbgInline(s, inst), .aggregate_init => try w.writeAggregateInit(s, inst), .union_init => try w.writeUnionInit(s, inst), .br => try w.writeBr(s, inst), @@ -367,17 +366,34 @@ const Writer = struct { try w.writeOperand(s, inst, 0, ty_op.operand); } - fn writeBlock(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + fn writeBlock(w: *Writer, s: anytype, tag: Air.Inst.Tag, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = w.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(w.air.extra[extra.end..][0..extra.data.body_len]); + try w.writeType(s, ty_pl.ty.toType()); + const body: []const Air.Inst.Index = @ptrCast(switch (tag) { + inline .block, .dbg_inline_block => |comptime_tag| body: { + const extra = w.air.extraData(switch (comptime_tag) { + .block => Air.Block, + .dbg_inline_block => Air.DbgInlineBlock, + else => unreachable, + }, ty_pl.payload); + switch (comptime_tag) { + .block => {}, + .dbg_inline_block => { + try s.writeAll(", "); + try w.writeInstRef(s, Air.internedToRef(extra.data.func), false); + }, + else => unreachable, + } + break :body w.air.extra[extra.end..][0..extra.data.body_len]; + }, + else => unreachable, + }); + if (w.skip_body) return s.writeAll(", ..."); const liveness_block = if (w.liveness) |liveness| liveness.getBlock(inst) else Liveness.BlockSlices{ .deaths = &.{} }; - try w.writeType(s, ty_pl.ty.toType()); - if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; @@ -661,13 +677,6 @@ const Writer = struct { try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); } - fn writeDbgInline(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_fn = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const func_index = ty_fn.func; - const owner_decl = w.module.funcOwnerDeclPtr(func_index); - try s.print("{}", .{owner_decl.name.fmt(&w.module.intern_pool)}); - } - fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; try w.writeOperand(s, inst, 0, pl_op.operand); diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index a0adbb818f..429caf0e7e 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -913,3 +913,20 @@ test "switch prong captures range" { S.a(&arr, 5); try expect(arr[5] == 5); } + +test "prong with inline call to unreachable" { + const U = union(enum) { + void: void, + bool: bool, + + inline fn unreach() noreturn { + unreachable; + } + }; + var u: U = undefined; + u = .{ .bool = true }; + switch (u) { + .void => U.unreach(), + .bool => |ok| try expect(ok), + } +}