mips-protos.h (mips_cfun_has_cprestore_slot_p): Declare.
authorRichard Sandiford <rdsandiford@googlemail.com>
Mon, 14 Sep 2009 18:52:16 +0000 (18:52 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Mon, 14 Sep 2009 18:52:16 +0000 (18:52 +0000)
gcc/
* config/mips/mips-protos.h (mips_cfun_has_cprestore_slot_p): Declare.
(mips_cprestore_address_p): Likewise.
(mips_save_gp_to_cprestore_slot): Likewise.
(mips_restore_gp): Rename to...
(mips_restore_gp_from_cprestore_slot): ...this.
(mips_must_initialize_gp_p): Declare.
(mips_emit_save_slot_move): Likewise.
(mips_output_load_label): Return nothing.
(mips_eh_uses): Declare.
* config/mips/mips.h (TARGET_SPLIT_CALLS): Require epilogue_completed.
(TARGET_CPRESTORE_DIRECTIVE): New macro.
(TARGET_ABSOLUTE_JUMPS): Likewise.
(EH_USES): Likewise.
(FIRST_PSEUDO_REGISTER): Update comment.
(MIPS_ABSOLUTE_JUMP): New macro, extracted from...
(MIPS_CALL): ...here.
(REGISTER_NAMES): Add $cprestore.
* config/mips/mips.c (machine_function): Remove has_gp_insn_p.
Add load_label_length, has_inflexible_gp_insn_p,
has_flexible_gp_insn_p, must_initialize_gp_p and
must_restore_gp_when_clobbered_p.
(mips_expand_call): Don't generate split instructions here.
(mips_split_call): Update the call to mips_restore_gp after
the above name change.
(mips16_cfun_returns_in_fpr_p): Move earlier in file.
(mips_find_gp_ref): New function.
(mips_insn_has_inflexible_gp_ref_p): Likewise.
(mips_cfun_has_inflexible_gp_ref_p): Likewise.
(mips_insn_has_flexible_gp_ref_p): Likewise.
(mips_cfun_has_flexible_gp_ref_p): Likewise.
(mips_function_has_gp_insn): Delete.
(mips_global_pointer): Drop the df_regs_ever_live_p check.
Use the new functions above.  Only return INVALID_REGNUM
for TARGET_ABSOLUTE_JUMPS.
(mips_must_initialize_gp_p): New function.
(mips_get_cprestore_base_and_offset): New function, extracted from...
(mips_cprestore_slot): ...here.  Take a bool parameter.
(mips_cfun_has_cprestore_slot_p): New function.
(mips_cprestore_address_p): Likewise.
(mips_save_gp_to_cprestore_slot): Likewise.
(mips_restore_gp): Rename to...
(mips_restore_gp_from_cprestore_slot): ...this.  Assert
epilogue_completed.  Update the call to mips_cprestore_slot.
Test cfun->machine->must_restore_gp_when_clobbered_p.
(mips_direct_save_slot_move_p): New function.
(mips_emit_save_slot_move): Likewise.
(mips_output_cplocal): Test mips_must_initialize_gp_p () instead
of cfun->machine->global_pointer.
(mips_output_function_prologue): Check mips_must_initialize_gp_p ().
(mips_save_reg): Use mips_emit_save_slot_move.
(mips_expand_prologue): Set must_initialize_gp_p.
Use mips_cfun_has_cprestore_slot_p.  Use gen_potential_cprestore
for all cprestore saves.  Emit a use_cprestore instruction after
setting up the cprestore slot.
(mips_restore_reg): Use mips_emit_save_slot_move.
(mips_process_load_label): New function.
(mips_load_label_length): Likewise.
(mips_output_load_label): Don't return asm: output it here instead.
Use mips_process_load_label.
(mips_adjust_insn_length): Adjust the length of branch instructions
that have length MAX_PIC_BRANCH_LENGTH.
(mips_output_conditional_branch): Update the call to
mips_output_load_label.  Assume the branch target is OPERANDS[0]
rather than OPERANDS[1].  Use MIPS_ABSOLUTE_JUMP for absolute jumps.
(mips_output_order_conditional_branch): Swap the meaning of
OPERANDS[0] and OPERANDS[1].
(mips_variable_issue): Don't count ghost instructions.
(mips_expand_ghost_gp_insns): New function.
(mips_reorg): Rerun mips_reorg_process_insns if it returns true.
(mips_output_mi_thunk): Set must_initialize_gp_p.
(mips_eh_uses): New function.
* config/mips/predicates.md (cprestore_save_slot_operand)
(cprestore_load_slot_operand): New predicates.
* config/mips/mips.md (UNSPEC_POTENTIAL_CPRESTORE): New unspec.
(UNSPEC_MOVE_GP): Likewise.
(UNSPEC_CPRESTORE, UNSPEC_RESTORE_GP, UNSPEC_EH_RETURN)
(UNSPEC_CONSTTABLE_INT, UNSPEC_CONSTTABLE_FLOAT): Bump to make room.
(CPRESTORE_SLOT_REGNUM): New register.
(MAX_PIC_BRANCH_LENGTH): New constant.
(jal_macro): Use MIPS_ABSOLUTE_JUMPS.
(length): Use MAX_PIC_BRANCH_LENGTH as a placeholder for PIC long
branches.  Fix commentary.
(loadgp_newabi_<mode>): Change from unspec_volatile to unspec.
Only split if mips_must_initialize_gp_p; expand to nothing otherwise.
Change type to "ghost".
(loadgp_absolute_<mode>): Likewise.
(loadgp_rtp_<mode>): Likewise.
(copygp_mips16): Likewise.
(loadgp_blockage): Remove redundant mode attribute.
(potential_cprestore): New instruction.
(cprestore): Turn into an unspec set.
(use_cprestore): New instruction.
(*branch_fp): Swap operands 0 and 1.  Remove redundant mode attribute.
(*branch_fp_inverted): Likewise.
(*branch_order<mode>): Likewise.
(*branch_order<mode>_inverted): Likewise.
(*branch_equality<mode>): Likewise.
(*branch_equality<mode>_inverted): Likewise.
(*branch_bit<bbv><mode>): Likewise.
(*branch_bit<bbv><mode>_inverted): Likewise.
(*branch_equality<mode>_mips16): Remove redundant mode.
(jump): Turn into a define_expand.
(*jump_absolute): New instruction.
(*jump_pic): Likewise.
(*jump_mips16): Rename previously-unnamed pattern.  Remove
redundant mode attribute.
(restore_gp): Split on epilogue_completed rather than
reload_completed.  Change type to "ghost".
(move_gp<mode>): New instruction.
* config/mips/mips-dsp.md (mips_bposge): Swap operands 0 and 1.
Remove redundant mode attribute.
* config/mips/mips-ps-3d.md (bc1any4t): Likewise.
(bc1any4f, bc1any2t, bc1any2f): Likewise.
(*branch_upper_lower, *branch_upper_lower_inverted): Likewise.

gcc/testsuite/
* gcc.target/mips/branch-helper.h: New file.
* gcc.target/mips/branch-2.c,
* gcc.target/mips/branch-3.c,
* gcc.target/mips/branch-4.c,
* gcc.target/mips/branch-5.c,
* gcc.target/mips/branch-6.c,
* gcc.target/mips/branch-7.c,
* gcc.target/mips/branch-8.c,
* gcc.target/mips/branch-9.c,
* gcc.target/mips/branch-10.c,
* gcc.target/mips/branch-11.c,
* gcc.target/mips/branch-12.c,
* gcc.target/mips/branch-13.c,
* gcc.target/mips/branch-14.c,
* gcc.target/mips/branch-15.c: New tests.

From-SVN: r151695

24 files changed:
gcc/ChangeLog
gcc/config/mips/mips-dsp.md
gcc/config/mips/mips-protos.h
gcc/config/mips/mips-ps-3d.md
gcc/config/mips/mips.c
gcc/config/mips/mips.h
gcc/config/mips/mips.md
gcc/config/mips/predicates.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/mips/branch-10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-11.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-12.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-13.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-14.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-15.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-3.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-4.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-5.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-6.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-7.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-8.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-9.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/branch-helper.h [new file with mode: 0644]

index 8971af86ff48af01621a7b7dd1921a51dfe245e0..75333c13f38a3bafd4f91deb96a856a1480f0ae9 100644 (file)
@@ -1,3 +1,120 @@
+2009-09-14  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       * config/mips/mips-protos.h (mips_cfun_has_cprestore_slot_p): Declare.
+       (mips_cprestore_address_p): Likewise.
+       (mips_save_gp_to_cprestore_slot): Likewise.
+       (mips_restore_gp): Rename to...
+       (mips_restore_gp_from_cprestore_slot): ...this.
+       (mips_must_initialize_gp_p): Declare.
+       (mips_emit_save_slot_move): Likewise.
+       (mips_output_load_label): Return nothing.
+       (mips_eh_uses): Declare.
+       * config/mips/mips.h (TARGET_SPLIT_CALLS): Require epilogue_completed.
+       (TARGET_CPRESTORE_DIRECTIVE): New macro.
+       (TARGET_ABSOLUTE_JUMPS): Likewise.
+       (EH_USES): Likewise.
+       (FIRST_PSEUDO_REGISTER): Update comment.
+       (MIPS_ABSOLUTE_JUMP): New macro, extracted from...
+       (MIPS_CALL): ...here.
+       (REGISTER_NAMES): Add $cprestore.
+       * config/mips/mips.c (machine_function): Remove has_gp_insn_p.
+       Add load_label_length, has_inflexible_gp_insn_p,
+       has_flexible_gp_insn_p, must_initialize_gp_p and
+       must_restore_gp_when_clobbered_p.
+       (mips_expand_call): Don't generate split instructions here.
+       (mips_split_call): Update the call to mips_restore_gp after
+       the above name change.
+       (mips16_cfun_returns_in_fpr_p): Move earlier in file.
+       (mips_find_gp_ref): New function.
+       (mips_insn_has_inflexible_gp_ref_p): Likewise.
+       (mips_cfun_has_inflexible_gp_ref_p): Likewise.
+       (mips_insn_has_flexible_gp_ref_p): Likewise.
+       (mips_cfun_has_flexible_gp_ref_p): Likewise.
+       (mips_function_has_gp_insn): Delete.
+       (mips_global_pointer): Drop the df_regs_ever_live_p check.
+       Use the new functions above.  Only return INVALID_REGNUM
+       for TARGET_ABSOLUTE_JUMPS.
+       (mips_must_initialize_gp_p): New function.
+       (mips_get_cprestore_base_and_offset): New function, extracted from...
+       (mips_cprestore_slot): ...here.  Take a bool parameter.
+       (mips_cfun_has_cprestore_slot_p): New function.
+       (mips_cprestore_address_p): Likewise.
+       (mips_save_gp_to_cprestore_slot): Likewise.
+       (mips_restore_gp): Rename to...
+       (mips_restore_gp_from_cprestore_slot): ...this.  Assert
+       epilogue_completed.  Update the call to mips_cprestore_slot.
+       Test cfun->machine->must_restore_gp_when_clobbered_p.
+       (mips_direct_save_slot_move_p): New function.
+       (mips_emit_save_slot_move): Likewise.
+       (mips_output_cplocal): Test mips_must_initialize_gp_p () instead
+       of cfun->machine->global_pointer.
+       (mips_output_function_prologue): Check mips_must_initialize_gp_p ().
+       (mips_save_reg): Use mips_emit_save_slot_move.
+       (mips_expand_prologue): Set must_initialize_gp_p.
+       Use mips_cfun_has_cprestore_slot_p.  Use gen_potential_cprestore
+       for all cprestore saves.  Emit a use_cprestore instruction after
+       setting up the cprestore slot.
+       (mips_restore_reg): Use mips_emit_save_slot_move.
+       (mips_process_load_label): New function.
+       (mips_load_label_length): Likewise.
+       (mips_output_load_label): Don't return asm: output it here instead.
+       Use mips_process_load_label.
+       (mips_adjust_insn_length): Adjust the length of branch instructions
+       that have length MAX_PIC_BRANCH_LENGTH.
+       (mips_output_conditional_branch): Update the call to
+       mips_output_load_label.  Assume the branch target is OPERANDS[0]
+       rather than OPERANDS[1].  Use MIPS_ABSOLUTE_JUMP for absolute jumps.
+       (mips_output_order_conditional_branch): Swap the meaning of
+       OPERANDS[0] and OPERANDS[1].
+       (mips_variable_issue): Don't count ghost instructions.
+       (mips_expand_ghost_gp_insns): New function.
+       (mips_reorg): Rerun mips_reorg_process_insns if it returns true.
+       (mips_output_mi_thunk): Set must_initialize_gp_p.
+       (mips_eh_uses): New function.
+       * config/mips/predicates.md (cprestore_save_slot_operand)
+       (cprestore_load_slot_operand): New predicates.
+       * config/mips/mips.md (UNSPEC_POTENTIAL_CPRESTORE): New unspec.
+       (UNSPEC_MOVE_GP): Likewise.
+       (UNSPEC_CPRESTORE, UNSPEC_RESTORE_GP, UNSPEC_EH_RETURN)
+       (UNSPEC_CONSTTABLE_INT, UNSPEC_CONSTTABLE_FLOAT): Bump to make room.
+       (CPRESTORE_SLOT_REGNUM): New register.
+       (MAX_PIC_BRANCH_LENGTH): New constant.
+       (jal_macro): Use MIPS_ABSOLUTE_JUMPS.
+       (length): Use MAX_PIC_BRANCH_LENGTH as a placeholder for PIC long
+       branches.  Fix commentary.
+       (loadgp_newabi_<mode>): Change from unspec_volatile to unspec.
+       Only split if mips_must_initialize_gp_p; expand to nothing otherwise.
+       Change type to "ghost".
+       (loadgp_absolute_<mode>): Likewise.
+       (loadgp_rtp_<mode>): Likewise.
+       (copygp_mips16): Likewise.
+       (loadgp_blockage): Remove redundant mode attribute.
+       (potential_cprestore): New instruction.
+       (cprestore): Turn into an unspec set.
+       (use_cprestore): New instruction.
+       (*branch_fp): Swap operands 0 and 1.  Remove redundant mode attribute.
+       (*branch_fp_inverted): Likewise.
+       (*branch_order<mode>): Likewise.
+       (*branch_order<mode>_inverted): Likewise.
+       (*branch_equality<mode>): Likewise.
+       (*branch_equality<mode>_inverted): Likewise.
+       (*branch_bit<bbv><mode>): Likewise.
+       (*branch_bit<bbv><mode>_inverted): Likewise.
+       (*branch_equality<mode>_mips16): Remove redundant mode.
+       (jump): Turn into a define_expand.
+       (*jump_absolute): New instruction.
+       (*jump_pic): Likewise.
+       (*jump_mips16): Rename previously-unnamed pattern.  Remove
+       redundant mode attribute.
+       (restore_gp): Split on epilogue_completed rather than
+       reload_completed.  Change type to "ghost".
+       (move_gp<mode>): New instruction.
+       * config/mips/mips-dsp.md (mips_bposge): Swap operands 0 and 1.
+       Remove redundant mode attribute.
+       * config/mips/mips-ps-3d.md (bc1any4t): Likewise.
+       (bc1any4f, bc1any2t, bc1any2f): Likewise.
+       (*branch_upper_lower, *branch_upper_lower_inverted): Likewise.
+
 2009-09-14  Michael Meissner  <meissner@linux.vnet.ibm.com>
 
        PR target/41210
index 5112582d3a2baac5860b2c7e1a06fb1924452aaa..ff2004ccb5470ead5c55f3e5a50949931ac57f96 100644 (file)
 (define_insn "mips_bposge"
   [(set (pc)
        (if_then_else (ge (reg:CCDSP CCDSP_PO_REGNUM)
-                         (match_operand:SI 0 "immediate_operand" "I"))
-                     (label_ref (match_operand 1 "" ""))
+                         (match_operand:SI 1 "immediate_operand" "I"))
+                     (label_ref (match_operand 0 "" ""))
                      (pc)))]
   "ISA_HAS_DSP"
-  "%*bposge%0\t%1%/"
-  [(set_attr "type"    "branch")
-   (set_attr "mode"    "none")])
+  "%*bposge%1\t%0%/"
+  [(set_attr "type"    "branch")])
 
