| ... | ... | @@ -51,13 +51,14 @@ gpa: Allocator, |
| 51 | 51 | air: Air, |
| 52 | 52 | liveness: Liveness, |
| 53 | 53 | bin_file: *link.File, |
| 54 | debug_output: DebugInfoOutput, |
| 54 | 55 | target: *const std.Target, |
| 55 | 56 | mod_fn: *const Module.Fn, |
| 56 | 57 | err_msg: ?*ErrorMsg, |
| 57 | 58 | args: []MCValue, |
| 58 | 59 | ret_mcv: MCValue, |
| 59 | 60 | fn_type: Type, |
| 60 | | arg_index: usize, |
| 61 | arg_index: u32, |
| 61 | 62 | src_loc: Module.SrcLoc, |
| 62 | 63 | stack_align: u32, |
| 63 | 64 | |
| ... | ... | @@ -75,6 +76,12 @@ end_di_column: u32, |
| 75 | 76 | /// which is a relative jump, based on the address following the reloc. |
| 76 | 77 | exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, |
| 77 | 78 | |
| 79 | /// We postpone the creation of debug info for function args and locals |
| 80 | /// until after all Mir instructions have been generated. Only then we |
| 81 | /// will know saved_regs_stack_space which is necessary in order to |
| 82 | /// calculate the right stack offsest with respect to the `.fp` register. |
| 83 | dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .{}, |
| 84 | |
| 78 | 85 | /// Whenever there is a runtime branch, we push a Branch onto this stack, |
| 79 | 86 | /// and pop it off when the runtime branch joins. This provides an "overlay" |
| 80 | 87 | /// of the table of mappings from instructions to `MCValue` from within the branch. |
| ... | ... | @@ -160,6 +167,213 @@ const MCValue = union(enum) { |
| 160 | 167 | stack_argument_offset: u32, |
| 161 | 168 | }; |
| 162 | 169 | |
| 170 | const DbgInfoReloc = struct { |
| 171 | tag: Air.Inst.Tag, |
| 172 | ty: Type, |
| 173 | name: [:0]const u8, |
| 174 | mcv: MCValue, |
| 175 | |
| 176 | fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void { |
| 177 | switch (reloc.tag) { |
| 178 | .arg => try reloc.genArgDbgInfo(function), |
| 179 | |
| 180 | .dbg_var_ptr, |
| 181 | .dbg_var_val, |
| 182 | => try reloc.genVarDbgInfo(function), |
| 183 | |
| 184 | else => unreachable, |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void { |
| 189 | const name_with_null = reloc.name.ptr[0 .. reloc.name.len + 1]; |
| 190 | |
| 191 | switch (function.debug_output) { |
| 192 | .dwarf => |dw| { |
| 193 | const dbg_info = &dw.dbg_info; |
| 194 | switch (reloc.mcv) { |
| 195 | .register => |reg| { |
| 196 | try dbg_info.ensureUnusedCapacity(3); |
| 197 | dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); |
| 198 | dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 199 | 1, // ULEB128 dwarf expression length |
| 200 | reg.dwarfLocOp(), |
| 201 | }); |
| 202 | try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); |
| 203 | try function.addDbgInfoTypeReloc(reloc.ty); // DW.AT.type, DW.FORM.ref4 |
| 204 | dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string |
| 205 | }, |
| 206 | |
| 207 | .stack_offset, |
| 208 | .stack_argument_offset, |
| 209 | => |offset| { |
| 210 | const adjusted_offset = switch (reloc.mcv) { |
| 211 | .stack_offset => -@intCast(i32, offset), |
| 212 | .stack_argument_offset => @intCast(i32, function.saved_regs_stack_space + offset), |
| 213 | else => unreachable, |
| 214 | }; |
| 215 | |
| 216 | try dbg_info.ensureUnusedCapacity(8); |
| 217 | dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); |
| 218 | const fixup = dbg_info.items.len; |
| 219 | dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 220 | 1, // we will backpatch it after we encode the displacement in LEB128 |
| 221 | Register.x29.dwarfLocOpDeref(), // frame pointer |
| 222 | }); |
| 223 | leb128.writeILEB128(dbg_info.writer(), adjusted_offset) catch unreachable; |
| 224 | dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); |
| 225 | try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); |
| 226 | try function.addDbgInfoTypeReloc(reloc.ty); // DW.AT.type, DW.FORM.ref4 |
| 227 | dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string |
| 228 | |
| 229 | }, |
| 230 | |
| 231 | else => unreachable, // not a possible argument |
| 232 | } |
| 233 | }, |
| 234 | .plan9 => {}, |
| 235 | .none => {}, |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void { |
| 240 | const name_with_null = reloc.name.ptr[0 .. reloc.name.len + 1]; |
| 241 | const ty = switch (reloc.tag) { |
| 242 | .dbg_var_ptr => reloc.ty.childType(), |
| 243 | .dbg_var_val => reloc.ty, |
| 244 | else => unreachable, |
| 245 | }; |
| 246 | |
| 247 | switch (function.debug_output) { |
| 248 | .dwarf => |dw| { |
| 249 | const dbg_info = &dw.dbg_info; |
| 250 | try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); |
| 251 | const endian = function.target.cpu.arch.endian(); |
| 252 | |
| 253 | switch (reloc.mcv) { |
| 254 | .register => |reg| { |
| 255 | try dbg_info.ensureUnusedCapacity(2); |
| 256 | dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 257 | 1, // ULEB128 dwarf expression length |
| 258 | reg.dwarfLocOp(), |
| 259 | }); |
| 260 | }, |
| 261 | |
| 262 | .ptr_stack_offset, |
| 263 | .stack_offset, |
| 264 | => |off| { |
| 265 | try dbg_info.ensureUnusedCapacity(7); |
| 266 | const fixup = dbg_info.items.len; |
| 267 | dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 268 | 1, // we will backpatch it after we encode the displacement in LEB128 |
| 269 | Register.x29.dwarfLocOpDeref(), // frame pointer |
| 270 | }); |
| 271 | leb128.writeILEB128(dbg_info.writer(), -@intCast(i32, off)) catch unreachable; |
| 272 | dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); |
| 273 | }, |
| 274 | |
| 275 | .memory, |
| 276 | .linker_load, |
| 277 | => { |
| 278 | const ptr_width = @intCast(u8, @divExact(function.target.cpu.arch.ptrBitWidth(), 8)); |
| 279 | const is_ptr = switch (reloc.tag) { |
| 280 | .dbg_var_ptr => true, |
| 281 | .dbg_var_val => false, |
| 282 | else => unreachable, |
| 283 | }; |
| 284 | try dbg_info.ensureUnusedCapacity(2 + ptr_width); |
| 285 | dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 286 | 1 + ptr_width + @boolToInt(is_ptr), |
| 287 | DW.OP.addr, // literal address |
| 288 | }); |
| 289 | const offset = @intCast(u32, dbg_info.items.len); |
| 290 | const addr = switch (reloc.mcv) { |
| 291 | .memory => |addr| addr, |
| 292 | else => 0, |
| 293 | }; |
| 294 | switch (ptr_width) { |
| 295 | 0...4 => { |
| 296 | try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian); |
| 297 | }, |
| 298 | 5...8 => { |
| 299 | try dbg_info.writer().writeInt(u64, addr, endian); |
| 300 | }, |
| 301 | else => unreachable, |
| 302 | } |
| 303 | if (is_ptr) { |
| 304 | // We need deref the address as we point to the value via GOT entry. |
| 305 | try dbg_info.append(DW.OP.deref); |
| 306 | } |
| 307 | switch (reloc.mcv) { |
| 308 | .linker_load => |load_struct| try dw.addExprlocReloc( |
| 309 | load_struct.sym_index, |
| 310 | offset, |
| 311 | is_ptr, |
| 312 | ), |
| 313 | else => {}, |
| 314 | } |
| 315 | }, |
| 316 | |
| 317 | .immediate => |x| { |
| 318 | try dbg_info.ensureUnusedCapacity(2); |
| 319 | const fixup = dbg_info.items.len; |
| 320 | dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 321 | 1, |
| 322 | if (ty.isSignedInt()) DW.OP.consts else DW.OP.constu, |
| 323 | }); |
| 324 | if (ty.isSignedInt()) { |
| 325 | try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x)); |
| 326 | } else { |
| 327 | try leb128.writeULEB128(dbg_info.writer(), x); |
| 328 | } |
| 329 | try dbg_info.append(DW.OP.stack_value); |
| 330 | dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); |
| 331 | }, |
| 332 | |
| 333 | .undef => { |
| 334 | // DW.AT.location, DW.FORM.exprloc |
| 335 | // uleb128(exprloc_len) |
| 336 | // DW.OP.implicit_value uleb128(len_of_bytes) bytes |
| 337 | const abi_size = @intCast(u32, ty.abiSize(function.target.*)); |
| 338 | var implicit_value_len = std.ArrayList(u8).init(function.gpa); |
| 339 | defer implicit_value_len.deinit(); |
| 340 | try leb128.writeULEB128(implicit_value_len.writer(), abi_size); |
| 341 | const total_exprloc_len = 1 + implicit_value_len.items.len + abi_size; |
| 342 | try leb128.writeULEB128(dbg_info.writer(), total_exprloc_len); |
| 343 | try dbg_info.ensureUnusedCapacity(total_exprloc_len); |
| 344 | dbg_info.appendAssumeCapacity(DW.OP.implicit_value); |
| 345 | dbg_info.appendSliceAssumeCapacity(implicit_value_len.items); |
| 346 | dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size); |
| 347 | }, |
| 348 | |
| 349 | .none => { |
| 350 | try dbg_info.ensureUnusedCapacity(3); |
| 351 | dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc |
| 352 | 2, DW.OP.lit0, DW.OP.stack_value, |
| 353 | }); |
| 354 | }, |
| 355 | |
| 356 | .stack_argument_offset => unreachable, |
| 357 | |
| 358 | else => { |
| 359 | try dbg_info.ensureUnusedCapacity(2); |
| 360 | dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 361 | 1, DW.OP.nop, |
| 362 | }); |
| 363 | log.debug("TODO generate debug info for {}", .{reloc.mcv}); |
| 364 | }, |
| 365 | } |
| 366 | |
| 367 | try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); |
| 368 | try function.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 |
| 369 | dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string |
| 370 | }, |
| 371 | .plan9 => {}, |
| 372 | .none => {}, |
| 373 | } |
| 374 | } |
| 375 | }; |
| 376 | |
| 163 | 377 | const Branch = struct { |
| 164 | 378 | inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{}, |
| 165 | 379 | |
| ... | ... | @@ -262,6 +476,7 @@ pub fn generate( |
| 262 | 476 | .gpa = bin_file.allocator, |
| 263 | 477 | .air = air, |
| 264 | 478 | .liveness = liveness, |
| 479 | .debug_output = debug_output, |
| 265 | 480 | .target = &bin_file.options.target, |
| 266 | 481 | .bin_file = bin_file, |
| 267 | 482 | .mod_fn = module_fn, |
| ... | ... | @@ -279,6 +494,7 @@ pub fn generate( |
| 279 | 494 | defer function.stack.deinit(bin_file.allocator); |
| 280 | 495 | defer function.blocks.deinit(bin_file.allocator); |
| 281 | 496 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 497 | defer function.dbg_info_relocs.deinit(bin_file.allocator); |
| 282 | 498 | |
| 283 | 499 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 284 | 500 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, |
| ... | ... | @@ -302,6 +518,10 @@ pub fn generate( |
| 302 | 518 | else => |e| return e, |
| 303 | 519 | }; |
| 304 | 520 | |
| 521 | for (function.dbg_info_relocs.items) |reloc| { |
| 522 | try reloc.genDbgInfo(function); |
| 523 | } |
| 524 | |
| 305 | 525 | var mir = Mir{ |
| 306 | 526 | .instructions = function.mir_instructions.toOwnedSlice(), |
| 307 | 527 | .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), |
| ... | ... | @@ -854,23 +1074,20 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { |
| 854 | 1074 | |
| 855 | 1075 | /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, |
| 856 | 1076 | /// after codegen for this symbol is done. |
| 857 | | fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { |
| 1077 | fn addDbgInfoTypeReloc(self: Self, ty: Type) !void { |
| 858 | 1078 | switch (self.debug_output) { |
| 859 | | .dwarf => |dbg_out| { |
| 860 | | assert(ty.hasRuntimeBits()); |
| 861 | | const index = dbg_out.dbg_info.items.len; |
| 862 | | try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 |
| 863 | | |
| 864 | | const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{ |
| 865 | | .target = self.target.*, |
| 866 | | }); |
| 867 | | if (!gop.found_existing) { |
| 868 | | gop.value_ptr.* = .{ |
| 869 | | .off = undefined, |
| 870 | | .relocs = .{}, |
| 871 | | }; |
| 872 | | } |
| 873 | | try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index)); |
| 1079 | .dwarf => |dw| { |
| 1080 | const dbg_info = &dw.dbg_info; |
| 1081 | const index = dbg_info.items.len; |
| 1082 | try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 |
| 1083 | const mod = self.bin_file.options.module.?; |
| 1084 | const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); |
| 1085 | const atom = switch (self.bin_file.tag) { |
| 1086 | .elf => &fn_owner_decl.link.elf.dbg_info_atom, |
| 1087 | .macho => &fn_owner_decl.link.macho.dbg_info_atom, |
| 1088 | else => unreachable, |
| 1089 | }; |
| 1090 | try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index)); |
| 874 | 1091 | }, |
| 875 | 1092 | .plan9 => {}, |
| 876 | 1093 | .none => {}, |
| ... | ... | @@ -3872,8 +4089,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 3872 | 4089 | self.arg_index += 1; |
| 3873 | 4090 | |
| 3874 | 4091 | const ty = self.air.typeOfIndex(inst); |
| 3875 | | |
| 3876 | 4092 | const result = self.args[arg_index]; |
| 4093 | const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index); |
| 4094 | |
| 3877 | 4095 | const mcv = switch (result) { |
| 3878 | 4096 | // Copy registers to the stack |
| 3879 | 4097 | .register => |reg| blk: { |
| ... | ... | @@ -3889,8 +4107,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 3889 | 4107 | }, |
| 3890 | 4108 | else => result, |
| 3891 | 4109 | }; |
| 3892 | | // TODO generate debug info |
| 3893 | | // try self.genArgDbgInfo(inst, mcv); |
| 4110 | |
| 4111 | const tag = self.air.instructions.items(.tag)[inst]; |
| 4112 | try self.dbg_info_relocs.append(self.gpa, .{ |
| 4113 | .tag = tag, |
| 4114 | .ty = ty, |
| 4115 | .name = name, |
| 4116 | .mcv = result, |
| 4117 | }); |
| 3894 | 4118 | |
| 3895 | 4119 | if (self.liveness.isUnused(inst)) |
| 3896 | 4120 | return self.finishAirBookkeeping(); |
| ... | ... | @@ -4378,10 +4602,21 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 4378 | 4602 | |
| 4379 | 4603 | fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { |
| 4380 | 4604 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 4381 | | const name = self.air.nullTerminatedString(pl_op.payload); |
| 4382 | 4605 | const operand = pl_op.operand; |
| 4383 | | // TODO emit debug info for this variable |
| 4384 | | _ = name; |
| 4606 | const tag = self.air.instructions.items(.tag)[inst]; |
| 4607 | const ty = self.air.typeOf(operand); |
| 4608 | const mcv = try self.resolveInst(operand); |
| 4609 | const name = self.air.nullTerminatedString(pl_op.payload); |
| 4610 | |
| 4611 | log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv }); |
| 4612 | |
| 4613 | try self.dbg_info_relocs.append(self.gpa, .{ |
| 4614 | .tag = tag, |
| 4615 | .ty = ty, |
| 4616 | .name = name, |
| 4617 | .mcv = mcv, |
| 4618 | }); |
| 4619 | |
| 4385 | 4620 | return self.finishAir(inst, .dead, .{ operand, .none, .none }); |
| 4386 | 4621 | } |
| 4387 | 4622 | |