Skip to content

Commit c18dc11

Browse files
fiamrsc
authored andcommitted
cmd/cgo: Add support for C function pointers
* Add a new kind of Name, "fpvar" which stands for function pointer variable * When walking the AST, find functions used as expressions and create a new Name object for them * Track functions which are only used in expr contexts, and avoid generating bridge code for them R=golang-dev, minux.ma, fullung, rsc, iant CC=golang-dev https://golang.org/cl/9835047
1 parent 469250f commit c18dc11

File tree

6 files changed

+148
-19
lines changed

6 files changed

+148
-19
lines changed

misc/cgo/test/cgo_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,6 @@ func Test5548(t *testing.T) { test5548(t) }
4444
func Test5603(t *testing.T) { test5603(t) }
4545
func Test3250(t *testing.T) { test3250(t) }
4646
func TestCallbackStack(t *testing.T) { testCallbackStack(t) }
47+
func TestFpVar(t *testing.T) { testFpVar(t) }
4748

4849
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }

misc/cgo/test/fpvar.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2013 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// This file contains test cases for cgo with function pointer variables.
6+
7+
package cgotest
8+
9+
/*
10+
typedef int (*intFunc) ();
11+
12+
int
13+
bridge_int_func(intFunc f)
14+
{
15+
return f();
16+
}
17+
18+
int fortytwo()
19+
{
20+
return 42;
21+
}
22+
23+
*/
24+
import "C"
25+
import "testing"
26+
27+
func callBridge(f C.intFunc) int {
28+
return int(C.bridge_int_func(f))
29+
}
30+
31+
func callCBridge(f C.intFunc) C.int {
32+
return C.bridge_int_func(f)
33+
}
34+
35+
func testFpVar(t *testing.T) {
36+
const expected = 42
37+
f := C.intFunc(C.fortytwo)
38+
res1 := C.bridge_int_func(f)
39+
if r1 := int(res1); r1 != expected {
40+
t.Errorf("got %d, want %d", r1, expected)
41+
}
42+
res2 := callCBridge(f)
43+
if r2 := int(res2); r2 != expected {
44+
t.Errorf("got %d, want %d", r2, expected)
45+
}
46+
r3 := callBridge(f)
47+
if r3 != expected {
48+
t.Errorf("got %d, want %d", r3, expected)
49+
}
50+
}

src/cmd/cgo/doc.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,33 @@ function returns void). For example:
7676
n, err := C.sqrt(-1)
7777
_, err := C.voidFunc()
7878
79+
Calling C function pointers is currently not supported, however you can
80+
declare Go variables which hold C function pointers and pass them
81+
back and forth between Go and C. C code may call function pointers
82+
received from Go. For example:
83+
84+
package main
85+
// typedef int (*intFunc) ();
86+
//
87+
// int
88+
// bridge_int_func(intFunc f)
89+
// {
90+
// return f();
91+
// }
92+
//
93+
// int fortytwo()
94+
// {
95+
// return 42;
96+
// }
97+
import "C"
98+
import "fmt"
99+
100+
func main() {
101+
f := C.intFunc(C.fortytwo)
102+
fmt.Println(int(C.bridge_int_func(f)))
103+
// Output: 42
104+
}
105+
79106
In C, a function argument written as a fixed size array
80107
actually requires a pointer to the first element of the array.
81108
C compilers are aware of this calling convention and adjust

src/cmd/cgo/gcc.go

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -548,25 +548,40 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
548548

549549
}
550550

