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 | ||||
| 	// 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() { | ||||
|  |  | |||
							
								
								
									
										14
									
								
								ir.go
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								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 != "" { | ||||
|  |  | |||
							
								
								
									
										16
									
								
								main.go
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								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. | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -29,10 +29,6 @@ void RTC0_IRQHandler() { | |||
| 	rtc_wakeup = true; | ||||
| } | ||||
| 
 | ||||
| void _start() { | ||||
| 	main(); | ||||
| } | ||||
| 
 | ||||
| __attribute__((weak)) | ||||
| void __aeabi_unwind_cpp_pr0() { | ||||
| 	// dummy, not actually used
 | ||||
|  |  | |||
|  | @ -11,6 +11,11 @@ func _Cfunc_rtc_sleep(ticks uint32) | |||
| 
 | ||||
| const Microsecond = 1 | ||||
| 
 | ||||
| //go:export _start | ||||
| func _start() { | ||||
| 	main() | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	initUART() | ||||
| 	initLFCLK() | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem