aboutsummaryrefslogtreecommitdiffhomepage
path: root/transform/optimizer.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2023-09-19 22:37:44 +0200
committerRon Evans <[email protected]>2023-10-04 13:05:58 +0200
commit3b1913ac57420af2c665c6f1c3847a6e63774ecd (patch)
tree77460ae8c35853f7f16d4f8575576e677d78e737 /transform/optimizer.go
parent1da1abe3147796aa56a5486ed6f07afdd88d8234 (diff)
downloadtinygo-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.go102
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 {