diff --git a/BRANCH_TODO b/BRANCH_TODO index 7b625204f6..3594632ea1 100644 --- a/BRANCH_TODO +++ b/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 diff --git a/src/AstGen.zig b/src/AstGen.zig index 1c75a12069..b5b31c4095 100644 --- a/src/AstGen.zig +++ b/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); diff --git a/src/Module.zig b/src/Module.zig index 7bf117b875..3f699efbdd 100644 --- a/src/Module.zig +++ b/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; diff --git a/src/Sema.zig b/src/Sema.zig index 44f8c7d370..898093a839 100644 --- a/src/Sema.zig +++ b/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, ); } diff --git a/src/Zir.zig b/src/Zir.zig index a52763b4d0..a158150263 100644 --- a/src/Zir.zig +++ b/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); diff --git a/src/codegen.zig b/src/codegen.zig index 2ee57a998d..f588f7c3b6 100644 --- a/src/codegen.zig +++ b/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; } diff --git a/src/ir.zig b/src/ir.zig index 2026297026..7dcdd2f286 100644 --- a/src/ir.zig +++ b/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; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 7fbf17015e..3f87fd390b 100644 --- a/src/link/Elf.zig +++ b/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();