Hi all,
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.
New test has been written that checks this for: beq (if (a)), bne (if
(a==1))
Patch bootstrapped and regression tested on arm-none-linux-gnueabihf,
however, on my native Aarch32 setup the test times out when run as part
of a big "make check-gcc" regression, but not when run individually.
Patch also regression tested on arm-none-eabi, arm-none-linux-gnueabi
with no issues.
Also, I don't have commit rights yet, so could someone commit it on my
behalf?
Thanks,
Stam Markianos-Wright
gcc/ChangeLog:
2019-10-11 Stamatis Markianos-Wright <[email protected]>
* config/arm/arm.md: Update b<cond> for Thumb2 range checks.
* config/arm/arm.c: New function arm_gen_far_branch.
* config/arm/arm-protos.h: New function arm_gen_far_branch
prototype.
gcc/testsuite/ChangeLog:
2019-10-11 Stamatis Markianos-Wright <[email protected]>
* testsuite/gcc.target/arm/pr91816.c: New test.
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index f995974f9bb..1dce333d1c3 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -570,4 +570,7 @@ 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 */
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 39e1a1ef9a2..1a693d2ddca 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -32139,6 +32139,31 @@ arm_run_selftests (void)
}
} /* Namespace selftest. */
+
+/* Generate code to enable conditional branches in functions over 1 MiB. */
+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 */
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index f861c72ccfc..634fd0a59da 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -6686,9 +6686,16 @@
;; 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"
@@ -6947,22 +6954,42 @@
(pc)))]
"TARGET_32BIT"
"*
- if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2)
- {
- arm_ccfsm_state += 2;
- return \"\";
- }
- return \"b%d1\\t%l0\";
+ if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2)
+ {
+ arm_ccfsm_state += 2;
+ return \"\";
+ }
+ switch (get_attr_length (insn))
+ {
+ // Thumb2 16-bit b{cond}
+ case 2:
+
+ // Thumb2 32-bit b{cond}
+ case 4: return \"b%d1\\t%l0\";break;
+
+ // Thumb2 b{cond} out of range. Use unconditional branch.
+ case 8: return arm_gen_far_branch \
+ (operands, 0, \"Lbcond\", \"b%D1\t\");
+ break;
+
+ // A32 b{cond}
+ default: return \"b%d1\\t%l0\";
+ }
"
[(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")
+ (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -250))
+ (le (minus (match_dup 0) (pc)) (const_int 256)))
+ (const_int 2)
+ (if_then_else (and (ge (minus (match_dup 0) (pc))
+ (const_int -1048568))
+ (le (minus (match_dup 0) (pc)) (const_int 1048576)))
+ (const_int 4)
+ (const_int 8)))
+ (const_int 10)))
+ ]
)
(define_insn "*arm_cond_branch_reversed"
@@ -6978,17 +7005,36 @@
arm_ccfsm_state += 2;
return \"\";
}
- return \"b%D1\\t%l0\";
+ switch (get_attr_length (insn))
+ {
+ // Thumb2 16-bit b{cond}
+ case 2:
+
+ // Thumb2 32-bit b{cond}
+ case 4: return \"b%D1\\t%l0\";break;
+
+ // Thumb2 b{cond} out of range. Use unconditional branch.
+ case 8: return arm_gen_far_branch \
+ (operands, 0, \"Lbcond\", \"b%d1\t\");
+ break;
+ // A32 b{cond}
+ default: return \"b%D1\\t%l0\";
+ }
"
[(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")
+ (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -250))
+ (le (minus (match_dup 0) (pc)) (const_int 256)))
+ (const_int 2)
+ (if_then_else (and (ge (minus (match_dup 0) (pc))
+ (const_int -1048568))
+ (le (minus (match_dup 0) (pc)) (const_int 1048576)))
+ (const_int 4)
+ (const_int 8)))
+ (const_int 10)))
+ ]
)
diff --git a/gcc/testsuite/gcc.target/arm/pr91816.c b/gcc/testsuite/gcc.target/arm/pr91816.c
new file mode 100644
index 00000000000..176bf61780b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr91816.c
@@ -0,0 +1,102 @@
+/* { dg-do compile } */
+/* { dg-options "-march=armv7-a -mthumb -mfpu=vfpv3-d16" } */
+int printf(const char *, ...);
+
+__attribute__((noinline,noclone)) void f1(int a)
+{
+ if (a) {
+#define HW0 printf("Hello World!\n");
+#define HW1 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0
+#define HW2 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1
+#define HW3 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2
+#define HW4 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3
+#define HW5 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4
+ HW0
+ }
+}
+
+__attribute__((noinline,noclone)) void f2(int a)
+{
+ if (a) {
+#define HW0 printf("Hello World!\n");
+#define HW1 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0
+#define HW2 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1
+#define HW3 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2
+#define HW4 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3
+#define HW5 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4
+ HW3
+ }
+}
+
+
+__attribute__((noinline,noclone)) void f3(int a)
+{
+ if (a) {
+#define HW0 printf("Hello World!\n");
+#define HW1 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0
+#define HW2 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1
+#define HW3 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2
+#define HW4 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3
+#define HW5 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4
+ HW5
+ }
+}
+
+__attribute__((noinline,noclone)) void f4(int a)
+{
+ if (a==1) {
+#define HW0 printf("Hello World!\n");
+#define HW1 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0
+#define HW2 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1
+#define HW3 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2
+#define HW4 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3
+#define HW5 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4
+ HW0
+ }
+}
+
+__attribute__((noinline,noclone)) void f5(int a)
+{
+ if (a==1) {
+#define HW0 printf("Hello World!\n");
+#define HW1 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0
+#define HW2 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1
+#define HW3 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2
+#define HW4 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3
+#define HW5 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4
+ HW3
+ }
+}
+
+
+__attribute__((noinline,noclone)) void f6(int a)
+{
+ if (a==1) {
+#define HW0 printf("Hello World!\n");
+#define HW1 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0 HW0
+#define HW2 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1 HW1
+#define HW3 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2 HW2
+#define HW4 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3 HW3
+#define HW5 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4 HW4
+ HW5
+ }
+}
+
+
+int main(void)
+{
+ f1(0);
+ f2(0);
+ f3(0);
+ f4(0);
+ f5(0);
+ f6(0);
+ return 0;
+}
+
+
+/* { dg-final { scan-assembler-times "beq\\t.L\[0-9\]" 2 } } */
+/* { dg-final { scan-assembler-times "beq\\t.Lbcond\[0-9\]" 1 } } */
+/* { dg-final { scan-assembler-times "bne\\t.L\[0-9\]" 2 } } */
+/* { dg-final { scan-assembler-times "bne\\t.Lbcond\[0-9\]" 1 } } */
+/* { dg-final { scan-assembler-times "b\\t.L\[0-9\]" 2 } } */