mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 15:33:40 +00:00
compiler: include type information about the runtime in the compiler
These types are often known to the compiler already. Moving them into
the compiler is an important step for two related reasons:
* It makes the compiler better testable. Together with
https://github.com/tinygo-org/tinygo/pull/1008 it will make it
possible to test the compiler without having to load the runtime
package.
* It makes it easier to compile packages independently as the type
information of the runtime package doesn't need to be present.
I had to use hack to get this to work well: internal/task.Task is now an
opaque struct. This is necessary because there is a dependency from
*runtime.channel -> *runtime.channelBlockedList -> *internal/task.Task.
I don't want to include the definition of the internal/task.Task struct
in the compiler directly as that would make changing the internal/task
package a lot harder and the compiler doesn't need to know the layout of
that struct anyway.
This commit is contained in:
+42
-29
@@ -1,5 +1,7 @@
|
||||
package compiler
|
||||
|
||||
//go:generate go run ./mkruntimetypes.go
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"errors"
|
||||
@@ -49,11 +51,14 @@ type compilerContext struct {
|
||||
targetData llvm.TargetData
|
||||
intType llvm.Type
|
||||
i8ptrType llvm.Type // for convenience
|
||||
runtimeTypes map[string]types.Type
|
||||
funcPtrAddrSpace int
|
||||
uintptrType llvm.Type
|
||||
program *ssa.Program
|
||||
diagnostics []error
|
||||
astComments map[string]*ast.CommentGroup
|
||||
runtimePkg *types.Package // package runtime
|
||||
taskPkg *types.Package // package internal/task
|
||||
}
|
||||
|
||||
// builder contains all information relevant to build a single function.
|
||||
@@ -101,11 +106,12 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
||||
// configuration, ready to compile Go SSA to LLVM IR.
|
||||
func newCompilerContext(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) *compilerContext {
|
||||
c := &compilerContext{
|
||||
Config: config,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
machine: machine,
|
||||
targetData: machine.CreateTargetData(),
|
||||
Config: config,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
machine: machine,
|
||||
targetData: machine.CreateTargetData(),
|
||||
runtimeTypes: make(map[string]types.Type),
|
||||
}
|
||||
|
||||
c.ctx = llvm.NewContext()
|
||||
@@ -246,6 +252,8 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||
|
||||
c.program = lprogram.LoadSSA()
|
||||
c.program.Build()
|
||||
c.runtimePkg = c.program.ImportedPackage("runtime").Pkg
|
||||
c.taskPkg = c.program.ImportedPackage("internal/task").Pkg
|
||||
|
||||
// Initialize debug information.
|
||||
if c.Debug() {
|
||||
@@ -260,20 +268,6 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||
|
||||
c.loadASTComments(lprogram)
|
||||
|
||||
// Declare runtime types.
|
||||
// TODO: lazily create runtime types in getLLVMRuntimeType when they are
|
||||
// needed. Eventually this will be required anyway, when packages are
|
||||
// compiled independently (and the runtime types are not available).
|
||||
for _, member := range c.program.ImportedPackage("runtime").Members {
|
||||
if member, ok := member.(*ssa.Type); ok {
|
||||
if typ, ok := member.Type().(*types.Named); ok {
|
||||
if _, ok := typ.Underlying().(*types.Struct); ok {
|
||||
c.getLLVMType(typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Predeclare the runtime.alloc function, which is used by the wordpack
|
||||
// functionality.
|
||||
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
||||
@@ -453,16 +447,14 @@ func sortPackages(program *ssa.Program, mainPath string) []*ssa.Package {
|
||||
}
|
||||
|
||||
// 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)).
|
||||
// it as a LLVM type, creating it if necessary.
|
||||
func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
||||
fullName := "runtime." + name
|
||||
typ := c.mod.GetTypeByName(fullName)
|
||||
if typ.IsNil() {
|
||||
println(c.mod.String())
|
||||
panic("could not find runtime type: " + fullName)
|
||||
llvmType := c.mod.GetTypeByName(fullName)
|
||||
if llvmType.IsNil() {
|
||||
llvmType = c.getLLVMType(c.getRuntimeType(name))
|
||||
}
|
||||
return typ
|
||||
return llvmType
|
||||
}
|
||||
|
||||
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
|
||||
@@ -522,6 +514,10 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||
llvmType = c.ctx.StructCreateNamed(llvmName)
|
||||
underlying := c.getLLVMType(st)
|
||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||
} else if typ.String() == "internal/task.Task" && llvmType.StructElementTypesCount() == 0 {
|
||||
// Note: this struct is an opaque struct. Give it a body.
|
||||
underlying := c.getLLVMType(st)
|
||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||
}
|
||||
return llvmType
|
||||
}
|
||||
@@ -542,6 +538,23 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||
case *types.Struct:
|
||||
members := make([]llvm.Type, typ.NumFields())
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
if ptr, ok := typ.Field(i).Type().(*types.Pointer); ok {
|
||||
if named, ok := ptr.Elem().(*types.Named); ok && named.String() == "internal/task.Task" {
|
||||
// Special workaround for internal/task.Task. It is
|
||||
// referenced from the runtime.channel type, which
|
||||
// references runtime.channelBlockedList, which references
|
||||
// internal/task.Task. To avoid having to define
|
||||
// internal/task.Task as a compiler-internal type, make the
|
||||
// type opaque.
|
||||
ptrTo := c.mod.GetTypeByName(named.String())
|
||||
if ptrTo.IsNil() {
|
||||
ptrTo = c.ctx.StructCreateNamed(named.String())
|
||||
}
|
||||
members[i] = llvm.PointerType(ptrTo, 0)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
members[i] = c.getLLVMType(typ.Field(i).Type())
|
||||
}
|
||||
return c.ctx.StructType(members, false)
|
||||
@@ -645,11 +658,11 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
||||
Encoding: encoding,
|
||||
})
|
||||
case *types.Chan:
|
||||
return c.getDIType(types.NewPointer(c.program.ImportedPackage("runtime").Members["channel"].(*ssa.Type).Type()))
|
||||
return c.getDIType(types.NewPointer(c.getRuntimeType("channel")))
|
||||
case *types.Interface:
|
||||
return c.getDIType(c.program.ImportedPackage("runtime").Members["_interface"].(*ssa.Type).Type())
|
||||
return c.getDIType(c.getRuntimeType("_interface"))
|
||||
case *types.Map:
|
||||
return c.getDIType(types.NewPointer(c.program.ImportedPackage("runtime").Members["hashmap"].(*ssa.Type).Type()))
|
||||
return c.getDIType(types.NewPointer(c.getRuntimeType("hashmap")))
|
||||
case *types.Named:
|
||||
return c.dibuilder.CreateTypedef(llvm.DITypedef{
|
||||
Type: c.getDIType(typ.Underlying()),
|
||||
|
||||
Reference in New Issue
Block a user