ERR/E3.0 (slice 2): emit DWARF line-info
Attach LLVM debug metadata so a captured return-address PC resolves to file:line:col (the runtime half E3.3 needs) and sx binaries become debuggable in lldb/gdb. - llvm_api.zig: bind llvm-c/DebugInfo.h (DIBuilder C API was unbound). - emit_llvm.zig: DIBuilder + one DICompileUnit/DIFile on the main file, a DISubprogram per function (LLVMSetSubprogram), and a DILocation per instruction from Inst.span (errors.SourceLoc.compute, scoped to the subprogram). Plus the "Debug Info Version"/"Dwarf Version" module flags and LLVMDIBuilderFinalize. - Gated on opt none/less + a wired source map (setDebugContext from core.zig), mirroring lower.zig's tracesEnabled; release strips it. Verified: sx ir/sx asm --opt none show correct DILocations + .loc directives; the 290-example JIT suite (-O0 -> debug on) verifies and runs unchanged. +2 DWARF unit tests.
This commit is contained in:
@@ -951,3 +951,69 @@ test "emit: box_any and unbox_any" {
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "insertvalue") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "extractvalue") != null);
|
||||
}
|
||||
|
||||
test "emit: ERR E3.0 — DWARF debug info (compile unit + subprogram + per-inst location)" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func main() -> s64 { return 42; } — with the `return` instruction
|
||||
// carrying a span that lands on line 3 of the source map below.
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
// "a\nb\nXYZ" — byte offset 4 ('X') is line 3, col 1.
|
||||
b.current_span = .{ .start = 4, .end = 5 };
|
||||
const c42 = b.constInt(42, .s64);
|
||||
b.ret(c42, .s64);
|
||||
b.finalize();
|
||||
|
||||
// Source map keyed on the main file. setDebugContext + opt none
|
||||
// turns DWARF emission on (release opt levels skip it entirely).
|
||||
var sources = std.StringHashMap([:0]const u8).init(alloc);
|
||||
defer sources.deinit();
|
||||
try sources.put("probe.sx", "a\nb\nXYZ");
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_dwarf", .{ .opt_level = .none });
|
||||
defer emitter.deinit();
|
||||
emitter.setDebugContext(&sources, "probe.sx");
|
||||
emitter.emit();
|
||||
|
||||
try std.testing.expect(emitter.verify());
|
||||
|
||||
const ir_str = emitter.dumpToString();
|
||||
// Module flags, compile unit on the main file, a subprogram for main,
|
||||
// and the return instruction's location resolved to line 3.
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "\"Debug Info Version\"") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "\"Dwarf Version\"") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "DICompileUnit") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "DIFile(filename: \"probe.sx\"") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "DISubprogram(name: \"main\"") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "DILocation(line: 3") != null);
|
||||
}
|
||||
|
||||
test "emit: ERR E3.0 — no DWARF without a debug context (unit-test default)" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
var b = Builder.init(&module);
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
b.ret(b.constInt(42, .s64), .s64);
|
||||
b.finalize();
|
||||
|
||||
// No setDebugContext call → no source map → debug info off even at
|
||||
// opt none. Confirms the gate keeps the metadata out by default.
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_no_dwarf", .{ .opt_level = .none });
|
||||
defer emitter.deinit();
|
||||
emitter.emit();
|
||||
|
||||
try std.testing.expect(emitter.verify());
|
||||
const ir_str = emitter.dumpToString();
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "DICompileUnit") == null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "!dbg") == null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user