diff options
author | Ayke van Laethem <[email protected]> | 2021-03-30 14:09:23 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-06-24 15:00:30 +0200 |
commit | 2bb70812a8cc0562204622689652132938567a81 (patch) | |
tree | 89eb666457b3c8d2ea248689d37331b1177c30bc | |
parent | 293f4ea7bc9858c219c75bd3c2bc88dea1d42b99 (diff) | |
download | tinygo-2bb70812a8cc0562204622689652132938567a81.tar.gz tinygo-2bb70812a8cc0562204622689652132938567a81.zip |
compiler: add function and global section pragmas
This patch adds a new pragma for functions and globals to set the
section name. This can be useful to place a function or global in a
special device specific section, for example:
* Functions may be placed in RAM to make them run faster, or in flash
(if RAM is the default) to not let them take up RAM.
* DMA memory may only be placed in a special memory area.
* Some RAM may be faster than other RAM, and some globals may be
performance critical thus placing them in this special RAM area can
help.
* Some (large) global variables may need to be placed in external RAM,
which can be done by placing them in a special section.
To use it, you have to place a function or global in a special section,
for example:
//go:section .externalram
var externalRAMBuffer [1024]byte
This can then be placed in a special section of the linker script, for
example something like this:
.bss.extram (NOLOAD) : {
*(.externalram)
} > ERAM
-rw-r--r-- | compiler/compiler.go | 6 | ||||
-rw-r--r-- | compiler/symbol.go | 10 | ||||
-rw-r--r-- | compiler/testdata/pragma.go | 25 | ||||
-rw-r--r-- | compiler/testdata/pragma.ll | 16 | ||||
-rw-r--r-- | transform/globals.go | 2 |
5 files changed, 58 insertions, 1 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go index a34d98483..cc696d5b0 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -762,6 +762,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package if !info.extern { global.SetInitializer(llvm.ConstNull(global.Type().ElementType())) global.SetVisibility(llvm.HiddenVisibility) + if info.section != "" { + global.SetSection(info.section) + } } } } @@ -787,6 +790,9 @@ func (b *builder) createFunction() { b.llvmFn.SetVisibility(llvm.HiddenVisibility) b.llvmFn.SetUnnamedAddr(true) } + if b.info.section != "" { + b.llvmFn.SetSection(b.info.section) + } if b.info.exported && strings.HasPrefix(b.Triple, "wasm") { // Set the exported name. This is necessary for WebAssembly because // otherwise the function is not exported. diff --git a/compiler/symbol.go b/compiler/symbol.go index 6eec892dd..49ccfa20d 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -24,6 +24,7 @@ type functionInfo struct { module string // go:wasm-module importName string // go:linkname, go:export - The name the developer assigns linkName string // go:linkname, go:export - The name that we map for the particular module -> importName + section string // go:section - object file section name exported bool // go:export, CGo nobounds bool // go:nobounds variadic bool // go:variadic (CGo only) @@ -270,6 +271,10 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { if hasUnsafeImport(f.Pkg.Pkg) { info.linkName = parts[2] } + case "//go:section": + if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) { + info.section = parts[1] + } case "//go:nobounds": // Skip bounds checking in this function. Useful for some // runtime functions. @@ -325,6 +330,7 @@ type globalInfo struct { linkName string // go:extern extern bool // go:extern align int // go:align + section string // go:section } // loadASTComments loads comments on globals from the AST, for use later in the @@ -438,6 +444,10 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) { if err == nil { info.align = align } + case "//go:section": + if len(parts) == 2 { + info.section = parts[1] + } } } } diff --git a/compiler/testdata/pragma.go b/compiler/testdata/pragma.go index 505e78a9f..b6ebc83b2 100644 --- a/compiler/testdata/pragma.go +++ b/compiler/testdata/pragma.go @@ -39,3 +39,28 @@ func inlineFunc() { //go:noinline func noinlineFunc() { } + +// This function should have the specified section. +//go:section .special_function_section +func functionInSection() { +} + +//export exportedFunctionInSection +//go:section .special_function_section +func exportedFunctionInSection() { +} + +// This function should not: it's only a declaration and not a definition. +//go:section .special_function_section +func undefinedFunctionNotInSection() + +//go:section .special_global_section +var globalInSection uint32 + +//go:section .special_global_section +//go:extern undefinedGlobalNotInSection +var undefinedGlobalNotInSection uint32 + +//go:align 1024 +//go:section .global_section +var multipleGlobalPragmas uint32 diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll index 0fdc753e5..0515098c5 100644 --- a/compiler/testdata/pragma.ll +++ b/compiler/testdata/pragma.ll @@ -6,6 +6,9 @@ target triple = "wasm32--wasi" @extern_global = external global [0 x i8], align 1 @main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32 @main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16 [email protected] = hidden global i32 0, section ".special_global_section", align 4 +@undefinedGlobalNotInSection = external global i32, align 4 [email protected] = hidden global i32 0, section ".global_section", align 1024 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) @@ -38,6 +41,19 @@ entry: ret void } +define hidden void @main.functionInSection(i8* %context, i8* %parentHandle) unnamed_addr section ".special_function_section" { +entry: + ret void +} + +define void @exportedFunctionInSection() #3 section ".special_function_section" { +entry: + ret void +} + +declare void @main.undefinedFunctionNotInSection(i8*, i8*) + attributes #0 = { "wasm-export-name"="extern_func" } attributes #1 = { inlinehint } attributes #2 = { noinline } +attributes #3 = { "wasm-export-name"="exportedFunctionInSection" } diff --git a/transform/globals.go b/transform/globals.go index 2d0349e9f..d147062bf 100644 --- a/transform/globals.go +++ b/transform/globals.go @@ -11,7 +11,7 @@ import "tinygo.org/x/go-llvm" func ApplyFunctionSections(mod llvm.Module) { llvmFn := mod.FirstFunction() for !llvmFn.IsNil() { - if !llvmFn.IsDeclaration() { + if !llvmFn.IsDeclaration() && llvmFn.Section() == "" { name := llvmFn.Name() llvmFn.SetSection(".text." + name) } |