compiler: move Optimizer function to transform package
This refactor is a prerequisite to much larger refactors in the compiler.
Этот коммит содержится в:
		
							родитель
							
								
									78bd7e094f
								
							
						
					
					
						коммит
						9d3de55229
					
				
					 5 изменённых файлов: 252 добавлений и 239 удалений
				
			
		|  | @ -72,16 +72,16 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri | ||||||
| 	// exactly. | 	// exactly. | ||||||
| 	errs = nil | 	errs = nil | ||||||
| 	switch config.Options.Opt { | 	switch config.Options.Opt { | ||||||
| 	case "none:", "0": | 	case "none", "0": | ||||||
| 		errs = c.Optimize(0, 0, 0) // -O0 | 		errs = transform.Optimize(c.Module(), config, 0, 0, 0) // -O0 | ||||||
| 	case "1": | 	case "1": | ||||||
| 		errs = c.Optimize(1, 0, 0) // -O1 | 		errs = transform.Optimize(c.Module(), config, 1, 0, 0) // -O1 | ||||||
| 	case "2": | 	case "2": | ||||||
| 		errs = c.Optimize(2, 0, 225) // -O2 | 		errs = transform.Optimize(c.Module(), config, 2, 0, 225) // -O2 | ||||||
| 	case "s": | 	case "s": | ||||||
| 		errs = c.Optimize(2, 1, 225) // -Os | 		errs = transform.Optimize(c.Module(), config, 2, 1, 225) // -Os | ||||||
| 	case "z": | 	case "z": | ||||||
| 		errs = c.Optimize(2, 2, 5) // -Oz, default | 		errs = transform.Optimize(c.Module(), config, 2, 2, 5) // -Oz, default | ||||||
| 	default: | 	default: | ||||||
| 		errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)} | 		errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -34,30 +34,6 @@ func init() { | ||||||
| // The TinyGo import path. | // The TinyGo import path. | ||||||
| const tinygoPath = "github.com/tinygo-org/tinygo" | const tinygoPath = "github.com/tinygo-org/tinygo" | ||||||
| 
 | 
 | ||||||
| // functionsUsedInTransform is a list of function symbols that may be used |  | ||||||
| // during TinyGo optimization passes so they have to be marked as external |  | ||||||
| // linkage until all TinyGo passes have finished. |  | ||||||
| var functionsUsedInTransforms = []string{ |  | ||||||
| 	"runtime.alloc", |  | ||||||
| 	"runtime.free", |  | ||||||
| 	"runtime.nilPanic", |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var taskFunctionsUsedInTransforms = []string{} |  | ||||||
| 
 |  | ||||||
| var coroFunctionsUsedInTransforms = []string{ |  | ||||||
| 	"internal/task.start", |  | ||||||
| 	"internal/task.Pause", |  | ||||||
| 	"internal/task.fake", |  | ||||||
| 	"internal/task.Current", |  | ||||||
| 	"internal/task.createTask", |  | ||||||
| 	"(*internal/task.Task).setState", |  | ||||||
| 	"(*internal/task.Task).returnTo", |  | ||||||
| 	"(*internal/task.Task).returnCurrent", |  | ||||||
| 	"(*internal/task.Task).setReturnPtr", |  | ||||||
| 	"(*internal/task.Task).getReturnPtr", |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Compiler struct { | type Compiler struct { | ||||||
| 	*compileopts.Config | 	*compileopts.Config | ||||||
| 	mod                     llvm.Module | 	mod                     llvm.Module | ||||||
|  | @ -156,21 +132,6 @@ func (c *Compiler) Module() llvm.Module { | ||||||
| 	return c.mod | 	return c.mod | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // getFunctionsUsedInTransforms gets a list of all special functions that should be preserved during transforms and optimization. |  | ||||||
| func (c *Compiler) getFunctionsUsedInTransforms() []string { |  | ||||||
| 	fnused := functionsUsedInTransforms |  | ||||||
| 	switch c.Scheduler() { |  | ||||||
| 	case "none": |  | ||||||
| 	case "coroutines": |  | ||||||
| 		fnused = append(append([]string{}, fnused...), coroFunctionsUsedInTransforms...) |  | ||||||
| 	case "tasks": |  | ||||||
| 		fnused = append(append([]string{}, fnused...), taskFunctionsUsedInTransforms...) |  | ||||||
| 	default: |  | ||||||
| 		panic(fmt.Errorf("invalid scheduler %q", c.Scheduler())) |  | ||||||
| 	} |  | ||||||
| 	return fnused |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Compile the given package path or .go file path. Return an error when this | // Compile the given package path or .go file path. Return an error when this | ||||||
| // fails (in any stage). | // fails (in any stage). | ||||||
| func (c *Compiler) Compile(mainPath string) []error { | func (c *Compiler) Compile(mainPath string) []error { | ||||||
|  | @ -338,14 +299,8 @@ func (c *Compiler) Compile(mainPath string) []error { | ||||||
| 	realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main") | 	realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main") | ||||||
| 	realMain.SetLinkage(llvm.ExternalLinkage) // keep alive until goroutine lowering | 	realMain.SetLinkage(llvm.ExternalLinkage) // keep alive until goroutine lowering | ||||||
| 
 | 
 | ||||||
| 	// Make sure these functions are kept in tact during TinyGo transformation passes. | 	// Replace callMain placeholder with actual main function. | ||||||
| 	for _, name := range c.getFunctionsUsedInTransforms() { | 	c.mod.NamedFunction("runtime.callMain").ReplaceAllUsesWith(realMain) | ||||||
| 		fn := c.mod.NamedFunction(name) |  | ||||||
| 		if fn.IsNil() { |  | ||||||
| 			panic(fmt.Errorf("missing core function %q", name)) |  | ||||||
| 		} |  | ||||||
| 		fn.SetLinkage(llvm.ExternalLinkage) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	// Load some attributes | 	// Load some attributes | ||||||
| 	getAttr := func(attrName string) llvm.Attribute { | 	getAttr := func(attrName string) llvm.Attribute { | ||||||
|  |  | ||||||
|  | @ -1,185 +0,0 @@ | ||||||
| package compiler |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 
 |  | ||||||
| 	"github.com/tinygo-org/tinygo/compileopts" |  | ||||||
| 	"github.com/tinygo-org/tinygo/compiler/ircheck" |  | ||||||
| 	"github.com/tinygo-org/tinygo/transform" |  | ||||||
| 	"tinygo.org/x/go-llvm" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Run the LLVM optimizer over the module. |  | ||||||
| // The inliner can be disabled (if necessary) by passing 0 to the inlinerThreshold. |  | ||||||
| func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []error { |  | ||||||
| 	builder := llvm.NewPassManagerBuilder() |  | ||||||
| 	defer builder.Dispose() |  | ||||||
| 	builder.SetOptLevel(optLevel) |  | ||||||
| 	builder.SetSizeLevel(sizeLevel) |  | ||||||
| 	if inlinerThreshold != 0 { |  | ||||||
| 		builder.UseInlinerWithThreshold(inlinerThreshold) |  | ||||||
| 	} |  | ||||||
| 	builder.AddCoroutinePassesToExtensionPoints() |  | ||||||
| 
 |  | ||||||
| 	if c.PanicStrategy() == "trap" { |  | ||||||
| 		transform.ReplacePanicsWithTrap(c.mod) // -panic=trap |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// run a check of all of our code |  | ||||||
| 	if c.VerifyIR() { |  | ||||||
| 		errs := ircheck.Module(c.mod) |  | ||||||
| 		if errs != nil { |  | ||||||
| 			return errs |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Replace callMain placeholder with actual main function. |  | ||||||
| 	c.mod.NamedFunction("runtime.callMain").ReplaceAllUsesWith(c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")) |  | ||||||
| 
 |  | ||||||
| 	// Run function passes for each function. |  | ||||||
| 	funcPasses := llvm.NewFunctionPassManagerForModule(c.mod) |  | ||||||
| 	defer funcPasses.Dispose() |  | ||||||
| 	builder.PopulateFunc(funcPasses) |  | ||||||
| 	funcPasses.InitializeFunc() |  | ||||||
| 	for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { |  | ||||||
| 		funcPasses.RunFunc(fn) |  | ||||||
| 	} |  | ||||||
| 	funcPasses.FinalizeFunc() |  | ||||||
| 
 |  | ||||||
| 	if optLevel > 0 { |  | ||||||
| 		// Run some preparatory passes for the Go optimizer. |  | ||||||
| 		goPasses := llvm.NewPassManager() |  | ||||||
| 		defer goPasses.Dispose() |  | ||||||
| 		goPasses.AddGlobalDCEPass() |  | ||||||
| 		goPasses.AddGlobalOptimizerPass() |  | ||||||
| 		goPasses.AddConstantPropagationPass() |  | ||||||
| 		goPasses.AddAggressiveDCEPass() |  | ||||||
| 		goPasses.AddFunctionAttrsPass() |  | ||||||
| 		goPasses.Run(c.mod) |  | ||||||
| 
 |  | ||||||
| 		// Run Go-specific optimization passes. |  | ||||||
| 		transform.OptimizeMaps(c.mod) |  | ||||||
| 		transform.OptimizeStringToBytes(c.mod) |  | ||||||
| 		transform.OptimizeAllocs(c.mod) |  | ||||||
| 		transform.LowerInterfaces(c.mod) |  | ||||||
| 
 |  | ||||||
| 		errs := transform.LowerInterrupts(c.mod) |  | ||||||
| 		if len(errs) > 0 { |  | ||||||
| 			return errs |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if c.FuncImplementation() == compileopts.FuncValueSwitch { |  | ||||||
| 			transform.LowerFuncValues(c.mod) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// After interfaces are lowered, there are many more opportunities for |  | ||||||
| 		// interprocedural optimizations. To get them to work, function |  | ||||||
| 		// attributes have to be updated first. |  | ||||||
| 		goPasses.Run(c.mod) |  | ||||||
| 
 |  | ||||||
| 		// Run TinyGo-specific interprocedural optimizations. |  | ||||||
| 		transform.OptimizeAllocs(c.mod) |  | ||||||
| 		transform.OptimizeStringToBytes(c.mod) |  | ||||||
| 
 |  | ||||||
| 		// Lower runtime.isnil calls to regular nil comparisons. |  | ||||||
| 		isnil := c.mod.NamedFunction("runtime.isnil") |  | ||||||
| 		if !isnil.IsNil() { |  | ||||||
| 			for _, use := range getUses(isnil) { |  | ||||||
| 				c.builder.SetInsertPointBefore(use) |  | ||||||
| 				ptr := use.Operand(0) |  | ||||||
| 				if !ptr.IsABitCastInst().IsNil() { |  | ||||||
| 					ptr = ptr.Operand(0) |  | ||||||
| 				} |  | ||||||
| 				nilptr := llvm.ConstPointerNull(ptr.Type()) |  | ||||||
| 				icmp := c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "") |  | ||||||
| 				use.ReplaceAllUsesWith(icmp) |  | ||||||
| 				use.EraseFromParentAsInstruction() |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	} else { |  | ||||||
| 		// Must be run at any optimization level. |  | ||||||
| 		transform.LowerInterfaces(c.mod) |  | ||||||
| 		if c.FuncImplementation() == compileopts.FuncValueSwitch { |  | ||||||
| 			transform.LowerFuncValues(c.mod) |  | ||||||
| 		} |  | ||||||
| 		errs := transform.LowerInterrupts(c.mod) |  | ||||||
| 		if len(errs) > 0 { |  | ||||||
| 			return errs |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Lower async implementations. |  | ||||||
| 	switch c.Scheduler() { |  | ||||||
| 	case "coroutines": |  | ||||||
| 		// Lower async as coroutines. |  | ||||||
| 		err := transform.LowerCoroutines(c.mod, c.NeedsStackObjects()) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return []error{err} |  | ||||||
| 		} |  | ||||||
| 	case "tasks": |  | ||||||
| 		// No transformations necessary. |  | ||||||
| 	case "none": |  | ||||||
| 		// Check for any goroutine starts. |  | ||||||
| 		if start := c.mod.NamedFunction("internal/task.start"); !start.IsNil() && len(getUses(start)) > 0 { |  | ||||||
| 			errs := []error{} |  | ||||||
| 			for _, call := range getUses(start) { |  | ||||||
| 				errs = append(errs, errorAt(call, "attempted to start a goroutine without a scheduler")) |  | ||||||
| 			} |  | ||||||
| 			return errs |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		return []error{errors.New("invalid scheduler")} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if c.VerifyIR() { |  | ||||||
| 		if errs := ircheck.Module(c.mod); errs != nil { |  | ||||||
| 			return errs |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if err := c.Verify(); err != nil { |  | ||||||
| 		return []error{errors.New("optimizations caused a verification failure")} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if sizeLevel >= 2 { |  | ||||||
| 		// Set the "optsize" attribute to make slightly smaller binaries at the |  | ||||||
| 		// cost of some performance. |  | ||||||
| 		kind := llvm.AttributeKindID("optsize") |  | ||||||
| 		attr := c.ctx.CreateEnumAttribute(kind, 0) |  | ||||||
| 		for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { |  | ||||||
| 			fn.AddFunctionAttr(attr) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// After TinyGo-specific transforms have finished, undo exporting these functions. |  | ||||||
| 	for _, name := range c.getFunctionsUsedInTransforms() { |  | ||||||
| 		fn := c.mod.NamedFunction(name) |  | ||||||
| 		if fn.IsNil() { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		fn.SetLinkage(llvm.InternalLinkage) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Run function passes again, because without it, llvm.coro.size.i32() |  | ||||||
| 	// doesn't get lowered. |  | ||||||
| 	for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { |  | ||||||
| 		funcPasses.RunFunc(fn) |  | ||||||
| 	} |  | ||||||
| 	funcPasses.FinalizeFunc() |  | ||||||
| 
 |  | ||||||
| 	// Run module passes. |  | ||||||
| 	modPasses := llvm.NewPassManager() |  | ||||||
| 	defer modPasses.Dispose() |  | ||||||
| 	builder.Populate(modPasses) |  | ||||||
| 	modPasses.Run(c.mod) |  | ||||||
| 
 |  | ||||||
| 	hasGCPass := transform.AddGlobalsBitmap(c.mod) |  | ||||||
| 	hasGCPass = transform.MakeGCStackSlots(c.mod) || hasGCPass |  | ||||||
| 	if hasGCPass { |  | ||||||
| 		if err := c.Verify(); err != nil { |  | ||||||
| 			return []error{errors.New("GC pass caused a verification failure")} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  | @ -5,8 +5,9 @@ package transform | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"github.com/tinygo-org/tinygo/compiler/llvmutil" |  | ||||||
| 	"strconv" | 	"strconv" | ||||||
|  | 
 | ||||||
|  | 	"github.com/tinygo-org/tinygo/compiler/llvmutil" | ||||||
| 	"tinygo.org/x/go-llvm" | 	"tinygo.org/x/go-llvm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										242
									
								
								transform/optimizer.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										242
									
								
								transform/optimizer.go
									
										
									
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,242 @@ | ||||||
|  | package transform | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"github.com/tinygo-org/tinygo/compileopts" | ||||||
|  | 	"github.com/tinygo-org/tinygo/compiler/ircheck" | ||||||
|  | 	"tinygo.org/x/go-llvm" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Optimize runs a number of optimization and transformation passes over the | ||||||
|  | // given module. Some passes are specific to TinyGo, others are generic LLVM | ||||||
|  | // passes. You can set a preferred performance (0-3) and size (0-2) level and | ||||||
|  | // control the limits of the inliner (higher numbers mean more inlining, set it | ||||||
|  | // to 0 to disable entirely). | ||||||
|  | // | ||||||
|  | // Please note that some optimizations are not optional, thus Optimize must | ||||||
|  | // alwasy be run before emitting machine code. Set all controls (optLevel, | ||||||
|  | // sizeLevel, inlinerThreshold) to 0 to reduce the number of optimizations to a | ||||||
|  | // minimum. | ||||||
|  | func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel int, inlinerThreshold uint) []error { | ||||||
|  | 	builder := llvm.NewPassManagerBuilder() | ||||||
|  | 	defer builder.Dispose() | ||||||
|  | 	builder.SetOptLevel(optLevel) | ||||||
|  | 	builder.SetSizeLevel(sizeLevel) | ||||||
|  | 	if inlinerThreshold != 0 { | ||||||
|  | 		builder.UseInlinerWithThreshold(inlinerThreshold) | ||||||
|  | 	} | ||||||
|  | 	builder.AddCoroutinePassesToExtensionPoints() | ||||||
|  | 
 | ||||||
|  | 	// Make sure these functions are kept in tact during TinyGo transformation passes. | ||||||
|  | 	for _, name := range getFunctionsUsedInTransforms(config) { | ||||||
|  | 		fn := mod.NamedFunction(name) | ||||||
|  | 		if fn.IsNil() { | ||||||
|  | 			panic(fmt.Errorf("missing core function %q", name)) | ||||||
|  | 		} | ||||||
|  | 		fn.SetLinkage(llvm.ExternalLinkage) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if config.PanicStrategy() == "trap" { | ||||||
|  | 		ReplacePanicsWithTrap(mod) // -panic=trap | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// run a check of all of our code | ||||||
|  | 	if config.VerifyIR() { | ||||||
|  | 		errs := ircheck.Module(mod) | ||||||
|  | 		if errs != nil { | ||||||
|  | 			return errs | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Run function passes for each function. | ||||||
|  | 	funcPasses := llvm.NewFunctionPassManagerForModule(mod) | ||||||
|  | 	defer funcPasses.Dispose() | ||||||
|  | 	builder.PopulateFunc(funcPasses) | ||||||
|  | 	funcPasses.InitializeFunc() | ||||||
|  | 	for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { | ||||||
|  | 		funcPasses.RunFunc(fn) | ||||||
|  | 	} | ||||||
|  | 	funcPasses.FinalizeFunc() | ||||||
|  | 
 | ||||||
|  | 	if optLevel > 0 { | ||||||
|  | 		// Run some preparatory passes for the Go optimizer. | ||||||
|  | 		goPasses := llvm.NewPassManager() | ||||||
|  | 		defer goPasses.Dispose() | ||||||
|  | 		goPasses.AddGlobalDCEPass() | ||||||
|  | 		goPasses.AddGlobalOptimizerPass() | ||||||
|  | 		goPasses.AddConstantPropagationPass() | ||||||
|  | 		goPasses.AddAggressiveDCEPass() | ||||||
|  | 		goPasses.AddFunctionAttrsPass() | ||||||
|  | 		goPasses.Run(mod) | ||||||
|  | 
 | ||||||
|  | 		// Run Go-specific optimization passes. | ||||||
|  | 		OptimizeMaps(mod) | ||||||
|  | 		OptimizeStringToBytes(mod) | ||||||
|  | 		OptimizeAllocs(mod) | ||||||
|  | 		LowerInterfaces(mod) | ||||||
|  | 
 | ||||||
|  | 		errs := LowerInterrupts(mod) | ||||||
|  | 		if len(errs) > 0 { | ||||||
|  | 			return errs | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if config.FuncImplementation() == compileopts.FuncValueSwitch { | ||||||
|  | 			LowerFuncValues(mod) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// After interfaces are lowered, there are many more opportunities for | ||||||
|  | 		// interprocedural optimizations. To get them to work, function | ||||||
|  | 		// attributes have to be updated first. | ||||||
|  | 		goPasses.Run(mod) | ||||||
|  | 
 | ||||||
|  | 		// Run TinyGo-specific interprocedural optimizations. | ||||||
|  | 		OptimizeAllocs(mod) | ||||||
|  | 		OptimizeStringToBytes(mod) | ||||||
|  | 
 | ||||||
|  | 		// Lower runtime.isnil calls to regular nil comparisons. | ||||||
|  | 		isnil := mod.NamedFunction("runtime.isnil") | ||||||
|  | 		if !isnil.IsNil() { | ||||||
|  | 			builder := mod.Context().NewBuilder() | ||||||
|  | 			defer builder.Dispose() | ||||||
|  | 			for _, use := range getUses(isnil) { | ||||||
|  | 				builder.SetInsertPointBefore(use) | ||||||
|  | 				ptr := use.Operand(0) | ||||||
|  | 				if !ptr.IsABitCastInst().IsNil() { | ||||||
|  | 					ptr = ptr.Operand(0) | ||||||
|  | 				} | ||||||
|  | 				nilptr := llvm.ConstPointerNull(ptr.Type()) | ||||||
|  | 				icmp := builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "") | ||||||
|  | 				use.ReplaceAllUsesWith(icmp) | ||||||
|  | 				use.EraseFromParentAsInstruction() | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} else { | ||||||
|  | 		// Must be run at any optimization level. | ||||||
|  | 		LowerInterfaces(mod) | ||||||
|  | 		if config.FuncImplementation() == compileopts.FuncValueSwitch { | ||||||
|  | 			LowerFuncValues(mod) | ||||||
|  | 		} | ||||||
|  | 		errs := LowerInterrupts(mod) | ||||||
|  | 		if len(errs) > 0 { | ||||||
|  | 			return errs | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Lower async implementations. | ||||||
|  | 	switch config.Scheduler() { | ||||||
|  | 	case "coroutines": | ||||||
|  | 		// Lower async as coroutines. | ||||||
|  | 		err := LowerCoroutines(mod, config.NeedsStackObjects()) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return []error{err} | ||||||
|  | 		} | ||||||
|  | 	case "tasks": | ||||||
|  | 		// No transformations necessary. | ||||||
|  | 	case "none": | ||||||
|  | 		// Check for any goroutine starts. | ||||||
|  | 		if start := mod.NamedFunction("internal/task.start"); !start.IsNil() && len(getUses(start)) > 0 { | ||||||
|  | 			errs := []error{} | ||||||
|  | 			for _, call := range getUses(start) { | ||||||
|  | 				errs = append(errs, errorAt(call, "attempted to start a goroutine without a scheduler")) | ||||||
|  | 			} | ||||||
|  | 			return errs | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		return []error{errors.New("invalid scheduler")} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if config.VerifyIR() { | ||||||
|  | 		if errs := ircheck.Module(mod); errs != nil { | ||||||
|  | 			return errs | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { | ||||||
|  | 		return []error{errors.New("optimizations caused a verification failure")} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if sizeLevel >= 2 { | ||||||
|  | 		// Set the "optsize" attribute to make slightly smaller binaries at the | ||||||
|  | 		// cost of some performance. | ||||||
|  | 		kind := llvm.AttributeKindID("optsize") | ||||||
|  | 		attr := mod.Context().CreateEnumAttribute(kind, 0) | ||||||
|  | 		for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { | ||||||
|  | 			fn.AddFunctionAttr(attr) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// After TinyGo-specific transforms have finished, undo exporting these functions. | ||||||
|  | 	for _, name := range getFunctionsUsedInTransforms(config) { | ||||||
|  | 		fn := mod.NamedFunction(name) | ||||||
|  | 		if fn.IsNil() { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		fn.SetLinkage(llvm.InternalLinkage) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Run function passes again, because without it, llvm.coro.size.i32() | ||||||
|  | 	// doesn't get lowered. | ||||||
|  | 	for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { | ||||||
|  | 		funcPasses.RunFunc(fn) | ||||||
|  | 	} | ||||||
|  | 	funcPasses.FinalizeFunc() | ||||||
|  | 
 | ||||||
|  | 	// Run module passes. | ||||||
|  | 	modPasses := llvm.NewPassManager() | ||||||
|  | 	defer modPasses.Dispose() | ||||||
|  | 	builder.Populate(modPasses) | ||||||
|  | 	modPasses.Run(mod) | ||||||
|  | 
 | ||||||
|  | 	hasGCPass := AddGlobalsBitmap(mod) | ||||||
|  | 	hasGCPass = MakeGCStackSlots(mod) || hasGCPass | ||||||
|  | 	if hasGCPass { | ||||||
|  | 		if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { | ||||||
|  | 			return []error{errors.New("GC pass caused a verification failure")} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // functionsUsedInTransform is a list of function symbols that may be used | ||||||
|  | // during TinyGo optimization passes so they have to be marked as external | ||||||
|  | // linkage until all TinyGo passes have finished. | ||||||
|  | var functionsUsedInTransforms = []string{ | ||||||
|  | 	"runtime.alloc", | ||||||
|  | 	"runtime.free", | ||||||
|  | 	"runtime.nilPanic", | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var taskFunctionsUsedInTransforms = []string{} | ||||||
|  | 
 | ||||||
|  | // These functions need to be preserved in the IR until after the coroutines | ||||||
|  | // pass has run. | ||||||
|  | var coroFunctionsUsedInTransforms = []string{ | ||||||
|  | 	"internal/task.start", | ||||||
|  | 	"internal/task.Pause", | ||||||
|  | 	"internal/task.fake", | ||||||
|  | 	"internal/task.Current", | ||||||
|  | 	"internal/task.createTask", | ||||||
|  | 	"(*internal/task.Task).setState", | ||||||
|  | 	"(*internal/task.Task).returnTo", | ||||||
|  | 	"(*internal/task.Task).returnCurrent", | ||||||
|  | 	"(*internal/task.Task).setReturnPtr", | ||||||
|  | 	"(*internal/task.Task).getReturnPtr", | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // getFunctionsUsedInTransforms gets a list of all special functions that should be preserved during transforms and optimization. | ||||||
|  | func getFunctionsUsedInTransforms(config *compileopts.Config) []string { | ||||||
|  | 	fnused := functionsUsedInTransforms | ||||||
|  | 	switch config.Scheduler() { | ||||||
|  | 	case "none": | ||||||
|  | 	case "coroutines": | ||||||
|  | 		fnused = append(append([]string{}, fnused...), coroFunctionsUsedInTransforms...) | ||||||
|  | 	case "tasks": | ||||||
|  | 		fnused = append(append([]string{}, fnused...), taskFunctionsUsedInTransforms...) | ||||||
|  | 	default: | ||||||
|  | 		panic(fmt.Errorf("invalid scheduler %q", config.Scheduler())) | ||||||
|  | 	} | ||||||
|  | 	return fnused | ||||||
|  | } | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem