Skip to content

Commit c9b1f0f

Browse files
committed
compiler: integrate ir package into compiler package
The ir package has long lost its original purpose (which was doing some analysis and optimization on the Go SSA directly). There is very little of it left, which is best integrated directly in the compiler package to avoid unnecessary abstraction.
1 parent 65caa6d commit c9b1f0f

File tree

9 files changed

+148
-184
lines changed

9 files changed

+148
-184
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ endif
111111
clean:
112112
@rm -rf build
113113

114-
FMT_PATHS = ./*.go builder cgo compiler compiler/testdata interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
114+
FMT_PATHS = ./*.go builder cgo compiler compiler/testdata interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
115115
fmt:
116116
@gofmt -l -w $(FMT_PATHS)
117117
fmt-check:

compiler/calls.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const (
3535

3636
// createCall creates a new call to runtime.<fnName> with the given arguments.
3737
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
38-
fn := b.ir.Program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
38+
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
3939
llvmFn := b.getFunction(fn)
4040
if llvmFn.IsNil() {
4141
panic("trying to call non-existent function: " + fn.RelString(nil))

compiler/compiler.go

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"github.com/tinygo-org/tinygo/compileopts"
1919
"github.com/tinygo-org/tinygo/compiler/llvmutil"
2020
"github.com/tinygo-org/tinygo/goenv"
21-
"github.com/tinygo-org/tinygo/ir"
2221
"github.com/tinygo-org/tinygo/loader"
2322
"golang.org/x/tools/go/ssa"
2423
"tinygo.org/x/go-llvm"
@@ -52,7 +51,7 @@ type compilerContext struct {
5251
i8ptrType llvm.Type // for convenience
5352
funcPtrAddrSpace int
5453
uintptrType llvm.Type
55-
ir *ir.Program
54+
program *ssa.Program
5655
diagnostics []error
5756
astComments map[string]*ast.CommentGroup
5857
}
@@ -245,7 +244,8 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
245244
return c.mod, nil, []error{err}
246245
}
247246

248-
c.ir = ir.NewProgram(lprogram, pkgName)
247+
c.program = lprogram.LoadSSA()
248+
c.program.Build()
249249

250250
// Initialize debug information.
251251
if c.Debug() {
@@ -264,7 +264,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
264264
// TODO: lazily create runtime types in getLLVMRuntimeType when they are
265265
// needed. Eventually this will be required anyway, when packages are
266266
// compiled independently (and the runtime types are not available).
267-
for _, member := range c.ir.Program.ImportedPackage("runtime").Members {
267+
for _, member := range c.program.ImportedPackage("runtime").Members {
268268
if member, ok := member.(*ssa.Type); ok {
269269
if typ, ok := member.Type().(*types.Named); ok {
270270
if _, ok := typ.Underlying().(*types.Struct); ok {
@@ -276,11 +276,13 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
276276

277277
// Predeclare the runtime.alloc function, which is used by the wordpack
278278
// functionality.
279-
c.getFunction(c.ir.Program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
279+
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
280+
281+
sortedPackages := sortPackages(c.program, pkgName)
280282

281283
// Find package initializers.
282284
var initFuncs []llvm.Value
283-
for _, pkg := range c.ir.Packages() {
285+
for _, pkg := range sortedPackages {
284286
for _, member := range pkg.Members {
285287
switch member := member.(type) {
286288
case *ssa.Function:
@@ -294,19 +296,19 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
294296
// Add definitions to declarations.
295297
irbuilder := c.ctx.NewBuilder()
296298
defer irbuilder.Dispose()
297-
for _, pkg := range c.ir.Packages() {
299+
for _, pkg := range sortedPackages {
298300
c.createPackage(pkg, irbuilder)
299301
}
300302

301303
// After all packages are imported, add a synthetic initializer function
302304
// that calls the initializer of each package.
303-
initFn := c.ir.Program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)
305+
initFn := c.program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)
304306
llvmInitFn := c.getFunction(initFn)
305307
llvmInitFn.SetLinkage(llvm.InternalLinkage)
306308
llvmInitFn.SetUnnamedAddr(true)
307309
if c.Debug() {
308310
difunc := c.attachDebugInfo(initFn)
309-
pos := c.ir.Program.Fset.Position(initFn.Pos())
311+
pos := c.program.Fset.Position(initFn.Pos())
310312
irbuilder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
311313
}
312314
block := c.ctx.AddBasicBlock(llvmInitFn, "entry")
@@ -318,7 +320,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
318320

319321
// Conserve for goroutine lowering. Without marking these as external, they
320322
// would be optimized away.
321-
realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
323+
realMain := c.mod.NamedFunction(pkgName + ".main")
322324
realMain.SetLinkage(llvm.ExternalLinkage) // keep alive until goroutine lowering
323325

324326
// Replace callMain placeholder with actual main function.
@@ -374,7 +376,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
374376

375377
// Gather the list of (C) file paths that should be included in the build.
376378
var extraFiles []string
377-
for _, pkg := range c.ir.LoaderProgram.Sorted() {
379+
for _, pkg := range lprogram.Sorted() {
378380
for _, file := range pkg.CFiles {
379381
extraFiles = append(extraFiles, filepath.Join(pkg.Package.Dir, file))
380382
}
@@ -383,6 +385,73 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
383385
return c.mod, extraFiles, c.diagnostics
384386
}
385387

388+
// sortPackages returns a list of all packages, sorted by import order.
389+
func sortPackages(program *ssa.Program, mainPath string) []*ssa.Package {
390+
// Find the main package, which is a bit difficult when running a .go file
391+
// directly.
392+
mainPkg := program.ImportedPackage(mainPath)
393+
if mainPkg == nil {
394+
for _, pkgInfo := range program.AllPackages() {
395+
if pkgInfo.Pkg.Name() == "main" {
396+
if mainPkg != nil {
397+
panic("more than one main package found")
398+
}
399+
mainPkg = pkgInfo
400+
}
401+
}
402+
}
403+
if mainPkg == nil {
404+
panic("could not find main package")
405+
}
406+
407+
packageList := []*ssa.Package{}
408+
packageSet := map[string]struct{}{}
409+
worklist := []string{"runtime", mainPath}
410+
for len(worklist) != 0 {
411+
pkgPath := worklist[0]
412+
var pkg *ssa.Package
413+
if pkgPath == mainPath {
414+
pkg = mainPkg // necessary for compiling individual .go files
415+
} else {
416+
pkg = program.ImportedPackage(pkgPath)
417+
}
418+
if pkg == nil {
419+
// Non-SSA package (e.g. cgo).
420+
packageSet[pkgPath] = struct{}{}
421+
worklist = worklist[1:]
422+
continue
423+
}
424+
if _, ok := packageSet[pkgPath]; ok {
425+
// Package already in the final package list.
426+
worklist = worklist[1:]
427+
continue
428+
}
429+
430+
unsatisfiedImports := make([]string, 0)
431+
imports := pkg.Pkg.Imports()
432+
for _, pkg := range imports {
433+
if _, ok := packageSet[pkg.Path()]; ok {
434+
continue
435+
}
436+
unsatisfiedImports = append(unsatisfiedImports, pkg.Path())
437+
}
438+
if len(unsatisfiedImports) == 0 {
439+
// All dependencies of this package are satisfied, so add this
440+
// package to the list.
441+
packageList = append(packageList, pkg)
442+
packageSet[pkgPath] = struct{}{}
443+
worklist = worklist[1:]
444+
} else {
445+
// Prepend all dependencies to the worklist and reconsider this
446+
// package (by not removing it from the worklist). At that point, it
447+
// must be possible to add it to packageList.
448+
worklist = append(unsatisfiedImports, worklist...)
449+
}
450+
}
451+
452+
return packageList
453+
}
454+
386455
// getLLVMRuntimeType obtains a named type from the runtime package and returns
387456
// it as a LLVM type, creating it if necessary. It is a shorthand for
388457
// getLLVMType(getRuntimeType(name)).
@@ -576,11 +645,11 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
576645
Encoding: encoding,
577646
})
578647
case *types.Chan:
579-
return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["channel"].(*ssa.Type).Type()))
648+
return c.getDIType(types.NewPointer(c.program.ImportedPackage("runtime").Members["channel"].(*ssa.Type).Type()))
580649
case *types.Interface:
581-
return c.getDIType(c.ir.Program.ImportedPackage("runtime").Members["_interface"].(*ssa.Type).Type())
650+
return c.getDIType(c.program.ImportedPackage("runtime").Members["_interface"].(*ssa.Type).Type())
582651
case *types.Map:
583-
return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["hashmap"].(*ssa.Type).Type()))
652+
return c.getDIType(types.NewPointer(c.program.ImportedPackage("runtime").Members["hashmap"].(*ssa.Type).Type()))
584653
case *types.Named:
585654
return c.dibuilder.CreateTypedef(llvm.DITypedef{
586655
Type: c.getDIType(typ.Underlying()),
@@ -688,7 +757,7 @@ func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
688757
return dilocal
689758
}
690759

691-
pos := b.ir.Program.Fset.Position(variable.Pos())
760+
pos := b.program.Fset.Position(variable.Pos())
692761

693762
// Check whether this is a function parameter.
694763
for i, param := range b.fn.Params {
@@ -722,7 +791,7 @@ func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
722791
// attachDebugInfo adds debug info to a function declaration. It returns the
723792
// DISubprogram metadata node.
724793
func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata {
725-
pos := c.ir.Program.Fset.Position(f.Syntax().Pos())
794+
pos := c.program.Fset.Position(f.Syntax().Pos())
726795
return c.attachDebugInfoRaw(f, c.getFunction(f), "", pos.Filename, pos.Line)
727796
}
728797

@@ -876,7 +945,7 @@ func (c *compilerContext) createFunction(irbuilder llvm.Builder, fn *ssa.Functio
876945
// Create debug info file if needed.
877946
b.difunc = b.attachDebugInfo(b.fn)
878947
}
879-
pos := b.ir.Program.Fset.Position(b.fn.Pos())
948+
pos := b.program.Fset.Position(b.fn.Pos())
880949
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
881950
}
882951

@@ -978,7 +1047,7 @@ func (c *compilerContext) createFunction(irbuilder llvm.Builder, fn *ssa.Functio
9781047
continue
9791048
}
9801049
dbgVar := b.getLocalVariable(variable)
981-
pos := b.ir.Program.Fset.Position(instr.Pos())
1050+
pos := b.program.Fset.Position(instr.Pos())
9821051
b.dibuilder.InsertValueAtEnd(b.getValue(instr.X), dbgVar, b.dibuilder.CreateExpression(nil), llvm.DebugLoc{
9831052
Line: uint(pos.Line),
9841053
Col: uint(pos.Column),
@@ -1032,7 +1101,7 @@ func (c *compilerContext) createFunction(irbuilder llvm.Builder, fn *ssa.Functio
10321101
// particular Go SSA instruction.
10331102
func (b *builder) createInstruction(instr ssa.Instruction) {
10341103
if b.Debug() {
1035-
pos := b.ir.Program.Fset.Position(instr.Pos())
1104+
pos := b.program.Fset.Position(instr.Pos())
10361105
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
10371106
}
10381107

compiler/errors.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
// makeError makes it easy to create an error from a token.Pos with a message.
1515
func (c *compilerContext) makeError(pos token.Pos, msg string) types.Error {
1616
return types.Error{
17-
Fset: c.ir.Program.Fset,
17+
Fset: c.program.Fset,
1818
Pos: pos,
1919
Msg: msg,
2020
}

compiler/goroutine.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
2929
default:
3030
panic("unreachable")
3131
}
32-
start := b.getFunction(b.ir.Program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
32+
start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
3333
b.createCall(start, []llvm.Value{callee, paramBundle, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
3434
return llvm.Undef(funcPtr.Type().ElementType().ReturnType())
3535
}
@@ -75,7 +75,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
7575
builder.SetInsertPointAtEnd(entry)
7676

7777
if c.Debug() {
78-
pos := c.ir.Program.Fset.Position(pos)
78+
pos := c.program.Fset.Position(pos)
7979
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
8080
File: c.getDIFile(pos.Filename),
8181
Parameters: nil, // do not show parameters in debugger
@@ -131,7 +131,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
131131
builder.SetInsertPointAtEnd(entry)
132132

133133
if c.Debug() {
134-
pos := c.ir.Program.Fset.Position(pos)
134+
pos := c.program.Fset.Position(pos)
135135
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
136136
File: c.getDIFile(pos.Filename),
137137
Parameters: nil, // do not show parameters in debugger

compiler/interface.go

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"strconv"
1212
"strings"
1313

14-
"github.com/tinygo-org/tinygo/ir"
1514
"golang.org/x/tools/go/ssa"
1615
"tinygo.org/x/go-llvm"
1716
)
@@ -236,7 +235,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
236235
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
237236
}
238237

239-
ms := c.ir.Program.MethodSets.MethodSet(typ)
238+
ms := c.program.MethodSets.MethodSet(typ)
240239
if ms.Len() == 0 {
241240
// no methods, so can leave that one out
242241
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
@@ -247,7 +246,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
247246
for i := 0; i < ms.Len(); i++ {
248247
method := ms.At(i)
249248
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
250-
fn := c.ir.Program.MethodValue(method)
249+
fn := c.program.MethodValue(method)
251250
llvmFn := c.getFunction(fn)
252251
if llvmFn.IsNil() {
253252
// compiler error, so panic
@@ -311,7 +310,7 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
311310
// external *i8 indicating the indicating the signature of this method. It is
312311
// used during the interface lowering pass.
313312
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
314-
signature := ir.MethodSignature(method)
313+
signature := methodSignature(method)
315314
signatureGlobal := c.mod.NamedGlobal("func " + signature)
316315
if signatureGlobal.IsNil() {
317316
signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature)
@@ -489,7 +488,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
489488

490489
// add debug info if needed
491490
if c.Debug() {
492-
pos := c.ir.Program.Fset.Position(fn.Pos())
491+
pos := c.program.Fset.Position(fn.Pos())
493492
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
494493
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
495494
}
@@ -522,3 +521,50 @@ func isAnonymous(typ types.Type) bool {
522521
}
523522
return false
524523
}
524+
525+
// methodSignature creates a readable version of a method signature (including
526+
// the function name, excluding the receiver name). This string is used
527+
// internally to match interfaces and to call the correct method on an
528+
// interface. Examples:
529+
//
530+
// String() string
531+
// Read([]byte) (int, error)
532+
func methodSignature(method *types.Func) string {
533+
return method.Name() + signature(method.Type().(*types.Signature))
534+
}
535+
536+
// Make a readable version of a function (pointer) signature.
537+
// Examples:
538+
//
539+
// () string
540+
// (string, int) (int, error)
541+
func signature(sig *types.Signature) string {
542+
s := ""
543+
if sig.Params().Len() == 0 {
544+
s += "()"
545+
} else {
546+
s += "("
547+
for i := 0; i < sig.Params().Len(); i++ {
548+
if i > 0 {
549+
s += ", "
550+
}
551+
s += sig.Params().At(i).Type().String()
552+
}
553+
s += ")"
554+
}
555+
if sig.Results().Len() == 0 {
556+
// keep as-is
557+
} else if sig.Results().Len() == 1 {
558+
s += " " + sig.Results().At(0).Type().String()
559+
} else {
560+
s += " ("
561+
for i := 0; i < sig.Results().Len(); i++ {
562+
if i > 0 {
563+
s += ", "
564+
}
565+
s += sig.Results().At(i).Type().String()
566+
}
567+
s += ")"
568+
}
569+
return s
570+
}

compiler/interrupt.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
3939

4040
// Create a new global of type runtime/interrupt.handle. Globals of this
4141
// type are lowered in the interrupt lowering pass.
42-
globalType := b.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
42+
globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
4343
globalLLVMType := b.getLLVMType(globalType)
4444
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
4545
if global := b.mod.NamedGlobal(globalName); !global.IsNil() {
@@ -56,7 +56,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
5656

5757
// Add debug info to the interrupt global.
5858
if b.Debug() {
59-
pos := b.ir.Program.Fset.Position(instr.Pos())
59+
pos := b.program.Fset.Position(instr.Pos())
6060
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
6161
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
6262
LinkageName: globalName,

0 commit comments

Comments
 (0)