diff options
author | Ayke van Laethem <[email protected]> | 2021-02-07 16:02:16 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-02-11 09:51:15 +0100 |
commit | 2e9c3a1d8d5e59baee62ce12812f0ef37d112f33 (patch) | |
tree | 3f89a557d312c69c1466b0931a8ea22912551268 | |
parent | 550218264204aa62e9d602a1a380531040c18deb (diff) | |
download | tinygo-2e9c3a1d8d5e59baee62ce12812f0ef37d112f33.tar.gz tinygo-2e9c3a1d8d5e59baee62ce12812f0ef37d112f33.zip |
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();
-rw-r--r-- | cgo/cgo.go | 17 | ||||
-rw-r--r-- | cgo/libclang.go | 6 | ||||
-rw-r--r-- | cgo/testdata/types.go | 10 | ||||
-rw-r--r-- | cgo/testdata/types.out.go | 5 | ||||
-rw-r--r-- | compiler/symbol.go | 33 | ||||
-rw-r--r-- | testdata/cgo/main.c | 8 | ||||
-rw-r--r-- | testdata/cgo/main.go | 4 | ||||
-rw-r--r-- | testdata/cgo/main.h | 3 | ||||
-rw-r--r-- | testdata/cgo/out.txt | 2 |
9 files changed, 77 insertions, 11 deletions
diff --git a/cgo/cgo.go b/cgo/cgo.go index 7cc61b9ce..73d26e1d8 100644 --- a/cgo/cgo.go +++ b/cgo/cgo.go @@ -54,9 +54,10 @@ type constantInfo struct { // functionInfo stores some information about a CGo function found by libclang // and declared in the AST. type functionInfo struct { - args []paramInfo - results *ast.FieldList - pos token.Pos + args []paramInfo + results *ast.FieldList + pos token.Pos + variadic bool } // paramInfo is a parameter of a CGo function (see functionInfo). @@ -484,6 +485,16 @@ func (p *cgoPackage) addFuncDecls() { Results: fn.results, }, } + if fn.variadic { + decl.Doc = &ast.CommentGroup{ + List: []*ast.Comment{ + &ast.Comment{ + Slash: fn.pos, + Text: "//go:variadic", + }, + }, + } + } obj.Decl = decl for i, arg := range fn.args { args[i] = &ast.Field{ diff --git a/cgo/libclang.go b/cgo/libclang.go index 649494f12..f1ba3baa1 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -152,12 +152,10 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient return C.CXChildVisit_Continue } 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)) fn := &functionInfo{ - pos: pos, + pos: pos, + variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0, } p.functions[name] = fn for i := 0; i < numArgs; i++ { diff --git a/cgo/testdata/types.go b/cgo/testdata/types.go index 2aa0982a4..9edec55df 100644 --- a/cgo/testdata/types.go +++ b/cgo/testdata/types.go @@ -104,6 +104,10 @@ typedef struct { unsigned char e : 3; // Note that C++ allows bitfields bigger than the underlying type. } bitfield_t; + +// Function signatures. +void variadic0(); +void variadic2(int x, int y, ...); */ import "C" @@ -163,3 +167,9 @@ func accessUnion() { var _ *C.int = union2d.unionfield_i() var _ *[2]float64 = union2d.unionfield_d() } + +// Test function signatures. +func accessFunctions() { + C.variadic0() + C.variadic2(3, 5) +} diff --git a/cgo/testdata/types.out.go b/cgo/testdata/types.out.go index af638b9e7..c825d5dd5 100644 --- a/cgo/testdata/types.out.go +++ b/cgo/testdata/types.out.go @@ -4,6 +4,11 @@ import "unsafe" 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.optionA = 0 const C.optionB = 1 diff --git a/compiler/symbol.go b/compiler/symbol.go index 10051be6c..348eb5255 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -25,6 +25,7 @@ type functionInfo struct { linkName string // go:linkname, go:export exported bool // go:export, CGo nobounds bool // go:nobounds + variadic bool // go:variadic (CGo only) inline inlineType // go:inline } @@ -88,8 +89,23 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value { 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) + 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") for i, info := range paramInfos { @@ -162,10 +178,9 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo { } else { // Pick the default linkName. 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 } @@ -223,6 +238,16 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { if hasUnsafeImport(f.Pkg.Pkg) { 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 + } } } } diff --git a/testdata/cgo/main.c b/testdata/cgo/main.c index 8f3b09464..3a2e9c57e 100644 --- a/testdata/cgo/main.c +++ b/testdata/cgo/main.c @@ -34,6 +34,14 @@ int doCallback(int a, int b, binop_t callback) { return callback(a, b); } +int variadic0() { + return 1; +} + +int variadic2(int x, int y, ...) { + return x * y; +} + void store(int value, int *ptr) { *ptr = value; } diff --git a/testdata/cgo/main.go b/testdata/cgo/main.go index 616bc5bfc..d9e2d7297 100644 --- a/testdata/cgo/main.go +++ b/testdata/cgo/main.go @@ -36,6 +36,10 @@ func main() { cb = C.binop_t(C.mul) println("callback 2:", C.doCallback(20, 30, cb)) + // variadic functions + println("variadic0:", C.variadic0()) + println("variadic2:", C.variadic2(3, 5)) + // equivalent types var goInt8 int8 = 5 var _ C.int8_t = goInt8 diff --git a/testdata/cgo/main.h b/testdata/cgo/main.h index b4ac4ee88..857a796ef 100644 --- a/testdata/cgo/main.h +++ b/testdata/cgo/main.h @@ -10,6 +10,9 @@ int doCallback(int a, int b, binop_t cb); typedef int * intPointer; void store(int value, int *ptr); +int variadic0(); +int variadic2(int x, int y, ...); + # define CONST_INT 5 # define CONST_INT2 5llu # define CONST_FLOAT 5.8 diff --git a/testdata/cgo/out.txt b/testdata/cgo/out.txt index 9da7386a1..31fd4d330 100644 --- a/testdata/cgo/out.txt +++ b/testdata/cgo/out.txt @@ -12,6 +12,8 @@ defined char: 99 25: 25 callback 1: 50 callback 2: 600 +variadic0: 1 +variadic2: 15 bool: true true float: +3.100000e+000 double: +3.200000e+000 |