cgo: add support for variadic functions
This doesn't yet add support for actually making use of variadic
functions, but at least allows (unintended) variadic functions like the
following to work:
    void foo();
			
			
Этот коммит содержится в:
		
							родитель
							
								
									5502182642
								
							
						
					
					
						коммит
						2e9c3a1d8d
					
				
					 9 изменённых файлов: 77 добавлений и 11 удалений
				
			
		
							
								
								
									
										17
									
								
								cgo/cgo.go
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								cgo/cgo.go
									
										
									
									
									
								
							|  | @ -54,9 +54,10 @@ type constantInfo struct { | ||||||
| // 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 { | ||||||
| 	args    []paramInfo | 	args     []paramInfo | ||||||
| 	results *ast.FieldList | 	results  *ast.FieldList | ||||||
| 	pos     token.Pos | 	pos      token.Pos | ||||||
|  | 	variadic bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // paramInfo is a parameter of a CGo function (see functionInfo). | // paramInfo is a parameter of a CGo function (see functionInfo). | ||||||
|  | @ -484,6 +485,16 @@ func (p *cgoPackage) addFuncDecls() { | ||||||
| 				Results: fn.results, | 				Results: fn.results, | ||||||
| 			}, | 			}, | ||||||
| 		} | 		} | ||||||
|  | 		if fn.variadic { | ||||||
|  | 			decl.Doc = &ast.CommentGroup{ | ||||||
|  | 				List: []*ast.Comment{ | ||||||
|  | 					&ast.Comment{ | ||||||
|  | 						Slash: fn.pos, | ||||||
|  | 						Text:  "//go:variadic", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 		obj.Decl = decl | 		obj.Decl = decl | ||||||
| 		for i, arg := range fn.args { | 		for i, arg := range fn.args { | ||||||
| 			args[i] = &ast.Field{ | 			args[i] = &ast.Field{ | ||||||
|  |  | ||||||
|  | @ -152,12 +152,10 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient | ||||||
| 			return C.CXChildVisit_Continue | 			return C.CXChildVisit_Continue | ||||||
| 		} | 		} | ||||||
| 		cursorType := C.tinygo_clang_getCursorType(c) | 		cursorType := C.tinygo_clang_getCursorType(c) | ||||||
| 		if C.clang_isFunctionTypeVariadic(cursorType) != 0 { |  | ||||||
| 			return C.CXChildVisit_Continue // not supported |  | ||||||
| 		} |  | ||||||
| 		numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c)) | 		numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c)) | ||||||
| 		fn := &functionInfo{ | 		fn := &functionInfo{ | ||||||
| 			pos: pos, | 			pos:      pos, | ||||||
|  | 			variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0, | ||||||
| 		} | 		} | ||||||
| 		p.functions[name] = fn | 		p.functions[name] = fn | ||||||
| 		for i := 0; i < numArgs; i++ { | 		for i := 0; i < numArgs; i++ { | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								cgo/testdata/types.go
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										10
									
								
								cgo/testdata/types.go
									
										
									
									
										предоставленный
									
									
								
							|  | @ -104,6 +104,10 @@ typedef struct { | ||||||
| 	unsigned char e : 3; | 	unsigned char e : 3; | ||||||
| 	// Note that C++ allows bitfields bigger than the underlying type. | 	// Note that C++ allows bitfields bigger than the underlying type. | ||||||
| } bitfield_t; | } bitfield_t; | ||||||
|  | 
 | ||||||
|  | // Function signatures. | ||||||
|  | void variadic0(); | ||||||
|  | void variadic2(int x, int y, ...); | ||||||
| */ | */ | ||||||
| import "C" | import "C" | ||||||
| 
 | 
 | ||||||
|  | @ -163,3 +167,9 @@ func accessUnion() { | ||||||
| 	var _ *C.int = union2d.unionfield_i() | 	var _ *C.int = union2d.unionfield_i() | ||||||
| 	var _ *[2]float64 = union2d.unionfield_d() | 	var _ *[2]float64 = union2d.unionfield_d() | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // Test function signatures. | ||||||
|  | func accessFunctions() { | ||||||
|  | 	C.variadic0() | ||||||
|  | 	C.variadic2(3, 5) | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								cgo/testdata/types.out.go
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										5
									
								
								cgo/testdata/types.out.go
									
										
									
									
										предоставленный
									
									
								
							|  | @ -4,6 +4,11 @@ import "unsafe" | ||||||
| 
 | 
 | ||||||
| var _ unsafe.Pointer | var _ unsafe.Pointer | ||||||
| 
 | 
 | ||||||
|  | func C.variadic0() //go:variadic | ||||||
|  | func C.variadic2(x C.int, y C.int) //go:variadic | ||||||
|  | var C.variadic0$funcaddr unsafe.Pointer | ||||||
|  | var C.variadic2$funcaddr unsafe.Pointer | ||||||
|  | 
 | ||||||
| const C.option2A = 20 | const C.option2A = 20 | ||||||
| const C.optionA = 0 | const C.optionA = 0 | ||||||
| const C.optionB = 1 | const C.optionB = 1 | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ type functionInfo struct { | ||||||
| 	linkName string     // go:linkname, go:export | 	linkName string     // go:linkname, go:export | ||||||
| 	exported bool       // go:export, CGo | 	exported bool       // go:export, CGo | ||||||
| 	nobounds bool       // go:nobounds | 	nobounds bool       // go:nobounds | ||||||
|  | 	variadic bool       // go:variadic (CGo only) | ||||||
| 	inline   inlineType // go:inline | 	inline   inlineType // go:inline | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -88,8 +89,23 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value { | ||||||
| 		paramTypes = append(paramTypes, info.llvmType) | 		paramTypes = append(paramTypes, info.llvmType) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fnType := llvm.FunctionType(retType, paramTypes, false) | 	fnType := llvm.FunctionType(retType, paramTypes, info.variadic) | ||||||
| 	llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType) | 	llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType) | ||||||
|  | 	if strings.HasPrefix(c.Triple, "wasm") { | ||||||
|  | 		// C functions without prototypes like this: | ||||||
|  | 		//   void foo(); | ||||||
|  | 		// are actually variadic functions. However, it appears that it has been | ||||||
|  | 		// decided in WebAssembly that such prototype-less functions are not | ||||||
|  | 		// allowed in WebAssembly. | ||||||
|  | 		// In C, this can only happen when there are zero parameters, hence this | ||||||
|  | 		// check here. For more information: | ||||||
|  | 		// https://reviews.llvm.org/D48443 | ||||||
|  | 		// https://github.com/WebAssembly/tool-conventions/issues/16 | ||||||
|  | 		if info.variadic && len(fn.Params) == 0 { | ||||||
|  | 			attr := c.ctx.CreateStringAttribute("no-prototype", "") | ||||||
|  | 			llvmFn.AddFunctionAttr(attr) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null") | 	dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null") | ||||||
| 	for i, info := range paramInfos { | 	for i, info := range paramInfos { | ||||||
|  | @ -162,10 +178,9 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo { | ||||||
| 	} else { | 	} else { | ||||||
| 		// Pick the default linkName. | 		// Pick the default linkName. | ||||||
| 		info.linkName = f.RelString(nil) | 		info.linkName = f.RelString(nil) | ||||||
| 		// Check for //go: pragmas, which may change the link name (among |  | ||||||
| 		// others). |  | ||||||
| 		info.parsePragmas(f) |  | ||||||
| 	} | 	} | ||||||
|  | 	// Check for //go: pragmas, which may change the link name (among others). | ||||||
|  | 	info.parsePragmas(f) | ||||||
| 	return info | 	return info | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -223,6 +238,16 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { | ||||||
| 				if hasUnsafeImport(f.Pkg.Pkg) { | 				if hasUnsafeImport(f.Pkg.Pkg) { | ||||||
| 					info.nobounds = true | 					info.nobounds = true | ||||||
| 				} | 				} | ||||||
|  | 			case "//go:variadic": | ||||||
|  | 				// The //go:variadic pragma is emitted by the CGo preprocessing | ||||||
|  | 				// pass for C variadic functions. This includes both explicit | ||||||
|  | 				// (with ...) and implicit (no parameters in signature) | ||||||
|  | 				// functions. | ||||||
|  | 				if strings.HasPrefix(f.Name(), "C.") { | ||||||
|  | 					// This prefix cannot naturally be created, it must have | ||||||
|  | 					// been created as a result of CGo preprocessing. | ||||||
|  | 					info.variadic = true | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								testdata/cgo/main.c
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										8
									
								
								testdata/cgo/main.c
									
										
									
									
										предоставленный
									
									
								
							|  | @ -34,6 +34,14 @@ int doCallback(int a, int b, binop_t callback) { | ||||||
| 	return callback(a, b); | 	return callback(a, b); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int variadic0() { | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int variadic2(int x, int y, ...) { | ||||||
|  | 	return x * y; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void store(int value, int *ptr) { | void store(int value, int *ptr) { | ||||||
| 	*ptr = value; | 	*ptr = value; | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								testdata/cgo/main.go
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										4
									
								
								testdata/cgo/main.go
									
										
									
									
										предоставленный
									
									
								
							|  | @ -36,6 +36,10 @@ func main() { | ||||||
| 	cb = C.binop_t(C.mul) | 	cb = C.binop_t(C.mul) | ||||||
| 	println("callback 2:", C.doCallback(20, 30, cb)) | 	println("callback 2:", C.doCallback(20, 30, cb)) | ||||||
| 
 | 
 | ||||||
|  | 	// variadic functions | ||||||
|  | 	println("variadic0:", C.variadic0()) | ||||||
|  | 	println("variadic2:", C.variadic2(3, 5)) | ||||||
|  | 
 | ||||||
| 	// equivalent types | 	// equivalent types | ||||||
| 	var goInt8 int8 = 5 | 	var goInt8 int8 = 5 | ||||||
| 	var _ C.int8_t = goInt8 | 	var _ C.int8_t = goInt8 | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								testdata/cgo/main.h
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										3
									
								
								testdata/cgo/main.h
									
										
									
									
										предоставленный
									
									
								
							|  | @ -10,6 +10,9 @@ int doCallback(int a, int b, binop_t cb); | ||||||
| typedef int * intPointer; | typedef int * intPointer; | ||||||
| void store(int value, int *ptr); | void store(int value, int *ptr); | ||||||
| 
 | 
 | ||||||
|  | int variadic0(); | ||||||
|  | int variadic2(int x, int y, ...); | ||||||
|  | 
 | ||||||
| # define CONST_INT 5 | # define CONST_INT 5 | ||||||
| # define CONST_INT2 5llu | # define CONST_INT2 5llu | ||||||
| # define CONST_FLOAT 5.8 | # define CONST_FLOAT 5.8 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								testdata/cgo/out.txt
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										2
									
								
								testdata/cgo/out.txt
									
										
									
									
										предоставленный
									
									
								
							|  | @ -12,6 +12,8 @@ defined char: 99 | ||||||
| 25: 25 | 25: 25 | ||||||
| callback 1: 50 | callback 1: 50 | ||||||
| callback 2: 600 | callback 2: 600 | ||||||
|  | variadic0: 1 | ||||||
|  | variadic2: 15 | ||||||
| bool: true true | bool: true true | ||||||
| float: +3.100000e+000 | float: +3.100000e+000 | ||||||
| double: +3.200000e+000 | double: +3.200000e+000 | ||||||
|  |  | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem