From 44f77a6dea2f312ee1743f3dde465c1b8453ee13 Mon Sep 17 00:00:00 2001 From: Stam Markianos-Wright Date: Mon, 3 Feb 2020 10:25:46 +0000 Subject: [PATCH] This patch is for PR target/91816 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 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 for Thumb2 range checks. gcc/testsuite/ChangeLog 2020-02-03 Stam Markianos-Wright PR target/91816 * gcc.target/arm/pr91816.c: New test. --- gcc/ChangeLog | 8 +++ gcc/config/arm/arm-protos.h | 2 + gcc/config/arm/arm.c | 34 ++++++++++++ gcc/config/arm/arm.md | 104 ++++++++++++++++++++++++++++-------- gcc/testsuite/ChangeLog | 5 ++ 5 files changed, 131 insertions(+), 22 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ca88b31e6e8..c0ef95fdfda 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,11 @@ +2020-02-03 Stam Markianos-Wright + + 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 for Thumb2 range checks. + 2020-02-03 Julian Brown Tobias Burnus diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 883062759e2..eaff6543699 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -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 */ diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index b54382dbff3..b5ae7e3e9ce 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -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 */ diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index dd73263409a..5baf82d2ad6 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -7584,9 +7584,15 @@ ;; 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' 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' pos_range = 1048574, neg_range = -1048576 giving +;; (-1048568 -> 1048576). + (define_expand "cbranchsi4" [(set (pc) (if_then_else (match_operator 0 "expandable_comparison_operator" @@ -7759,23 +7765,50 @@ (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" @@ -7785,23 +7818,50 @@ (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)))))] ) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 67ac4a79892..cc9f3c15f85 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2020-02-03 Stam Markianos-Wright + + PR target/91816 + * gcc.target/arm/pr91816.c: New test. + 2020-02-03 Julian Brown Tobias Burnus -- 2.30.2