mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-24 11:24:05 +08:00
033425d0ed
PR go/91781 reflect: promote integer closure return to full word The libffi library expects an integer return type to be promoted to a full word. Implement that when returning from a closure written in Go. This only matters on big-endian systems when returning an integer smaller than the pointer size, which is why we didn't notice it until now. Fixes https://gcc.gnu.org/PR91781. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/195858 From-SVN: r275813
253 lines
5.8 KiB
C
253 lines
5.8 KiB
C
/* go-reflect-call.c -- call reflection support for Go.
|
|
|
|
Copyright 2009 The Go Authors. All rights reserved.
|
|
Use of this source code is governed by a BSD-style
|
|
license that can be found in the LICENSE file. */
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "runtime.h"
|
|
#include "go-assert.h"
|
|
|
|
#ifdef USE_LIBFFI
|
|
#include "ffi.h"
|
|
#endif
|
|
|
|
#if defined(USE_LIBFFI) && FFI_GO_CLOSURES
|
|
|
|
/* The functions in this file are only called from reflect_call. As
|
|
reflect_call calls a libffi function, which will be compiled
|
|
without -fsplit-stack, it will always run with a large stack. */
|
|
|
|
static size_t go_results_size (const struct functype *)
|
|
__attribute__ ((no_split_stack));
|
|
static void go_set_results (const struct functype *, unsigned char *, void **)
|
|
__attribute__ ((no_split_stack));
|
|
|
|
/* Get the total size required for the result parameters of a
|
|
function. */
|
|
|
|
static size_t
|
|
go_results_size (const struct functype *func)
|
|
{
|
|
int count;
|
|
const struct _type **types;
|
|
size_t off;
|
|
size_t maxalign;
|
|
int i;
|
|
|
|
count = func->out.__count;
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
types = (const struct _type **) func->out.__values;
|
|
|
|
/* A single integer return value is always promoted to a full word.
|
|
There is similar code below and in libgo/go/reflect/makefunc_ffi.go.*/
|
|
if (count == 1)
|
|
{
|
|
switch (types[0]->kind & kindMask)
|
|
{
|
|
case kindBool:
|
|
case kindInt8:
|
|
case kindInt16:
|
|
case kindInt32:
|
|
case kindUint8:
|
|
case kindUint16:
|
|
case kindUint32:
|
|
return sizeof (ffi_arg);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
off = 0;
|
|
maxalign = 0;
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
size_t align;
|
|
|
|
align = types[i]->fieldAlign;
|
|
if (align > maxalign)
|
|
maxalign = align;
|
|
off = (off + align - 1) & ~ (align - 1);
|
|
off += types[i]->size;
|
|
}
|
|
|
|
off = (off + maxalign - 1) & ~ (maxalign - 1);
|
|
|
|
// The libffi library doesn't understand a struct with no fields.
|
|
// We generate a struct with a single field of type void. When used
|
|
// as a return value, libffi will think that requires a byte.
|
|
if (off == 0)
|
|
off = 1;
|
|
|
|
return off;
|
|
}
|
|
|
|
/* Copy the results of calling a function via FFI from CALL_RESULT
|
|
into the addresses in RESULTS. */
|
|
|
|
static void
|
|
go_set_results (const struct functype *func, unsigned char *call_result,
|
|
void **results)
|
|
{
|
|
int count;
|
|
const struct _type **types;
|
|
size_t off;
|
|
int i;
|
|
|
|
count = func->out.__count;
|
|
if (count == 0)
|
|
return;
|
|
|
|
types = (const struct _type **) func->out.__values;
|
|
|
|
/* A single integer return value is always promoted to a full word.
|
|
There is similar code above and in libgo/go/reflect/makefunc_ffi.go.*/
|
|
if (count == 1)
|
|
{
|
|
switch (types[0]->kind & kindMask)
|
|
{
|
|
case kindBool:
|
|
case kindInt8:
|
|
case kindInt16:
|
|
case kindInt32:
|
|
case kindUint8:
|
|
case kindUint16:
|
|
case kindUint32:
|
|
{
|
|
union
|
|
{
|
|
unsigned char buf[sizeof (ffi_arg)];
|
|
ffi_arg v;
|
|
} u;
|
|
ffi_arg v;
|
|
|
|
__builtin_memcpy (&u.buf, call_result, sizeof (ffi_arg));
|
|
v = u.v;
|
|
|
|
switch (types[0]->size)
|
|
{
|
|
case 1:
|
|
{
|
|
uint8_t b;
|
|
|
|
b = (uint8_t) v;
|
|
__builtin_memcpy (results[0], &b, 1);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
uint16_t s;
|
|
|
|
s = (uint16_t) v;
|
|
__builtin_memcpy (results[0], &s, 2);
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
{
|
|
uint32_t w;
|
|
|
|
w = (uint32_t) v;
|
|
__builtin_memcpy (results[0], &w, 4);
|
|
}
|
|
break;
|
|
|
|
case 8:
|
|
{
|
|
uint64_t d;
|
|
|
|
d = (uint64_t) v;
|
|
__builtin_memcpy (results[0], &d, 8);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
abort ();
|
|
}
|
|
}
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
off = 0;
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
size_t align;
|
|
size_t size;
|
|
|
|
align = types[i]->fieldAlign;
|
|
size = types[i]->size;
|
|
off = (off + align - 1) & ~ (align - 1);
|
|
__builtin_memcpy (results[i], call_result + off, size);
|
|
off += size;
|
|
}
|
|
}
|
|
|
|
/* The code that converts the Go type to an FFI type is written in Go,
|
|
so that it can allocate Go heap memory. */
|
|
extern void ffiFuncToCIF(const struct functype*, _Bool, _Bool, ffi_cif*)
|
|
__asm__ ("runtime.ffiFuncToCIF");
|
|
|
|
/* Call a function. The type of the function is FUNC_TYPE, and the
|
|
closure is FUNC_VAL. PARAMS is an array of parameter addresses.
|
|
RESULTS is an array of result addresses.
|
|
|
|
If IS_INTERFACE is true this is a call to an interface method and
|
|
the first argument is the receiver, which is always a pointer.
|
|
This argument, the receiver, is not described in FUNC_TYPE.
|
|
|
|
If IS_METHOD is true this is a call to a method expression. The
|
|
first argument is the receiver. It is described in FUNC_TYPE, but
|
|
regardless of FUNC_TYPE, it is passed as a pointer. */
|
|
|
|
void
|
|
reflect_call (const struct functype *func_type, FuncVal *func_val,
|
|
_Bool is_interface, _Bool is_method, void **params,
|
|
void **results)
|
|
{
|
|
ffi_cif cif;
|
|
unsigned char *call_result;
|
|
|
|
__go_assert ((func_type->typ.kind & kindMask) == kindFunc);
|
|
ffiFuncToCIF (func_type, is_interface, is_method, &cif);
|
|
|
|
call_result = (unsigned char *) malloc (go_results_size (func_type));
|
|
|
|
ffi_call_go (&cif, (void (*)(void)) func_val->fn, call_result, params,
|
|
func_val);
|
|
|
|
/* Some day we may need to free result values if RESULTS is
|
|
NULL. */
|
|
if (results != NULL)
|
|
go_set_results (func_type, call_result, results);
|
|
|
|
free (call_result);
|
|
}
|
|
|
|
#else /* !defined(USE_LIBFFI) */
|
|
|
|
void
|
|
reflect_call (const struct functype *func_type __attribute__ ((unused)),
|
|
FuncVal *func_val __attribute__ ((unused)),
|
|
_Bool is_interface __attribute__ ((unused)),
|
|
_Bool is_method __attribute__ ((unused)),
|
|
void **params __attribute__ ((unused)),
|
|
void **results __attribute__ ((unused)))
|
|
{
|
|
/* Without FFI there is nothing we can do. */
|
|
runtime_throw("libgo built without FFI does not support "
|
|
"reflect.Call or runtime.SetFinalizer");
|
|
}
|
|
|
|
#endif /* !defined(USE_LIBFFI) */
|