summaryrefslogtreecommitdiff
path: root/gcc/config/arm/arm.c
diff options
context:
space:
mode:
authorWei Guozhi <carrot@google.com>2011-06-11 02:28:35 +0000
committerWei Guozhi <carrot@gcc.gnu.org>2011-06-11 02:28:35 +0000
commitba68a139acb11620867d3202fd708d655082ef86 (patch)
tree1543c3e468e1422a10357fa1a8ed474c5aa0e2c2 /gcc/config/arm/arm.c
parentd351f68e1cda392fc68fef0baae7ff6ad5441234 (diff)
downloadgcc-ba68a139acb11620867d3202fd708d655082ef86.tar.gz
ldmstm.md (ldm2_ia, [...]): Add condition !arm_arch7 to these insns.
* config/arm/ldmstm.md (ldm2_ia, stm2_ia, ldm2_ib, stm2_ib, ldm2_da, stm2_da, ldm2_db, stm2_db): Add condition !arm_arch7 to these insns. (ldrd, ldrd_reg1, ldrd_reg2 and peephole2): New insn patterns and related peephole2. (strd, strd_reg1, strd_reg2 and peephole2): New insn patterns and related peephole2. * config/arm/arm-protos.h (arm_check_ldrd_operands): New prototype. (arm_legitimate_ldrd_p): New prototype. (arm_output_ldrd): New prototype. * config/arm/arm.c (arm_check_ldrd_operands): New function. (arm_legitimate_ldrd_p): New function. (arm_output_ldrd): New function. * gcc.target/arm/pr45335.c: New test. * gcc.target/arm/pr45335-2.c: New test. * gcc.target/arm/pr45335-3.c: New test. * gcc.target/arm/pr40457-1.c: Add another possible output "ldrd". * gcc.target/arm/pr40457-2.c: Changed to store 3 words. * gcc.target/arm/pr40457-3.c: Changed to store 3 words. From-SVN: r174940
Diffstat (limited to 'gcc/config/arm/arm.c')
-rw-r--r--gcc/config/arm/arm.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index c1bae17be06..efc2bcb1ee5 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -23853,4 +23853,234 @@ arm_attr_length_push_multi(rtx parallel_op, rtx first_op)
return 4;
}
+/* Check the validity of operands in an ldrd/strd instruction. */
+bool
+arm_check_ldrd_operands (rtx reg1, rtx reg2, rtx off1, rtx off2)
+{
+ HOST_WIDE_INT offset1 = 0;
+ HOST_WIDE_INT offset2 = 0;
+ int regno1 = REGNO (reg1);
+ int regno2 = REGNO (reg2);
+ HOST_WIDE_INT max_offset = 1020;
+
+ if (TARGET_ARM)
+ max_offset = 255;
+
+ if (off1 != NULL_RTX)
+ offset1 = INTVAL (off1);
+ if (off2 != NULL_RTX)
+ offset2 = INTVAL (off2);
+
+ /* The offset range of LDRD is [-max_offset, max_offset]. Here we check if
+ both offsets lie in the range [-max_offset, max_offset+4]. If one of the
+ offsets is max_offset+4, the following condition
+ ((offset1 + 4) == offset2)
+ will ensure offset1 to be max_offset, suitable for instruction LDRD. */
+ if ((offset1 > (max_offset + 4)) || (offset1 < -max_offset)
+ || ((offset1 & 3) != 0))
+ return false;
+ if ((offset2 > (max_offset + 4)) || (offset2 < -max_offset)
+ || ((offset2 & 3) != 0))
+ return false;
+
+ if ((offset1 + 4) == offset2)
+ {
+ if (TARGET_THUMB2)
+ return true;
+
+ /* TARGET_ARM */
+ if (((regno1 & 1) == 0) && ((regno1 + 1) == regno2)) /* ldrd */
+ return true;
+
+ if ((regno1 < regno2) && ((offset1 <= 4) && (offset1 >= -8))) /* ldm */
+ return true;
+ }
+ if ((offset2 + 4) == offset1)
+ {
+ if (TARGET_THUMB2)
+ return true;
+
+ /* TARGET_ARM */
+ if (((regno2 & 1) == 0) && ((regno2 + 1) == regno1)) /* ldrd */
+ return true;
+
+ if ((regno2 < regno1) && ((offset2 <= 4) && (offset2 >= -8))) /* ldm */
+ return true;
+ }
+
+ return false;
+}
+
+/* Check if the two memory accesses can be merged to an ldrd/strd instruction.
+ That is they use the same base register, and the gap between constant
+ offsets should be 4. */
+bool
+arm_legitimate_ldrd_p (rtx reg1, rtx reg2, rtx mem1, rtx mem2, bool ldrd)
+{
+ rtx base1, base2;
+ rtx offset1 = NULL_RTX;
+ rtx offset2 = NULL_RTX;
+ rtx addr1 = XEXP (mem1, 0);
+ rtx addr2 = XEXP (mem2, 0);
+
+ if (MEM_VOLATILE_P (mem1) || MEM_VOLATILE_P (mem2))
+ return false;
+
+ if (REG_P (addr1))
+ base1 = addr1;
+ else if (GET_CODE (addr1) == PLUS)
+ {
+ base1 = XEXP (addr1, 0);
+ offset1 = XEXP (addr1, 1);
+ if (!REG_P (base1) || (GET_CODE (offset1) != CONST_INT))
+ return false;
+ }
+ else
+ return false;
+
+ if (REG_P (addr2))
+ base2 = addr2;
+ else if (GET_CODE (addr2) == PLUS)
+ {
+ base2 = XEXP (addr2, 0);
+ offset2 = XEXP (addr2, 1);
+ if (!REG_P (base2) || (GET_CODE (offset2) != CONST_INT))
+ return false;
+ }
+ else
+ return false;
+
+ if (base1 != base2)
+ return false;
+
+ if (ldrd && ((reg1 == reg2) || (reg1 == base1)))
+ return false;
+
+ return arm_check_ldrd_operands (reg1, reg2, offset1, offset2);
+}
+
+/* Output instructions for ldrd and count the number of bytes has been
+ outputted. Do not actually output instructions if EMIT_P is false. */
+int
+arm_output_ldrd (rtx reg1, rtx reg2, rtx base, rtx off1, rtx off2, bool emit_p)
+{
+ int length = 0;
+ rtx operands[5];
+ HOST_WIDE_INT offset1 = 0;
+ HOST_WIDE_INT offset2 = 0;
+
+ if (off1 != NULL_RTX)
+ offset1 = INTVAL (off1);
+ else
+ off1 = GEN_INT (0);
+ if (off2 != NULL_RTX)
+ offset2 = INTVAL (off2);
+ else
+ off2 = GEN_INT (0);
+ if (offset1 > offset2)
+ {
+ rtx tmp;
+ HOST_WIDE_INT t = offset1; offset1 = offset2; offset2 = t;
+ tmp = off1; off1 = off2; off2 = tmp;
+ tmp = reg1; reg1 = reg2; reg2 = tmp;
+ }
+
+ operands[0] = reg1;
+ operands[1] = reg2;
+ operands[2] = base;
+ operands[3] = off1;
+ operands[4] = off2;
+
+ if (TARGET_THUMB2)
+ {
+ if (fix_cm3_ldrd && (base == reg1))
+ {
+ if (offset1 <= -256)
+ {
+ if (emit_p)
+ output_asm_insn ("sub\t%2, %2, %n3", operands);
+ length = 4;
+
+ if (emit_p)
+ output_asm_insn ("ldr\t%1, [%2, #4]", operands);
+ if (low_register_operand (reg2, SImode)
+ && low_register_operand (base, SImode))
+ length += 2;
+ else
+ length += 4;
+
+ if (emit_p)
+ output_asm_insn ("ldr\t%0, [%2]", operands);
+ if (low_register_operand (base, SImode))
+ length += 2;
+ else
+ length += 4;
+ }
+ else
+ {
+ if (emit_p)
+ output_asm_insn ("ldr\t%1, [%2, %4]", operands);
+ if (low_register_operand (reg2, SImode) && (offset2 >= 0)
+ && low_register_operand (base, SImode) && (offset2 < 128))
+ length += 2;
+ else
+ length += 4;
+
+ if (emit_p)
+ output_asm_insn ("ldr\t%0, [%2, %3]", operands);
+ if (low_register_operand (base, SImode)
+ && (offset1 >= 0) && (offset1 < 128))
+ length += 2;
+ else
+ length += 4;
+ }
+ }
+ else
+ {
+ if (emit_p)
+ output_asm_insn ("ldrd\t%0, %1, [%2, %3]", operands);
+ length = 4;
+ }
+ }
+ else /* TARGET_ARM */
+ {
+ if ((REGNO (reg2) == (REGNO (reg1) + 1)) && ((REGNO (reg1) & 1) == 0))
+ {
+ if (emit_p)
+ output_asm_insn ("ldrd\t%0, %1, [%2, %3]", operands);
+ length = 4;
+ }
+ else
+ {
+ if (emit_p)
+ {
+ switch (offset1)
+ {
+ case -8:
+ output_asm_insn ("ldm%(db%)\t%2, {%0, %1}", operands);
+ break;
+
+ case -4:
+ output_asm_insn ("ldm%(da%)\t%2, {%0, %1}", operands);
+ break;
+
+ case 0:
+ output_asm_insn ("ldm%(ia%)\t%2, {%0, %1}", operands);
+ break;
+
+ case 4:
+ output_asm_insn ("ldm%(ib%)\t%2, {%0, %1}", operands);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+ length = 4;
+ }
+ }
+
+ return length;
+}
+
#include "gt-arm.h"