mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-18 03:24:00 +00:00
compiler: add //go:noescape pragma
This only works on declarations, not definitions. This is intentional: it follows the upstream Go implemetation. However, we might want to loosen this requirement at some point: TinyGo sometimes stores pointers in memory mapped I/O knowing they won't actually escape, but the compiler doesn't know about this.
This commit is contained in:
committed by
Ron Evans
parent
d1fe02df23
commit
fd625f7265
+7
-5
@@ -19,8 +19,9 @@ const maxFieldsPerParam = 3
|
|||||||
// useful while declaring or defining a function.
|
// useful while declaring or defining a function.
|
||||||
type paramInfo struct {
|
type paramInfo struct {
|
||||||
llvmType llvm.Type
|
llvmType llvm.Type
|
||||||
name string // name, possibly with suffixes for e.g. struct fields
|
name string // name, possibly with suffixes for e.g. struct fields
|
||||||
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
|
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
|
||||||
|
flags paramFlags // extra flags for this parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
||||||
@@ -28,9 +29,9 @@ type paramInfo struct {
|
|||||||
type paramFlags uint8
|
type paramFlags uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Parameter may have the deferenceable_or_null attribute. This attribute
|
// Whether this is a full or partial Go parameter (int, slice, etc).
|
||||||
// cannot be applied to unsafe.Pointer and to the data pointer of slices.
|
// The extra context parameter is not a Go parameter.
|
||||||
paramIsDeferenceableOrNull = 1 << iota
|
paramIsGoParam = 1 << iota
|
||||||
)
|
)
|
||||||
|
|
||||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||||
@@ -195,6 +196,7 @@ func (c *compilerContext) getParamInfo(t llvm.Type, name string, goType types.Ty
|
|||||||
info := paramInfo{
|
info := paramInfo{
|
||||||
llvmType: t,
|
llvmType: t,
|
||||||
name: name,
|
name: name,
|
||||||
|
flags: paramIsGoParam,
|
||||||
}
|
}
|
||||||
if goType != nil {
|
if goType != nil {
|
||||||
switch underlying := goType.Underlying().(type) {
|
switch underlying := goType.Underlying().(type) {
|
||||||
|
|||||||
+20
-3
@@ -33,6 +33,7 @@ type functionInfo struct {
|
|||||||
exported bool // go:export, CGo
|
exported bool // go:export, CGo
|
||||||
interrupt bool // go:interrupt
|
interrupt bool // go:interrupt
|
||||||
nobounds bool // go:nobounds
|
nobounds bool // go:nobounds
|
||||||
|
noescape bool // go:noescape
|
||||||
variadic bool // go:variadic (CGo only)
|
variadic bool // go:variadic (CGo only)
|
||||||
inline inlineType // go:inline
|
inline inlineType // go:inline
|
||||||
}
|
}
|
||||||
@@ -127,11 +128,20 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
c.addStandardDeclaredAttributes(llvmFn)
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
|
|
||||||
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
||||||
for i, info := range paramInfos {
|
for i, paramInfo := range paramInfos {
|
||||||
if info.elemSize != 0 {
|
if paramInfo.elemSize != 0 {
|
||||||
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, info.elemSize)
|
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, paramInfo.elemSize)
|
||||||
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
|
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
|
||||||
}
|
}
|
||||||
|
if info.noescape && paramInfo.flags¶mIsGoParam != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
|
||||||
|
// Parameters to functions with a //go:noescape parameter should get
|
||||||
|
// the nocapture attribute. However, the context parameter should
|
||||||
|
// not.
|
||||||
|
// (It may be safe to add the nocapture parameter to the context
|
||||||
|
// parameter, but I'd like to stay on the safe side here).
|
||||||
|
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
||||||
|
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a number of function or parameter attributes, depending on the
|
// Set a number of function or parameter attributes, depending on the
|
||||||
@@ -394,6 +404,13 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||||
info.nobounds = true
|
info.nobounds = true
|
||||||
}
|
}
|
||||||
|
case "//go:noescape":
|
||||||
|
// Don't let pointer parameters escape.
|
||||||
|
// Following the upstream Go implementation, we only do this for
|
||||||
|
// declarations, not definitions.
|
||||||
|
if len(f.Blocks) == 0 {
|
||||||
|
info.noescape = true
|
||||||
|
}
|
||||||
case "//go:variadic":
|
case "//go:variadic":
|
||||||
// The //go:variadic pragma is emitted by the CGo preprocessing
|
// The //go:variadic pragma is emitted by the CGo preprocessing
|
||||||
// pass for C variadic functions. This includes both explicit
|
// pass for C variadic functions. This includes both explicit
|
||||||
|
|||||||
Vendored
+9
@@ -106,3 +106,12 @@ var undefinedGlobalNotInSection uint32
|
|||||||
//go:align 1024
|
//go:align 1024
|
||||||
//go:section .global_section
|
//go:section .global_section
|
||||||
var multipleGlobalPragmas uint32
|
var multipleGlobalPragmas uint32
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func doesNotEscapeParam(a *int, b []int, c chan int, d *[0]byte)
|
||||||
|
|
||||||
|
// The //go:noescape pragma only works on declarations, not definitions.
|
||||||
|
//
|
||||||
|
//go:noescape
|
||||||
|
func stillEscapes(a *int, b []int, c chan int, d *[0]byte) {
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+8
@@ -85,6 +85,14 @@ entry:
|
|||||||
|
|
||||||
declare void @main.undefinedFunctionNotInSection(ptr) #1
|
declare void @main.undefinedFunctionNotInSection(ptr) #1
|
||||||
|
|
||||||
|
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(32), ptr nocapture, ptr) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(32) %c, ptr %d, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
|||||||
Reference in New Issue
Block a user