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();
Этот коммит содержится в:
Ayke van Laethem 2021-02-07 16:02:16 +01:00 коммит произвёл Ron Evans
родитель 5502182642
коммит 2e9c3a1d8d
9 изменённых файлов: 77 добавлений и 11 удалений

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

@ -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{

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

@ -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++ {

10
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)
}

5
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

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

@ -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
}
}
}
}

8
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;
}

4
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

3
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

2
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