compiler: refactor named types to create them lazily
This commit refactors named types to be created lazily. Instead of defining all types in advance, do it only when necessary.
Этот коммит содержится в:
родитель
88bb61f287
коммит
6b5b4a681d
9 изменённых файлов: 51 добавлений и 63 удалений
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
// emitMakeChan returns a new channel value for the given channel type.
|
||||
func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
|
||||
chanType := c.mod.GetTypeByName("runtime.channel")
|
||||
chanType := c.getLLVMType(c.getRuntimeType("channel"))
|
||||
size := c.targetData.TypeAllocSize(chanType)
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
ptr := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "chan.alloc")
|
||||
|
|
|
@ -275,25 +275,6 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||
|
||||
var frames []*Frame
|
||||
|
||||
// Declare all named struct types.
|
||||
for _, t := range c.ir.NamedTypes {
|
||||
if named, ok := t.Type.Type().(*types.Named); ok {
|
||||
if _, ok := named.Underlying().(*types.Struct); ok {
|
||||
t.LLVMType = c.ctx.StructCreateNamed(named.Obj().Pkg().Path() + "." + named.Obj().Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define all named struct types.
|
||||
for _, t := range c.ir.NamedTypes {
|
||||
if named, ok := t.Type.Type().(*types.Named); ok {
|
||||
if st, ok := named.Underlying().(*types.Struct); ok {
|
||||
llvmType := c.getLLVMType(st)
|
||||
t.LLVMType.StructSetBody(llvmType.StructElementTypes(), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Declare all globals.
|
||||
for _, g := range c.ir.Globals {
|
||||
typ := g.Type().(*types.Pointer).Elem()
|
||||
|
@ -413,6 +394,22 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||
return c.diagnostics
|
||||
}
|
||||
|
||||
// getRuntimeType obtains a named type from the runtime package and returns it
|
||||
// as a Go type.
|
||||
func (c *Compiler) getRuntimeType(name string) types.Type {
|
||||
return c.ir.Program.ImportedPackage("runtime").Type(name).Type()
|
||||
}
|
||||
|
||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||
// it as a LLVM type, creating it if necessary. It is a shorthand for
|
||||
// getLLVMType(getRuntimeType(name)).
|
||||
func (c *Compiler) getLLVMRuntimeType(name string) llvm.Type {
|
||||
return c.getLLVMType(c.getRuntimeType(name))
|
||||
}
|
||||
|
||||
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
|
||||
// named struct types (or Go types implemented as named LLVM structs such as
|
||||
// strings) it also creates it first if necessary.
|
||||
func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
||||
switch typ := goType.(type) {
|
||||
case *types.Array:
|
||||
|
@ -441,7 +438,7 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
|||
case types.Complex128:
|
||||
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
|
||||
case types.String, types.UntypedString:
|
||||
return c.mod.GetTypeByName("runtime._string")
|
||||
return c.getLLVMRuntimeType("_string")
|
||||
case types.Uintptr:
|
||||
return c.uintptrType
|
||||
case types.UnsafePointer:
|
||||
|
@ -450,16 +447,23 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
|||
panic("unknown basic type: " + typ.String())
|
||||
}
|
||||
case *types.Chan:
|
||||
return llvm.PointerType(c.mod.GetTypeByName("runtime.channel"), 0)
|
||||
return llvm.PointerType(c.getLLVMRuntimeType("channel"), 0)
|
||||
case *types.Interface:
|
||||
return c.mod.GetTypeByName("runtime._interface")
|
||||
return c.getLLVMRuntimeType("_interface")
|
||||
case *types.Map:
|
||||
return llvm.PointerType(c.mod.GetTypeByName("runtime.hashmap"), 0)
|
||||
return llvm.PointerType(c.getLLVMRuntimeType("hashmap"), 0)
|
||||
case *types.Named:
|
||||
if _, ok := typ.Underlying().(*types.Struct); ok {
|
||||
llvmType := c.mod.GetTypeByName(typ.Obj().Pkg().Path() + "." + typ.Obj().Name())
|
||||
if st, ok := typ.Underlying().(*types.Struct); ok {
|
||||
// Structs are a special case. While other named types are ignored
|
||||
// in LLVM IR, named structs are implemented as named structs in
|
||||
// LLVM. This is because it is otherwise impossible to create
|
||||
// self-referencing types such as linked lists.
|
||||
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
|
||||
llvmType := c.mod.GetTypeByName(llvmName)
|
||||
if llvmType.IsNil() {
|
||||
panic("underlying type not found: " + typ.Obj().Pkg().Path() + "." + typ.Obj().Name())
|
||||
llvmType = c.ctx.StructCreateNamed(llvmName)
|
||||
underlying := c.getLLVMType(st)
|
||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||
}
|
||||
return llvmType
|
||||
}
|
||||
|
@ -1693,9 +1697,9 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
var iteratorType llvm.Type
|
||||
switch typ := expr.X.Type().Underlying().(type) {
|
||||
case *types.Basic: // string
|
||||
iteratorType = c.mod.GetTypeByName("runtime.stringIterator")
|
||||
iteratorType = c.getLLVMRuntimeType("stringIterator")
|
||||
case *types.Map:
|
||||
iteratorType = c.mod.GetTypeByName("runtime.hashmapIterator")
|
||||
iteratorType = c.getLLVMRuntimeType("hashmapIterator")
|
||||
default:
|
||||
panic("unknown type in range: " + typ.String())
|
||||
}
|
||||
|
@ -1855,7 +1859,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
|
||||
newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
|
||||
newLen := c.builder.CreateSub(high, low, "")
|
||||
str := llvm.Undef(c.mod.GetTypeByName("runtime._string"))
|
||||
str := llvm.Undef(c.getLLVMRuntimeType("_string"))
|
||||
str = c.builder.CreateInsertValue(str, newPtr, 0, "")
|
||||
str = c.builder.CreateInsertValue(str, newLen, 1, "")
|
||||
return str, nil
|
||||
|
@ -2234,7 +2238,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
|||
global.SetUnnamedAddr(true)
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
strPtr := c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
strObj := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._string"), []llvm.Value{strPtr, strLen})
|
||||
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
return strObj
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
if !expr.IsNil() {
|
||||
|
@ -2287,7 +2291,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
|||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstPointerNull(c.i8ptrType),
|
||||
}
|
||||
return llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), fields)
|
||||
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
||||
case *types.Pointer:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil pointer constant")
|
||||
|
|
|
@ -29,7 +29,7 @@ func (c *Compiler) deferInitFunc(frame *Frame) {
|
|||
frame.deferClosureFuncs = make(map[*ir.Function]int)
|
||||
|
||||
// Create defer list pointer.
|
||||
deferType := llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)
|
||||
deferType := llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)
|
||||
frame.deferPtr = c.builder.CreateAlloca(deferType, "deferPtr")
|
||||
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) {
|
|||
}
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0), c.i8ptrType}
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0), c.i8ptrType}
|
||||
for _, arg := range callback.Args {
|
||||
valueTypes = append(valueTypes, c.getLLVMType(arg.Type()))
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) {
|
|||
// Direct call.
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)}
|
||||
for _, param := range callback.Params {
|
||||
valueTypes = append(valueTypes, c.getLLVMType(param.Type()))
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) {
|
|||
case *ssa.MakeClosure:
|
||||
// Get the real defer struct type and cast to it.
|
||||
fn := c.ir.GetFunction(callback.Fn.(*ssa.Function))
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)}
|
||||
params := fn.Signature.Params()
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
valueTypes = append(valueTypes, c.getLLVMType(params.At(i).Type()))
|
||||
|
|
|
@ -51,7 +51,7 @@ func (c *Compiler) LowerFuncValues() {
|
|||
}
|
||||
|
||||
// Find all func values used in the program with their signatures.
|
||||
funcValueWithSignaturePtr := llvm.PointerType(c.mod.GetTypeByName("runtime.funcValueWithSignature"), 0)
|
||||
funcValueWithSignaturePtr := llvm.PointerType(c.getLLVMRuntimeType("funcValueWithSignature"), 0)
|
||||
signatures := map[string]*funcSignatureInfo{}
|
||||
for global := c.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||
if global.Type() != funcValueWithSignaturePtr {
|
||||
|
|
|
@ -52,7 +52,7 @@ func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signa
|
|||
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
||||
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
||||
if funcValueWithSignatureGlobal.IsNil() {
|
||||
funcValueWithSignatureType := c.mod.GetTypeByName("runtime.funcValueWithSignature")
|
||||
funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature")
|
||||
funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
|
||||
llvm.ConstPtrToInt(funcPtr, c.uintptrType),
|
||||
sigGlobal,
|
||||
|
@ -126,7 +126,7 @@ func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type {
|
|||
rawPtr := c.getRawFuncType(typ)
|
||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
||||
case funcValueSwitch:
|
||||
return c.mod.GetTypeByName("runtime.funcValue")
|
||||
return c.getLLVMRuntimeType("funcValue")
|
||||
default:
|
||||
panic("unimplemented func value variant")
|
||||
}
|
||||
|
|
|
@ -321,7 +321,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||
|
||||
// Coroutine setup.
|
||||
c.builder.SetInsertPointBefore(f.EntryBasicBlock().FirstInstruction())
|
||||
taskState := c.builder.CreateAlloca(c.mod.GetTypeByName("runtime.taskState"), "task.state")
|
||||
taskState := c.builder.CreateAlloca(c.getLLVMRuntimeType("taskState"), "task.state")
|
||||
stateI8 := c.builder.CreateBitCast(taskState, c.i8ptrType, "task.state.i8")
|
||||
id := c.builder.CreateCall(coroIdFunc, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
|
|
|
@ -163,8 +163,8 @@ func (c *Compiler) LowerInterfaces() {
|
|||
// run runs the pass itself.
|
||||
func (p *lowerInterfacesPass) run() {
|
||||
// Collect all type codes.
|
||||
typecodeIDPtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typecodeID"), 0)
|
||||
typeInInterfacePtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typeInInterface"), 0)
|
||||
typecodeIDPtr := llvm.PointerType(p.getLLVMRuntimeType("typecodeID"), 0)
|
||||
typeInInterfacePtr := llvm.PointerType(p.getLLVMRuntimeType("typeInInterface"), 0)
|
||||
var typesInInterfaces []llvm.Value
|
||||
for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||
switch global.Type() {
|
||||
|
|
|
@ -28,14 +28,14 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.
|
|||
itfMethodSetGlobal := c.getTypeMethodSet(typ)
|
||||
itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
||||
if itfConcreteTypeGlobal.IsNil() {
|
||||
typeInInterface := c.mod.GetTypeByName("runtime.typeInInterface")
|
||||
typeInInterface := c.getLLVMRuntimeType("typeInInterface")
|
||||
itfConcreteTypeGlobal = llvm.AddGlobal(c.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name())
|
||||
itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}))
|
||||
itfConcreteTypeGlobal.SetGlobalConstant(true)
|
||||
itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage)
|
||||
}
|
||||
itfTypeCode := c.builder.CreatePtrToInt(itfConcreteTypeGlobal, c.uintptrType, "")
|
||||
itf := llvm.Undef(c.mod.GetTypeByName("runtime._interface"))
|
||||
itf := llvm.Undef(c.getLLVMRuntimeType("_interface"))
|
||||
itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "")
|
||||
itf = c.builder.CreateInsertValue(itf, itfValue, 1, "")
|
||||
return itf
|
||||
|
@ -48,7 +48,7 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
|
|||
globalName := "type:" + getTypeCodeName(typ)
|
||||
global := c.mod.NamedGlobal(globalName)
|
||||
if global.IsNil() {
|
||||
global = llvm.AddGlobal(c.mod, c.mod.GetTypeByName("runtime.typecodeID"), globalName)
|
||||
global = llvm.AddGlobal(c.mod, c.getLLVMRuntimeType("typecodeID"), globalName)
|
||||
global.SetGlobalConstant(true)
|
||||
}
|
||||
return global
|
||||
|
@ -163,11 +163,11 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
|||
ms := c.ir.Program.MethodSets.MethodSet(typ)
|
||||
if ms.Len() == 0 {
|
||||
// no methods, so can leave that one out
|
||||
return llvm.ConstPointerNull(llvm.PointerType(c.mod.GetTypeByName("runtime.interfaceMethodInfo"), 0))
|
||||
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
|
||||
}
|
||||
|
||||
methods := make([]llvm.Value, ms.Len())
|
||||
interfaceMethodInfoType := c.mod.GetTypeByName("runtime.interfaceMethodInfo")
|
||||
interfaceMethodInfoType := c.getLLVMRuntimeType("interfaceMethodInfo")
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
method := ms.At(i)
|
||||
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
||||
|
|
16
ir/ir.go
16
ir/ir.go
|
@ -26,7 +26,6 @@ type Program struct {
|
|||
Globals []*Global
|
||||
globalMap map[*ssa.Global]*Global
|
||||
comments map[string]*ast.CommentGroup
|
||||
NamedTypes []*NamedType
|
||||
}
|
||||
|
||||
// Function or method.
|
||||
|
@ -50,19 +49,6 @@ type Global struct {
|
|||
extern bool // go:extern
|
||||
}
|
||||
|
||||
// Type with a name and possibly methods.
|
||||
type NamedType struct {
|
||||
*ssa.Type
|
||||
LLVMType llvm.Type
|
||||
}
|
||||
|
||||
// Type that is at some point put in an interface.
|
||||
type TypeWithMethods struct {
|
||||
t types.Type
|
||||
Num int
|
||||
Methods map[string]*types.Selection
|
||||
}
|
||||
|
||||
// Interface type that is at some point used in a type assert (to check whether
|
||||
// it implements another interface).
|
||||
type Interface struct {
|
||||
|
@ -210,8 +196,6 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
|||
case *ssa.Function:
|
||||
p.addFunction(member)
|
||||
case *ssa.Type:
|
||||
t := &NamedType{Type: member}
|
||||
p.NamedTypes = append(p.NamedTypes, t)
|
||||
methods := getAllMethods(pkg.Prog, member.Type())
|
||||
if !types.IsInterface(member.Type()) {
|
||||
// named type
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче