diff options
author | Ayke van Laethem <[email protected]> | 2023-09-19 22:37:44 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2023-10-04 13:05:58 +0200 |
commit | 3b1913ac57420af2c665c6f1c3847a6e63774ecd (patch) | |
tree | 77460ae8c35853f7f16d4f8575576e677d78e737 /transform/optimizer.go | |
parent | 1da1abe3147796aa56a5486ed6f07afdd88d8234 (diff) | |
download | tinygo-3b1913ac57420af2c665c6f1c3847a6e63774ecd.tar.gz tinygo-3b1913ac57420af2c665c6f1c3847a6e63774ecd.zip |
all: use the new LLVM pass manager
The old LLVM pass manager is deprecated and should not be used anymore.
Moreover, the pass manager builder (which we used to set up a pass
pipeline) is actually removed from LLVM entirely in LLVM 17:
https://reviews.llvm.org/D145387
https://reviews.llvm.org/D145835
The new pass manager does change the binary size in many cases: both
growing and shrinking it. However, on average the binary size remains
more or less the same.
This is needed as a preparation for LLVM 17.
Diffstat (limited to 'transform/optimizer.go')
-rw-r--r-- | transform/optimizer.go | 102 |
1 files changed, 32 insertions, 70 deletions
diff --git a/transform/optimizer.go b/transform/optimizer.go index 20258ef4f..42acc2ddc 100644 --- a/transform/optimizer.go +++ b/transform/optimizer.go @@ -14,54 +14,22 @@ import ( // OptimizePackage runs optimization passes over the LLVM module for the given // Go package. func OptimizePackage(mod llvm.Module, config *compileopts.Config) { - optLevel, sizeLevel, _ := config.OptLevels() - - // Run function passes for each function in the module. - // These passes are intended to be run on each function right - // after they're created to reduce IR size (and maybe also for - // cache locality to improve performance), but for now they're - // run here for each function in turn. Maybe this can be - // improved in the future. - builder := llvm.NewPassManagerBuilder() - defer builder.Dispose() - builder.SetOptLevel(optLevel) - builder.SetSizeLevel(sizeLevel) - funcPasses := llvm.NewFunctionPassManagerForModule(mod) - defer funcPasses.Dispose() - builder.PopulateFunc(funcPasses) - funcPasses.InitializeFunc() - for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { - if fn.IsDeclaration() { - continue - } - funcPasses.RunFunc(fn) - } - funcPasses.FinalizeFunc() + _, speedLevel, _ := config.OptLevel() // Run TinyGo-specific optimization passes. - if optLevel > 0 { + if speedLevel > 0 { OptimizeMaps(mod) } } // 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). +// passes. // // 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) - } +// alwasy be run before emitting machine code. +func Optimize(mod llvm.Module, config *compileopts.Config) []error { + optLevel, speedLevel, _ := config.OptLevel() // Make sure these functions are kept in tact during TinyGo transformation passes. for _, name := range functionsUsedInTransforms { @@ -84,23 +52,20 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i } } - if optLevel > 0 { + if speedLevel > 0 { // Run some preparatory passes for the Go optimizer. - goPasses := llvm.NewPassManager() - defer goPasses.Dispose() - goPasses.AddGlobalDCEPass() - goPasses.AddGlobalOptimizerPass() - goPasses.AddIPSCCPPass() - goPasses.AddInstructionCombiningPass() // necessary for OptimizeReflectImplements - goPasses.AddAggressiveDCEPass() - goPasses.AddFunctionAttrsPass() - goPasses.Run(mod) + po := llvm.NewPassBuilderOptions() + defer po.Dispose() + err := mod.RunPasses("globaldce,globalopt,ipsccp,instcombine,adce,function-attrs", llvm.TargetMachine{}, po) + if err != nil { + return []error{fmt.Errorf("could not build pass pipeline: %w", err)} + } // Run TinyGo-specific optimization passes. OptimizeStringToBytes(mod) OptimizeReflectImplements(mod) OptimizeAllocs(mod, nil, nil) - err := LowerInterfaces(mod, config) + err = LowerInterfaces(mod, config) if err != nil { return []error{err} } @@ -113,7 +78,10 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i // 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) + err = mod.RunPasses("globaldce,globalopt,ipsccp,instcombine,adce,function-attrs", llvm.TargetMachine{}, po) + if err != nil { + return []error{fmt.Errorf("could not build pass pipeline: %w", err)} + } // Run TinyGo-specific interprocedural optimizations. OptimizeAllocs(mod, config.Options.PrintAllocs, func(pos token.Position, msg string) { @@ -134,10 +102,12 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i } // Clean up some leftover symbols of the previous transformations. - goPasses := llvm.NewPassManager() - defer goPasses.Dispose() - goPasses.AddGlobalDCEPass() - goPasses.Run(mod) + po := llvm.NewPassBuilderOptions() + defer po.Dispose() + err = mod.RunPasses("globaldce", llvm.TargetMachine{}, po) + if err != nil { + return []error{fmt.Errorf("could not build pass pipeline: %w", err)} + } } if config.Scheduler() == "none" { @@ -169,23 +139,15 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i fn.SetLinkage(llvm.InternalLinkage) } - // Run function passes again, because without it, llvm.coro.size.i32() - // doesn't get lowered. - 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) + // Run the default pass pipeline. + // TODO: set the PrepareForThinLTO flag somehow. + po := llvm.NewPassBuilderOptions() + defer po.Dispose() + passes := fmt.Sprintf("default<%s>", optLevel) + err := mod.RunPasses(passes, llvm.TargetMachine{}, po) + if err != nil { + return []error{fmt.Errorf("could not build pass pipeline: %w", err)} } - funcPasses.FinalizeFunc() - - // Run module passes. - // TODO: somehow set the PrepareForThinLTO flag in the pass manager builder. - modPasses := llvm.NewPassManager() - defer modPasses.Dispose() - builder.Populate(modPasses) - modPasses.Run(mod) hasGCPass := MakeGCStackSlots(mod) if hasGCPass { |