diff options
author | Ayke van Laethem <[email protected]> | 2021-11-01 14:41:52 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-11-03 13:40:13 +0100 |
commit | d7b7583e83b877031c78cf9a727bfd338cf65892 (patch) | |
tree | 54720a40d838adc55b3f04dc7f89a3120be9bcfc | |
parent | 1869efe9546308f624ab1110137a5670ca9f515b (diff) | |
download | tinygo-d7b7583e83b877031c78cf9a727bfd338cf65892.tar.gz tinygo-d7b7583e83b877031c78cf9a727bfd338cf65892.zip |
compiler: refactor when the optsize attribute is set
This commit has a few related changes:
* It sets the optsize attribute immediately in the compiler instead of
adding it to each function afterwards in a loop. This seems to me
like the more appropriate way to do it.
* It centralizes setting the optsize attribute in the transform
package, to make later changes easier.
* It sets the optsize in a few more places: to runtime.initAll and to
WebAssembly i64 wrappers.
This commit does not affect the binary size of any of the smoke tests,
so should be risk-free.
-rw-r--r-- | builder/build.go | 16 | ||||
-rw-r--r-- | compiler/compiler.go | 3 | ||||
-rw-r--r-- | compiler/interface.go | 2 | ||||
-rw-r--r-- | compiler/symbol.go | 28 | ||||
-rw-r--r-- | transform/interface-lowering.go | 15 | ||||
-rw-r--r-- | transform/interface-lowering_test.go | 3 | ||||
-rw-r--r-- | transform/interrupt.go | 7 | ||||
-rw-r--r-- | transform/interrupt_test.go | 3 | ||||
-rw-r--r-- | transform/optimizer.go | 8 | ||||
-rw-r--r-- | transform/transform.go | 15 | ||||
-rw-r--r-- | transform/wasm-abi.go | 8 | ||||
-rw-r--r-- | transform/wasm-abi_test.go | 3 |
12 files changed, 69 insertions, 42 deletions
diff --git a/builder/build.go b/builder/build.go index 12ea76f19..0b5cc2f18 100644 --- a/builder/build.go +++ b/builder/build.go @@ -86,6 +86,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil } defer os.RemoveAll(dir) + optLevel, sizeLevel, _ := config.OptLevels() compilerConfig := &compiler.Config{ Triple: config.Triple(), CPU: config.CPU(), @@ -94,6 +95,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil GOARCH: config.GOARCH(), CodeModel: config.CodeModel(), RelocationModel: config.RelocationModel(), + SizeLevel: sizeLevel, Scheduler: config.Scheduler(), FuncImplementation: config.FuncImplementation(), @@ -133,7 +135,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil var packageJobs []*compileJob packageBitcodePaths := make(map[string]string) packageActionIDs := make(map[string]string) - optLevel, sizeLevel, _ := config.OptLevels() for _, pkg := range lprogram.Sorted() { pkg := pkg // necessary to avoid a race condition @@ -285,16 +286,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil return errors.New("verification error after interpreting " + pkgInit.Name()) } - 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) - } - } - // 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 @@ -380,6 +371,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil llvmInitFn := mod.NamedFunction("runtime.initAll") llvmInitFn.SetLinkage(llvm.InternalLinkage) llvmInitFn.SetUnnamedAddr(true) + transform.AddStandardAttributes(llvmInitFn, config) llvmInitFn.Param(0).SetName("context") llvmInitFn.Param(1).SetName("parentHandle") block := mod.Context().AddBasicBlock(llvmInitFn, "entry") @@ -772,7 +764,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error { // stack-allocated values. // Use -wasm-abi=generic to disable this behaviour. if config.WasmAbi() == "js" { - err := transform.ExternalInt64AsPtr(mod) + err := transform.ExternalInt64AsPtr(mod, config) if err != nil { return err } diff --git a/compiler/compiler.go b/compiler/compiler.go index 2c79047db..e908549d5 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -48,6 +48,7 @@ type Config struct { GOARCH string CodeModel string RelocationModel string + SizeLevel int // Various compiler options that determine how code is generated. Scheduler string @@ -816,7 +817,7 @@ func (b *builder) createFunction() { b.addError(b.fn.Pos(), errValue) return } - b.addStandardAttributes(b.llvmFn) + b.addStandardDefinedAttributes(b.llvmFn) if !b.info.exported { b.llvmFn.SetVisibility(llvm.HiddenVisibility) b.llvmFn.SetUnnamedAddr(true) diff --git a/compiler/interface.go b/compiler/interface.go index 07ed0bb75..7460c6583 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -457,6 +457,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll if llvmFn.IsNil() { llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.uintptrType}, false) llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) + c.addStandardDeclaredAttributes(llvmFn) methods := c.getMethodsString(assertedType.Underlying().(*types.Interface)) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods)) } @@ -478,6 +479,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value { paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.Uintptr])) llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)).ElementType() llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) + c.addStandardDeclaredAttributes(llvmFn) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method))) methods := c.getMethodsString(instr.Value.Type().Underlying().(*types.Interface)) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods)) diff --git a/compiler/symbol.go b/compiler/symbol.go index e235baed2..b4f47d26f 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -108,6 +108,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value { llvmFn.AddFunctionAttr(attr) } } + c.addStandardDeclaredAttributes(llvmFn) dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null") for i, info := range paramInfos { @@ -323,10 +324,23 @@ func getParams(sig *types.Signature) []*types.Var { return params } -// addStandardAttributes adds the set of attributes that are added to every -// function emitted by TinyGo (even thunks/wrappers), possibly depending on the -// architecture. -func (c *compilerContext) addStandardAttributes(llvmFn llvm.Value) { +// addStandardDeclaredAttributes adds attributes that are set for any function, +// whether declared or defined. +func (c *compilerContext) addStandardDeclaredAttributes(llvmFn llvm.Value) { + if c.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) + llvmFn.AddFunctionAttr(attr) + } +} + +// addStandardDefinedAttributes adds the set of attributes that are added to +// every function defined by TinyGo (even thunks/wrappers), possibly depending +// on the architecture. It does not set attributes only set for declared +// functions, use addStandardDeclaredAttributes for this. +func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) { // TinyGo does not currently raise exceptions, so set the 'nounwind' flag. // This behavior matches Clang when compiling C source files. // It reduces binary size on Linux a little bit on non-x86_64 targets by @@ -338,6 +352,12 @@ func (c *compilerContext) addStandardAttributes(llvmFn llvm.Value) { } } +// addStandardAttribute adds all attributes added to defined functions. +func (c *compilerContext) addStandardAttributes(llvmFn llvm.Value) { + c.addStandardDeclaredAttributes(llvmFn) + c.addStandardDefinedAttributes(llvmFn) +} + // globalInfo contains some information about a specific global. By default, // linkName is equal to .RelString(nil) on a global and extern is false, but for // some symbols this is different (due to //go:extern for example). diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go index 928bc8f41..ba1a27f06 100644 --- a/transform/interface-lowering.go +++ b/transform/interface-lowering.go @@ -33,6 +33,7 @@ import ( "sort" "strings" + "github.com/tinygo-org/tinygo/compileopts" "tinygo.org/x/go-llvm" ) @@ -84,7 +85,7 @@ type interfaceInfo struct { // should be seen as a regular function call (see LowerInterfaces). type lowerInterfacesPass struct { mod llvm.Module - sizeLevel int // LLVM optimization size level, 1 means -opt=s and 2 means -opt=z + config *compileopts.Config builder llvm.Builder ctx llvm.Context uintptrType llvm.Type @@ -97,10 +98,10 @@ type lowerInterfacesPass struct { // emitted by the compiler as higher-level intrinsics. They need some lowering // before LLVM can work on them. This is done so that a few cleanup passes can // run before assigning the final type codes. -func LowerInterfaces(mod llvm.Module, sizeLevel int) error { +func LowerInterfaces(mod llvm.Module, config *compileopts.Config) error { p := &lowerInterfacesPass{ mod: mod, - sizeLevel: sizeLevel, + config: config, builder: mod.Context().NewBuilder(), ctx: mod.Context(), uintptrType: mod.Context().IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8), @@ -343,9 +344,7 @@ func (p *lowerInterfacesPass) defineInterfaceImplementsFunc(fn llvm.Value, itf * fn.Param(0).SetName("actualType") fn.SetLinkage(llvm.InternalLinkage) fn.SetUnnamedAddr(true) - if p.sizeLevel >= 2 { - fn.AddFunctionAttr(p.ctx.CreateEnumAttribute(llvm.AttributeKindID("optsize"), 0)) - } + AddStandardAttributes(fn, p.config) // Start the if/else chain at the entry block. entry := p.ctx.AddBasicBlock(fn, "entry") @@ -389,9 +388,7 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte parentHandle.SetName("parentHandle") fn.SetLinkage(llvm.InternalLinkage) fn.SetUnnamedAddr(true) - if p.sizeLevel >= 2 { - fn.AddFunctionAttr(p.ctx.CreateEnumAttribute(llvm.AttributeKindID("optsize"), 0)) - } + AddStandardAttributes(fn, p.config) // TODO: debug info diff --git a/transform/interface-lowering_test.go b/transform/interface-lowering_test.go index 145b778f9..1dd635a3c 100644 --- a/transform/interface-lowering_test.go +++ b/transform/interface-lowering_test.go @@ -3,6 +3,7 @@ package transform_test import ( "testing" + "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/transform" "tinygo.org/x/go-llvm" ) @@ -10,7 +11,7 @@ import ( func TestInterfaceLowering(t *testing.T) { t.Parallel() testTransform(t, "testdata/interface", func(mod llvm.Module) { - err := transform.LowerInterfaces(mod, 0) + err := transform.LowerInterfaces(mod, &compileopts.Config{Options: &compileopts.Options{Opt: "2"}}) if err != nil { t.Error(err) } diff --git a/transform/interrupt.go b/transform/interrupt.go index 8cfaf0cb6..11fab18ce 100644 --- a/transform/interrupt.go +++ b/transform/interrupt.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/tinygo-org/tinygo/compileopts" "tinygo.org/x/go-llvm" ) @@ -22,7 +23,7 @@ import ( // simply call the registered handlers. This might seem like it causes extra // overhead, but in fact inlining and const propagation will eliminate most if // not all of that. -func LowerInterrupts(mod llvm.Module, sizeLevel int) []error { +func LowerInterrupts(mod llvm.Module, config *compileopts.Config) []error { var errs []error // Discover interrupts. The runtime/interrupt.Register call is a compiler @@ -174,9 +175,7 @@ func LowerInterrupts(mod llvm.Module, sizeLevel int) []error { // Create the wrapper function which is the actual interrupt handler // that is inserted in the interrupt vector. fn.SetUnnamedAddr(true) - if sizeLevel >= 2 { - fn.AddFunctionAttr(ctx.CreateEnumAttribute(llvm.AttributeKindID("optsize"), 0)) - } + AddStandardAttributes(fn, config) fn.SetSection(".text." + name) if isSoftwareVectored { fn.SetLinkage(llvm.InternalLinkage) diff --git a/transform/interrupt_test.go b/transform/interrupt_test.go index 1740f4858..4cf7e6de1 100644 --- a/transform/interrupt_test.go +++ b/transform/interrupt_test.go @@ -3,6 +3,7 @@ package transform_test import ( "testing" + "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/transform" "tinygo.org/x/go-llvm" ) @@ -12,7 +13,7 @@ func TestInterruptLowering(t *testing.T) { for _, subtest := range []string{"avr", "cortexm"} { t.Run(subtest, func(t *testing.T) { testTransform(t, "testdata/interrupt-"+subtest, func(mod llvm.Module) { - errs := transform.LowerInterrupts(mod, 0) + errs := transform.LowerInterrupts(mod, &compileopts.Config{Options: &compileopts.Options{Opt: "2"}}) if len(errs) != 0 { t.Fail() for _, err := range errs { diff --git a/transform/optimizer.go b/transform/optimizer.go index 91e446045..ec48a1192 100644 --- a/transform/optimizer.go +++ b/transform/optimizer.go @@ -68,12 +68,12 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i OptimizeStringToBytes(mod) OptimizeReflectImplements(mod) OptimizeAllocs(mod, nil, nil) - err := LowerInterfaces(mod, sizeLevel) + err := LowerInterfaces(mod, config) if err != nil { return []error{err} } - errs := LowerInterrupts(mod, sizeLevel) + errs := LowerInterrupts(mod, config) if len(errs) > 0 { return errs } @@ -97,7 +97,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i } else { // Must be run at any optimization level. - err := LowerInterfaces(mod, sizeLevel) + err := LowerInterfaces(mod, config) if err != nil { return []error{err} } @@ -105,7 +105,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i if config.FuncImplementation() == "switch" { LowerFuncValues(mod) } - errs := LowerInterrupts(mod, sizeLevel) + errs := LowerInterrupts(mod, config) if len(errs) > 0 { return errs } diff --git a/transform/transform.go b/transform/transform.go index 226d69781..441f9efa7 100644 --- a/transform/transform.go +++ b/transform/transform.go @@ -11,3 +11,18 @@ // lowering pass, which replaces stub runtime calls to get an interface method // with the method implementation (either a direct call or a thunk). package transform + +import ( + "github.com/tinygo-org/tinygo/compileopts" + "tinygo.org/x/go-llvm" +) + +// AddStandardAttributes is a helper function to add standard function +// attributes to a function. For example, it adds optsize when requested from +// the -opt= compiler flag. +func AddStandardAttributes(fn llvm.Value, config *compileopts.Config) { + _, sizeLevel, _ := config.OptLevels() + if sizeLevel >= 2 { + fn.AddFunctionAttr(fn.Type().Context().CreateEnumAttribute(llvm.AttributeKindID("optsize"), 0)) + } +} diff --git a/transform/wasm-abi.go b/transform/wasm-abi.go index 7c4d37009..46639c229 100644 --- a/transform/wasm-abi.go +++ b/transform/wasm-abi.go @@ -4,6 +4,7 @@ import ( "errors" "strings" + "github.com/tinygo-org/tinygo/compileopts" "tinygo.org/x/go-llvm" ) @@ -15,7 +16,7 @@ import ( // // This pass can be enabled/disabled with the -wasm-abi flag, and is enabled by // default as of december 2019. -func ExternalInt64AsPtr(mod llvm.Module) error { +func ExternalInt64AsPtr(mod llvm.Module, config *compileopts.Config) error { ctx := mod.Context() builder := ctx.NewBuilder() defer builder.Dispose() @@ -79,10 +80,7 @@ func ExternalInt64AsPtr(mod llvm.Module) error { fn.SetName(name + "$i64wrap") externalFnType := llvm.FunctionType(returnType, paramTypes, fnType.IsFunctionVarArg()) externalFn := llvm.AddFunction(mod, name, externalFnType) - optsize := fn.GetEnumFunctionAttribute(llvm.AttributeKindID("optsize")) - if !optsize.IsNil() { - fn.AddFunctionAttr(optsize) - } + AddStandardAttributes(fn, config) if fn.IsDeclaration() { // Just a declaration: the definition doesn't exist on the Go side diff --git a/transform/wasm-abi_test.go b/transform/wasm-abi_test.go index a09129283..9895e97fa 100644 --- a/transform/wasm-abi_test.go +++ b/transform/wasm-abi_test.go @@ -3,6 +3,7 @@ package transform_test import ( "testing" + "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/transform" "tinygo.org/x/go-llvm" ) @@ -11,7 +12,7 @@ func TestWasmABI(t *testing.T) { t.Parallel() testTransform(t, "testdata/wasm-abi", func(mod llvm.Module) { // Run ABI change pass. - err := transform.ExternalInt64AsPtr(mod) + err := transform.ExternalInt64AsPtr(mod, &compileopts.Config{Options: &compileopts.Options{Opt: "2"}}) if err != nil { t.Errorf("failed to change wasm ABI: %v", err) } |