loader/cgo: add support for pointer types

Этот коммит содержится в:
Ayke van Laethem 2019-02-07 13:20:19 +01:00
родитель 01f6aff422
коммит 35fb594f8f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
7 изменённых файлов: 165 добавлений и 120 удалений

Просмотреть файл

@ -1325,7 +1325,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
return nil return nil
} }
store := c.builder.CreateStore(llvmVal, llvmAddr) 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) { if c.ir.IsVolatile(valType) {
// Volatile store, for memory-mapped registers. // Volatile store, for memory-mapped registers.
store.SetVolatile(true) store.SetVolatile(true)
@ -1957,7 +1957,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
var bufptr, buflen llvm.Value var bufptr, buflen llvm.Value
switch ptrTyp := expr.X.Type().Underlying().(type) { switch ptrTyp := expr.X.Type().Underlying().(type) {
case *types.Pointer: case *types.Pointer:
typ := expr.X.Type().(*types.Pointer).Elem().Underlying() typ := expr.X.Type().Underlying().(*types.Pointer).Elem().Underlying()
switch typ := typ.(type) { switch typ := typ.(type) {
case *types.Array: case *types.Array:
bufptr = val 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()) return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
} }
case token.MUL: // *x, dereference pointer 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 { 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())

Просмотреть файл

@ -17,37 +17,33 @@ import (
type fileInfo struct { type fileInfo struct {
*ast.File *ast.File
filename string filename string
functions []*functionInfo functions map[string]*functionInfo
typedefs []*typedefInfo globals map[string]*globalInfo
globals []*globalInfo typedefs map[string]*typedefInfo
importCPos token.Pos importCPos token.Pos
} }
// functionInfo stores some information about a Cgo function found by libclang // functionInfo stores some information about a Cgo function found by libclang
// and declared in the AST. // and declared in the AST.
type functionInfo struct { type functionInfo struct {
name string args []paramInfo
args []paramInfo results *ast.FieldList
result string
} }
// paramInfo is a parameter of a Cgo function (see functionInfo). // paramInfo is a parameter of a Cgo function (see functionInfo).
type paramInfo struct { type paramInfo struct {
name string name string
typeName string typeExpr ast.Expr
} }
// typedefInfo contains information about a single typedef in C. // typedefInfo contains information about a single typedef in C.
type typedefInfo struct { type typedefInfo struct {
newName string // newly defined type name typeExpr ast.Expr
oldName string // old type name, may be something like "unsigned long long"
size int // size in bytes
} }
// globalInfo contains information about a declared global variable in C. // globalInfo contains information about a declared global variable in C.
type globalInfo struct { type globalInfo struct {
name string typeExpr ast.Expr
typeName string
} }
// cgoAliases list type aliases between Go and C, for types that are equivalent // cgoAliases list type aliases between Go and C, for types that are equivalent
@ -84,8 +80,11 @@ typedef unsigned long long _Cgo_ulonglong;
// comment with libclang, and modifies the AST to use this information. // comment with libclang, and modifies the AST to use this information.
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) error { func (p *Package) processCgo(filename string, f *ast.File, cflags []string) error {
info := &fileInfo{ info := &fileInfo{
File: f, File: f,
filename: filename, filename: filename,
functions: map[string]*functionInfo{},
globals: map[string]*globalInfo{},
typedefs: map[string]*typedefInfo{},
} }
// Find `import "C"` statements in the file. // 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() { func (info *fileInfo) addFuncDecls() {
// TODO: replace all uses of importCPos with the real locations from // TODO: replace all uses of importCPos with the real locations from
// libclang. // 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{ obj := &ast.Object{
Kind: ast.Fun, Kind: ast.Fun,
Name: mapCgoType(fn.name), Name: "C." + name,
} }
args := make([]*ast.Field, len(fn.args)) args := make([]*ast.Field, len(fn.args))
decl := &ast.FuncDecl{ decl := &ast.FuncDecl{
Name: &ast.Ident{ Name: &ast.Ident{
NamePos: info.importCPos, NamePos: info.importCPos,
Name: mapCgoType(fn.name), Name: "C." + name,
Obj: obj, Obj: obj,
}, },
Type: &ast.FuncType{ Type: &ast.FuncType{
@ -170,16 +175,7 @@ func (info *fileInfo) addFuncDecls() {
List: args, List: args,
Closing: info.importCPos, Closing: info.importCPos,
}, },
Results: &ast.FieldList{ Results: fn.results,
List: []*ast.Field{
&ast.Field{
Type: &ast.Ident{
NamePos: info.importCPos,
Name: mapCgoType(fn.result),
},
},
},
},
}, },
} }
obj.Decl = decl obj.Decl = decl
@ -191,15 +187,12 @@ func (info *fileInfo) addFuncDecls() {
Name: arg.name, Name: arg.name,
Obj: &ast.Object{ Obj: &ast.Object{
Kind: ast.Var, Kind: ast.Var,
Name: mapCgoType(arg.name), Name: arg.name,
Decl: decl, Decl: decl,
}, },
}, },
}, },
Type: &ast.Ident{ Type: arg.typeExpr,
NamePos: info.importCPos,
Name: mapCgoType(arg.typeName),
},
} }
} }
info.Decls = append(info.Decls, decl) info.Decls = append(info.Decls, decl)
@ -221,21 +214,24 @@ func (info *fileInfo) addVarDecls() {
Lparen: info.importCPos, Lparen: info.importCPos,
Rparen: 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{ obj := &ast.Object{
Kind: ast.Typ, Kind: ast.Typ,
Name: mapCgoType(global.name), Name: "C." + name,
} }
valueSpec := &ast.ValueSpec{ valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{&ast.Ident{ Names: []*ast.Ident{&ast.Ident{
NamePos: info.importCPos, NamePos: info.importCPos,
Name: mapCgoType(global.name), Name: "C." + name,
Obj: obj, Obj: obj,
}}, }},
Type: &ast.Ident{ Type: global.typeExpr,
NamePos: info.importCPos,
Name: mapCgoType(global.typeName),
},
} }
obj.Decl = valueSpec obj.Decl = valueSpec
gen.Specs = append(gen.Specs, valueSpec) gen.Specs = append(gen.Specs, valueSpec)
@ -292,55 +288,32 @@ func (info *fileInfo) addTypedefs() {
TokPos: info.importCPos, TokPos: info.importCPos,
Tok: token.TYPE, Tok: token.TYPE,
} }
for _, typedef := range info.typedefs { names := make([]string, 0, len(info.typedefs))
newType := mapCgoType(typedef.newName) for name := range info.typedefs {
oldType := mapCgoType(typedef.oldName) names = append(names, name)
switch oldType { }
// TODO: plain char (may be signed or unsigned) sort.Strings(names)
case "C.schar", "C.short", "C.int", "C.long", "C.longlong": for _, name := range names {
switch typedef.size { typedef := info.typedefs[name]
case 1: typeName := "C." + name
oldType = "int8" if strings.HasPrefix(name, "_Cgo_") {
case 2: typeName = "C." + name[len("_Cgo_"):]
oldType = "int16"
case 4:
oldType = "int32"
case 8:
oldType = "int64"
}
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"
}
} }
if strings.HasPrefix(newType, "C._Cgo_") { if _, ok := cgoAliases[typeName]; ok {
newType = "C." + newType[len("C._Cgo_"):]
}
if _, ok := cgoAliases[newType]; ok {
// This is a type that also exists in Go (defined in stdint.h). // This is a type that also exists in Go (defined in stdint.h).
continue continue
} }
obj := &ast.Object{ obj := &ast.Object{
Kind: ast.Typ, Kind: ast.Typ,
Name: newType, Name: typeName,
} }
typeSpec := &ast.TypeSpec{ typeSpec := &ast.TypeSpec{
Name: &ast.Ident{ Name: &ast.Ident{
NamePos: info.importCPos, NamePos: info.importCPos,
Name: newType, Name: typeName,
Obj: obj, Obj: obj,
}, },
Type: &ast.Ident{ Type: typedef.typeExpr,
NamePos: info.importCPos,
Name: oldType,
},
} }
obj.Decl = typeSpec obj.Decl = typeSpec
gen.Specs = append(gen.Specs, typeSpec) gen.Specs = append(gen.Specs, typeSpec)
@ -362,31 +335,9 @@ func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
if x.Name == "C" { if x.Name == "C" {
cursor.Replace(&ast.Ident{ cursor.Replace(&ast.Ident{
NamePos: x.NamePos, NamePos: x.NamePos,
Name: mapCgoType(node.Sel.Name), Name: "C." + node.Sel.Name,
}) })
} }
} }
return true 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 ( import (
"errors" "errors"
"go/ast"
"strings"
"unsafe" "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 return C.CXChildVisit_Continue // not supported
} }
numArgs := C.clang_Cursor_getNumArguments(c) numArgs := C.clang_Cursor_getNumArguments(c)
fn := &functionInfo{name: name} fn := &functionInfo{}
info.functions = append(info.functions, fn) info.functions[name] = fn
for i := C.int(0); i < numArgs; i++ { for i := C.int(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))
argTypeName := getString(C.clang_getTypeSpelling(argType)) fn.args = append(fn.args, paramInfo{
fn.args = append(fn.args, paramInfo{argName, argTypeName}) name: argName,
typeExpr: info.makeASTType(argType),
})
} }
resultType := C.clang_getCursorResultType(c) resultType := C.clang_getCursorResultType(c)
resultTypeName := getString(C.clang_getTypeSpelling(resultType)) if resultType.kind != C.CXType_Void {
fn.result = resultTypeName fn.results = &ast.FieldList{
List: []*ast.Field{
&ast.Field{
Type: info.makeASTType(resultType),
},
},
}
}
case C.CXCursor_TypedefDecl: case C.CXCursor_TypedefDecl:
typedefType := C.clang_getCursorType(c) typedefType := C.clang_getCursorType(c)
name := getString(C.clang_getTypedefName(typedefType)) name := getString(C.clang_getTypedefName(typedefType))
underlyingType := C.clang_getTypedefDeclUnderlyingType(c) underlyingType := C.clang_getTypedefDeclUnderlyingType(c)
underlyingTypeName := getString(C.clang_getTypeSpelling(underlyingType)) expr := info.makeASTType(underlyingType)
typeSize := C.clang_Type_getSizeOf(underlyingType) if strings.HasPrefix(name, "_Cgo_") {
info.typedefs = append(info.typedefs, &typedefInfo{ expr := expr.(*ast.Ident)
newName: name, typeSize := C.clang_Type_getSizeOf(underlyingType)
oldName: underlyingTypeName, switch expr.Name {
size: int(typeSize), // 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: case C.CXCursor_VarDecl:
name := getString(C.clang_getCursorSpelling(c)) name := getString(C.clang_getCursorSpelling(c))
cursorType := C.clang_getCursorType(c) cursorType := C.clang_getCursorType(c)
cursorTypeName := getString(C.clang_getTypeSpelling(cursorType)) info.globals[name] = &globalInfo{
info.globals = append(info.globals, &globalInfo{ typeExpr: info.makeASTType(cursorType),
name: name, }
typeName: cursorTypeName,
})
} }
return C.CXChildVisit_Continue return C.CXChildVisit_Continue
} }
@ -130,3 +165,44 @@ func getString(clangString C.CXString) (s string) {
C.clang_disposeString(clangString) C.clang_disposeString(clangString)
return 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 предоставленный
Просмотреть файл

@ -9,3 +9,7 @@ int fortytwo() {
int add(int a, int b) { int add(int a, int b) {
return a + b; return a + b;
} }
void store(int value, int *ptr) {
*ptr = value;
}

6
testdata/cgo/main.go предоставленный
Просмотреть файл

@ -17,4 +17,10 @@ func main() {
var y C.longlong = -(1 << 40) var y C.longlong = -(1 << 40)
println("longlong:", y) println("longlong:", y)
println("global:", C.global) 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 предоставленный
Просмотреть файл

@ -1,3 +1,9 @@
typedef short myint; typedef short myint;
int add(int a, int b); 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; extern int global;

2
testdata/cgo/out.txt предоставленный
Просмотреть файл

@ -4,3 +4,5 @@ myint: 3 5
myint size: 2 myint size: 2
longlong: -1099511627776 longlong: -1099511627776
global: 3 global: 3
15: 15
25: 25