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.
Этот коммит содержится в:
родитель
a536ddcda8
коммит
1789570f52
8 изменённых файлов: 108 добавлений и 49 удалений
32
cgo/cgo.go
32
cgo/cgo.go
|
@ -496,6 +496,14 @@ func (p *cgoPackage) addFuncDecls() {
|
|||
}
|
||||
args := make([]*ast.Field, len(fn.args))
|
||||
decl := &ast.FuncDecl{
|
||||
Doc: &ast.CommentGroup{
|
||||
List: []*ast.Comment{
|
||||
{
|
||||
Slash: fn.pos - 1,
|
||||
Text: "//export " + name,
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: &ast.Ident{
|
||||
NamePos: fn.pos,
|
||||
Name: "C." + name,
|
||||
|
@ -512,14 +520,10 @@ func (p *cgoPackage) addFuncDecls() {
|
|||
},
|
||||
}
|
||||
if fn.variadic {
|
||||
decl.Doc = &ast.CommentGroup{
|
||||
List: []*ast.Comment{
|
||||
{
|
||||
Slash: fn.pos,
|
||||
Text: "//go:variadic",
|
||||
},
|
||||
},
|
||||
}
|
||||
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||
Slash: fn.pos - 1,
|
||||
Text: "//go:variadic",
|
||||
})
|
||||
}
|
||||
obj.Decl = decl
|
||||
for i, arg := range fn.args {
|
||||
|
@ -652,13 +656,21 @@ func (p *cgoPackage) addVarDecls() {
|
|||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
global := p.globals[name]
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
TokPos: global.pos,
|
||||
Tok: token.VAR,
|
||||
Lparen: 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{
|
||||
Kind: ast.Var,
|
||||
Name: "C." + name,
|
||||
|
|
|
@ -30,7 +30,14 @@ func normalizeResult(result string) string {
|
|||
func TestCGo(t *testing.T) {
|
||||
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
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Skip tests that require specific Go version.
|
||||
|
|
23
cgo/testdata/symbols.go
предоставленный
Обычный файл
23
cgo/testdata/symbols.go
предоставленный
Обычный файл
|
@ -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
|
||||
}
|
44
cgo/testdata/symbols.out.go
предоставленный
Обычный файл
44
cgo/testdata/symbols.out.go
предоставленный
Обычный файл
|
@ -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
|
10
cgo/testdata/types.go
предоставленный
10
cgo/testdata/types.go
предоставленный
|
@ -104,10 +104,6 @@ typedef struct {
|
|||
unsigned char e : 3;
|
||||
// Note that C++ allows bitfields bigger than the underlying type.
|
||||
} bitfield_t;
|
||||
|
||||
// Function signatures.
|
||||
void variadic0();
|
||||
void variadic2(int x, int y, ...);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
|
@ -171,9 +167,3 @@ func accessUnion() {
|
|||
var _ *C.int = union2d.unionfield_i()
|
||||
var _ *[2]float64 = union2d.unionfield_d()
|
||||
}
|
||||
|
||||
// Test function signatures.
|
||||
func accessFunctions() {
|
||||
C.variadic0()
|
||||
C.variadic2(3, 5)
|
||||
}
|
||||
|
|
5
cgo/testdata/types.out.go
предоставленный
5
cgo/testdata/types.out.go
предоставленный
|
@ -4,11 +4,6 @@ import "unsafe"
|
|||
|
||||
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.optionA = 0
|
||||
const C.optionB = 1
|
||||
|
|
|
@ -2802,9 +2802,8 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
|||
// var C.add unsafe.Pointer
|
||||
// Instead of a load from the global, create a bitcast of the
|
||||
// function pointer itself.
|
||||
globalName := b.getGlobalInfo(unop.X.(*ssa.Global)).linkName
|
||||
name := globalName[:len(globalName)-len("$funcaddr")]
|
||||
fn := b.getFunction(b.fn.Pkg.Members["C."+name].(*ssa.Function))
|
||||
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
|
||||
fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function))
|
||||
if fn.IsNil() {
|
||||
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// exported.
|
||||
func (c *compilerContext) getFunctionInfo(f *ssa.Function) 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 {
|
||||
info := functionInfo{
|
||||
// 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).
|
||||
info.parsePragmas(f)
|
||||
|
@ -460,20 +455,14 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
|||
|
||||
// getGlobalInfo returns some information about a specific global.
|
||||
func (c *compilerContext) getGlobalInfo(g *ssa.Global) 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 {
|
||||
info := globalInfo{
|
||||
// Pick the default linkName.
|
||||
info.linkName = g.RelString(nil)
|
||||
// Check for //go: pragmas, which may change the link name (among
|
||||
// others).
|
||||
doc := c.astComments[info.linkName]
|
||||
if doc != nil {
|
||||
info.parsePragmas(doc)
|
||||
}
|
||||
linkName: g.RelString(nil),
|
||||
}
|
||||
// Check for //go: pragmas, which may change the link name (among others).
|
||||
doc := c.astComments[info.linkName]
|
||||
if doc != nil {
|
||||
info.parsePragmas(doc)
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче