From 83ad0b61377a0f63e1ac7bd1c79124fd2fb892f2 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 4 Sep 2018 20:39:22 +0200 Subject: [PATCH] all: move bootstrapping IR to Go runtime This has the benefit of not requiring a 'runtime' IR file, so that complete relocatable files can be built without requiring input IR. This makes the compiler a lot easier to use without the Makefile. Code size is not affected. --- compiler.go | 21 +++++++++------------ ir.go | 14 +++++++++++++- main.go | 16 +++++++++------- src/runtime/runtime.go | 33 +++++++++++++++++++++++++++++++++ src/runtime/runtime.ll | 26 +------------------------- src/runtime/runtime_nrf.c | 4 ---- src/runtime/runtime_nrf.go | 5 +++++ 7 files changed, 70 insertions(+), 49 deletions(-) diff --git a/compiler.go b/compiler.go index 4a3467c1..e52ca05e 100644 --- a/compiler.go +++ b/compiler.go @@ -348,10 +348,6 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { // After all packages are imported, add a synthetic initializer function // that calls the initializer of each package. initFn := c.mod.NamedFunction("runtime.initAll") - if initFn.IsNil() { - initType := llvm.FunctionType(llvm.VoidType(), nil, false) - initFn = llvm.AddFunction(c.mod, "runtime.initAll", initType) - } initFn.SetLinkage(llvm.InternalLinkage) block := c.ctx.AddBasicBlock(initFn, "entry") c.builder.SetInsertPointAtEnd(block) @@ -363,19 +359,18 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { // Adjust main function. realMain := c.mod.NamedFunction(c.ir.mainPkg.Pkg.Path() + ".main") if c.ir.NeedsScheduler() { - c.mod.NamedFunction("main.main$async").ReplaceAllUsesWith(realMain) + c.mod.NamedFunction("runtime.main_mainAsync").ReplaceAllUsesWith(realMain) } else { - c.mod.NamedFunction("main.main").ReplaceAllUsesWith(realMain) + c.mod.NamedFunction("runtime.main_main").ReplaceAllUsesWith(realMain) } - // Set functions referenced in runtime.ll to internal linkage, to improve - // optimization (hopefully). - c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.InternalLinkage) - // Only use a scheduler when necessary. if c.ir.NeedsScheduler() { // Enable the scheduler. - c.mod.NamedGlobal("has_scheduler").SetInitializer(llvm.ConstInt(llvm.Int1Type(), 1, false)) + hasScheduler := c.mod.NamedGlobal("runtime.hasScheduler") + hasScheduler.SetInitializer(llvm.ConstInt(llvm.Int1Type(), 1, false)) + hasScheduler.SetGlobalConstant(true) + hasScheduler.SetUnnamedAddr(true) } // Initialize runtime type information, for interfaces. @@ -1104,7 +1099,9 @@ func (c *Compiler) parseFunc(frame *Frame) error { if c.dumpSSA { fmt.Printf("\nfunc %s:\n", frame.fn.fn) } - frame.fn.llvmFn.SetLinkage(llvm.InternalLinkage) + if !frame.fn.IsExported() { + frame.fn.llvmFn.SetLinkage(llvm.InternalLinkage) + } // Pre-create all basic blocks in the function. for _, block := range frame.fn.fn.DomPreorder() { diff --git a/ir.go b/ir.go index 7a405247..cf6c0958 100644 --- a/ir.go +++ b/ir.go @@ -35,7 +35,8 @@ type Program struct { type Function struct { fn *ssa.Function llvmFn llvm.Value - linkName string // go:linkname pragma + linkName string // go:linkname or go:export pragma + exported bool // go:export nobounds bool // go:nobounds pragma blocking bool // calculated by AnalyseBlockingRecursive flag bool // used by dead code elimination @@ -178,11 +179,22 @@ func (f *Function) parsePragmas() { if hasUnsafeImport(f.fn.Pkg.Pkg) { f.nobounds = true } + case "//go:export": + if len(parts) != 2 { + continue + } + f.linkName = parts[1] + f.exported = true } } } } +// Return true iff this function is externally visible. +func (f *Function) IsExported() bool { + return f.exported +} + // Return the link name for this function. func (f *Function) LinkName() string { if f.linkName != "" { diff --git a/main.go b/main.go index b98f567c..5bccfe56 100644 --- a/main.go +++ b/main.go @@ -30,13 +30,15 @@ func Compile(pkgName, runtimePath, outpath, target string, printIR, dumpSSA bool } // Add C/LLVM runtime. - runtime, err := llvm.ParseBitcodeFile(runtimePath) - if err != nil { - return err - } - err = c.LinkModule(runtime) - if err != nil { - return err + if runtimePath != "" { + runtime, err := llvm.ParseBitcodeFile(runtimePath) + if err != nil { + return err + } + err = c.LinkModule(runtime) + if err != nil { + return err + } } // Compile Go code to IR. diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index 94b1f270..41af1b42 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -6,6 +6,39 @@ import ( const Compiler = "tgo" +// The compiler will fill this with calls to the initialization function of each +// package. +func initAll() + +// These signatures are used to call the correct main function: with scheduling +// or without scheduling. +func main_main() +func main_mainAsync(parent *coroutine) *coroutine + +// The compiler will change this to true if there are 'go' statements in the +// compiled program and turn it into a const. +var hasScheduler bool + +// Entry point for Go. Initialize all packages and call main.main(). +//go:export main +func main() int { + // Run initializers of all packages. + initAll() + + // This branch must be optimized away. Only one of the targets must remain, + // or there will be link errors. + if hasScheduler { + // Initialize main and run the scheduler. + coro := main_mainAsync(nil) + scheduler(coro) + return 0 + } else { + // No scheduler is necessary. Call main directly. + main_main() + return 0 + } +} + func Sleep(d Duration) { // This function is treated specially by the compiler: when goroutines are // used, it is transformed into a llvm.coro.suspend() call. diff --git a/src/runtime/runtime.ll b/src/runtime/runtime.ll index 67a62b49..2770c174 100644 --- a/src/runtime/runtime.ll +++ b/src/runtime/runtime.ll @@ -1,27 +1,3 @@ source_filename = "runtime/runtime.ll" -declare void @runtime.initAll() -declare void @main.main() -declare i8* @main.main$async(i8*) -declare void @runtime.scheduler(i8*) - -; Will be changed to true if there are 'go' statements in the compiled program. -@has_scheduler = private unnamed_addr constant i1 false - -define i32 @main() { - call void @runtime.initAll() - %has_scheduler = load i1, i1* @has_scheduler - ; This branch will be optimized away. Only one of the targets will remain. - br i1 %has_scheduler, label %with_scheduler, label %without_scheduler - -with_scheduler: - ; Initialize main and run the scheduler. - %main = call i8* @main.main$async(i8* null) - call void @runtime.scheduler(i8* %main) - ret i32 0 - -without_scheduler: - ; No scheduler is necessary. Call main directly. - call void @main.main() - ret i32 0 -} +; dummy file diff --git a/src/runtime/runtime_nrf.c b/src/runtime/runtime_nrf.c index 2927ac6a..14e098a9 100644 --- a/src/runtime/runtime_nrf.c +++ b/src/runtime/runtime_nrf.c @@ -29,10 +29,6 @@ void RTC0_IRQHandler() { rtc_wakeup = true; } -void _start() { - main(); -} - __attribute__((weak)) void __aeabi_unwind_cpp_pr0() { // dummy, not actually used diff --git a/src/runtime/runtime_nrf.go b/src/runtime/runtime_nrf.go index 93af1b1f..8afd00b7 100644 --- a/src/runtime/runtime_nrf.go +++ b/src/runtime/runtime_nrf.go @@ -11,6 +11,11 @@ func _Cfunc_rtc_sleep(ticks uint32) const Microsecond = 1 +//go:export _start +func _start() { + main() +} + func init() { initUART() initLFCLK()