The FFI documentation doesn't mention it, but it turns out that the FFI library promotes integer result types. That is, if a function is declared as returning short, FFI will actually store the result as an int. The code in libgo was getting away with not supporting this because allocating one or two bytes would always permit storing four or eight bytes, and because I was only testing on little-endian systems. On big-endian systems this needs to be handled correctly. This patch does that. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Ran some but not all tests on sparc-sun-solaris2.11. Committed to mainline.
Ian
diff -r ff8b35faf485 libgo/runtime/go-reflect-call.c --- a/libgo/runtime/go-reflect-call.c Fri Feb 10 16:12:23 2012 -0800 +++ b/libgo/runtime/go-reflect-call.c Fri Feb 10 23:03:30 2012 -0800 @@ -5,6 +5,7 @@ license that can be found in the LICENSE file. */ #include <stdio.h> +#include <stdint.h> #include <stdlib.h> #include "ffi.h" @@ -326,6 +327,28 @@ 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) @@ -362,6 +385,81 @@ 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) { @@ -388,7 +486,7 @@ ffi_cif cif; unsigned char *call_result; - __go_assert (func_type->__common.__code == GO_FUNC); + __go_assert ((func_type->__common.__code & GO_CODE_MASK) == GO_FUNC); go_func_to_cif (func_type, is_interface, is_method, &cif); call_result = (unsigned char *) malloc (go_results_size (func_type));