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));

Reply via email to