loader/cgo: add support for function pointers
Этот коммит содержится в:
родитель
35fb594f8f
коммит
95d895646a
8 изменённых файлов: 134 добавлений и 7 удалений
|
@ -2990,6 +2990,16 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
||||||
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
||||||
// zero-length data
|
// zero-length data
|
||||||
return c.getZeroValue(x.Type().ElementType())
|
return c.getZeroValue(x.Type().ElementType())
|
||||||
|
} else if strings.HasSuffix(unop.X.String(), "$funcaddr") {
|
||||||
|
// CGo function pointer. The cgo part has rewritten CGo function
|
||||||
|
// pointers as stub global variables of the form:
|
||||||
|
// var C.add unsafe.Pointer
|
||||||
|
// Instead of a load from the global, create a bitcast of the
|
||||||
|
// function pointer itself.
|
||||||
|
global := c.ir.GetGlobal(unop.X.(*ssa.Global))
|
||||||
|
name := global.LinkName()[:len(global.LinkName())-len("$funcaddr")]
|
||||||
|
fn := c.mod.NamedFunction(name)
|
||||||
|
return c.builder.CreateBitCast(fn, c.i8ptrType, ""), nil
|
||||||
} else {
|
} else {
|
||||||
load := c.builder.CreateLoad(x, "")
|
load := c.builder.CreateLoad(x, "")
|
||||||
if c.ir.IsVolatile(valType) {
|
if c.ir.IsVolatile(valType) {
|
||||||
|
|
14
ir/ir.go
14
ir/ir.go
|
@ -269,10 +269,16 @@ func (f *Function) parsePragmas() {
|
||||||
}
|
}
|
||||||
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
||||||
for _, comment := range decl.Doc.List {
|
for _, comment := range decl.Doc.List {
|
||||||
if !strings.HasPrefix(comment.Text, "//go:") {
|
text := comment.Text
|
||||||
|
if strings.HasPrefix(text, "//export ") {
|
||||||
|
// Rewrite '//export' to '//go:export' for compatibility with
|
||||||
|
// gc.
|
||||||
|
text = "//go:" + text[2:]
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(text, "//go:") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parts := strings.Fields(comment.Text)
|
parts := strings.Fields(text)
|
||||||
switch parts[0] {
|
switch parts[0] {
|
||||||
case "//go:export":
|
case "//go:export":
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
|
@ -322,7 +328,7 @@ func (f *Function) IsNoBounds() bool {
|
||||||
|
|
||||||
// Return true iff this function is externally visible.
|
// Return true iff this function is externally visible.
|
||||||
func (f *Function) IsExported() bool {
|
func (f *Function) IsExported() bool {
|
||||||
return f.exported
|
return f.exported || f.CName() != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true for functions annotated with //go:interrupt. The function name is
|
// Return true for functions annotated with //go:interrupt. The function name is
|
||||||
|
@ -330,7 +336,7 @@ func (f *Function) IsExported() bool {
|
||||||
//
|
//
|
||||||
// On some platforms (like AVR), interrupts need a special compiler flag.
|
// On some platforms (like AVR), interrupts need a special compiler flag.
|
||||||
func (f *Function) IsInterrupt() bool {
|
func (f *Function) IsInterrupt() bool {
|
||||||
return f.exported
|
return f.interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the link name for this function.
|
// Return the link name for this function.
|
||||||
|
|
|
@ -130,6 +130,9 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
|
||||||
// Declare functions found by libclang.
|
// Declare functions found by libclang.
|
||||||
info.addFuncDecls()
|
info.addFuncDecls()
|
||||||
|
|
||||||
|
// Declare stub function pointer values found by libclang.
|
||||||
|
info.addFuncPtrDecls()
|
||||||
|
|
||||||
// Declare globals found by libclang.
|
// Declare globals found by libclang.
|
||||||
info.addVarDecls()
|
info.addVarDecls()
|
||||||
|
|
||||||
|
@ -199,6 +202,55 @@ func (info *fileInfo) addFuncDecls() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addFuncPtrDecls creates stub declarations of function pointer values. These
|
||||||
|
// values will later be replaced with the real values in the compiler.
|
||||||
|
// It adds code like the following to the AST:
|
||||||
|
//
|
||||||
|
// var (
|
||||||
|
// C.add unsafe.Pointer
|
||||||
|
// C.mul unsafe.Pointer
|
||||||
|
// // ...
|
||||||
|
// )
|
||||||
|
func (info *fileInfo) addFuncPtrDecls() {
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: info.importCPos,
|
||||||
|
Tok: token.VAR,
|
||||||
|
Lparen: info.importCPos,
|
||||||
|
Rparen: info.importCPos,
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(info.functions))
|
||||||
|
for name := range info.functions {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: "C." + name + "$funcaddr",
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{&ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: "C." + name + "$funcaddr",
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Type: &ast.SelectorExpr{
|
||||||
|
X: &ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: "unsafe",
|
||||||
|
},
|
||||||
|
Sel: &ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: "Pointer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
}
|
||||||
|
info.Decls = append(info.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
// addVarDecls declares external C globals in the Go source.
|
// addVarDecls declares external C globals in the Go source.
|
||||||
// It adds code like the following to the AST:
|
// It adds code like the following to the AST:
|
||||||
//
|
//
|
||||||
|
@ -327,15 +379,34 @@ func (info *fileInfo) addTypedefs() {
|
||||||
// separate namespace (no _Cgo_ hacks like in gc).
|
// separate namespace (no _Cgo_ hacks like in gc).
|
||||||
func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
|
func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
|
||||||
switch node := cursor.Node().(type) {
|
switch node := cursor.Node().(type) {
|
||||||
|
case *ast.CallExpr:
|
||||||
|
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
x, ok := fun.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := info.functions[fun.Sel.Name]; ok && x.Name == "C" {
|
||||||
|
node.Fun = &ast.Ident{
|
||||||
|
NamePos: x.NamePos,
|
||||||
|
Name: "C." + fun.Sel.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
x, ok := node.X.(*ast.Ident)
|
x, ok := node.X.(*ast.Ident)
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if x.Name == "C" {
|
if x.Name == "C" {
|
||||||
|
name := "C." + node.Sel.Name
|
||||||
|
if _, ok := info.functions[node.Sel.Name]; ok {
|
||||||
|
name += "$funcaddr"
|
||||||
|
}
|
||||||
cursor.Replace(&ast.Ident{
|
cursor.Replace(&ast.Ident{
|
||||||
NamePos: x.NamePos,
|
NamePos: x.NamePos,
|
||||||
Name: "C." + node.Sel.Name,
|
Name: name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ package loader
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -90,13 +92,16 @@ func tinygo_clang_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.in
|
||||||
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||||
return C.CXChildVisit_Continue // not supported
|
return C.CXChildVisit_Continue // not supported
|
||||||
}
|
}
|
||||||
numArgs := C.clang_Cursor_getNumArguments(c)
|
numArgs := int(C.clang_Cursor_getNumArguments(c))
|
||||||
fn := &functionInfo{}
|
fn := &functionInfo{}
|
||||||
info.functions[name] = fn
|
info.functions[name] = fn
|
||||||
for i := C.int(0); i < numArgs; i++ {
|
for i := 0; i < numArgs; i++ {
|
||||||
arg := C.clang_Cursor_getArgument(c, C.uint(i))
|
arg := C.clang_Cursor_getArgument(c, C.uint(i))
|
||||||
argName := getString(C.clang_getCursorSpelling(arg))
|
argName := getString(C.clang_getCursorSpelling(arg))
|
||||||
argType := C.clang_getArgType(cursorType, C.uint(i))
|
argType := C.clang_getArgType(cursorType, C.uint(i))
|
||||||
|
if argName == "" {
|
||||||
|
argName = "$" + strconv.Itoa(i)
|
||||||
|
}
|
||||||
fn.args = append(fn.args, paramInfo{
|
fn.args = append(fn.args, paramInfo{
|
||||||
name: argName,
|
name: argName,
|
||||||
typeExpr: info.makeASTType(argType),
|
typeExpr: info.makeASTType(argType),
|
||||||
|
@ -196,6 +201,23 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
||||||
Star: info.importCPos,
|
Star: info.importCPos,
|
||||||
X: info.makeASTType(C.clang_getPointeeType(typ)),
|
X: info.makeASTType(C.clang_getPointeeType(typ)),
|
||||||
}
|
}
|
||||||
|
case C.CXType_FunctionProto:
|
||||||
|
// Be compatible with gc, which uses the *[0]byte type for function
|
||||||
|
// pointer types.
|
||||||
|
// Return type [0]byte because this is a function type, not a pointer to
|
||||||
|
// this function type.
|
||||||
|
return &ast.ArrayType{
|
||||||
|
Lbrack: info.importCPos,
|
||||||
|
Len: &ast.BasicLit{
|
||||||
|
ValuePos: info.importCPos,
|
||||||
|
Kind: token.INT,
|
||||||
|
Value: "0",
|
||||||
|
},
|
||||||
|
Elt: &ast.Ident{
|
||||||
|
NamePos: info.importCPos,
|
||||||
|
Name: "byte",
|
||||||
|
},
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// Fallback, probably incorrect but at least the error points to an odd
|
// Fallback, probably incorrect but at least the error points to an odd
|
||||||
// type name.
|
// type name.
|
||||||
|
|
4
testdata/cgo/main.c
предоставленный
4
testdata/cgo/main.c
предоставленный
|
@ -10,6 +10,10 @@ int add(int a, int b) {
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int doCallback(int a, int b, binop_t callback) {
|
||||||
|
return callback(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
void store(int value, int *ptr) {
|
void store(int value, int *ptr) {
|
||||||
*ptr = value;
|
*ptr = value;
|
||||||
}
|
}
|
||||||
|
|
10
testdata/cgo/main.go
предоставленный
10
testdata/cgo/main.go
предоставленный
|
@ -3,6 +3,7 @@ package main
|
||||||
/*
|
/*
|
||||||
int fortytwo(void);
|
int fortytwo(void);
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
int mul(int, int);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
@ -23,4 +24,13 @@ func main() {
|
||||||
println("15:", *ptr)
|
println("15:", *ptr)
|
||||||
C.store(25, &n)
|
C.store(25, &n)
|
||||||
println("25:", *ptr)
|
println("25:", *ptr)
|
||||||
|
cb := C.binop_t(C.add)
|
||||||
|
println("callback 1:", C.doCallback(20, 30, cb))
|
||||||
|
cb = C.binop_t(C.mul)
|
||||||
|
println("callback 2:", C.doCallback(20, 30, cb))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export mul
|
||||||
|
func mul(a, b C.int) C.int {
|
||||||
|
return a * b
|
||||||
}
|
}
|
||||||
|
|
2
testdata/cgo/main.h
предоставленный
2
testdata/cgo/main.h
предоставленный
|
@ -1,5 +1,7 @@
|
||||||
typedef short myint;
|
typedef short myint;
|
||||||
int add(int a, int b);
|
int add(int a, int b);
|
||||||
|
typedef int (*binop_t) (int, int);
|
||||||
|
int doCallback(int a, int b, binop_t cb);
|
||||||
typedef int * intPointer;
|
typedef int * intPointer;
|
||||||
extern int global;
|
extern int global;
|
||||||
void store(int value, int *ptr);
|
void store(int value, int *ptr);
|
||||||
|
|
2
testdata/cgo/out.txt
предоставленный
2
testdata/cgo/out.txt
предоставленный
|
@ -6,3 +6,5 @@ longlong: -1099511627776
|
||||||
global: 3
|
global: 3
|
||||||
15: 15
|
15: 15
|
||||||
25: 25
|
25: 25
|
||||||
|
callback 1: 50
|
||||||
|
callback 2: 600
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче