aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-03-30 14:09:23 +0200
committerRon Evans <[email protected]>2021-06-24 15:00:30 +0200
commit2bb70812a8cc0562204622689652132938567a81 (patch)
tree89eb666457b3c8d2ea248689d37331b1177c30bc
parent293f4ea7bc9858c219c75bd3c2bc88dea1d42b99 (diff)
downloadtinygo-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.go6
-rw-r--r--compiler/symbol.go10
-rw-r--r--compiler/testdata/pragma.go25
-rw-r--r--compiler/testdata/pragma.ll16
-rw-r--r--transform/globals.go2
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)
}