551+
// mangleName does name mangling to translate names
552+
// from the original Go source files to the names
553+
// used in the final Go files generated by cgo.
554+
func (p *Package) mangleName(n *Name) {
555+
// When using gccgo variables have to be
556+
// exported so that they become global symbols
557+
// that the C code can refer to.
558+
prefix := "_C"
559+
if *gccgo && n.IsVar() {
560+
prefix = "C"
561+
}
562+
n.Mangle = prefix + n.Kind + "_" + n.Go
563+
}
564+
551565
// rewriteRef rewrites all the C.xxx references in f.AST to refer to the
552566
// Go equivalents, now that we have figured out the meaning of all
553567
// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names
554568
// with full definitions instead of mangled names.
555569
func (p *Package) rewriteRef(f *File) {
570+
// Keep a list of all the functions, to remove the ones
571+
// only used as expressions and avoid generating bridge
572+
// code for them.
573+
functions := make(map[string]bool)
574+
556575
// Assign mangled names.
557576
for _, n := range f.Name {
558577
if n.Kind == "not-type" {
559578
n.Kind = "var"
560579
}
561580
if n.Mangle == "" {
562-
// When using gccgo variables have to be
563-
// exported so that they become global symbols
564-
// that the C code can refer to.
565-
prefix := "_C"
566-
if *gccgo && n.Kind == "var" {
567-
prefix = "C"
568-
}
569-
n.Mangle = prefix + n.Kind + "_" + n.Go
581+
p.mangleName(n)
582+
}
583+
if n.Kind == "func" {
584+
functions[n.Go] = false
570585
}
571586
}
572587

@@ -590,6 +605,7 @@ func (p *Package) rewriteRef(f *File) {
590605
error_(r.Pos(), "call of non-function C.%s", r.Name.Go)
591606
break
592607
}
608+
functions[r.Name.Go] = true
593609
if r.Context == "call2" {
594610
// Invent new Name for the two-result function.
595611
n := f.Name["2"+r.Name.Go]
@@ -606,13 +622,26 @@ func (p *Package) rewriteRef(f *File) {
606622
}
607623
case "expr":
608624
if r.Name.Kind == "func" {
609-
error_(r.Pos(), "must call C.%s", r.Name.Go)
610-
}
611-
if r.Name.Kind == "type" {
625+
// Function is being used in an expression, to e.g. pass around a C function pointer.
626+
// Create a new Name for this Ref which causes the variable to be declared in Go land.
627+
fpName := "fp_" + r.Name.Go
628+
name := f.Name[fpName]
629+
if name == nil {
630+
name = &Name{
631+
Go: fpName,
632+
C: r.Name.C,
633+
Kind: "fpvar",
634+
Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")},
635+
}
636+
p.mangleName(name)
637+
f.Name[fpName] = name
638+
}
639+
r.Name = name
640+
expr = ast.NewIdent(name.Mangle)
641+
} else if r.Name.Kind == "type" {
612642
// Okay - might be new(T)
613643
expr = r.Name.Type.Go
614-
}
615-
if r.Name.Kind == "var" {
644+
} else if r.Name.Kind == "var" {
616645
expr = &ast.StarExpr{X: expr}
617646
}
618647

@@ -644,6 +673,14 @@ func (p *Package) rewriteRef(f *File) {
644673
}
645674
*r.Expr = expr
646675
}
676+
677+
// Remove functions only used as expressions, so their respective
678+
// bridge functions are not generated.
679+
for name, used := range functions {
680+
if !used {
681+
delete(f.Name, name)
682+
}
683+
}
647684
}
648685

649686
// gccBaseCmd returns the start of the compiler command line.

src/cmd/cgo/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,18 @@ type Name struct {
8080
Mangle string // name used in generated Go
8181
C string // name used in C
8282
Define string // #define expansion
83-
Kind string // "const", "type", "var", "func", "not-type"
83+
Kind string // "const", "type", "var", "fpvar", "func", "not-type"
8484
Type *Type // the type of xxx
8585
FuncType *FuncType
8686
AddError bool
8787
Const string // constant definition
8888
}
8989

90+
// IsVar returns true if Kind is either "var" or "fpvar"
91+
func (n *Name) IsVar() bool {
92+
return n.Kind == "var" || n.Kind == "fpvar"
93+
}
94+
9095
// A ExpFunc is an exported function, callable from C.
9196
// Such functions are identified in the Go input file
9297
// by doc comments containing the line //export ExpName

src/cmd/cgo/out.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func (p *Package) writeDefs() {
9797
cVars := make(map[string]bool)
9898
for _, key := range nameKeys(p.Name) {
9999
n := p.Name[key]
100-
if n.Kind != "var" {
100+
if !n.IsVar() {
101101
continue
102102
}
103103

@@ -113,17 +113,26 @@ func (p *Package) writeDefs() {
113113

114114
cVars[n.C] = true
115115
}
116-
116+
var amp string
117+
var node ast.Node
118+
if n.Kind == "var" {
119+
amp = "&"
120+
node = &ast.StarExpr{X: n.Type.Go}
121+
} else if n.Kind == "fpvar" {
122+
node = n.Type.Go
123+
} else {
124+
panic(fmt.Errorf("invalid var kind %q", n.Kind))
125+
}
117126
if *gccgo {
118127
fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle)
119-
fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C)
128+
fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C)
120129
} else {
121-
fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C)
130+
fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C)
122131
}
123132
fmt.Fprintf(fc, "\n")
124133

125134
fmt.Fprintf(fgo2, "var %s ", n.Mangle)
126-
conf.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go})
135+
conf.Fprint(fgo2, fset, node)
127136
fmt.Fprintf(fgo2, "\n")
128137
}
129138
fmt.Fprintf(fc, "\n")

0 commit comments

Comments
 (0)