diff --git a/compiler/compiler.go b/compiler/compiler.go index a34d9848..cc696d5b 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 6eec892d..49ccfa20 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 505e78a9..b6ebc83b 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 0fdc753e..0515098c 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 +@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4 +@undefinedGlobalNotInSection = external global i32, align 4 +@main.multipleGlobalPragmas = 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 2d0349e9..d147062b 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) }