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
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче