@ -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 opt Level > 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 opt Level > 0 {
if speed Level > 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 {