aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Coplan <alex.coplan@arm.com>2020-05-18 16:29:04 +0100
committerAlex Coplan <alex.coplan@arm.com>2020-05-18 16:31:43 +0100
commit8b8f3117263ca79b3febadadb07732588d99d5f6 (patch)
treed6cebeaf9f743b78ad81dc883005418a59d2172e
parent94f687bd9ae37ece9391a3c42783c5768d26e81d (diff)
downloadgcc-8b8f3117263ca79b3febadadb07732588d99d5f6.zip
gcc-8b8f3117263ca79b3febadadb07732588d99d5f6.tar.gz
gcc-8b8f3117263ca79b3febadadb07732588d99d5f6.tar.bz2
[arm] Don't generate invalid LDRD insns
This fixes a bug in the arm backend where GCC generates invalid LDRD instructions. The LDRD instruction requires the first transfer register to be even, but GCC attempts to use odd registers here. For example, with the following C code: struct c { double a; } __attribute((aligned)) __attribute((packed)); struct c d; struct c f(struct c); void e() { f(d); } The struct d is passed in registers r1 and r2 to the function f, and GCC attempted to do this with a LDRD instruction when compiling with -march=armv7-a on a soft float toolchain. The fix is analogous to the corresponding one for STRD in the same function: https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=52057dc4ac5295caebf83147f688d769c93cbc8d 2020-05-18 Alex Coplan <alex.coplan@arm.com> gcc/: * config/arm/arm.c (output_move_double): Fix codegen when loading into a register pair with an odd base register. gcc/testsuite/: * gcc.c-torture/compile/packed-aligned-1.c: New test. * gcc.c-torture/execute/packed-aligned.c: New test.
-rw-r--r--gcc/ChangeLog5
-rw-r--r--gcc/config/arm/arm.c28
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/gcc.c-torture/compile/packed-aligned-1.c11
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/packed-aligned.c28
5 files changed, 64 insertions, 12 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 580b3cd..dffe88f 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,8 @@
+2020-05-18 Alex Coplan <alex.coplan@arm.com>
+
+ * config/arm/arm.c (output_move_double): Fix codegen when loading into
+ a register pair with an odd base register.
+
2020-05-18 Uroš Bizjak <ubizjak@gmail.com>
* config/i386/i386-expand.c (ix86_expand_fp_absneg_operator):
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 349918a..635e7ad 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -19575,6 +19575,7 @@ output_move_double (rtx *operands, bool emit, int *count)
if (code0 == REG)
{
unsigned int reg0 = REGNO (operands[0]);
+ const bool can_ldrd = TARGET_LDRD && (TARGET_THUMB2 || (reg0 % 2 == 0));
otherops[0] = gen_rtx_REG (SImode, 1 + reg0);
@@ -19586,7 +19587,7 @@ output_move_double (rtx *operands, bool emit, int *count)
if (emit)
{
- if (TARGET_LDRD
+ if (can_ldrd
&& !(fix_cm3_ldrd && reg0 == REGNO(XEXP (operands[1], 0))))
output_asm_insn ("ldrd%?\t%0, [%m1]", operands);
else
@@ -19595,7 +19596,7 @@ output_move_double (rtx *operands, bool emit, int *count)
break;
case PRE_INC:
- gcc_assert (TARGET_LDRD);
+ gcc_assert (can_ldrd);
if (emit)
output_asm_insn ("ldrd%?\t%0, [%m1, #8]!", operands);
break;
@@ -19603,7 +19604,7 @@ output_move_double (rtx *operands, bool emit, int *count)
case PRE_DEC:
if (emit)
{
- if (TARGET_LDRD)
+ if (can_ldrd)
output_asm_insn ("ldrd%?\t%0, [%m1, #-8]!", operands);
else
output_asm_insn ("ldmdb%?\t%m1!, %M0", operands);
@@ -19613,7 +19614,7 @@ output_move_double (rtx *operands, bool emit, int *count)
case POST_INC:
if (emit)
{
- if (TARGET_LDRD)
+ if (can_ldrd)
output_asm_insn ("ldrd%?\t%0, [%m1], #8", operands);
else
output_asm_insn ("ldmia%?\t%m1!, %M0", operands);
@@ -19621,7 +19622,7 @@ output_move_double (rtx *operands, bool emit, int *count)
break;
case POST_DEC:
- gcc_assert (TARGET_LDRD);
+ gcc_assert (can_ldrd);
if (emit)
output_asm_insn ("ldrd%?\t%0, [%m1], #-8", operands);
break;
@@ -19643,6 +19644,7 @@ output_move_double (rtx *operands, bool emit, int *count)
/* Registers overlap so split out the increment. */
if (emit)
{
+ gcc_assert (can_ldrd);
output_asm_insn ("add%?\t%1, %1, %2", otherops);
output_asm_insn ("ldrd%?\t%0, [%1] @split", otherops);
}
@@ -19654,10 +19656,11 @@ output_move_double (rtx *operands, bool emit, int *count)
/* Use a single insn if we can.
FIXME: IWMMXT allows offsets larger than ldrd can
handle, fix these up with a pair of ldr. */
- if (TARGET_THUMB2
+ if (can_ldrd
+ && (TARGET_THUMB2
|| !CONST_INT_P (otherops[2])
|| (INTVAL (otherops[2]) > -256
- && INTVAL (otherops[2]) < 256))
+ && INTVAL (otherops[2]) < 256)))
{
if (emit)
output_asm_insn ("ldrd%?\t%0, [%1, %2]!", otherops);
@@ -19680,10 +19683,11 @@ output_move_double (rtx *operands, bool emit, int *count)
/* Use a single insn if we can.
FIXME: IWMMXT allows offsets larger than ldrd can handle,
fix these up with a pair of ldr. */
- if (TARGET_THUMB2
+ if (can_ldrd
+ && (TARGET_THUMB2
|| !CONST_INT_P (otherops[2])
|| (INTVAL (otherops[2]) > -256
- && INTVAL (otherops[2]) < 256))
+ && INTVAL (otherops[2]) < 256)))
{
if (emit)
output_asm_insn ("ldrd%?\t%0, [%1], %2", otherops);
@@ -19714,7 +19718,7 @@ output_move_double (rtx *operands, bool emit, int *count)
operands[1] = otherops[0];
if (emit)
{
- if (TARGET_LDRD)
+ if (can_ldrd)
output_asm_insn ("ldrd%?\t%0, [%1]", operands);
else
output_asm_insn ("ldmia%?\t%1, %M0", operands);
@@ -19759,7 +19763,7 @@ output_move_double (rtx *operands, bool emit, int *count)
}
otherops[0] = gen_rtx_REG(SImode, REGNO(operands[0]) + 1);
operands[1] = otherops[0];
- if (TARGET_LDRD
+ if (can_ldrd
&& (REG_P (otherops[2])
|| TARGET_THUMB2
|| (CONST_INT_P (otherops[2])
@@ -19820,7 +19824,7 @@ output_move_double (rtx *operands, bool emit, int *count)
if (count)
*count = 2;
- if (TARGET_LDRD)
+ if (can_ldrd)
return "ldrd%?\t%0, [%1]";
return "ldmia%?\t%1, %M0";
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 33425f2..89423f5 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2020-05-18 Alex Coplan <alex.coplan@arm.com>
+ * gcc.c-torture/compile/packed-aligned-1.c: New test.
+ * gcc.c-torture/execute/packed-aligned.c: New test.
+
2020-05-18 Richard Biener <rguenther@suse.de>
PR middle-end/95171
diff --git a/gcc/testsuite/gcc.c-torture/compile/packed-aligned-1.c b/gcc/testsuite/gcc.c-torture/compile/packed-aligned-1.c
new file mode 100644
index 0000000..9f0923e
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/packed-aligned-1.c
@@ -0,0 +1,11 @@
+struct c {
+ double a;
+} __attribute((packed)) __attribute((aligned));
+
+void f(struct c *, struct c);
+
+void g(struct c *ptr)
+{
+ ptr++;
+ f(ptr, *ptr);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/packed-aligned.c b/gcc/testsuite/gcc.c-torture/execute/packed-aligned.c
new file mode 100644
index 0000000..f768af0
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/packed-aligned.c
@@ -0,0 +1,28 @@
+struct c {
+ double a;
+} __attribute((packed)) __attribute((aligned));
+
+extern void abort(void);
+
+double g_expect = 32.25;
+
+void f(unsigned x, struct c y)
+{
+ if (x != 0)
+ abort();
+
+ if (y.a != g_expect)
+ abort();
+}
+
+struct c e = { 64.25 };
+
+int main(void)
+{
+ struct c d = { 32.25 };
+ f(0, d);
+
+ g_expect = 64.25;
+ f(0, e);
+ return 0;
+}