index a1e28ce23c6e797e01d19a37c4ad232ccd9c93c5..91fa72974e84723769428c1238d3f3616ed74fab 100644 (file)
@@ -219,7 +219,10 @@ extern rtx mips_subword (rtx, bool);
 extern bool mips_split_64bit_move_p (rtx, rtx);
 extern void mips_split_doubleword_move (rtx, rtx);
 extern const char *mips_output_move (rtx, rtx);
-extern void mips_restore_gp (rtx);
+extern bool mips_cfun_has_cprestore_slot_p (void);
+extern bool mips_cprestore_address_p (rtx, bool);
+extern void mips_save_gp_to_cprestore_slot (rtx, rtx, rtx, rtx);
+extern void mips_restore_gp_from_cprestore_slot (rtx);
 #ifdef RTX_CODE
 extern void mips_expand_scc (rtx *);
 extern void mips_expand_conditional_branch (rtx *);
@@ -276,7 +279,9 @@ extern bool mips_small_data_pattern_p (rtx);
 extern rtx mips_rewrite_small_data (rtx);
 extern HOST_WIDE_INT mips_initial_elimination_offset (int, int);
 extern rtx mips_return_addr (int, rtx);
+extern bool mips_must_initialize_gp_p (void);
 extern enum mips_loadgp_style mips_current_loadgp_style (void);
+extern void mips_emit_save_slot_move (rtx, rtx, rtx);
 extern void mips_expand_prologue (void);
 extern void mips_expand_before_return (void);
 extern void mips_expand_epilogue (bool);
@@ -296,7 +301,7 @@ extern int mips_register_move_cost (enum machine_mode, enum reg_class,
                                    enum reg_class);
 
 extern int mips_adjust_insn_length (rtx, int);
-extern const char *mips_output_load_label (void);
+extern void mips_output_load_label (rtx);
 extern const char *mips_output_conditional_branch (rtx, rtx *, const char *,
                                                   const char *);
 extern const char *mips_output_order_conditional_branch (rtx, rtx *, bool);
