Skip to content

Commit 52bac4d

Browse files
aykevldeadprogram
authored andcommitted
compiler: support recursive types
Previously, the cycle was broken by inserting an unsafe.Pointer type in some places. This is of course incorrect, and makes debugging harder. However, LLVM provides a way to make temporary nodes that are later replaced, exactly for this purpose. This commit uses those temporary metadata nodes to allow such recursive types.
1 parent be9e4f4 commit 52bac4d

File tree

3 files changed

+37
-5
lines changed

3 files changed

+37
-5
lines changed

compiler/compiler.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package compiler
22

33
import (
4+
"debug/dwarf"
45
"errors"
56
"fmt"
67
"go/ast"
@@ -91,6 +92,7 @@ type Compiler struct {
9192
dibuilder *llvm.DIBuilder
9293
cu llvm.Metadata
9394
difiles map[string]llvm.Metadata
95+
ditypes map[types.Type]llvm.Metadata
9496
machine llvm.TargetMachine
9597
targetData llvm.TargetData
9698
intType llvm.Type
@@ -136,6 +138,7 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
136138
c := &Compiler{
137139
Config: config,
138140
difiles: make(map[string]llvm.Metadata),
141+
ditypes: make(map[types.Type]llvm.Metadata),
139142
}
140143

141144
target, err := llvm.GetTargetFromTriple(config.Triple)
@@ -596,6 +599,17 @@ func isPointer(typ types.Type) bool {
596599

597600
// Get the DWARF type for this Go type.
598601
func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
602+
if md, ok := c.ditypes[typ]; ok {
603+
return md
604+
}
605+
md := c.createDIType(typ)
606+
c.ditypes[typ] = md
607+
return md
608+
}
609+
610+
// createDIType creates a new DWARF type. Don't call this function directly,
611+
// call getDIType instead.
612+
func (c *Compiler) createDIType(typ types.Type) llvm.Metadata {
599613
llvmType := c.getLLVMType(typ)
600614
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
601615
switch typ := typ.(type) {
@@ -732,14 +746,17 @@ func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
732746
},
733747
})
734748
case *types.Struct:
749+
// Placeholder metadata node, to be replaced afterwards.
750+
temporaryMDNode := c.dibuilder.CreateReplaceableCompositeType(llvm.Metadata{}, llvm.DIReplaceableCompositeType{
751+
Tag: dwarf.TagStructType,
752+
SizeInBits: sizeInBytes * 8,
753+
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
754+
})
755+
c.ditypes[typ] = temporaryMDNode
735756
elements := make([]llvm.Metadata, typ.NumFields())
736757
for i := range elements {
737758
field := typ.Field(i)
738759
fieldType := field.Type()
739-
if _, ok := fieldType.Underlying().(*types.Pointer); ok {
740-
// XXX hack to avoid recursive types
741-
fieldType = types.Typ[types.UnsafePointer]
742-
}
743760
llvmField := c.getLLVMType(fieldType)
744761
elements[i] = c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
745762
Name: field.Name(),
@@ -749,11 +766,13 @@ func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
749766
Type: c.getDIType(fieldType),
750767
})
751768
}
752-
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
769+
md := c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
753770
SizeInBits: sizeInBytes * 8,
754771
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
755772
Elements: elements,
756773
})
774+
temporaryMDNode.ReplaceAllUsesWith(md)
775+
return md
757776
default:
758777
panic("unknown type while generating DWARF debug type: " + typ.String())
759778
}

testdata/structs.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ type s8 struct {
6060
b byte // 1 element
6161
}
6262

63+
// linked list (recursive type)
64+
type s9 struct {
65+
n int
66+
next *s9
67+
s []*s9
68+
}
69+
6370
func test0(s s0) {
6471
println("test0")
6572
}
@@ -106,6 +113,10 @@ func test8(s s8) {
106113
println("test8", len(s.a), cap(s.a), s.a[0], s.a[1], s.b)
107114
}
108115

116+
func test9(s s9) {
117+
println("test9", s.next.next)
118+
}
119+
109120
func main() {
110121
test0(s0{})
111122
test1(s1{1})
@@ -119,4 +130,5 @@ func main() {
119130
test6(s6{"foo", 5})
120131
test7(s7{a: nil, b: 8})
121132
test8(s8{[]byte{12, 13, 14}[:2], 6})
133+
test9(s9{next: &s9{}})
122134
}

testdata/structs.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ test5 1 2 3
99
test6 foo 3 5
1010
test7 (0:nil) 8
1111
test8 2 3 12 13 6
12+
test9 nil

0 commit comments

Comments
 (0)