diff options
author | Ayke van Laethem <[email protected]> | 2019-02-07 13:20:19 +0100 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2019-02-08 13:19:02 +0100 |
commit | 35fb594f8f75129a1839b25e96cf82fa3357a0e0 (patch) | |
tree | 818199a97ca6022156ee742be96303ce252f731a | |
parent | 01f6aff4227120abf7dc98baef26f09d6576d856 (diff) | |
download | tinygo-35fb594f8f75129a1839b25e96cf82fa3357a0e0.tar.gz tinygo-35fb594f8f75129a1839b25e96cf82fa3357a0e0.zip |
loader/cgo: add support for pointer types
-rw-r--r-- | compiler/compiler.go | 6 | ||||
-rw-r--r-- | loader/cgo.go | 149 | ||||
-rw-r--r-- | loader/libclang.go | 112 | ||||
-rw-r--r-- | testdata/cgo/main.c | 4 | ||||
-rw-r--r-- | testdata/cgo/main.go | 6 | ||||
-rw-r--r-- | testdata/cgo/main.h | 6 | ||||
-rw-r--r-- | testdata/cgo/out.txt | 2 |
7 files changed, 165 insertions, 120 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go index 5fa6c217a..77a6dbe1a 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -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()) diff --git a/loader/cgo.go b/loader/cgo.go index 6aeae648f..5e09da61f 100644 --- a/loader/cgo.go +++ b/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 + args []paramInfo + 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 @@ -84,8 +80,11 @@ typedef unsigned long long _Cgo_ulonglong; // comment with libclang, and modifies the AST to use this information. func (p *Package) processCgo(filename string, f *ast.File, cflags []string) error { info := &fileInfo{ - File: f, - filename: filename, + 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" - } - 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_") { - newType = "C." + newType[len("C._Cgo_"):] + names := make([]string, 0, len(info.typedefs)) + for name := range info.typedefs { + names = append(names, name) + } + 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 _, 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 - } -} diff --git a/loader/libclang.go b/loader/libclang.go index 370c8b1e8..706f2ba23 100644 --- a/loader/libclang.go +++ b/loader/libclang.go @@ -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)) - typeSize := C.clang_Type_getSizeOf(underlyingType) - info.typedefs = append(info.typedefs, &typedefInfo{ - newName: name, - oldName: underlyingTypeName, - size: int(typeSize), - }) + expr := info.makeASTType(underlyingType) + if strings.HasPrefix(name, "_Cgo_") { + expr := expr.(*ast.Ident) + typeSize := C.clang_Type_getSizeOf(underlyingType) + 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, + } +} diff --git a/testdata/cgo/main.c b/testdata/cgo/main.c index 5fa47db98..8eb3d50ae 100644 --- a/testdata/cgo/main.c +++ b/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; +} diff --git a/testdata/cgo/main.go b/testdata/cgo/main.go index ba962e04c..0416a1b88 100644 --- a/testdata/cgo/main.go +++ b/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) } diff --git a/testdata/cgo/main.h b/testdata/cgo/main.h index 6c1202253..d4d205b84 100644 --- a/testdata/cgo/main.h +++ b/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; diff --git a/testdata/cgo/out.txt b/testdata/cgo/out.txt index e0cec2f4a..6414dfd3e 100644 --- a/testdata/cgo/out.txt +++ b/testdata/cgo/out.txt @@ -4,3 +4,5 @@ myint: 3 5 myint size: 2 longlong: -1099511627776 global: 3 +15: 15 +25: 25 |