@@ -334,6 +339,7 @@ extern void mips_expand_atomic_qihi (union mips_gen_fn_ptrs,
 
 extern void mips_expand_vector_init (rtx, rtx);
 
+extern bool mips_eh_uses (unsigned int);
 extern bool mips_epilogue_uses (unsigned int);
 extern void mips_final_prescan_insn (rtx, rtx *, int);
 
index 98932d85b11cd8cf19ce9df376c2b763d4287b94..c13c7a69b28902e97702f8c618017a1cf529c7a3 100644 (file)
 ; Branch on Any of Four Floating Point Condition Codes True
 (define_insn "bc1any4t"
   [(set (pc)
-       (if_then_else (ne (match_operand:CCV4 0 "register_operand" "z")
+       (if_then_else (ne (match_operand:CCV4 1 "register_operand" "z")
                          (const_int 0))
-                     (label_ref (match_operand 1 "" ""))
+                     (label_ref (match_operand 0 "" ""))
                      (pc)))]
   "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
-  "%*bc1any4t\t%0,%1%/"
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  "%*bc1any4t\t%1,%0%/"
+  [(set_attr "type" "branch")])
 
 ; Branch on Any of Four Floating Point Condition Codes False
 (define_insn "bc1any4f"
   [(set (pc)
-       (if_then_else (ne (match_operand:CCV4 0 "register_operand" "z")
+       (if_then_else (ne (match_operand:CCV4 1 "register_operand" "z")
                          (const_int -1))
-                     (label_ref (match_operand 1 "" ""))
+                     (label_ref (match_operand 0 "" ""))
                      (pc)))]
   "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
-  "%*bc1any4f\t%0,%1%/"
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  "%*bc1any4f\t%1,%0%/"
+  [(set_attr "type" "branch")])
 
 ; Branch on Any of Two Floating Point Condition Codes True
 (define_insn "bc1any2t"
   [(set (pc)
-       (if_then_else (ne (match_operand:CCV2 0 "register_operand" "z")
+       (if_then_else (ne (match_operand:CCV2 1 "register_operand" "z")
                          (const_int 0))
-                     (label_ref (match_operand 1 "" ""))
+                     (label_ref (match_operand 0 "" ""))
                      (pc)))]
   "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
-  "%*bc1any2t\t%0,%1%/"
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  "%*bc1any2t\t%1,%0%/"
+  [(set_attr "type" "branch")])
 
 ; Branch on Any of Two Floating Point Condition Codes False
 (define_insn "bc1any2f"
   [(set (pc)
-       (if_then_else (ne (match_operand:CCV2 0 "register_operand" "z")
+       (if_then_else (ne (match_operand:CCV2 1 "register_operand" "z")
                          (const_int -1))
-                     (label_ref (match_operand 1 "" ""))
+                     (label_ref (match_operand 0 "" ""))
                      (pc)))]
   "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
-  "%*bc1any2f\t%0,%1%/"
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  "%*bc1any2f\t%1,%0%/"
+  [(set_attr "type" "branch")])
 
 ; Used to access one register in a CCV2 pair.  Operand 0 is the register
 ; pair and operand 1 is the index of the register we want (a CONST_INT).
 (define_insn "*branch_upper_lower"
   [(set (pc)
         (if_then_else
-        (match_operator 0 "equality_operator"
+        (match_operator 1 "equality_operator"
            [(unspec:CC [(match_operand:CCV2 2 "register_operand" "z")
                         (match_operand 3 "const_int_operand")]
                        UNSPEC_SINGLE_CC)
             (const_int 0)])
-        (label_ref (match_operand 1 "" ""))
+        (label_ref (match_operand 0 "" ""))
         (pc)))]
   "TARGET_HARD_FLOAT"
 {
   operands[2]
     = gen_rtx_REG (CCmode, REGNO (operands[2]) + INTVAL (operands[3]));
   return mips_output_conditional_branch (insn, operands,
-                                        MIPS_BRANCH ("b%F0", "%2,%1"),
-                                        MIPS_BRANCH ("b%W0", "%2,%1"));
+                                        MIPS_BRANCH ("b%F1", "%2,%0"),
+                                        MIPS_BRANCH ("b%W1", "%2,%0"));
 }
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 ; As above, but with the sense of the condition reversed.
 (define_insn "*branch_upper_lower_inverted"
   [(set (pc)
         (if_then_else
-        (match_operator 0 "equality_operator"
+        (match_operator 1 "equality_operator"
            [(unspec:CC [(match_operand:CCV2 2 "register_operand" "z")
                         (match_operand 3 "const_int_operand")]
                        UNSPEC_SINGLE_CC)
             (const_int 0)])
         (pc)
-        (label_ref (match_operand 1 "" ""))))]
+        (label_ref (match_operand 0 "" ""))))]
   "TARGET_HARD_FLOAT"
 {
   operands[2]
     = gen_rtx_REG (CCmode, REGNO (operands[2]) + INTVAL (operands[3]));
   return mips_output_conditional_branch (insn, operands,
-                                        MIPS_BRANCH ("b%W0", "%2,%1"),
-                                        MIPS_BRANCH ("b%F0", "%2,%1"));
+                                        MIPS_BRANCH ("b%W1", "%2,%0"),
+                                        MIPS_BRANCH ("b%F1", "%2,%0"));
 }
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 ;----------------------------------------------------------------------------
 ; Floating Point Reduced Precision Reciprocal Square Root Instructions.
index 958abce0815b73a743f48938afd454da45c71686..2b0df8f0adf4e55b5bb82156d8c0b337ee27502c 100644 (file)
@@ -307,6 +307,10 @@ struct GTY(())  machine_function {
      if the function doesn't need one.  */
   unsigned int global_pointer;
 
+  /* How many instructions it takes to load a label into $AT, or 0 if
+     this property hasn't yet been calculated.  */
+  unsigned int load_label_length;
+
   /* True if mips_adjust_insn_length should ignore an instruction's
      hazard attribute.  */
   bool ignore_hazard_length_p;
@@ -315,8 +319,23 @@ struct GTY(())  machine_function {
      .set nomacro.  */
   bool all_noreorder_p;
 
-  /* True if the function is known to have an instruction that needs $gp.  */
-  bool has_gp_insn_p;
+  /* True if the function has "inflexible" and "flexible" references
+     to the global pointer.  See mips_cfun_has_inflexible_gp_ref_p
+     and mips_cfun_has_flexible_gp_ref_p for details.  */
+  bool has_inflexible_gp_insn_p;
+  bool has_flexible_gp_insn_p;
+
+  /* True if the function's prologue must load the global pointer
+     value into pic_offset_table_rtx and store the same value in
+     the function's cprestore slot (if any).  Even if this value
+     is currently false, we may decide to set it to true later;
+     see mips_must_initialize_gp_p () for details.  */
+  bool must_initialize_gp_p;
+
+  /* True if the current function must restore $gp after any potential
+     clobber.  This value is only meaningful during the first post-epilogue
+     split_insns pass; see mips_must_initialize_gp_p () for details.  */
+  bool must_restore_gp_when_clobbered_p;
 
   /* True if we have emitted an instruction to initialize
      mips16_gp_pseudo_rtx.  */
@@ -6313,9 +6332,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
     {
       rtx (*fn) (rtx, rtx);
 
-      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
-       fn = gen_call_split;
-      else if (type == MIPS_CALL_SIBCALL)
+      if (type == MIPS_CALL_SIBCALL)
        fn = gen_sibcall_internal;
       else
        fn = gen_call_internal;
@@ -6328,9 +6345,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
       rtx (*fn) (rtx, rtx, rtx, rtx);
       rtx reg1, reg2;
 
-      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
-       fn = gen_call_value_multiple_split;
-      else if (type == MIPS_CALL_SIBCALL)
+      if (type == MIPS_CALL_SIBCALL)
        fn = gen_sibcall_value_multiple_internal;
       else
        fn = gen_call_value_multiple_internal;
@@ -6343,9 +6358,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
     {
       rtx (*fn) (rtx, rtx, rtx);
 
-      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
-       fn = gen_call_value_split;
-      else if (type == MIPS_CALL_SIBCALL)
+      if (type == MIPS_CALL_SIBCALL)
        fn = gen_sibcall_value_internal;
       else
        fn = gen_call_value_internal;
@@ -6375,7 +6388,7 @@ mips_split_call (rtx insn, rtx call_pattern)
     /* Pick a temporary register that is suitable for both MIPS16 and
        non-MIPS16 code.  $4 and $5 are used for returning complex double
        values in soft-float code, so $6 is the first suitable candidate.  */
-    mips_restore_gp (gen_rtx_REG (Pmode, GP_ARG_FIRST + 2));
+    mips_restore_gp_from_cprestore_slot (gen_rtx_REG (Pmode, GP_ARG_FIRST + 2));
 }
 
 /* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
@@ -8566,42 +8579,131 @@ mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
   return buffer;
 }
 \f
-/* Return true if the current function has an insn that implicitly
-   refers to $gp.  */
+/* Return true if the current function returns its value in a floating-point
+   register in MIPS16 mode.  */
 
 static bool
-mips_function_has_gp_insn (void)
+mips16_cfun_returns_in_fpr_p (void)
 {
-  /* Don't bother rechecking if we found one last time.  */
-  if (!cfun->machine->has_gp_insn_p)
-    {
-      rtx insn;
+  tree return_type = DECL_RESULT (current_function_decl);
+  return (TARGET_MIPS16
+         && TARGET_HARD_FLOAT_ABI
+         && !aggregate_value_p (return_type, current_function_decl)
+         && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+}
 
+/* Return true if predicate PRED is true for at least one instruction.
+   Cache the result in *CACHE, and assume that the result is true
+   if *CACHE is already true.  */
+
+static bool
+mips_find_gp_ref (bool *cache, bool (*pred) (rtx))
+{
+  rtx insn;
+
+  if (!*cache)
+    {
       push_topmost_sequence ();
       for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-       if (USEFUL_INSN_P (insn)
-           && (get_attr_got (insn) != GOT_UNSET
-               || mips_small_data_pattern_p (PATTERN (insn))))
+       if (USEFUL_INSN_P (insn) && pred (insn))
          {
-           cfun->machine->has_gp_insn_p = true;
+           *cache = true;
            break;
          }
       pop_topmost_sequence ();
     }
-  return cfun->machine->has_gp_insn_p;
+  return *cache;
 }
 
-/* Return true if the current function returns its value in a floating-point
-   register in MIPS16 mode.  */
+/* Return true if INSN refers to the global pointer in an "inflexible" way.
+   See mips_cfun_has_inflexible_gp_ref_p for details.  */
 
 static bool
-mips16_cfun_returns_in_fpr_p (void)
+mips_insn_has_inflexible_gp_ref_p (rtx insn)
 {
-  tree return_type = DECL_RESULT (current_function_decl);
-  return (TARGET_MIPS16
-         && TARGET_HARD_FLOAT_ABI
-         && !aggregate_value_p (return_type, current_function_decl)
-         && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+  /* Uses of pic_offset_table_rtx in CALL_INSN_FUNCTION_USAGE
+     indicate that the target could be a traditional MIPS
+     lazily-binding stub.  */
+  return find_reg_fusage (insn, USE, pic_offset_table_rtx);
+}
+
+/* Return true if the current function refers to the global pointer
+   in a way that forces $28 to be valid.  This means that we can't
+   change the choice of global pointer, even for NewABI code.
+
+   One example of this (and one which needs several checks) is that
+   $28 must be valid when calling traditional MIPS lazy-binding stubs.
+   (This restriction does not apply to PLTs.)  */
+
+static bool
+mips_cfun_has_inflexible_gp_ref_p (void)
+{
+  /* If the function has a nonlocal goto, $28 must hold the correct
+     global pointer for the target function.  That is, the target
+     of the goto implicitly uses $28.  */
+  if (crtl->has_nonlocal_goto)
+    return true;
+
+  if (TARGET_ABICALLS_PIC2)
+    {
+      /* Symbolic accesses implicitly use the global pointer unless
+        -mexplicit-relocs is in effect.  JAL macros to symbolic addresses
+        might go to traditional MIPS lazy-binding stubs.  */
+      if (!TARGET_EXPLICIT_RELOCS)
+       return true;
+
+      /* FUNCTION_PROFILER includes a JAL to _mcount, which again
+        can be lazily-bound.  */
+      if (crtl->profile)
+       return true;
+
+      /* MIPS16 functions that return in FPRs need to call an
+        external libgcc routine.  This call is only made explict
+        during mips_expand_epilogue, and it too might be lazily bound.  */
+      if (mips16_cfun_returns_in_fpr_p ())
+       return true;
+    }
+
+  return mips_find_gp_ref (&cfun->machine->has_inflexible_gp_insn_p,
+                          mips_insn_has_inflexible_gp_ref_p);
+}
+
+/* Return true if INSN refers to the global pointer in a "flexible" way.
+   See mips_cfun_has_flexible_gp_ref_p for details.  */
+
+static bool
+mips_insn_has_flexible_gp_ref_p (rtx insn)
+{
+  return (get_attr_got (insn) != GOT_UNSET
+         || mips_small_data_pattern_p (PATTERN (insn))
+         || reg_overlap_mentioned_p (pic_offset_table_rtx, PATTERN (insn)));
+}
+
+/* Return true if the current function references the global pointer,
+   but if those references do not inherently require the global pointer
+   to be $28.  Assume !mips_cfun_has_inflexible_gp_ref_p ().  */
+
+static bool
+mips_cfun_has_flexible_gp_ref_p (void)
+{
+  /* Reload can sometimes introduce constant pool references
+     into a function that otherwise didn't need them.  For example,
+     suppose we have an instruction like:
+
+       (set (reg:DF R1) (float:DF (reg:SI R2)))
+
+     If R2 turns out to be a constant such as 1, the instruction may
+     have a REG_EQUAL note saying that R1 == 1.0.  Reload then has
+     the option of using this constant if R2 doesn't get allocated
+     to a register.
+
+     In cases like these, reload will have added the constant to the
+     pool but no instruction will yet refer to it.  */
+  if (TARGET_ABICALLS_PIC2 && !reload_completed && crtl->uses_const_pool)
+    return true;
+
+  return mips_find_gp_ref (&cfun->machine->has_flexible_gp_insn_p,
+                          mips_insn_has_flexible_gp_ref_p);
 }
 
 /* Return the register that should be used as the global pointer
@@ -8617,57 +8719,18 @@ mips_global_pointer (void)
   if (!TARGET_USE_GOT)
     return GLOBAL_POINTER_REGNUM;
 
-  /* We must always provide $gp when it is used implicitly.  */
-  if (!TARGET_EXPLICIT_RELOCS)
-    return GLOBAL_POINTER_REGNUM;
-
-  /* FUNCTION_PROFILER includes a jal macro, so we need to give it
-     a valid gp.  */
-  if (crtl->profile)
-    return GLOBAL_POINTER_REGNUM;
-
-  /* If the function has a nonlocal goto, $gp must hold the correct
-     global pointer for the target function.  */
-  if (crtl->has_nonlocal_goto)
+  /* If there are inflexible references to $gp, we must use the
+     standard register.  */
+  if (mips_cfun_has_inflexible_gp_ref_p ())
     return GLOBAL_POINTER_REGNUM;
 
-  /* There's no need to initialize $gp if it isn't referenced now,
-     and if we can be sure that no new references will be added during
-     or after reload.  */
-  if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
-      && !mips_function_has_gp_insn ())
-    {
-      /* The function doesn't use $gp at the moment.  If we're generating
-        -call_nonpic code, no new uses will be introduced during or after
-        reload.  */
-      if (TARGET_ABICALLS_PIC0)
-       return INVALID_REGNUM;
-
-      /* We need to handle the following implicit gp references:
-
-        - Reload can sometimes introduce constant pool references
-          into a function that otherwise didn't need them.  For example,
-          suppose we have an instruction like:
-
-              (set (reg:DF R1) (float:DF (reg:SI R2)))
-
-          If R2 turns out to be constant such as 1, the instruction may
-          have a REG_EQUAL note saying that R1 == 1.0.  Reload then has
-          the option of using this constant if R2 doesn't get allocated
-          to a register.
-
-          In cases like these, reload will have added the constant to the
-          pool but no instruction will yet refer to it.
+  /* If there are no current references to $gp, then the only uses
+     we can introduce later are those involved in long branches.  */
+  if (TARGET_ABSOLUTE_JUMPS && !mips_cfun_has_flexible_gp_ref_p ())
+    return INVALID_REGNUM;
 
-        - MIPS16 functions that return in FPRs need to call an
-          external libgcc routine.  */
-      if (!crtl->uses_const_pool
-         && !mips16_cfun_returns_in_fpr_p ())
-       return INVALID_REGNUM;
-    }
-
-  /* We need a global pointer, but perhaps we can use a call-clobbered
-     register instead of $gp.  */
+  /* If the global pointer is call-saved, try to use a call-clobbered
+     alternative.  */
   if (TARGET_CALL_SAVED_GP && current_function_is_leaf)
     for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
       if (!df_regs_ever_live_p (regno)
@@ -8679,6 +8742,119 @@ mips_global_pointer (void)
   return GLOBAL_POINTER_REGNUM;
 }
 
+/* Return true if current function's prologue must load the global
+   pointer value into pic_offset_table_rtx and store the same value in
+   the function's cprestore slot (if any).
+
+   One problem we have to deal with is that, when emitting GOT-based
+   position independent code, long-branch sequences will need to load
+   the address of the branch target from the GOT.  We don't know until
+   the very end of compilation whether (and where) the function needs
+   long branches, so we must ensure that _any_ branch can access the
+   global pointer in some form.  However, we do not want to pessimize
+   the usual case in which all branches are short.
+
+   We handle this as follows:
+
+   (1) During reload, we set cfun->machine->global_pointer to
+       INVALID_REGNUM if we _know_ that the current function
+       doesn't need a global pointer.  This is only valid if
+       long branches don't need the GOT.
+
+       Otherwise, we assume that we might need a global pointer
+       and pick an appropriate register.
+
+   (2) If cfun->machine->global_pointer != INVALID_REGNUM,
+       we ensure that the global pointer is available at every
+       block boundary bar entry and exit.  We do this in one of two ways:
+
+       - If the function has a cprestore slot, we ensure that this
+        slot is valid at every branch.  However, as explained in
+        point (6) below, there is no guarantee that pic_offset_table_rtx
+        itself is valid if new uses of the global pointer are introduced
+        after the first post-epilogue split.
+
+        We guarantee that the cprestore slot is valid by loading it
+        into a fake register, CPRESTORE_SLOT_REGNUM.  We then make
+        this register live at every block boundary bar function entry
+        and exit.  It is then invalid to move the load (and thus the
+        preceding store) across a block boundary.
+
+       - If the function has no cprestore slot, we guarantee that
+        pic_offset_table_rtx itself is valid at every branch.
+
+       See mips_eh_uses for the handling of the register liveness.
+
+   (3) During prologue and epilogue generation, we emit "ghost"
+       placeholder instructions to manipulate the global pointer.
+
+   (4) During prologue generation, we set cfun->machine->must_initialize_gp_p
+       and cfun->machine->must_restore_gp_when_clobbered_p if we already know
+       that the function needs a global pointer.  (There is no need to set
+       them earlier than this, and doing it as late as possible leads to
+       fewer false positives.)
+
+   (5) If cfun->machine->must_initialize_gp_p is true during a
+       split_insns pass, we split the ghost instructions into real
+       instructions.  These split instructions can then be optimized in
+       the usual way.  Otherwise, we keep the ghost instructions intact,
+       and optimize for the case where they aren't needed.  We still
+       have the option of splitting them later, if we need to introduce
+       new uses of the global pointer.
+
+       For example, the scheduler ignores a ghost instruction that
+       stores $28 to the stack, but it handles the split form of
+       the ghost instruction as an ordinary store.
+
+   (6) [OldABI only.]  If cfun->machine->must_restore_gp_when_clobbered_p
+       is true during the first post-epilogue split_insns pass, we split
+       calls and restore_gp patterns into instructions that explicitly
+       load pic_offset_table_rtx from the cprestore slot.  Otherwise,
+       we split these patterns into instructions that _don't_ load from
+       the cprestore slot.
+
+       If cfun->machine->must_restore_gp_when_clobbered_p is true at the
+       time of the split, then any instructions that exist at that time
+       can make free use of pic_offset_table_rtx.  However, if we want
+       to introduce new uses of the global pointer after the split,
+       we must explicitly load the value from the cprestore slot, since
+       pic_offset_table_rtx itself might not be valid at a given point
+       in the function.
+
+       The idea is that we want to be able to delete redundant
+       loads from the cprestore slot in the usual case where no
+       long branches are needed.
+
+   (7) If cfun->machine->must_initialize_gp_p is still false at the end
+       of md_reorg, we decide whether the global pointer is needed for
+       long branches.  If so, we set cfun->machine->must_initialize_gp_p
+       to true and split the ghost instructions into real instructions
+       at that stage.
+
+   Note that the ghost instructions must have a zero length for three reasons:
+
+   - Giving the length of the underlying $gp sequence might cause
+     us to use long branches in cases where they aren't really needed.
+
+   - They would perturb things like alignment calculations.
+
+   - More importantly, the hazard detection in md_reorg relies on
+     empty instructions having a zero length.
+
+   If we find a long branch and split the ghost instructions at the
+   end of md_reorg, the split could introduce more long branches.
+   That isn't a problem though, because we still do the split before
+   the final shorten_branches pass.
+
+   This is extremely ugly, but it seems like the best compromise between
+   correctness and efficiency.  */
+
+bool
+mips_must_initialize_gp_p (void)
+{
+  return cfun->machine->must_initialize_gp_p;
+}
+
 /* Return true if REGNO is a register that is ordinarily call-clobbered
    but must nevertheless be preserved by an interrupt handler.  */
 
@@ -9198,48 +9374,118 @@ mips_set_return_address (rtx address, rtx scratch)
   mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address);
 }
 
-/* Return a MEM rtx for the cprestore slot, using TEMP as a temporary base
-   register if need be.  */
+/* Return true if the current function has a cprestore slot.  */
 
-static rtx
-mips_cprestore_slot (rtx temp)
+bool
+mips_cfun_has_cprestore_slot_p (void)
+{
+  return (cfun->machine->global_pointer != INVALID_REGNUM
+         && cfun->machine->frame.cprestore_size > 0);
+}
+
+/* Fill *BASE and *OFFSET such that *BASE + *OFFSET refers to the
+   cprestore slot.  LOAD_P is true if the caller wants to load from
+   the cprestore slot; it is false if the caller wants to store to
+   the slot.  */
+
+static void
+mips_get_cprestore_base_and_offset (rtx *base, HOST_WIDE_INT *offset,
+                                   bool load_p)
 {
   const struct mips_frame_info *frame;
-  rtx base;
-  HOST_WIDE_INT offset;
 
   frame = &cfun->machine->frame;
-  if (frame_pointer_needed)
-    {
-      base = hard_frame_pointer_rtx;
-      offset = frame->args_size - frame->hard_frame_pointer_offset;
+  /* .cprestore always uses the stack pointer instead of the frame pointer.
+     We have a free choice for direct stores for non-MIPS16 functions,
+     and for MIPS16 functions whose cprestore slot is in range of the
+     stack pointer.  Using the stack pointer would sometimes give more
+     (early) scheduling freedom, but using the frame pointer would
+     sometimes give more (late) scheduling freedom.  It's hard to
+     predict which applies to a given function, so let's keep things
+     simple.
+
+     Loads must always use the frame pointer in functions that call
+     alloca, and there's little benefit to using the stack pointer
+     otherwise.  */
+  if (frame_pointer_needed && !(TARGET_CPRESTORE_DIRECTIVE && !load_p))
+    {
+      *base = hard_frame_pointer_rtx;
+      *offset = frame->args_size - frame->hard_frame_pointer_offset;
     }
   else
     {
-      base = stack_pointer_rtx;
-      offset = frame->args_size;
+      *base = stack_pointer_rtx;
+      *offset = frame->args_size;
     }
+}
+
+/* Return true if X is the load or store address of the cprestore slot;
+   LOAD_P says which.  */
+
+bool
+mips_cprestore_address_p (rtx x, bool load_p)
+{
+  rtx given_base, required_base;
+  HOST_WIDE_INT given_offset, required_offset;
+
+  mips_split_plus (x, &given_base, &given_offset);
+  mips_get_cprestore_base_and_offset (&required_base, &required_offset, load_p);
+  return given_base == required_base && given_offset == required_offset;
+}
+
+/* Return a MEM rtx for the cprestore slot.  LOAD_P is true if we are
+   going to load from it, false if we are going to store to it.
+   Use TEMP as a temporary register if need be.  */
+
+static rtx
+mips_cprestore_slot (rtx temp, bool load_p)
+{
+  rtx base;
+  HOST_WIDE_INT offset;
+
+  mips_get_cprestore_base_and_offset (&base, &offset, load_p);
   return gen_frame_mem (Pmode, mips_add_offset (temp, base, offset));
 }
 
+/* Emit instructions to save global pointer value GP into cprestore
+   slot MEM.  OFFSET is the offset that MEM applies to the base register.
+
+   MEM may not be a legitimate address.  If it isn't, TEMP is a
+   temporary register that can be used, otherwise it is a SCRATCH.  */
+
+void
+mips_save_gp_to_cprestore_slot (rtx mem, rtx offset, rtx gp, rtx temp)
+{
+  if (TARGET_CPRESTORE_DIRECTIVE)
+    {
+      gcc_assert (gp == pic_offset_table_rtx);
+      emit_insn (gen_cprestore (mem, offset));
+    }
+  else
+    mips_emit_move (mips_cprestore_slot (temp, false), gp);
+}
+
 /* Restore $gp from its save slot, using TEMP as a temporary base register
-   if need be.  This function is for o32 and o64 abicalls only.  */
+   if need be.  This function is for o32 and o64 abicalls only.
+
+   See mips_must_initialize_gp_p for details about how we manage the
+   global pointer.  */
 
 void
-mips_restore_gp (rtx temp)
+mips_restore_gp_from_cprestore_slot (rtx temp)
 {
-  gcc_assert (TARGET_ABICALLS && TARGET_OLDABI);
+  gcc_assert (TARGET_ABICALLS && TARGET_OLDABI && epilogue_completed);
 
-  if (cfun->machine->global_pointer == INVALID_REGNUM)
+  if (!cfun->machine->must_restore_gp_when_clobbered_p)
     return;
 
   if (TARGET_MIPS16)
     {
-      mips_emit_move (temp, mips_cprestore_slot (temp));
+      mips_emit_move (temp, mips_cprestore_slot (temp, true));
       mips_emit_move (pic_offset_table_rtx, temp);
     }
   else
-    mips_emit_move (pic_offset_table_rtx, mips_cprestore_slot (temp));
+    mips_emit_move (pic_offset_table_rtx, mips_cprestore_slot (temp, true));
   if (!TARGET_EXPLICIT_RELOCS)
     emit_insn (gen_blockage ());
 }
@@ -9327,6 +9573,89 @@ mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT sp_offset,
        offset -= GET_MODE_SIZE (fpr_mode);
       }
 }
+
+/* Return true if a move between register REGNO and its save slot (MEM)
+   can be done in a single move.  LOAD_P is true if we are loading
+   from the slot, false if we are storing to it.  */
+
+static bool
+mips_direct_save_slot_move_p (unsigned int regno, rtx mem, bool load_p)
+{
+  /* There is a specific MIPS16 instruction for saving $31 to the stack.  */
+  if (TARGET_MIPS16 && !load_p && regno == GP_REG_FIRST + 31)
+    return false;
+
+  return mips_secondary_reload_class (REGNO_REG_CLASS (regno),
+                                     GET_MODE (mem), mem, load_p) == NO_REGS;
+}
+
+/* Emit a move from SRC to DEST, given that one of them is a register
+   save slot and that the other is a register.  TEMP is a temporary
+   GPR of the same mode that is available if need be.  */
+
+void
+mips_emit_save_slot_move (rtx dest, rtx src, rtx temp)
+{
+  unsigned int regno;
+  rtx mem;
+
+  if (REG_P (src))
+    {
+      regno = REGNO (src);
+      mem = dest;
+    }
+  else
+    {
+      regno = REGNO (dest);
+      mem = src;
+    }
+
+  if (regno == cfun->machine->global_pointer && !mips_must_initialize_gp_p ())
+    {
+      /* We don't yet know whether we'll need this instruction or not.
+        Postpone the decision by emitting a ghost move.  This move
+        is specifically not frame-related; only the split version is.  */
+      if (TARGET_64BIT)
+       emit_insn (gen_move_gpdi (dest, src));
+      else
+       emit_insn (gen_move_gpsi (dest, src));
+      return;
+    }
+
+  if (regno == HI_REGNUM)
+    {
+      if (REG_P (dest))
+       {
+         mips_emit_move (temp, src);
+         if (TARGET_64BIT)
+           emit_insn (gen_mthisi_di (gen_rtx_REG (TImode, MD_REG_FIRST),
+                                     temp, gen_rtx_REG (DImode, LO_REGNUM)));
+         else
+           emit_insn (gen_mthisi_di (gen_rtx_REG (DImode, MD_REG_FIRST),
+                                     temp, gen_rtx_REG (SImode, LO_REGNUM)));
+       }
+      else
+       {
+         if (TARGET_64BIT)
+           emit_insn (gen_mfhidi_ti (temp,
+                                     gen_rtx_REG (TImode, MD_REG_FIRST)));
+         else
+           emit_insn (gen_mfhisi_di (temp,
+                                     gen_rtx_REG (DImode, MD_REG_FIRST)));
+         mips_emit_move (dest, temp);
+       }
+    }
+  else if (mips_direct_save_slot_move_p (regno, mem, mem == src))
+    mips_emit_move (dest, src);
+  else
+    {
+      gcc_assert (!reg_overlap_mentioned_p (dest, temp));
+      mips_emit_move (temp, src);
+      mips_emit_move (dest, temp);
+    }
+  if (MEM_P (dest))
+    mips_set_frame_expr (mips_frame_set (dest, src));
+}
 \f
 /* If we're generating n32 or n64 abicalls, and the current function
    does not use $28 as its global pointer, emit a cplocal directive.
@@ -9336,7 +9665,7 @@ static void
 mips_output_cplocal (void)
 {
   if (!TARGET_EXPLICIT_RELOCS
-      && cfun->machine->global_pointer != INVALID_REGNUM
+      && mips_must_initialize_gp_p ()
       && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
     output_asm_insn (".cplocal %+", 0);
 }
@@ -9408,7 +9737,8 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
   /* Handle the initialization of $gp for SVR4 PIC, if applicable.
      Also emit the ".set noreorder; .set nomacro" sequence for functions
      that need it.  */
-  if (mips_current_loadgp_style () == LOADGP_OLDABI)
+  if (mips_must_initialize_gp_p ()
+      && mips_current_loadgp_style () == LOADGP_OLDABI)
     {
       if (TARGET_MIPS16)
        {
@@ -9490,33 +9820,7 @@ mips_save_reg (rtx reg, rtx mem)
       mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
     }
   else
-    {
-      if (REGNO (reg) == HI_REGNUM)
-       {
-         if (TARGET_64BIT)
-           emit_insn (gen_mfhidi_ti (MIPS_PROLOGUE_TEMP (DImode),
-                                     gen_rtx_REG (TImode, MD_REG_FIRST)));
-         else
-           emit_insn (gen_mfhisi_di (MIPS_PROLOGUE_TEMP (SImode),
-                                     gen_rtx_REG (DImode, MD_REG_FIRST)));
-         mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
-       }
-      else if ((TARGET_MIPS16
-               && REGNO (reg) != GP_REG_FIRST + 31
-               && !M16_REG_P (REGNO (reg)))
-              || ACC_REG_P (REGNO (reg)))
-       {
-         /* If the register has no direct store instruction, move it
-            through a temporary.  Note that there's a special MIPS16
-            instruction to save $31.  */
-         mips_emit_move (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
-         mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
-       }
-      else
-       mips_emit_move (mem, reg);
-
-      mips_set_frame_expr (mips_frame_set (mem, reg));
-    }
+    mips_emit_save_slot_move (mem, reg, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
 }
 
 /* The __gnu_local_gp symbol.  */
@@ -9599,7 +9903,19 @@ mips_expand_prologue (void)
   rtx insn;
 
   if (cfun->machine->global_pointer != INVALID_REGNUM)
-    SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+    {
+      /* Check whether an insn uses pic_offset_table_rtx, either explicitly
+        or implicitly.  If so, we can commit to using a global pointer
+        straight away, otherwise we need to defer the decision.  */
+      if (mips_cfun_has_inflexible_gp_ref_p ()
+         || mips_cfun_has_flexible_gp_ref_p ())
+       {
+         cfun->machine->must_initialize_gp_p = true;
+         cfun->machine->must_restore_gp_when_clobbered_p = true;
+       }
+
+      SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+    }
 
   frame = &cfun->machine->frame;
   size = frame->total_size;
@@ -9801,17 +10117,22 @@ mips_expand_prologue (void)
   mips_emit_loadgp ();
 
   /* Initialize the $gp save slot.  */
-  if (frame->cprestore_size > 0
-      && cfun->machine->global_pointer != INVALID_REGNUM)
+  if (mips_cfun_has_cprestore_slot_p ())
     {
-      if (TARGET_MIPS16)
-       mips_emit_move (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
-                       MIPS16_PIC_TEMP);
-      else if (TARGET_ABICALLS_PIC2)
-       emit_insn (gen_cprestore (GEN_INT (frame->args_size)));
-      else
-       emit_move_insn (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
-                       pic_offset_table_rtx);
+      rtx base, mem, gp, temp;
+      HOST_WIDE_INT offset;
+
+      mips_get_cprestore_base_and_offset (&base, &offset, false);
+      mem = gen_frame_mem (Pmode, plus_constant (base, offset));
+      gp = TARGET_MIPS16 ? MIPS16_PIC_TEMP : pic_offset_table_rtx;
+      temp = (SMALL_OPERAND (offset)
+             ? gen_rtx_SCRATCH (Pmode)
+             : MIPS_PROLOGUE_TEMP (Pmode));
+      emit_insn (gen_potential_cprestore (mem, GEN_INT (offset), gp, temp));
+
+      mips_get_cprestore_base_and_offset (&base, &offset, true);
+      mem = gen_frame_mem (Pmode, plus_constant (base, offset));
+      emit_insn (gen_use_cprestore (mem));
     }
 
   /* We need to search back to the last use of K0 or K1.  */
@@ -9844,27 +10165,7 @@ mips_restore_reg (rtx reg, rtx mem)
   if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
     reg = gen_rtx_REG (GET_MODE (reg), GP_REG_FIRST + 7);
 
-  if (REGNO (reg) == HI_REGNUM)
-    {
-      mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
-      if (TARGET_64BIT)
-       emit_insn (gen_mthisi_di (gen_rtx_REG (TImode, MD_REG_FIRST),
-                                 MIPS_EPILOGUE_TEMP (DImode),
-                                 gen_rtx_REG (DImode, LO_REGNUM)));
-      else
-       emit_insn (gen_mthisi_di (gen_rtx_REG (DImode, MD_REG_FIRST),
-                                 MIPS_EPILOGUE_TEMP (SImode),
-                                 gen_rtx_REG (SImode, LO_REGNUM)));
-    }
-  else if ((TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
-          || ACC_REG_P (REGNO (reg)))
-    {
-      /* Can't restore directly; move through a temporary.  */
-      mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
-      mips_emit_move (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
-    }
-  else
-    mips_emit_move (reg, mem);
+  mips_emit_save_slot_move (reg, mem, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
 }
 
 /* Emit any instructions needed before a return.  */
@@ -10732,12 +11033,112 @@ mips_init_libfuncs (void)
     synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
 }
 
+/* Build up a multi-insn sequence that loads label TARGET into $AT.  */
+
+static void
+mips_process_load_label (rtx target)
+{
+  rtx base, gp, intop;
+  HOST_WIDE_INT offset;
+
+  mips_multi_start ();
+  switch (mips_abi)
+    {
+    case ABI_N32:
+      mips_multi_add_insn ("lw\t%@,%%got_page(%0)(%+)", target, 0);
+      mips_multi_add_insn ("addiu\t%@,%@,%%got_ofst(%0)", target, 0);
+      break;
+
+    case ABI_64:
+      mips_multi_add_insn ("ld\t%@,%%got_page(%0)(%+)", target, 0);
+      mips_multi_add_insn ("daddiu\t%@,%@,%%got_ofst(%0)", target, 0);
+      break;
+
+    default:
+      gp = pic_offset_table_rtx;
+      if (mips_cfun_has_cprestore_slot_p ())
+       {
+         gp = gen_rtx_REG (Pmode, AT_REGNUM);
+         mips_get_cprestore_base_and_offset (&base, &offset, true);
+         if (!SMALL_OPERAND (offset))
+           {
+             intop = GEN_INT (CONST_HIGH_PART (offset));
+             mips_multi_add_insn ("lui\t%0,%1", gp, intop, 0);
+             mips_multi_add_insn ("addu\t%0,%0,%1", gp, base, 0);
+
+             base = gp;
+             offset = CONST_LOW_PART (offset);
+           }
+         intop = GEN_INT (offset);
+         if (ISA_HAS_LOAD_DELAY)
+           mips_multi_add_insn ("lw\t%0,%1(%2)%#", gp, intop, base, 0);
+         else
+           mips_multi_add_insn ("lw\t%0,%1(%2)", gp, intop, base, 0);
+       }
+      if (ISA_HAS_LOAD_DELAY)
+       mips_multi_add_insn ("lw\t%@,%%got(%0)(%1)%#", target, gp, 0);
+      else
+       mips_multi_add_insn ("lw\t%@,%%got(%0)(%1)", target, gp, 0);
+      mips_multi_add_insn ("addiu\t%@,%@,%%lo(%0)", target, 0);
+      break;
+    }
+}
+
+/* Return the number of instructions needed to load a label into $AT.  */
+
+static unsigned int
+mips_load_label_length (void)
+{
+  if (cfun->machine->load_label_length == 0)
+    {
+      mips_process_load_label (pc_rtx);
+      cfun->machine->load_label_length = mips_multi_num_insns;
+    }
+  return cfun->machine->load_label_length;
+}
+
+/* Emit an asm sequence to start a noat block and load the address
+   of a label into $1.  */
+
+void
+mips_output_load_label (rtx target)
+{
+  mips_push_asm_switch (&mips_noat);
+  if (TARGET_EXPLICIT_RELOCS)
+    {
+      mips_process_load_label (target);
+      mips_multi_write ();
+    }
+  else
+    {
+      if (Pmode == DImode)
+       output_asm_insn ("dla\t%@,%0", &target);
+      else
+       output_asm_insn ("la\t%@,%0", &target);
+    }
+}
+
 /* Return the length of INSN.  LENGTH is the initial length computed by
    attributes in the machine-description file.  */
 
 int
 mips_adjust_insn_length (rtx insn, int length)
 {
+  /* mips.md uses MAX_PIC_BRANCH_LENGTH as a placeholder for the length
+     of a PIC long-branch sequence.  Substitute the correct value.  */
+  if (length == MAX_PIC_BRANCH_LENGTH
+      && INSN_CODE (insn) >= 0
+      && get_attr_type (insn) == TYPE_BRANCH)
+    {
+      /* Add the branch-over instruction and its delay slot, if this
+        is a conditional branch.  */
+      length = simplejump_p (insn) ? 0 : 8;
+
+      /* Load the label into $AT and jump to it.  Ignore the delay
+        slot of the jump.  */
+      length += mips_load_label_length () + 4;
+    }
+
   /* A unconditional jump has an unfilled delay slot if it is not part
      of a sequence.  A conditional jump normally has a delay slot, but
      does not on MIPS16.  */
@@ -10769,38 +11170,9 @@ mips_adjust_insn_length (rtx insn, int length)
   return length;
 }
 
-/* Return an asm sequence to start a noat block and load the address
-   of a label into $1.  */
-
-const char *
-mips_output_load_label (void)
-{
-  if (TARGET_EXPLICIT_RELOCS)
-    switch (mips_abi)
-      {
-      case ABI_N32:
-       return "%[lw\t%@,%%got_page(%0)(%+)\n\taddiu\t%@,%@,%%got_ofst(%0)";
-
-      case ABI_64:
-       return "%[ld\t%@,%%got_page(%0)(%+)\n\tdaddiu\t%@,%@,%%got_ofst(%0)";
-
-      default:
-       if (ISA_HAS_LOAD_DELAY)
-         return "%[lw\t%@,%%got(%0)(%+)%#\n\taddiu\t%@,%@,%%lo(%0)";
-       return "%[lw\t%@,%%got(%0)(%+)\n\taddiu\t%@,%@,%%lo(%0)";
-      }
-  else
-    {
-      if (Pmode == DImode)
-       return "%[dla\t%@,%0";
-      else
-       return "%[la\t%@,%0";
-    }
-}
-
 /* Return the assembly code for INSN, which has the operands given by
-   OPERANDS, and which branches to OPERANDS[1] if some condition is true.
-   BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[1]
+   OPERANDS, and which branches to OPERANDS[0] if some condition is true.
+   BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[0]
    is in range of a direct branch.  BRANCH_IF_FALSE is an inverted
    version of BRANCH_IF_TRUE.  */
 
@@ -10812,7 +11184,7 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
   unsigned int length;
   rtx taken, not_taken;
 
-  gcc_assert (LABEL_P (operands[1]));  
+  gcc_assert (LABEL_P (operands[0]));
 
   length = get_attr_length (insn);
   if (length <= 8)
@@ -10826,10 +11198,10 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
      not use branch-likely instructions.  */
   mips_branch_likely = false;
   not_taken = gen_label_rtx ();
-  taken = operands[1];
+  taken = operands[0];
 
   /* Generate the reversed branch to NOT_TAKEN.  */
-  operands[1] = not_taken;
+  operands[0] = not_taken;
   output_asm_insn (branch_if_false, operands);
 
   /* If INSN has a delay slot, we must provide delay slots for both the
@@ -10851,11 +11223,11 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
     }
 
   /* Output the unconditional branch to TAKEN.  */
-  if (length <= 16)
-    output_asm_insn ("j\t%0%/", &taken);
+  if (TARGET_ABSOLUTE_JUMPS)
+    output_asm_insn (MIPS_ABSOLUTE_JUMP ("j\t%0%/"), &taken);
   else
     {
-      output_asm_insn (mips_output_load_label (), &taken);
+      mips_output_load_label (taken);
       output_asm_insn ("jr\t%@%]%/", 0);
     }
 
@@ -10881,10 +11253,10 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
   return "";
 }
 
-/* Return the assembly code for INSN, which branches to OPERANDS[1]
+/* Return the assembly code for INSN, which branches to OPERANDS[0]
    if some ordering condition is true.  The condition is given by
-   OPERANDS[0] if !INVERTED_P, otherwise it is the inverse of
-   OPERANDS[0].  OPERANDS[2] is the comparison's first operand;
+   OPERANDS[1] if !INVERTED_P, otherwise it is the inverse of
+   OPERANDS[1].  OPERANDS[2] is the comparison's first operand;
    its second is always zero.  */
 
 const char *
@@ -10892,17 +11264,17 @@ mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
 {
   const char *branch[2];
 
-  /* Make BRANCH[1] branch to OPERANDS[1] when the condition is true.
+  /* Make BRANCH[1] branch to OPERANDS[0] when the condition is true.
      Make BRANCH[0] branch on the inverse condition.  */
-  switch (GET_CODE (operands[0]))
+  switch (GET_CODE (operands[1]))
     {
       /* These cases are equivalent to comparisons against zero.  */
     case LEU:
       inverted_p = !inverted_p;
       /* Fall through.  */
     case GTU:
-      branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%1");
-      branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%1");
+      branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%0");
+      branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%0");
       break;
 
       /* These cases are always true or always false.  */
@@ -10910,13 +11282,13 @@ mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
       inverted_p = !inverted_p;
       /* Fall through.  */
     case GEU:
-      branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%1");
-      branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%1");
+      branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%0");
+      branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%0");
       break;
 
     default:
-      branch[!inverted_p] = MIPS_BRANCH ("b%C0z", "%2,%1");
-      branch[inverted_p] = MIPS_BRANCH ("b%N0z", "%2,%1");
+      branch[!inverted_p] = MIPS_BRANCH ("b%C1z", "%2,%0");
+      branch[inverted_p] = MIPS_BRANCH ("b%N1z", "%2,%0");
       break;
     }
   return mips_output_conditional_branch (insn, operands, branch[1], branch[0]);
@@ -11915,7 +12287,8 @@ mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
   /* Ignore USEs and CLOBBERs; don't count them against the issue rate.  */
   if (USEFUL_INSN_P (insn))
     {
-      more--;
+      if (get_attr_type (insn) != TYPE_GHOST)
+       more--;
       if (!reload_completed && TUNE_MACC_CHAINS)
        mips_macc_chains_record (insn);
       vr4130_last_insn = insn;
@@ -14173,6 +14546,46 @@ mips_reorg_process_insns (void)
   htab_delete (htab);
 }
 
+/* If we are using a GOT, but have not decided to use a global pointer yet,
+   see whether we need one to implement long branches.  Convert the ghost
+   global-pointer instructions into real ones if so.  */
+
+static bool
+mips_expand_ghost_gp_insns (void)
+{
+  rtx insn;
+  int normal_length;
+
+  /* Quick exit if we already know that we will or won't need a
+     global pointer.  */
+  if (!TARGET_USE_GOT
+      || cfun->machine->global_pointer == INVALID_REGNUM
+      || mips_must_initialize_gp_p ())
+    return false;
+
+  shorten_branches (get_insns ());
+
+  /* Look for a branch that is longer than normal.  The normal length for
+     non-MIPS16 branches is 8, because the length includes the delay slot.
+     It is 4 for MIPS16, because MIPS16 branches are extended instructions,
+     but they have no delay slot.  */
+  normal_length = (TARGET_MIPS16 ? 4 : 8);
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    if (JUMP_P (insn)
+       && USEFUL_INSN_P (insn)
+       && get_attr_length (insn) > normal_length)
+      break;
+
+  if (insn == NULL_RTX)
+    return false;
+
+  /* We've now established that we need $gp.  */
+  cfun->machine->must_initialize_gp_p = true;
+  split_all_insns_noflow ();
+
+  return true;
+}
+
 /* Implement TARGET_MACHINE_DEPENDENT_REORG.  */
 
 static void
@@ -14189,6 +14602,10 @@ mips_reorg (void)
       && TUNE_MIPS4130
       && TARGET_VR4130_ALIGN)
     vr4130_align_insns ();
+  if (mips_expand_ghost_gp_insns ())
+    /* The expansion could invalidate some of the VR4130 alignment
+       optimizations, but this should be an extremely rare case anyhow.  */
+    mips_reorg_process_insns ();
 }
 \f
 /* Implement TARGET_ASM_OUTPUT_MI_THUNK.  Generate rtl rather than asm text
@@ -14222,6 +14639,7 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
         TARGET_CALL_SAVED_GP.  */
       cfun->machine->global_pointer
        = TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+      cfun->machine->must_initialize_gp_p = true;
       SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
 
       /* Set up the global pointer for n32 or n64 abicalls.  */
@@ -15137,6 +15555,31 @@ mips_order_regs_for_local_alloc (void)
     }
 }
 
+/* Implement EH_USES.  */
+
+bool
+mips_eh_uses (unsigned int regno)
+{
+  if (reload_completed && !TARGET_ABSOLUTE_JUMPS)
+    {
+      /* We need to force certain registers to be live in order to handle
+        PIC long branches correctly.  See mips_must_initialize_gp_p for
+        details.  */
+      if (mips_cfun_has_cprestore_slot_p ())
+       {
+         if (regno == CPRESTORE_SLOT_REGNUM)
+           return true;
+       }
+      else
+       {
+         if (cfun->machine->global_pointer == regno)
+           return true;
+       }
+    }
+
+  return false;
+}
+
 /* Implement EPILOGUE_USES.  */
 
 bool
index 352dbd25618b4a59745eb820f5b3d35ebf9a07ff..eda74479ed24e9807f45088d63c9f0dfd91f53a7 100644 (file)
@@ -161,10 +161,13 @@ enum mips_code_readable_setting {
 
 /* True if the call patterns should be split into a jalr followed by
    an instruction to restore $gp.  It is only safe to split the load
-   from the call when every use of $gp is explicit.  */
+   from the call when every use of $gp is explicit.
+
+   See mips_must_initialize_gp_p for details about how we manage the
+   global pointer.  */
 
 #define TARGET_SPLIT_CALLS \
-  (TARGET_EXPLICIT_RELOCS && TARGET_CALL_CLOBBERED_GP)
+  (TARGET_EXPLICIT_RELOCS && TARGET_CALL_CLOBBERED_GP && epilogue_completed)
 
 /* True if we're generating a form of -mabicalls in which we can use
    operators like %hi and %lo to refer to locally-binding symbols.
@@ -202,6 +205,17 @@ enum mips_code_readable_setting {
 /* True if TARGET_USE_GOT and if $gp is a call-saved register.  */
 #define TARGET_CALL_SAVED_GP (TARGET_USE_GOT && !TARGET_CALL_CLOBBERED_GP)
 
+/* True if we should use .cprestore to store to the cprestore slot.
+
+   We continue to use .cprestore for explicit-reloc code so that JALs
+   inside inline asms will work correctly.  */
+#define TARGET_CPRESTORE_DIRECTIVE \
+  (TARGET_ABICALLS_PIC2 && !TARGET_MIPS16)
+
+/* True if we can use the J and JAL instructions.  */
+#define TARGET_ABSOLUTE_JUMPS \
+  (!flag_pic || TARGET_ABSOLUTE_ABICALLS)
+
 /* True if indirect calls must use register class PIC_FN_ADDR_REG.
    This is true for both the PIC and non-PIC VxWorks RTP modes.  */
 #define TARGET_USE_PIC_FN_ADDR_REG (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
@@ -1300,6 +1314,8 @@ enum mips_code_readable_setting {
 
 #define EH_RETURN_STACKADJ_RTX  gen_rtx_REG (Pmode, GP_REG_FIRST + 3)
 
+#define EH_USES(N) mips_eh_uses (N)
+
 /* Offsets recorded in opcodes are a multiple of this alignment factor.
    The default for this in 64-bit mode is 8, which causes problems with
    SFmode register saves.  */
@@ -1543,11 +1559,12 @@ enum mips_code_readable_setting {
    - 8 condition code registers
    - 2 accumulator registers (hi and lo)
    - 32 registers each for coprocessors 0, 2 and 3
-   - 3 fake registers:
+   - 4 fake registers:
        - ARG_POINTER_REGNUM
        - FRAME_POINTER_REGNUM
        - GOT_VERSION_REGNUM (see the comment above load_call<mode> for details)
-   - 3 dummy entries that were used at various times in the past.
+       - CPRESTORE_SLOT_REGNUM
+   - 2 dummy entries that were used at various times in the past.
    - 6 DSP accumulator registers (3 hi-lo pairs) for MIPS DSP ASE
    - 6 DSP control registers  */
 
@@ -2661,6 +2678,13 @@ typedef struct mips_args {
 #define MIPS_BRANCH(OPCODE, OPERANDS) \
   "%*" OPCODE "%?\t" OPERANDS "%/"
 
+/* Return an asm string that forces INSN to be treated as an absolute
+   J or JAL instruction instead of an assembler macro.  */
+#define MIPS_ABSOLUTE_JUMP(INSN) \
+  (TARGET_ABICALLS_PIC2                                                \
+   ? ".option\tpic0\n\t" INSN "\n\t.option\tpic2"              \
+   : INSN)
+
 /* Return the asm template for a call.  INSN is the instruction's mnemonic
    ("j" or "jal"), OPERANDS are its operands, and OPNO is the operand number
    of the target.
@@ -2675,11 +2699,7 @@ typedef struct mips_args {
    ? "%*" INSN "\t%" #OPNO "%/"                                        \
    : REG_P (OPERANDS[OPNO])                                    \
    ? "%*" INSN "r\t%" #OPNO "%/"                               \
-   : TARGET_ABICALLS_PIC2                                      \
-   ? (".option\tpic0\n\t"                                      \
-      "%*" INSN "\t%" #OPNO "%/\n\t"                           \
-      ".option\tpic2")                                         \
-   : "%*" INSN "\t%" #OPNO "%/")
+   : MIPS_ABSOLUTE_JUMP ("%*" INSN "\t%" #OPNO "%/"))
 \f
 /* Control the assembler format that we output.  */
 
@@ -2707,7 +2727,7 @@ typedef struct mips_args {
   "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",         \
   "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",         \
   "hi",   "lo",   "",     "$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",        \
-  "$fcc5","$fcc6","$fcc7","", "", "$arg", "$frame", "$fakec",             \
+  "$fcc5","$fcc6","$fcc7","", "$cprestore", "$arg", "$frame", "$fakec",           \
   "$c0r0", "$c0r1", "$c0r2", "$c0r3", "$c0r4", "$c0r5", "$c0r6", "$c0r7",  \
   "$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15", \
   "$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23", \
index a510e0a7b7445034cf5419bbe7fc65cd85c24d37..4e1e7852c71038716a784a1751a954805e9f37a7 100644 (file)
    (UNSPEC_STORE_WORD           2)
    (UNSPEC_GET_FNADDR           3)
    (UNSPEC_BLOCKAGE             4)
-   (UNSPEC_CPRESTORE            5)
-   (UNSPEC_RESTORE_GP           6)
-   (UNSPEC_EH_RETURN            7)
-   (UNSPEC_CONSTTABLE_INT       8)
-   (UNSPEC_CONSTTABLE_FLOAT     9)
+   (UNSPEC_POTENTIAL_CPRESTORE  5)
+   (UNSPEC_CPRESTORE            6)
+   (UNSPEC_RESTORE_GP           7)
+   (UNSPEC_MOVE_GP              8)
+   (UNSPEC_EH_RETURN            9)
+   (UNSPEC_CONSTTABLE_INT      10)
+   (UNSPEC_CONSTTABLE_FLOAT    11)
    (UNSPEC_ALIGN               14)
    (UNSPEC_HIGH                        17)
    (UNSPEC_LOAD_LEFT           18)
@@ -77,6 +79,7 @@
    (UNSPEC_ADDRESS_FIRST       100)
 
    (TLS_GET_TP_REGNUM          3)
+   (CPRESTORE_SLOT_REGNUM      76)
    (GOT_VERSION_REGNUM         79)
 
    ;; For MIPS Paired-Singled Floating Point Instructions.
 
    (UNSPEC_MIPS_CACHE          600)
    (UNSPEC_R10K_CACHE_BARRIER  601)
+
+   ;; PIC long branch sequences are never longer than 100 bytes.
+   (MAX_PIC_BRANCH_LENGTH      100)
   ]
 )
 
 ;;
 ;; jal is always a macro for TARGET_CALL_CLOBBERED_GP because it includes
 ;; an instruction to restore $gp.  Direct jals are also macros for
-;; flag_pic && !TARGET_ABSOLUTE_ABICALLS because they first load
-;; the target address into a register.
+;; !TARGET_ABSOLUTE_JUMPS because they first load the target address
+;; into a register.
 (define_attr "jal_macro" "no,yes"
   (cond [(eq_attr "jal" "direct")
-        (symbol_ref "((TARGET_CALL_CLOBBERED_GP
-                       || (flag_pic && !TARGET_ABSOLUTE_ABICALLS))
+        (symbol_ref "(TARGET_CALL_CLOBBERED_GP || !TARGET_ABSOLUTE_JUMPS
                       ? JAL_MACRO_YES : JAL_MACRO_NO)")
         (eq_attr "jal" "indirect")
         (symbol_ref "(TARGET_CALL_CLOBBERED_GP
               (ne (symbol_ref "TARGET_MIPS16") (const_int 0)))
          (const_int 8)
 
-         ;; Direct branch instructions have a range of [-0x40000,0x3fffc].
-         ;; If a branch is outside this range, we have a choice of two
-         ;; sequences.  For PIC, an out-of-range branch like:
+         ;; 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:
          ;;
          ;;    bne     r1,r2,target
          ;;    dslot
          ;;    nop
          ;; 1:
          ;;
-         ;; where the load address can be up to three instructions long
-         ;; (lw, nop, addiu).
-         ;;
          ;; The non-PIC case is similar except that we use a direct
          ;; jump instead of an la/jr pair.  Since the target of this
          ;; jump is an absolute 28-bit bit address (the other bits
          ;; will add the length of the implicit nop.  The values for
          ;; forward and backward branches will be different as well.
          (eq_attr "type" "branch")
-         (cond [(and (le (minus (match_dup 1) (pc)) (const_int 131064))
-                      (le (minus (pc) (match_dup 1)) (const_int 131068)))
-                  (const_int 4)
-                (ne (symbol_ref "flag_pic") (const_int 0))
-                (const_int 24)
-                ] (const_int 12))
+         (cond [(and (le (minus (match_dup 0) (pc)) (const_int 131064))
+                         (le (minus (pc) (match_dup 0)) (const_int 131068)))
+                  (const_int 4)
+
+                ;; The non-PIC case: branch, first delay slot, and J.
+                (ne (symbol_ref "TARGET_ABSOLUTE_JUMPS") (const_int 0))
+                  (const_int 12)]
+
+                ;; Use MAX_PIC_BRANCH_LENGTH as a (gross) overestimate.
+                ;; mips_adjust_insn_length substitutes the correct length.
+                ;;
+                ;; Note that we can't simply use (symbol_ref ...) here
+                ;; because genattrtab needs to know the maximum length
+                ;; of an insn.
+                (const_int MAX_PIC_BRANCH_LENGTH))
 
          ;; "Ghost" instructions occupy no space.
          (eq_attr "type" "ghost")
 ;; function address.
 (define_insn_and_split "loadgp_newabi_<mode>"
   [(set (match_operand:P 0 "register_operand" "=d")
-       (unspec_volatile:P [(match_operand:P 1)
-                           (match_operand:P 2 "register_operand" "d")]
-                          UNSPEC_LOADGP))]
+       (unspec:P [(match_operand:P 1)
+                  (match_operand:P 2 "register_operand" "d")]
+                 UNSPEC_LOADGP))]
   "mips_current_loadgp_style () == LOADGP_NEWABI"
-  "#"
-  ""
+  { return mips_must_initialize_gp_p () ? "#" : ""; }
+  "&& mips_must_initialize_gp_p ()"
   [(set (match_dup 0) (match_dup 3))
    (set (match_dup 0) (match_dup 4))
    (set (match_dup 0) (match_dup 5))]
   operands[4] = gen_rtx_PLUS (Pmode, operands[0], operands[2]);
   operands[5] = gen_rtx_LO_SUM (Pmode, operands[0], operands[1]);
 }
-  [(set_attr "length" "12")])
+  [(set_attr "type" "ghost")])
 
 ;; Likewise, for -mno-shared code.  Operand 0 is the __gnu_local_gp symbol.
 (define_insn_and_split "loadgp_absolute_<mode>"
   [(set (match_operand:P 0 "register_operand" "=d")
-       (unspec_volatile:P [(match_operand:P 1)] UNSPEC_LOADGP))]
+       (unspec:P [(match_operand:P 1)] UNSPEC_LOADGP))]
   "mips_current_loadgp_style () == LOADGP_ABSOLUTE"
-  "#"
-  ""
+  { return mips_must_initialize_gp_p () ? "#" : ""; }
+  "&& mips_must_initialize_gp_p ()"
   [(const_int 0)]
 {
   mips_emit_move (operands[0], operands[1]);
   DONE;
 }
-  [(set_attr "length" "8")])
+  [(set_attr "type" "ghost")])
 
 ;; This blockage instruction prevents the gp load from being
 ;; scheduled after an implicit use of gp.  It also prevents
   [(unspec_volatile [(reg:SI 28)] UNSPEC_BLOCKAGE)]
   ""
   ""
-  [(set_attr "type" "ghost")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "ghost")])
 
 ;; Initialize $gp for RTP PIC.  Operand 0 is the __GOTT_BASE__ symbol
 ;; and operand 1 is the __GOTT_INDEX__ symbol.
 (define_insn_and_split "loadgp_rtp_<mode>"
   [(set (match_operand:P 0 "register_operand" "=d")
-       (unspec_volatile:P [(match_operand:P 1 "symbol_ref_operand")
-                           (match_operand:P 2 "symbol_ref_operand")]
-                          UNSPEC_LOADGP))]
+       (unspec:P [(match_operand:P 1 "symbol_ref_operand")
+                  (match_operand:P 2 "symbol_ref_operand")]
+                 UNSPEC_LOADGP))]
   "mips_current_loadgp_style () == LOADGP_RTP"
-  "#"
-  ""
+  { return mips_must_initialize_gp_p () ? "#" : ""; }
+  "&& mips_must_initialize_gp_p ()"
   [(set (match_dup 0) (high:P (match_dup 3)))
    (set (match_dup 0) (unspec:P [(match_dup 0)
                                 (match_dup 3)] UNSPEC_LOAD_GOT))
   operands[3] = mips_unspec_address (operands[1], SYMBOL_ABSOLUTE);
   operands[4] = mips_unspec_address (operands[2], SYMBOL_HALF);
 }
-  [(set_attr "length" "12")])
+  [(set_attr "type" "ghost")])
 
 ;; Initialize the global pointer for MIPS16 code.  Operand 0 is the
 ;; global pointer and operand 1 is the MIPS16 register that holds
 ;; the required value.
 (define_insn_and_split "copygp_mips16"
   [(set (match_operand:SI 0 "register_operand" "=y")
-       (unspec_volatile:SI [(match_operand:SI 1 "register_operand" "d")]
-                           UNSPEC_COPYGP))]
+       (unspec:SI [(match_operand:SI 1 "register_operand" "d")]
+                  UNSPEC_COPYGP))]
   "TARGET_MIPS16"
-  "#"
-  "&& reload_completed"
-  [(set (match_dup 0) (match_dup 1))])
+  { return mips_must_initialize_gp_p () ? "#" : ""; }
+  "&& mips_must_initialize_gp_p ()"
+  [(set (match_dup 0) (match_dup 1))]
+  ""
+  [(set_attr "type" "ghost")])
+
+;; A placeholder for where the cprestore instruction should go,
+;; if we decide we need one.  Operand 0 and operand 1 are as for
+;; "cprestore".  Operand 2 is a register that holds the gp value.
+;;
+;; The "cprestore" pattern requires operand 2 to be pic_offset_table_rtx,
+;; otherwise any register that holds the correct value will do.
+(define_insn_and_split "potential_cprestore"
+  [(set (match_operand:SI 0 "cprestore_save_slot_operand" "=X,X")
+       (unspec:SI [(match_operand:SI 1 "const_int_operand" "I,i")
+                   (match_operand:SI 2 "register_operand" "d,d")]
+                  UNSPEC_POTENTIAL_CPRESTORE))
+   (clobber (match_operand:SI 3 "scratch_operand" "=X,&d"))]
+  "!TARGET_CPRESTORE_DIRECTIVE || operands[2] == pic_offset_table_rtx"
+  { return mips_must_initialize_gp_p () ? "#" : ""; }
+  "mips_must_initialize_gp_p ()"
+  [(const_int 0)]
+{
+  mips_save_gp_to_cprestore_slot (operands[0], operands[1],
+                                 operands[2], operands[3]);
+  DONE;
+}
+  [(set_attr "type" "ghost")])
 
 ;; Emit a .cprestore directive, which normally expands to a single store
-;; instruction.  Note that we continue to use .cprestore for explicit reloc
-;; code so that jals inside inline asms will work correctly.
+;; instruction.  Operand 0 is a (possibly illegitimate) sp-based MEM
+;; for the cprestore slot.  Operand 1 is the offset of the slot from
+;; the stack pointer.  (This is redundant with operand 0, but it makes
+;; things a little simpler.)
 (define_insn "cprestore"
-  [(unspec_volatile [(match_operand 0 "const_int_operand" "I,i")
-                     (use (reg:SI 28))]
-                   UNSPEC_CPRESTORE)]
-  ""
+  [(set (match_operand:SI 0 "cprestore_save_slot_operand" "=X,X")
+       (unspec:SI [(match_operand:SI 1 "const_int_operand" "I,i")
+                   (reg:SI 28)]
+                  UNSPEC_CPRESTORE))]
+  "TARGET_CPRESTORE_DIRECTIVE"
 {
   if (mips_nomacro.nesting_level > 0 && which_alternative == 1)
-    return ".set\tmacro\;.cprestore\t%0\;.set\tnomacro";
+    return ".set\tmacro\;.cprestore\t%1\;.set\tnomacro";
   else
-    return ".cprestore\t%0";
+    return ".cprestore\t%1";
 }
   [(set_attr "type" "store")
    (set_attr "length" "4,12")])
 
+(define_insn "use_cprestore"
+  [(set (reg:SI CPRESTORE_SLOT_REGNUM)
+       (match_operand:SI 0 "cprestore_load_slot_operand"))]
+  ""
+  ""
+  [(set_attr "type" "ghost")])
+
 ;; Expand in-line code to clear the instruction cache between operand[0] and
 ;; operand[1].
 (define_expand "clear_cache"
 (define_insn "*branch_fp"
   [(set (pc)
         (if_then_else
-         (match_operator 0 "equality_operator"
+         (match_operator 1 "equality_operator"
                          [(match_operand:CC 2 "register_operand" "z")
                          (const_int 0)])
-         (label_ref (match_operand 1 "" ""))
+         (label_ref (match_operand 0 "" ""))
          (pc)))]
   "TARGET_HARD_FLOAT"
 {
   return mips_output_conditional_branch (insn, operands,
-                                        MIPS_BRANCH ("b%F0", "%Z2%1"),
-                                        MIPS_BRANCH ("b%W0", "%Z2%1"));
+                                        MIPS_BRANCH ("b%F1", "%Z2%0"),
+                                        MIPS_BRANCH ("b%W1", "%Z2%0"));
 }
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 (define_insn "*branch_fp_inverted"
   [(set (pc)
         (if_then_else
-         (match_operator 0 "equality_operator"
+         (match_operator 1 "equality_operator"
                          [(match_operand:CC 2 "register_operand" "z")
                          (const_int 0)])
          (pc)
-         (label_ref (match_operand 1 "" ""))))]
+         (label_ref (match_operand 0 "" ""))))]
   "TARGET_HARD_FLOAT"
 {
   return mips_output_conditional_branch (insn, operands,
-                                        MIPS_BRANCH ("b%W0", "%Z2%1"),
-                                        MIPS_BRANCH ("b%F0", "%Z2%1"));
+                                        MIPS_BRANCH ("b%W1", "%Z2%0"),
+                                        MIPS_BRANCH ("b%F1", "%Z2%0"));
 }
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 ;; Conditional branches on ordered comparisons with zero.
 
 (define_insn "*branch_order<mode>"
   [(set (pc)
        (if_then_else
-        (match_operator 0 "order_operator"
+        (match_operator 1 "order_operator"
                         [(match_operand:GPR 2 "register_operand" "d")
                          (const_int 0)])
-        (label_ref (match_operand 1 "" ""))
+        (label_ref (match_operand 0 "" ""))
         (pc)))]
   "!TARGET_MIPS16"
   { return mips_output_order_conditional_branch (insn, operands, false); }
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 (define_insn "*branch_order<mode>_inverted"
   [(set (pc)
        (if_then_else
-        (match_operator 0 "order_operator"
+        (match_operator 1 "order_operator"
                         [(match_operand:GPR 2 "register_operand" "d")
                          (const_int 0)])
         (pc)
-        (label_ref (match_operand 1 "" ""))))]
+        (label_ref (match_operand 0 "" ""))))]
   "!TARGET_MIPS16"
   { return mips_output_order_conditional_branch (insn, operands, true); }
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 ;; Conditional branch on equality comparison.
 
 (define_insn "*branch_equality<mode>"
   [(set (pc)
        (if_then_else
-        (match_operator 0 "equality_operator"
+        (match_operator 1 "equality_operator"
                         [(match_operand:GPR 2 "register_operand" "d")
                          (match_operand:GPR 3 "reg_or_0_operand" "dJ")])
-        (label_ref (match_operand 1 "" ""))
+        (label_ref (match_operand 0 "" ""))
         (pc)))]
   "!TARGET_MIPS16"
 {
   return mips_output_conditional_branch (insn, operands,
-                                        MIPS_BRANCH ("b%C0", "%2,%z3,%1"),
-                                        MIPS_BRANCH ("b%N0", "%2,%z3,%1"));
+                                        MIPS_BRANCH ("b%C1", "%2,%z3,%0"),
+                                        MIPS_BRANCH ("b%N1", "%2,%z3,%0"));
 }
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 (define_insn "*branch_equality<mode>_inverted"
   [(set (pc)
        (if_then_else
-        (match_operator 0 "equality_operator"
+        (match_operator 1 "equality_operator"
                         [(match_operand:GPR 2 "register_operand" "d")
                          (match_operand:GPR 3 "reg_or_0_operand" "dJ")])
         (pc)
-        (label_ref (match_operand 1 "" ""))))]
+        (label_ref (match_operand 0 "" ""))))]
   "!TARGET_MIPS16"
 {
   return mips_output_conditional_branch (insn, operands,
-                                        MIPS_BRANCH ("b%N0", "%2,%z3,%1"),
-                                        MIPS_BRANCH ("b%C0", "%2,%z3,%1"));
+                                        MIPS_BRANCH ("b%N1", "%2,%z3,%0"),
+                                        MIPS_BRANCH ("b%C1", "%2,%z3,%0"));
 }
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 ;; MIPS16 branches
 
        return "bt%N0z\t%3";
     }
 }
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 (define_expand "cbranch<mode>4"
   [(set (pc)
   [(set (pc)
        (if_then_else
         (equality_op (zero_extract:GPR
-                      (match_operand:GPR 0 "register_operand" "d")
+                      (match_operand:GPR 1 "register_operand" "d")
                       (const_int 1)
                       (match_operand 2 "const_int_operand" ""))
                      (const_int 0))
-        (label_ref (match_operand 1 ""))
+        (label_ref (match_operand 0 ""))
         (pc)))]
   "ISA_HAS_BBIT && UINTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)"
 {
   return
     mips_output_conditional_branch (insn, operands,
-                                   MIPS_BRANCH ("bbit<bbv>", "%0,%2,%1"),
-                                   MIPS_BRANCH ("bbit<bbinv>", "%0,%2,%1"));
+                                   MIPS_BRANCH ("bbit<bbv>", "%1,%2,%0"),
+                                   MIPS_BRANCH ("bbit<bbinv>", "%1,%2,%0"));
 }
   [(set_attr "type"         "branch")
-   (set_attr "mode"         "none")
    (set_attr "branch_likely" "no")])
 
 (define_insn "*branch_bit<bbv><mode>_inverted"
   [(set (pc)
        (if_then_else
         (equality_op (zero_extract:GPR
-                      (match_operand:GPR 0 "register_operand" "d")
+                      (match_operand:GPR 1 "register_operand" "d")
                       (const_int 1)
                       (match_operand 2 "const_int_operand" ""))
                      (const_int 0))
         (pc)
-        (label_ref (match_operand 1 ""))))]
+        (label_ref (match_operand 0 ""))))]
   "ISA_HAS_BBIT && UINTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)"
 {
   return
     mips_output_conditional_branch (insn, operands,
-                                   MIPS_BRANCH ("bbit<bbinv>", "%0,%2,%1"),
-                                   MIPS_BRANCH ("bbit<bbv>", "%0,%2,%1"));
+                                   MIPS_BRANCH ("bbit<bbinv>", "%1,%2,%0"),
+                                   MIPS_BRANCH ("bbit<bbv>", "%1,%2,%0"));
 }
   [(set_attr "type"         "branch")
-   (set_attr "mode"         "none")
    (set_attr "branch_likely" "no")])
 \f
 ;;
 
 ;; Unconditional branches.
 
