diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index b2d32fb539..1017977bbc 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -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 here—self.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, diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index a23bc86c57..5c82f0abb0 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -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 => { diff --git a/src/codegen/llvm/ir.zig b/src/codegen/llvm/ir.zig index 6a7c6c6857..dc8c6e5b68 100644 --- a/src/codegen/llvm/ir.zig +++ b/src/codegen/llvm/ir.zig @@ -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 {