In PR target/59216 we have a case where the compiler generates incorrect
code for (neg:DI (sign_extend:DI (reg:SI))).  This splitter pattern
generates the wrong output when the register contains INT_MIN.

The shortest sequence we can use here is three insns, but since there
are cases where not having the splitter pattern might result in 4, it's
still beneficial to keep the pattern and to split later, once we know
the register allocation.

Bootstrapped/tested on arm-linux-gnueabihi.

        PR target/59216

gcc/
        * arm.md (negdi_extendsidi): Fix invalid split.

gcc/testsuite/
        * gcc.target/arm/negdi-4.c: Delete invalid test.
        * gcc.dg/torture/pr59216.c: New test.

Applied to trunk.

R.
Index: gcc/testsuite/gcc.target/arm/negdi-4.c
===================================================================
--- gcc/testsuite/gcc.target/arm/negdi-4.c      (revision 205116)
+++ gcc/testsuite/gcc.target/arm/negdi-4.c      (working copy)
@@ -1,16 +0,0 @@
-/* { dg-do compile } */
-/* { dg-require-effective-target arm32 } */
-/* { dg-options "-O2" } */
-
-signed long long negdi_extendsidi (signed int x)
-{
-  return -((signed long long) x);
-}
-/*
-Expected output:
-        rsbs    r0, r0, #0
-        mov     r1, r0, asr #31
-*/
-/* { dg-final { scan-assembler-times "rsb" 1 } } */
-/* { dg-final { scan-assembler-times "asr" 1 } } */
-/* { dg-final { scan-assembler-times "rsc" 0 } } */
Index: gcc/testsuite/gcc.dg/torture/pr59216.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/pr59216.c      (revision 0)
+++ gcc/testsuite/gcc.dg/torture/pr59216.c      (revision 0)
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+
+#include <limits.h>
+
+extern void abort (void);
+extern void exit (int);
+
+long long __attribute__((noinline)) f(int a)
+{
+  return -(long long) a;
+}
+
+int
+main()
+{
+  if (f(0) != 0)
+    abort ();
+
+  if (f(1) != -(long long)1)
+    abort ();
+
+  if (f(-1) != -(long long)-1)
+    abort ();
+
+  if (f(INT_MIN) != -(long long)INT_MIN)
+    abort ();
+
+  if (f(INT_MAX) != -(long long)INT_MAX)
+    abort ();
+
+  exit (0);
+}
Index: gcc/config/arm/arm.md
===================================================================
--- gcc/config/arm/arm.md       (revision 205116)
+++ gcc/config/arm/arm.md       (working copy)
@@ -4710,47 +4710,74 @@ (define_expand "negdf2"
 
 ;; Negate an extended 32-bit value.
 (define_insn_and_split "*negdi_extendsidi"
-  [(set (match_operand:DI 0 "s_register_operand" "=r,&r,l,&l")
-       (neg:DI (sign_extend:DI (match_operand:SI 1 "s_register_operand" 
"0,r,0,l"))))
+  [(set (match_operand:DI 0 "s_register_operand" "=l,r")
+       (neg:DI (sign_extend:DI
+                (match_operand:SI 1 "s_register_operand" "l,r"))))
    (clobber (reg:CC CC_REGNUM))]
   "TARGET_32BIT"
-  "#" ; rsb\\t%Q0, %1, #0\;asr\\t%R0, %Q0, #31
+  "#"
   "&& reload_completed"
   [(const_int 0)]
   {
-     operands[2] = gen_highpart (SImode, operands[0]);
-     operands[0] = gen_lowpart (SImode, operands[0]);
-     rtx tmp = gen_rtx_SET (VOIDmode,
-                            operands[0],
-                            gen_rtx_MINUS (SImode,
-                                           const0_rtx,
-                                           operands[1]));
-     if (TARGET_ARM)
-       {
-         emit_insn (tmp);
-       }
-     else
-       {
-         /* Set the flags, to emit the short encoding in Thumb2.  */
-         rtx flags = gen_rtx_SET (VOIDmode,
-                                  gen_rtx_REG (CCmode, CC_REGNUM),
-                                  gen_rtx_COMPARE (CCmode,
-                                                   const0_rtx,
-                                                   operands[1]));
-         emit_insn (gen_rtx_PARALLEL (VOIDmode,
-                                      gen_rtvec (2,
-                                                 flags,
-                                                 tmp)));
-       }
-       emit_insn (gen_rtx_SET (VOIDmode,
-                              operands[2],
-                              gen_rtx_ASHIFTRT (SImode,
-                                                operands[0],
-                                                GEN_INT (31))));
-     DONE;
+    rtx low = gen_lowpart (SImode, operands[0]);
+    rtx high = gen_highpart (SImode, operands[0]);
+
+    if (reg_overlap_mentioned_p (low, operands[1]))
+      {
+       /* Input overlaps the low word of the output.  Use:
+               asr     Rhi, Rin, #31
+               rsbs    Rlo, Rin, #0
+               rsc     Rhi, Rhi, #0 (thumb2: sbc Rhi, Rhi, Rhi, lsl #1).  */
+       rtx cc_reg = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+
+       emit_insn (gen_rtx_SET (VOIDmode, high,
+                               gen_rtx_ASHIFTRT (SImode, operands[1],
+                                                 GEN_INT (31))));
+
+       emit_insn (gen_subsi3_compare (low, const0_rtx, operands[1]));
+       if (TARGET_ARM)
+         emit_insn (gen_rtx_SET (VOIDmode, high,
+                                 gen_rtx_MINUS (SImode,
+                                                gen_rtx_MINUS (SImode,
+                                                               const0_rtx,
+                                                               high),
+                                                gen_rtx_LTU (SImode,
+                                                             cc_reg,
+                                                             const0_rtx))));
+       else
+         {
+           rtx two_x = gen_rtx_ASHIFT (SImode, high, GEN_INT (1));
+           emit_insn (gen_rtx_SET (VOIDmode, high,
+                                   gen_rtx_MINUS (SImode,
+                                                  gen_rtx_MINUS (SImode,
+                                                                 high,
+                                                                 two_x),
+                                                  gen_rtx_LTU (SImode,
+                                                               cc_reg,
+                                                               const0_rtx))));
+         }
+      }
+    else
+      {
+       /* No overlap, or overlap on high word.  Use:
+               rsb     Rlo, Rin, #0
+               bic     Rhi, Rlo, Rin
+               asr     Rhi, Rhi, #31
+          Flags not needed for this sequence.  */
+       emit_insn (gen_rtx_SET (VOIDmode, low,
+                               gen_rtx_NEG (SImode, operands[1])));
+       emit_insn (gen_rtx_SET (VOIDmode, high,
+                               gen_rtx_AND (SImode,
+                                            gen_rtx_NOT (SImode, operands[1]),
+                                            low)));
+       emit_insn (gen_rtx_SET (VOIDmode, high,
+                               gen_rtx_ASHIFTRT (SImode, high,
+                                                 GEN_INT (31))));
+      }
+    DONE;
   }
-  [(set_attr "length" "8,8,4,4")
-   (set_attr "arch" "a,a,t2,t2")
+  [(set_attr "length" "12")
+   (set_attr "arch" "t2,*")
    (set_attr "type" "multiple")]
 )
 

Reply via email to