-(define_insn "jump"
+(define_expand "jump"
   [(set (pc)
-       (label_ref (match_operand 0 "" "")))]
-  "!TARGET_MIPS16"
+       (label_ref (match_operand 0)))])
+
+(define_insn "*jump_absolute"
+  [(set (pc)
+       (label_ref (match_operand 0)))]
+  "!TARGET_MIPS16 && TARGET_ABSOLUTE_JUMPS"
+  { return MIPS_ABSOLUTE_JUMP ("%*j\t%l0%/"); }
+  [(set_attr "type" "jump")])
+
+(define_insn "*jump_pic"
+  [(set (pc)
+       (label_ref (match_operand 0)))]
+  "!TARGET_MIPS16 && !TARGET_ABSOLUTE_JUMPS"
 {
-  if (flag_pic)
+  if (get_attr_length (insn) <= 8)
+    return "%*b\t%l0%/";
+  else
     {
-      if (get_attr_length (insn) <= 8)
-       return "%*b\t%l0%/";
-      else
-       {
-         output_asm_insn (mips_output_load_label (), operands);
-         return "%*jr\t%@%/%]";
-       }
+      mips_output_load_label (operands[0]);
+      return "%*jr\t%@%/%]";
     }
-  else
-    return "%*j\t%l0%/";
 }
-  [(set_attr "type"    "jump")
-   (set_attr "mode"    "none")
-   (set (attr "length")
-       ;; We can't use `j' when emitting PIC.  Emit a branch if it's
-       ;; in range, otherwise load the address of the branch target into
-       ;; $at and then jump to it.
-       (if_then_else
-        (ior (eq (symbol_ref "flag_pic") (const_int 0))
-             (lt (abs (minus (match_dup 0)
-                             (plus (pc) (const_int 4))))
-                 (const_int 131072)))
-        (const_int 4) (const_int 16)))])
+  [(set_attr "type" "branch")])
 
 ;; We need a different insn for the mips16, because a mips16 branch
 ;; does not have a delay slot.
 
