loader/cgo: add support for pointer types
Этот коммит содержится в:
родитель
01f6aff422
коммит
35fb594f8f
7 изменённых файлов: 165 добавлений и 120 удалений
|
@ -1325,7 +1325,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
|||
return nil
|
||||
}
|
||||
store := c.builder.CreateStore(llvmVal, llvmAddr)
|
||||
valType := instr.Addr.Type().(*types.Pointer).Elem()
|
||||
valType := instr.Addr.Type().Underlying().(*types.Pointer).Elem()
|
||||
if c.ir.IsVolatile(valType) {
|
||||
// Volatile store, for memory-mapped registers.
|
||||
store.SetVolatile(true)
|
||||
|
@ -1957,7 +1957,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
var bufptr, buflen llvm.Value
|
||||
switch ptrTyp := expr.X.Type().Underlying().(type) {
|
||||
case *types.Pointer:
|
||||
typ := expr.X.Type().(*types.Pointer).Elem().Underlying()
|
||||
typ := expr.X.Type().Underlying().(*types.Pointer).Elem().Underlying()
|
||||
switch typ := typ.(type) {
|
||||
case *types.Array:
|
||||
bufptr = val
|
||||
|
@ -2986,7 +2986,7 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
|||
return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
|
||||
}
|
||||
case token.MUL: // *x, dereference pointer
|
||||
valType := unop.X.Type().(*types.Pointer).Elem()
|
||||
valType := unop.X.Type().Underlying().(*types.Pointer).Elem()
|
||||
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
||||
// zero-length data
|
||||
return c.getZeroValue(x.Type().ElementType())
|
||||
|
|
141
loader/cgo.go
141
loader/cgo.go
|
@ -17,37 +17,33 @@ import (
|
|||
type fileInfo struct {
|
||||
*ast.File
|
||||
filename string
|
||||
functions []*functionInfo
|
||||
typedefs []*typedefInfo
|
||||
globals []*globalInfo
|
||||
functions map[string]*functionInfo
|
||||
globals map[string]*globalInfo
|
||||
typedefs map[string]*typedefInfo
|
||||
importCPos token.Pos
|
||||
}
|
||||
|
||||
// functionInfo stores some information about a Cgo function found by libclang
|
||||
// and declared in the AST.
|
||||
type functionInfo struct {
|
||||
name string
|
||||
args []paramInfo
|
||||
result string
|
||||
results *ast.FieldList
|
||||
}
|
||||
|
||||
// paramInfo is a parameter of a Cgo function (see functionInfo).
|
||||
type paramInfo struct {
|
||||
name string
|
||||
typeName string
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// typedefInfo contains information about a single typedef in C.
|
||||
type typedefInfo struct {
|
||||
newName string // newly defined type name
|
||||
oldName string // old type name, may be something like "unsigned long long"
|
||||
size int // size in bytes
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// globalInfo contains information about a declared global variable in C.
|
||||
type globalInfo struct {
|
||||
name string
|
||||
typeName string
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||
|
@ -86,6 +82,9 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
|
|||
info := &fileInfo{
|
||||
File: f,
|
||||
filename: filename,
|
||||
functions: map[string]*functionInfo{},
|
||||
globals: map[string]*globalInfo{},
|
||||
typedefs: map[string]*typedefInfo{},
|
||||
}
|
||||
|
||||
// Find `import "C"` statements in the file.
|
||||
|
@ -151,16 +150,22 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
|
|||
func (info *fileInfo) addFuncDecls() {
|
||||
// TODO: replace all uses of importCPos with the real locations from
|
||||
// libclang.
|
||||
for _, fn := range info.functions {
|
||||
names := make([]string, 0, len(info.functions))
|
||||
for name := range info.functions {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
fn := info.functions[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: mapCgoType(fn.name),
|
||||
Name: "C." + name,
|
||||
}
|
||||
args := make([]*ast.Field, len(fn.args))
|
||||
decl := &ast.FuncDecl{
|
||||
Name: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: mapCgoType(fn.name),
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
|
@ -170,16 +175,7 @@ func (info *fileInfo) addFuncDecls() {
|
|||
List: args,
|
||||
Closing: info.importCPos,
|
||||
},
|
||||
Results: &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Type: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: mapCgoType(fn.result),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Results: fn.results,
|
||||
},
|
||||
}
|
||||
obj.Decl = decl
|
||||
|
@ -191,15 +187,12 @@ func (info *fileInfo) addFuncDecls() {
|
|||
Name: arg.name,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: mapCgoType(arg.name),
|
||||
Name: arg.name,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: mapCgoType(arg.typeName),
|
||||
},
|
||||
Type: arg.typeExpr,
|
||||
}
|
||||
}
|
||||
info.Decls = append(info.Decls, decl)
|
||||
|
@ -221,21 +214,24 @@ func (info *fileInfo) addVarDecls() {
|
|||
Lparen: info.importCPos,
|
||||
Rparen: info.importCPos,
|
||||
}
|
||||
for _, global := range info.globals {
|
||||
names := make([]string, 0, len(info.globals))
|
||||
for name := range info.globals {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
global := info.globals[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: mapCgoType(global.name),
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{&ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: mapCgoType(global.name),
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: mapCgoType(global.typeName),
|
||||
},
|
||||
Type: global.typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
|
@ -292,55 +288,32 @@ func (info *fileInfo) addTypedefs() {
|
|||
TokPos: info.importCPos,
|
||||
Tok: token.TYPE,
|
||||
}
|
||||
for _, typedef := range info.typedefs {
|
||||
newType := mapCgoType(typedef.newName)
|
||||
oldType := mapCgoType(typedef.oldName)
|
||||
switch oldType {
|
||||
// TODO: plain char (may be signed or unsigned)
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typedef.size {
|
||||
case 1:
|
||||
oldType = "int8"
|
||||
case 2:
|
||||
oldType = "int16"
|
||||
case 4:
|
||||
oldType = "int32"
|
||||
case 8:
|
||||
oldType = "int64"
|
||||
names := make([]string, 0, len(info.typedefs))
|
||||
for name := range info.typedefs {
|
||||
names = append(names, name)
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typedef.size {
|
||||
case 1:
|
||||
oldType = "uint8"
|
||||
case 2:
|
||||
oldType = "uint16"
|
||||
case 4:
|
||||
oldType = "uint32"
|
||||
case 8:
|
||||
oldType = "uint64"
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
typedef := info.typedefs[name]
|
||||
typeName := "C." + name
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
typeName = "C." + name[len("_Cgo_"):]
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(newType, "C._Cgo_") {
|
||||
newType = "C." + newType[len("C._Cgo_"):]
|
||||
}
|
||||
if _, ok := cgoAliases[newType]; ok {
|
||||
if _, ok := cgoAliases[typeName]; ok {
|
||||
// This is a type that also exists in Go (defined in stdint.h).
|
||||
continue
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: newType,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: newType,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: oldType,
|
||||
},
|
||||
Type: typedef.typeExpr,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
|
@ -362,31 +335,9 @@ func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
|
|||
if x.Name == "C" {
|
||||
cursor.Replace(&ast.Ident{
|
||||
NamePos: x.NamePos,
|
||||
Name: mapCgoType(node.Sel.Name),
|
||||
Name: "C." + node.Sel.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// mapCgoType converts a C type name into a Go type name with a "C." prefix.
|
||||
func mapCgoType(t string) string {
|
||||
switch t {
|
||||
case "signed char":
|
||||
return "C.schar"
|
||||
case "long long":
|
||||
return "C.longlong"
|
||||
case "unsigned char":
|
||||
return "C.schar"
|
||||
case "unsigned short":
|
||||
return "C.ushort"
|
||||
case "unsigned int":
|
||||
return "C.uint"
|
||||
case "unsigned long":
|
||||
return "C.ulong"
|
||||
case "unsigned long long":
|
||||
return "C.ulonglong"
|
||||
default:
|
||||
return "C." + t
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ package loader
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"go/ast"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -89,37 +91,70 @@ func tinygo_clang_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.in
|
|||
return C.CXChildVisit_Continue // not supported
|
||||
}
|
||||
numArgs := C.clang_Cursor_getNumArguments(c)
|
||||
fn := &functionInfo{name: name}
|
||||
info.functions = append(info.functions, fn)
|
||||
fn := &functionInfo{}
|
||||
info.functions[name] = fn
|
||||
for i := C.int(0); i < numArgs; i++ {
|
||||
arg := C.clang_Cursor_getArgument(c, C.uint(i))
|
||||
argName := getString(C.clang_getCursorSpelling(arg))
|
||||
argType := C.clang_getArgType(cursorType, C.uint(i))
|
||||
argTypeName := getString(C.clang_getTypeSpelling(argType))
|
||||
fn.args = append(fn.args, paramInfo{argName, argTypeName})
|
||||
fn.args = append(fn.args, paramInfo{
|
||||
name: argName,
|
||||
typeExpr: info.makeASTType(argType),
|
||||
})
|
||||
}
|
||||
resultType := C.clang_getCursorResultType(c)
|
||||
resultTypeName := getString(C.clang_getTypeSpelling(resultType))
|
||||
fn.result = resultTypeName
|
||||
if resultType.kind != C.CXType_Void {
|
||||
fn.results = &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Type: info.makeASTType(resultType),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typedefType := C.clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
underlyingType := C.clang_getTypedefDeclUnderlyingType(c)
|
||||
underlyingTypeName := getString(C.clang_getTypeSpelling(underlyingType))
|
||||
expr := info.makeASTType(underlyingType)
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
expr := expr.(*ast.Ident)
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
info.typedefs = append(info.typedefs, &typedefInfo{
|
||||
newName: name,
|
||||
oldName: underlyingTypeName,
|
||||
size: int(typeSize),
|
||||
})
|
||||
switch expr.Name {
|
||||
// TODO: plain char (may be signed or unsigned)
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "int8"
|
||||
case 2:
|
||||
expr.Name = "int16"
|
||||
case 4:
|
||||
expr.Name = "int32"
|
||||
case 8:
|
||||
expr.Name = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "uint8"
|
||||
case 2:
|
||||
expr.Name = "uint16"
|
||||
case 4:
|
||||
expr.Name = "uint32"
|
||||
case 8:
|
||||
expr.Name = "uint64"
|
||||
}
|
||||
}
|
||||
}
|
||||
info.typedefs[name] = &typedefInfo{
|
||||
typeExpr: expr,
|
||||
}
|
||||
case C.CXCursor_VarDecl:
|
||||
name := getString(C.clang_getCursorSpelling(c))
|
||||
cursorType := C.clang_getCursorType(c)
|
||||
cursorTypeName := getString(C.clang_getTypeSpelling(cursorType))
|
||||
info.globals = append(info.globals, &globalInfo{
|
||||
name: name,
|
||||
typeName: cursorTypeName,
|
||||
})
|
||||
info.globals[name] = &globalInfo{
|
||||
typeExpr: info.makeASTType(cursorType),
|
||||
}
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
@ -130,3 +165,44 @@ func getString(clangString C.CXString) (s string) {
|
|||
C.clang_disposeString(clangString)
|
||||
return
|
||||
}
|
||||
|
||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||
// it converts a libclang type to a type in the Go AST.
|
||||
func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
||||
var typeName string
|
||||
switch typ.kind {
|
||||
case C.CXType_SChar:
|
||||
typeName = "schar"
|
||||
case C.CXType_UChar:
|
||||
typeName = "uchar"
|
||||
case C.CXType_Short:
|
||||
typeName = "short"
|
||||
case C.CXType_UShort:
|
||||
typeName = "ushort"
|
||||
case C.CXType_Int:
|
||||
typeName = "int"
|
||||
case C.CXType_UInt:
|
||||
typeName = "uint"
|
||||
case C.CXType_Long:
|
||||
typeName = "long"
|
||||
case C.CXType_ULong:
|
||||
typeName = "ulong"
|
||||
case C.CXType_LongLong:
|
||||
typeName = "longlong"
|
||||
case C.CXType_ULongLong:
|
||||
typeName = "ulonglong"
|
||||
case C.CXType_Pointer:
|
||||
return &ast.StarExpr{
|
||||
Star: info.importCPos,
|
||||
X: info.makeASTType(C.clang_getPointeeType(typ)),
|
||||
}
|
||||
default:
|
||||
// Fallback, probably incorrect but at least the error points to an odd
|
||||
// type name.
|
||||
typeName = getString(C.clang_getTypeSpelling(typ))
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "C." + typeName,
|
||||
}
|
||||
}
|
||||
|
|
4
testdata/cgo/main.c
предоставленный
4
testdata/cgo/main.c
предоставленный
|
@ -9,3 +9,7 @@ int fortytwo() {
|
|||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
void store(int value, int *ptr) {
|
||||
*ptr = value;
|
||||
}
|
||||
|
|
6
testdata/cgo/main.go
предоставленный
6
testdata/cgo/main.go
предоставленный
|
@ -17,4 +17,10 @@ func main() {
|
|||
var y C.longlong = -(1 << 40)
|
||||
println("longlong:", y)
|
||||
println("global:", C.global)
|
||||
var ptr C.intPointer
|
||||
var n C.int = 15
|
||||
ptr = C.intPointer(&n)
|
||||
println("15:", *ptr)
|
||||
C.store(25, &n)
|
||||
println("25:", *ptr)
|
||||
}
|
||||
|
|
6
testdata/cgo/main.h
предоставленный
6
testdata/cgo/main.h
предоставленный
|
@ -1,3 +1,9 @@
|
|||
typedef short myint;
|
||||
int add(int a, int b);
|
||||
typedef int * intPointer;
|
||||
extern int global;
|
||||
void store(int value, int *ptr);
|
||||
|
||||
// test duplicate definitions
|
||||
int add(int a, int b);
|
||||
extern int global;
|
||||
|
|
2
testdata/cgo/out.txt
предоставленный
2
testdata/cgo/out.txt
предоставленный
|
@ -4,3 +4,5 @@ myint: 3 5
|
|||
myint size: 2
|
||||
longlong: -1099511627776
|
||||
global: 3
|
||||
15: 15
|
||||
25: 25
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче