llvm: set precise scopes on namespace types and variables

This will allow accessing non-local declarations from debuggers, which,
AFAICT, was impossible before.
Getting scopes right already works for type declarations and functions,
but will need some fiddling for variables:

For those, I tried imitating what Clang does for static member
variables, but LLDB tries to re-mangle those and then fails at lookup,
while GDB outright crashes. Hopefully I can find some other dwarven
incantation to do the right thing.
This commit is contained in:
Tau 2024-06-20 19:10:26 +02:00
parent ebd9efa850
commit 876258abe4
3 changed files with 549 additions and 465 deletions

View File

@ -1140,18 +1140,7 @@ pub const Object = struct {
try self.genModuleLevelAssembly();
if (!self.builder.strip) {
{
var i: usize = 0;
while (i < self.debug_unresolved_namespace_scopes.count()) : (i += 1) {
const namespace_index = self.debug_unresolved_namespace_scopes.keys()[i];
const fwd_ref = self.debug_unresolved_namespace_scopes.values()[i];
const namespace = zcu.namespacePtr(namespace_index);
const debug_type = try self.lowerDebugType(namespace.getType(zcu));
self.builder.debugForwardReferenceSetType(fwd_ref, debug_type);
}
}
try self.genNamespaces();
self.builder.debugForwardReferenceSetType(
self.debug_enums_fwd_ref,
@ -1641,6 +1630,7 @@ pub const Object = struct {
const file, const subprogram = if (!wip.strip) debug_info: {
const file = try o.getDebugFile(file_scope);
const scope = try o.namespaceToDebugScope(decl.src_namespace);
const line_number = decl.navSrcLine(zcu) + 1;
const is_internal_linkage = decl.val.getExternFunc(zcu) == null;
@ -1648,6 +1638,7 @@ pub const Object = struct {
const subprogram = try o.builder.debugSubprogram(
file,
scope,
try o.builder.metadataString(decl.name.toSlice(ip)),
try o.builder.metadataStringFromStrtabString(function_index.name(&o.builder)),
line_number,
@ -1924,6 +1915,7 @@ pub const Object = struct {
if (o.debug_type_map.get(ty)) |debug_type| return debug_type;
switch (ty.zigTypeTag(zcu)) {
.Void,
.NoReturn,
@ -1938,9 +1930,9 @@ pub const Object = struct {
.Int => {
const info = ty.intInfo(zcu);
assert(info.bits != 0);
const name = try o.allocTypeName(ty);
defer gpa.free(name);
const builder_name = try o.builder.metadataString(name);
const int_name = try o.allocTypeName(ty);
defer gpa.free(int_name);
const builder_name = try o.builder.metadataString(int_name);
const debug_bits = ty.abiSize(pt) * 8; // lldb cannot handle non-byte sized types
const debug_int_type = switch (info.signedness) {
.signed => try o.builder.debugSignedType(builder_name, debug_bits),
@ -1949,68 +1941,12 @@ pub const Object = struct {
try o.debug_type_map.put(gpa, ty, debug_int_type);
return debug_int_type;
},
.Enum => {
const owner_decl_index = ty.getOwnerDecl(zcu);
const owner_decl = zcu.declPtr(owner_decl_index);
if (!ty.hasRuntimeBitsIgnoreComptime(pt)) {
const debug_enum_type = try o.makeEmptyNamespaceDebugType(owner_decl_index);
try o.debug_type_map.put(gpa, ty, debug_enum_type);
return debug_enum_type;
}
const enum_type = ip.loadEnumType(ty.toIntern());
const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len);
defer gpa.free(enumerators);
const int_ty = Type.fromInterned(enum_type.tag_ty);
const int_info = ty.intInfo(zcu);
assert(int_info.bits != 0);
for (enum_type.names.get(ip), 0..) |field_name_ip, i| {
var bigint_space: Value.BigIntSpace = undefined;
const bigint = if (enum_type.values.len != 0)
Value.fromInterned(enum_type.values.get(ip)[i]).toBigInt(&bigint_space, pt)
else
std.math.big.int.Mutable.init(&bigint_space.limbs, i).toConst();
enumerators[i] = try o.builder.debugEnumerator(
try o.builder.metadataString(field_name_ip.toSlice(ip)),
int_info.signedness == .unsigned,
int_info.bits,
bigint,
);
}
const file_scope = zcu.namespacePtr(owner_decl.src_namespace).fileScope(zcu);
const file = try o.getDebugFile(file_scope);
const scope = try o.namespaceToDebugScope(owner_decl.src_namespace);
const name = try o.allocTypeName(ty);
defer gpa.free(name);
const debug_enum_type = try o.builder.debugEnumerationType(
try o.builder.metadataString(name),
file,
scope,
owner_decl.typeSrcLine(zcu) + 1, // Line
try o.lowerDebugType(int_ty),
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(enumerators),
);
try o.debug_type_map.put(gpa, ty, debug_enum_type);
try o.debug_enums.append(gpa, debug_enum_type);
return debug_enum_type;
},
.Float => {
const bits = ty.floatBits(target);
const name = try o.allocTypeName(ty);
defer gpa.free(name);
const float_name = try o.allocTypeName(ty);
defer gpa.free(float_name);
const debug_float_type = try o.builder.debugFloatType(
try o.builder.metadataString(name),
try o.builder.metadataString(float_name),
bits,
);
try o.debug_type_map.put(gpa, ty, debug_float_type);
@ -2068,6 +2004,7 @@ pub const Object = struct {
const name = try o.allocTypeName(ty);
defer gpa.free(name);
const line = 0;
const ptr_size = ptr_ty.abiSize(pt);
@ -2146,34 +2083,6 @@ pub const Object = struct {
return debug_ptr_type;
},
.Opaque => {
if (ty.toIntern() == .anyopaque_type) {
const debug_opaque_type = try o.builder.debugSignedType(
try o.builder.metadataString("anyopaque"),
0,
);
try o.debug_type_map.put(gpa, ty, debug_opaque_type);
return debug_opaque_type;
}
const name = try o.allocTypeName(ty);
defer gpa.free(name);
const owner_decl_index = ty.getOwnerDecl(zcu);
const owner_decl = zcu.declPtr(owner_decl_index);
const file_scope = zcu.namespacePtr(owner_decl.src_namespace).fileScope(zcu);
const debug_opaque_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
try o.getDebugFile(file_scope),
try o.namespaceToDebugScope(owner_decl.src_namespace),
owner_decl.typeSrcLine(zcu) + 1, // Line
.none, // Underlying type
0, // Size
0, // Align
.none, // Fields
);
try o.debug_type_map.put(gpa, ty, debug_opaque_type);
return debug_opaque_type;
},
.Array => {
const debug_array_type = try o.builder.debugArrayType(
.none, // Name
@ -2203,9 +2112,9 @@ pub const Object = struct {
.Int => blk: {
const info = elem_ty.intInfo(zcu);
assert(info.bits != 0);
const name = try o.allocTypeName(ty);
defer gpa.free(name);
const builder_name = try o.builder.metadataString(name);
const vec_name = try o.allocTypeName(ty);
defer gpa.free(vec_name);
const builder_name = try o.builder.metadataString(vec_name);
break :blk switch (info.signedness) {
.signed => try o.builder.debugSignedType(builder_name, info.bits),
.unsigned => try o.builder.debugUnsignedType(builder_name, info.bits),
@ -2240,6 +2149,7 @@ pub const Object = struct {
.Optional => {
const name = try o.allocTypeName(ty);
defer gpa.free(name);
const child_ty = ty.optionalChild(zcu);
if (!child_ty.hasRuntimeBitsIgnoreComptime(pt)) {
const debug_bool_type = try o.builder.debugBoolType(
@ -2399,326 +2309,6 @@ pub const Object = struct {
try o.debug_type_map.put(gpa, ty, debug_error_set);
return debug_error_set;
},
.Struct => {
const name = try o.allocTypeName(ty);
defer gpa.free(name);
if (zcu.typeToPackedStruct(ty)) |struct_type| {
const backing_int_ty = struct_type.backingIntTypeUnordered(ip);
if (backing_int_ty != .none) {
const info = Type.fromInterned(backing_int_ty).intInfo(zcu);
const builder_name = try o.builder.metadataString(name);
const debug_int_type = switch (info.signedness) {
.signed => try o.builder.debugSignedType(builder_name, ty.abiSize(pt) * 8),
.unsigned => try o.builder.debugUnsignedType(builder_name, ty.abiSize(pt) * 8),
};
try o.debug_type_map.put(gpa, ty, debug_int_type);
return debug_int_type;
}
}
switch (ip.indexToKey(ty.toIntern())) {
.anon_struct_type => |tuple| {
var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
defer fields.deinit(gpa);
try fields.ensureUnusedCapacity(gpa, tuple.types.len);
comptime assert(struct_layout_version == 2);
var offset: u64 = 0;
const debug_fwd_ref = try o.builder.debugForwardReference();
for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| {
if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(pt)) continue;
const field_size = Type.fromInterned(field_ty).abiSize(pt);
const field_align = Type.fromInterned(field_ty).abiAlignment(pt);
const field_offset = field_align.forward(offset);
offset = field_offset + field_size;
const field_name = if (tuple.names.len != 0)
tuple.names.get(ip)[i].toSlice(ip)
else
try std.fmt.allocPrintZ(gpa, "{d}", .{i});
defer if (tuple.names.len == 0) gpa.free(field_name);
fields.appendAssumeCapacity(try o.builder.debugMemberType(
try o.builder.metadataString(field_name),
.none, // File
debug_fwd_ref,
0,
try o.lowerDebugType(Type.fromInterned(field_ty)),
field_size * 8,
(field_align.toByteUnits() orelse 0) * 8,
field_offset * 8,
));
}
const debug_struct_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
.none, // File
o.debug_compile_unit, // Scope
0, // Line
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(fields.items),
);
o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type);
try o.debug_type_map.put(gpa, ty, debug_struct_type);
return debug_struct_type;
},
.struct_type => {
if (!ip.loadStructType(ty.toIntern()).haveFieldTypes(ip)) {
// This can happen if a struct type makes it all the way to
// flush() without ever being instantiated or referenced (even
// via pointer). The only reason we are hearing about it now is
// that it is being used as a namespace to put other debug types
// into. Therefore we can satisfy this by making an empty namespace,
// rather than changing the frontend to unnecessarily resolve the
// struct field types.
const owner_decl_index = ty.getOwnerDecl(zcu);
const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index);
try o.debug_type_map.put(gpa, ty, debug_struct_type);
return debug_struct_type;
}
},
else => {},
}
if (!ty.hasRuntimeBitsIgnoreComptime(pt)) {
const owner_decl_index = ty.getOwnerDecl(zcu);
const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index);
try o.debug_type_map.put(gpa, ty, debug_struct_type);
return debug_struct_type;
}
const struct_type = zcu.typeToStruct(ty).?;
var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
defer fields.deinit(gpa);
try fields.ensureUnusedCapacity(gpa, struct_type.field_types.len);
const debug_fwd_ref = try o.builder.debugForwardReference();
// Set as forward reference while the type is lowered in case it references itself
try o.debug_type_map.put(gpa, ty, debug_fwd_ref);
comptime assert(struct_layout_version == 2);
var it = struct_type.iterateRuntimeOrder(ip);
while (it.next()) |field_index| {
const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]);
if (!field_ty.hasRuntimeBitsIgnoreComptime(pt)) continue;
const field_size = field_ty.abiSize(pt);
const field_align = pt.structFieldAlignment(
struct_type.fieldAlign(ip, field_index),
field_ty,
struct_type.layout,
);
const field_offset = ty.structFieldOffset(field_index, pt);
const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse
try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
fields.appendAssumeCapacity(try o.builder.debugMemberType(
try o.builder.metadataString(field_name.toSlice(ip)),
.none, // File
debug_fwd_ref,
0, // Line
try o.lowerDebugType(field_ty),
field_size * 8,
(field_align.toByteUnits() orelse 0) * 8,
field_offset * 8,
));
}
const debug_struct_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
.none, // File
o.debug_compile_unit, // Scope
0, // Line
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(fields.items),
);
o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type);
// Set to real type now that it has been lowered fully
const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
map_ptr.* = debug_struct_type;
return debug_struct_type;
},
.Union => {
const owner_decl_index = ty.getOwnerDecl(zcu);
const name = try o.allocTypeName(ty);
defer gpa.free(name);
const union_type = ip.loadUnionType(ty.toIntern());
if (!union_type.haveFieldTypes(ip) or
!ty.hasRuntimeBitsIgnoreComptime(pt) or
!union_type.haveLayout(ip))
{
const debug_union_type = try o.makeEmptyNamespaceDebugType(owner_decl_index);
try o.debug_type_map.put(gpa, ty, debug_union_type);
return debug_union_type;
}
const layout = pt.getUnionLayout(union_type);
const debug_fwd_ref = try o.builder.debugForwardReference();
// Set as forward reference while the type is lowered in case it references itself
try o.debug_type_map.put(gpa, ty, debug_fwd_ref);
if (layout.payload_size == 0) {
const debug_union_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
.none, // File
o.debug_compile_unit, // Scope
0, // Line
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(
&.{try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty))},
),
);
// Set to real type now that it has been lowered fully
const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
map_ptr.* = debug_union_type;
return debug_union_type;
}
var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
defer fields.deinit(gpa);
try fields.ensureUnusedCapacity(gpa, union_type.loadTagType(ip).names.len);
const debug_union_fwd_ref = if (layout.tag_size == 0)
debug_fwd_ref
else
try o.builder.debugForwardReference();
const tag_type = union_type.loadTagType(ip);
for (0..tag_type.names.len) |field_index| {
const field_ty = union_type.field_types.get(ip)[field_index];
if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(pt)) continue;
const field_size = Type.fromInterned(field_ty).abiSize(pt);
const field_align: InternPool.Alignment = switch (union_type.flagsUnordered(ip).layout) {
.@"packed" => .none,
.auto, .@"extern" => pt.unionFieldNormalAlignment(union_type, @intCast(field_index)),
};
const field_name = tag_type.names.get(ip)[field_index];
fields.appendAssumeCapacity(try o.builder.debugMemberType(
try o.builder.metadataString(field_name.toSlice(ip)),
.none, // File
debug_union_fwd_ref,
0, // Line
try o.lowerDebugType(Type.fromInterned(field_ty)),
field_size * 8,
(field_align.toByteUnits() orelse 0) * 8,
0, // Offset
));
}
var union_name_buf: ?[:0]const u8 = null;
defer if (union_name_buf) |buf| gpa.free(buf);
const union_name = if (layout.tag_size == 0) name else name: {
union_name_buf = try std.fmt.allocPrintZ(gpa, "{s}:Payload", .{name});
break :name union_name_buf.?;
};
const debug_union_type = try o.builder.debugUnionType(
try o.builder.metadataString(union_name),
.none, // File
o.debug_compile_unit, // Scope
0, // Line
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(fields.items),
);
o.builder.debugForwardReferenceSetType(debug_union_fwd_ref, debug_union_type);
if (layout.tag_size == 0) {
// Set to real type now that it has been lowered fully
const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
map_ptr.* = debug_union_type;
return debug_union_type;
}
var tag_offset: u64 = undefined;
var payload_offset: u64 = undefined;
if (layout.tag_align.compare(.gte, layout.payload_align)) {
tag_offset = 0;
payload_offset = layout.payload_align.forward(layout.tag_size);
} else {
payload_offset = 0;
tag_offset = layout.tag_align.forward(layout.payload_size);
}
const debug_tag_type = try o.builder.debugMemberType(
try o.builder.metadataString("tag"),
.none, // File
debug_fwd_ref,
0, // Line
try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty)),
layout.tag_size * 8,
(layout.tag_align.toByteUnits() orelse 0) * 8,
tag_offset * 8,
);
const debug_payload_type = try o.builder.debugMemberType(
try o.builder.metadataString("payload"),
.none, // File
debug_fwd_ref,
0, // Line
debug_union_type,
layout.payload_size * 8,
(layout.payload_align.toByteUnits() orelse 0) * 8,
payload_offset * 8,
);
const full_fields: [2]Builder.Metadata =
if (layout.tag_align.compare(.gte, layout.payload_align))
.{ debug_tag_type, debug_payload_type }
else
.{ debug_payload_type, debug_tag_type };
const debug_tagged_union_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
.none, // File
o.debug_compile_unit, // Scope
0, // Line
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(&full_fields),
);
o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_tagged_union_type);
// Set to real type now that it has been lowered fully
const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
map_ptr.* = debug_tagged_union_type;
return debug_tagged_union_type;
},
.Fn => {
const fn_info = zcu.typeToFunc(ty).?;
@ -2776,6 +2366,422 @@ pub const Object = struct {
.Frame => @panic("TODO implement lowerDebugType for Frame types"),
.AnyFrame => @panic("TODO implement lowerDebugType for AnyFrame types"),
// These are the types that need a correct scope.
.Enum,
.Struct,
.Union,
.Opaque => {}
}
const owner_decl_index = ty.getOwnerDeclOrNull(zcu);
const owner_decl: ?*Zcu.Decl =
if (owner_decl_index) |owner| zcu.declPtr(owner) else null;
const file = if (owner_decl) |owner|
try o.getDebugFile(zcu.namespacePtr(owner.src_namespace).fileScope(zcu)) else .none;
const scope = if (owner_decl) |owner|
try o.namespaceToDebugScope(owner.src_namespace) else o.debug_compile_unit;
const line = if (owner_decl) |owner| owner.typeSrcLine(zcu) + 1 else 0;
const name = if (owner_decl) |owner| owner.name.toSlice(ip) else try o.allocTypeName(ty);
defer if (owner_decl == null) gpa.free(name);
switch (ty.zigTypeTag(zcu)) {
.Enum => {
if (!ty.hasRuntimeBitsIgnoreComptime(pt)) {
const debug_enum_type = try o.makeEmptyNamespaceDebugType(owner_decl_index.?);
try o.debug_type_map.put(gpa, ty, debug_enum_type);
return debug_enum_type;
}
const enum_type = ip.loadEnumType(ty.toIntern());
const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len);
defer gpa.free(enumerators);
const int_ty = Type.fromInterned(enum_type.tag_ty);
const int_info = ty.intInfo(zcu);
assert(int_info.bits != 0);
for (enum_type.names.get(ip), 0..) |field_name_ip, i| {
var bigint_space: Value.BigIntSpace = undefined;
const bigint = if (enum_type.values.len != 0)
Value.fromInterned(enum_type.values.get(ip)[i]).toBigInt(&bigint_space, pt)
else
std.math.big.int.Mutable.init(&bigint_space.limbs, i).toConst();
enumerators[i] = try o.builder.debugEnumerator(
try o.builder.metadataString(field_name_ip.toSlice(ip)),
int_info.signedness == .unsigned,
int_info.bits,
bigint,
);
}
const debug_enum_type = try o.builder.debugEnumerationType(
try o.builder.metadataString(name),
file,
scope,
line,
try o.lowerDebugType(int_ty),
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(enumerators),
);
try o.debug_type_map.put(gpa, ty, debug_enum_type);
try o.debug_enums.append(gpa, debug_enum_type);
return debug_enum_type;
},
.Opaque => {
if (ty.toIntern() == .anyopaque_type) {
const debug_opaque_type = try o.builder.debugSignedType(
try o.builder.metadataString("anyopaque"),
0,
);
try o.debug_type_map.put(gpa, ty, debug_opaque_type);
return debug_opaque_type;
}
const debug_opaque_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
file,
scope,
line,
.none, // Underlying type
0, // Size
0, // Align
.none, // Fields
);
try o.debug_type_map.put(gpa, ty, debug_opaque_type);
return debug_opaque_type;
},
.Struct => {
if (zcu.typeToPackedStruct(ty)) |struct_type| {
const backing_int_ty = struct_type.backingIntTypeUnordered(ip);
if (backing_int_ty != .none) {
const info = Type.fromInterned(backing_int_ty).intInfo(zcu);
const builder_name = try o.builder.metadataString(name);
const debug_int_type = switch (info.signedness) {
.signed => try o.builder.debugSignedType(builder_name, ty.abiSize(pt) * 8),
.unsigned => try o.builder.debugUnsignedType(builder_name, ty.abiSize(pt) * 8),
};
try o.debug_type_map.put(gpa, ty, debug_int_type);
return debug_int_type;
}
}
switch (ip.indexToKey(ty.toIntern())) {
.anon_struct_type => |tuple| {
var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
defer fields.deinit(gpa);
try fields.ensureUnusedCapacity(gpa, tuple.types.len);
comptime assert(struct_layout_version == 2);
var offset: u64 = 0;
const debug_fwd_ref = try o.builder.debugForwardReference();
for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| {
if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(pt)) continue;
const field_size = Type.fromInterned(field_ty).abiSize(pt);
const field_align = Type.fromInterned(field_ty).abiAlignment(pt);
const field_offset = field_align.forward(offset);
offset = field_offset + field_size;
const field_name = if (tuple.names.len != 0)
tuple.names.get(ip)[i].toSlice(ip)
else
try std.fmt.allocPrintZ(gpa, "{d}", .{i});
defer if (tuple.names.len == 0) gpa.free(field_name);
fields.appendAssumeCapacity(try o.builder.debugMemberType(
try o.builder.metadataString(field_name),
.none, // File
debug_fwd_ref,
0,
try o.lowerDebugType(Type.fromInterned(field_ty)),
field_size * 8,
(field_align.toByteUnits() orelse 0) * 8,
field_offset * 8,
));
}
const debug_struct_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
file,
scope,
0, // Line
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(fields.items),
);
o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type);
try o.debug_type_map.put(gpa, ty, debug_struct_type);
return debug_struct_type;
},
.struct_type => {
if (!ip.loadStructType(ty.toIntern()).haveFieldTypes(ip)) {
// This can happen if a struct type makes it all the way to
// flush() without ever being instantiated or referenced (even
// via pointer). The only reason we are hearing about it now is
// that it is being used as a namespace to put other debug types
// into. Therefore we can satisfy this by making an empty namespace,
// rather than changing the frontend to unnecessarily resolve the
// struct field types.
const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index.?);
try o.debug_type_map.put(gpa, ty, debug_struct_type);
return debug_struct_type;
}
},
else => {},
}
if (!ty.hasRuntimeBitsIgnoreComptime(pt)) {
const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index.?);
try o.debug_type_map.put(gpa, ty, debug_struct_type);
return debug_struct_type;
}
const struct_type = zcu.typeToStruct(ty).?;
var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
defer fields.deinit(gpa);
try fields.ensureUnusedCapacity(gpa, struct_type.field_types.len);
const debug_fwd_ref = try o.builder.debugForwardReference();
// Set as forward reference while the type is lowered in case it references itself
try o.debug_type_map.put(gpa, ty, debug_fwd_ref);
comptime assert(struct_layout_version == 2);
var it = struct_type.iterateRuntimeOrder(ip);
while (it.next()) |field_index| {
const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]);
if (!field_ty.hasRuntimeBitsIgnoreComptime(pt)) continue;
const field_size = field_ty.abiSize(pt);
const field_align = pt.structFieldAlignment(
struct_type.fieldAlign(ip, field_index),
field_ty,
struct_type.layout,
);
const field_offset = ty.structFieldOffset(field_index, pt);
const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse
try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
fields.appendAssumeCapacity(try o.builder.debugMemberType(
try o.builder.metadataString(field_name.toSlice(ip)),
file,
debug_fwd_ref,
0, // Line
try o.lowerDebugType(field_ty),
field_size * 8,
(field_align.toByteUnits() orelse 0) * 8,
field_offset * 8,
));
}
const debug_struct_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
file,
scope,
line,
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(fields.items),
);
o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type);
// Set to real type now that it has been lowered fully
const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
map_ptr.* = debug_struct_type;
return debug_struct_type;
},
.Union => {
const union_type = ip.loadUnionType(ty.toIntern());
if (!union_type.haveFieldTypes(ip) or
!ty.hasRuntimeBitsIgnoreComptime(pt) or
!union_type.haveLayout(ip))
{
const debug_union_type = try o.makeEmptyNamespaceDebugType(owner_decl_index.?);
try o.debug_type_map.put(gpa, ty, debug_union_type);
return debug_union_type;
}
const layout = pt.getUnionLayout(union_type);
const debug_fwd_ref = try o.builder.debugForwardReference();
// Set as forward reference while the type is lowered in case it references itself
try o.debug_type_map.put(gpa, ty, debug_fwd_ref);
if (layout.payload_size == 0) {
const debug_union_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
file,
scope,
0, // Line
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(
&.{try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty))},
),
);
// Set to real type now that it has been lowered fully
const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
map_ptr.* = debug_union_type;
return debug_union_type;
}
var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
defer fields.deinit(gpa);
try fields.ensureUnusedCapacity(gpa, union_type.loadTagType(ip).names.len);
const debug_union_fwd_ref = if (layout.tag_size == 0)
debug_fwd_ref
else
try o.builder.debugForwardReference();
const tag_type = union_type.loadTagType(ip);
for (0..tag_type.names.len) |field_index| {
const field_ty = union_type.field_types.get(ip)[field_index];
if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(pt)) continue;
const field_size = Type.fromInterned(field_ty).abiSize(pt);
const field_align: InternPool.Alignment = switch (union_type.flagsUnordered(ip).layout) {
.@"packed" => .none,
.auto, .@"extern" => pt.unionFieldNormalAlignment(union_type, @intCast(field_index)),
};
const field_name = tag_type.names.get(ip)[field_index];
fields.appendAssumeCapacity(try o.builder.debugMemberType(
try o.builder.metadataString(field_name.toSlice(ip)),
file,
debug_union_fwd_ref,
0, // Line
try o.lowerDebugType(Type.fromInterned(field_ty)),
field_size * 8,
(field_align.toByteUnits() orelse 0) * 8,
0, // Offset
));
}
var union_name_buf: ?[:0]const u8 = null;
defer if (union_name_buf) |buf| gpa.free(buf);
const union_name = if (layout.tag_size == 0) name else name: {
union_name_buf = try std.fmt.allocPrintZ(gpa, "{s}:Payload", .{name});
break :name union_name_buf.?;
};
const debug_union_type = try o.builder.debugUnionType(
try o.builder.metadataString(union_name),
file,
scope,
line,
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(fields.items),
);
o.builder.debugForwardReferenceSetType(debug_union_fwd_ref, debug_union_type);
if (layout.tag_size == 0) {
// Set to real type now that it has been lowered fully
const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
map_ptr.* = debug_union_type;
return debug_union_type;
}
var tag_offset: u64 = undefined;
var payload_offset: u64 = undefined;
if (layout.tag_align.compare(.gte, layout.payload_align)) {
tag_offset = 0;
payload_offset = layout.payload_align.forward(layout.tag_size);
} else {
payload_offset = 0;
tag_offset = layout.tag_align.forward(layout.payload_size);
}
const debug_tag_type = try o.builder.debugMemberType(
try o.builder.metadataString("tag"),
file, // File
debug_fwd_ref,
0, // Line
try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty)),
layout.tag_size * 8,
(layout.tag_align.toByteUnits() orelse 0) * 8,
tag_offset * 8,
);
const debug_payload_type = try o.builder.debugMemberType(
try o.builder.metadataString("payload"),
file,
debug_fwd_ref,
0, // Line
debug_union_type,
layout.payload_size * 8,
(layout.payload_align.toByteUnits() orelse 0) * 8,
payload_offset * 8,
);
const full_fields: [2]Builder.Metadata =
if (layout.tag_align.compare(.gte, layout.payload_align))
.{ debug_tag_type, debug_payload_type }
else
.{ debug_payload_type, debug_tag_type };
const debug_tagged_union_type = try o.builder.debugStructType(
try o.builder.metadataString(name),
file, // File
scope,
line,
.none, // Underlying type
ty.abiSize(pt) * 8,
(ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
try o.builder.debugTuple(&full_fields),
);
o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_tagged_union_type);
// Set to real type now that it has been lowered fully
const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
map_ptr.* = debug_tagged_union_type;
return debug_tagged_union_type;
},
else => unreachable, // Handled above.
}
}
fn genNamespaces(o: *Object) !void {
var i: usize = 0;
while (i < o.debug_unresolved_namespace_scopes.count()) : (i += 1) {
const namespace_index = o.debug_unresolved_namespace_scopes.keys()[i];
const fwd_ref = o.debug_unresolved_namespace_scopes.values()[i];
const namespace = o.pt.zcu.namespacePtr(namespace_index);
const debug_type = try o.lowerDebugType(namespace.getType(o.pt.zcu));
o.builder.debugForwardReferenceSetType(fwd_ref, debug_type);
}
}
@ -4718,17 +4724,37 @@ pub const DeclGen = struct {
if (!owner_mod.strip) {
const debug_file = try o.getDebugFile(file_scope);
const debug_global_var = try o.builder.debugGlobalVar(
try o.builder.metadataString(decl.name.toSlice(ip)), // Name
try o.builder.metadataStringFromStrtabString(variable_index.name(&o.builder)), // Linkage name
debug_file, // File
debug_file, // Scope
const linkage_name = try o.builder.metadataStringFromStrtabString(variable_index.name(&o.builder));
const debug_global_var = if (!decl.isExtern(zcu)) blk: {
// Imitate a C++ static member variable since neither
// GDB or LLDB can really cope with regular variables
// directly inside a struct type.
const ty = try o.lowerDebugType(decl.typeOf(zcu));
const name = try o.builder.metadataString(decl.name.toSlice(ip));
break :blk try o.builder.debugGlobalVar(
name,
linkage_name,
debug_file,
debug_file,
line_number,
ty,
variable_index,
.none,
.internal,
);
} else try o.builder.debugGlobalVar(
linkage_name,
linkage_name,
debug_file,
debug_file,
line_number,
try o.lowerDebugType(decl.typeOf(zcu)),
variable_index,
.{ .local = !decl.isExtern(zcu) },
.none,
.external,
);
const debug_expression = try o.builder.debugExpression(&.{});
const debug_global_var_expression = try o.builder.debugGlobalVarExpression(
@ -5178,6 +5204,7 @@ pub const FuncGen = struct {
self.scope = try o.builder.debugSubprogram(
self.file,
self.file, // TODO Get the correct scope into hereself.scope is the function's *inner* scope.
try o.builder.metadataString(decl.name.toSlice(&zcu.intern_pool)),
try o.builder.metadataString(decl.fqn.toSlice(&zcu.intern_pool)),
line_number,

View File

@ -7651,6 +7651,7 @@ pub const Metadata = enum(u32) {
composite_vector_type,
derived_pointer_type,
derived_member_type,
derived_static_member_type,
subroutine_type,
enumerator_unsigned,
enumerator_signed_positive,
@ -7663,6 +7664,7 @@ pub const Metadata = enum(u32) {
parameter,
global_var,
@"global_var local",
@"global_var decl",
global_var_expression,
constant,
@ -7696,6 +7698,7 @@ pub const Metadata = enum(u32) {
.composite_vector_type,
.derived_pointer_type,
.derived_member_type,
.derived_static_member_type,
.subroutine_type,
.enumerator_unsigned,
.enumerator_signed_positive,
@ -7707,6 +7710,7 @@ pub const Metadata = enum(u32) {
.parameter,
.global_var,
.@"global_var local",
.@"global_var decl",
.global_var_expression,
=> false,
};
@ -7860,6 +7864,7 @@ pub const Metadata = enum(u32) {
}
};
scope: Metadata,
file: Metadata,
name: MetadataString,
linkage_name: MetadataString,
@ -7990,8 +7995,10 @@ pub const Metadata = enum(u32) {
};
pub const GlobalVar = struct {
pub const Options = struct {
local: bool,
pub const Options = enum {
internal,
internal_decl,
external,
};
name: MetadataString,
@ -8001,6 +8008,7 @@ pub const Metadata = enum(u32) {
line: u32,
ty: Metadata,
variable: Variable.Index,
declaration: Metadata,
};
pub const GlobalVarExpression = struct {
@ -9985,7 +9993,7 @@ pub fn printUnbuffered(
try metadata_formatter.specialized(.@"distinct !", .DISubprogram, .{
.name = extra.name,
.linkageName = extra.linkage_name,
.scope = extra.file,
.scope = extra.scope,
.file = extra.file,
.line = extra.line,
.type = extra.ty,
@ -10079,8 +10087,8 @@ pub fn printUnbuffered(
else => extra.name,
},
.scope = extra.scope,
.file = null,
.line = null,
.file = extra.file,
.line = extra.line,
.baseType = extra.underlying_type,
.size = extra.bitSize(),
.@"align" = extra.bitAlign(),
@ -10101,6 +10109,7 @@ pub fn printUnbuffered(
},
.derived_pointer_type,
.derived_member_type,
.derived_static_member_type,
=> |kind| {
const extra = self.metadataExtraData(Metadata.DerivedType, metadata_item.data);
try metadata_formatter.specialized(.@"!", .DIDerivedType, .{
@ -10109,7 +10118,8 @@ pub fn printUnbuffered(
DW_TAG_member,
}, switch (kind) {
.derived_pointer_type => .DW_TAG_pointer_type,
.derived_member_type => .DW_TAG_member,
.derived_member_type,
.derived_static_member_type => .DW_TAG_member,
else => unreachable,
}),
.name = switch (extra.name) {
@ -10126,7 +10136,7 @@ pub fn printUnbuffered(
0 => null,
else => |bit_offset| bit_offset,
},
.flags = null,
.flags = null, // TODO staticness
.extraData = null,
.dwarfAddressSpace = null,
.annotations = null,
@ -10246,6 +10256,7 @@ pub fn printUnbuffered(
},
.global_var,
.@"global_var local",
.@"global_var decl",
=> |kind| {
const extra = self.metadataExtraData(Metadata.GlobalVar, metadata_item.data);
try metadata_formatter.specialized(.@"distinct !", .DIGlobalVariable, .{
@ -10255,12 +10266,8 @@ pub fn printUnbuffered(
.file = extra.file,
.line = extra.line,
.type = extra.ty,
.isLocal = switch (kind) {
.global_var => false,
.@"global_var local" => true,
else => unreachable,
},
.isDefinition = true,
.isLocal = kind != .global_var,
.isDefinition = kind != .@"global_var decl",
.declaration = null,
.templateParams = null,
.@"align" = null,
@ -11749,6 +11756,7 @@ pub fn debugCompileUnit(
pub fn debugSubprogram(
self: *Builder,
file: Metadata,
scope: Metadata,
name: MetadataString,
linkage_name: MetadataString,
line: u32,
@ -11760,6 +11768,7 @@ pub fn debugSubprogram(
try self.ensureUnusedMetadataCapacity(1, Metadata.Subprogram, 0);
return self.debugSubprogramAssumeCapacity(
file,
scope,
name,
linkage_name,
line,
@ -11949,6 +11958,28 @@ pub fn debugPointerType(
);
}
pub fn debugStaticMemberType(
self: *Builder,
name: MetadataString,
file: Metadata,
scope: Metadata,
line: u32,
underlying_type: Metadata,
) Allocator.Error!Metadata {
try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0);
return self.debugMemberTypeAssumeCapacity(
name,
file,
scope,
line,
underlying_type,
0,
0,
0,
true,
);
}
pub fn debugMemberType(
self: *Builder,
name: MetadataString,
@ -11970,6 +12001,7 @@ pub fn debugMemberType(
size_in_bits,
align_in_bits,
offset_in_bits,
false,
);
}
@ -12063,6 +12095,7 @@ pub fn debugGlobalVar(
line: u32,
ty: Metadata,
variable: Variable.Index,
declaration: Metadata,
options: Metadata.GlobalVar.Options,
) Allocator.Error!Metadata {
try self.ensureUnusedMetadataCapacity(1, Metadata.GlobalVar, 0);
@ -12074,6 +12107,7 @@ pub fn debugGlobalVar(
line,
ty,
variable,
declaration,
options,
);
}
@ -12224,6 +12258,7 @@ pub fn debugCompileUnitAssumeCapacity(
fn debugSubprogramAssumeCapacity(
self: *Builder,
file: Metadata,
scope: Metadata,
name: MetadataString,
linkage_name: MetadataString,
line: u32,
@ -12237,6 +12272,7 @@ fn debugSubprogramAssumeCapacity(
@as(u3, @truncate(@as(u32, @bitCast(options.sp_flags)) >> 2)));
return self.metadataDistinctAssumeCapacity(tag, Metadata.Subprogram{
.file = file,
.scope = scope,
.name = name,
.linkage_name = linkage_name,
.line = line,
@ -12499,21 +12535,25 @@ fn debugMemberTypeAssumeCapacity(
size_in_bits: u64,
align_in_bits: u64,
offset_in_bits: u64,
static: bool,
) Metadata {
assert(!self.strip);
return self.metadataSimpleAssumeCapacity(.derived_member_type, Metadata.DerivedType{
.name = name,
.file = file,
.scope = scope,
.line = line,
.underlying_type = underlying_type,
.size_in_bits_lo = @truncate(size_in_bits),
.size_in_bits_hi = @truncate(size_in_bits >> 32),
.align_in_bits_lo = @truncate(align_in_bits),
.align_in_bits_hi = @truncate(align_in_bits >> 32),
.offset_in_bits_lo = @truncate(offset_in_bits),
.offset_in_bits_hi = @truncate(offset_in_bits >> 32),
});
return self.metadataSimpleAssumeCapacity(
if (static) .derived_static_member_type else .derived_member_type,
Metadata.DerivedType{
.name = name,
.file = file,
.scope = scope,
.line = line,
.underlying_type = underlying_type,
.size_in_bits_lo = @truncate(size_in_bits),
.size_in_bits_hi = @truncate(size_in_bits >> 32),
.align_in_bits_lo = @truncate(align_in_bits),
.align_in_bits_hi = @truncate(align_in_bits >> 32),
.offset_in_bits_lo = @truncate(offset_in_bits),
.offset_in_bits_hi = @truncate(offset_in_bits >> 32),
}
);
}
fn debugSubroutineTypeAssumeCapacity(
@ -12769,11 +12809,16 @@ fn debugGlobalVarAssumeCapacity(
line: u32,
ty: Metadata,
variable: Variable.Index,
declaration: Metadata,
options: Metadata.GlobalVar.Options,
) Metadata {
assert(!self.strip);
return self.metadataDistinctAssumeCapacity(
if (options.local) .@"global_var local" else .global_var,
switch (options) {
.internal => .@"global_var local",
.internal_decl => .@"global_var decl",
.external => .global_var,
},
Metadata.GlobalVar{
.name = name,
.linkage_name = linkage_name,
@ -12782,6 +12827,7 @@ fn debugGlobalVarAssumeCapacity(
.line = line,
.ty = ty,
.variable = variable,
.declaration = declaration,
},
);
}
@ -13818,7 +13864,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
const extra = self.metadataExtraData(Metadata.Subprogram, data);
try metadata_block.writeAbbrevAdapted(MetadataBlock.Subprogram{
.scope = extra.file,
.scope = extra.scope,
.name = extra.name,
.linkage_name = extra.linkage_name,
.file = extra.file,
@ -13898,12 +13944,14 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
},
.derived_pointer_type,
.derived_member_type,
.derived_static_member_type,
=> |kind| {
const extra = self.metadataExtraData(Metadata.DerivedType, data);
try metadata_block.writeAbbrevAdapted(MetadataBlock.DerivedType{
.tag = switch (kind) {
.derived_pointer_type => DW.TAG.pointer_type,
.derived_member_type => DW.TAG.member,
.derived_member_type,
.derived_static_member_type => DW.TAG.member,
else => unreachable,
},
.name = extra.name,
@ -13914,6 +13962,9 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
.size_in_bits = extra.bitSize(),
.align_in_bits = extra.bitAlign(),
.offset_in_bits = extra.bitOffset(),
.flags = .{
.StaticMember = kind == .derived_static_member_type,
},
}, metadata_adapter);
},
.subroutine_type => {
@ -14041,6 +14092,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
},
.global_var,
.@"global_var local",
.@"global_var decl",
=> |kind| {
const extra = self.metadataExtraData(Metadata.GlobalVar, data);
try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVar{
@ -14051,6 +14103,8 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
.line = extra.line,
.ty = extra.ty,
.local = kind == .@"global_var local",
.defined = kind != .@"global_var decl",
.declaration = extra.declaration,
}, metadata_adapter);
},
.global_var_expression => {

View File

@ -694,7 +694,7 @@ pub const MetadataBlock = struct {
pub const ops = [_]AbbrevOp{
.{ .literal = 20 },
.{ .literal = 1 }, // is distinct
.{ .literal = std.dwarf.LANG.C99 }, // source language
.{ .literal = std.dwarf.LANG.C_plus_plus_11 }, // source language
MetadataAbbrev, // file
MetadataAbbrev, // producer
.{ .fixed = 1 }, // isOptimized
@ -863,7 +863,7 @@ pub const MetadataBlock = struct {
.{ .vbr = 6 }, // size in bits
.{ .vbr = 6 }, // align in bits
.{ .vbr = 6 }, // offset in bits
.{ .literal = 0 }, // flags
.{ .fixed = 32 }, // flags
.{ .literal = 0 }, // extra data
};
@ -876,6 +876,7 @@ pub const MetadataBlock = struct {
size_in_bits: u64,
align_in_bits: u64,
offset_in_bits: u64,
flags: Builder.Metadata.DIFlags,
};
pub const SubroutineType = struct {
@ -1002,8 +1003,8 @@ pub const MetadataBlock = struct {
LineAbbrev, // line
MetadataAbbrev, // type
.{ .fixed = 1 }, // local
.{ .literal = 1 }, // defined
.{ .literal = 0 }, // static data members declaration
.{ .fixed = 1 }, // defined
MetadataAbbrev, // static data members declaration
.{ .literal = 0 }, // template params
.{ .literal = 0 }, // align in bits
.{ .literal = 0 }, // annotations
@ -1016,6 +1017,8 @@ pub const MetadataBlock = struct {
line: u32,
ty: Builder.Metadata,
local: bool,
defined: bool,
declaration: Builder.Metadata,
};
pub const GlobalVarExpression = struct {