From 36be1dee1d8f88f1da9a27a9ce6d20f8976f210c Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Thu, 2 Aug 2012 21:31:57 +0000 Subject: [PATCH] re PR target/51931 (No support for MIPS16 long branches) gcc/ PR target/51931 * config/mips/mips-protos.h (mips_strip_unspec_address): Declare. * config/mips/mips.c (mips_strip_unspec_address): Make extern. (mips16_rewrite_pool_constant): Make a copy of the pool constant before adding to a PC-relative table. (mips16_lay_out_constants): Add a SPLIT_P parameter. (mips16_load_branch_target, mips16_split_long_branches): New functions. (mips_reorg): Update call to mips16_lay_out_constants. Call mips16_split_long_branches. * config/mips/predicates.md (pc_or_label_operand): Delete. * config/mips/mips.md (length): Add a calculation for MIPS16 branches. Move the extended_mips16 handling further down. (*branch_equality_mips16): Replace use pc_or_label_operand with explicit label_ref and pc. Follow the usual operand numbering. (*branch_equality_mips16_inverted): New pattern. (*jump_mips16): Add length attribute. (indirect_jump_and_restore_): New pattern. (consttable_int): Call mips_strip_unspec_address on the operand. gcc/testsuite/ PR target/51931 * gcc.c-torture/compile/20001226-1.c: Remove nomips16 attribute. * g++.dg/opt/longbranch1.C: Likewise. From-SVN: r190104 --- gcc/ChangeLog | 21 ++ gcc/config/mips/mips-protos.h | 1 + gcc/config/mips/mips.c | 129 +++++++++++- gcc/config/mips/mips.md | 198 +++++++++++++++--- gcc/config/mips/predicates.md | 3 - gcc/testsuite/ChangeLog | 6 + gcc/testsuite/g++.dg/opt/longbranch1.C | 4 - .../gcc.c-torture/compile/20001226-1.c | 4 - 8 files changed, 313 insertions(+), 53 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 524ae209da8..61062b80561 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,24 @@ +2012-08-02 Richard Sandiford + + PR target/51931 + * config/mips/mips-protos.h (mips_strip_unspec_address): Declare. + * config/mips/mips.c (mips_strip_unspec_address): Make extern. + (mips16_rewrite_pool_constant): Make a copy of the pool constant + before adding to a PC-relative table. + (mips16_lay_out_constants): Add a SPLIT_P parameter. + (mips16_load_branch_target, mips16_split_long_branches): New functions. + (mips_reorg): Update call to mips16_lay_out_constants. + Call mips16_split_long_branches. + * config/mips/predicates.md (pc_or_label_operand): Delete. + * config/mips/mips.md (length): Add a calculation for MIPS16 branches. + Move the extended_mips16 handling further down. + (*branch_equality_mips16): Replace use pc_or_label_operand + with explicit label_ref and pc. Follow the usual operand numbering. + (*branch_equality_mips16_inverted): New pattern. + (*jump_mips16): Add length attribute. + (indirect_jump_and_restore_): New pattern. + (consttable_int): Call mips_strip_unspec_address on the operand. + 2012-08-02 Richard Sandiford * recog.c (split_insn): Copy the original SET_SRC before using diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h index cba76b6672b..bae67a83041 100644 --- a/gcc/config/mips/mips-protos.h +++ b/gcc/config/mips/mips-protos.h @@ -190,6 +190,7 @@ extern rtx mips_pic_base_register (rtx); extern rtx mips_got_load (rtx, rtx, enum mips_symbol_type); extern bool mips_split_symbol (rtx, rtx, enum machine_mode, rtx *); extern rtx mips_unspec_address (rtx, enum mips_symbol_type); +extern rtx mips_strip_unspec_address (rtx); extern void mips_move_integer (rtx, rtx, unsigned HOST_WIDE_INT); extern bool mips_legitimize_move (enum machine_mode, rtx, rtx); diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index e9e4ec10c43..ef1cccdcbe7 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -2563,7 +2563,7 @@ mips_unspec_address (rtx address, enum mips_symbol_type symbol_type) /* If OP is an UNSPEC address, return the address to which it refers, otherwise return OP itself. */ -static rtx +rtx mips_strip_unspec_address (rtx op) { rtx base, offset; @@ -14070,7 +14070,7 @@ mips16_rewrite_pool_constant (struct mips16_constant_pool *pool, rtx *x) split_const (*x, &base, &offset); if (GET_CODE (base) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (base)) { - label = mips16_add_constant (pool, get_pool_constant (base), + label = mips16_add_constant (pool, copy_rtx (get_pool_constant (base)), get_pool_mode (base)); base = gen_rtx_LABEL_REF (Pmode, label); *x = mips_unspec_address_offset (base, offset, SYMBOL_PC_RELATIVE); @@ -14126,10 +14126,11 @@ mips_cfg_in_reorg (void) || TARGET_RELAX_PIC_CALLS); } -/* Build MIPS16 constant pools. */ +/* Build MIPS16 constant pools. Split the instructions if SPLIT_P, + otherwise assume that they are already split. */ static void -mips16_lay_out_constants (void) +mips16_lay_out_constants (bool split_p) { struct mips16_constant_pool pool; struct mips16_rewrite_pool_refs_info info; @@ -14138,10 +14139,13 @@ mips16_lay_out_constants (void) if (!TARGET_MIPS16_PCREL_LOADS) return; - if (mips_cfg_in_reorg ()) - split_all_insns (); - else - split_all_insns_noflow (); + if (split_p) + { + if (mips_cfg_in_reorg ()) + split_all_insns (); + else + split_all_insns_noflow (); + } barrier = 0; memset (&pool, 0, sizeof (pool)); for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) @@ -15490,6 +15494,110 @@ mips_df_reorg (void) df_finish_pass (false); } +/* Emit code to load LABEL_REF SRC into MIPS16 register DEST. This is + called very late in mips_reorg, but the caller is required to run + mips16_lay_out_constants on the result. */ + +static void +mips16_load_branch_target (rtx dest, rtx src) +{ + if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS) + { + rtx page, low; + + if (mips_cfun_has_cprestore_slot_p ()) + mips_emit_move (dest, mips_cprestore_slot (dest, true)); + else + mips_emit_move (dest, pic_offset_table_rtx); + page = mips_unspec_address (src, SYMBOL_GOTOFF_PAGE); + low = mips_unspec_address (src, SYMBOL_GOT_PAGE_OFST); + emit_insn (gen_rtx_SET (VOIDmode, dest, + PMODE_INSN (gen_unspec_got, (dest, page)))); + emit_insn (gen_rtx_SET (VOIDmode, dest, + gen_rtx_LO_SUM (Pmode, dest, low))); + } + else + { + src = mips_unspec_address (src, SYMBOL_ABSOLUTE); + mips_emit_move (dest, src); + } +} + +/* If we're compiling a MIPS16 function, look for and split any long branches. + This must be called after all other instruction modifications in + mips_reorg. */ + +static void +mips16_split_long_branches (void) +{ + bool something_changed; + + if (!TARGET_MIPS16) + return; + + /* Loop until the alignments for all targets are sufficient. */ + do + { + rtx insn; + + shorten_branches (get_insns ()); + something_changed = false; + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (JUMP_P (insn) + && USEFUL_INSN_P (insn) + && get_attr_length (insn) > 8) + { + rtx old_label, new_label, temp, saved_temp; + rtx target, jump, jump_sequence; + + start_sequence (); + + /* Free up a MIPS16 register by saving it in $1. */ + saved_temp = gen_rtx_REG (Pmode, AT_REGNUM); + temp = gen_rtx_REG (Pmode, GP_REG_FIRST + 2); + emit_move_insn (saved_temp, temp); + + /* Load the branch target into TEMP. */ + old_label = JUMP_LABEL (insn); + target = gen_rtx_LABEL_REF (Pmode, old_label); + mips16_load_branch_target (temp, target); + + /* Jump to the target and restore the register's + original value. */ + jump = emit_jump_insn (PMODE_INSN (gen_indirect_jump_and_restore, + (temp, temp, saved_temp))); + JUMP_LABEL (jump) = old_label; + LABEL_NUSES (old_label)++; + + /* Rewrite any symbolic references that are supposed to use + a PC-relative constant pool. */ + mips16_lay_out_constants (false); + + if (simplejump_p (insn)) + /* We're going to replace INSN with a longer form. */ + new_label = NULL_RTX; + else + { + /* Create a branch-around label for the original + instruction. */ + new_label = gen_label_rtx (); + emit_label (new_label); + } + + jump_sequence = get_insns (); + end_sequence (); + + emit_insn_after (jump_sequence, insn); + if (new_label) + invert_jump (insn, new_label, false); + else + delete_insn (insn); + something_changed = true; + } + } + while (something_changed); +} + /* Implement TARGET_MACHINE_DEPENDENT_REORG. */ static void @@ -15500,7 +15608,7 @@ mips_reorg (void) to date if the CFG is available. */ if (mips_cfg_in_reorg ()) compute_bb_for_insn (); - mips16_lay_out_constants (); + mips16_lay_out_constants (true); if (mips_cfg_in_reorg ()) { mips_df_reorg (); @@ -15519,6 +15627,7 @@ mips_reorg (void) /* The expansion could invalidate some of the VR4130 alignment optimizations, but this should be an extremely rare case anyhow. */ mips_reorg_process_insns (); + mips16_split_long_branches (); } /* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text @@ -15639,7 +15748,7 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, insn = get_insns (); insn_locators_alloc (); split_all_insns_noflow (); - mips16_lay_out_constants (); + mips16_lay_out_constants (true); shorten_branches (insn); final_start_function (insn, file, 1); final (insn, file, 1); diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md index febe07799e6..759958ba460 100644 --- a/gcc/config/mips/mips.md +++ b/gcc/config/mips/mips.md @@ -402,11 +402,7 @@ ;; Length of instruction in bytes. (define_attr "length" "" - (cond [(and (eq_attr "extended_mips16" "yes") - (match_test "TARGET_MIPS16")) - (const_int 8) - - ;; Direct branch instructions have a range of [-0x20000,0x1fffc], + (cond [;; Direct branch instructions have a range of [-0x20000,0x1fffc], ;; relative to the address of the delay slot. If a branch is ;; outside this range, we have a choice of two sequences. ;; For PIC, an out-of-range branch like: @@ -431,14 +427,21 @@ ;; using la/jr in this case too, but we do not do so at ;; present. ;; - ;; Note that this value does not account for the delay slot + ;; The value we specify here does not account for the delay slot ;; instruction, whose length is added separately. If the RTL ;; pattern has no explicit delay slot, mips_adjust_insn_length - ;; will add the length of the implicit nop. The values for - ;; forward and backward branches will be different as well. - (eq_attr "type" "branch") + ;; will add the length of the implicit nop. The range of + ;; [-0x20000, 0x1fffc] from the address of the delay slot + ;; therefore translates to a range of: + ;; + ;; [-(0x20000 - sizeof (branch)), 0x1fffc - sizeof (slot)] + ;; == [-0x1fffc, 0x1fff8] + ;; + ;; from the shorten_branches reference address. + (and (eq_attr "type" "branch") + (not (match_test "TARGET_MIPS16"))) (cond [(and (le (minus (match_dup 0) (pc)) (const_int 131064)) - (le (minus (pc) (match_dup 0)) (const_int 131068))) + (le (minus (pc) (match_dup 0)) (const_int 131068))) (const_int 4) ;; The non-PIC case: branch, first delay slot, and J. @@ -453,6 +456,100 @@ ;; of an insn. (const_int MAX_PIC_BRANCH_LENGTH)) + ;; An unextended MIPS16 branch has a range of [-0x100, 0xfe] + ;; from the address of the following instruction, which leads + ;; to a range of: + ;; + ;; [-(0x100 - sizeof (branch)), 0xfe] + ;; == [-0xfe, 0xfe] + ;; + ;; from the shorten_branches reference address. Extended branches + ;; likewise have a range of [-0x10000, 0xfffe] from the address + ;; of the following instruction, which leads to a range of: + ;; + ;; [-(0x10000 - sizeof (branch)), 0xfffe] + ;; == [-0xfffc, 0xfffe] + ;; + ;; from the reference address. + ;; + ;; When a branch is out of range, mips_reorg splits it into a form + ;; that uses in-range branches. There are four basic sequences: + ;; + ;; (1) Absolute addressing with a readable text segment + ;; (32-bit addresses): + ;; + ;; b... foo 2 bytes + ;; move $1,$2 2 bytes + ;; lw $2,label 2 bytes + ;; jr $2 2 bytes + ;; move $2,$1 2 bytes + ;; .align 2 0 or 2 bytes + ;; label: + ;; .word target 4 bytes + ;; foo: + ;; (16 bytes in the worst case) + ;; + ;; (2) Absolute addressing with a readable text segment + ;; (64-bit addresses): + ;; + ;; b... foo 2 bytes + ;; move $1,$2 2 bytes + ;; ld $2,label 2 bytes + ;; jr $2 2 bytes + ;; move $2,$1 2 bytes + ;; .align 3 0 to 6 bytes + ;; label: + ;; .dword target 8 bytes + ;; foo: + ;; (24 bytes in the worst case) + ;; + ;; (3) Absolute addressing without a readable text segment + ;; (which requires 32-bit addresses at present): + ;; + ;; b... foo 2 bytes + ;; move $1,$2 2 bytes + ;; lui $2,%hi(target) 4 bytes + ;; sll $2,8 2 bytes + ;; sll $2,8 2 bytes + ;; addiu $2,%lo(target) 4 bytes + ;; jr $2 2 bytes + ;; move $2,$1 2 bytes + ;; foo: + ;; (20 bytes) + ;; + ;; (4) PIC addressing (which requires 32-bit addresses at present): + ;; + ;; b... foo 2 bytes + ;; move $1,$2 2 bytes + ;; lw $2,cprestore 0, 2 or 4 bytes + ;; lw $2,%got(target)($2) 4 bytes + ;; addiu $2,%lo(target) 4 bytes + ;; jr $2 2 bytes + ;; move $2,$1 2 bytes + ;; foo: + ;; (20 bytes in the worst case) + ;; + ;; Note that the conditions test adjusted lengths, whereas the + ;; result is an unadjusted length, and is thus twice the true value. + (and (eq_attr "type" "branch") + (match_test "TARGET_MIPS16")) + (cond [(and (le (minus (match_dup 0) (pc)) (const_int 254)) + (le (minus (pc) (match_dup 0)) (const_int 254))) + (const_int 4) + (and (le (minus (match_dup 0) (pc)) (const_int 65534)) + (le (minus (pc) (match_dup 0)) (const_int 65532))) + (const_int 8) + (and (match_test "TARGET_ABICALLS") + (not (match_test "TARGET_ABSOLUTE_ABICALLS"))) + (const_int 40) + (match_test "Pmode == SImode") + (const_int 32) + ] (const_int 48)) + + (and (eq_attr "extended_mips16" "yes") + (match_test "TARGET_MIPS16")) + (const_int 8) + ;; "Ghost" instructions occupy no space. (eq_attr "type" "ghost") (const_int 0) @@ -5400,28 +5497,29 @@ (define_insn "*branch_equality_mips16" [(set (pc) (if_then_else - (match_operator 0 "equality_operator" - [(match_operand:GPR 1 "register_operand" "d,t") + (match_operator 1 "equality_operator" + [(match_operand:GPR 2 "register_operand" "d,t") (const_int 0)]) - (match_operand 2 "pc_or_label_operand" "") - (match_operand 3 "pc_or_label_operand" "")))] + (label_ref (match_operand 0 "" "")) + (pc)))] "TARGET_MIPS16" -{ - if (operands[2] != pc_rtx) - { - if (which_alternative == 0) - return "b%C0z\t%1,%2"; - else - return "bt%C0z\t%2"; - } - else - { - if (which_alternative == 0) - return "b%N0z\t%1,%3"; - else - return "bt%N0z\t%3"; - } -} + "@ + b%C1z\t%2,%0 + bt%C1z\t%0" + [(set_attr "type" "branch")]) + +(define_insn "*branch_equality_mips16_inverted" + [(set (pc) + (if_then_else + (match_operator 1 "equality_operator" + [(match_operand:GPR 2 "register_operand" "d,t") + (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "TARGET_MIPS16" + "@ + b%N1z\t%2,%0 + bt%N1z\t%0" [(set_attr "type" "branch")]) (define_expand "cbranch4" @@ -5717,7 +5815,30 @@ (label_ref (match_operand 0 "" "")))] "TARGET_MIPS16" "b\t%l0" - [(set_attr "type" "branch")]) + [(set_attr "type" "branch") + (set (attr "length") + ;; This calculation is like the normal branch one, but the + ;; range of the unextended instruction is [-0x800, 0x7fe] rather + ;; than [-0x100, 0xfe]. This translates to a range of: + ;; + ;; [-(0x800 - sizeof (branch)), 0x7fe] + ;; == [-0x7fe, 0x7fe] + ;; + ;; from the shorten_branches reference address. Long-branch + ;; sequences will replace this one, so the minimum length + ;; is one instruction shorter than for conditional branches. + (cond [(and (le (minus (match_dup 0) (pc)) (const_int 2046)) + (le (minus (pc) (match_dup 0)) (const_int 2046))) + (const_int 4) + (and (le (minus (match_dup 0) (pc)) (const_int 65534)) + (le (minus (pc) (match_dup 0)) (const_int 65532))) + (const_int 8) + (and (match_test "TARGET_ABICALLS") + (not (match_test "TARGET_ABSOLUTE_ABICALLS"))) + (const_int 36) + (match_test "Pmode == SImode") + (const_int 28) + ] (const_int 44)))]) (define_expand "indirect_jump" [(set (pc) (match_operand 0 "register_operand"))] @@ -5735,6 +5856,18 @@ [(set_attr "type" "jump") (set_attr "mode" "none")]) +;; A combined jump-and-move instruction, used for MIPS16 long-branch +;; sequences. Having a dedicated pattern is more convenient than +;; creating a SEQUENCE for this special case. +(define_insn "indirect_jump_and_restore_" + [(set (pc) (match_operand:P 1 "register_operand" "d")) + (set (match_operand:P 0 "register_operand" "=d") + (match_operand:P 2 "register_operand" "y"))] + "" + "%(%%)" + [(set_attr "type" "multi") + (set_attr "extended_mips16" "yes")]) + (define_expand "tablejump" [(set (pc) (match_operand 0 "register_operand")) @@ -6549,7 +6682,8 @@ UNSPEC_CONSTTABLE_INT)] "TARGET_MIPS16" { - assemble_integer (operands[0], INTVAL (operands[1]), + assemble_integer (mips_strip_unspec_address (operands[0]), + INTVAL (operands[1]), BITS_PER_UNIT * INTVAL (operands[1]), 1); return ""; } diff --git a/gcc/config/mips/predicates.md b/gcc/config/mips/predicates.md index 97971e9a205..31c43a53ebb 100644 --- a/gcc/config/mips/predicates.md +++ b/gcc/config/mips/predicates.md @@ -139,9 +139,6 @@ (match_operand 0 "hilo_operand") (match_operand 0 "register_operand"))) -(define_special_predicate "pc_or_label_operand" - (match_code "pc,label_ref")) - (define_predicate "const_call_insn_operand" (match_code "const,symbol_ref,label_ref") { diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 8ef8e789b38..78a829aaf26 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2012-08-02 Richard Sandiford + + PR target/51931 + * gcc.c-torture/compile/20001226-1.c: Remove nomips16 attribute. + * g++.dg/opt/longbranch1.C: Likewise. + 2012-08-02 Marc Glisse PR tree-optimization/53805 diff --git a/gcc/testsuite/g++.dg/opt/longbranch1.C b/gcc/testsuite/g++.dg/opt/longbranch1.C index df545012cc7..a64a57aea2a 100644 --- a/gcc/testsuite/g++.dg/opt/longbranch1.C +++ b/gcc/testsuite/g++.dg/opt/longbranch1.C @@ -26,10 +26,6 @@ muchcode; muchcode; muchcode; muchcode; muchcode; muchcode; \ muchcode; muchcode; muchcode; muchcode; muchcode; muchcode -#ifdef __mips -/* See PR 51931. */ -__attribute__((nomips16)) -#endif int main (int argc, char **argv) { diff --git a/gcc/testsuite/gcc.c-torture/compile/20001226-1.c b/gcc/testsuite/gcc.c-torture/compile/20001226-1.c index e833fb044c6..127c4daec45 100644 --- a/gcc/testsuite/gcc.c-torture/compile/20001226-1.c +++ b/gcc/testsuite/gcc.c-torture/compile/20001226-1.c @@ -21,10 +21,6 @@ #define C1024(x,y) C256(x,y) C256(x+16,y) C256(x+32,y) C256(x+48,y) #define C4096(x,y) C1024(x,y) C1024(x,y+16) C1024(x,y+32) C1024(x,y+48) -#ifdef __mips -/* See PR 51931. */ -__attribute__((nomips16)) -#endif unsigned foo(int x[64], int y[64]) { C4096(x,y);