LLVM: add debug type lowering for ptr, slice, opaque, optional

also fix issue with memoization and recursiveness.
This commit is contained in:
Andrew Kelley 2022-03-07 22:14:22 -07:00
parent 40c0bdd385
commit 3654ec8770

View File

@ -1075,15 +1075,13 @@ pub const DeclGen = struct {
},
.Optional => {
var buf: Type.Payload.ElemType = undefined;
const child_type = t.optionalChild(&buf);
if (!child_type.hasRuntimeBits()) {
const child_ty = t.optionalChild(&buf);
if (!child_ty.hasRuntimeBits()) {
return dg.context.intType(1);
}
const payload_llvm_ty = try dg.llvmType(child_type);
const payload_llvm_ty = try dg.llvmType(child_ty);
if (t.isPtrLikeOptional()) {
return payload_llvm_ty;
} else if (!child_type.hasRuntimeBits()) {
return dg.context.intType(1);
}
const fields: [2]*const llvm.Type = .{
@ -1905,20 +1903,19 @@ pub const DeclGen = struct {
}
fn lowerDebugType(dg: *DeclGen, ty: Type) Allocator.Error!*llvm.DIType {
const gop = try dg.object.di_type_map.getOrPut(dg.gpa, ty);
const gpa = dg.gpa;
// Be careful not to reference this `gop` variable after any recursive calls
// to `lowerDebugType`.
const gop = try dg.object.di_type_map.getOrPut(gpa, ty);
if (gop.found_existing) return gop.value_ptr.*;
errdefer assert(dg.object.di_type_map.remove(ty));
if (!gop.found_existing) {
gop.value_ptr.* = try lowerDebugTypeRaw(dg, ty);
}
return gop.value_ptr.*;
}
fn lowerDebugTypeRaw(dg: *DeclGen, ty: Type) Allocator.Error!*llvm.DIType {
const target = dg.module.getTarget();
const dib = dg.object.di_builder.?;
const gpa = dg.gpa;
switch (ty.zigTypeTag()) {
.Void, .NoReturn => return dib.createBasicType("void", 0, DW.ATE.signed),
.Void, .NoReturn => {
gop.value_ptr.* = dib.createBasicType("void", 0, DW.ATE.signed);
return gop.value_ptr.*;
},
.Int => {
const info = ty.intInfo(target);
assert(info.bits != 0);
@ -1927,13 +1924,18 @@ pub const DeclGen = struct {
.signed => DW.ATE.signed,
.unsigned => DW.ATE.unsigned,
};
return dib.createBasicType(name, info.bits, dwarf_encoding);
gop.value_ptr.* = dib.createBasicType(name, info.bits, dwarf_encoding);
return gop.value_ptr.*;
},
.Enum => {
const owner_decl = ty.getOwnerDecl();
if (!ty.hasRuntimeBits()) {
return dg.makeEmptyNamespaceDIType(owner_decl);
const enum_di_ty = try dg.makeEmptyNamespaceDIType(owner_decl);
// The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
// means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, enum_di_ty);
return enum_di_ty;
}
const field_names = ty.enumFields().keys();
@ -1966,7 +1968,7 @@ pub const DeclGen = struct {
var buffer: Type.Payload.Bits = undefined;
const int_ty = ty.intTagType(&buffer);
return dib.createEnumerationType(
const enum_di_ty = dib.createEnumerationType(
di_scope,
name,
di_file,
@ -1978,79 +1980,264 @@ pub const DeclGen = struct {
try lowerDebugType(dg, int_ty),
"",
);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, enum_di_ty);
return enum_di_ty;
},
.Float => {
const bits = ty.floatBits(target);
const name = try ty.nameAlloc(gpa); // TODO this is a leak
return dib.createBasicType(name, bits, DW.ATE.float);
gop.value_ptr.* = dib.createBasicType(name, bits, DW.ATE.float);
return gop.value_ptr.*;
},
.Bool => {
gop.value_ptr.* = dib.createBasicType("bool", 1, DW.ATE.boolean);
return gop.value_ptr.*;
},
.Bool => return dib.createBasicType("bool", 1, DW.ATE.boolean),
.Pointer => {
if (ty.isSlice()) {
@panic("TODO debug info type for slices");
//var buf: Type.SlicePtrFieldTypeBuffer = undefined;
//const ptr_type = ty.slicePtrFieldType(&buf);
// Normalize everything that the debug info does not represent.
const ptr_info = ty.ptrInfo().data;
//const fields: [2]*const llvm.Type = .{
// try dg.llvmType(ptr_type),
// try dg.llvmType(Type.usize),
//};
//return dg.context.structType(&fields, fields.len, .False);
if (ptr_info.sentinel != null or
ptr_info.@"addrspace" != .generic or
ptr_info.bit_offset != 0 or
ptr_info.host_size != 0 or
ptr_info.@"allowzero" or
!ptr_info.mutable or
ptr_info.@"volatile" or
ptr_info.size == .Many or ptr_info.size == .C)
{
var payload: Type.Payload.Pointer = .{
.data = .{
.pointee_type = ptr_info.pointee_type,
.sentinel = null,
.@"align" = ptr_info.@"align",
.@"addrspace" = .generic,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = true,
.@"volatile" = false,
.size = switch (ptr_info.size) {
.Many, .C, .One => .One,
.Slice => .Slice,
},
},
};
const bland_ptr_ty = Type.initPayload(&payload.base);
const ptr_di_ty = try dg.lowerDebugType(bland_ptr_ty);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, ptr_di_ty);
return ptr_di_ty;
}
if (ty.isSlice()) {
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const ptr_ty = ty.slicePtrFieldType(&buf);
const len_ty = Type.usize;
const name = try ty.nameAlloc(gpa); // TODO this is a leak
const di_file: ?*llvm.DIFile = null;
const line = 0;
const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
const fwd_decl = dib.createReplaceableCompositeType(
DW.TAG.structure_type,
name.ptr,
compile_unit_scope,
di_file,
line,
);
gop.value_ptr.* = fwd_decl;
const ptr_size = ptr_ty.abiSize(target);
const ptr_align = ptr_ty.abiAlignment(target);
const len_size = len_ty.abiSize(target);
const len_align = len_ty.abiAlignment(target);
var offset: u64 = 0;
offset += ptr_size;
offset = std.mem.alignForwardGeneric(u64, offset, len_align);
const len_offset = offset;
const fields: [2]*llvm.DIType = .{
dib.createMemberType(
fwd_decl.toScope(),
"ptr",
di_file,
line,
ptr_size * 8, // size in bits
ptr_align * 8, // align in bits
0, // offset in bits
0, // flags
try dg.lowerDebugType(ptr_ty),
),
dib.createMemberType(
fwd_decl.toScope(),
"len",
di_file,
line,
len_size * 8, // size in bits
len_align * 8, // align in bits
len_offset * 8, // offset in bits
0, // flags
try dg.lowerDebugType(len_ty),
),
};
const replacement_di_type = dib.createStructType(
compile_unit_scope,
name.ptr,
di_file,
line,
ty.abiSize(target) * 8, // size in bits
ty.abiAlignment(target) * 8, // align in bits
0, // flags
null, // derived from
&fields,
fields.len,
0, // run time lang
null, // vtable holder
"", // unique id
);
dib.replaceTemporary(fwd_decl, replacement_di_type);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, replacement_di_type);
return replacement_di_type;
}
const ptr_info = ty.ptrInfo().data;
const elem_di_ty = try lowerDebugType(dg, ptr_info.pointee_type);
const name = try ty.nameAlloc(gpa); // TODO this is a leak
return dib.createPointerType(
const ptr_di_ty = dib.createPointerType(
elem_di_ty,
target.cpu.arch.ptrBitWidth(),
ty.ptrAlignment(target) * 8,
name,
);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, ptr_di_ty);
return ptr_di_ty;
},
.Opaque => {
@panic("TODO debug info type for opaque");
const name = try ty.nameAlloc(gpa); // TODO this is a leak
const owner_decl = ty.getOwnerDecl();
const opaque_di_ty = dib.createForwardDeclType(
DW.TAG.structure_type,
name,
try dg.namespaceToDebugScope(owner_decl.src_namespace),
try dg.object.getDIFile(gpa, owner_decl.src_namespace.file_scope),
owner_decl.src_node + 1,
);
// The recursive call to `lowerDebugType` va `namespaceToDebugScope`
// means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, opaque_di_ty);
return opaque_di_ty;
},
.Array => {
const elem_di_ty = try lowerDebugType(dg, ty.childType());
return dib.createArrayType(
const array_di_ty = dib.createArrayType(
ty.abiSize(target) * 8,
ty.abiAlignment(target) * 8,
elem_di_ty,
try lowerDebugType(dg, ty.childType()),
@intCast(c_int, ty.arrayLen()),
);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, array_di_ty);
return array_di_ty;
},
.Vector => {
@panic("TODO debug info type for vector");
},
.Optional => {
@panic("TODO debug info type for optional");
//var buf: Type.Payload.ElemType = undefined;
//const child_type = ty.optionalChild(&buf);
//if (!child_type.hasRuntimeBits()) {
// return dg.context.intType(1);
//}
//const payload_llvm_ty = try dg.llvmType(child_type);
//if (ty.isPtrLikeOptional()) {
// return payload_llvm_ty;
//} else if (!child_type.hasRuntimeBits()) {
// return dg.context.intType(1);
//}
const name = try ty.nameAlloc(gpa); // TODO this is a leak
var buf: Type.Payload.ElemType = undefined;
const child_ty = ty.optionalChild(&buf);
if (!child_ty.hasRuntimeBits()) {
gop.value_ptr.* = dib.createBasicType(name, 1, DW.ATE.boolean);
return gop.value_ptr.*;
}
if (ty.isPtrLikeOptional()) {
const ptr_di_ty = try dg.lowerDebugType(child_ty);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, ptr_di_ty);
return ptr_di_ty;
}
//const fields: [2]*const llvm.Type = .{
// payload_llvm_ty, dg.context.intType(1),
//};
//return dg.context.structType(&fields, fields.len, .False);
const di_file: ?*llvm.DIFile = null;
const line = 0;
const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
const fwd_decl = dib.createReplaceableCompositeType(
DW.TAG.structure_type,
name.ptr,
compile_unit_scope,
di_file,
line,
);
gop.value_ptr.* = fwd_decl;
const non_null_ty = Type.bool;
const payload_size = child_ty.abiSize(target);
const payload_align = child_ty.abiAlignment(target);
const non_null_size = non_null_ty.abiSize(target);
const non_null_align = non_null_ty.abiAlignment(target);
var offset: u64 = 0;
offset += payload_size;
offset = std.mem.alignForwardGeneric(u64, offset, non_null_align);
const non_null_offset = offset;
const fields: [2]*llvm.DIType = .{
dib.createMemberType(
fwd_decl.toScope(),
"data",
di_file,
line,
payload_size * 8, // size in bits
payload_align * 8, // align in bits
0, // offset in bits
0, // flags
try dg.lowerDebugType(child_ty),
),
dib.createMemberType(
fwd_decl.toScope(),
"some",
di_file,
line,
non_null_size * 8, // size in bits
non_null_align * 8, // align in bits
non_null_offset * 8, // offset in bits
0, // flags
try dg.lowerDebugType(non_null_ty),
),
};
const replacement_di_type = dib.createStructType(
compile_unit_scope,
name.ptr,
di_file,
line,
ty.abiSize(target) * 8, // size in bits
ty.abiAlignment(target) * 8, // align in bits
0, // flags
null, // derived from
&fields,
fields.len,
0, // run time lang
null, // vtable holder
"", // unique id
);
dib.replaceTemporary(fwd_decl, replacement_di_type);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, replacement_di_type);
return replacement_di_type;
},
.ErrorUnion => {
const err_set_ty = ty.errorUnionSet();
const err_set_di_ty = try dg.lowerDebugType(err_set_ty);
const payload_ty = ty.errorUnionPayload();
if (!payload_ty.hasRuntimeBits()) {
const err_set_di_ty = try dg.lowerDebugType(err_set_ty);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, err_set_di_ty);
return err_set_di_ty;
}
const payload_di_ty = try dg.lowerDebugType(payload_ty);
const name = try ty.nameAlloc(gpa); // TODO this is a leak
const di_file: ?*llvm.DIFile = null;
const line = 0;
@ -2062,6 +2249,7 @@ pub const DeclGen = struct {
di_file,
line,
);
gop.value_ptr.* = fwd_decl;
const err_set_size = err_set_ty.abiSize(target);
const err_set_align = err_set_ty.abiAlignment(target);
@ -2083,7 +2271,7 @@ pub const DeclGen = struct {
err_set_align * 8, // align in bits
0, // offset in bits
0, // flags
err_set_di_ty,
try dg.lowerDebugType(err_set_ty),
),
dib.createMemberType(
fwd_decl.toScope(),
@ -2094,7 +2282,7 @@ pub const DeclGen = struct {
payload_align * 8, // align in bits
payload_offset * 8, // offset in bits
0, // flags
payload_di_ty,
try dg.lowerDebugType(payload_ty),
),
};
@ -2114,13 +2302,15 @@ pub const DeclGen = struct {
"", // unique id
);
dib.replaceTemporary(fwd_decl, replacement_di_type);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, replacement_di_type);
return replacement_di_type;
},
.ErrorSet => {
// TODO make this a proper enum with all the error codes in it.
// will need to consider how to take incremental compilation into account.
return dib.createBasicType("anyerror", 16, DW.ATE.unsigned);
gop.value_ptr.* = dib.createBasicType("anyerror", 16, DW.ATE.unsigned);
return gop.value_ptr.*;
},
.Struct => {
@panic("TODO debug info type for struct");
@ -2347,11 +2537,14 @@ pub const DeclGen = struct {
}
}
return dib.createSubroutineType(
const fn_di_ty = dib.createSubroutineType(
param_di_types.items.ptr,
@intCast(c_int, param_di_types.items.len),
0,
);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try dg.object.di_type_map.put(gpa, ty, fn_di_ty);
return fn_di_ty;
},
.ComptimeInt => unreachable,
.ComptimeFloat => unreachable,