Commit 0c433c31 by Richard Sandiford Committed by Richard Sandiford

mips-protos.h (mips_cfun_has_cprestore_slot_p): Declare.

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
parent af0b8d73
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> 2009-09-14 Michael Meissner <meissner@linux.vnet.ibm.com>
PR target/41210 PR target/41210
......
...@@ -1102,11 +1102,10 @@ ...@@ -1102,11 +1102,10 @@
(define_insn "mips_bposge" (define_insn "mips_bposge"
[(set (pc) [(set (pc)
(if_then_else (ge (reg:CCDSP CCDSP_PO_REGNUM) (if_then_else (ge (reg:CCDSP CCDSP_PO_REGNUM)
(match_operand:SI 0 "immediate_operand" "I")) (match_operand:SI 1 "immediate_operand" "I"))
(label_ref (match_operand 1 "" "")) (label_ref (match_operand 0 "" ""))
(pc)))] (pc)))]
"ISA_HAS_DSP" "ISA_HAS_DSP"
"%*bposge%0\t%1%/" "%*bposge%1\t%0%/"
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
...@@ -219,7 +219,10 @@ extern rtx mips_subword (rtx, bool); ...@@ -219,7 +219,10 @@ extern rtx mips_subword (rtx, bool);
extern bool mips_split_64bit_move_p (rtx, rtx); extern bool mips_split_64bit_move_p (rtx, rtx);
extern void mips_split_doubleword_move (rtx, rtx); extern void mips_split_doubleword_move (rtx, rtx);
extern const char *mips_output_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 #ifdef RTX_CODE
extern void mips_expand_scc (rtx *); extern void mips_expand_scc (rtx *);
extern void mips_expand_conditional_branch (rtx *); extern void mips_expand_conditional_branch (rtx *);
...@@ -276,7 +279,9 @@ extern bool mips_small_data_pattern_p (rtx); ...@@ -276,7 +279,9 @@ extern bool mips_small_data_pattern_p (rtx);
extern rtx mips_rewrite_small_data (rtx); extern rtx mips_rewrite_small_data (rtx);
extern HOST_WIDE_INT mips_initial_elimination_offset (int, int); extern HOST_WIDE_INT mips_initial_elimination_offset (int, int);
extern rtx mips_return_addr (int, rtx); 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 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_prologue (void);
extern void mips_expand_before_return (void); extern void mips_expand_before_return (void);
extern void mips_expand_epilogue (bool); extern void mips_expand_epilogue (bool);
...@@ -296,7 +301,7 @@ extern int mips_register_move_cost (enum machine_mode, enum reg_class, ...@@ -296,7 +301,7 @@ extern int mips_register_move_cost (enum machine_mode, enum reg_class,
enum reg_class); enum reg_class);
extern int mips_adjust_insn_length (rtx, int); 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 *, extern const char *mips_output_conditional_branch (rtx, rtx *, const char *,
const char *); const char *);
extern const char *mips_output_order_conditional_branch (rtx, rtx *, bool); 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, ...@@ -334,6 +339,7 @@ extern void mips_expand_atomic_qihi (union mips_gen_fn_ptrs,
extern void mips_expand_vector_init (rtx, rtx); extern void mips_expand_vector_init (rtx, rtx);
extern bool mips_eh_uses (unsigned int);
extern bool mips_epilogue_uses (unsigned int); extern bool mips_epilogue_uses (unsigned int);
extern void mips_final_prescan_insn (rtx, rtx *, int); extern void mips_final_prescan_insn (rtx, rtx *, int);
......
...@@ -439,50 +439,46 @@ ...@@ -439,50 +439,46 @@
; Branch on Any of Four Floating Point Condition Codes True ; Branch on Any of Four Floating Point Condition Codes True
(define_insn "bc1any4t" (define_insn "bc1any4t"
[(set (pc) [(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)) (const_int 0))
(label_ref (match_operand 1 "" "")) (label_ref (match_operand 0 "" ""))
(pc)))] (pc)))]
"TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT" "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
"%*bc1any4t\t%0,%1%/" "%*bc1any4t\t%1,%0%/"
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
; Branch on Any of Four Floating Point Condition Codes False ; Branch on Any of Four Floating Point Condition Codes False
(define_insn "bc1any4f" (define_insn "bc1any4f"
[(set (pc) [(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)) (const_int -1))
(label_ref (match_operand 1 "" "")) (label_ref (match_operand 0 "" ""))
(pc)))] (pc)))]
"TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT" "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
"%*bc1any4f\t%0,%1%/" "%*bc1any4f\t%1,%0%/"
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
; Branch on Any of Two Floating Point Condition Codes True ; Branch on Any of Two Floating Point Condition Codes True
(define_insn "bc1any2t" (define_insn "bc1any2t"
[(set (pc) [(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)) (const_int 0))
(label_ref (match_operand 1 "" "")) (label_ref (match_operand 0 "" ""))
(pc)))] (pc)))]
"TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT" "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
"%*bc1any2t\t%0,%1%/" "%*bc1any2t\t%1,%0%/"
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
; Branch on Any of Two Floating Point Condition Codes False ; Branch on Any of Two Floating Point Condition Codes False
(define_insn "bc1any2f" (define_insn "bc1any2f"
[(set (pc) [(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)) (const_int -1))
(label_ref (match_operand 1 "" "")) (label_ref (match_operand 0 "" ""))
(pc)))] (pc)))]
"TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT" "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
"%*bc1any2f\t%0,%1%/" "%*bc1any2f\t%1,%0%/"
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
; Used to access one register in a CCV2 pair. Operand 0 is the register ; 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). ; pair and operand 1 is the index of the register we want (a CONST_INT).
...@@ -497,45 +493,43 @@ ...@@ -497,45 +493,43 @@
(define_insn "*branch_upper_lower" (define_insn "*branch_upper_lower"
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(match_operator 0 "equality_operator" (match_operator 1 "equality_operator"
[(unspec:CC [(match_operand:CCV2 2 "register_operand" "z") [(unspec:CC [(match_operand:CCV2 2 "register_operand" "z")
(match_operand 3 "const_int_operand")] (match_operand 3 "const_int_operand")]
UNSPEC_SINGLE_CC) UNSPEC_SINGLE_CC)
(const_int 0)]) (const_int 0)])
(label_ref (match_operand 1 "" "")) (label_ref (match_operand 0 "" ""))
(pc)))] (pc)))]
"TARGET_HARD_FLOAT" "TARGET_HARD_FLOAT"
{ {
operands[2] operands[2]
= gen_rtx_REG (CCmode, REGNO (operands[2]) + INTVAL (operands[3])); = gen_rtx_REG (CCmode, REGNO (operands[2]) + INTVAL (operands[3]));
return mips_output_conditional_branch (insn, operands, return mips_output_conditional_branch (insn, operands,
MIPS_BRANCH ("b%F0", "%2,%1"), MIPS_BRANCH ("b%F1", "%2,%0"),
MIPS_BRANCH ("b%W0", "%2,%1")); MIPS_BRANCH ("b%W1", "%2,%0"));
} }
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
; As above, but with the sense of the condition reversed. ; As above, but with the sense of the condition reversed.
(define_insn "*branch_upper_lower_inverted" (define_insn "*branch_upper_lower_inverted"
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(match_operator 0 "equality_operator" (match_operator 1 "equality_operator"
[(unspec:CC [(match_operand:CCV2 2 "register_operand" "z") [(unspec:CC [(match_operand:CCV2 2 "register_operand" "z")
(match_operand 3 "const_int_operand")] (match_operand 3 "const_int_operand")]
UNSPEC_SINGLE_CC) UNSPEC_SINGLE_CC)
(const_int 0)]) (const_int 0)])
(pc) (pc)
(label_ref (match_operand 1 "" ""))))] (label_ref (match_operand 0 "" ""))))]
"TARGET_HARD_FLOAT" "TARGET_HARD_FLOAT"
{ {
operands[2] operands[2]
= gen_rtx_REG (CCmode, REGNO (operands[2]) + INTVAL (operands[3])); = gen_rtx_REG (CCmode, REGNO (operands[2]) + INTVAL (operands[3]));
return mips_output_conditional_branch (insn, operands, return mips_output_conditional_branch (insn, operands,
MIPS_BRANCH ("b%W0", "%2,%1"), MIPS_BRANCH ("b%W1", "%2,%0"),
MIPS_BRANCH ("b%F0", "%2,%1")); MIPS_BRANCH ("b%F1", "%2,%0"));
} }
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
;---------------------------------------------------------------------------- ;----------------------------------------------------------------------------
; Floating Point Reduced Precision Reciprocal Square Root Instructions. ; Floating Point Reduced Precision Reciprocal Square Root Instructions.
......
...@@ -307,6 +307,10 @@ struct GTY(()) machine_function { ...@@ -307,6 +307,10 @@ struct GTY(()) machine_function {
if the function doesn't need one. */ if the function doesn't need one. */
unsigned int global_pointer; 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 /* True if mips_adjust_insn_length should ignore an instruction's
hazard attribute. */ hazard attribute. */
bool ignore_hazard_length_p; bool ignore_hazard_length_p;
...@@ -315,8 +319,23 @@ struct GTY(()) machine_function { ...@@ -315,8 +319,23 @@ struct GTY(()) machine_function {
.set nomacro. */ .set nomacro. */
bool all_noreorder_p; bool all_noreorder_p;
/* True if the function is known to have an instruction that needs $gp. */ /* True if the function has "inflexible" and "flexible" references
bool has_gp_insn_p; 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 /* True if we have emitted an instruction to initialize
mips16_gp_pseudo_rtx. */ mips16_gp_pseudo_rtx. */
...@@ -6313,9 +6332,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr, ...@@ -6313,9 +6332,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
{ {
rtx (*fn) (rtx, rtx); rtx (*fn) (rtx, rtx);
if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS) if (type == MIPS_CALL_SIBCALL)
fn = gen_call_split;
else if (type == MIPS_CALL_SIBCALL)
fn = gen_sibcall_internal; fn = gen_sibcall_internal;
else else
fn = gen_call_internal; fn = gen_call_internal;
...@@ -6328,9 +6345,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr, ...@@ -6328,9 +6345,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
rtx (*fn) (rtx, rtx, rtx, rtx); rtx (*fn) (rtx, rtx, rtx, rtx);
rtx reg1, reg2; rtx reg1, reg2;
if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS) if (type == MIPS_CALL_SIBCALL)
fn = gen_call_value_multiple_split;
else if (type == MIPS_CALL_SIBCALL)
fn = gen_sibcall_value_multiple_internal; fn = gen_sibcall_value_multiple_internal;
else else
fn = gen_call_value_multiple_internal; fn = gen_call_value_multiple_internal;
...@@ -6343,9 +6358,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr, ...@@ -6343,9 +6358,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
{ {
rtx (*fn) (rtx, rtx, rtx); rtx (*fn) (rtx, rtx, rtx);
if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS) if (type == MIPS_CALL_SIBCALL)
fn = gen_call_value_split;
else if (type == MIPS_CALL_SIBCALL)
fn = gen_sibcall_value_internal; fn = gen_sibcall_value_internal;
else else
fn = gen_call_value_internal; fn = gen_call_value_internal;
...@@ -6375,7 +6388,7 @@ mips_split_call (rtx insn, rtx call_pattern) ...@@ -6375,7 +6388,7 @@ mips_split_call (rtx insn, rtx call_pattern)
/* Pick a temporary register that is suitable for both MIPS16 and /* Pick a temporary register that is suitable for both MIPS16 and
non-MIPS16 code. $4 and $5 are used for returning complex double non-MIPS16 code. $4 and $5 are used for returning complex double
values in soft-float code, so $6 is the first suitable candidate. */ 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. */ /* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */
...@@ -8566,42 +8579,131 @@ mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust) ...@@ -8566,42 +8579,131 @@ mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
return buffer; return buffer;
} }
/* Return true if the current function has an insn that implicitly /* Return true if the current function returns its value in a floating-point
refers to $gp. */ register in MIPS16 mode. */
static bool 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. */ tree return_type = DECL_RESULT (current_function_decl);
if (!cfun->machine->has_gp_insn_p) return (TARGET_MIPS16
{ && TARGET_HARD_FLOAT_ABI
rtx insn; && !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 (); push_topmost_sequence ();
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (USEFUL_INSN_P (insn) if (USEFUL_INSN_P (insn) && pred (insn))
&& (get_attr_got (insn) != GOT_UNSET
|| mips_small_data_pattern_p (PATTERN (insn))))
{ {
cfun->machine->has_gp_insn_p = true; *cache = true;
break; break;
} }
pop_topmost_sequence (); 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 /* Return true if INSN refers to the global pointer in an "inflexible" way.
register in MIPS16 mode. */ See mips_cfun_has_inflexible_gp_ref_p for details. */
static bool 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); /* Uses of pic_offset_table_rtx in CALL_INSN_FUNCTION_USAGE
return (TARGET_MIPS16 indicate that the target could be a traditional MIPS
&& TARGET_HARD_FLOAT_ABI lazily-binding stub. */
&& !aggregate_value_p (return_type, current_function_decl) return find_reg_fusage (insn, USE, pic_offset_table_rtx);
&& mips_return_mode_in_fpr_p (DECL_MODE (return_type))); }
/* 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 /* Return the register that should be used as the global pointer
...@@ -8617,57 +8719,18 @@ mips_global_pointer (void) ...@@ -8617,57 +8719,18 @@ mips_global_pointer (void)
if (!TARGET_USE_GOT) if (!TARGET_USE_GOT)
return GLOBAL_POINTER_REGNUM; return GLOBAL_POINTER_REGNUM;
/* We must always provide $gp when it is used implicitly. */ /* If there are inflexible references to $gp, we must use the
if (!TARGET_EXPLICIT_RELOCS) standard register. */
return GLOBAL_POINTER_REGNUM; if (mips_cfun_has_inflexible_gp_ref_p ())
/* 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)
return GLOBAL_POINTER_REGNUM; return GLOBAL_POINTER_REGNUM;
/* There's no need to initialize $gp if it isn't referenced now, /* If there are no current references to $gp, then the only uses
and if we can be sure that no new references will be added during we can introduce later are those involved in long branches. */
or after reload. */ if (TARGET_ABSOLUTE_JUMPS && !mips_cfun_has_flexible_gp_ref_p ())
if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM) return INVALID_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.
- MIPS16 functions that return in FPRs need to call an /* If the global pointer is call-saved, try to use a call-clobbered
external libgcc routine. */ alternative. */
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 (TARGET_CALL_SAVED_GP && current_function_is_leaf) if (TARGET_CALL_SAVED_GP && current_function_is_leaf)
for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
if (!df_regs_ever_live_p (regno) if (!df_regs_ever_live_p (regno)
...@@ -8679,6 +8742,119 @@ mips_global_pointer (void) ...@@ -8679,6 +8742,119 @@ mips_global_pointer (void)
return GLOBAL_POINTER_REGNUM; 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 /* Return true if REGNO is a register that is ordinarily call-clobbered
but must nevertheless be preserved by an interrupt handler. */ but must nevertheless be preserved by an interrupt handler. */
...@@ -9198,48 +9374,118 @@ mips_set_return_address (rtx address, rtx scratch) ...@@ -9198,48 +9374,118 @@ mips_set_return_address (rtx address, rtx scratch)
mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address); 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 /* Return true if the current function has a cprestore slot. */
register if need be. */
static rtx bool
mips_cprestore_slot (rtx temp) 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; const struct mips_frame_info *frame;
rtx base;
HOST_WIDE_INT offset;
frame = &cfun->machine->frame; frame = &cfun->machine->frame;
if (frame_pointer_needed) /* .cprestore always uses the stack pointer instead of the frame pointer.
{ We have a free choice for direct stores for non-MIPS16 functions,
base = hard_frame_pointer_rtx; and for MIPS16 functions whose cprestore slot is in range of the
offset = frame->args_size - frame->hard_frame_pointer_offset; 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 else
{ {
base = stack_pointer_rtx; *base = stack_pointer_rtx;
offset = frame->args_size; *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)); 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 /* 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 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; return;
if (TARGET_MIPS16) 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); mips_emit_move (pic_offset_table_rtx, temp);
} }
else 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) if (!TARGET_EXPLICIT_RELOCS)
emit_insn (gen_blockage ()); emit_insn (gen_blockage ());
} }
...@@ -9327,6 +9573,89 @@ mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT sp_offset, ...@@ -9327,6 +9573,89 @@ mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT sp_offset,
offset -= GET_MODE_SIZE (fpr_mode); 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));
}
/* If we're generating n32 or n64 abicalls, and the current function /* If we're generating n32 or n64 abicalls, and the current function
does not use $28 as its global pointer, emit a cplocal directive. does not use $28 as its global pointer, emit a cplocal directive.
...@@ -9336,7 +9665,7 @@ static void ...@@ -9336,7 +9665,7 @@ static void
mips_output_cplocal (void) mips_output_cplocal (void)
{ {
if (!TARGET_EXPLICIT_RELOCS if (!TARGET_EXPLICIT_RELOCS
&& cfun->machine->global_pointer != INVALID_REGNUM && mips_must_initialize_gp_p ()
&& cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM) && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
output_asm_insn (".cplocal %+", 0); output_asm_insn (".cplocal %+", 0);
} }
...@@ -9408,7 +9737,8 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) ...@@ -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. /* Handle the initialization of $gp for SVR4 PIC, if applicable.
Also emit the ".set noreorder; .set nomacro" sequence for functions Also emit the ".set noreorder; .set nomacro" sequence for functions
that need it. */ 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) if (TARGET_MIPS16)
{ {
...@@ -9490,33 +9820,7 @@ mips_save_reg (rtx reg, rtx mem) ...@@ -9490,33 +9820,7 @@ mips_save_reg (rtx reg, rtx mem)
mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2))); mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
} }
else else
{ mips_emit_save_slot_move (mem, reg, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
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));
}
} }
/* The __gnu_local_gp symbol. */ /* The __gnu_local_gp symbol. */
...@@ -9599,7 +9903,19 @@ mips_expand_prologue (void) ...@@ -9599,7 +9903,19 @@ mips_expand_prologue (void)
rtx insn; rtx insn;
if (cfun->machine->global_pointer != INVALID_REGNUM) 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; frame = &cfun->machine->frame;
size = frame->total_size; size = frame->total_size;
...@@ -9801,17 +10117,22 @@ mips_expand_prologue (void) ...@@ -9801,17 +10117,22 @@ mips_expand_prologue (void)
mips_emit_loadgp (); mips_emit_loadgp ();
/* Initialize the $gp save slot. */ /* Initialize the $gp save slot. */
if (frame->cprestore_size > 0 if (mips_cfun_has_cprestore_slot_p ())
&& cfun->machine->global_pointer != INVALID_REGNUM)
{ {
if (TARGET_MIPS16) rtx base, mem, gp, temp;
mips_emit_move (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)), HOST_WIDE_INT offset;
MIPS16_PIC_TEMP);
else if (TARGET_ABICALLS_PIC2) mips_get_cprestore_base_and_offset (&base, &offset, false);
emit_insn (gen_cprestore (GEN_INT (frame->args_size))); mem = gen_frame_mem (Pmode, plus_constant (base, offset));
else gp = TARGET_MIPS16 ? MIPS16_PIC_TEMP : pic_offset_table_rtx;
emit_move_insn (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)), temp = (SMALL_OPERAND (offset)
pic_offset_table_rtx); ? 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. */ /* We need to search back to the last use of K0 or K1. */
...@@ -9844,27 +10165,7 @@ mips_restore_reg (rtx reg, rtx mem) ...@@ -9844,27 +10165,7 @@ mips_restore_reg (rtx reg, rtx mem)
if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31) if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
reg = gen_rtx_REG (GET_MODE (reg), GP_REG_FIRST + 7); reg = gen_rtx_REG (GET_MODE (reg), GP_REG_FIRST + 7);
if (REGNO (reg) == HI_REGNUM) mips_emit_save_slot_move (reg, mem, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
{
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);
} }
/* Emit any instructions needed before a return. */ /* Emit any instructions needed before a return. */
...@@ -10732,12 +11033,112 @@ mips_init_libfuncs (void) ...@@ -10732,12 +11033,112 @@ mips_init_libfuncs (void)
synchronize_libfunc = init_one_libfunc ("__sync_synchronize"); 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 /* Return the length of INSN. LENGTH is the initial length computed by
attributes in the machine-description file. */ attributes in the machine-description file. */
int int
mips_adjust_insn_length (rtx insn, int length) 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 /* 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 of a sequence. A conditional jump normally has a delay slot, but
does not on MIPS16. */ does not on MIPS16. */
...@@ -10769,38 +11170,9 @@ mips_adjust_insn_length (rtx insn, int length) ...@@ -10769,38 +11170,9 @@ mips_adjust_insn_length (rtx insn, int length)
return 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 /* Return the assembly code for INSN, which has the operands given by
OPERANDS, and which branches to OPERANDS[1] if some condition is true. 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[1] 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 is in range of a direct branch. BRANCH_IF_FALSE is an inverted
version of BRANCH_IF_TRUE. */ version of BRANCH_IF_TRUE. */
...@@ -10812,7 +11184,7 @@ mips_output_conditional_branch (rtx insn, rtx *operands, ...@@ -10812,7 +11184,7 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
unsigned int length; unsigned int length;
rtx taken, not_taken; rtx taken, not_taken;
gcc_assert (LABEL_P (operands[1])); gcc_assert (LABEL_P (operands[0]));
length = get_attr_length (insn); length = get_attr_length (insn);
if (length <= 8) if (length <= 8)
...@@ -10826,10 +11198,10 @@ mips_output_conditional_branch (rtx insn, rtx *operands, ...@@ -10826,10 +11198,10 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
not use branch-likely instructions. */ not use branch-likely instructions. */
mips_branch_likely = false; mips_branch_likely = false;
not_taken = gen_label_rtx (); not_taken = gen_label_rtx ();
taken = operands[1]; taken = operands[0];
/* Generate the reversed branch to NOT_TAKEN. */ /* Generate the reversed branch to NOT_TAKEN. */
operands[1] = not_taken; operands[0] = not_taken;
output_asm_insn (branch_if_false, operands); output_asm_insn (branch_if_false, operands);
/* If INSN has a delay slot, we must provide delay slots for both the /* 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, ...@@ -10851,11 +11223,11 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
} }
/* Output the unconditional branch to TAKEN. */ /* Output the unconditional branch to TAKEN. */
if (length <= 16) if (TARGET_ABSOLUTE_JUMPS)
output_asm_insn ("j\t%0%/", &taken); output_asm_insn (MIPS_ABSOLUTE_JUMP ("j\t%0%/"), &taken);
else else
{ {
output_asm_insn (mips_output_load_label (), &taken); mips_output_load_label (taken);
output_asm_insn ("jr\t%@%]%/", 0); output_asm_insn ("jr\t%@%]%/", 0);
} }
...@@ -10881,10 +11253,10 @@ mips_output_conditional_branch (rtx insn, rtx *operands, ...@@ -10881,10 +11253,10 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
return ""; 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 if some ordering condition is true. The condition is given by
OPERANDS[0] if !INVERTED_P, otherwise it is the inverse of OPERANDS[1] if !INVERTED_P, otherwise it is the inverse of
OPERANDS[0]. OPERANDS[2] is the comparison's first operand; OPERANDS[1]. OPERANDS[2] is the comparison's first operand;
its second is always zero. */ its second is always zero. */
const char * const char *
...@@ -10892,17 +11264,17 @@ mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p) ...@@ -10892,17 +11264,17 @@ mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
{ {
const char *branch[2]; 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. */ 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. */ /* These cases are equivalent to comparisons against zero. */
case LEU: case LEU:
inverted_p = !inverted_p; inverted_p = !inverted_p;
/* Fall through. */ /* Fall through. */
case GTU: case GTU:
branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%1"); branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%0");
branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%1"); branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%0");
break; break;
/* These cases are always true or always false. */ /* These cases are always true or always false. */
...@@ -10910,13 +11282,13 @@ mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p) ...@@ -10910,13 +11282,13 @@ mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
inverted_p = !inverted_p; inverted_p = !inverted_p;
/* Fall through. */ /* Fall through. */
case GEU: case GEU:
branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%1"); branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%0");
branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%1"); branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%0");
break; break;
default: default:
branch[!inverted_p] = MIPS_BRANCH ("b%C0z", "%2,%1"); branch[!inverted_p] = MIPS_BRANCH ("b%C1z", "%2,%0");
branch[inverted_p] = MIPS_BRANCH ("b%N0z", "%2,%1"); branch[inverted_p] = MIPS_BRANCH ("b%N1z", "%2,%0");
break; break;
} }
return mips_output_conditional_branch (insn, operands, branch[1], branch[0]); 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, ...@@ -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. */ /* Ignore USEs and CLOBBERs; don't count them against the issue rate. */
if (USEFUL_INSN_P (insn)) if (USEFUL_INSN_P (insn))
{ {
more--; if (get_attr_type (insn) != TYPE_GHOST)
more--;
if (!reload_completed && TUNE_MACC_CHAINS) if (!reload_completed && TUNE_MACC_CHAINS)
mips_macc_chains_record (insn); mips_macc_chains_record (insn);
vr4130_last_insn = insn; vr4130_last_insn = insn;
...@@ -14173,6 +14546,46 @@ mips_reorg_process_insns (void) ...@@ -14173,6 +14546,46 @@ mips_reorg_process_insns (void)
htab_delete (htab); 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. */ /* Implement TARGET_MACHINE_DEPENDENT_REORG. */
static void static void
...@@ -14189,6 +14602,10 @@ mips_reorg (void) ...@@ -14189,6 +14602,10 @@ mips_reorg (void)
&& TUNE_MIPS4130 && TUNE_MIPS4130
&& TARGET_VR4130_ALIGN) && TARGET_VR4130_ALIGN)
vr4130_align_insns (); 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 ();
} }
/* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text /* 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, ...@@ -14222,6 +14639,7 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
TARGET_CALL_SAVED_GP. */ TARGET_CALL_SAVED_GP. */
cfun->machine->global_pointer cfun->machine->global_pointer
= TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM; = 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_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
/* Set up the global pointer for n32 or n64 abicalls. */ /* Set up the global pointer for n32 or n64 abicalls. */
...@@ -15137,6 +15555,31 @@ mips_order_regs_for_local_alloc (void) ...@@ -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. */ /* Implement EPILOGUE_USES. */
bool bool
......
...@@ -161,10 +161,13 @@ enum mips_code_readable_setting { ...@@ -161,10 +161,13 @@ enum mips_code_readable_setting {
/* True if the call patterns should be split into a jalr followed by /* 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 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 \ #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 /* 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. operators like %hi and %lo to refer to locally-binding symbols.
...@@ -202,6 +205,17 @@ enum mips_code_readable_setting { ...@@ -202,6 +205,17 @@ enum mips_code_readable_setting {
/* True if TARGET_USE_GOT and if $gp is a call-saved register. */ /* 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) #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. /* 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. */ 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) #define TARGET_USE_PIC_FN_ADDR_REG (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
...@@ -1300,6 +1314,8 @@ enum mips_code_readable_setting { ...@@ -1300,6 +1314,8 @@ enum mips_code_readable_setting {
#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, GP_REG_FIRST + 3) #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. /* 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 The default for this in 64-bit mode is 8, which causes problems with
SFmode register saves. */ SFmode register saves. */
...@@ -1543,11 +1559,12 @@ enum mips_code_readable_setting { ...@@ -1543,11 +1559,12 @@ enum mips_code_readable_setting {
- 8 condition code registers - 8 condition code registers
- 2 accumulator registers (hi and lo) - 2 accumulator registers (hi and lo)
- 32 registers each for coprocessors 0, 2 and 3 - 32 registers each for coprocessors 0, 2 and 3
- 3 fake registers: - 4 fake registers:
- ARG_POINTER_REGNUM - ARG_POINTER_REGNUM
- FRAME_POINTER_REGNUM - FRAME_POINTER_REGNUM
- GOT_VERSION_REGNUM (see the comment above load_call<mode> for details) - 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 accumulator registers (3 hi-lo pairs) for MIPS DSP ASE
- 6 DSP control registers */ - 6 DSP control registers */
...@@ -2661,6 +2678,13 @@ typedef struct mips_args { ...@@ -2661,6 +2678,13 @@ typedef struct mips_args {
#define MIPS_BRANCH(OPCODE, OPERANDS) \ #define MIPS_BRANCH(OPCODE, OPERANDS) \
"%*" OPCODE "%?\t" 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 /* 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 ("j" or "jal"), OPERANDS are its operands, and OPNO is the operand number
of the target. of the target.
...@@ -2675,11 +2699,7 @@ typedef struct mips_args { ...@@ -2675,11 +2699,7 @@ typedef struct mips_args {
? "%*" INSN "\t%" #OPNO "%/" \ ? "%*" INSN "\t%" #OPNO "%/" \
: REG_P (OPERANDS[OPNO]) \ : REG_P (OPERANDS[OPNO]) \
? "%*" INSN "r\t%" #OPNO "%/" \ ? "%*" INSN "r\t%" #OPNO "%/" \
: TARGET_ABICALLS_PIC2 \ : MIPS_ABSOLUTE_JUMP ("%*" INSN "\t%" #OPNO "%/"))
? (".option\tpic0\n\t" \
"%*" INSN "\t%" #OPNO "%/\n\t" \
".option\tpic2") \
: "%*" INSN "\t%" #OPNO "%/")
/* Control the assembler format that we output. */ /* Control the assembler format that we output. */
...@@ -2707,7 +2727,7 @@ typedef struct mips_args { ...@@ -2707,7 +2727,7 @@ typedef struct mips_args {
"$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", \ "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", \
"$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", \ "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", \
"hi", "lo", "", "$fcc0","$fcc1","$fcc2","$fcc3","$fcc4", \ "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", \ "$c0r0", "$c0r1", "$c0r2", "$c0r3", "$c0r4", "$c0r5", "$c0r6", "$c0r7", \
"$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15", \ "$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15", \
"$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23", \ "$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23", \
......
...@@ -29,11 +29,13 @@ ...@@ -29,11 +29,13 @@
(UNSPEC_STORE_WORD 2) (UNSPEC_STORE_WORD 2)
(UNSPEC_GET_FNADDR 3) (UNSPEC_GET_FNADDR 3)
(UNSPEC_BLOCKAGE 4) (UNSPEC_BLOCKAGE 4)
(UNSPEC_CPRESTORE 5) (UNSPEC_POTENTIAL_CPRESTORE 5)
(UNSPEC_RESTORE_GP 6) (UNSPEC_CPRESTORE 6)
(UNSPEC_EH_RETURN 7) (UNSPEC_RESTORE_GP 7)
(UNSPEC_CONSTTABLE_INT 8) (UNSPEC_MOVE_GP 8)
(UNSPEC_CONSTTABLE_FLOAT 9) (UNSPEC_EH_RETURN 9)
(UNSPEC_CONSTTABLE_INT 10)
(UNSPEC_CONSTTABLE_FLOAT 11)
(UNSPEC_ALIGN 14) (UNSPEC_ALIGN 14)
(UNSPEC_HIGH 17) (UNSPEC_HIGH 17)
(UNSPEC_LOAD_LEFT 18) (UNSPEC_LOAD_LEFT 18)
...@@ -77,6 +79,7 @@ ...@@ -77,6 +79,7 @@
(UNSPEC_ADDRESS_FIRST 100) (UNSPEC_ADDRESS_FIRST 100)
(TLS_GET_TP_REGNUM 3) (TLS_GET_TP_REGNUM 3)
(CPRESTORE_SLOT_REGNUM 76)
(GOT_VERSION_REGNUM 79) (GOT_VERSION_REGNUM 79)
;; For MIPS Paired-Singled Floating Point Instructions. ;; For MIPS Paired-Singled Floating Point Instructions.
...@@ -256,6 +259,9 @@ ...@@ -256,6 +259,9 @@
(UNSPEC_MIPS_CACHE 600) (UNSPEC_MIPS_CACHE 600)
(UNSPEC_R10K_CACHE_BARRIER 601) (UNSPEC_R10K_CACHE_BARRIER 601)
;; PIC long branch sequences are never longer than 100 bytes.
(MAX_PIC_BRANCH_LENGTH 100)
] ]
) )
...@@ -281,12 +287,11 @@ ...@@ -281,12 +287,11 @@
;; ;;
;; jal is always a macro for TARGET_CALL_CLOBBERED_GP because it includes ;; jal is always a macro for TARGET_CALL_CLOBBERED_GP because it includes
;; an instruction to restore $gp. Direct jals are also macros for ;; an instruction to restore $gp. Direct jals are also macros for
;; flag_pic && !TARGET_ABSOLUTE_ABICALLS because they first load ;; !TARGET_ABSOLUTE_JUMPS because they first load the target address
;; the target address into a register. ;; into a register.
(define_attr "jal_macro" "no,yes" (define_attr "jal_macro" "no,yes"
(cond [(eq_attr "jal" "direct") (cond [(eq_attr "jal" "direct")
(symbol_ref "((TARGET_CALL_CLOBBERED_GP (symbol_ref "(TARGET_CALL_CLOBBERED_GP || !TARGET_ABSOLUTE_JUMPS
|| (flag_pic && !TARGET_ABSOLUTE_ABICALLS))
? JAL_MACRO_YES : JAL_MACRO_NO)") ? JAL_MACRO_YES : JAL_MACRO_NO)")
(eq_attr "jal" "indirect") (eq_attr "jal" "indirect")
(symbol_ref "(TARGET_CALL_CLOBBERED_GP (symbol_ref "(TARGET_CALL_CLOBBERED_GP
...@@ -498,9 +503,10 @@ ...@@ -498,9 +503,10 @@
(ne (symbol_ref "TARGET_MIPS16") (const_int 0))) (ne (symbol_ref "TARGET_MIPS16") (const_int 0)))
(const_int 8) (const_int 8)
;; Direct branch instructions have a range of [-0x40000,0x3fffc]. ;; Direct branch instructions have a range of [-0x20000,0x1fffc],
;; If a branch is outside this range, we have a choice of two ;; relative to the address of the delay slot. If a branch is
;; sequences. For PIC, an out-of-range branch like: ;; outside this range, we have a choice of two sequences.
;; For PIC, an out-of-range branch like:
;; ;;
;; bne r1,r2,target ;; bne r1,r2,target
;; dslot ;; dslot
...@@ -514,9 +520,6 @@ ...@@ -514,9 +520,6 @@
;; nop ;; nop
;; 1: ;; 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 ;; 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 instead of an la/jr pair. Since the target of this
;; jump is an absolute 28-bit bit address (the other bits ;; jump is an absolute 28-bit bit address (the other bits
...@@ -531,12 +534,21 @@ ...@@ -531,12 +534,21 @@
;; will add the length of the implicit nop. The values for ;; will add the length of the implicit nop. The values for
;; forward and backward branches will be different as well. ;; forward and backward branches will be different as well.
(eq_attr "type" "branch") (eq_attr "type" "branch")
(cond [(and (le (minus (match_dup 1) (pc)) (const_int 131064)) (cond [(and (le (minus (match_dup 0) (pc)) (const_int 131064))
(le (minus (pc) (match_dup 1)) (const_int 131068))) (le (minus (pc) (match_dup 0)) (const_int 131068)))
(const_int 4) (const_int 4)
(ne (symbol_ref "flag_pic") (const_int 0))
(const_int 24) ;; The non-PIC case: branch, first delay slot, and J.
] (const_int 12)) (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. ;; "Ghost" instructions occupy no space.
(eq_attr "type" "ghost") (eq_attr "type" "ghost")
...@@ -4754,12 +4766,12 @@ ...@@ -4754,12 +4766,12 @@
;; function address. ;; function address.
(define_insn_and_split "loadgp_newabi_<mode>" (define_insn_and_split "loadgp_newabi_<mode>"
[(set (match_operand:P 0 "register_operand" "=d") [(set (match_operand:P 0 "register_operand" "=d")
(unspec_volatile:P [(match_operand:P 1) (unspec:P [(match_operand:P 1)
(match_operand:P 2 "register_operand" "d")] (match_operand:P 2 "register_operand" "d")]
UNSPEC_LOADGP))] UNSPEC_LOADGP))]
"mips_current_loadgp_style () == LOADGP_NEWABI" "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 3))
(set (match_dup 0) (match_dup 4)) (set (match_dup 0) (match_dup 4))
(set (match_dup 0) (match_dup 5))] (set (match_dup 0) (match_dup 5))]
...@@ -4768,21 +4780,21 @@ ...@@ -4768,21 +4780,21 @@
operands[4] = gen_rtx_PLUS (Pmode, operands[0], operands[2]); operands[4] = gen_rtx_PLUS (Pmode, operands[0], operands[2]);
operands[5] = gen_rtx_LO_SUM (Pmode, operands[0], operands[1]); 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. ;; Likewise, for -mno-shared code. Operand 0 is the __gnu_local_gp symbol.
(define_insn_and_split "loadgp_absolute_<mode>" (define_insn_and_split "loadgp_absolute_<mode>"
[(set (match_operand:P 0 "register_operand" "=d") [(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" "mips_current_loadgp_style () == LOADGP_ABSOLUTE"
"#" { return mips_must_initialize_gp_p () ? "#" : ""; }
"" "&& mips_must_initialize_gp_p ()"
[(const_int 0)] [(const_int 0)]
{ {
mips_emit_move (operands[0], operands[1]); mips_emit_move (operands[0], operands[1]);
DONE; DONE;
} }
[(set_attr "length" "8")]) [(set_attr "type" "ghost")])
;; This blockage instruction prevents the gp load from being ;; This blockage instruction prevents the gp load from being
;; scheduled after an implicit use of gp. It also prevents ;; scheduled after an implicit use of gp. It also prevents
...@@ -4791,19 +4803,18 @@ ...@@ -4791,19 +4803,18 @@
[(unspec_volatile [(reg:SI 28)] UNSPEC_BLOCKAGE)] [(unspec_volatile [(reg:SI 28)] UNSPEC_BLOCKAGE)]
"" ""
"" ""
[(set_attr "type" "ghost") [(set_attr "type" "ghost")])
(set_attr "mode" "none")])
;; Initialize $gp for RTP PIC. Operand 0 is the __GOTT_BASE__ symbol ;; Initialize $gp for RTP PIC. Operand 0 is the __GOTT_BASE__ symbol
;; and operand 1 is the __GOTT_INDEX__ symbol. ;; and operand 1 is the __GOTT_INDEX__ symbol.
(define_insn_and_split "loadgp_rtp_<mode>" (define_insn_and_split "loadgp_rtp_<mode>"
[(set (match_operand:P 0 "register_operand" "=d") [(set (match_operand:P 0 "register_operand" "=d")
(unspec_volatile:P [(match_operand:P 1 "symbol_ref_operand") (unspec:P [(match_operand:P 1 "symbol_ref_operand")
(match_operand:P 2 "symbol_ref_operand")] (match_operand:P 2 "symbol_ref_operand")]
UNSPEC_LOADGP))] UNSPEC_LOADGP))]
"mips_current_loadgp_style () == LOADGP_RTP" "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) (high:P (match_dup 3)))
(set (match_dup 0) (unspec:P [(match_dup 0) (set (match_dup 0) (unspec:P [(match_dup 0)
(match_dup 3)] UNSPEC_LOAD_GOT)) (match_dup 3)] UNSPEC_LOAD_GOT))
...@@ -4813,37 +4824,72 @@ ...@@ -4813,37 +4824,72 @@
operands[3] = mips_unspec_address (operands[1], SYMBOL_ABSOLUTE); operands[3] = mips_unspec_address (operands[1], SYMBOL_ABSOLUTE);
operands[4] = mips_unspec_address (operands[2], SYMBOL_HALF); 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 ;; Initialize the global pointer for MIPS16 code. Operand 0 is the
;; global pointer and operand 1 is the MIPS16 register that holds ;; global pointer and operand 1 is the MIPS16 register that holds
;; the required value. ;; the required value.
(define_insn_and_split "copygp_mips16" (define_insn_and_split "copygp_mips16"
[(set (match_operand:SI 0 "register_operand" "=y") [(set (match_operand:SI 0 "register_operand" "=y")
(unspec_volatile:SI [(match_operand:SI 1 "register_operand" "d")] (unspec:SI [(match_operand:SI 1 "register_operand" "d")]
UNSPEC_COPYGP))] UNSPEC_COPYGP))]
"TARGET_MIPS16" "TARGET_MIPS16"
"#" { return mips_must_initialize_gp_p () ? "#" : ""; }
"&& reload_completed" "&& mips_must_initialize_gp_p ()"
[(set (match_dup 0) (match_dup 1))]) [(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 ;; Emit a .cprestore directive, which normally expands to a single store
;; instruction. Note that we continue to use .cprestore for explicit reloc ;; instruction. Operand 0 is a (possibly illegitimate) sp-based MEM
;; code so that jals inside inline asms will work correctly. ;; 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" (define_insn "cprestore"
[(unspec_volatile [(match_operand 0 "const_int_operand" "I,i") [(set (match_operand:SI 0 "cprestore_save_slot_operand" "=X,X")
(use (reg:SI 28))] (unspec:SI [(match_operand:SI 1 "const_int_operand" "I,i")
UNSPEC_CPRESTORE)] (reg:SI 28)]
"" UNSPEC_CPRESTORE))]
"TARGET_CPRESTORE_DIRECTIVE"
{ {
if (mips_nomacro.nesting_level > 0 && which_alternative == 1) 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 else
return ".cprestore\t%0"; return ".cprestore\t%1";
} }
[(set_attr "type" "store") [(set_attr "type" "store")
(set_attr "length" "4,12")]) (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 ;; Expand in-line code to clear the instruction cache between operand[0] and
;; operand[1]. ;; operand[1].
(define_expand "clear_cache" (define_expand "clear_cache"
...@@ -5149,100 +5195,94 @@ ...@@ -5149,100 +5195,94 @@
(define_insn "*branch_fp" (define_insn "*branch_fp"
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(match_operator 0 "equality_operator" (match_operator 1 "equality_operator"
[(match_operand:CC 2 "register_operand" "z") [(match_operand:CC 2 "register_operand" "z")
(const_int 0)]) (const_int 0)])
(label_ref (match_operand 1 "" "")) (label_ref (match_operand 0 "" ""))
(pc)))] (pc)))]
"TARGET_HARD_FLOAT" "TARGET_HARD_FLOAT"
{ {
return mips_output_conditional_branch (insn, operands, return mips_output_conditional_branch (insn, operands,
MIPS_BRANCH ("b%F0", "%Z2%1"), MIPS_BRANCH ("b%F1", "%Z2%0"),
MIPS_BRANCH ("b%W0", "%Z2%1")); MIPS_BRANCH ("b%W1", "%Z2%0"));
} }
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
(define_insn "*branch_fp_inverted" (define_insn "*branch_fp_inverted"
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(match_operator 0 "equality_operator" (match_operator 1 "equality_operator"
[(match_operand:CC 2 "register_operand" "z") [(match_operand:CC 2 "register_operand" "z")
(const_int 0)]) (const_int 0)])
(pc) (pc)
(label_ref (match_operand 1 "" ""))))] (label_ref (match_operand 0 "" ""))))]
"TARGET_HARD_FLOAT" "TARGET_HARD_FLOAT"
{ {
return mips_output_conditional_branch (insn, operands, return mips_output_conditional_branch (insn, operands,
MIPS_BRANCH ("b%W0", "%Z2%1"), MIPS_BRANCH ("b%W1", "%Z2%0"),
MIPS_BRANCH ("b%F0", "%Z2%1")); MIPS_BRANCH ("b%F1", "%Z2%0"));
} }
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
;; Conditional branches on ordered comparisons with zero. ;; Conditional branches on ordered comparisons with zero.
(define_insn "*branch_order<mode>" (define_insn "*branch_order<mode>"
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(match_operator 0 "order_operator" (match_operator 1 "order_operator"
[(match_operand:GPR 2 "register_operand" "d") [(match_operand:GPR 2 "register_operand" "d")
(const_int 0)]) (const_int 0)])
(label_ref (match_operand 1 "" "")) (label_ref (match_operand 0 "" ""))
(pc)))] (pc)))]
"!TARGET_MIPS16" "!TARGET_MIPS16"
{ return mips_output_order_conditional_branch (insn, operands, false); } { return mips_output_order_conditional_branch (insn, operands, false); }
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
(define_insn "*branch_order<mode>_inverted" (define_insn "*branch_order<mode>_inverted"
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(match_operator 0 "order_operator" (match_operator 1 "order_operator"
[(match_operand:GPR 2 "register_operand" "d") [(match_operand:GPR 2 "register_operand" "d")
(const_int 0)]) (const_int 0)])
(pc) (pc)
(label_ref (match_operand 1 "" ""))))] (label_ref (match_operand 0 "" ""))))]
"!TARGET_MIPS16" "!TARGET_MIPS16"
{ return mips_output_order_conditional_branch (insn, operands, true); } { return mips_output_order_conditional_branch (insn, operands, true); }
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
;; Conditional branch on equality comparison. ;; Conditional branch on equality comparison.
(define_insn "*branch_equality<mode>" (define_insn "*branch_equality<mode>"
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(match_operator 0 "equality_operator" (match_operator 1 "equality_operator"
[(match_operand:GPR 2 "register_operand" "d") [(match_operand:GPR 2 "register_operand" "d")
(match_operand:GPR 3 "reg_or_0_operand" "dJ")]) (match_operand:GPR 3 "reg_or_0_operand" "dJ")])
(label_ref (match_operand 1 "" "")) (label_ref (match_operand 0 "" ""))
(pc)))] (pc)))]
"!TARGET_MIPS16" "!TARGET_MIPS16"
{ {
return mips_output_conditional_branch (insn, operands, return mips_output_conditional_branch (insn, operands,
MIPS_BRANCH ("b%C0", "%2,%z3,%1"), MIPS_BRANCH ("b%C1", "%2,%z3,%0"),
MIPS_BRANCH ("b%N0", "%2,%z3,%1")); MIPS_BRANCH ("b%N1", "%2,%z3,%0"));
} }
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
(define_insn "*branch_equality<mode>_inverted" (define_insn "*branch_equality<mode>_inverted"
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(match_operator 0 "equality_operator" (match_operator 1 "equality_operator"
[(match_operand:GPR 2 "register_operand" "d") [(match_operand:GPR 2 "register_operand" "d")
(match_operand:GPR 3 "reg_or_0_operand" "dJ")]) (match_operand:GPR 3 "reg_or_0_operand" "dJ")])
(pc) (pc)
(label_ref (match_operand 1 "" ""))))] (label_ref (match_operand 0 "" ""))))]
"!TARGET_MIPS16" "!TARGET_MIPS16"
{ {
return mips_output_conditional_branch (insn, operands, return mips_output_conditional_branch (insn, operands,
MIPS_BRANCH ("b%N0", "%2,%z3,%1"), MIPS_BRANCH ("b%N1", "%2,%z3,%0"),
MIPS_BRANCH ("b%C0", "%2,%z3,%1")); MIPS_BRANCH ("b%C1", "%2,%z3,%0"));
} }
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
;; MIPS16 branches ;; MIPS16 branches
...@@ -5271,8 +5311,7 @@ ...@@ -5271,8 +5311,7 @@
return "bt%N0z\t%3"; return "bt%N0z\t%3";
} }
} }
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
(define_expand "cbranch<mode>4" (define_expand "cbranch<mode>4"
[(set (pc) [(set (pc)
...@@ -5313,42 +5352,40 @@ ...@@ -5313,42 +5352,40 @@
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(equality_op (zero_extract:GPR (equality_op (zero_extract:GPR
(match_operand:GPR 0 "register_operand" "d") (match_operand:GPR 1 "register_operand" "d")
(const_int 1) (const_int 1)
(match_operand 2 "const_int_operand" "")) (match_operand 2 "const_int_operand" ""))
(const_int 0)) (const_int 0))
(label_ref (match_operand 1 "")) (label_ref (match_operand 0 ""))
(pc)))] (pc)))]
"ISA_HAS_BBIT && UINTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)" "ISA_HAS_BBIT && UINTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)"
{ {
return return
mips_output_conditional_branch (insn, operands, mips_output_conditional_branch (insn, operands,
MIPS_BRANCH ("bbit<bbv>", "%0,%2,%1"), MIPS_BRANCH ("bbit<bbv>", "%1,%2,%0"),
MIPS_BRANCH ("bbit<bbinv>", "%0,%2,%1")); MIPS_BRANCH ("bbit<bbinv>", "%1,%2,%0"));
} }
[(set_attr "type" "branch") [(set_attr "type" "branch")
(set_attr "mode" "none")
(set_attr "branch_likely" "no")]) (set_attr "branch_likely" "no")])
(define_insn "*branch_bit<bbv><mode>_inverted" (define_insn "*branch_bit<bbv><mode>_inverted"
[(set (pc) [(set (pc)
(if_then_else (if_then_else
(equality_op (zero_extract:GPR (equality_op (zero_extract:GPR
(match_operand:GPR 0 "register_operand" "d") (match_operand:GPR 1 "register_operand" "d")
(const_int 1) (const_int 1)
(match_operand 2 "const_int_operand" "")) (match_operand 2 "const_int_operand" ""))
(const_int 0)) (const_int 0))
(pc) (pc)
(label_ref (match_operand 1 ""))))] (label_ref (match_operand 0 ""))))]
"ISA_HAS_BBIT && UINTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)" "ISA_HAS_BBIT && UINTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)"
{ {
return return
mips_output_conditional_branch (insn, operands, mips_output_conditional_branch (insn, operands,
MIPS_BRANCH ("bbit<bbinv>", "%0,%2,%1"), MIPS_BRANCH ("bbit<bbinv>", "%1,%2,%0"),
MIPS_BRANCH ("bbit<bbv>", "%0,%2,%1")); MIPS_BRANCH ("bbit<bbv>", "%1,%2,%0"));
} }
[(set_attr "type" "branch") [(set_attr "type" "branch")
(set_attr "mode" "none")
(set_attr "branch_likely" "no")]) (set_attr "branch_likely" "no")])
;; ;;
...@@ -5535,47 +5572,41 @@ ...@@ -5535,47 +5572,41 @@
;; Unconditional branches. ;; Unconditional branches.
(define_insn "jump" (define_expand "jump"
[(set (pc) [(set (pc)
(label_ref (match_operand 0 "" "")))] (label_ref (match_operand 0)))])
"!TARGET_MIPS16"
(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) mips_output_load_label (operands[0]);
return "%*b\t%l0%/"; return "%*jr\t%@%/%]";
else
{
output_asm_insn (mips_output_load_label (), operands);
return "%*jr\t%@%/%]";
}
} }
else
return "%*j\t%l0%/";
} }
[(set_attr "type" "jump") [(set_attr "type" "branch")])
(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)))])
;; We need a different insn for the mips16, because a mips16 branch ;; We need a different insn for the mips16, because a mips16 branch
;; does not have a delay slot. ;; does not have a delay slot.
(define_insn "" (define_insn "*jump_mips16"
[(set (pc) [(set (pc)
(label_ref (match_operand 0 "" "")))] (label_ref (match_operand 0 "" "")))]
"TARGET_MIPS16" "TARGET_MIPS16"
"b\t%l0" "b\t%l0"
[(set_attr "type" "branch") [(set_attr "type" "branch")])
(set_attr "mode" "none")])
(define_expand "indirect_jump" (define_expand "indirect_jump"
[(set (pc) (match_operand 0 "register_operand"))] [(set (pc) (match_operand 0 "register_operand"))]
...@@ -5876,14 +5907,28 @@ ...@@ -5876,14 +5907,28 @@
(clobber (match_scratch:SI 0 "=&d"))] (clobber (match_scratch:SI 0 "=&d"))]
"TARGET_CALL_CLOBBERED_GP" "TARGET_CALL_CLOBBERED_GP"
"#" "#"
"&& reload_completed" "&& epilogue_completed"
[(const_int 0)] [(const_int 0)]
{ {
mips_restore_gp (operands[0]); mips_restore_gp_from_cprestore_slot (operands[0]);
DONE; DONE;
} }
[(set_attr "type" "load") [(set_attr "type" "ghost")])
(set_attr "length" "12")])
;; 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")])
;; ;;
;; .................... ;; ....................
......
...@@ -244,6 +244,14 @@ ...@@ -244,6 +244,14 @@
} }
}) })
(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" (define_predicate "consttable_operand"
(match_test "CONSTANT_P (op)")) (match_test "CONSTANT_P (op)"))
......
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> 2009-09-14 Michael Meissner <meissner@linux.vnet.ibm.com>
PR target/41331 PR target/41331
......
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* 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;
}
/* 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;
}
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* 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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment