aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/arm/arm.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/arm/arm.c')
-rw-r--r--gcc/config/arm/arm.c30
1 files changed, 30 insertions, 0 deletions
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index c8c4301..bc802ad 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -793,6 +793,9 @@ static const struct attribute_spec arm_attribute_table[] =
#undef TARGET_MODES_TIEABLE_P
#define TARGET_MODES_TIEABLE_P arm_modes_tieable_p
+
+#undef TARGET_CAN_CHANGE_MODE_CLASS
+#define TARGET_CAN_CHANGE_MODE_CLASS arm_can_change_mode_class
/* Obstack for minipool constant handling. */
static struct obstack minipool_obstack;
@@ -31243,6 +31246,33 @@ arm_coproc_ldc_stc_legitimate_address (rtx op)
return false;
}
+/* Implement TARGET_CAN_CHANGE_MODE_CLASS.
+
+ In VFPv1, VFP registers could only be accessed in the mode they were
+ set, so subregs would be invalid there. However, we don't support
+ VFPv1 at the moment, and the restriction was lifted in VFPv2.
+
+ In big-endian mode, modes greater than word size (i.e. DFmode) are stored in
+ VFP registers in little-endian order. We can't describe that accurately to
+ GCC, so avoid taking subregs of such values.
+
+ The only exception is going from a 128-bit to a 64-bit type. In that
+ case the data layout happens to be consistent for big-endian, so we
+ explicitly allow that case. */
+
+static bool
+arm_can_change_mode_class (machine_mode from, machine_mode to,
+ reg_class_t rclass)
+{
+ if (TARGET_BIG_END
+ && !(GET_MODE_SIZE (from) == 16 && GET_MODE_SIZE (to) == 8)
+ && (GET_MODE_SIZE (from) > UNITS_PER_WORD
+ || GET_MODE_SIZE (to) > UNITS_PER_WORD)
+ && reg_classes_intersect_p (VFP_REGS, rclass))
+ return false;
+ return true;
+}
+
#if CHECKING_P
namespace selftest {