This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Fix PR29983 (iwmmxt ICE)
- From: Paul Brook <paul at codesourcery dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Thu, 1 Mar 2007 22:58:26 +0000
- Subject: Fix PR29983 (iwmmxt ICE)
The attached patch fixes PR28893.
The root of the problem is that coprocessor loads (wldrd) have a larger
address offset range then core loads (ldrd). I see 3 solutions:
1) Add accurate constraints, and teach reload how to cope. This is tricky
because neither of the alternatives are a subset of the other.
2) Limit offsets to those accepted by ldrd.
3) Fixup bad ldrd during assembly generation using a pair or ldr instructions.
(1) seems unreasonably hard, so I've implemented a combination of (2) and (3).
My theory is that DImode values will usually live in core registers, so (2) is
best, and vector modes will usually live in coprocessor registers, so (3) is
best.
We already do (3) in several other cases, just not for writeback.
A side-effect of (2) is that we now allow reg+reg addresses. The secondary
reload classes also needed tweaking to accommodate this. This is effectively
the same as the VFP code.
The testcase is very fickle, depending on behavior of other passes and
register allocation choices. I haven't been able to come up with a sensible
testcase.
I'm fairly sure the bug is still latent on FSF head, but doesn't trigger
because the insn gets split into two SImode loads by an early RTL pass.
I think this is also a regression because ldrd support is newer than iwmmxt.
Tested with cross to arm-linux-gnueabi.
Applied to trunk.
4.2 requires minor tweaks to accommodate the Thumb-2 merge. I'll apply there
once I'm sure it builds.
Paul
2007-03-01 Paul Brook <paul@codesourcery.com>
* config/arm/arm.c (arm_legitimate_index_p): Limit iWMMXt addressing
modes to LDRD for DImode.
(output_move_double): Fixup out of range ldrd/strd.
(vfp_secondary_reload_class): Rename...
(coproc_secondary_reload_class): ... to this. Add wb argument.
* config/arm/arm.h (SECONDARY_OUTPUT_RELOAD_CLASS): Use
coproc_secondary_reload_class for CLASS_IWMMXT.
(SECONDARY_INPUT_RELOAD_CLASS): Ditto.
* arm-protos.h (coproc_secondary_reload_class): Update prototype.
Index: gcc/config/arm/arm.c
===================================================================
--- gcc/config/arm/arm.c (revision 163241)
+++ gcc/config/arm/arm.c (working copy)
@@ -3901,10 +3901,15 @@ arm_legitimate_index_p (enum machine_mod
&& (INTVAL (index) & 3) == 0);
if (TARGET_REALLY_IWMMXT && VALID_IWMMXT_REG_MODE (mode))
- return (code == CONST_INT
- && INTVAL (index) < 1024
- && INTVAL (index) > -1024
- && (INTVAL (index) & 3) == 0);
+ {
+ /* For DImode assume values will usually live in core regs
+ and only allow LDRD addressing modes. */
+ if (!TARGET_LDRD || mode != DImode)
+ return (code == CONST_INT
+ && INTVAL (index) < 1024
+ && INTVAL (index) > -1024
+ && (INTVAL (index) & 3) == 0);
+ }
if (arm_address_register_rtx_p (index, strict_p)
&& (GET_MODE_SIZE (mode) <= 4))
@@ -5839,12 +5844,12 @@ arm_eliminable_register (rtx x)
}
/* Return GENERAL_REGS if a scratch register required to reload x to/from
- VFP registers. Otherwise return NO_REGS. */
+ coprocessor registers. Otherwise return NO_REGS. */
enum reg_class
-vfp_secondary_reload_class (enum machine_mode mode, rtx x)
+coproc_secondary_reload_class (enum machine_mode mode, rtx x, bool wb)
{
- if (arm_coproc_mem_operand (x, FALSE) || s_register_operand (x, mode))
+ if (arm_coproc_mem_operand (x, wb) || s_register_operand (x, mode))
return NO_REGS;
return GENERAL_REGS;
@@ -9221,12 +9226,37 @@ output_move_double (rtx *operands)
output_asm_insn ("ldr%(d%)\t%0, [%1] @split", otherops);
}
else
- output_asm_insn ("ldr%(d%)\t%0, [%1, %2]!", otherops);
+ {
+ /* IWMMXT allows offsets larger than ldrd can handle,
+ fix these up with a pair of ldr. */
+ if (GET_CODE (otherops[2]) == CONST_INT
+ && (INTVAL(otherops[2]) <= -256
+ || INTVAL(otherops[2]) >= 256))
+ {
+ output_asm_insn ("ldr%?\t%0, [%1, %2]!", otherops);
+ otherops[0] = gen_rtx_REG (SImode, 1 + reg0);
+ output_asm_insn ("ldr%?\t%0, [%1, #4]", otherops);
+ }
+ else
+ output_asm_insn ("ldr%(d%)\t%0, [%1, %2]!", otherops);
+ }
}
else
{
- /* We only allow constant increments, so this is safe. */
- output_asm_insn ("ldr%(d%)\t%0, [%1], %2", otherops);
+ /* IWMMXT allows offsets larger than ldrd can handle,
+ fix these up with a pair of ldr. */
+ if (GET_CODE (otherops[2]) == CONST_INT
+ && (INTVAL(otherops[2]) <= -256
+ || INTVAL(otherops[2]) >= 256))
+ {
+ otherops[0] = gen_rtx_REG (SImode, 1 + reg0);
+ output_asm_insn ("ldr%?\t%0, [%1, #4]", otherops);
+ otherops[0] = operands[0];
+ output_asm_insn ("ldr%?\t%0, [%1], %2", otherops);
+ }
+ else
+ /* We only allow constant increments, so this is safe. */
+ output_asm_insn ("ldr%(d%)\t%0, [%1], %2", otherops);
}
break;
@@ -9364,7 +9394,29 @@ output_move_double (rtx *operands)
otherops[1] = XEXP (XEXP (XEXP (operands[0], 0), 1), 0);
otherops[2] = XEXP (XEXP (XEXP (operands[0], 0), 1), 1);
- if (GET_CODE (XEXP (operands[0], 0)) == PRE_MODIFY)
+ /* IWMMXT allows offsets larger than ldrd can handle,
+ fix these up with a pair of ldr. */
+ if (GET_CODE (otherops[2]) == CONST_INT
+ && (INTVAL(otherops[2]) <= -256
+ || INTVAL(otherops[2]) >= 256))
+ {
+ rtx reg1;
+ reg1 = gen_rtx_REG (SImode, 1 + REGNO (operands[1]));
+ if (GET_CODE (XEXP (operands[0], 0)) == PRE_MODIFY)
+ {
+ output_asm_insn ("ldr%?\t%0, [%1, %2]!", otherops);
+ otherops[0] = reg1;
+ output_asm_insn ("ldr%?\t%0, [%1, #4]", otherops);
+ }
+ else
+ {
+ otherops[0] = reg1;
+ output_asm_insn ("ldr%?\t%0, [%1, #4]", otherops);
+ otherops[0] = operands[1];
+ output_asm_insn ("ldr%?\t%0, [%1], %2", otherops);
+ }
+ }
+ else if (GET_CODE (XEXP (operands[0], 0)) == PRE_MODIFY)
output_asm_insn ("str%(d%)\t%0, [%1, %2]!", otherops);
else
output_asm_insn ("str%(d%)\t%0, [%1], %2", otherops);
Index: gcc/config/arm/arm.h
===================================================================
--- gcc/config/arm/arm.h (revision 163241)
+++ gcc/config/arm/arm.h (working copy)
@@ -1130,10 +1130,12 @@ enum reg_class
or out of a register in CLASS in MODE. If it can be done directly,
NO_REGS is returned. */
#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \
- /* Restrict which direct reloads are allowed for VFP regs. */ \
+ /* Restrict which direct reloads are allowed for VFP/iWMMXt regs. */ \
((TARGET_VFP && TARGET_HARD_FLOAT \
&& (CLASS) == VFP_REGS) \
- ? vfp_secondary_reload_class (MODE, X) \
+ ? coproc_secondary_reload_class (MODE, X, FALSE) \
+ : (TARGET_IWMMXT && (CLASS) == IWMMXT_REGS) \
+ ? coproc_secondary_reload_class (MODE, X, TRUE) \
: TARGET_32BIT \
? (((MODE) == HImode && ! arm_arch4 && true_regnum (X) == -1) \
? GENERAL_REGS : NO_REGS) \
@@ -1141,10 +1143,12 @@ enum reg_class
/* If we need to load shorts byte-at-a-time, then we need a scratch. */
#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \
- /* Restrict which direct reloads are allowed for VFP regs. */ \
+ /* Restrict which direct reloads are allowed for VFP/iWMMXt regs. */ \
((TARGET_VFP && TARGET_HARD_FLOAT \
&& (CLASS) == VFP_REGS) \
- ? vfp_secondary_reload_class (MODE, X) : \
+ ? coproc_secondary_reload_class (MODE, X, FALSE) : \
+ (TARGET_IWMMXT && (CLASS) == IWMMXT_REGS) ? \
+ coproc_secondary_reload_class (MODE, X, TRUE) : \
/* Cannot load constants into Cirrus registers. */ \
(TARGET_MAVERICK && TARGET_HARD_FLOAT \
&& (CLASS) == CIRRUS_REGS \
Index: gcc/config/arm/arm-protos.h
===================================================================
--- gcc/config/arm/arm-protos.h (revision 163241)
+++ gcc/config/arm/arm-protos.h (working copy)
@@ -69,7 +69,8 @@ extern rtx thumb_legitimize_reload_addre
int);
extern int arm_const_double_rtx (rtx);
extern int neg_const_double_rtx_ok_for_fpa (rtx);
-extern enum reg_class vfp_secondary_reload_class (enum machine_mode, rtx);
+extern enum reg_class coproc_secondary_reload_class (enum machine_mode, rtx,
+ bool);
extern bool arm_tls_referenced_p (rtx);
extern int cirrus_memory_offset (rtx);