This patch is for PR target/91816
authorStam Markianos-Wright <stam.markianos-wright@arm.com>
Mon, 3 Feb 2020 10:25:46 +0000 (10:25 +0000)
committerStam Markianos-Wright <stam.markianos-wright@arm.com>
Mon, 3 Feb 2020 10:30:59 +0000 (10:30 +0000)
This is a patch for an issue where the compiler was generating a conditional
branch in Thumb2, which was too far for b{cond} to handle.

This was originally reported at binutils:
https://sourceware.org/bugzilla/show_bug.cgi?id=24991

And then raised for GCC:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91816

As can be seen here:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cihfddaf.html

the range of a 32-bit Thumb B{cond} is +/-1MB.

This is now checked for in arm.md and an unconditional branch is generated if
the jump would be greater than 1MB.

gcc/ChangeLog

2020-02-03  Stam Markianos-Wright  <stam.markianos-wright@arm.com>

PR target/91816
* config/arm/arm-protos.h: New function arm_gen_far_branch prototype.
* config/arm/arm.c (arm_gen_far_branch): New function
arm_gen_far_branch.
* config/arm/arm.md: Update b<cond> for Thumb2 range checks.

gcc/testsuite/ChangeLog

2020-02-03  Stam Markianos-Wright  <stam.markianos-wright@arm.com>

PR target/91816
* gcc.target/arm/pr91816.c: New test.

gcc/ChangeLog
gcc/config/arm/arm-protos.h
gcc/config/arm/arm.c
gcc/config/arm/arm.md
gcc/testsuite/ChangeLog

index ca88b31e6e839e54c32cb6a4ddc56091339a7a61..c0ef95fdfda919b156400ff7b9a806a01079b663 100644 (file)
@@ -1,3 +1,11 @@
+2020-02-03  Stam Markianos-Wright  <stam.markianos-wright@arm.com>
+
+       PR target/91816
+       * config/arm/arm-protos.h: New function arm_gen_far_branch prototype.
+       * config/arm/arm.c (arm_gen_far_branch): New function
+       arm_gen_far_branch.
+       * config/arm/arm.md: Update b<cond> for Thumb2 range checks.
+
 2020-02-03  Julian Brown  <julian@codesourcery.com>
            Tobias Burnus  <tobias@codesourcery.com>
 
index 883062759e22f5a8957bd4320f2bbc0061d941b5..eaff6543699755a2c4987fc3ceab1f8e15ec17b0 100644 (file)
@@ -577,4 +577,6 @@ void arm_parse_option_features (sbitmap, const cpu_arch_option *,
 
 void arm_initialize_isa (sbitmap, const enum isa_feature *);
 
+const char * arm_gen_far_branch (rtx *, int, const char * , const char *);
+
 #endif /* ! GCC_ARM_PROTOS_H */
index b54382dbff3e589b683447468bc56c4ffae734e3..b5ae7e3e9ce08967368a046f2ca5b2fd7f18677e 100644 (file)
@@ -33041,6 +33041,40 @@ arm_run_selftests (void)
 }
 } /* Namespace selftest.  */
 
+
+/* Generate code to enable conditional branches in functions over 1 MiB.
+   Parameters are:
+     operands: is the operands list of the asm insn (see arm_cond_branch or
+       arm_cond_branch_reversed).
+     pos_label: is an index into the operands array where operands[pos_label] is
+       the asm label of the final jump destination.
+     dest: is a string which is used to generate the asm label of the intermediate
+       destination
+   branch_format: is a string denoting the intermediate branch format, e.g.
+     "beq", "bne", etc.  */
+
+const char *
+arm_gen_far_branch (rtx * operands, int pos_label, const char * dest,
+                   const char * branch_format)
+{
+  rtx_code_label * tmp_label = gen_label_rtx ();
+  char label_buf[256];
+  char buffer[128];
+  ASM_GENERATE_INTERNAL_LABEL (label_buf, dest , \
+                       CODE_LABEL_NUMBER (tmp_label));
+  const char *label_ptr = arm_strip_name_encoding (label_buf);
+  rtx dest_label = operands[pos_label];
+  operands[pos_label] = tmp_label;
+
+  snprintf (buffer, sizeof (buffer), "%s%s", branch_format , label_ptr);
+  output_asm_insn (buffer, operands);
+
+  snprintf (buffer, sizeof (buffer), "b\t%%l0%d\n%s:", pos_label, label_ptr);
+  operands[pos_label] = dest_label;
+  output_asm_insn (buffer, operands);
+  return "";
+}
+
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::arm_run_selftests
 #endif /* CHECKING_P */
index dd73263409a339a03bee97c27992b8d78c4babf3..5baf82d2ad6f423bc0e63a01be35e9fb67ebadb1 100644 (file)
 ;; And for backward branches we have 
 ;;   (neg_range - neg_base_offs + pc_offs) = (neg_range - (-2 or -4) + 4).
 ;;
+;; In 16-bit Thumb these ranges are:
 ;; For a 'b'       pos_range = 2046, neg_range = -2048 giving (-2040->2048).
 ;; For a 'b<cond>' pos_range = 254,  neg_range = -256  giving (-250 ->256).
 