-(define_insn ""
+(define_insn "*jump_mips16"
   [(set (pc)
        (label_ref (match_operand 0 "" "")))]
   "TARGET_MIPS16"
   "b\t%l0"
-  [(set_attr "type" "branch")
-   (set_attr "mode" "none")])
+  [(set_attr "type" "branch")])
 
 (define_expand "indirect_jump"
   [(set (pc) (match_operand 0 "register_operand"))]
    (clobber (match_scratch:SI 0 "=&d"))]
   "TARGET_CALL_CLOBBERED_GP"
   "#"
-  "&& reload_completed"
+  "&& epilogue_completed"
   [(const_int 0)]
 {
-  mips_restore_gp (operands[0]);
+  mips_restore_gp_from_cprestore_slot (operands[0]);
   DONE;
 }
-  [(set_attr "type" "load")
-   (set_attr "length" "12")])
+  [(set_attr "type" "ghost")])
+
+;; Move between $gp and its register save slot.
+(define_insn_and_split "move_gp<mode>"
+  [(set (match_operand:GPR 0 "nonimmediate_operand" "=d,m")
+       (unspec:GPR [(match_operand:GPR 1 "move_operand" "m,d")]
+                   UNSPEC_MOVE_GP))]
+  ""
+  { return mips_must_initialize_gp_p () ? "#" : ""; }
+  "mips_must_initialize_gp_p ()"
+  [(const_int 0)]
+{
+  mips_emit_move (operands[0], operands[1]);
+  DONE;
+}
+  [(set_attr "type" "ghost")])
 \f
 ;;
 ;;  ....................
