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.
Этот коммит содержится в:
Ayke van Laethem 2019-10-13 17:11:57 +02:00 коммит произвёл Ron Evans
родитель be9e4f439c
коммит 52bac4d75b
3 изменённых файлов: 37 добавлений и 5 удалений

Просмотреть файл

@ -1,6 +1,7 @@
package compiler package compiler
import ( import (
"debug/dwarf"
"errors" "errors"
"fmt" "fmt"
"go/ast" "go/ast"
@ -91,6 +92,7 @@ type Compiler struct {
dibuilder *llvm.DIBuilder dibuilder *llvm.DIBuilder
cu llvm.Metadata cu llvm.Metadata
difiles map[string]llvm.Metadata difiles map[string]llvm.Metadata
ditypes map[types.Type]llvm.Metadata
machine llvm.TargetMachine machine llvm.TargetMachine
targetData llvm.TargetData targetData llvm.TargetData
intType llvm.Type intType llvm.Type
@ -136,6 +138,7 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
c := &Compiler{ c := &Compiler{
Config: config, Config: config,
difiles: make(map[string]llvm.Metadata), difiles: make(map[string]llvm.Metadata),
ditypes: make(map[types.Type]llvm.Metadata),
} }
target, err := llvm.GetTargetFromTriple(config.Triple) target, err := llvm.GetTargetFromTriple(config.Triple)
@ -596,6 +599,17 @@ func isPointer(typ types.Type) bool {
// Get the DWARF type for this Go type. // Get the DWARF type for this Go type.
func (c *Compiler) getDIType(typ types.Type) llvm.Metadata { func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
if md, ok := c.ditypes[typ]; ok {
return md
}
md := c.createDIType(typ)
c.ditypes[typ] = md
return md
}
// createDIType creates a new DWARF type. Don't call this function directly,
// call getDIType instead.
func (c *Compiler) createDIType(typ types.Type) llvm.Metadata {
llvmType := c.getLLVMType(typ) llvmType := c.getLLVMType(typ)
sizeInBytes := c.targetData.TypeAllocSize(llvmType) sizeInBytes := c.targetData.TypeAllocSize(llvmType)
switch typ := typ.(type) { switch typ := typ.(type) {
@ -732,14 +746,17 @@ func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
}, },
}) })
case *types.Struct: case *types.Struct:
// Placeholder metadata node, to be replaced afterwards.
temporaryMDNode := c.dibuilder.CreateReplaceableCompositeType(llvm.Metadata{}, llvm.DIReplaceableCompositeType{
Tag: dwarf.TagStructType,
SizeInBits: sizeInBytes * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
})
c.ditypes[typ] = temporaryMDNode
elements := make([]llvm.Metadata, typ.NumFields()) elements := make([]llvm.Metadata, typ.NumFields())
for i := range elements { for i := range elements {
field := typ.Field(i) field := typ.Field(i)
fieldType := field.Type() fieldType := field.Type()
if _, ok := fieldType.Underlying().(*types.Pointer); ok {
// XXX hack to avoid recursive types
fieldType = types.Typ[types.UnsafePointer]
}
llvmField := c.getLLVMType(fieldType) llvmField := c.getLLVMType(fieldType)
elements[i] = c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{ elements[i] = c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: field.Name(), Name: field.Name(),
@ -749,11 +766,13 @@ func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
Type: c.getDIType(fieldType), Type: c.getDIType(fieldType),
}) })
} }
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{ md := c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
SizeInBits: sizeInBytes * 8, SizeInBits: sizeInBytes * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8, AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
Elements: elements, Elements: elements,
}) })
temporaryMDNode.ReplaceAllUsesWith(md)
return md
default: default:
panic("unknown type while generating DWARF debug type: " + typ.String()) panic("unknown type while generating DWARF debug type: " + typ.String())
} }

12
testdata/structs.go предоставленный
Просмотреть файл

@ -60,6 +60,13 @@ type s8 struct {
b byte // 1 element b byte // 1 element
} }
// linked list (recursive type)
type s9 struct {
n int
next *s9
s []*s9
}
func test0(s s0) { func test0(s s0) {
println("test0") println("test0")
} }
@ -106,6 +113,10 @@ func test8(s s8) {
println("test8", len(s.a), cap(s.a), s.a[0], s.a[1], s.b) println("test8", len(s.a), cap(s.a), s.a[0], s.a[1], s.b)
} }
func test9(s s9) {
println("test9", s.next.next)
}
func main() { func main() {
test0(s0{}) test0(s0{})
test1(s1{1}) test1(s1{1})
@ -119,4 +130,5 @@ func main() {
test6(s6{"foo", 5}) test6(s6{"foo", 5})
test7(s7{a: nil, b: 8}) test7(s7{a: nil, b: 8})
test8(s8{[]byte{12, 13, 14}[:2], 6}) test8(s8{[]byte{12, 13, 14}[:2], 6})
test9(s9{next: &s9{}})
} }

1
testdata/structs.txt предоставленный
Просмотреть файл

@ -9,3 +9,4 @@ test5 1 2 3
test6 foo 3 5 test6 foo 3 5
test7 (0:nil) 8 test7 (0:nil) 8
test8 2 3 12 13 6 test8 2 3 12 13 6
test9 nil