8a9f2a6bbd
Compiling with LTO revealed a number of cases in the runtime and standard library where C and Go disagreed about the type of an object or function (or where Go and code generated by the compiler disagreed). In all cases the underlying representation was the same (e.g., uintptr vs. void*), so this wasn't causing actual problems, but it did result in a number of annoying warnings when compiling with LTO. Reviewed-on: https://go-review.googlesource.com/c/160700 From-SVN: r268923
259 lines
5.9 KiB
C
259 lines
5.9 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"
|
|
#include "go-type.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 __go_func_type *)
|
|
__attribute__ ((no_split_stack));
|
|
static void go_set_results (const struct __go_func_type *, 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 __go_func_type *func)
|
|
{
|
|
int count;
|
|
const struct __go_type_descriptor **types;
|
|
size_t off;
|
|
size_t maxalign;
|
|
int i;
|
|
|
|
count = func->__out.__count;
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
types = (const struct __go_type_descriptor **) func->__out.__values;
|
|
|
|
/* A single integer return value is always promoted to a full
|
|
word. */
|
|
if (count == 1)
|
|
{
|
|
switch (types[0]->__code & GO_CODE_MASK)
|
|
{
|
|
case GO_BOOL:
|
|
case GO_INT8:
|
|
case GO_INT16:
|
|
case GO_INT32:
|
|
case GO_UINT8:
|
|
case GO_UINT16:
|
|
case GO_UINT32:
|
|
case GO_INT:
|
|
case GO_UINT:
|
|
return sizeof (ffi_arg);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
off = 0;
|
|
maxalign = 0;
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
size_t align;
|
|
|
|
align = types[i]->__field_align;
|
|
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 __go_func_type *func, unsigned char *call_result,
|
|
void **results)
|
|
{
|
|
int count;
|
|
const struct __go_type_descriptor **types;
|
|
size_t off;
|
|
int i;
|
|
|
|
count = func->__out.__count;
|
|
if (count == 0)
|
|
return;
|
|
|
|
types = (const struct __go_type_descriptor **) func->__out.__values;
|
|
|
|
/* A single integer return value is always promoted to a full
|
|
word. */
|
|
if (count == 1)
|
|
{
|
|
switch (types[0]->__code & GO_CODE_MASK)
|
|
{
|
|
case GO_BOOL:
|
|
case GO_INT8:
|
|
case GO_INT16:
|
|
case GO_INT32:
|
|
case GO_UINT8:
|
|
case GO_UINT16:
|
|
case GO_UINT32:
|
|
case GO_INT:
|
|
case GO_UINT:
|
|
{
|
|
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]->__field_align;
|
|
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 __go_func_type*, _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 __go_func_type *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->__common.__code & GO_CODE_MASK) == GO_FUNC);
|
|
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 __go_func_type *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) */
|