+;; In 32-bit Thumb these ranges are:
+;; For a 'b'       +/- 16MB is not checked for.
+;; For a 'b<cond>' pos_range = 1048574,  neg_range = -1048576  giving
+;; (-1048568 -> 1048576).
+
 (define_expand "cbranchsi4"
   [(set (pc) (if_then_else
              (match_operator 0 "expandable_comparison_operator"
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
   "TARGET_32BIT"
-  "*
-  if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2)
+  {
+    if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2)
     {
       arm_ccfsm_state += 2;
-      return \"\";
+      return "";
     }
-  return \"b%d1\\t%l0\";
-  "
+    switch (get_attr_length (insn))
+      {
+       case 2: /* Thumb2 16-bit b{cond}.  */
+       case 4: /* Thumb2 32-bit b{cond} or A32 b{cond}.  */
+         return "b%d1\t%l0";
+         break;
+
+       /* Thumb2 b{cond} out of range.  Use 16-bit b{cond} and
+          unconditional branch b.  */
+       default: return arm_gen_far_branch (operands, 0, "Lbcond", "b%D1\t");
+      }
+  }
   [(set_attr "conds" "use")
    (set_attr "type" "branch")
    (set (attr "length")
-       (if_then_else
-          (and (match_test "TARGET_THUMB2")
-               (and (ge (minus (match_dup 0) (pc)) (const_int -250))
-                    (le (minus (match_dup 0) (pc)) (const_int 256))))
-          (const_int 2)
-          (const_int 4)))]
+    (if_then_else (match_test "!TARGET_THUMB2")
+
+      ;;Target is not Thumb2, therefore is A32.  Generate b{cond}.
+      (const_int 4)
+
+      ;; Check if target is within 16-bit Thumb2 b{cond} range.
+      (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -250))
+                        (le (minus (match_dup 0) (pc)) (const_int 256)))
+
+       ;; Target is Thumb2, within narrow range.
+       ;; Generate b{cond}.
+       (const_int 2)
+
+       ;; Check if target is within 32-bit Thumb2 b{cond} range.
+       (if_then_else (and (ge (minus (match_dup 0) (pc))(const_int -1048568))
+                          (le (minus (match_dup 0) (pc)) (const_int 1048576)))
+
+         ;; Target is Thumb2, within wide range.
+         ;; Generate b{cond}
+         (const_int 4)
+         ;; Target is Thumb2, out of range.
+         ;; Generate narrow b{cond} and unconditional branch b.
+         (const_int 6)))))]
 )
 
 (define_insn "*arm_cond_branch_reversed"
                      (pc)
                      (label_ref (match_operand 0 "" ""))))]
   "TARGET_32BIT"
-  "*
-  if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2)
+  {
+    if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2)
     {
       arm_ccfsm_state += 2;
-      return \"\";
+      return "";
     }
-  return \"b%D1\\t%l0\";
-  "
+    switch (get_attr_length (insn))
+      {
+       case 2: /* Thumb2 16-bit b{cond}.  */
+       case 4: /* Thumb2 32-bit b{cond} or A32 b{cond}.  */
+         return "b%D1\t%l0";
+         break;
+
+       /* Thumb2 b{cond} out of range.  Use 16-bit b{cond} and
+          unconditional branch b.  */
+       default: return arm_gen_far_branch (operands, 0, "Lbcond", "b%d1\t");
+      }
+  }
   [(set_attr "conds" "use")
    (set_attr "type" "branch")
    (set (attr "length")
-       (if_then_else
-          (and (match_test "TARGET_THUMB2")
-               (and (ge (minus (match_dup 0) (pc)) (const_int -250))
-                    (le (minus (match_dup 0) (pc)) (const_int 256))))
-          (const_int 2)
-          (const_int 4)))]
+    (if_then_else (match_test "!TARGET_THUMB2")
+
+      ;;Target is not Thumb2, therefore is A32.  Generate b{cond}.
+      (const_int 4)
+
+      ;; Check if target is within 16-bit Thumb2 b{cond} range.
+      (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -250))
+                        (le (minus (match_dup 0) (pc)) (const_int 256)))
+
+       ;; Target is Thumb2, within narrow range.
+       ;; Generate b{cond}.
+       (const_int 2)
+
+       ;; Check if target is within 32-bit Thumb2 b{cond} range.
+       (if_then_else (and (ge (minus (match_dup 0) (pc))(const_int -1048568))
+                          (le (minus (match_dup 0) (pc)) (const_int 1048576)))
+
+         ;; Target is Thumb2, within wide range.
+         ;; Generate b{cond}.
+         (const_int 4)
+         ;; Target is Thumb2, out of range.
+         ;; Generate narrow b{cond} and unconditional branch b.
+         (const_int 6)))))]
 )
 
 \f
index 67ac4a798926144fa2c2c43d25a0c14c3221e801..cc9f3c15f85261b53f02524600e254b7dd9c46fc 100644 (file)
@@ -1,3 +1,8 @@
+2020-02-03  Stam Markianos-Wright  <stam.markianos-wright@arm.com>
+
+       PR target/91816
+       * gcc.target/arm/pr91816.c: New test.
+
 2020-02-03  Julian Brown  <julian@codesourcery.com>
            Tobias Burnus  <tobias@codesourcery.com>