mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-12 06:53:40 +00:00
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.
This commit is contained in:
committed by
Ron Evans
parent
1da1abe314
commit
3b1913ac57
@@ -38,11 +38,12 @@ func TestAllocs2(t *testing.T) {
|
||||
mod := compileGoFileForTesting(t, "./testdata/allocs2.go")
|
||||
|
||||
// Run functionattrs pass, which is necessary for escape analysis.
|
||||
pm := llvm.NewPassManager()
|
||||
defer pm.Dispose()
|
||||
pm.AddInstructionCombiningPass()
|
||||
pm.AddFunctionAttrsPass()
|
||||
pm.Run(mod)
|
||||
po := llvm.NewPassBuilderOptions()
|
||||
defer po.Dispose()
|
||||
err := mod.RunPasses("function(instcombine),function-attrs", llvm.TargetMachine{}, po)
|
||||
if err != nil {
|
||||
t.Error("failed to run passes:", err)
|
||||
}
|
||||
|
||||
// Run heap to stack transform.
|
||||
var testOutputs []allocsTestOutput
|
||||
|
||||
@@ -15,9 +15,11 @@ func TestInterfaceLowering(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
pm := llvm.NewPassManager()
|
||||
defer pm.Dispose()
|
||||
pm.AddGlobalDCEPass()
|
||||
pm.Run(mod)
|
||||
po := llvm.NewPassBuilderOptions()
|
||||
defer po.Dispose()
|
||||
err = mod.RunPasses("globaldce", llvm.TargetMachine{}, po)
|
||||
if err != nil {
|
||||
t.Error("failed to run passes:", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@ func TestOptimizeMaps(t *testing.T) {
|
||||
|
||||
// Run an optimization pass, to clean up the result.
|
||||
// This shows that all code related to the map is really eliminated.
|
||||
pm := llvm.NewPassManager()
|
||||
defer pm.Dispose()
|
||||
pm.AddDeadStoreEliminationPass()
|
||||
pm.AddAggressiveDCEPass()
|
||||
pm.Run(mod)
|
||||
po := llvm.NewPassBuilderOptions()
|
||||
defer po.Dispose()
|
||||
err := mod.RunPasses("dse,adce", llvm.TargetMachine{}, po)
|
||||
if err != nil {
|
||||
t.Error("failed to run passes:", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+32
-70
@@ -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 {
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
// the -opt= compiler flag.
|
||||
func AddStandardAttributes(fn llvm.Value, config *compileopts.Config) {
|
||||
ctx := fn.Type().Context()
|
||||
_, sizeLevel, _ := config.OptLevels()
|
||||
_, _, sizeLevel := config.OptLevel()
|
||||
if sizeLevel >= 1 {
|
||||
fn.AddFunctionAttr(ctx.CreateEnumAttribute(llvm.AttributeKindID("optsize"), 0))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user