
This reduces code size in a few cases when tested against the drivers smoketests (although there was one minor increase) without significantly increasing compile time. In fact, in my testing compile time appears to be going down a little bit (around 1%, within the noise).
154 строки
4,2 КиБ
Go
154 строки
4,2 КиБ
Go
package compiler
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"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 := c.checkModule()
|
|
if errs != nil {
|
|
return errs
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
if c.funcImplementation() == 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()
|
|
}
|
|
}
|
|
|
|
err := c.LowerGoroutines()
|
|
if err != nil {
|
|
return []error{err}
|
|
}
|
|
} else {
|
|
// Must be run at any optimization level.
|
|
transform.LowerInterfaces(c.mod)
|
|
if c.funcImplementation() == funcValueSwitch {
|
|
transform.LowerFuncValues(c.mod)
|
|
}
|
|
err := c.LowerGoroutines()
|
|
if err != nil {
|
|
return []error{err}
|
|
}
|
|
}
|
|
if c.VerifyIR() {
|
|
if errs := c.checkModule(); 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
|
|
}
|