summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog7
-rw-r--r--gcc/calls.c41
-rw-r--r--gcc/config/arm/arm.c9
3 files changed, 55 insertions, 2 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 76fe48cff39..417807a970b 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,10 @@
+2011-08-01 Julian Brown <julian@codesourcery.com>
+
+ * calls.c (emit_library_call_value_1): Support padding for libcall
+ arguments and return values.
+ * config/arm/arm.c (arm_pad_arg_upward): Pad half-float values
+ downwards in big-endian mode.
+
2011-08-01 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
PR debug/49887
diff --git a/gcc/calls.c b/gcc/calls.c
index dfa9ceb5563..7ad30b4245c 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -3829,13 +3829,41 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
-
+ int size = 0;
+
/* Handle calls that pass values in multiple non-contiguous
locations. The PA64 has examples of this for library calls. */
if (reg != 0 && GET_CODE (reg) == PARALLEL)
emit_group_load (reg, val, NULL_TREE, GET_MODE_SIZE (mode));
else if (reg != 0 && partial == 0)
- emit_move_insn (reg, val);
+ {
+ emit_move_insn (reg, val);
+#ifdef BLOCK_REG_PADDING
+ size = GET_MODE_SIZE (argvec[argnum].mode);
+
+ /* Copied from load_register_parameters. */
+
+ /* Handle case where we have a value that needs shifting
+ up to the msb. eg. a QImode value and we're padding
+ upward on a BYTES_BIG_ENDIAN machine. */
+ if (size < UNITS_PER_WORD
+ && (argvec[argnum].locate.where_pad
+ == (BYTES_BIG_ENDIAN ? upward : downward)))
+ {
+ rtx x;
+ int shift = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
+
+ /* Assigning REG here rather than a temp makes CALL_FUSAGE
+ report the whole reg as used. Strictly speaking, the
+ call only uses SIZE bytes at the msb end, but it doesn't
+ seem worth generating rtl to say that. */
+ reg = gen_rtx_REG (word_mode, REGNO (reg));
+ x = expand_shift (LSHIFT_EXPR, word_mode, reg, shift, reg, 1);
+ if (x != reg)
+ emit_move_insn (reg, x);
+ }
+#endif
+ }
NO_DEFER_POP;
}
@@ -3901,6 +3929,15 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
valreg,
old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far);
+ /* Right-shift returned value if necessary. */
+ if (!pcc_struct_value
+ && TYPE_MODE (tfom) != BLKmode
+ && targetm.calls.return_in_msb (tfom))
+ {
+ shift_return_value (TYPE_MODE (tfom), false, valreg);
+ valreg = gen_rtx_REG (TYPE_MODE (tfom), REGNO (valreg));
+ }
+
/* For calls to `setjmp', etc., inform function.c:setjmp_warnings
that it should complain if nonvolatile values are live. For
functions that cannot return, inform flow that control does not
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 4193515b342..e0b8c3dd4a0 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -11261,6 +11261,15 @@ arm_pad_arg_upward (enum machine_mode mode, const_tree type)
if (type && BYTES_BIG_ENDIAN && INTEGRAL_TYPE_P (type))
return false;
+ /* Half-float values are only passed to libcalls, not regular functions.
+ They should be passed and returned as "short"s (see RTABI). To achieve
+ that effect in big-endian mode, pad downwards so the value is passed in
+ the least-significant end of the register. ??? This needs to be here
+ rather than in arm_pad_reg_upward due to peculiarity in the handling of
+ libcall arguments. */
+ if (BYTES_BIG_ENDIAN && mode == HFmode)
+ return false;
+
return true;
}