From cf640290a3b731e7c68c8976247a6fd9ec0f9868 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 7 Nov 2021 15:33:44 +0100 Subject: [PATCH] compiler: add "target-cpu" and "target-features" attributes This matches Clang, and with that, it adds support for inlining between Go and C because LLVM only allows inlining if the "target-cpu" and "target-features" string attributes match. For example, take a look at the following code: // int add(int a, int b) { // return a + b; // } import "C" func main() { println(C.add(3, 5)) } The 'add' function is not inlined into the main function before this commit, but after it, it can be inlined and trivially be optimized to `println(8)`. --- compiler/compiler.go | 2 +- compiler/symbol.go | 6 ++++++ transform/interface-lowering_test.go | 3 +-- transform/transform.go | 11 +++++++++-- transform/transform_test.go | 5 +++++ transform/wasm-abi_test.go | 3 +-- 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index 3d56fb1b..c279800c 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -23,7 +23,7 @@ import ( // Version of the compiler pacakge. Must be incremented each time the compiler // package changes in a way that affects the generated LLVM module. // This version is independent of the TinyGo version number. -const Version = 24 // last change: add layout param to runtime.alloc calls +const Version = 25 // last change: add "target-cpu" and "target-features" attributes func init() { llvm.InitializeAllTargets() diff --git a/compiler/symbol.go b/compiler/symbol.go index 902277a5..1a95ec33 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -346,6 +346,12 @@ func (c *compilerContext) addStandardDeclaredAttributes(llvmFn llvm.Value) { attr := c.ctx.CreateEnumAttribute(kind, 0) llvmFn.AddFunctionAttr(attr) } + if c.CPU != "" { + llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("target-cpu", c.CPU)) + } + if c.Features != "" { + llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("target-features", c.Features)) + } } // addStandardDefinedAttributes adds the set of attributes that are added to diff --git a/transform/interface-lowering_test.go b/transform/interface-lowering_test.go index 1dd635a3..7bcce605 100644 --- a/transform/interface-lowering_test.go +++ b/transform/interface-lowering_test.go @@ -3,7 +3,6 @@ package transform_test import ( "testing" - "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/transform" "tinygo.org/x/go-llvm" ) @@ -11,7 +10,7 @@ import ( func TestInterfaceLowering(t *testing.T) { t.Parallel() testTransform(t, "testdata/interface", func(mod llvm.Module) { - err := transform.LowerInterfaces(mod, &compileopts.Config{Options: &compileopts.Options{Opt: "2"}}) + err := transform.LowerInterfaces(mod, defaultTestConfig) if err != nil { t.Error(err) } diff --git a/transform/transform.go b/transform/transform.go index 3027f913..ab08317e 100644 --- a/transform/transform.go +++ b/transform/transform.go @@ -21,11 +21,18 @@ import ( // attributes to a function. For example, it adds optsize when requested from // the -opt= compiler flag. func AddStandardAttributes(fn llvm.Value, config *compileopts.Config) { + ctx := fn.Type().Context() _, sizeLevel, _ := config.OptLevels() if sizeLevel >= 1 { - fn.AddFunctionAttr(fn.Type().Context().CreateEnumAttribute(llvm.AttributeKindID("optsize"), 0)) + fn.AddFunctionAttr(ctx.CreateEnumAttribute(llvm.AttributeKindID("optsize"), 0)) } if sizeLevel >= 2 { - fn.AddFunctionAttr(fn.Type().Context().CreateEnumAttribute(llvm.AttributeKindID("minsize"), 0)) + fn.AddFunctionAttr(ctx.CreateEnumAttribute(llvm.AttributeKindID("minsize"), 0)) + } + if config.CPU() != "" { + fn.AddFunctionAttr(ctx.CreateStringAttribute("target-cpu", config.CPU())) + } + if config.Features() != "" { + fn.AddFunctionAttr(ctx.CreateStringAttribute("target-features", config.Features())) } } diff --git a/transform/transform_test.go b/transform/transform_test.go index a578c396..ae531eeb 100644 --- a/transform/transform_test.go +++ b/transform/transform_test.go @@ -21,6 +21,11 @@ import ( var update = flag.Bool("update", false, "update transform package tests") +var defaultTestConfig = &compileopts.Config{ + Target: &compileopts.TargetSpec{}, + Options: &compileopts.Options{Opt: "2"}, +} + // testTransform runs a transformation pass on an input file (pathPrefix+".ll") // and checks whether it matches the expected output (pathPrefix+".out.ll"). The // output is compared with a fuzzy match that ignores some irrelevant lines such diff --git a/transform/wasm-abi_test.go b/transform/wasm-abi_test.go index 9895e97f..374bba13 100644 --- a/transform/wasm-abi_test.go +++ b/transform/wasm-abi_test.go @@ -3,7 +3,6 @@ package transform_test import ( "testing" - "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/transform" "tinygo.org/x/go-llvm" ) @@ -12,7 +11,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, &compileopts.Config{Options: &compileopts.Options{Opt: "2"}}) + err := transform.ExternalInt64AsPtr(mod, defaultTestConfig) if err != nil { t.Errorf("failed to change wasm ABI: %v", err) }