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.
Этот коммит содержится в:
родитель
0746d61639
коммит
83ad0b6137
7 изменённых файлов: 70 добавлений и 49 удалений
21
compiler.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
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
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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче