Skip to content

Commit 7939c06

Browse files
committed
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 #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.
1 parent dd04f34 commit 7939c06

File tree

8 files changed

+822
-42
lines changed

8 files changed

+822
-42
lines changed

compiler/calls.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"go/types"
55
"strconv"
66

7-
"golang.org/x/tools/go/ssa"
87
"tinygo.org/x/go-llvm"
98
)
109

@@ -35,11 +34,9 @@ const (
3534

3635
// createCall creates a new call to runtime.<fnName> with the given arguments.
3736
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
38-
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
39-
llvmFn := b.getFunction(fn)
40-
if llvmFn.IsNil() {
41-
panic("trying to call non-existent function: " + fn.RelString(nil))
42-
}
37+
llvmFn := b.getFunctionRaw(b.getRuntimeFuncType(fnName), functionInfo{
38+
linkName: "runtime." + fnName,
39+
})
4340
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
4441
args = append(args, llvm.ConstPointerNull(b.i8ptrType)) // coroutine handle
4542
return b.createCall(llvmFn, args, name)

compiler/compiler.go

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

3+
//go:generate go run ./mkruntimetypes.go
4+
35
import (
46
"debug/dwarf"
57
"errors"
@@ -49,11 +51,14 @@ type compilerContext struct {
4951
targetData llvm.TargetData
5052
intType llvm.Type
5153
i8ptrType llvm.Type // for convenience
54+
runtimeTypes map[string]types.Type
5255
funcPtrAddrSpace int
5356
uintptrType llvm.Type
5457
program *ssa.Program
5558
diagnostics []error
5659
astComments map[string]*ast.CommentGroup
60+
runtimePkg *types.Package // package runtime
61+
taskPkg *types.Package // package internal/task
5762
}
5863

5964
// builder contains all information relevant to build a single function.
@@ -101,11 +106,12 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
101106
// configuration, ready to compile Go SSA to LLVM IR.
102107
func newCompilerContext(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) *compilerContext {
103108
c := &compilerContext{
104-
Config: config,
105-
difiles: make(map[string]llvm.Metadata),
106-
ditypes: make(map[types.Type]llvm.Metadata),
107-
machine: machine,
108-
targetData: machine.CreateTargetData(),
109+
Config: config,
110+
difiles: make(map[string]llvm.Metadata),
111+
ditypes: make(map[types.Type]llvm.Metadata),
112+
machine: machine,
113+
targetData: machine.CreateTargetData(),
114+
runtimeTypes: make(map[string]types.Type),
109115
}
110116

111117
c.ctx = llvm.NewContext()
@@ -246,6 +252,8 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
246252

247253
c.program = lprogram.LoadSSA()
248254
c.program.Build()
255+
c.runtimePkg = c.program.ImportedPackage("runtime").Pkg
256+
c.taskPkg = c.program.ImportedPackage("internal/task").Pkg
249257

250258
// Initialize debug information.
251259
if c.Debug() {
@@ -260,20 +268,6 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
260268

261269
c.loadASTComments(lprogram)
262270

263-
// Declare runtime types.
264-
// TODO: lazily create runtime types in getLLVMRuntimeType when they are
265-
// needed. Eventually this will be required anyway, when packages are
266-
// compiled independently (and the runtime types are not available).
267-
for _, member := range c.program.ImportedPackage("runtime").Members {
268-
if member, ok := member.(*ssa.Type); ok {
269-
if typ, ok := member.Type().(*types.Named); ok {
270-
if _, ok := typ.Underlying().(*types.Struct); ok {
271-
c.getLLVMType(typ)
272-
}
273-
}
274-
}
275-
}
276-
277271
// Predeclare the runtime.alloc function, which is used by the wordpack
278272
// functionality.
279273
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
@@ -453,16 +447,14 @@ func sortPackages(program *ssa.Program, mainPath string) []*ssa.Package {
453447
}
454448

455449
// getLLVMRuntimeType obtains a named type from the runtime package and returns
456-
// it as a LLVM type, creating it if necessary. It is a shorthand for
457-
// getLLVMType(getRuntimeType(name)).
450+
// it as a LLVM type, creating it if necessary.
458451
func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
459452
fullName := "runtime." + name
460-
typ := c.mod.GetTypeByName(fullName)
461-
if typ.IsNil() {
462-
println(c.mod.String())
463-
panic("could not find runtime type: " + fullName)
453+
llvmType := c.mod.GetTypeByName(fullName)
454+
if llvmType.IsNil() {
455+
llvmType = c.getLLVMType(c.getRuntimeType(name))
464456
}
465-
return typ
457+
return llvmType
466458
}
467459

468460
// 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 {
522514
llvmType = c.ctx.StructCreateNamed(llvmName)
523515
underlying := c.getLLVMType(st)
524516
llvmType.StructSetBody(underlying.StructElementTypes(), false)
517+
} else if typ.String() == "internal/task.Task" && llvmType.StructElementTypesCount() == 0 {
518+
// Note: this struct is an opaque struct. Give it a body.
519+
underlying := c.getLLVMType(st)
520+
llvmType.StructSetBody(underlying.StructElementTypes(), false)
525521
}
526522
return llvmType
527523
}
@@ -542,6 +538,23 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
542538
case *types.Struct:
543539
members := make([]llvm.Type, typ.NumFields())
544540
for i := 0; i < typ.NumFields(); i++ {
541+
if ptr, ok := typ.Field(i).Type().(*types.Pointer); ok {
542+
if named, ok := ptr.Elem().(*types.Named); ok && named.String() == "internal/task.Task" {
543+
// Special workaround for internal/task.Task. It is
544+
// referenced from the runtime.channel type, which
545+
// references runtime.channelBlockedList, which references
546+
// internal/task.Task. To avoid having to define
547+
// internal/task.Task as a compiler-internal type, make the
548+
// type opaque.
549+
ptrTo := c.mod.GetTypeByName(named.String())
550+
if ptrTo.IsNil() {
551+
ptrTo = c.ctx.StructCreateNamed(named.String())
552+
}
553+
members[i] = llvm.PointerType(ptrTo, 0)
554+
continue
555+
}
556+
}
557+
545558
members[i] = c.getLLVMType(typ.Field(i).Type())
546559
}
547560
return c.ctx.StructType(members, false)
@@ -645,11 +658,11 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
645658
Encoding: encoding,
646659
})
647660
case *types.Chan:
648-
return c.getDIType(types.NewPointer(c.program.ImportedPackage("runtime").Members["channel"].(*ssa.Type).Type()))
661+
return c.getDIType(types.NewPointer(c.getRuntimeType("channel")))
649662
case *types.Interface:
650-
return c.getDIType(c.program.ImportedPackage("runtime").Members["_interface"].(*ssa.Type).Type())
663+
return c.getDIType(c.getRuntimeType("_interface"))
651664
case *types.Map:
652-
return c.getDIType(types.NewPointer(c.program.ImportedPackage("runtime").Members["hashmap"].(*ssa.Type).Type()))
665+
return c.getDIType(types.NewPointer(c.getRuntimeType("hashmap")))
653666
case *types.Named:
654667
return c.dibuilder.CreateTypedef(llvm.DITypedef{
655668
Type: c.getDIType(typ.Underlying()),

compiler/compiler_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ func runCompilerTest(t *testing.T, name string) {
8181
t.Fatal(err)
8282
}
8383
c := newCompilerContext("main", machine, &config)
84+
c.runtimePkg = types.NewPackage("runtime", "runtime")
85+
c.taskPkg = types.NewPackage("internal/task", "task")
8486
irbuilder := c.ctx.NewBuilder()
8587
defer irbuilder.Dispose()
8688

0 commit comments

Comments
 (0)