From 1789570f52598e96b1d638b9d52910d0278b8f15 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 11 Nov 2021 14:12:38 +0100 Subject: [PATCH] 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. --- cgo/cgo.go | 32 ++++++++++++++++++--------- cgo/cgo_test.go | 9 +++++++- cgo/testdata/symbols.go | 23 +++++++++++++++++++ cgo/testdata/symbols.out.go | 44 +++++++++++++++++++++++++++++++++++++ cgo/testdata/types.go | 10 --------- cgo/testdata/types.out.go | 5 ----- compiler/compiler.go | 5 ++--- compiler/symbol.go | 29 ++++++++---------------- 8 files changed, 108 insertions(+), 49 deletions(-) create mode 100644 cgo/testdata/symbols.go create mode 100644 cgo/testdata/symbols.out.go diff --git a/cgo/cgo.go b/cgo/cgo.go index dc117396..defae0bc 100644 --- a/cgo/cgo.go +++ b/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, diff --git a/cgo/cgo_test.go b/cgo/cgo_test.go index a5043712..b46c36dd 100644 --- a/cgo/cgo_test.go +++ b/cgo/cgo_test.go @@ -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. diff --git a/cgo/testdata/symbols.go b/cgo/testdata/symbols.go new file mode 100644 index 00000000..c724cd92 --- /dev/null +++ b/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 +} diff --git a/cgo/testdata/symbols.out.go b/cgo/testdata/symbols.out.go new file mode 100644 index 00000000..97a9522b --- /dev/null +++ b/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 diff --git a/cgo/testdata/types.go b/cgo/testdata/types.go index 42bd6136..50d5b8b6 100644 --- a/cgo/testdata/types.go +++ b/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) -} diff --git a/cgo/testdata/types.out.go b/cgo/testdata/types.out.go index c89e9b00..1864e33f 100644 --- a/cgo/testdata/types.out.go +++ b/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 diff --git a/compiler/compiler.go b/compiler/compiler.go index fe04ae64..c9f34cda 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -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) } diff --git a/compiler/symbol.go b/compiler/symbol.go index 1a95ec33..4218d5d5 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -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 }