hoist_code inserts a hoisted expression at the end of the target block,
which insert_insn_end_basic_block places before the block's final jump.
If that jump modifies one of the expression's operands, as an asm goto
does for its outputs and, with a "memory" clobber, for memory, the
hoisted expression reads the value from before the jump.  Skip such
expressions.

Bootstrapped and regression tested on x86_64-linux-gnu and aarch64-linux-gnu
(Full disclosure: the testing was with combined fixes for 3 other PRs).

        PR rtl-optimization/127847

gcc/ChangeLog:

        * gcse.cc (hoist_code): Don't hoist an expression into a block
        whose final jump insn modifies one of its operands.

gcc/testsuite/ChangeLog:

        * gcc.dg/torture/asm-goto-hoist-1.c: New test.
        * gcc.target/aarch64/asm-goto-hoist-2.c: New test.
        * gcc.target/aarch64/asm-goto-hoist-3.c: New test.
        * gcc.target/aarch64/asm-goto-hoist-4.c: New test.
        * gcc.target/i386/asm-goto-hoist-2.c: New test.
        * gcc.target/i386/asm-goto-hoist-3.c: New test.
        * gcc.target/i386/asm-goto-hoist-4.c: New test.

Signed-off-by: Vineet Gupta <[email protected]>
---
 gcc/gcse.cc                                   |  7 +++
 .../gcc.dg/torture/asm-goto-hoist-1.c         | 45 ++++++++++++++
 .../gcc.target/aarch64/asm-goto-hoist-2.c     | 62 +++++++++++++++++++
 .../gcc.target/aarch64/asm-goto-hoist-3.c     | 24 +++++++
 .../gcc.target/aarch64/asm-goto-hoist-4.c     | 30 +++++++++
 .../gcc.target/i386/asm-goto-hoist-2.c        | 62 +++++++++++++++++++
 .../gcc.target/i386/asm-goto-hoist-3.c        | 25 ++++++++
 .../gcc.target/i386/asm-goto-hoist-4.c        | 31 ++++++++++
 8 files changed, 286 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/torture/asm-goto-hoist-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/asm-goto-hoist-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/asm-goto-hoist-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/asm-goto-hoist-4.c

diff --git a/gcc/gcse.cc b/gcc/gcse.cc
index c9fe67c8e22b..30bbfd33153f 100644
--- a/gcc/gcse.cc
+++ b/gcc/gcse.cc
@@ -3351,6 +3351,13 @@ hoist_code (void)
              int insn_inserted_p;
              occr_t occr;
 
+             /* EXPR is inserted before BB's final jump, so punt if that
+                jump modifies one of EXPR's operands, e.g. an asm goto
+                output or memory clobbered by an asm goto.  */
+             if (JUMP_P (BB_END (bb))
+                 && modified_in_p (expr->expr, BB_END (bb)))
+               continue;
+
              /* If an expression is computed in BB and is available at end of
                 BB, hoist all occurrences dominated by BB to BB.  */
              if (bitmap_bit_p (comp[bb->index], i))
