stage2: dbg_stmt ZIR instructions have line/col

instead of node indexes.

 * AstGen: dbg_stmt instructions now have line and column indexes,
   relative to the parent declaration. This allows codegen to emit debug
   info without having the source bytes, tokens, or AST nodes loaded
   in memory.
 * ZIR: each decl has the absolute line number. This allows computing
   line numbers from offsets without consulting source code bytes.

Memory management: creating a function definition does not prematurely
set the Decl arena. Instead the function is allocated with the general
purpose allocator.

Codegen no longer looks at source code bytes for any reason. They can
remain unloaded from disk.
This commit is contained in:
Andrew Kelley 2021-05-01 21:57:52 -07:00
parent 6248e2a560
commit eadcefc124
8 changed files with 253 additions and 130 deletions

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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,
);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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();