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. | ||||
| 	errs = nil | ||||
| 	switch config.Options.Opt { | ||||
| 	case "none:", "0": | ||||
| 		errs = c.Optimize(0, 0, 0) // -O0 | ||||
| 	case "none", "0": | ||||
| 		errs = transform.Optimize(c.Module(), config, 0, 0, 0) // -O0 | ||||
| 	case "1": | ||||
| 		errs = c.Optimize(1, 0, 0) // -O1 | ||||
| 		errs = transform.Optimize(c.Module(), config, 1, 0, 0) // -O1 | ||||
| 	case "2": | ||||
| 		errs = c.Optimize(2, 0, 225) // -O2 | ||||
| 		errs = transform.Optimize(c.Module(), config, 2, 0, 225) // -O2 | ||||
| 	case "s": | ||||
| 		errs = c.Optimize(2, 1, 225) // -Os | ||||
| 		errs = transform.Optimize(c.Module(), config, 2, 1, 225) // -Os | ||||
| 	case "z": | ||||
| 		errs = c.Optimize(2, 2, 5) // -Oz, default | ||||
| 		errs = transform.Optimize(c.Module(), config, 2, 2, 5) // -Oz, default | ||||
| 	default: | ||||
| 		errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)} | ||||
| 	} | ||||
|  |  | |||
|  | @ -34,30 +34,6 @@ func init() { | |||
| // The TinyGo import path. | ||||
| 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 { | ||||
| 	*compileopts.Config | ||||
| 	mod                     llvm.Module | ||||
|  | @ -156,21 +132,6 @@ func (c *Compiler) Module() llvm.Module { | |||
| 	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 | ||||
| // fails (in any stage). | ||||
| 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.SetLinkage(llvm.ExternalLinkage) // keep alive until goroutine lowering | ||||
| 
 | ||||
| 	// Make sure these functions are kept in tact during TinyGo transformation passes. | ||||
| 	for _, name := range c.getFunctionsUsedInTransforms() { | ||||
| 		fn := c.mod.NamedFunction(name) | ||||
| 		if fn.IsNil() { | ||||
| 			panic(fmt.Errorf("missing core function %q", name)) | ||||
| 		} | ||||
| 		fn.SetLinkage(llvm.ExternalLinkage) | ||||
| 	} | ||||
| 	// Replace callMain placeholder with actual main function. | ||||
| 	c.mod.NamedFunction("runtime.callMain").ReplaceAllUsesWith(realMain) | ||||
| 
 | ||||
| 	// Load some attributes | ||||
| 	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 ( | ||||
| 	"errors" | ||||
| 	"github.com/tinygo-org/tinygo/compiler/llvmutil" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/tinygo-org/tinygo/compiler/llvmutil" | ||||
| 	"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