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.
Этот коммит содержится в:
родитель
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
предоставленный
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
предоставленный
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
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче