mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-16 02:33:28 +00:00
cgo: add support for #cgo noescape lines
Here is the proposal: https://github.com/golang/go/issues/56378 They are documented here: https://pkg.go.dev/cmd/cgo@master#hdr-Optimizing_calls_of_C_code This would have been very useful to fix https://github.com/tinygo-org/bluetooth/issues/176 in a nice way. That bug is now fixed in a different way using a wrapper function, but once this new noescape pragma gets included in TinyGo we could remove the workaround and use `#cgo noescape` instead.
This commit is contained in:
committed by
Ron Evans
parent
fd625f7265
commit
b7a3fd8d2f
+53
@@ -18,6 +18,7 @@ import (
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -42,6 +43,7 @@ type cgoPackage struct {
|
||||
fset *token.FileSet
|
||||
tokenFiles map[string]*token.File
|
||||
definedGlobally map[string]ast.Node
|
||||
noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
|
||||
anonDecls map[interface{}]string
|
||||
cflags []string // CFlags from #cgo lines
|
||||
ldflags []string // LDFlags from #cgo lines
|
||||
@@ -80,6 +82,13 @@ type bitfieldInfo struct {
|
||||
endBit int64 // may be 0 meaning "until the end of the field"
|
||||
}
|
||||
|
||||
// Information about a #cgo noescape line in the source code.
|
||||
type noescapingFunc struct {
|
||||
name string
|
||||
pos token.Pos
|
||||
used bool // true if used somewhere in the source (for proper error reporting)
|
||||
}
|
||||
|
||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||
// in both languages. See addTypeAliases.
|
||||
var cgoAliases = map[string]string{
|
||||
@@ -255,6 +264,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
fset: fset,
|
||||
tokenFiles: map[string]*token.File{},
|
||||
definedGlobally: map[string]ast.Node{},
|
||||
noescapingFuncs: map[string]*noescapingFunc{},
|
||||
anonDecls: map[interface{}]string{},
|
||||
visitedFiles: map[string][]byte{},
|
||||
}
|
||||
@@ -418,6 +428,22 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
})
|
||||
}
|
||||
|
||||
// Show an error when a #cgo noescape line isn't used in practice.
|
||||
// This matches upstream Go. I think the goal is to avoid issues with
|
||||
// misspelled function names, which seems very useful.
|
||||
var unusedNoescapeLines []*noescapingFunc
|
||||
for _, value := range p.noescapingFuncs {
|
||||
if !value.used {
|
||||
unusedNoescapeLines = append(unusedNoescapeLines, value)
|
||||
}
|
||||
}
|
||||
sort.SliceStable(unusedNoescapeLines, func(i, j int) bool {
|
||||
return unusedNoescapeLines[i].pos < unusedNoescapeLines[j].pos
|
||||
})
|
||||
for _, value := range unusedNoescapeLines {
|
||||
p.addError(value.pos, fmt.Sprintf("function %#v in #cgo noescape line is not used", value.name))
|
||||
}
|
||||
|
||||
// Print the newly generated in-memory AST, for debugging.
|
||||
//ast.Print(fset, p.generated)
|
||||
|
||||
@@ -483,6 +509,33 @@ func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) strin
|
||||
}
|
||||
text = text[:lineStart] + string(spaces) + text[lineEnd:]
|
||||
|
||||
allFields := strings.Fields(line[4:])
|
||||
switch allFields[0] {
|
||||
case "noescape":
|
||||
// The code indicates that pointer parameters will not be captured
|
||||
// by the called C function.
|
||||
if len(allFields) < 2 {
|
||||
p.addErrorAfter(pos, text[:lineStart], "missing function name in #cgo noescape line")
|
||||
continue
|
||||
}
|
||||
if len(allFields) > 2 {
|
||||
p.addErrorAfter(pos, text[:lineStart], "multiple function names in #cgo noescape line")
|
||||
continue
|
||||
}
|
||||
name := allFields[1]
|
||||
p.noescapingFuncs[name] = &noescapingFunc{
|
||||
name: name,
|
||||
pos: pos,
|
||||
used: false,
|
||||
}
|
||||
continue
|
||||
case "nocallback":
|
||||
// We don't do anything special when calling a C function, so there
|
||||
// appears to be no optimization that we can do here.
|
||||
// Accept, but ignore the parameter for compatibility.
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the text before the colon in the #cgo directive.
|
||||
colon := strings.IndexByte(line, ':')
|
||||
if colon < 0 {
|
||||
|
||||
Reference in New Issue
Block a user