mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-22 05:19:03 +00:00
cgo: add //go: pragmas to generated functions and globals
This patch adds //go: pragmas directly to declared functions and globals found during CGo processing. This simplifies the logic in the compiler: it no longer has to consider special "C." prefixed function names. It also makes the cgo pass more flexible in the pragmas it emits for functions and global variables.
This commit is contained in:
committed by
Ron Evans
parent
a536ddcda8
commit
1789570f52
+22
-10
@@ -496,6 +496,14 @@ func (p *cgoPackage) addFuncDecls() {
|
|||||||
}
|
}
|
||||||
args := make([]*ast.Field, len(fn.args))
|
args := make([]*ast.Field, len(fn.args))
|
||||||
decl := &ast.FuncDecl{
|
decl := &ast.FuncDecl{
|
||||||
|
Doc: &ast.CommentGroup{
|
||||||
|
List: []*ast.Comment{
|
||||||
|
{
|
||||||
|
Slash: fn.pos - 1,
|
||||||
|
Text: "//export " + name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: fn.pos,
|
NamePos: fn.pos,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
@@ -512,14 +520,10 @@ func (p *cgoPackage) addFuncDecls() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
if fn.variadic {
|
if fn.variadic {
|
||||||
decl.Doc = &ast.CommentGroup{
|
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||||
List: []*ast.Comment{
|
Slash: fn.pos - 1,
|
||||||
{
|
Text: "//go:variadic",
|
||||||
Slash: fn.pos,
|
})
|
||||||
Text: "//go:variadic",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
obj.Decl = decl
|
obj.Decl = decl
|
||||||
for i, arg := range fn.args {
|
for i, arg := range fn.args {
|
||||||
@@ -652,13 +656,21 @@ func (p *cgoPackage) addVarDecls() {
|
|||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
|
global := p.globals[name]
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: token.NoPos,
|
TokPos: global.pos,
|
||||||
Tok: token.VAR,
|
Tok: token.VAR,
|
||||||
Lparen: token.NoPos,
|
Lparen: token.NoPos,
|
||||||
Rparen: token.NoPos,
|
Rparen: token.NoPos,
|
||||||
|
Doc: &ast.CommentGroup{
|
||||||
|
List: []*ast.Comment{
|
||||||
|
{
|
||||||
|
Slash: global.pos - 1,
|
||||||
|
Text: "//go:extern " + name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
global := p.globals[name]
|
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Var,
|
Kind: ast.Var,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
|
|||||||
+8
-1
@@ -30,7 +30,14 @@ func normalizeResult(result string) string {
|
|||||||
func TestCGo(t *testing.T) {
|
func TestCGo(t *testing.T) {
|
||||||
var cflags = []string{"--target=armv6m-unknown-unknown-eabi"}
|
var cflags = []string{"--target=armv6m-unknown-unknown-eabi"}
|
||||||
|
|
||||||
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
|
for _, name := range []string{
|
||||||
|
"basic",
|
||||||
|
"errors",
|
||||||
|
"types",
|
||||||
|
"symbols",
|
||||||
|
"flags",
|
||||||
|
"const",
|
||||||
|
} {
|
||||||
name := name // avoid a race condition
|
name := name // avoid a race condition
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
// Skip tests that require specific Go version.
|
// Skip tests that require specific Go version.
|
||||||
|
|||||||
Vendored
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Function signatures.
|
||||||
|
int foo(int a, int b);
|
||||||
|
void variadic0();
|
||||||
|
void variadic2(int x, int y, ...);
|
||||||
|
|
||||||
|
// Global variable signatures.
|
||||||
|
extern int someValue;
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// Test function signatures.
|
||||||
|
func accessFunctions() {
|
||||||
|
C.foo(3, 4)
|
||||||
|
C.variadic0()
|
||||||
|
C.variadic2(3, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func accessGlobals() {
|
||||||
|
_ = C.someValue
|
||||||
|
}
|
||||||
Vendored
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
//export foo
|
||||||
|
func C.foo(a C.int, b C.int) C.int
|
||||||
|
|
||||||
|
//export variadic0
|
||||||
|
//go:variadic
|
||||||
|
func C.variadic0()
|
||||||
|
|
||||||
|
//export variadic2
|
||||||
|
//go:variadic
|
||||||
|
func C.variadic2(x C.int, y C.int)
|
||||||
|
|
||||||
|
var C.foo$funcaddr unsafe.Pointer
|
||||||
|
var C.variadic0$funcaddr unsafe.Pointer
|
||||||
|
var C.variadic2$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
|
//go:extern someValue
|
||||||
|
var C.someValue C.int
|
||||||
|
|
||||||
|
type C.int16_t = int16
|
||||||
|
type C.int32_t = int32
|
||||||
|
type C.int64_t = int64
|
||||||
|
type C.int8_t = int8
|
||||||
|
type C.uint16_t = uint16
|
||||||
|
type C.uint32_t = uint32
|
||||||
|
type C.uint64_t = uint64
|
||||||
|
type C.uint8_t = uint8
|
||||||
|
type C.uintptr_t = uintptr
|
||||||
|
type C.char uint8
|
||||||
|
type C.int int32
|
||||||
|
type C.long int32
|
||||||
|
type C.longlong int64
|
||||||
|
type C.schar int8
|
||||||
|
type C.short int16
|
||||||
|
type C.uchar uint8
|
||||||
|
type C.uint uint32
|
||||||
|
type C.ulong uint32
|
||||||
|
type C.ulonglong uint64
|
||||||
|
type C.ushort uint16
|
||||||
Vendored
-10
@@ -104,10 +104,6 @@ typedef struct {
|
|||||||
unsigned char e : 3;
|
unsigned char e : 3;
|
||||||
// Note that C++ allows bitfields bigger than the underlying type.
|
// Note that C++ allows bitfields bigger than the underlying type.
|
||||||
} bitfield_t;
|
} bitfield_t;
|
||||||
|
|
||||||
// Function signatures.
|
|
||||||
void variadic0();
|
|
||||||
void variadic2(int x, int y, ...);
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
@@ -171,9 +167,3 @@ func accessUnion() {
|
|||||||
var _ *C.int = union2d.unionfield_i()
|
var _ *C.int = union2d.unionfield_i()
|
||||||
var _ *[2]float64 = union2d.unionfield_d()
|
var _ *[2]float64 = union2d.unionfield_d()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test function signatures.
|
|
||||||
func accessFunctions() {
|
|
||||||
C.variadic0()
|
|
||||||
C.variadic2(3, 5)
|
|
||||||
}
|
|
||||||
|
|||||||
Vendored
-5
@@ -4,11 +4,6 @@ import "unsafe"
|
|||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
func C.variadic0() //go:variadic
|
|
||||||
func C.variadic2(x C.int, y C.int) //go:variadic
|
|
||||||
var C.variadic0$funcaddr unsafe.Pointer
|
|
||||||
var C.variadic2$funcaddr unsafe.Pointer
|
|
||||||
|
|
||||||
const C.option2A = 20
|
const C.option2A = 20
|
||||||
const C.optionA = 0
|
const C.optionA = 0
|
||||||
const C.optionB = 1
|
const C.optionB = 1
|
||||||
|
|||||||
@@ -2802,9 +2802,8 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
|||||||
// var C.add unsafe.Pointer
|
// var C.add unsafe.Pointer
|
||||||
// Instead of a load from the global, create a bitcast of the
|
// Instead of a load from the global, create a bitcast of the
|
||||||
// function pointer itself.
|
// function pointer itself.
|
||||||
globalName := b.getGlobalInfo(unop.X.(*ssa.Global)).linkName
|
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
|
||||||
name := globalName[:len(globalName)-len("$funcaddr")]
|
fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function))
|
||||||
fn := b.getFunction(b.fn.Pkg.Members["C."+name].(*ssa.Function))
|
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
|
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-20
@@ -208,14 +208,9 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
// present in *ssa.Function, such as the link name and whether it should be
|
// present in *ssa.Function, such as the link name and whether it should be
|
||||||
// exported.
|
// exported.
|
||||||
func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
||||||
info := functionInfo{}
|
info := functionInfo{
|
||||||
if strings.HasPrefix(f.Name(), "C.") {
|
|
||||||
// Created by CGo: such a name cannot be created by regular C code.
|
|
||||||
info.linkName = f.Name()[2:]
|
|
||||||
info.exported = true
|
|
||||||
} else {
|
|
||||||
// Pick the default linkName.
|
// Pick the default linkName.
|
||||||
info.linkName = f.RelString(nil)
|
linkName: f.RelString(nil),
|
||||||
}
|
}
|
||||||
// Check for //go: pragmas, which may change the link name (among others).
|
// Check for //go: pragmas, which may change the link name (among others).
|
||||||
info.parsePragmas(f)
|
info.parsePragmas(f)
|
||||||
@@ -460,20 +455,14 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
|||||||
|
|
||||||
// getGlobalInfo returns some information about a specific global.
|
// getGlobalInfo returns some information about a specific global.
|
||||||
func (c *compilerContext) getGlobalInfo(g *ssa.Global) globalInfo {
|
func (c *compilerContext) getGlobalInfo(g *ssa.Global) globalInfo {
|
||||||
info := globalInfo{}
|
info := globalInfo{
|
||||||
if strings.HasPrefix(g.Name(), "C.") {
|
|
||||||
// Created by CGo: such a name cannot be created by regular C code.
|
|
||||||
info.linkName = g.Name()[2:]
|
|
||||||
info.extern = true
|
|
||||||
} else {
|
|
||||||
// Pick the default linkName.
|
// Pick the default linkName.
|
||||||
info.linkName = g.RelString(nil)
|
linkName: g.RelString(nil),
|
||||||
// Check for //go: pragmas, which may change the link name (among
|
}
|
||||||
// others).
|
// Check for //go: pragmas, which may change the link name (among others).
|
||||||
doc := c.astComments[info.linkName]
|
doc := c.astComments[info.linkName]
|
||||||
if doc != nil {
|
if doc != nil {
|
||||||
info.parsePragmas(doc)
|
info.parsePragmas(doc)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user