Browse Source

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.
pull/6/head
Ayke van Laethem 6 years ago
parent
commit
83ad0b6137
No known key found for this signature in database GPG Key ID: E97FF5335DFDFDED
  1. 21
      compiler.go
  2. 14
      ir.go
  3. 16
      main.go
  4. 33
      src/runtime/runtime.go
  5. 26
      src/runtime/runtime.ll
  6. 4
      src/runtime/runtime_nrf.c
  7. 5
      src/runtime/runtime_nrf.go

21
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 // After all packages are imported, add a synthetic initializer function
// that calls the initializer of each package. // that calls the initializer of each package.
initFn := c.mod.NamedFunction("runtime.initAll") 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) initFn.SetLinkage(llvm.InternalLinkage)
block := c.ctx.AddBasicBlock(initFn, "entry") block := c.ctx.AddBasicBlock(initFn, "entry")
c.builder.SetInsertPointAtEnd(block) c.builder.SetInsertPointAtEnd(block)
@ -363,19 +359,18 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
// Adjust main function. // Adjust main function.
realMain := c.mod.NamedFunction(c.ir.mainPkg.Pkg.Path() + ".main") realMain := c.mod.NamedFunction(c.ir.mainPkg.Pkg.Path() + ".main")
if c.ir.NeedsScheduler() { if c.ir.NeedsScheduler() {
c.mod.NamedFunction("main.main$async").ReplaceAllUsesWith(realMain) c.mod.NamedFunction("runtime.main_mainAsync").ReplaceAllUsesWith(realMain)
} else { } 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. // Only use a scheduler when necessary.
if c.ir.NeedsScheduler() { if c.ir.NeedsScheduler() {
// Enable the scheduler. // 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. // Initialize runtime type information, for interfaces.
@ -1104,7 +1099,9 @@ func (c *Compiler) parseFunc(frame *Frame) error {
if c.dumpSSA { if c.dumpSSA {
fmt.Printf("\nfunc %s:\n", frame.fn.fn) 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. // Pre-create all basic blocks in the function.
for _, block := range frame.fn.fn.DomPreorder() { for _, block := range frame.fn.fn.DomPreorder() {

14
ir.go

@ -35,7 +35,8 @@ type Program struct {
type Function struct { type Function struct {
fn *ssa.Function fn *ssa.Function
llvmFn llvm.Value 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 nobounds bool // go:nobounds pragma
blocking bool // calculated by AnalyseBlockingRecursive blocking bool // calculated by AnalyseBlockingRecursive
flag bool // used by dead code elimination flag bool // used by dead code elimination
@ -178,11 +179,22 @@ func (f *Function) parsePragmas() {
if hasUnsafeImport(f.fn.Pkg.Pkg) { if hasUnsafeImport(f.fn.Pkg.Pkg) {
f.nobounds = true 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. // Return the link name for this function.
func (f *Function) LinkName() string { func (f *Function) LinkName() string {
if f.linkName != "" { if f.linkName != "" {

16
main.go

@ -30,13 +30,15 @@ func Compile(pkgName, runtimePath, outpath, target string, printIR, dumpSSA bool
} }
// Add C/LLVM runtime. // Add C/LLVM runtime.
runtime, err := llvm.ParseBitcodeFile(runtimePath) if runtimePath != "" {
if err != nil { runtime, err := llvm.ParseBitcodeFile(runtimePath)
return err if err != nil {
} return err
err = c.LinkModule(runtime) }
if err != nil { err = c.LinkModule(runtime)
return err if err != nil {
return err
}
} }
// Compile Go code to IR. // Compile Go code to IR.

33
src/runtime/runtime.go

@ -6,6 +6,39 @@ import (
const Compiler = "tgo" 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) { func Sleep(d Duration) {
// This function is treated specially by the compiler: when goroutines are // This function is treated specially by the compiler: when goroutines are
// used, it is transformed into a llvm.coro.suspend() call. // used, it is transformed into a llvm.coro.suspend() call.

26
src/runtime/runtime.ll

@ -1,27 +1,3 @@
source_filename = "runtime/runtime.ll" source_filename = "runtime/runtime.ll"
declare void @runtime.initAll() ; dummy file
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
}

4
src/runtime/runtime_nrf.c

@ -29,10 +29,6 @@ void RTC0_IRQHandler() {
rtc_wakeup = true; rtc_wakeup = true;
} }
void _start() {
main();
}
__attribute__((weak)) __attribute__((weak))
void __aeabi_unwind_cpp_pr0() { void __aeabi_unwind_cpp_pr0() {
// dummy, not actually used // dummy, not actually used

5
src/runtime/runtime_nrf.go

@ -11,6 +11,11 @@ func _Cfunc_rtc_sleep(ticks uint32)
const Microsecond = 1 const Microsecond = 1
//go:export _start
func _start() {
main()
}
func init() { func init() {
initUART() initUART()
initLFCLK() initLFCLK()

Loading…
Cancel
Save