[arm] PR target/85026: Fix ldrsh length estimate in Thumb state
authorKyrylo Tkachov <kyrylo.tkachov@arm.com>
Fri, 23 Mar 2018 16:43:43 +0000 (16:43 +0000)
committerKyrylo Tkachov <ktkachov@gcc.gnu.org>
Fri, 23 Mar 2018 16:43:43 +0000 (16:43 +0000)
This bug has been reported against GCC 7.3.0 but it is latent in all release branches and on trunk.
We underestimate the length of the LRSH instruction in Thumb state.
Unlike other load instructions LDRSH can be encoded in 16 bits only when using a register offset.
In the testcase we have "ldrsh   r2, [r4]" being assigned a length of 2, which is wrong.
So we don't calculate branch ranges properly and cause the assembler error.

The fix is to make the unaligned_loadhis insn similar to the *arm_extendqihi_insn insn that outputs an LDRSB.
Just remove the wrong 2-byte alternative. I don't think this is worth inventing a new "register-offset-only" constraint.
This also makes the patch safer for backporting.

Bootstrapped and tested on arm-none-linux-gnueabihf.

PR target/85026
* config/arm/arm.md (unaligned_loadhis): Remove first alternative.
Clean up attributes.

* g++.dg/pr85026.C: New test.

From-SVN: r258818

gcc/ChangeLog
gcc/config/arm/arm.md
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/pr85026.C [new file with mode: 0644]

index e407bc7afc1bf9b42e20747e8c61991c80e5c9a4..53b67fb832bb610179e933accaec7287ca949068 100644 (file)
@@ -1,3 +1,9 @@
+2018-03-23  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+       PR target/85026
+       * config/arm/arm.md (unaligned_loadhis): Remove first alternative.
+       Clean up attributes.
+
 2018-03-23  Richard Biener  <rguenther@suse.de>
 
        PR debug/85020
index c6229c2242c73b65704191afe68d188a770320ea..2d5359e532f321cec8ce6c507bf79b295ecc6a4c 100644 (file)
    (set_attr "type" "load_4")])
 
 (define_insn "unaligned_loadhis"
-  [(set (match_operand:SI 0 "s_register_operand" "=l,r")
+  [(set (match_operand:SI 0 "s_register_operand" "=r")
        (sign_extend:SI
-         (unspec:HI [(match_operand:HI 1 "memory_operand" "Uw,Uh")]
+         (unspec:HI [(match_operand:HI 1 "memory_operand" "Uh")]
                     UNSPEC_UNALIGNED_LOAD)))]
   "unaligned_access"
   "ldrsh%?\t%0, %1\t@ unaligned"
-  [(set_attr "arch" "t2,any")
-   (set_attr "length" "2,4")
-   (set_attr "predicable" "yes")
-   (set_attr "predicable_short_it" "yes,no")
+  [(set_attr "predicable" "yes")
    (set_attr "type" "load_byte")])
 
 (define_insn "unaligned_loadhiu"
index 939c8ffb9ac304c8d3737935bccf56299e9e64c6..e709ff18b48ebecea0f90652a320b01bc4627670 100644 (file)
@@ -1,3 +1,8 @@
+2018-03-23  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+       PR target/85026
+       * g++.dg/pr85026.C: New test.
+
 2018-03-23  David Edelsohn  <dje.gcc@gmail.com>
 
        * g++.dg/debug/pr84704.C: XFAIL AIX.
diff --git a/gcc/testsuite/g++.dg/pr85026.C b/gcc/testsuite/g++.dg/pr85026.C
new file mode 100644 (file)
index 0000000..e1e3ccd
--- /dev/null
@@ -0,0 +1,61 @@
+/* PR target/85026.  */
+/* { dg-do assemble } */
+/* { dg-options "-O2 -std=gnu++11" } */
+
+template <class> class a;
+class b;
+struct c {
+  typedef a<b> &g;
+};
+template <typename d> struct e { typedef typename d::f iter; };
+class h {
+public:
+  void __attribute__((noreturn)) i();
+} ab;
+template <class> class a {
+public:
+  typedef b *f;
+  b &operator[](unsigned m) {
+    if (ac)
+      ab.i();
+    return ad[m];
+  }
+  f n() { return ad; }
+  f m_fn3();
+  b *ad;
+  unsigned ac;
+};
+class b {
+public:
+  short j;
+  short k;
+  signed l;
+} __attribute__((__packed__));
+void o(a<b> &m, b &p2, b &p) {
+  p2 = p = m[0];
+  if (bool at = false)
+    ;
+  else
+    for (c::g au(m);; at = true)
+      if (bool av = false)
+        ;
+      else
+        for (e<a<int>>::iter aw = au.n(), ax = au.m_fn3(); ax;
+             av ? (void)0 : (void)0)
+          if (bool ay = 0)
+            ;
+          else
+            for (b az = *aw; !ay; ay = true) {
+              if (p2.j)
+                p2.j = az.j;
+              else if (p.j)
+                p.j = az.j;
+              if (p2.k)
+                p2.k = az.k;
+              else if (az.k > p.k)
+                p.k = az.k;
+              if (az.l < p2.l)
+                if (az.l > p.l)
+                  p.l = az.l;
+            }
+}