diff options
author | Richard Sandiford <rdsandiford@googlemail.com> | 2012-08-02 21:31:57 +0000 |
---|---|---|
committer | Richard Sandiford <rsandifo@gcc.gnu.org> | 2012-08-02 21:31:57 +0000 |
commit | 36be1dee1d8f88f1da9a27a9ce6d20f8976f210c (patch) | |
tree | 5e7dd3cafaad2526a4126abfd48740a3a4fbdac8 /gcc/config/mips/mips.c | |
parent | b5b071a5688e68b783547add84949c63c49d7dc1 (diff) | |
download | gcc-36be1dee1d8f88f1da9a27a9ce6d20f8976f210c.zip gcc-36be1dee1d8f88f1da9a27a9ce6d20f8976f210c.tar.gz gcc-36be1dee1d8f88f1da9a27a9ce6d20f8976f210c.tar.bz2 |
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<mode>_mips16): Replace use pc_or_label_operand
with explicit label_ref and pc. Follow the usual operand numbering.
(*branch_equality<mode>_mips16_inverted): New pattern.
(*jump_mips16): Add length attribute.
(indirect_jump_and_restore_<mode>): 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
Diffstat (limited to 'gcc/config/mips/mips.c')
-rw-r--r-- | gcc/config/mips/mips.c | 129 |
1 files changed, 119 insertions, 10 deletions
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index e9e4ec1..ef1cccd 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); |