index a9a0177197e5f9c472aa9bbbce2933540c6c6c8e..e1cb4573688ff26f26ab71675bf4527a8c292795 100644 (file)
     }
 })
 
+(define_predicate "cprestore_save_slot_operand"
+  (and (match_code "mem")
+       (match_test "mips_cprestore_address_p (XEXP (op, 0), false)")))
+
+(define_predicate "cprestore_load_slot_operand"
+  (and (match_code "mem")
+       (match_test "mips_cprestore_address_p (XEXP (op, 0), true)")))
+
 (define_predicate "consttable_operand"
   (match_test "CONSTANT_P (op)"))
 
index bcc86bfc148f00005b1a7890ce532c20363f7180..23e9dbf5b025f2e843cc1b4a00db8c8b20fad16d 100644 (file)
@@ -1,3 +1,21 @@
+2009-09-14  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       * gcc.target/mips/branch-helper.h: New file.
+       * gcc.target/mips/branch-2.c,
+       * gcc.target/mips/branch-3.c,
+       * gcc.target/mips/branch-4.c,
+       * gcc.target/mips/branch-5.c,
+       * gcc.target/mips/branch-6.c,
+       * gcc.target/mips/branch-7.c,
+       * gcc.target/mips/branch-8.c,
+       * gcc.target/mips/branch-9.c,
+       * gcc.target/mips/branch-10.c,
+       * gcc.target/mips/branch-11.c,
+       * gcc.target/mips/branch-12.c,
+       * gcc.target/mips/branch-13.c,
+       * gcc.target/mips/branch-14.c,
+       * gcc.target/mips/branch-15.c: New tests.
+
 2009-09-14  Michael Meissner  <meissner@linux.vnet.ibm.com>
 
        PR target/41331
diff --git a/gcc/testsuite/gcc.target/mips/branch-10.c b/gcc/testsuite/gcc.target/mips/branch-10.c
new file mode 100644 (file)
index 0000000..7fdebfc
--- /dev/null
@@ -0,0 +1,13 @@
+/* { dg-options "-mabicalls -mshared -mabi=n32" } */
+/* { dg-final { scan-assembler-not "(\\\$28|%gp_rel|%got)" } } */
+/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (void (*bar) (void), volatile int *x)
+{
+  bar ();
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fff8;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-11.c b/gcc/testsuite/gcc.target/mips/branch-11.c
new file mode 100644 (file)
index 0000000..1c57f82
--- /dev/null
@@ -0,0 +1,17 @@
+/* { dg-options "-mabicalls -mshared -mabi=n32" } */
+/* { dg-final { scan-assembler "\tsd\t\\\$28," } } */
+/* { dg-final { scan-assembler "\tld\t\\\$28," } } */
+/* { dg-final { scan-assembler "\taddiu\t\\\$28,\\\$28,%lo\\(%neg\\(%gp_rel\\(foo\\)\\)\\)\n" } } */
+/* { dg-final { scan-assembler "\tlw\t\\\$1,%got_page\\(\[^)\]*\\)\\(\\\$28\\)\n" } } */
+/* { dg-final { scan-assembler "\taddiu\t\\\$1,\\\$1,%got_ofst\\(\[^)\]*\\)\n" } } */
+/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (void (*bar) (void), volatile int *x)
+{
+  bar ();
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fffc;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-12.c b/gcc/testsuite/gcc.target/mips/branch-12.c
new file mode 100644 (file)
index 0000000..f1b6f1e
--- /dev/null
@@ -0,0 +1,13 @@
+/* { dg-options "-mabicalls -mshared -mabi=64" } */
+/* { dg-final { scan-assembler-not "(\\\$28|%gp_rel|%got)" } } */
+/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (void (*bar) (void), volatile int *x)
+{
+  bar ();
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fff8;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-13.c b/gcc/testsuite/gcc.target/mips/branch-13.c
new file mode 100644 (file)
index 0000000..cc0b607
--- /dev/null
@@ -0,0 +1,17 @@
+/* { dg-options "-mabicalls -mshared -mabi=64" } */
+/* { dg-final { scan-assembler "\tsd\t\\\$28," } } */
+/* { dg-final { scan-assembler "\tld\t\\\$28," } } */
+/* { dg-final { scan-assembler "\tdaddiu\t\\\$28,\\\$28,%lo\\(%neg\\(%gp_rel\\(foo\\)\\)\\)\n" } } */
+/* { dg-final { scan-assembler "\tld\t\\\$1,%got_page\\(\[^)\]*\\)\\(\\\$28\\)\n" } } */
+/* { dg-final { scan-assembler "\tdaddiu\t\\\$1,\\\$1,%got_ofst\\(\[^)\]*\\)\n" } } */
+/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (void (*bar) (void), volatile int *x)
+{
+  bar ();
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fffc;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-14.c b/gcc/testsuite/gcc.target/mips/branch-14.c
new file mode 100644 (file)
index 0000000..026417e
--- /dev/null
@@ -0,0 +1,23 @@
+/* An executable version of branch-2.c.  */
+/* { dg-do run } */
+
+#include "branch-helper.h"
+
+void __attribute__((noinline))
+foo (volatile int *x)
+{
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fff8;
+}
+
+int
+main (void)
+{
+  int x = 0;
+  int y = 1;
+
+  foo (&x);
+  foo (&y);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-15.c b/gcc/testsuite/gcc.target/mips/branch-15.c
new file mode 100644 (file)
index 0000000..dee7a05
--- /dev/null
@@ -0,0 +1,23 @@
+/* An executable version of branch-3.c.  */
+/* { dg-do run } */
+
+#include "branch-helper.h"
+
+void
+foo (volatile int *x)
+{
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fffc;
+}
+
+int
+main (void)
+{
+  int x = 0;
+  int y = 1;
+
+  foo (&x);
+  foo (&y);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-2.c b/gcc/testsuite/gcc.target/mips/branch-2.c
new file mode 100644 (file)
index 0000000..845e748
--- /dev/null
@@ -0,0 +1,13 @@
+/* { dg-options "-mabicalls -mshared -mabi=32" } */
+/* { dg-final { scan-assembler-not "(\\\$25|\\\$28|cpload)" } } */
+/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "cprestore" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (volatile int *x)
+{
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fff8;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-3.c b/gcc/testsuite/gcc.target/mips/branch-3.c
new file mode 100644 (file)
index 0000000..0a4ffbb
--- /dev/null
@@ -0,0 +1,13 @@
+/* { dg-options "-mabicalls -mshared -mabi=32" } */
+/* { dg-final { scan-assembler "\t\\.cpload\t\\\$25\n" } } */
+/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "cprestore" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (volatile int *x)
+{
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fffc;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-4.c b/gcc/testsuite/gcc.target/mips/branch-4.c
new file mode 100644 (file)
index 0000000..277bd0a
--- /dev/null
@@ -0,0 +1,12 @@
+/* { dg-options "-mabicalls -mshared -mabi=n32" } */
+/* { dg-final { scan-assembler-not "(\\\$25|\\\$28|%gp_rel|%got)" } } */
+/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (volatile int *x)
+{
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fff8;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-5.c b/gcc/testsuite/gcc.target/mips/branch-5.c
new file mode 100644 (file)
index 0000000..3d151d8
--- /dev/null
@@ -0,0 +1,14 @@
+/* { dg-options "-mabicalls -mshared -mabi=n32" } */
+/* { dg-final { scan-assembler "\taddiu\t\\\$3,\\\$3,%lo\\(%neg\\(%gp_rel\\(foo\\)\\)\\)\n" } } */
+/* { dg-final { scan-assembler "\tlw\t\\\$1,%got_page\\(\[^)\]*\\)\\(\\\$3\\)\\n" } } */
+/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "\\\$28" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (volatile int *x)
+{
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fffc;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-6.c b/gcc/testsuite/gcc.target/mips/branch-6.c
new file mode 100644 (file)
index 0000000..9bf73f0
--- /dev/null
@@ -0,0 +1,12 @@
+/* { dg-options "-mabicalls -mshared -mabi=64" } */
+/* { dg-final { scan-assembler-not "(\\\$25|\\\$28|%gp_rel|%got)" } } */
+/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (volatile int *x)
+{
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fff8;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-7.c b/gcc/testsuite/gcc.target/mips/branch-7.c
new file mode 100644 (file)
index 0000000..053ec61
--- /dev/null
@@ -0,0 +1,14 @@
+/* { dg-options "-mabicalls -mshared -mabi=64" } */
+/* { dg-final { scan-assembler "\tdaddiu\t\\\$3,\\\$3,%lo\\(%neg\\(%gp_rel\\(foo\\)\\)\\)\n" } } */
+/* { dg-final { scan-assembler "\tld\t\\\$1,%got_page\\(\[^)\]*\\)\\(\\\$3\\)\\n" } } */
+/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "\\\$28" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (volatile int *x)
+{
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fffc;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-8.c b/gcc/testsuite/gcc.target/mips/branch-8.c
new file mode 100644 (file)
index 0000000..c2cbae3
--- /dev/null
@@ -0,0 +1,13 @@
+/* { dg-options "-mabicalls -mshared -mabi=32" } */
+/* { dg-final { scan-assembler-not "(\\\$28|cpload|cprestore)" } } */
+/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (void (*bar) (void), volatile int *x)
+{
+  bar ();
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fff8;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-9.c b/gcc/testsuite/gcc.target/mips/branch-9.c
new file mode 100644 (file)
index 0000000..2b83ea5
--- /dev/null
@@ -0,0 +1,18 @@
+/* { dg-options "-mabicalls -mshared -mabi=32" } */
+/* { dg-final { scan-assembler "\t\\.cpload\t\\\$25\n" } } */
+/* { dg-final { scan-assembler "\t\\.cprestore\t16\n" } } */
+/* { dg-final { scan-assembler "\tlw\t\\\$1,16\\(\\\$fp\\)\n" } } */
+/* { dg-final { scan-assembler "\tlw\t\\\$1,%got\\(\[^)\]*\\)\\(\\\$1\\)\n" } } */
+/* { dg-final { scan-assembler "\taddiu\t\\\$1,\\\$1,%lo\\(\[^)\]*\\)\n" } } */
+/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "\tlw\t\\\$28,16\\(\\\$sp\\)\n" } } */
+
+#include "branch-helper.h"
+
+NOMIPS16 void
+foo (void (*bar) (void), volatile int *x)
+{
+  bar ();
+  if (__builtin_expect (*x == 0, 1))
+    OCCUPY_0x1fffc;
+}
diff --git a/gcc/testsuite/gcc.target/mips/branch-helper.h b/gcc/testsuite/gcc.target/mips/branch-helper.h
new file mode 100644 (file)
index 0000000..85399be
--- /dev/null
@@ -0,0 +1,37 @@
+/* DN(X) generates 2**N copies of asm instruction X.  */
+#define D0(X) X
+#define D1(X) X "\n\t" X
+#define D2(X) D1 (D1 (X))
+#define D3(X) D2 (D1 (X))
+#define D4(X) D2 (D2 (X))
+#define D5(X) D4 (D1 (X))
+#define D6(X) D4 (D2 (X))
+#define D7(X) D4 (D2 (D1 (X)))
+#define D8(X) D4 (D4 (X))
+#define D9(X) D8 (D1 (X))
+#define D10(X) D8 (D2 (X))
+#define D11(X) D8 (D2 (D1 (X)))
+#define D12(X) D8 (D4 (X))
+#define D13(X) D8 (D4 (D1 (X)))
+#define D14(X) D8 (D4 (D2 (X)))
+
+/* Emit something that is 0x1fff8 bytes long, which is the largest
+   permissible range for non-MIPS16 forward branches.  */
+#define OCCUPY_0x1fff8 \
+  asm (D14 ("nop") "\n\t" \
+       D13 ("nop") "\n\t" \
+       D12 ("nop") "\n\t" \
+       D11 ("nop") "\n\t" \
+       D10 ("nop") "\n\t" \
+       D9 ("nop") "\n\t" \
+       D8 ("nop") "\n\t" \
+       D7 ("nop") "\n\t" \
+       D6 ("nop") "\n\t" \
+       D5 ("nop") "\n\t" \
+       D4 ("nop") "\n\t" \
+       D3 ("nop") "\n\t" \
+       D2 ("nop") "\n\t" \
+       D1 ("nop"))
+
+/* Likewise emit something that is 0x1fffc bytes long.  */
+#define OCCUPY_0x1fffc do { asm ("nop"); OCCUPY_0x1fff8; } while (0)