The x86 backend can generate non-canonical RTL when it simplifies address expressions.

In particular addresses which have the form
(plus (mult) (A) (B) (label_ref))

If the multiplication can be simplified to a constant, the x86 backend will end up generating

(plus (constant) (label_ref))

Which is obviously non-canonical and should be written
(const (plus (label_ref) (constant))

This change merely canonicalizes the RTL, leaving it to other code to simplify. At first I wanted to simplify, but it just gets painful if, for example B above is (const_int 0), in which case our example collapses into

(label_ref)

The subsequent code in the ix86_legitimize_address assumes it's still working with a PLUS. Fixable, but painful.

It's also the case that simplify_rtx might return NULL. So the code also has to DTRT in that case and the caller's don't DTRT if ix86_legitimize_address returns NULL.

Anyway, I did verify that the relevant code in the short example gets optimized if we just fix the canonicalization (namely the addition of (const_int 0) is eliminated).

Bootstrapped and regression tested on x86_64-unknown-linux-gnu. Verified it fixes the original and reduced testcase.

OK for the trunk?

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 53d58b3..80f0ba8 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,8 @@
+2014-03-26  Jeff Law  <l...@redhat.com>
+
+       * i386/i386.c (ix86_legitimize_address): Canonicalize
+       (plus (const) (label_ref)).
+
 2014-03-26  Richard Biener  <rguent...@suse.de>
 
        * tree-pretty-print.c (percent_K_format): Implement special
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 842be68..79f4aff 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -13939,6 +13939,28 @@ ix86_legitimize_address (rtx x, rtx oldx 
ATTRIBUTE_UNUSED,
          && REG_P (XEXP (x, 0)))
        return x;
 
+      /* We might have started with something like
+
+        (plus (mult (const_int 1) (const_int 4)) (label_ref))
+
+        Which we change into:
+
+        (plus (const_int 4) (label_ref))
+
+        Which is obviously not canonical RTL.  Passing the non
+        canonical RTL up to our caller is bad.
+
+        Do not simplify, just canonicalize.  Simplification opens up
+        a can of worms here as that can change the structure of X which
+        this code isn't really prepared to handle.  */
+      if (COMMUTATIVE_ARITH_P (x)
+         && swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
+       {
+         rtx temp = XEXP (x, 0);
+         XEXP (x, 0) = XEXP (x, 1);
+         XEXP (x, 1) = temp;
+       }
+
       if (flag_pic && SYMBOLIC_CONST (XEXP (x, 1)))
        {
          changed = 1;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index cdc8e9a..789e27d 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2014-02-27  Jeff Law  <l...@redhat.com>
+
+       PR target/60648
+       * g++.dg/pr60648.C: New test.
+
 2014-03-26  Jakub Jelinek  <ja...@redhat.com>
 
        PR sanitizer/60636

Reply via email to