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 { | ||||
| 			// zero-length data | ||||
| 			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 { | ||||
| 			load := c.builder.CreateLoad(x, "") | ||||
| 			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 { | ||||
| 		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 | ||||
| 			} | ||||
| 			parts := strings.Fields(comment.Text) | ||||
| 			parts := strings.Fields(text) | ||||
| 			switch parts[0] { | ||||
| 			case "//go:export": | ||||
| 				if len(parts) != 2 { | ||||
|  | @ -322,7 +328,7 @@ func (f *Function) IsNoBounds() bool { | |||
| 
 | ||||
| // Return true iff this function is externally visible. | ||||
| 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 | ||||
|  | @ -330,7 +336,7 @@ func (f *Function) IsExported() bool { | |||
| // | ||||
| // On some platforms (like AVR), interrupts need a special compiler flag. | ||||
| func (f *Function) IsInterrupt() bool { | ||||
| 	return f.exported | ||||
| 	return f.interrupt | ||||
| } | ||||
| 
 | ||||
| // 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. | ||||
| 	info.addFuncDecls() | ||||
| 
 | ||||
| 	// Declare stub function pointer values found by libclang. | ||||
| 	info.addFuncPtrDecls() | ||||
| 
 | ||||
| 	// Declare globals found by libclang. | ||||
| 	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. | ||||
| // 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). | ||||
| func (info *fileInfo) walker(cursor *astutil.Cursor) bool { | ||||
| 	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: | ||||
| 		x, ok := node.X.(*ast.Ident) | ||||
| 		if !ok { | ||||
| 			return true | ||||
| 		} | ||||
| 		if x.Name == "C" { | ||||
| 			name := "C." + node.Sel.Name | ||||
| 			if _, ok := info.functions[node.Sel.Name]; ok { | ||||
| 				name += "$funcaddr" | ||||
| 			} | ||||
| 			cursor.Replace(&ast.Ident{ | ||||
| 				NamePos: x.NamePos, | ||||
| 				Name:    "C." + node.Sel.Name, | ||||
| 				Name:    name, | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ package loader | |||
| import ( | ||||
| 	"errors" | ||||
| 	"go/ast" | ||||
| 	"go/token" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"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 { | ||||
| 			return C.CXChildVisit_Continue // not supported | ||||
| 		} | ||||
| 		numArgs := C.clang_Cursor_getNumArguments(c) | ||||
| 		numArgs := int(C.clang_Cursor_getNumArguments(c)) | ||||
| 		fn := &functionInfo{} | ||||
| 		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)) | ||||
| 			argName := getString(C.clang_getCursorSpelling(arg)) | ||||
| 			argType := C.clang_getArgType(cursorType, C.uint(i)) | ||||
| 			if argName == "" { | ||||
| 				argName = "$" + strconv.Itoa(i) | ||||
| 			} | ||||
| 			fn.args = append(fn.args, paramInfo{ | ||||
| 				name:     argName, | ||||
| 				typeExpr: info.makeASTType(argType), | ||||
|  | @ -196,6 +201,23 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr { | |||
| 			Star: info.importCPos, | ||||
| 			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: | ||||
| 		// Fallback, probably incorrect but at least the error points to an odd | ||||
| 		// 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; | ||||
| } | ||||
| 
 | ||||
| int doCallback(int a, int b, binop_t callback) { | ||||
| 	return callback(a, b); | ||||
| } | ||||
| 
 | ||||
| void store(int value, int *ptr) { | ||||
| 	*ptr = value; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										10
									
								
								testdata/cgo/main.go
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										10
									
								
								testdata/cgo/main.go
									
										
									
									
										предоставленный
									
									
								
							|  | @ -3,6 +3,7 @@ package main | |||
| /* | ||||
| int fortytwo(void); | ||||
| #include "main.h" | ||||
| int mul(int, int); | ||||
| */ | ||||
| import "C" | ||||
| 
 | ||||
|  | @ -23,4 +24,13 @@ func main() { | |||
| 	println("15:", *ptr) | ||||
| 	C.store(25, &n) | ||||
| 	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; | ||||
| int add(int a, int b); | ||||
| typedef int (*binop_t) (int, int); | ||||
| int doCallback(int a, int b, binop_t cb); | ||||
| typedef int * intPointer; | ||||
| extern int global; | ||||
| void store(int value, int *ptr); | ||||
|  |  | |||
							
								
								
									
										2
									
								
								testdata/cgo/out.txt
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										2
									
								
								testdata/cgo/out.txt
									
										
									
									
										предоставленный
									
									
								
							|  | @ -6,3 +6,5 @@ longlong: -1099511627776 | |||
| global: 3 | ||||
| 15: 15 | ||||
| 25: 25 | ||||
| callback 1: 50 | ||||
| callback 2: 600 | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem