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