Commit eadcefc124
Changed files (8)
src/link/Elf.zig
@@ -2221,21 +2221,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
// For functions we need to add a prologue to the debug line program.
try dbg_line_buffer.ensureCapacity(26);
- const line_off: u28 = blk: {
- const tree = decl.namespace.file_scope.tree;
- const node_tags = tree.nodes.items(.tag);
- const node_datas = tree.nodes.items(.data);
- const token_starts = tree.tokens.items(.start);
-
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table. Currently this involves scanning over the source code for newlines.
- const fn_decl = decl.src_node;
- assert(node_tags[fn_decl] == .fn_decl);
- const block = node_datas[fn_decl].rhs;
- const lbrace = tree.firstToken(block);
- const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]);
- break :blk @intCast(u28, line_delta);
- };
+ const func = decl.val.castTag(.function).?.data;
+ const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
const ptr_width_bytes = self.ptrWidthBytes();
dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
@@ -2750,19 +2737,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
if (self.llvm_object) |_| return;
- const tree = decl.namespace.file_scope.tree;
- const node_tags = tree.nodes.items(.tag);
- const node_datas = tree.nodes.items(.data);
- const token_starts = tree.tokens.items(.start);
-
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table. Currently this involves scanning over the source code for newlines.
- const fn_decl = decl.src_node;
- assert(node_tags[fn_decl] == .fn_decl);
- const block = node_datas[fn_decl].rhs;
- const lbrace = tree.firstToken(block);
- const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]);
- const casted_line_off = @intCast(u28, line_delta);
+ const func = decl.val.castTag(.function).?.data;
+ const casted_line_off = @intCast(u28, decl.src_line + func.lbrace_line);
const shdr = &self.sections.items[self.debug_line_section_index.?];
const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
src/AstGen.zig
@@ -94,6 +94,7 @@ pub fn generate(gpa: *Allocator, file: *Scope.File) InnerError!Zir {
.force_comptime = true,
.parent = &file.base,
.decl_node_index = 0,
+ .decl_line = 0,
.astgen = &astgen,
};
defer gen_scope.instructions.deinit(gpa);
@@ -2056,7 +2057,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
// ZIR instructions that are always either `noreturn` or `void`.
.breakpoint,
.fence,
- .dbg_stmt_node,
+ .dbg_stmt,
.ensure_result_used,
.ensure_result_non_error,
.@"export",
@@ -2395,9 +2396,25 @@ fn varDecl(
}
fn emitDbgNode(gz: *GenZir, node: ast.Node.Index) !void {
- if (!gz.force_comptime) {
- _ = try gz.addNode(.dbg_stmt_node, node);
- }
+ // The instruction emitted here is for debugging runtime code.
+ // If the current block will be evaluated only during semantic analysis
+ // then no dbg_stmt ZIR instruction is needed.
+ if (gz.force_comptime) return;
+
+ const astgen = gz.astgen;
+ const tree = &astgen.file.tree;
+ const node_tags = tree.nodes.items(.tag);
+ const token_starts = tree.tokens.items(.start);
+ const decl_start = token_starts[tree.firstToken(gz.decl_node_index)];
+ const node_start = token_starts[tree.firstToken(node)];
+ const source = tree.source[decl_start..node_start];
+ const loc = std.zig.findLineColumn(source, source.len);
+ _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
+ .dbg_stmt = .{
+ .line = @intCast(u32, loc.line),
+ .column = @intCast(u32, loc.column),
+ },
+ } });
}
fn assign(gz: *GenZir, scope: *Scope, infix_node: ast.Node.Index) InnerError!void {
@@ -2689,6 +2706,7 @@ fn fnDecl(
var decl_gz: GenZir = .{
.force_comptime = true,
.decl_node_index = fn_proto.ast.proto_node,
+ .decl_line = gz.calcLine(decl_node),
.parent = &gz.base,
.astgen = astgen,
};
@@ -2791,7 +2809,7 @@ fn fnDecl(
return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
}
break :func try decl_gz.addFunc(.{
- .src_node = fn_proto.ast.proto_node,
+ .src_node = decl_node,
.ret_ty = return_type_inst,
.param_types = param_types,
.body = &[0]Zir.Inst.Index{},
@@ -2810,6 +2828,7 @@ fn fnDecl(
var fn_gz: GenZir = .{
.force_comptime = false,
.decl_node_index = fn_proto.ast.proto_node,
+ .decl_line = decl_gz.decl_line,
.parent = &decl_gz.base,
.astgen = astgen,
};
@@ -2866,7 +2885,7 @@ fn fnDecl(
astgen.fn_block = prev_fn_block;
break :func try decl_gz.addFunc(.{
- .src_node = fn_proto.ast.proto_node,
+ .src_node = decl_node,
.ret_ty = return_type_inst,
.param_types = param_types,
.body = fn_gz.instructions.items,
@@ -2889,12 +2908,16 @@ fn fnDecl(
_ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
try decl_gz.setBlockBody(block_inst);
- try wip_decls.payload.ensureUnusedCapacity(gpa, 8);
+ try wip_decls.payload.ensureUnusedCapacity(gpa, 9);
{
const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
const casted = @bitCast([4]u32, contents_hash);
wip_decls.payload.appendSliceAssumeCapacity(&casted);
}
+ {
+ const line_delta = decl_gz.decl_line - gz.decl_line;
+ wip_decls.payload.appendAssumeCapacity(line_delta);
+ }
wip_decls.payload.appendAssumeCapacity(fn_name_str_index);
wip_decls.payload.appendAssumeCapacity(block_inst);
if (align_inst != .none) {
@@ -2925,6 +2948,7 @@ fn globalVarDecl(
var block_scope: GenZir = .{
.parent = scope,
.decl_node_index = node,
+ .decl_line = gz.calcLine(node),
.astgen = astgen,
.force_comptime = true,
};
@@ -3024,12 +3048,16 @@ fn globalVarDecl(
const name_token = var_decl.ast.mut_token + 1;
const name_str_index = try astgen.identAsString(name_token);
- try wip_decls.payload.ensureUnusedCapacity(gpa, 8);
+ try wip_decls.payload.ensureUnusedCapacity(gpa, 9);
{
const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
const casted = @bitCast([4]u32, contents_hash);
wip_decls.payload.appendSliceAssumeCapacity(&casted);
}
+ {
+ const line_delta = block_scope.decl_line - gz.decl_line;
+ wip_decls.payload.appendAssumeCapacity(line_delta);
+ }
wip_decls.payload.appendAssumeCapacity(name_str_index);
wip_decls.payload.appendAssumeCapacity(block_inst);
if (align_inst != .none) {
@@ -3060,6 +3088,7 @@ fn comptimeDecl(
var decl_block: GenZir = .{
.force_comptime = true,
.decl_node_index = node,
+ .decl_line = gz.calcLine(node),
.parent = scope,
.astgen = astgen,
};
@@ -3071,12 +3100,16 @@ fn comptimeDecl(
}
try decl_block.setBlockBody(block_inst);
- try wip_decls.payload.ensureUnusedCapacity(gpa, 6);
+ try wip_decls.payload.ensureUnusedCapacity(gpa, 7);
{
const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
const casted = @bitCast([4]u32, contents_hash);
wip_decls.payload.appendSliceAssumeCapacity(&casted);
}
+ {
+ const line_delta = decl_block.decl_line - gz.decl_line;
+ wip_decls.payload.appendAssumeCapacity(line_delta);
+ }
wip_decls.payload.appendAssumeCapacity(0);
wip_decls.payload.appendAssumeCapacity(block_inst);
}
@@ -3107,6 +3140,7 @@ fn usingnamespaceDecl(
var decl_block: GenZir = .{
.force_comptime = true,
.decl_node_index = node,
+ .decl_line = gz.calcLine(node),
.parent = scope,
.astgen = astgen,
};
@@ -3116,12 +3150,16 @@ fn usingnamespaceDecl(
_ = try decl_block.addBreak(.break_inline, block_inst, namespace_inst);
try decl_block.setBlockBody(block_inst);
- try wip_decls.payload.ensureUnusedCapacity(gpa, 6);
+ try wip_decls.payload.ensureUnusedCapacity(gpa, 7);
{
const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
const casted = @bitCast([4]u32, contents_hash);
wip_decls.payload.appendSliceAssumeCapacity(&casted);
}
+ {
+ const line_delta = decl_block.decl_line - gz.decl_line;
+ wip_decls.payload.appendAssumeCapacity(line_delta);
+ }
wip_decls.payload.appendAssumeCapacity(0);
wip_decls.payload.appendAssumeCapacity(block_inst);
}
@@ -3147,6 +3185,7 @@ fn testDecl(
var decl_block: GenZir = .{
.force_comptime = true,
.decl_node_index = node,
+ .decl_line = gz.calcLine(node),
.parent = scope,
.astgen = astgen,
};
@@ -3167,6 +3206,7 @@ fn testDecl(
var fn_block: GenZir = .{
.force_comptime = false,
.decl_node_index = node,
+ .decl_line = decl_block.decl_line,
.parent = &decl_block.base,
.astgen = astgen,
};
@@ -3200,12 +3240,16 @@ fn testDecl(
_ = try decl_block.addBreak(.break_inline, block_inst, func_inst);
try decl_block.setBlockBody(block_inst);
- try wip_decls.payload.ensureUnusedCapacity(gpa, 6);
+ try wip_decls.payload.ensureUnusedCapacity(gpa, 7);
{
const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
const casted = @bitCast([4]u32, contents_hash);
wip_decls.payload.appendSliceAssumeCapacity(&casted);
}
+ {
+ const line_delta = decl_block.decl_line - gz.decl_line;
+ wip_decls.payload.appendAssumeCapacity(line_delta);
+ }
wip_decls.payload.appendAssumeCapacity(test_name);
wip_decls.payload.appendAssumeCapacity(block_inst);
}
@@ -3237,6 +3281,7 @@ fn structDeclInner(
var block_scope: GenZir = .{
.parent = scope,
.decl_node_index = node,
+ .decl_line = gz.calcLine(node),
.astgen = astgen,
.force_comptime = true,
.ref_start_index = gz.ref_start_index,
@@ -3448,6 +3493,7 @@ fn unionDeclInner(
var block_scope: GenZir = .{
.parent = scope,
.decl_node_index = node,
+ .decl_line = gz.calcLine(node),
.astgen = astgen,
.force_comptime = true,
.ref_start_index = gz.ref_start_index,
@@ -3797,6 +3843,7 @@ fn containerDecl(
var block_scope: GenZir = .{
.parent = scope,
.decl_node_index = node,
+ .decl_line = gz.calcLine(node),
.astgen = astgen,
.force_comptime = true,
.ref_start_index = gz.ref_start_index,
@@ -4464,7 +4511,9 @@ fn boolBinOp(
node: ast.Node.Index,
zir_tag: Zir.Inst.Tag,
) InnerError!Zir.Inst.Ref {
- const node_datas = gz.tree().nodes.items(.data);
+ const astgen = gz.astgen;
+ const tree = &astgen.file.tree;
+ const node_datas = tree.nodes.items(.data);
const lhs = try expr(gz, scope, bool_rl, node_datas[node].lhs);
const bool_br = try gz.addBoolBr(zir_tag, lhs);
src/codegen.zig
@@ -264,14 +264,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
src_loc: Module.SrcLoc,
stack_align: u32,
- /// Byte offset within the source file.
- prev_di_src: usize,
+ prev_di_line: u32,
+ prev_di_column: u32,
+ /// Byte offset within the source file of the ending curly.
+ end_di_line: u32,
+ end_di_column: u32,
/// Relative to the beginning of `code`.
prev_di_pc: usize,
- /// Used to find newlines and count line deltas.
- source: []const u8,
- /// Byte offset within the source file of the ending curly.
- rbrace_src: usize,
/// The value is an offset into the `Function` `code` from the beginning.
/// To perform the reloc, write 32-bit signed little-endian integer
@@ -411,25 +410,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
try branch_stack.append(.{});
- const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: {
- const namespace = module_fn.owner_decl.namespace;
- const tree = namespace.file_scope.tree;
- const node_tags = tree.nodes.items(.tag);
- const node_datas = tree.nodes.items(.data);
- const token_starts = tree.tokens.items(.start);
-
- const fn_decl = module_fn.owner_decl.src_node;
- assert(node_tags[fn_decl] == .fn_decl);
- const block = node_datas[fn_decl].rhs;
- const lbrace_src = token_starts[tree.firstToken(block)];
- const rbrace_src = token_starts[tree.lastToken(block)];
- break :blk .{
- .lbrace_src = lbrace_src,
- .rbrace_src = rbrace_src,
- .source = tree.source,
- };
- };
-
var function = Self{
.gpa = bin_file.allocator,
.target = &bin_file.options.target,
@@ -446,9 +426,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.src_loc = src_loc,
.stack_align = undefined,
.prev_di_pc = 0,
- .prev_di_src = src_data.lbrace_src,
- .rbrace_src = src_data.rbrace_src,
- .source = src_data.source,
+ .prev_di_line = module_fn.lbrace_line,
+ .prev_di_column = module_fn.lbrace_column,
+ .end_di_line = module_fn.rbrace_line,
+ .end_di_column = module_fn.rbrace_column,
};
defer function.stack.deinit(bin_file.allocator);
defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
@@ -701,7 +682,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
},
}
// Drop them off at the rbrace.
- try self.dbgAdvancePCAndLine(self.rbrace_src);
+ try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);
}
fn genBody(self: *Self, body: ir.Body) InnerError!void {
@@ -727,7 +708,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (self.debug_output) {
.dwarf => |dbg_out| {
try dbg_out.dbg_line.append(DW.LNS_set_prologue_end);
- try self.dbgAdvancePCAndLine(self.prev_di_src);
+ try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
},
.none => {},
}
@@ -737,27 +718,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (self.debug_output) {
.dwarf => |dbg_out| {
try dbg_out.dbg_line.append(DW.LNS_set_epilogue_begin);
- try self.dbgAdvancePCAndLine(self.prev_di_src);
+ try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
},
.none => {},
}
}
- fn dbgAdvancePCAndLine(self: *Self, abs_byte_off: usize) InnerError!void {
- self.prev_di_src = abs_byte_off;
- self.prev_di_pc = self.code.items.len;
+ fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {
switch (self.debug_output) {
.dwarf => |dbg_out| {
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table, and changing ir.Inst from storing byte offset to token. Currently
- // this involves scanning over the source code for newlines
- // (but only from the previous byte offset to the new one).
- const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, abs_byte_off);
+ const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
const delta_pc = self.code.items.len - self.prev_di_pc;
- // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
- // single-byte opcodes that add different numbers to both the PC and the line number
- // at the same time.
- try dbg_out.dbg_line.ensureCapacity(dbg_out.dbg_line.items.len + 11);
+ // TODO Look into using the DWARF special opcodes to compress this data.
+ // It lets you emit single-byte opcodes that add different numbers to
+ // both the PC and the line number at the same time.
+ try dbg_out.dbg_line.ensureUnusedCapacity(11);
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
if (delta_line != 0) {
@@ -768,6 +743,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
},
.none => {},
}
+ self.prev_di_line = line;
+ self.prev_di_column = column;
+ self.prev_di_pc = self.code.items.len;
}
/// Asserts there is already capacity to insert into top branch inst_table.
@@ -2317,7 +2295,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// well to be more efficient, as well as support inlined function calls correctly.
// For now we convert LazySrcLoc to absolute byte offset, to match what the
// existing codegen code expects.
- try self.dbgAdvancePCAndLine(inst.byte_offset);
+ try self.dbgAdvancePCAndLine(inst.line, inst.column);
assert(inst.base.isUnused());
return MCValue.dead;
}
src/ir.zig
@@ -622,7 +622,8 @@ pub const Inst = struct {
pub const base_tag = Tag.dbg_stmt;
base: Inst,
- byte_offset: u32,
+ line: u32,
+ column: u32,
pub fn operandCount(self: *const DbgStmt) usize {
return 0;
src/Module.zig
@@ -182,9 +182,12 @@ pub const Decl = struct {
/// The AST node index of this declaration.
/// Must be recomputed when the corresponding source file is modified.
src_node: ast.Node.Index,
+ /// Line number corresponding to `src_node`. Stored separately so that source files
+ /// do not need to be loaded into memory in order to compute debug line numbers.
+ src_line: u32,
/// Index to ZIR `extra` array to the entry in the parent's decl structure
/// (the part that says "for every decls_len"). The first item at this index is
- /// the contents hash, followed by the name.
+ /// the contents hash, followed by line, name, etc.
zir_decl_index: Zir.Inst.Index,
/// Represents the "shallow" analysis status. For example, for decls that are functions,
@@ -282,6 +285,7 @@ pub const Decl = struct {
if (decl.val.castTag(.function)) |payload| {
const func = payload.data;
func.deinit(gpa);
+ gpa.destroy(func);
} else if (decl.val.getTypeNamespace()) |namespace| {
if (namespace.getDecl() == decl) {
namespace.clearDecls(module);
@@ -323,7 +327,7 @@ pub const Decl = struct {
}
pub fn getNameZir(decl: Decl, zir: Zir) ?[:0]const u8 {
- const name_index = zir.extra[decl.zir_decl_index + 4];
+ const name_index = zir.extra[decl.zir_decl_index + 5];
if (name_index <= 1) return null;
return zir.nullTerminatedString(name_index);
}
@@ -341,7 +345,7 @@ pub const Decl = struct {
pub fn zirBlockIndex(decl: Decl) Zir.Inst.Index {
const zir = decl.namespace.file_scope.zir;
- return zir.extra[decl.zir_decl_index + 5];
+ return zir.extra[decl.zir_decl_index + 6];
}
pub fn zirAlignRef(decl: Decl) Zir.Inst.Ref {
@@ -357,6 +361,10 @@ pub const Decl = struct {
return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
}
+ pub fn relativeToLine(decl: Decl, offset: u32) u32 {
+ return decl.src_line + offset;
+ }
+
pub fn relativeToNodeIndex(decl: Decl, offset: i32) ast.Node.Index {
return @bitCast(ast.Node.Index, offset + @bitCast(i32, decl.src_node));
}
@@ -565,13 +573,21 @@ pub const EnumFull = struct {
/// the `Decl` only, with a `Value` tag of `extern_fn`.
pub const Fn = struct {
owner_decl: *Decl,
+ /// undefined unless analysis state is `success`.
+ body: ir.Body,
/// The ZIR instruction that is a function instruction. Use this to find
/// the body. We store this rather than the body directly so that when ZIR
/// is regenerated on update(), we can map this to the new corresponding
/// ZIR instruction.
zir_body_inst: Zir.Inst.Index,
- /// undefined unless analysis state is `success`.
- body: ir.Body,
+
+ /// Relative to owner Decl.
+ lbrace_line: u32,
+ /// Relative to owner Decl.
+ rbrace_line: u32,
+ lbrace_column: u16,
+ rbrace_column: u16,
+
state: Analysis,
pub const Analysis = enum {
@@ -1130,7 +1146,7 @@ pub const Scope = struct {
return &inst.base;
}
- pub fn addDbgStmt(block: *Scope.Block, src: LazySrcLoc, abs_byte_off: u32) !*ir.Inst {
+ pub fn addDbgStmt(block: *Scope.Block, src: LazySrcLoc, line: u32, column: u32) !*ir.Inst {
const inst = try block.sema.arena.create(ir.Inst.DbgStmt);
inst.* = .{
.base = .{
@@ -1138,7 +1154,8 @@ pub const Scope = struct {
.ty = Type.initTag(.void),
.src = src,
},
- .byte_offset = abs_byte_off,
+ .line = line,
+ .column = column,
};
try block.instructions.append(block.sema.gpa, &inst.base);
return &inst.base;
@@ -1177,6 +1194,8 @@ pub const Scope = struct {
ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len,
/// The containing decl AST node.
decl_node_index: ast.Node.Index,
+ /// The containing decl line index, absolute.
+ decl_line: u32,
/// Parents can be: `GenZir`, `File`
parent: *Scope,
/// All `GenZir` scopes for the same ZIR share this.
@@ -1218,6 +1237,7 @@ pub const Scope = struct {
.force_comptime = gz.force_comptime,
.ref_start_index = gz.ref_start_index,
.decl_node_index = gz.decl_node_index,
+ .decl_line = gz.decl_line,
.parent = scope,
.astgen = gz.astgen,
.suspend_node = gz.suspend_node,
@@ -1239,6 +1259,18 @@ pub const Scope = struct {
return false;
}
+ pub fn calcLine(gz: GenZir, node: ast.Node.Index) u32 {
+ const astgen = gz.astgen;
+ const tree = &astgen.file.tree;
+ const node_tags = tree.nodes.items(.tag);
+ const token_starts = tree.tokens.items(.start);
+ const decl_start = token_starts[tree.firstToken(gz.decl_node_index)];
+ const node_start = token_starts[tree.firstToken(node)];
+ const source = tree.source[decl_start..node_start];
+ const loc = std.zig.findLineColumn(source, source.len);
+ return @intCast(u32, gz.decl_line + loc.line);
+ }
+
pub fn tokSrcLoc(gz: GenZir, token_index: ast.TokenIndex) LazySrcLoc {
return .{ .token_offset = token_index - gz.srcToken() };
}
@@ -1259,10 +1291,6 @@ pub const Scope = struct {
return gz.astgen.file.tree.firstToken(gz.decl_node_index);
}
- pub fn tree(gz: *const GenZir) *const ast.Tree {
- return &gz.astgen.file.tree;
- }
-
pub fn indexToRef(gz: GenZir, inst: Zir.Inst.Index) Zir.Inst.Ref {
return @intToEnum(Zir.Inst.Ref, gz.ref_start_index + inst);
}
@@ -1376,13 +1404,40 @@ pub const Scope = struct {
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try astgen.instructions.ensureUnusedCapacity(gpa, 1);
+ var src_locs_buffer: [3]u32 = undefined;
+ var src_locs: []u32 = src_locs_buffer[0..0];
+ if (args.body.len != 0) {
+ const tree = &astgen.file.tree;
+ const node_tags = tree.nodes.items(.tag);
+ const node_datas = tree.nodes.items(.data);
+ const token_starts = tree.tokens.items(.start);
+ const decl_start = token_starts[tree.firstToken(gz.decl_node_index)];
+ const fn_decl = args.src_node;
+ assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
+ const block = node_datas[fn_decl].rhs;
+ const lbrace_start = token_starts[tree.firstToken(block)];
+ const rbrace_start = token_starts[tree.lastToken(block)];
+ const lbrace_source = tree.source[decl_start..lbrace_start];
+ const lbrace_loc = std.zig.findLineColumn(lbrace_source, lbrace_source.len);
+ const rbrace_source = tree.source[lbrace_start..rbrace_start];
+ const rbrace_loc = std.zig.findLineColumn(rbrace_source, rbrace_source.len);
+ const lbrace_line = @intCast(u32, lbrace_loc.line);
+ const rbrace_line = lbrace_line + @intCast(u32, rbrace_loc.line);
+ const columns = @intCast(u32, lbrace_loc.column) |
+ (@intCast(u32, rbrace_loc.column) << 16);
+ src_locs_buffer[0] = lbrace_line;
+ src_locs_buffer[1] = rbrace_line;
+ src_locs_buffer[2] = columns;
+ src_locs = &src_locs_buffer;
+ }
+
if (args.cc != .none or args.lib_name != 0 or
args.is_var_args or args.is_test or args.align_inst != .none)
{
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.ExtendedFunc).Struct.fields.len +
- args.param_types.len + args.body.len +
+ args.param_types.len + args.body.len + src_locs.len +
@boolToInt(args.lib_name != 0) +
@boolToInt(args.align_inst != .none) +
@boolToInt(args.cc != .none),
@@ -1404,6 +1459,7 @@ pub const Scope = struct {
}
astgen.appendRefsAssumeCapacity(args.param_types);
astgen.extra.appendSliceAssumeCapacity(args.body);
+ astgen.extra.appendSliceAssumeCapacity(src_locs);
const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
astgen.instructions.appendAssumeCapacity(.{
@@ -1427,7 +1483,7 @@ pub const Scope = struct {
try gz.astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.Func).Struct.fields.len +
- args.param_types.len + args.body.len,
+ args.param_types.len + args.body.len + src_locs.len,
);
const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Func{
@@ -1437,6 +1493,7 @@ pub const Scope = struct {
});
gz.astgen.appendRefsAssumeCapacity(args.param_types);
gz.astgen.extra.appendSliceAssumeCapacity(args.body);
+ gz.astgen.extra.appendSliceAssumeCapacity(src_locs);
const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func;
const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
@@ -3297,6 +3354,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
file.namespace = &struct_obj.namespace;
const new_decl = try mod.allocateNewDecl(&struct_obj.namespace, 0);
struct_obj.owner_decl = new_decl;
+ new_decl.src_line = 0;
new_decl.name = try file.fullyQualifiedNameZ(gpa);
new_decl.is_pub = true;
new_decl.is_exported = false;
@@ -3694,7 +3752,7 @@ pub fn scanNamespace(
cur_bit_bag >>= 4;
const decl_sub_index = extra_index;
- extra_index += 6;
+ extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)
extra_index += @truncate(u1, flags >> 2);
extra_index += @truncate(u1, flags >> 3);
@@ -3752,8 +3810,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
const has_linksection = (flags & 0b1000) != 0;
// zig fmt: on
- const decl_name_index = zir.extra[decl_sub_index + 4];
- const decl_index = zir.extra[decl_sub_index + 5];
+ const line = iter.parent_decl.relativeToLine(zir.extra[decl_sub_index + 4]);
+ const decl_name_index = zir.extra[decl_sub_index + 5];
+ const decl_index = zir.extra[decl_sub_index + 6];
const decl_block_inst_data = zir.instructions.items(.data)[decl_index].pl_node;
const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node);
@@ -3783,6 +3842,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
const gop = try namespace.decls.getOrPut(gpa, decl_name);
if (!gop.found_existing) {
const new_decl = try mod.allocateNewDecl(namespace, decl_node);
+ new_decl.src_line = line;
new_decl.name = decl_name;
gop.entry.value = new_decl;
// Exported decls, comptime decls, usingnamespace decls, and
@@ -3807,6 +3867,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
// have been re-ordered.
const prev_src_node = decl.src_node;
decl.src_node = decl_node;
+ decl.src_line = line;
decl.is_pub = is_pub;
decl.is_exported = is_exported;
@@ -4056,6 +4117,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
.name = "",
.namespace = namespace,
.src_node = src_node,
+ .src_line = undefined,
.has_tv = false,
.ty = undefined,
.val = undefined,
@@ -4292,6 +4354,7 @@ pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue)
const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node);
namespace.decls.putAssumeCapacityNoClobber(name, new_decl);
+ new_decl.src_line = scope_decl.src_line;
new_decl.name = name;
new_decl.ty = typed_value.ty;
new_decl.val = typed_value.val;
src/Sema.zig
@@ -397,8 +397,8 @@ pub fn analyzeBody(
try sema.zirFence(block, inst);
continue;
},
- .dbg_stmt_node => {
- try sema.zirDbgStmtNode(block, inst);
+ .dbg_stmt => {
+ try sema.zirDbgStmt(block, inst);
continue;
},
.ensure_err_payload_void => {
@@ -1920,7 +1920,7 @@ fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) InnerE
}
}
-fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
+fn zirDbgStmt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -1930,14 +1930,8 @@ fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
// instructions.
if (block.is_comptime) return;
- const src_node = sema.code.instructions.items(.data)[inst].node;
- const src: LazySrcLoc = .{ .node_offset = src_node };
-
- const src_loc = src.toSrcLoc(&block.base);
- const abs_byte_off = src_loc.byteOffset(sema.gpa) catch |err| {
- return sema.mod.fail(&block.base, src, "TODO modify dbg_stmt ZIR instructions to have line/column rather than node indexes. {s}", .{@errorName(err)});
- };
- _ = try block.addDbgStmt(src, abs_byte_off);
+ const inst_data = sema.code.instructions.items(.data)[inst].dbg_stmt;
+ _ = try block.addDbgStmt(.unneeded, inst_data.line, inst_data.column);
}
fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
@@ -2793,7 +2787,14 @@ fn zirFunc(
const src = inst_data.src();
const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len);
- const body_inst = if (extra.data.body_len != 0) inst else 0;
+
+ var body_inst: Zir.Inst.Index = 0;
+ var src_locs: Zir.Inst.Func.SrcLocs = undefined;
+ if (extra.data.body_len != 0) {
+ body_inst = inst;
+ const extra_index = extra.end + extra.data.param_types_len + extra.data.body_len;
+ src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
+ }
return sema.funcCommon(
block,
@@ -2805,6 +2806,7 @@ fn zirFunc(
Value.initTag(.null_value),
false,
inferred_error_set,
+ src_locs,
);
}
@@ -2819,6 +2821,7 @@ fn funcCommon(
align_val: Value,
var_args: bool,
inferred_error_set: bool,
+ src_locs: Zir.Inst.Func.SrcLocs,
) InnerError!*Inst {
const src: LazySrcLoc = .{ .node_offset = src_node_offset };
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
@@ -2872,18 +2875,17 @@ fn funcCommon(
const is_inline = fn_ty.fnCallingConvention() == .Inline;
const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued;
- // Use the Decl's arena for function memory.
- var fn_arena = std.heap.ArenaAllocator.init(sema.gpa);
- errdefer fn_arena.deinit();
-
- const new_func = try fn_arena.allocator.create(Module.Fn);
- const fn_payload = try fn_arena.allocator.create(Value.Payload.Function);
-
+ const fn_payload = try sema.arena.create(Value.Payload.Function);
+ const new_func = try sema.gpa.create(Module.Fn);
new_func.* = .{
.state = anal_state,
.zir_body_inst = body_inst,
.owner_decl = sema.owner_decl,
.body = undefined,
+ .lbrace_line = src_locs.lbrace_line,
+ .rbrace_line = src_locs.rbrace_line,
+ .lbrace_column = @truncate(u16, src_locs.columns),
+ .rbrace_column = @truncate(u16, src_locs.columns >> 16),
};
fn_payload.* = .{
.base = .{ .tag = .function },
@@ -2893,7 +2895,6 @@ fn funcCommon(
.ty = fn_ty,
.val = Value.initPayload(&fn_payload.base),
});
- try sema.owner_decl.finalizeNewArena(&fn_arena);
return result;
}
@@ -5577,7 +5578,13 @@ fn zirFuncExtended(
const param_types = sema.code.refSlice(extra_index, extra.data.param_types_len);
extra_index += param_types.len;
- const body_inst = if (extra.data.body_len != 0) inst else 0;
+ var body_inst: Zir.Inst.Index = 0;
+ var src_locs: Zir.Inst.Func.SrcLocs = undefined;
+ if (extra.data.body_len != 0) {
+ body_inst = inst;
+ extra_index += extra.data.body_len;
+ src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
+ }
return sema.funcCommon(
block,
@@ -5589,6 +5596,7 @@ fn zirFuncExtended(
align_val,
small.is_var_args,
small.is_inferred_error,
+ src_locs,
);
}
src/Zir.zig
@@ -321,8 +321,9 @@ pub const Inst = struct {
/// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.
error_set_decl,
/// Declares the beginning of a statement. Used for debug info.
- /// Uses the `node` union field.
- dbg_stmt_node,
+ /// Uses the `dbg_stmt` union field. The line and column are offset
+ /// from the parent declaration.
+ dbg_stmt,
/// Uses a name to identify a Decl and takes a pointer to it.
/// Uses the `str_tok` union field.
decl_ref,
@@ -1016,7 +1017,7 @@ pub const Inst = struct {
.enum_decl_nonexhaustive,
.opaque_decl,
.error_set_decl,
- .dbg_stmt_node,
+ .dbg_stmt,
.decl_ref,
.decl_val,
.load,
@@ -1276,7 +1277,7 @@ pub const Inst = struct {
.enum_decl_nonexhaustive = .pl_node,
.opaque_decl = .pl_node,
.error_set_decl = .pl_node,
- .dbg_stmt_node = .node,
+ .dbg_stmt = .dbg_stmt,
.decl_ref = .str_tok,
.decl_val = .str_tok,
.load = .un_node,
@@ -2118,6 +2119,10 @@ pub const Inst = struct {
switch_inst: Index,
prong_index: u32,
},
+ dbg_stmt: struct {
+ line: u32,
+ column: u32,
+ },
// Make sure we don't accidentally add a field to make this union
// bigger than expected. Note that in Debug builds, Zig is allowed
@@ -2153,6 +2158,7 @@ pub const Inst = struct {
@"unreachable",
@"break",
switch_capture,
+ dbg_stmt,
};
};
@@ -2193,6 +2199,7 @@ pub const Inst = struct {
/// 2. align: Ref, // if has_align is set
/// 3. param_type: Ref // for each param_types_len
/// 4. body: Index // for each body_len
+ /// 5. src_locs: Func.SrcLocs // if body_len != 0
pub const ExtendedFunc = struct {
src_node: i32,
return_type: Ref,
@@ -2231,10 +2238,21 @@ pub const Inst = struct {
/// 0. param_type: Ref // for each param_types_len
/// - `none` indicates that the param type is `anytype`.
/// 1. body: Index // for each body_len
+ /// 2. src_locs: SrcLocs // if body_len != 0
pub const Func = struct {
return_type: Ref,
param_types_len: u32,
body_len: u32,
+
+ pub const SrcLocs = struct {
+ /// Absolute line number in the source file.
+ lbrace_line: u32,
+ /// Absolute line number in the source file.
+ rbrace_line: u32,
+ /// lbrace_column is least significant bits u16
+ /// rbrace_column is most significant bits u16
+ columns: u32,
+ };
};
/// This data is stored inside extra, with trailing operands according to `operands_len`.
@@ -2398,6 +2416,7 @@ pub const Inst = struct {
/// 0bX000: whether corresponding decl has a linksection expression
/// 1. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
+ /// line: u32, // line number of decl, relative to parent
/// name: u32, // null terminated string index
/// - 0 means comptime or usingnamespace decl.
/// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
@@ -2435,6 +2454,7 @@ pub const Inst = struct {
/// 0bX000: whether corresponding decl has a linksection expression
/// 1. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
+ /// line: u32, // line number of decl, relative to parent
/// name: u32, // null terminated string index
/// - 0 means comptime or usingnamespace decl.
/// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
@@ -2467,6 +2487,7 @@ pub const Inst = struct {
/// 0bX000: whether corresponding decl has a linksection expression
/// 1. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
+ /// line: u32, // line number of decl, relative to parent
/// name: u32, // null terminated string index
/// - 0 means comptime or usingnamespace decl.
/// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
@@ -2509,6 +2530,7 @@ pub const Inst = struct {
/// 0bX000: whether corresponding decl has a linksection expression
/// 1. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
+ /// line: u32, // line number of decl, relative to parent
/// name: u32, // null terminated string index
/// - 0 means comptime or usingnamespace decl.
/// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
@@ -2978,7 +3000,6 @@ const Writer = struct {
.breakpoint,
.fence,
- .dbg_stmt_node,
.repeat,
.repeat_inline,
.alloc_inferred,
@@ -3007,6 +3028,8 @@ const Writer = struct {
.switch_capture_else_ref,
=> try self.writeSwitchCapture(stream, inst),
+ .dbg_stmt => try self.writeDbgStmt(stream, inst),
+
.extended => try self.writeExtended(stream, inst),
}
}
@@ -3606,6 +3629,8 @@ const Writer = struct {
const hash_u32s = self.code.extra[extra_index..][0..4];
extra_index += 4;
+ const line = self.code.extra[extra_index];
+ extra_index += 1;
const decl_name_index = self.code.extra[extra_index];
const decl_name = self.code.nullTerminatedString(decl_name_index);
extra_index += 1;
@@ -3646,8 +3671,8 @@ const Writer = struct {
}
}
const tag = self.code.instructions.items(.tag)[decl_index];
- try stream.print(" hash({}): %{d} = {s}(", .{
- std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag),
+ try stream.print(" line({d}) hash({}): %{d} = {s}(", .{
+ line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag),
});
const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node;
@@ -3979,6 +4004,11 @@ const Writer = struct {
const extra = self.code.extraData(Inst.Func, inst_data.payload_index);
const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
const body = self.code.extra[extra.end + param_types.len ..][0..extra.data.body_len];
+ var src_locs: Zir.Inst.Func.SrcLocs = undefined;
+ if (body.len != 0) {
+ const extra_index = extra.end + param_types.len + body.len;
+ src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
+ }
return self.writeFuncCommon(
stream,
param_types,
@@ -3989,6 +4019,7 @@ const Writer = struct {
.none,
body,
src,
+ src_locs,
);
}
@@ -4019,7 +4050,12 @@ const Writer = struct {
extra_index += param_types.len;
const body = self.code.extra[extra_index..][0..extra.data.body_len];
+ extra_index += body.len;
+ var src_locs: Zir.Inst.Func.SrcLocs = undefined;
+ if (body.len != 0) {
+ src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
+ }
return self.writeFuncCommon(
stream,
param_types,
@@ -4030,6 +4066,7 @@ const Writer = struct {
align_inst,
body,
src,
+ src_locs,
);
}
@@ -4111,6 +4148,7 @@ const Writer = struct {
align_inst: Inst.Ref,
body: []const Inst.Index,
src: LazySrcLoc,
+ src_locs: Zir.Inst.Func.SrcLocs,
) !void {
try stream.writeAll("[");
for (param_types) |param_type, i| {
@@ -4134,6 +4172,12 @@ const Writer = struct {
try stream.writeByteNTimes(' ', self.indent);
try stream.writeAll("}) ");
}
+ if (body.len != 0) {
+ try stream.print("(lbrace={d}:{d},rbrace={d}:{d}) ", .{
+ src_locs.lbrace_line, @truncate(u16, src_locs.columns),
+ src_locs.rbrace_line, @truncate(u16, src_locs.columns >> 16),
+ });
+ }
try self.writeSrc(stream, src);
}
@@ -4143,6 +4187,11 @@ const Writer = struct {
try stream.print(", {d})", .{inst_data.prong_index});
}
+ fn writeDbgStmt(self: *Writer, stream: anytype, inst: Inst.Index) !void {
+ const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt;
+ try stream.print("{d}, {d})", .{ inst_data.line, inst_data.column });
+ }
+
fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void {
var i: usize = @enumToInt(ref);
BRANCH_TODO
@@ -1,4 +1,3 @@
- * modify dbg_stmt ZIR instructions to have line/column rather than node indexes
* decouple AstGen from Module, Compilation
* AstGen threadlocal
* extern "foo" for vars and for functions