diff --git a/gcc/testsuite/gcc.dg/torture/asm-goto-hoist-1.c 
b/gcc/testsuite/gcc.dg/torture/asm-goto-hoist-1.c
new file mode 100644
index 000000000000..abb5441923d4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/asm-goto-hoist-1.c
@@ -0,0 +1,45 @@
+/* RTL code hoisting must not insert a computation that reads an asm goto
+   output in front of the asm goto: the asm goto is the block's final jump
+   and it is what sets the output.  The "cold" attribute makes the hoist pass
+   run at -O2 and -O3 too, not only at -Os.  */
+/* { dg-do run { target asm_goto_with_outputs } } */
+
+int g;
+
+__attribute__((noipa)) void
+use (int v)
+{
+  g = v;
+}
+
+__attribute__((noipa, cold)) int
+f1 (int a)
+{
+  int x;
+  asm goto ("" : "=r" (x) : "0" (a) : : lab);
+  return (x + 100) ^ 1;
+ lab:
+  return (x + 100) ^ 2;
+}
+
+__attribute__((noipa, cold)) int
+f2 (int a)
+{
+  int x;
+  asm goto ("" : "=r" (x) : "0" (a) : : lab);
+  use (x + 100);
+  return 1;
+ lab:
+  use (x + 100);
+  return 2;
+}
+
+int
+main (void)
+{
+  if (f1 (5) != 104)
+    __builtin_abort ();
+  if (f2 (7) != 1 || g != 107)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-2.c 
b/gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-2.c
new file mode 100644
index 000000000000..28a790e2ed9c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-2.c
@@ -0,0 +1,62 @@
+/* AArch64 version of gcc.target/i386/asm-goto-hoist-2.c: at -Os RTL code
+   hoisting used to move the read of the flags output in front of the
+   asm goto that sets it.  */
+/* { dg-do run } */
+/* { dg-options "-Os" } */
+
+int v;
+
+[[gnu::noipa]] void
+foo (void)
+{
+  v = 42;
+}
+
+[[gnu::noipa]] void
+bar (void)
+{
+  v = 43;
+}
+
+[[gnu::noipa]] int
+baz (void)
+{
+  bool err;
+  __asm goto ("ldr\tw1, %1\n\t"
+             "cmp\tw1, 0\n\t"
+             "b.ge\t%l[lab]\n\t"
+             "cmn\tw1, 1"
+             : "=@cceq" (err) : "m" (v) : "x1" : lab);
+  if (err)
+    {
+      bar ();
+      return -1;
+    }
+  return 0;
+
+ lab:
+  if (err)
+    {
+      foo ();
+      return -2;
+    }
+
+  return 1;
+}
+
+int
+main ()
+{
+  v = 0;
+  if (baz () != -2 || v != 42)
+    __builtin_abort ();
+  v = 1;
+  if (baz () != 1 || v != 1)
+    __builtin_abort ();
+  v = -1;
+  if (baz () != -1 || v != 43)
+    __builtin_abort ();
+  v = -2;
+  if (baz () != 0 || v != -2)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-3.c 
b/gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-3.c
new file mode 100644
index 000000000000..7ca74e21a5f0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-3.c
@@ -0,0 +1,24 @@
+/* RTL code hoisting used to compute x + 100 before the asm goto that
+   sets x, on both the fallthrough and the label path.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+__attribute__((noipa, cold)) int
+f (int a)
+{
+  int x;
+  asm goto ("mov\t%w0, %w1\n\t"
+           "cbz\t%w0, %l[lab]"
+           : "=r" (x) : "r" (a) : : lab);
+  return (x + 100) ^ 1;
+ lab:
+  return (x + 100) ^ 2;
+}
+
+int
+main (void)
+{
+  if (f (5) != 104 || f (0) != 102)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-4.c 
b/gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-4.c
new file mode 100644
index 000000000000..9515f637004e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/asm-goto-hoist-4.c
@@ -0,0 +1,30 @@
+/* RTL code hoisting used to move the load of *p in front of an asm goto
+   without outputs that stores to *p and, as the manual asks, has a
+   "memory" clobber.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+static int v;
+
+__attribute__((noipa, cold)) int
+f (int *p, int a)
+{
+  asm goto ("str\t%w1, [%0]\n\t"
+           "cbz\t%w1, %l[lab]"
+           : : "r" (p), "r" (a) : "memory" : lab);
+  return (*p + 100) ^ 1;
+ lab:
+  return (*p + 100) ^ 2;
+}
+
+int
+main (void)
+{
+  v = 1;
+  if (f (&v, 5) != 104)
+    __builtin_abort ();
+  v = 1;
+  if (f (&v, 0) != 102)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/asm-goto-hoist-2.c 
b/gcc/testsuite/gcc.target/i386/asm-goto-hoist-2.c
new file mode 100644
index 000000000000..67fe835dc803
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/asm-goto-hoist-2.c
@@ -0,0 +1,62 @@
+/* Same as pr124056.c, but at -Os, where RTL code hoisting used to move
+   the read of the flags output in front of the asm goto that sets it.  */
+/* { dg-do run } */
+/* { dg-options "-Os -masm=att" } */
+
+int v;
+
+[[gnu::noipa]] void
+foo (void)
+{
+  v = 42;
+}
+
+[[gnu::noipa]] void
+bar (void)
+{
+  v = 43;
+}
+
+[[gnu::noipa]] int
+baz (void)
+{
+  bool err;
+  __asm goto ("movl %1, %%ecx\n\t"
+              "test %%ecx, %%ecx\n\t"
+              "js 1f\n\t"
+              "jmp %l[lab]\n\t"
+           "1: cmpl $-1, %%ecx"
+              : "=@ccz" (err) : "m" (v) : "ecx" : lab);
+  if (err)
+    {
+      bar ();
+      return -1;
+    }
+  return 0;
+
+ lab:
+  if (err)
+    {
+      foo ();
+      return -2;
+    }
+
+  return 1;
+}
+
+int
+main ()
+{
+  v = 0;
+  if (baz () != -2 || v != 42)
+    __builtin_abort ();
+  v = 1;
+  if (baz () != 1 || v != 1)
+    __builtin_abort ();
+  v = -1;
+  if (baz () != -1 || v != 43)
+    __builtin_abort ();
+  v = -2;
+  if (baz () != 0 || v != -2)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.target/i386/asm-goto-hoist-3.c 
b/gcc/testsuite/gcc.target/i386/asm-goto-hoist-3.c
new file mode 100644
index 000000000000..57ace6c24bbc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/asm-goto-hoist-3.c
@@ -0,0 +1,25 @@
+/* RTL code hoisting used to compute x + 100 before the asm goto that
+   sets x, on both the fallthrough and the label path.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -masm=att" } */
+
+__attribute__((noipa, cold)) int
+f (int a)
+{
+  int x;
+  asm goto ("movl %1, %0\n\t"
+           "testl %0, %0\n\t"
+           "jz %l[lab]"
+           : "=r" (x) : "r" (a) : "cc" : lab);
+  return (x + 100) ^ 1;
+ lab:
+  return (x + 100) ^ 2;
+}
+
+int
+main (void)
+{
+  if (f (5) != 104 || f (0) != 102)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/asm-goto-hoist-4.c 
b/gcc/testsuite/gcc.target/i386/asm-goto-hoist-4.c
new file mode 100644
index 000000000000..38657622f247
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/asm-goto-hoist-4.c
@@ -0,0 +1,31 @@
+/* RTL code hoisting used to move the load of *p in front of an asm goto
+   without outputs that stores to *p and, as the manual asks, has a
+   "memory" clobber.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -masm=att" } */
+
+static int v;
+
+__attribute__((noipa, cold)) int
+f (int *p, int a)
+{
+  asm goto ("movl %1, (%0)\n\t"
+           "testl %1, %1\n\t"
+           "jz %l[lab]"
+           : : "r" (p), "r" (a) : "cc", "memory" : lab);
+  return (*p + 100) ^ 1;
+ lab:
+  return (*p + 100) ^ 2;
+}
+
+int
+main (void)
+{
+  v = 1;
+  if (f (&v, 5) != 104)
+    __builtin_abort ();
+  v = 1;
+  if (f (&v, 0) != 102)
+    __builtin_abort ();
+  return 0;
+}
-- 
2.53.0-Meta

Reply via email to