Conditional branches have a maximum range of [-1048576, 1048572]. Any
destination further away can not be reached by these.
To be able to have conditional branches in very large functions, we
invert the condition and change the destination to jump over an
unconditional branch to the original, far away, destination.
This patch backports the fix from trunk to the gcc-5-branch.
The original patch is at:
https://gcc.gnu.org/ml/gcc-patches/2015-08/msg01493.html
gcc/ChangeLog:
2015-09-09 Andre Vieira
Backport from mainline:
2015-08-27 Ramana Radhakrishnan
Andre Vieira
* config/aarch64/aarch64.md (*condjump): Handle functions > 1 MiB.
(*cb1): Likewise.
(*tb1): Likewise.
(*cb1): Likewise.
* config/aarch64/iterators.md (inv_cb): New code attribute.
(inv_tb): Likewise.
* config/aarch64/aarch64.c (aarch64_gen_far_branch): New.
* config/aarch64/aarch64-protos.h (aarch64_gen_far_branch): New.
gcc/testsuite/ChangeLog:
2015-09-09 Andre Vieira
Backport from mainline:
2015-08-27 Andre Vieira
* gcc.target/aarch64/long_branch_1.c: New test.
From 5b9f35c3a6dff67328e66e82f33ef3dc732ff5f7 Mon Sep 17 00:00:00 2001
From: Andre Simoes Dias Vieira
Date: Tue, 8 Sep 2015 16:51:14 +0100
Subject: [PATCH] Backport fix for far branches
---
gcc/config/aarch64/aarch64-protos.h | 1 +
gcc/config/aarch64/aarch64.c | 23 ++
gcc/config/aarch64/aarch64.md| 89 +++
gcc/config/aarch64/iterators.md | 6 ++
gcc/testsuite/gcc.target/aarch64/long_branch_1.c | 91
5 files changed, 195 insertions(+), 15 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/aarch64/long_branch_1.c
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
index 59c5824f894cf5dafe93a996180056696518feb4..8669694bfc33cc040baeab5aaddc7a0575071add 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -258,6 +258,7 @@ void aarch64_init_expanders (void);
void aarch64_print_operand (FILE *, rtx, char);
void aarch64_print_operand_address (FILE *, rtx);
void aarch64_emit_call_insn (rtx);
+const char * aarch64_gen_far_branch (rtx *, int, const char *, const char *);
/* Initialize builtins for SIMD intrinsics. */
void init_aarch64_simd_builtins (void);
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index cd20b48d7fd300ab496e67cae0d6e503ac305409..c2e4252cf689a4d7b28890743095536f3a390338 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -1029,6 +1029,29 @@ aarch64_split_128bit_move_p (rtx dst, rtx src)
/* Split a complex SIMD combine. */
+/* Generate code to enable conditional branches in functions over 1 MiB. */
+const char *
+aarch64_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 = targetm.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%%l%d\n%s:", pos_label, label_ptr);
+operands[pos_label] = dest_label;
+output_asm_insn (buffer, operands);
+return "";
+}
+
void
aarch64_split_simd_combine (rtx dst, rtx src1, rtx src2)
{
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index 4e7a6d19ac2ff8067912052697ca7a1fc403f910..c3c3beb75b4d4486d5b235e31b4db7bd213c7c7b 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -179,6 +179,13 @@
(const_string "no")
] (const_string "yes")))
+;; Attribute that specifies whether we are dealing with a branch to a
+;; label that is far away, i.e. further away than the maximum/minimum
+;; representable in a signed 21-bits number.
+;; 0 :=: no
+;; 1 :=: yes
+(define_attr "far_branch" "" (const_int 0))
+
;; ---
;; Pipeline descriptions and scheduling
;; ---
@@ -306,8 +313,23 @@
(label_ref (match_operand 2 "" ""))
(pc)))]
""
- "b%m0\\t%l2"
- [(set_attr "type" "branch")]
+ {
+if (get_attr_length (insn) == 8)
+ return aarch64_gen_far_branch (operands, 2, "Lbcond", "b%M0\\t");
+else
+ return "b%m0\\t%l2";
+ }
+ [(set_attr "type" "branch")
+ (set (attr "length")
+ (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -1048576))
+ (lt (minus (match_dup 2) (pc)) (const_int 1048572)))
+ (const_int 4)
+ (const_int 8)))
+ (set (attr "fa