summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Schulze Frielinghaus <stefansf@linux.ibm.com>2021-02-03 09:20:15 +0100
committerMarge Bot <ben+marge-bot@smart-cactus.org>2021-02-05 19:13:52 -0500
commit97a8fe7b356cbf22c331c86c35345fda446dc466 (patch)
tree4cd63de06d058e08fb3570a60e10f94fcb1daeda
parentd4618aeb878edb1aed12bad05a19fe25fe3bf0d3 (diff)
downloadhaskell-97a8fe7b356cbf22c331c86c35345fda446dc466.tar.gz
rts: Fix arguments for foreign calls of interpreter
Function arguments passed to the interpreter are extended to whole words. However, foreign function interface expects correctly typed argument pointers. Accordingly, we have to adjust argument pointers in case of a big-endian architecture. In contrast to function arguments where subwords are passed in the low bytes of a word, the return value is expected to reside in the high bytes of a word.
-rw-r--r--rts/Interpreter.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/rts/Interpreter.c b/rts/Interpreter.c
index 237b9c848a..cdfd7684ed 100644
--- a/rts/Interpreter.c
+++ b/rts/Interpreter.c
@@ -1775,7 +1775,18 @@ run_BCO:
// the call.
p = (StgPtr)arguments;
for (i = 0; i < nargs; i++) {
+#if defined(WORDS_BIGENDIAN)
+ // Arguments passed to the interpreter are extended to whole
+ // words. More precisely subwords are passed in the low bytes
+ // of a word. This means p must be adjusted in order to point
+ // to the proper subword. In all other cases the size of the
+ // argument type is a multiple of word size as e.g. for type
+ // double on 32bit machines and p must not be adjusted.
+ argptrs[i] = (void *)((StgWord8 *)p + (sizeof(W_) > cif->arg_types[i]->size
+ ? sizeof(W_) - cif->arg_types[i]->size : 0));
+#else
argptrs[i] = (void *)p;
+#endif
// get the size from the cif
p += ROUND_UP_WDS(cif->arg_types[i]->size);
}
@@ -1844,8 +1855,19 @@ run_BCO:
cap->r.rCurrentTSO->saved_errno = errno;
// Copy the return value back to the TSO stack. It is at
- // most 2 words large, and resides at arguments[0].
- memcpy(Sp, ret, sizeof(W_) * stg_min(stk_offset,ret_size));
+ // most 2 words large.
+#if defined(WORDS_BIGENDIAN)
+ if (sizeof(W_) >= cif->rtype->size) {
+ // In contrast to function arguments where subwords are passed
+ // in the low bytes of a word, the return value is expected to
+ // reside in the high bytes of a word.
+ SpW(0) = (*(StgPtr)ret) << ((sizeof(W_) - cif->rtype->size) * 8);
+ } else {
+ memcpy(Sp, ret, sizeof(W_) * ret_size);
+ }
+#else
+ memcpy(Sp, ret, sizeof(W_) * ret_size);
+#endif
goto nextInsn;
}