compiler: move Optimizer function to transform package
This refactor is a prerequisite to much larger refactors in the compiler.
Этот коммит содержится в:
родитель
78bd7e094f
коммит
9d3de55229
5 изменённых файлов: 252 добавлений и 239 удалений
|
@ -72,16 +72,16 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||||
// exactly.
|
// exactly.
|
||||||
errs = nil
|
errs = nil
|
||||||
switch config.Options.Opt {
|
switch config.Options.Opt {
|
||||||
case "none:", "0":
|
case "none", "0":
|
||||||
errs = c.Optimize(0, 0, 0) // -O0
|
errs = transform.Optimize(c.Module(), config, 0, 0, 0) // -O0
|
||||||
case "1":
|
case "1":
|
||||||
errs = c.Optimize(1, 0, 0) // -O1
|
errs = transform.Optimize(c.Module(), config, 1, 0, 0) // -O1
|
||||||
case "2":
|
case "2":
|
||||||
errs = c.Optimize(2, 0, 225) // -O2
|
errs = transform.Optimize(c.Module(), config, 2, 0, 225) // -O2
|
||||||
case "s":
|
case "s":
|
||||||
errs = c.Optimize(2, 1, 225) // -Os
|
errs = transform.Optimize(c.Module(), config, 2, 1, 225) // -Os
|
||||||
case "z":
|
case "z":
|
||||||
errs = c.Optimize(2, 2, 5) // -Oz, default
|
errs = transform.Optimize(c.Module(), config, 2, 2, 5) // -Oz, default
|
||||||
default:
|
default:
|
||||||
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
|
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,30 +34,6 @@ func init() {
|
||||||
// The TinyGo import path.
|
// The TinyGo import path.
|
||||||
const tinygoPath = "github.com/tinygo-org/tinygo"
|
const tinygoPath = "github.com/tinygo-org/tinygo"
|
||||||
|
|
||||||
// functionsUsedInTransform is a list of function symbols that may be used
|
|
||||||
// during TinyGo optimization passes so they have to be marked as external
|
|
||||||
// linkage until all TinyGo passes have finished.
|
|
||||||
var functionsUsedInTransforms = []string{
|
|
||||||
"runtime.alloc",
|
|
||||||
"runtime.free",
|
|
||||||
"runtime.nilPanic",
|
|
||||||
}
|
|
||||||
|
|
||||||
var taskFunctionsUsedInTransforms = []string{}
|
|
||||||
|
|
||||||
var coroFunctionsUsedInTransforms = []string{
|
|
||||||
"internal/task.start",
|
|
||||||
"internal/task.Pause",
|
|
||||||
"internal/task.fake",
|
|
||||||
"internal/task.Current",
|
|
||||||
"internal/task.createTask",
|
|
||||||
"(*internal/task.Task).setState",
|
|
||||||
"(*internal/task.Task).returnTo",
|
|
||||||
"(*internal/task.Task).returnCurrent",
|
|
||||||
"(*internal/task.Task).setReturnPtr",
|
|
||||||
"(*internal/task.Task).getReturnPtr",
|
|
||||||
}
|
|
||||||
|
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
*compileopts.Config
|
*compileopts.Config
|
||||||
mod llvm.Module
|
mod llvm.Module
|
||||||
|
@ -156,21 +132,6 @@ func (c *Compiler) Module() llvm.Module {
|
||||||
return c.mod
|
return c.mod
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFunctionsUsedInTransforms gets a list of all special functions that should be preserved during transforms and optimization.
|
|
||||||
func (c *Compiler) getFunctionsUsedInTransforms() []string {
|
|
||||||
fnused := functionsUsedInTransforms
|
|
||||||
switch c.Scheduler() {
|
|
||||||
case "none":
|
|
||||||
case "coroutines":
|
|
||||||
fnused = append(append([]string{}, fnused...), coroFunctionsUsedInTransforms...)
|
|
||||||
case "tasks":
|
|
||||||
fnused = append(append([]string{}, fnused...), taskFunctionsUsedInTransforms...)
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("invalid scheduler %q", c.Scheduler()))
|
|
||||||
}
|
|
||||||
return fnused
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile the given package path or .go file path. Return an error when this
|
// Compile the given package path or .go file path. Return an error when this
|
||||||
// fails (in any stage).
|
// fails (in any stage).
|
||||||
func (c *Compiler) Compile(mainPath string) []error {
|
func (c *Compiler) Compile(mainPath string) []error {
|
||||||
|
@ -338,14 +299,8 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||||
realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
|
realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
|
||||||
realMain.SetLinkage(llvm.ExternalLinkage) // keep alive until goroutine lowering
|
realMain.SetLinkage(llvm.ExternalLinkage) // keep alive until goroutine lowering
|
||||||
|
|
||||||
// Make sure these functions are kept in tact during TinyGo transformation passes.
|
// Replace callMain placeholder with actual main function.
|
||||||
for _, name := range c.getFunctionsUsedInTransforms() {
|
c.mod.NamedFunction("runtime.callMain").ReplaceAllUsesWith(realMain)
|
||||||
fn := c.mod.NamedFunction(name)
|
|
||||||
if fn.IsNil() {
|
|
||||||
panic(fmt.Errorf("missing core function %q", name))
|
|
||||||
}
|
|
||||||
fn.SetLinkage(llvm.ExternalLinkage)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load some attributes
|
// Load some attributes
|
||||||
getAttr := func(attrName string) llvm.Attribute {
|
getAttr := func(attrName string) llvm.Attribute {
|
||||||
|
|
|
@ -1,185 +0,0 @@
|
||||||
package compiler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
|
||||||
"github.com/tinygo-org/tinygo/compiler/ircheck"
|
|
||||||
"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 := ircheck.Module(c.mod)
|
|
||||||
if errs != nil {
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace callMain placeholder with actual main function.
|
|
||||||
c.mod.NamedFunction("runtime.callMain").ReplaceAllUsesWith(c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main"))
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
errs := transform.LowerInterrupts(c.mod)
|
|
||||||
if len(errs) > 0 {
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.FuncImplementation() == compileopts.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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Must be run at any optimization level.
|
|
||||||
transform.LowerInterfaces(c.mod)
|
|
||||||
if c.FuncImplementation() == compileopts.FuncValueSwitch {
|
|
||||||
transform.LowerFuncValues(c.mod)
|
|
||||||
}
|
|
||||||
errs := transform.LowerInterrupts(c.mod)
|
|
||||||
if len(errs) > 0 {
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lower async implementations.
|
|
||||||
switch c.Scheduler() {
|
|
||||||
case "coroutines":
|
|
||||||
// Lower async as coroutines.
|
|
||||||
err := transform.LowerCoroutines(c.mod, c.NeedsStackObjects())
|
|
||||||
if err != nil {
|
|
||||||
return []error{err}
|
|
||||||
}
|
|
||||||
case "tasks":
|
|
||||||
// No transformations necessary.
|
|
||||||
case "none":
|
|
||||||
// Check for any goroutine starts.
|
|
||||||
if start := c.mod.NamedFunction("internal/task.start"); !start.IsNil() && len(getUses(start)) > 0 {
|
|
||||||
errs := []error{}
|
|
||||||
for _, call := range getUses(start) {
|
|
||||||
errs = append(errs, errorAt(call, "attempted to start a goroutine without a scheduler"))
|
|
||||||
}
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return []error{errors.New("invalid scheduler")}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.VerifyIR() {
|
|
||||||
if errs := ircheck.Module(c.mod); 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
|
|
||||||
}
|
|
|
@ -5,8 +5,9 @@ package transform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
242
transform/optimizer.go
Обычный файл
242
transform/optimizer.go
Обычный файл
|
@ -0,0 +1,242 @@
|
||||||
|
package transform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/compiler/ircheck"
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
builder.AddCoroutinePassesToExtensionPoints()
|
||||||
|
|
||||||
|
// Make sure these functions are kept in tact during TinyGo transformation passes.
|
||||||
|
for _, name := range getFunctionsUsedInTransforms(config) {
|
||||||
|
fn := mod.NamedFunction(name)
|
||||||
|
if fn.IsNil() {
|
||||||
|
panic(fmt.Errorf("missing core function %q", name))
|
||||||
|
}
|
||||||
|
fn.SetLinkage(llvm.ExternalLinkage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.PanicStrategy() == "trap" {
|
||||||
|
ReplacePanicsWithTrap(mod) // -panic=trap
|
||||||
|
}
|
||||||
|
|
||||||
|
// run a check of all of our code
|
||||||
|
if config.VerifyIR() {
|
||||||
|
errs := ircheck.Module(mod)
|
||||||
|
if errs != nil {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run function passes for each function.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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(mod)
|
||||||
|
|
||||||
|
// Run Go-specific optimization passes.
|
||||||
|
OptimizeMaps(mod)
|
||||||
|
OptimizeStringToBytes(mod)
|
||||||
|
OptimizeAllocs(mod)
|
||||||
|
LowerInterfaces(mod)
|
||||||
|
|
||||||
|
errs := LowerInterrupts(mod)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.FuncImplementation() == compileopts.FuncValueSwitch {
|
||||||
|
LowerFuncValues(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(mod)
|
||||||
|
|
||||||
|
// Run TinyGo-specific interprocedural optimizations.
|
||||||
|
OptimizeAllocs(mod)
|
||||||
|
OptimizeStringToBytes(mod)
|
||||||
|
|
||||||
|
// Lower runtime.isnil calls to regular nil comparisons.
|
||||||
|
isnil := mod.NamedFunction("runtime.isnil")
|
||||||
|
if !isnil.IsNil() {
|
||||||
|
builder := mod.Context().NewBuilder()
|
||||||
|
defer builder.Dispose()
|
||||||
|
for _, use := range getUses(isnil) {
|
||||||
|
builder.SetInsertPointBefore(use)
|
||||||
|
ptr := use.Operand(0)
|
||||||
|
if !ptr.IsABitCastInst().IsNil() {
|
||||||
|
ptr = ptr.Operand(0)
|
||||||
|
}
|
||||||
|
nilptr := llvm.ConstPointerNull(ptr.Type())
|
||||||
|
icmp := builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
|
||||||
|
use.ReplaceAllUsesWith(icmp)
|
||||||
|
use.EraseFromParentAsInstruction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Must be run at any optimization level.
|
||||||
|
LowerInterfaces(mod)
|
||||||
|
if config.FuncImplementation() == compileopts.FuncValueSwitch {
|
||||||
|
LowerFuncValues(mod)
|
||||||
|
}
|
||||||
|
errs := LowerInterrupts(mod)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower async implementations.
|
||||||
|
switch config.Scheduler() {
|
||||||
|
case "coroutines":
|
||||||
|
// Lower async as coroutines.
|
||||||
|
err := LowerCoroutines(mod, config.NeedsStackObjects())
|
||||||
|
if err != nil {
|
||||||
|
return []error{err}
|
||||||
|
}
|
||||||
|
case "tasks":
|
||||||
|
// No transformations necessary.
|
||||||
|
case "none":
|
||||||
|
// Check for any goroutine starts.
|
||||||
|
if start := mod.NamedFunction("internal/task.start"); !start.IsNil() && len(getUses(start)) > 0 {
|
||||||
|
errs := []error{}
|
||||||
|
for _, call := range getUses(start) {
|
||||||
|
errs = append(errs, errorAt(call, "attempted to start a goroutine without a scheduler"))
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return []error{errors.New("invalid scheduler")}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.VerifyIR() {
|
||||||
|
if errs := ircheck.Module(mod); errs != nil {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); 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 := mod.Context().CreateEnumAttribute(kind, 0)
|
||||||
|
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||||
|
fn.AddFunctionAttr(attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After TinyGo-specific transforms have finished, undo exporting these functions.
|
||||||
|
for _, name := range getFunctionsUsedInTransforms(config) {
|
||||||
|
fn := 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 := 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(mod)
|
||||||
|
|
||||||
|
hasGCPass := AddGlobalsBitmap(mod)
|
||||||
|
hasGCPass = MakeGCStackSlots(mod) || hasGCPass
|
||||||
|
if hasGCPass {
|
||||||
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
|
return []error{errors.New("GC pass caused a verification failure")}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// functionsUsedInTransform is a list of function symbols that may be used
|
||||||
|
// during TinyGo optimization passes so they have to be marked as external
|
||||||
|
// linkage until all TinyGo passes have finished.
|
||||||
|
var functionsUsedInTransforms = []string{
|
||||||
|
"runtime.alloc",
|
||||||
|
"runtime.free",
|
||||||
|
"runtime.nilPanic",
|
||||||
|
}
|
||||||
|
|
||||||
|
var taskFunctionsUsedInTransforms = []string{}
|
||||||
|
|
||||||
|
// These functions need to be preserved in the IR until after the coroutines
|
||||||
|
// pass has run.
|
||||||
|
var coroFunctionsUsedInTransforms = []string{
|
||||||
|
"internal/task.start",
|
||||||
|
"internal/task.Pause",
|
||||||
|
"internal/task.fake",
|
||||||
|
"internal/task.Current",
|
||||||
|
"internal/task.createTask",
|
||||||
|
"(*internal/task.Task).setState",
|
||||||
|
"(*internal/task.Task).returnTo",
|
||||||
|
"(*internal/task.Task).returnCurrent",
|
||||||
|
"(*internal/task.Task).setReturnPtr",
|
||||||
|
"(*internal/task.Task).getReturnPtr",
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFunctionsUsedInTransforms gets a list of all special functions that should be preserved during transforms and optimization.
|
||||||
|
func getFunctionsUsedInTransforms(config *compileopts.Config) []string {
|
||||||
|
fnused := functionsUsedInTransforms
|
||||||
|
switch config.Scheduler() {
|
||||||
|
case "none":
|
||||||
|
case "coroutines":
|
||||||
|
fnused = append(append([]string{}, fnused...), coroFunctionsUsedInTransforms...)
|
||||||
|
case "tasks":
|
||||||
|
fnused = append(append([]string{}, fnused...), taskFunctionsUsedInTransforms...)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("invalid scheduler %q", config.Scheduler()))
|
||||||
|
}
|
||||||
|
return fnused
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче