From e34022ecd6f914b5a713594ca5b21b33929a3a1f Mon Sep 17 00:00:00 2001
From: Andre Simoes Dias Vieira <andsim01@arm.com>
Date: Tue, 25 Aug 2015 13:12:11 +0100
Subject: [PATCH] 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
@@ -322,6 +322,7 @@ unsigned aarch64_trampoline_size (void);
void aarch64_asm_output_labelref (FILE *, const char *);
void aarch64_cpu_cpp_builtins (cpp_reader *);
void aarch64_elf_asm_named_section (const char *, unsigned, tree);
+const char * aarch64_gen_far_branch (rtx *, int, const char *, const char *);
void aarch64_err_no_fpadvsimd (machine_mode, const char *);
void aarch64_expand_epilogue (bool);
void aarch64_expand_mov_immediate (rtx, rtx);
@@ -586,6 +586,29 @@ static const char * const aarch64_condition_codes[] =
"hi", "ls", "ge", "lt", "gt", "le", "al", "nv"
};
+/* 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_err_no_fpadvsimd (machine_mode mode, const char *msg)
{
@@ -181,6 +181,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
;; -------------------------------------------------------------------
@@ -308,8 +315,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 "far_branch")
+ (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -1048576))
+ (lt (minus (match_dup 2) (pc)) (const_int 1048572)))
+ (const_int 0)
+ (const_int 1)))]
)
(define_expand "casesi"
@@ -488,9 +510,23 @@
(label_ref (match_operand 1 "" ""))
(pc)))]
""
- "<cbz>\\t%<w>0, %l1"
- [(set_attr "type" "branch")]
-
+ {
+ if (get_attr_length (insn) == 8)
+ return aarch64_gen_far_branch (operands, 1, "Lcb", "<inv_cb>\\t%<w>0, ");
+ else
+ return "<cbz>\\t%<w>0, %l1";
+ }
+ [(set_attr "type" "branch")
+ (set (attr "length")
+ (if_then_else (and (ge (minus (match_dup 1) (pc)) (const_int -1048576))
+ (lt (minus (match_dup 1) (pc)) (const_int 1048572)))
+ (const_int 4)
+ (const_int 8)))
+ (set (attr "far_branch")
+ (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -1048576))
+ (lt (minus (match_dup 2) (pc)) (const_int 1048572)))
+ (const_int 0)
+ (const_int 1)))]
)
(define_insn "*tb<optab><mode>1"
@@ -506,8 +542,14 @@
{
if (get_attr_length (insn) == 8)
{
- operands[1] = GEN_INT (HOST_WIDE_INT_1U << UINTVAL (operands[1]));
- return "tst\t%<w>0, %1\;<bcond>\t%l2";
+ if (get_attr_far_branch (insn) == 1)
+ return aarch64_gen_far_branch (operands, 2, "Ltb",
+ "<inv_tb>\\t%<w>0, %1, ");
+ else
+ {
+ operands[1] = GEN_INT (HOST_WIDE_INT_1U << UINTVAL (operands[1]));
+ return "tst\t%<w>0, %1\;<bcond>\t%l2";
+ }
}
else
return "<tbz>\t%<w>0, %1, %l2";
@@ -517,7 +559,13 @@
(if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -32768))
(lt (minus (match_dup 2) (pc)) (const_int 32764)))
(const_int 4)
- (const_int 8)))]
+ (const_int 8)))
+ (set (attr "far_branch")
+ (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -1048576))
+ (lt (minus (match_dup 2) (pc)) (const_int 1048572)))
+ (const_int 0)
+ (const_int 1)))]
+
)
(define_insn "*cb<optab><mode>1"
@@ -530,12 +578,18 @@
{
if (get_attr_length (insn) == 8)
{
- char buf[64];
- uint64_t val = ((uint64_t ) 1)
- << (GET_MODE_SIZE (<MODE>mode) * BITS_PER_UNIT - 1);
- sprintf (buf, "tst\t%%<w>0, %" PRId64, val);
- output_asm_insn (buf, operands);
- return "<bcond>\t%l1";
+ if (get_attr_far_branch (insn) == 1)
+ return aarch64_gen_far_branch (operands, 1, "Ltb",
+ "<inv_tb>\\t%<w>0, <sizem1>, ");
+ else
+ {
+ char buf[64];
+ uint64_t val = ((uint64_t) 1)
+ << (GET_MODE_SIZE (<MODE>mode) * BITS_PER_UNIT - 1);
+ sprintf (buf, "tst\t%%<w>0, %" PRId64, val);
+ output_asm_insn (buf, operands);
+ return "<bcond>\t%l1";
+ }
}
else
return "<tbz>\t%<w>0, <sizem1>, %l1";
@@ -545,7 +599,12 @@
(if_then_else (and (ge (minus (match_dup 1) (pc)) (const_int -32768))
(lt (minus (match_dup 1) (pc)) (const_int 32764)))
(const_int 4)
- (const_int 8)))]
+ (const_int 8)))
+ (set (attr "far_branch")
+ (if_then_else (and (ge (minus (match_dup 1) (pc)) (const_int -1048576))
+ (lt (minus (match_dup 1) (pc)) (const_int 1048572)))
+ (const_int 0)
+ (const_int 1)))]
)
;; -------------------------------------------------------------------
@@ -817,9 +817,15 @@
;; Emit cbz/cbnz depending on comparison type.
(define_code_attr cbz [(eq "cbz") (ne "cbnz") (lt "cbnz") (ge "cbz")])
+;; Emit inverted cbz/cbnz depending on comparison type.
+(define_code_attr inv_cb [(eq "cbnz") (ne "cbz") (lt "cbz") (ge "cbnz")])
+
;; Emit tbz/tbnz depending on comparison type.
(define_code_attr tbz [(eq "tbz") (ne "tbnz") (lt "tbnz") (ge "tbz")])
+;; Emit inverted tbz/tbnz depending on comparison type.
+(define_code_attr inv_tb [(eq "tbnz") (ne "tbz") (lt "tbz") (ge "tbnz")])
+
;; Max/min attributes.
(define_code_attr maxmin [(smax "max")
(smin "min")
new file mode 100644
@@ -0,0 +1,91 @@
+/* { dg-do assemble } */
+/* { dg-timeout-factor 2.0 } */
+/* { dg-options "-O1 -fno-reorder-blocks -fno-tree-cselim --save-temps" } */
+
+
+__attribute__((noinline, noclone)) int
+restore (int a, int b)
+{
+ return a * b;
+}
+
+__attribute__((noinline, noclone)) void
+do_nothing (int *input)
+{
+ *input = restore (*input, 1);
+ return;
+}
+#define ENTRY_SUM(n, x) \
+ sum = sum / ((n) + (x)); \
+ sum = restore (sum, (n) + (x));
+
+#define ENTRY_SUM2(n, x) ENTRY_SUM ((n), (x)) ENTRY_SUM ((n), (x)+1)
+#define ENTRY_SUM4(n, x) ENTRY_SUM2 ((n), (x)) ENTRY_SUM2 ((n), (x)+2)
+#define ENTRY_SUM8(n, x) ENTRY_SUM4 ((n), (x)) ENTRY_SUM4 ((n), (x)+4)
+#define ENTRY_SUM16(n, x) ENTRY_SUM8 ((n), (x)) ENTRY_SUM8 ((n), (x)+8)
+#define ENTRY_SUM32(n, x) ENTRY_SUM16 ((n), (x)) ENTRY_SUM16 ((n), (x)+16)
+#define ENTRY_SUM64(n, x) ENTRY_SUM32 ((n), (x)) ENTRY_SUM32 ((n), (x)+32)
+#define ENTRY_SUM128(n, x) ENTRY_SUM64 ((n), (x)) ENTRY_SUM64 ((n), (x)+64)
+
+#define CASE_ENTRY(n) \
+ case n: \
+ sum = sum / (n + 1); \
+ sum = restore (sum, n + 1); \
+ if (sum == (n + addend)) \
+ break;\
+ ENTRY_SUM128 ((n), 2) \
+ ENTRY_SUM16 ((n), 130) \
+ break;
+
+#define CASE_ENTRY2(n) CASE_ENTRY ((n)) CASE_ENTRY ((n)+1)
+#define CASE_ENTRY4(n) CASE_ENTRY2 ((n)) CASE_ENTRY2 ((n)+2)
+#define CASE_ENTRY8(n) CASE_ENTRY4 ((n)) CASE_ENTRY4 ((n)+4)
+#define CASE_ENTRY16(n) CASE_ENTRY8 ((n)) CASE_ENTRY8 ((n)+8)
+#define CASE_ENTRY32(n) CASE_ENTRY16 ((n)) CASE_ENTRY16 ((n)+16)
+#define CASE_ENTRY64(n) CASE_ENTRY32 ((n)) CASE_ENTRY32 ((n)+32)
+#define CASE_ENTRY128(n) CASE_ENTRY64 ((n)) CASE_ENTRY64 ((n)+64)
+
+__attribute__((noinline, noclone)) long long
+test_and_branch (int selector, int addend, int cond)
+{
+ long long sum = selector + 1;
+
+ if (selector > 200)
+ {
+start0:
+ return sum - 1;
+start1:
+ return sum + 1;
+start2:
+ return sum;
+start3:
+ return sum - 2;
+ }
+ else
+ {
+ switch (selector)
+ {
+ CASE_ENTRY128 (1)
+ CASE_ENTRY64 (129)
+ CASE_ENTRY16 (193)
+ }
+
+ do_nothing ((int *)&sum);
+
+ if (cond == 0)
+ goto start0;
+ else if (cond < 0)
+ goto start1;
+ else if ((cond & 0x010) != 0)
+ goto start2;
+ else if (cond >= 14)
+ goto start3;
+
+ }
+
+ return -1;
+}
+
+/* { dg-final { scan-assembler "Lbcond" } } */
+/* { dg-final { scan-assembler "Lcb" } } */
+/* { dg-final { scan-assembler "Ltb" } } */
--
1.9.1