| ... | @@ -51,13 +51,14 @@ gpa: Allocator, | ... | @@ -51,13 +51,14 @@ gpa: Allocator, |
| 51 | air: Air, | 51 | air: Air, |
| 52 | liveness: Liveness, | 52 | liveness: Liveness, |
| 53 | bin_file: *link.File, | 53 | bin_file: *link.File, |
| | 54 | debug_output: DebugInfoOutput, |
| 54 | target: *const std.Target, | 55 | target: *const std.Target, |
| 55 | mod_fn: *const Module.Fn, | 56 | mod_fn: *const Module.Fn, |
| 56 | err_msg: ?*ErrorMsg, | 57 | err_msg: ?*ErrorMsg, |
| 57 | args: []MCValue, | 58 | args: []MCValue, |
| 58 | ret_mcv: MCValue, | 59 | ret_mcv: MCValue, |
| 59 | fn_type: Type, | 60 | fn_type: Type, |
| 60 | arg_index: usize, | 61 | arg_index: u32, |
| 61 | src_loc: Module.SrcLoc, | 62 | src_loc: Module.SrcLoc, |
| 62 | stack_align: u32, | 63 | stack_align: u32, |
| 63 | | 64 | |
| ... | @@ -75,6 +76,12 @@ end_di_column: u32, | ... | @@ -75,6 +76,12 @@ end_di_column: u32, |
| 75 | /// which is a relative jump, based on the address following the reloc. | 76 | /// which is a relative jump, based on the address following the reloc. |
| 76 | exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, | 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 | /// Whenever there is a runtime branch, we push a Branch onto this stack, | 85 | /// Whenever there is a runtime branch, we push a Branch onto this stack, |
| 79 | /// and pop it off when the runtime branch joins. This provides an "overlay" | 86 | /// and pop it off when the runtime branch joins. This provides an "overlay" |
| 80 | /// of the table of mappings from instructions to `MCValue` from within the branch. | 87 | /// of the table of mappings from instructions to `MCValue` from within the branch. |
| ... | @@ -160,6 +167,213 @@ const MCValue = union(enum) { | ... | @@ -160,6 +167,213 @@ const MCValue = union(enum) { |
| 160 | stack_argument_offset: u32, | 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 | const Branch = struct { | 377 | const Branch = struct { |
| 164 | inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{}, | 378 | inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{}, |
| 165 | | 379 | |
| ... | @@ -262,6 +476,7 @@ pub fn generate( | ... | @@ -262,6 +476,7 @@ pub fn generate( |
| 262 | .gpa = bin_file.allocator, | 476 | .gpa = bin_file.allocator, |
| 263 | .air = air, | 477 | .air = air, |
| 264 | .liveness = liveness, | 478 | .liveness = liveness, |
| | 479 | .debug_output = debug_output, |
| 265 | .target = &bin_file.options.target, | 480 | .target = &bin_file.options.target, |
| 266 | .bin_file = bin_file, | 481 | .bin_file = bin_file, |
| 267 | .mod_fn = module_fn, | 482 | .mod_fn = module_fn, |
| ... | @@ -279,6 +494,7 @@ pub fn generate( | ... | @@ -279,6 +494,7 @@ pub fn generate( |
| 279 | defer function.stack.deinit(bin_file.allocator); | 494 | defer function.stack.deinit(bin_file.allocator); |
| 280 | defer function.blocks.deinit(bin_file.allocator); | 495 | defer function.blocks.deinit(bin_file.allocator); |
| 281 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); | 496 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| | 497 | defer function.dbg_info_relocs.deinit(bin_file.allocator); |
| 282 | | 498 | |
| 283 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 499 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 284 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 500 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, |
| ... | @@ -302,6 +518,10 @@ pub fn generate( | ... | @@ -302,6 +518,10 @@ pub fn generate( |
| 302 | else => |e| return e, | 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 | var mir = Mir{ | 525 | var mir = Mir{ |
| 306 | .instructions = function.mir_instructions.toOwnedSlice(), | 526 | .instructions = function.mir_instructions.toOwnedSlice(), |
| 307 | .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), | 527 | .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), |
| ... | @@ -854,23 +1074,20 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { | ... | @@ -854,23 +1074,20 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { |
| 854 | | 1074 | |
| 855 | /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, | 1075 | /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, |
| 856 | /// after codegen for this symbol is done. | 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 | switch (self.debug_output) { | 1078 | switch (self.debug_output) { |
| 859 | .dwarf => |dbg_out| { | 1079 | .dwarf => |dw| { |
| 860 | assert(ty.hasRuntimeBits()); | 1080 | const dbg_info = &dw.dbg_info; |
| 861 | const index = dbg_out.dbg_info.items.len; | 1081 | const index = dbg_info.items.len; |
| 862 | try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 | 1082 | try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 |
| 863 | | 1083 | const mod = self.bin_file.options.module.?; |
| 864 | const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{ | 1084 | const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); |
| 865 | .target = self.target.*, | 1085 | const atom = switch (self.bin_file.tag) { |
| 866 | }); | 1086 | .elf => &fn_owner_decl.link.elf.dbg_info_atom, |
| 867 | if (!gop.found_existing) { | 1087 | .macho => &fn_owner_decl.link.macho.dbg_info_atom, |
| 868 | gop.value_ptr.* = .{ | 1088 | else => unreachable, |
| 869 | .off = undefined, | 1089 | }; |
| 870 | .relocs = .{}, | 1090 | try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index)); |
| 871 | }; | | |
| 872 | } | | |
| 873 | try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index)); | | |
| 874 | }, | 1091 | }, |
| 875 | .plan9 => {}, | 1092 | .plan9 => {}, |
| 876 | .none => {}, | 1093 | .none => {}, |
| ... | @@ -3872,8 +4089,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3872,8 +4089,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 3872 | self.arg_index += 1; | 4089 | self.arg_index += 1; |
| 3873 | | 4090 | |
| 3874 | const ty = self.air.typeOfIndex(inst); | 4091 | const ty = self.air.typeOfIndex(inst); |
| 3875 | | | |
| 3876 | const result = self.args[arg_index]; | 4092 | const result = self.args[arg_index]; |
| | 4093 | const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index); |
| | 4094 | |
| 3877 | const mcv = switch (result) { | 4095 | const mcv = switch (result) { |
| 3878 | // Copy registers to the stack | 4096 | // Copy registers to the stack |
| 3879 | .register => |reg| blk: { | 4097 | .register => |reg| blk: { |
| ... | @@ -3889,8 +4107,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3889,8 +4107,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 3889 | }, | 4107 | }, |
| 3890 | else => result, | 4108 | else => result, |
| 3891 | }; | 4109 | }; |
| 3892 | // TODO generate debug info | 4110 | |
| 3893 | // try self.genArgDbgInfo(inst, mcv); | 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 | if (self.liveness.isUnused(inst)) | 4119 | if (self.liveness.isUnused(inst)) |
| 3896 | return self.finishAirBookkeeping(); | 4120 | return self.finishAirBookkeeping(); |
| ... | @@ -4378,10 +4602,21 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4378,10 +4602,21 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 4378 | | 4602 | |
| 4379 | fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { | 4603 | fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { |
| 4380 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | 4604 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 4381 | const name = self.air.nullTerminatedString(pl_op.payload); | | |
| 4382 | const operand = pl_op.operand; | 4605 | const operand = pl_op.operand; |
| 4383 | // TODO emit debug info for this variable | 4606 | const tag = self.air.instructions.items(.tag)[inst]; |
| 4384 | _ = name; | 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 | return self.finishAir(inst, .dead, .{ operand, .none, .none }); | 4620 | return self.finishAir(inst, .dead, .{ operand, .none, .none }); |
| 4386 | } | 4621 | } |
| 4387 | | 4622 | |