i386.c (size_cost): New static variable.
* i386.c (size_cost): New static variable. (override_function): Use size_cost when -Os is specified. * i386.c (ix86_expand_prologue): Set use_fast_prologue_epilogue properly; Use current_function_calls_eh_return. (ix86_expand_epilogue): Avoid dummy optimize_size tests; use leave to avoid depdendancy chain. * local-alloc.c (update_equiv_regs): Use CFG to iterate over INSN stream; get BB loop_depth instead of computing it from LOOP notes. * reg-stack.c (subst_stack_reg_pat): Handle reversal of conditional moves. From-SVN: r45415
This commit is contained in:
parent
76f81d9581
commit
2ab0437e79
|
@ -1,3 +1,18 @@
|
|||
Wed Sep 5 17:28:49 CEST 2001 Jan Hubicka <jh@suse.cz>
|
||||
|
||||
* i386.c (size_cost): New static variable.
|
||||
(override_function): Use size_cost when -Os is specified.
|
||||
|
||||
* i386.c (ix86_expand_prologue): Set use_fast_prologue_epilogue
|
||||
properly; Use current_function_calls_eh_return.
|
||||
(ix86_expand_epilogue): Avoid dummy optimize_size tests;
|
||||
use leave to avoid depdendancy chain.
|
||||
|
||||
* local-alloc.c (update_equiv_regs): Use CFG to iterate over INSN stream;
|
||||
get BB loop_depth instead of computing it from LOOP notes.
|
||||
|
||||
* reg-stack.c (subst_stack_reg_pat): Handle reversal of conditional moves.
|
||||
|
||||
2001-09-05 John David Anglin <dave@hiauly1.hia.nrc.ca>
|
||||
|
||||
* som.h (ASM_PREFERRED_EH_DATA_FORMAT): Define.
|
||||
|
|
|
@ -47,6 +47,38 @@ Boston, MA 02111-1307, USA. */
|
|||
#define CHECK_STACK_LIMIT -1
|
||||
#endif
|
||||
|
||||
/* Processor costs (relative to an add) */
|
||||
struct processor_costs size_cost = { /* costs for tunning for size */
|
||||
2, /* cost of an add instruction */
|
||||
3, /* cost of a lea instruction */
|
||||
2, /* variable shift costs */
|
||||
3, /* constant shift costs */
|
||||
3, /* cost of starting a multiply */
|
||||
0, /* cost of multiply per each bit set */
|
||||
3, /* cost of a divide/mod */
|
||||
0, /* "large" insn */
|
||||
2, /* MOVE_RATIO */
|
||||
2, /* cost for loading QImode using movzbl */
|
||||
{2, 2, 2}, /* cost of loading integer registers
|
||||
in QImode, HImode and SImode.
|
||||
Relative to reg-reg move (2). */
|
||||
{2, 2, 2}, /* cost of storing integer registers */
|
||||
2, /* cost of reg,reg fld/fst */
|
||||
{2, 2, 2}, /* cost of loading fp registers
|
||||
in SFmode, DFmode and XFmode */
|
||||
{2, 2, 2}, /* cost of loading integer registers */
|
||||
3, /* cost of moving MMX register */
|
||||
{3, 3}, /* cost of loading MMX registers
|
||||
in SImode and DImode */
|
||||
{3, 3}, /* cost of storing MMX registers
|
||||
in SImode and DImode */
|
||||
3, /* cost of moving SSE register */
|
||||
{3, 3, 3}, /* cost of loading SSE registers
|
||||
in SImode, DImode and TImode */
|
||||
{3, 3, 3}, /* cost of storing SSE registers
|
||||
in SImode, DImode and TImode */
|
||||
3, /* MMX or SSE register to integer */
|
||||
};
|
||||
/* Processor costs (relative to an add) */
|
||||
struct processor_costs i386_cost = { /* 386 specific costs */
|
||||
1, /* cost of an add instruction */
|
||||
|
@ -798,7 +830,10 @@ override_options ()
|
|||
error ("bad value (%s) for -mcpu= switch", ix86_cpu_string);
|
||||
}
|
||||
|
||||
ix86_cost = processor_target_table[ix86_cpu].cost;
|
||||
if (optimize_size)
|
||||
ix86_cost = &size_cost;
|
||||
else
|
||||
ix86_cost = processor_target_table[ix86_cpu].cost;
|
||||
target_flags |= processor_target_table[ix86_cpu].target_enable;
|
||||
target_flags &= ~processor_target_table[ix86_cpu].target_disable;
|
||||
|
||||
|
@ -2664,11 +2699,12 @@ ix86_expand_prologue ()
|
|||
int use_mov = 0;
|
||||
HOST_WIDE_INT allocate;
|
||||
|
||||
if (TARGET_PROLOGUE_USING_MOVE && !optimize_size)
|
||||
if (!optimize_size)
|
||||
{
|
||||
use_fast_prologue_epilogue
|
||||
= !expensive_function_p (FAST_PROLOGUE_INSN_COUNT);
|
||||
use_mov = use_fast_prologue_epilogue;
|
||||
if (TARGET_PROLOGUE_USING_MOVE)
|
||||
use_mov = use_fast_prologue_epilogue;
|
||||
}
|
||||
ix86_compute_frame_layout (&frame);
|
||||
|
||||
|
@ -2807,13 +2843,13 @@ ix86_expand_epilogue (style)
|
|||
and there is exactly one register to pop. This heruistic may need some
|
||||
tuning in future. */
|
||||
if ((!sp_valid && frame.nregs <= 1)
|
||||
|| (TARGET_EPILOGUE_USING_MOVE && !optimize_size
|
||||
|| (TARGET_EPILOGUE_USING_MOVE
|
||||
&& use_fast_prologue_epilogue
|
||||
&& (frame.nregs > 1 || frame.to_allocate))
|
||||
|| (frame_pointer_needed && !frame.nregs && frame.to_allocate)
|
||||
|| (frame_pointer_needed && TARGET_USE_LEAVE && !optimize_size
|
||||
|| (frame_pointer_needed && TARGET_USE_LEAVE
|
||||
&& use_fast_prologue_epilogue && frame.nregs == 1)
|
||||
|| style == 2)
|
||||
|| current_function_calls_eh_return)
|
||||
{
|
||||
/* Restore registers. We can use ebp or esp to address the memory
|
||||
locations. If both are available, default to ebp, since offsets
|
||||
|
@ -2899,7 +2935,11 @@ ix86_expand_epilogue (style)
|
|||
}
|
||||
if (frame_pointer_needed)
|
||||
{
|
||||
if (TARGET_64BIT)
|
||||
/* Leave results in shorter depdendancy chains on CPUs that are
|
||||
able to grok it fast. */
|
||||
if (TARGET_USE_LEAVE)
|
||||
emit_insn (TARGET_64BIT ? gen_leave_rex64 () : gen_leave ());
|
||||
else if (TARGET_64BIT)
|
||||
emit_insn (gen_popdi1 (hard_frame_pointer_rtx));
|
||||
else
|
||||
emit_insn (gen_popsi1 (hard_frame_pointer_rtx));
|
||||
|
|
|
@ -821,224 +821,211 @@ update_equiv_regs ()
|
|||
/* Scan the insns and find which registers have equivalences. Do this
|
||||
in a separate scan of the insns because (due to -fcse-follow-jumps)
|
||||
a register can be set below its use. */
|
||||
loop_depth = 0;
|
||||
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
||||
for (block = 0; block < n_basic_blocks; block++)
|
||||
{
|
||||
rtx note;
|
||||
rtx set;
|
||||
rtx dest, src;
|
||||
int regno;
|
||||
basic_block bb = BASIC_BLOCK (block);
|
||||
loop_depth = bb->loop_depth;
|
||||
|
||||
if (GET_CODE (insn) == NOTE)
|
||||
for (insn = bb->head; insn != NEXT_INSN (bb->end); insn = NEXT_INSN (insn))
|
||||
{
|
||||
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
|
||||
++loop_depth;
|
||||
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
|
||||
rtx note;
|
||||
rtx set;
|
||||
rtx dest, src;
|
||||
int regno;
|
||||
|
||||
if (! INSN_P (insn))
|
||||
continue;
|
||||
|
||||
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
|
||||
if (REG_NOTE_KIND (note) == REG_INC)
|
||||
no_equiv (XEXP (note, 0), note, NULL);
|
||||
|
||||
set = single_set (insn);
|
||||
|
||||
/* If this insn contains more (or less) than a single SET,
|
||||
only mark all destinations as having no known equivalence. */
|
||||
if (set == 0)
|
||||
{
|
||||
if (! loop_depth)
|
||||
abort ();
|
||||
--loop_depth;
|
||||
note_stores (PATTERN (insn), no_equiv, NULL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (! INSN_P (insn))
|
||||
continue;
|
||||
|
||||
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
|
||||
if (REG_NOTE_KIND (note) == REG_INC)
|
||||
no_equiv (XEXP (note, 0), note, NULL);
|
||||
|
||||
set = single_set (insn);
|
||||
|
||||
/* If this insn contains more (or less) than a single SET,
|
||||
only mark all destinations as having no known equivalence. */
|
||||
if (set == 0)
|
||||
{
|
||||
note_stores (PATTERN (insn), no_equiv, NULL);
|
||||
continue;
|
||||
}
|
||||
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
|
||||
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
|
||||
{
|
||||
rtx part = XVECEXP (PATTERN (insn), 0, i);
|
||||
if (part != set)
|
||||
note_stores (part, no_equiv, NULL);
|
||||
int i;
|
||||
|
||||
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
|
||||
{
|
||||
rtx part = XVECEXP (PATTERN (insn), 0, i);
|
||||
if (part != set)
|
||||
note_stores (part, no_equiv, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dest = SET_DEST (set);
|
||||
src = SET_SRC (set);
|
||||
dest = SET_DEST (set);
|
||||
src = SET_SRC (set);
|
||||
|
||||
/* If this sets a MEM to the contents of a REG that is only used
|
||||
in a single basic block, see if the register is always equivalent
|
||||
to that memory location and if moving the store from INSN to the
|
||||
insn that set REG is safe. If so, put a REG_EQUIV note on the
|
||||
initializing insn.
|
||||
/* If this sets a MEM to the contents of a REG that is only used
|
||||
in a single basic block, see if the register is always equivalent
|
||||
to that memory location and if moving the store from INSN to the
|
||||
insn that set REG is safe. If so, put a REG_EQUIV note on the
|
||||
initializing insn.
|
||||
|
||||
Don't add a REG_EQUIV note if the insn already has one. The existing
|
||||
REG_EQUIV is likely more useful than the one we are adding.
|
||||
Don't add a REG_EQUIV note if the insn already has one. The existing
|
||||
REG_EQUIV is likely more useful than the one we are adding.
|
||||
|
||||
If one of the regs in the address has reg_equiv[REGNO].replace set,
|
||||
then we can't add this REG_EQUIV note. The reg_equiv[REGNO].replace
|
||||
optimization may move the set of this register immediately before
|
||||
insn, which puts it after reg_equiv[REGNO].init_insns, and hence
|
||||
the mention in the REG_EQUIV note would be to an uninitialized
|
||||
pseudo. */
|
||||
/* ????? This test isn't good enough; we might see a MEM with a use of
|
||||
a pseudo register before we see its setting insn that will cause
|
||||
reg_equiv[].replace for that pseudo to be set.
|
||||
Equivalences to MEMs should be made in another pass, after the
|
||||
reg_equiv[].replace information has been gathered. */
|
||||
If one of the regs in the address has reg_equiv[REGNO].replace set,
|
||||
then we can't add this REG_EQUIV note. The reg_equiv[REGNO].replace
|
||||
optimization may move the set of this register immediately before
|
||||
insn, which puts it after reg_equiv[REGNO].init_insns, and hence
|
||||
the mention in the REG_EQUIV note would be to an uninitialized
|
||||
pseudo. */
|
||||
/* ????? This test isn't good enough; we might see a MEM with a use of
|
||||
a pseudo register before we see its setting insn that will cause
|
||||
reg_equiv[].replace for that pseudo to be set.
|
||||
Equivalences to MEMs should be made in another pass, after the
|
||||
reg_equiv[].replace information has been gathered. */
|
||||
|
||||
if (GET_CODE (dest) == MEM && GET_CODE (src) == REG
|
||||
&& (regno = REGNO (src)) >= FIRST_PSEUDO_REGISTER
|
||||
&& REG_BASIC_BLOCK (regno) >= 0
|
||||
&& REG_N_SETS (regno) == 1
|
||||
&& reg_equiv[regno].init_insns != 0
|
||||
&& reg_equiv[regno].init_insns != const0_rtx
|
||||
&& ! find_reg_note (XEXP (reg_equiv[regno].init_insns, 0),
|
||||
REG_EQUIV, NULL_RTX)
|
||||
&& ! contains_replace_regs (XEXP (dest, 0)))
|
||||
{
|
||||
rtx init_insn = XEXP (reg_equiv[regno].init_insns, 0);
|
||||
if (validate_equiv_mem (init_insn, src, dest)
|
||||
&& ! memref_used_between_p (dest, init_insn, insn))
|
||||
REG_NOTES (init_insn)
|
||||
= gen_rtx_EXPR_LIST (REG_EQUIV, dest, REG_NOTES (init_insn));
|
||||
}
|
||||
|
||||
/* We only handle the case of a pseudo register being set
|
||||
once, or always to the same value. */
|
||||
/* ??? The mn10200 port breaks if we add equivalences for
|
||||
values that need an ADDRESS_REGS register and set them equivalent
|
||||
to a MEM of a pseudo. The actual problem is in the over-conservative
|
||||
handling of INPADDR_ADDRESS / INPUT_ADDRESS / INPUT triples in
|
||||
calculate_needs, but we traditionally work around this problem
|
||||
here by rejecting equivalences when the destination is in a register
|
||||
that's likely spilled. This is fragile, of course, since the
|
||||
preferred class of a pseudo depends on all instructions that set
|
||||
or use it. */
|
||||
|
||||
if (GET_CODE (dest) != REG
|
||||
|| (regno = REGNO (dest)) < FIRST_PSEUDO_REGISTER
|
||||
|| reg_equiv[regno].init_insns == const0_rtx
|
||||
|| (CLASS_LIKELY_SPILLED_P (reg_preferred_class (regno))
|
||||
&& GET_CODE (src) == MEM))
|
||||
{
|
||||
/* This might be seting a SUBREG of a pseudo, a pseudo that is
|
||||
also set somewhere else to a constant. */
|
||||
note_stores (set, no_equiv, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
|
||||
|
||||
/* cse sometimes generates function invariants, but doesn't put a
|
||||
REG_EQUAL note on the insn. Since this note would be redundant,
|
||||
there's no point creating it earlier than here. */
|
||||
if (! note && ! rtx_varies_p (src, 0))
|
||||
REG_NOTES (insn)
|
||||
= note = gen_rtx_EXPR_LIST (REG_EQUAL, src, REG_NOTES (insn));
|
||||
|
||||
/* Don't bother considering a REG_EQUAL note containing an EXPR_LIST
|
||||
since it represents a function call */
|
||||
if (note && GET_CODE (XEXP (note, 0)) == EXPR_LIST)
|
||||
note = NULL_RTX;
|
||||
|
||||
if (REG_N_SETS (regno) != 1
|
||||
&& (! note
|
||||
|| rtx_varies_p (XEXP (note, 0), 0)
|
||||
|| (reg_equiv[regno].replacement
|
||||
&& ! rtx_equal_p (XEXP (note, 0),
|
||||
reg_equiv[regno].replacement))))
|
||||
{
|
||||
no_equiv (dest, set, NULL);
|
||||
continue;
|
||||
}
|
||||
/* Record this insn as initializing this register. */
|
||||
reg_equiv[regno].init_insns
|
||||
= gen_rtx_INSN_LIST (VOIDmode, insn, reg_equiv[regno].init_insns);
|
||||
|
||||
/* If this register is known to be equal to a constant, record that
|
||||
it is always equivalent to the constant. */
|
||||
if (note && ! rtx_varies_p (XEXP (note, 0), 0))
|
||||
PUT_MODE (note, (enum machine_mode) REG_EQUIV);
|
||||
|
||||
/* If this insn introduces a "constant" register, decrease the priority
|
||||
of that register. Record this insn if the register is only used once
|
||||
more and the equivalence value is the same as our source.
|
||||
|
||||
The latter condition is checked for two reasons: First, it is an
|
||||
indication that it may be more efficient to actually emit the insn
|
||||
as written (if no registers are available, reload will substitute
|
||||
the equivalence). Secondly, it avoids problems with any registers
|
||||
dying in this insn whose death notes would be missed.
|
||||
|
||||
If we don't have a REG_EQUIV note, see if this insn is loading
|
||||
a register used only in one basic block from a MEM. If so, and the
|
||||
MEM remains unchanged for the life of the register, add a REG_EQUIV
|
||||
note. */
|
||||
|
||||
note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
|
||||
|
||||
if (note == 0 && REG_BASIC_BLOCK (regno) >= 0
|
||||
&& GET_CODE (SET_SRC (set)) == MEM
|
||||
&& validate_equiv_mem (insn, dest, SET_SRC (set)))
|
||||
REG_NOTES (insn) = note = gen_rtx_EXPR_LIST (REG_EQUIV, SET_SRC (set),
|
||||
REG_NOTES (insn));
|
||||
|
||||
if (note)
|
||||
{
|
||||
int regno = REGNO (dest);
|
||||
|
||||
/* Record whether or not we created a REG_EQUIV note for a LABEL_REF.
|
||||
We might end up substituting the LABEL_REF for uses of the
|
||||
pseudo here or later. That kind of transformation may turn an
|
||||
indirect jump into a direct jump, in which case we must rerun the
|
||||
jump optimizer to ensure that the JUMP_LABEL fields are valid. */
|
||||
if (GET_CODE (XEXP (note, 0)) == LABEL_REF
|
||||
|| (GET_CODE (XEXP (note, 0)) == CONST
|
||||
&& GET_CODE (XEXP (XEXP (note, 0), 0)) == PLUS
|
||||
&& (GET_CODE (XEXP (XEXP (XEXP (note, 0), 0), 0))
|
||||
== LABEL_REF)))
|
||||
recorded_label_ref = 1;
|
||||
|
||||
reg_equiv[regno].replacement = XEXP (note, 0);
|
||||
reg_equiv[regno].src = src;
|
||||
reg_equiv[regno].loop_depth = loop_depth;
|
||||
|
||||
/* Don't mess with things live during setjmp. */
|
||||
if (REG_LIVE_LENGTH (regno) >= 0 && optimize)
|
||||
if (GET_CODE (dest) == MEM && GET_CODE (src) == REG
|
||||
&& (regno = REGNO (src)) >= FIRST_PSEUDO_REGISTER
|
||||
&& REG_BASIC_BLOCK (regno) >= 0
|
||||
&& REG_N_SETS (regno) == 1
|
||||
&& reg_equiv[regno].init_insns != 0
|
||||
&& reg_equiv[regno].init_insns != const0_rtx
|
||||
&& ! find_reg_note (XEXP (reg_equiv[regno].init_insns, 0),
|
||||
REG_EQUIV, NULL_RTX)
|
||||
&& ! contains_replace_regs (XEXP (dest, 0)))
|
||||
{
|
||||
/* Note that the statement below does not affect the priority
|
||||
in local-alloc! */
|
||||
REG_LIVE_LENGTH (regno) *= 2;
|
||||
rtx init_insn = XEXP (reg_equiv[regno].init_insns, 0);
|
||||
if (validate_equiv_mem (init_insn, src, dest)
|
||||
&& ! memref_used_between_p (dest, init_insn, insn))
|
||||
REG_NOTES (init_insn)
|
||||
= gen_rtx_EXPR_LIST (REG_EQUIV, dest, REG_NOTES (init_insn));
|
||||
}
|
||||
|
||||
/* We only handle the case of a pseudo register being set
|
||||
once, or always to the same value. */
|
||||
/* ??? The mn10200 port breaks if we add equivalences for
|
||||
values that need an ADDRESS_REGS register and set them equivalent
|
||||
to a MEM of a pseudo. The actual problem is in the over-conservative
|
||||
handling of INPADDR_ADDRESS / INPUT_ADDRESS / INPUT triples in
|
||||
calculate_needs, but we traditionally work around this problem
|
||||
here by rejecting equivalences when the destination is in a register
|
||||
that's likely spilled. This is fragile, of course, since the
|
||||
preferred class of a pseudo depends on all instructions that set
|
||||
or use it. */
|
||||
|
||||
if (GET_CODE (dest) != REG
|
||||
|| (regno = REGNO (dest)) < FIRST_PSEUDO_REGISTER
|
||||
|| reg_equiv[regno].init_insns == const0_rtx
|
||||
|| (CLASS_LIKELY_SPILLED_P (reg_preferred_class (regno))
|
||||
&& GET_CODE (src) == MEM))
|
||||
{
|
||||
/* This might be seting a SUBREG of a pseudo, a pseudo that is
|
||||
also set somewhere else to a constant. */
|
||||
note_stores (set, no_equiv, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
|
||||
|
||||
/* cse sometimes generates function invariants, but doesn't put a
|
||||
REG_EQUAL note on the insn. Since this note would be redundant,
|
||||
there's no point creating it earlier than here. */
|
||||
if (! note && ! rtx_varies_p (src, 0))
|
||||
REG_NOTES (insn)
|
||||
= note = gen_rtx_EXPR_LIST (REG_EQUAL, src, REG_NOTES (insn));
|
||||
|
||||
/* Don't bother considering a REG_EQUAL note containing an EXPR_LIST
|
||||
since it represents a function call */
|
||||
if (note && GET_CODE (XEXP (note, 0)) == EXPR_LIST)
|
||||
note = NULL_RTX;
|
||||
|
||||
if (REG_N_SETS (regno) != 1
|
||||
&& (! note
|
||||
|| rtx_varies_p (XEXP (note, 0), 0)
|
||||
|| (reg_equiv[regno].replacement
|
||||
&& ! rtx_equal_p (XEXP (note, 0),
|
||||
reg_equiv[regno].replacement))))
|
||||
{
|
||||
no_equiv (dest, set, NULL);
|
||||
continue;
|
||||
}
|
||||
/* Record this insn as initializing this register. */
|
||||
reg_equiv[regno].init_insns
|
||||
= gen_rtx_INSN_LIST (VOIDmode, insn, reg_equiv[regno].init_insns);
|
||||
|
||||
/* If this register is known to be equal to a constant, record that
|
||||
it is always equivalent to the constant. */
|
||||
if (note && ! rtx_varies_p (XEXP (note, 0), 0))
|
||||
PUT_MODE (note, (enum machine_mode) REG_EQUIV);
|
||||
|
||||
/* If this insn introduces a "constant" register, decrease the priority
|
||||
of that register. Record this insn if the register is only used once
|
||||
more and the equivalence value is the same as our source.
|
||||
|
||||
The latter condition is checked for two reasons: First, it is an
|
||||
indication that it may be more efficient to actually emit the insn
|
||||
as written (if no registers are available, reload will substitute
|
||||
the equivalence). Secondly, it avoids problems with any registers
|
||||
dying in this insn whose death notes would be missed.
|
||||
|
||||
If we don't have a REG_EQUIV note, see if this insn is loading
|
||||
a register used only in one basic block from a MEM. If so, and the
|
||||
MEM remains unchanged for the life of the register, add a REG_EQUIV
|
||||
note. */
|
||||
|
||||
note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
|
||||
|
||||
if (note == 0 && REG_BASIC_BLOCK (regno) >= 0
|
||||
&& GET_CODE (SET_SRC (set)) == MEM
|
||||
&& validate_equiv_mem (insn, dest, SET_SRC (set)))
|
||||
REG_NOTES (insn) = note = gen_rtx_EXPR_LIST (REG_EQUIV, SET_SRC (set),
|
||||
REG_NOTES (insn));
|
||||
|
||||
if (note)
|
||||
{
|
||||
int regno = REGNO (dest);
|
||||
|
||||
/* Record whether or not we created a REG_EQUIV note for a LABEL_REF.
|
||||
We might end up substituting the LABEL_REF for uses of the
|
||||
pseudo here or later. That kind of transformation may turn an
|
||||
indirect jump into a direct jump, in which case we must rerun the
|
||||
jump optimizer to ensure that the JUMP_LABEL fields are valid. */
|
||||
if (GET_CODE (XEXP (note, 0)) == LABEL_REF
|
||||
|| (GET_CODE (XEXP (note, 0)) == CONST
|
||||
&& GET_CODE (XEXP (XEXP (note, 0), 0)) == PLUS
|
||||
&& (GET_CODE (XEXP (XEXP (XEXP (note, 0), 0), 0))
|
||||
== LABEL_REF)))
|
||||
recorded_label_ref = 1;
|
||||
|
||||
reg_equiv[regno].replacement = XEXP (note, 0);
|
||||
reg_equiv[regno].src = src;
|
||||
reg_equiv[regno].loop_depth = loop_depth;
|
||||
|
||||
/* Don't mess with things live during setjmp. */
|
||||
if (REG_LIVE_LENGTH (regno) >= 0 && optimize)
|
||||
{
|
||||
/* Note that the statement below does not affect the priority
|
||||
in local-alloc! */
|
||||
REG_LIVE_LENGTH (regno) *= 2;
|
||||
|
||||
|
||||
/* If the register is referenced exactly twice, meaning it is
|
||||
set once and used once, indicate that the reference may be
|
||||
replaced by the equivalence we computed above. Do this
|
||||
even if the register is only used in one block so that
|
||||
dependencies can be handled where the last register is
|
||||
used in a different block (i.e. HIGH / LO_SUM sequences)
|
||||
and to reduce the number of registers alive across calls.
|
||||
/* If the register is referenced exactly twice, meaning it is
|
||||
set once and used once, indicate that the reference may be
|
||||
replaced by the equivalence we computed above. Do this
|
||||
even if the register is only used in one block so that
|
||||
dependencies can be handled where the last register is
|
||||
used in a different block (i.e. HIGH / LO_SUM sequences)
|
||||
and to reduce the number of registers alive across
|
||||
calls. */
|
||||
|
||||
It would be nice to use "loop_depth * 2" in the compare
|
||||
below. Unfortunately, LOOP_DEPTH need not be constant within
|
||||
a basic block so this would be too complicated.
|
||||
|
||||
This case normally occurs when a parameter is read from
|
||||
memory and then used exactly once, not in a loop. */
|
||||
|
||||
if (REG_N_REFS (regno) == 2
|
||||
&& (rtx_equal_p (XEXP (note, 0), src)
|
||||
|| ! equiv_init_varies_p (src))
|
||||
&& GET_CODE (insn) == INSN
|
||||
&& equiv_init_movable_p (PATTERN (insn), regno))
|
||||
reg_equiv[regno].replace = 1;
|
||||
if (REG_N_REFS (regno) == 2
|
||||
&& (rtx_equal_p (XEXP (note, 0), src)
|
||||
|| ! equiv_init_varies_p (src))
|
||||
&& GET_CODE (insn) == INSN
|
||||
&& equiv_init_movable_p (PATTERN (insn), regno))
|
||||
reg_equiv[regno].replace = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1050,134 +1037,118 @@ update_equiv_regs ()
|
|||
can't replace the reference, and the initialzing reference is
|
||||
within the same loop (or in an inner loop), then move the register
|
||||
initialization just before the use, so that they are in the same
|
||||
basic block.
|
||||
|
||||
Skip this optimization if loop_depth isn't initially zero since
|
||||
that indicates a mismatch between loop begin and loop end notes
|
||||
(i.e. gcc.dg/noncompile/920721-2.c). */
|
||||
block = n_basic_blocks - 1;
|
||||
for (insn = (loop_depth == 0) ? get_last_insn () : NULL_RTX;
|
||||
insn; insn = PREV_INSN (insn))
|
||||
basic block. */
|
||||
for (block = n_basic_blocks - 1; block >= 0; block--)
|
||||
{
|
||||
rtx link;
|
||||
basic_block bb = BASIC_BLOCK (block);
|
||||
|
||||
if (! INSN_P (insn))
|
||||
loop_depth = bb->loop_depth;
|
||||
for (insn = bb->end; insn != PREV_INSN (bb->head); insn = PREV_INSN (insn))
|
||||
{
|
||||
if (GET_CODE (insn) == NOTE)
|
||||
rtx link;
|
||||
|
||||
if (! INSN_P (insn))
|
||||
continue;
|
||||
|
||||
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
|
||||
{
|
||||
if (NOTE_INSN_BASIC_BLOCK_P (insn))
|
||||
block = NOTE_BASIC_BLOCK (insn)->index - 1;
|
||||
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
|
||||
if (REG_NOTE_KIND (link) == REG_DEAD
|
||||
/* Make sure this insn still refers to the register. */
|
||||
&& reg_mentioned_p (XEXP (link, 0), PATTERN (insn)))
|
||||
{
|
||||
if (! loop_depth)
|
||||
int regno = REGNO (XEXP (link, 0));
|
||||
rtx equiv_insn;
|
||||
|
||||
if (! reg_equiv[regno].replace
|
||||
|| reg_equiv[regno].loop_depth < loop_depth)
|
||||
continue;
|
||||
|
||||
/* reg_equiv[REGNO].replace gets set only when
|
||||
REG_N_REFS[REGNO] is 2, i.e. the register is set
|
||||
once and used once. (If it were only set, but not used,
|
||||
flow would have deleted the setting insns.) Hence
|
||||
there can only be one insn in reg_equiv[REGNO].init_insns. */
|
||||
if (reg_equiv[regno].init_insns == NULL_RTX
|
||||
|| XEXP (reg_equiv[regno].init_insns, 1) != NULL_RTX)
|
||||
abort ();
|
||||
--loop_depth;
|
||||
}
|
||||
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
|
||||
++loop_depth;
|
||||
}
|
||||
equiv_insn = XEXP (reg_equiv[regno].init_insns, 0);
|
||||
|
||||
continue;
|
||||
}
|
||||
/* We may not move instructions that can throw, since
|
||||
that changes basic block boundaries and we are not
|
||||
prepared to adjust the CFG to match. */
|
||||
if (can_throw_internal (equiv_insn))
|
||||
continue;
|
||||
|
||||
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
|
||||
{
|
||||
if (REG_NOTE_KIND (link) == REG_DEAD
|
||||
/* Make sure this insn still refers to the register. */
|
||||
&& reg_mentioned_p (XEXP (link, 0), PATTERN (insn)))
|
||||
{
|
||||
int regno = REGNO (XEXP (link, 0));
|
||||
rtx equiv_insn;
|
||||
|
||||
if (! reg_equiv[regno].replace
|
||||
|| reg_equiv[regno].loop_depth < loop_depth)
|
||||
continue;
|
||||
|
||||
/* reg_equiv[REGNO].replace gets set only when
|
||||
REG_N_REFS[REGNO] is 2, i.e. the register is set
|
||||
once and used once. (If it were only set, but not used,
|
||||
flow would have deleted the setting insns.) Hence
|
||||
there can only be one insn in reg_equiv[REGNO].init_insns. */
|
||||
if (reg_equiv[regno].init_insns == NULL_RTX
|
||||
|| XEXP (reg_equiv[regno].init_insns, 1) != NULL_RTX)
|
||||
abort ();
|
||||
equiv_insn = XEXP (reg_equiv[regno].init_insns, 0);
|
||||
|
||||
/* We may not move instructions that can throw, since
|
||||
that changes basic block boundaries and we are not
|
||||
prepared to adjust the CFG to match. */
|
||||
if (can_throw_internal (equiv_insn))
|
||||
continue;
|
||||
|
||||
if (asm_noperands (PATTERN (equiv_insn)) < 0
|
||||
&& validate_replace_rtx (regno_reg_rtx[regno],
|
||||
reg_equiv[regno].src, insn))
|
||||
{
|
||||
rtx equiv_link;
|
||||
rtx last_link;
|
||||
rtx note;
|
||||
|
||||
/* Find the last note. */
|
||||
for (last_link = link; XEXP (last_link, 1);
|
||||
last_link = XEXP (last_link, 1))
|
||||
;
|
||||
|
||||
/* Append the REG_DEAD notes from equiv_insn. */
|
||||
equiv_link = REG_NOTES (equiv_insn);
|
||||
while (equiv_link)
|
||||
if (asm_noperands (PATTERN (equiv_insn)) < 0
|
||||
&& validate_replace_rtx (regno_reg_rtx[regno],
|
||||
reg_equiv[regno].src, insn))
|
||||
{
|
||||
note = equiv_link;
|
||||
equiv_link = XEXP (equiv_link, 1);
|
||||
if (REG_NOTE_KIND (note) == REG_DEAD)
|
||||
rtx equiv_link;
|
||||
rtx last_link;
|
||||
rtx note;
|
||||
|
||||
/* Find the last note. */
|
||||
for (last_link = link; XEXP (last_link, 1);
|
||||
last_link = XEXP (last_link, 1))
|
||||
;
|
||||
|
||||
/* Append the REG_DEAD notes from equiv_insn. */
|
||||
equiv_link = REG_NOTES (equiv_insn);
|
||||
while (equiv_link)
|
||||
{
|
||||
remove_note (equiv_insn, note);
|
||||
XEXP (last_link, 1) = note;
|
||||
XEXP (note, 1) = NULL_RTX;
|
||||
last_link = note;
|
||||
note = equiv_link;
|
||||
equiv_link = XEXP (equiv_link, 1);
|
||||
if (REG_NOTE_KIND (note) == REG_DEAD)
|
||||
{
|
||||
remove_note (equiv_insn, note);
|
||||
XEXP (last_link, 1) = note;
|
||||
XEXP (note, 1) = NULL_RTX;
|
||||
last_link = note;
|
||||
}
|
||||
}
|
||||
|
||||
remove_death (regno, insn);
|
||||
REG_N_REFS (regno) = 0;
|
||||
REG_FREQ (regno) = 0;
|
||||
PUT_CODE (equiv_insn, NOTE);
|
||||
NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
|
||||
NOTE_SOURCE_FILE (equiv_insn) = 0;
|
||||
|
||||
reg_equiv[regno].init_insns
|
||||
= XEXP (reg_equiv[regno].init_insns, 1);
|
||||
}
|
||||
/* Move the initialization of the register to just before
|
||||
INSN. Update the flow information. */
|
||||
else if (PREV_INSN (insn) != equiv_insn)
|
||||
{
|
||||
rtx new_insn;
|
||||
|
||||
remove_death (regno, insn);
|
||||
REG_N_REFS (regno) = 0;
|
||||
REG_FREQ (regno) = 0;
|
||||
PUT_CODE (equiv_insn, NOTE);
|
||||
NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
|
||||
NOTE_SOURCE_FILE (equiv_insn) = 0;
|
||||
|
||||
reg_equiv[regno].init_insns
|
||||
= XEXP (reg_equiv[regno].init_insns, 1);
|
||||
}
|
||||
/* Move the initialization of the register to just before
|
||||
INSN. Update the flow information. */
|
||||
else if (PREV_INSN (insn) != equiv_insn)
|
||||
{
|
||||
rtx new_insn;
|
||||
new_insn = emit_insn_before (PATTERN (equiv_insn), insn);
|
||||
REG_NOTES (new_insn) = REG_NOTES (equiv_insn);
|
||||
REG_NOTES (equiv_insn) = 0;
|
||||
|
||||
new_insn = emit_insn_before (PATTERN (equiv_insn), insn);
|
||||
REG_NOTES (new_insn) = REG_NOTES (equiv_insn);
|
||||
REG_NOTES (equiv_insn) = 0;
|
||||
/* Make sure this insn is recognized before reload begins,
|
||||
otherwise eliminate_regs_in_insn will abort. */
|
||||
INSN_CODE (new_insn) = INSN_CODE (equiv_insn);
|
||||
|
||||
/* Make sure this insn is recognized before reload begins,
|
||||
otherwise eliminate_regs_in_insn will abort. */
|
||||
INSN_CODE (new_insn) = INSN_CODE (equiv_insn);
|
||||
PUT_CODE (equiv_insn, NOTE);
|
||||
NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
|
||||
NOTE_SOURCE_FILE (equiv_insn) = 0;
|
||||
|
||||
PUT_CODE (equiv_insn, NOTE);
|
||||
NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
|
||||
NOTE_SOURCE_FILE (equiv_insn) = 0;
|
||||
XEXP (reg_equiv[regno].init_insns, 0) = new_insn;
|
||||
|
||||
XEXP (reg_equiv[regno].init_insns, 0) = new_insn;
|
||||
REG_BASIC_BLOCK (regno) = block >= 0 ? block : 0;
|
||||
REG_N_CALLS_CROSSED (regno) = 0;
|
||||
REG_LIVE_LENGTH (regno) = 2;
|
||||
|
||||
REG_BASIC_BLOCK (regno) = block >= 0 ? block : 0;
|
||||
REG_N_CALLS_CROSSED (regno) = 0;
|
||||
REG_LIVE_LENGTH (regno) = 2;
|
||||
if (block >= 0 && insn == BLOCK_HEAD (block))
|
||||
BLOCK_HEAD (block) = PREV_INSN (insn);
|
||||
|
||||
if (block >= 0 && insn == BLOCK_HEAD (block))
|
||||
BLOCK_HEAD (block) = PREV_INSN (insn);
|
||||
|
||||
/* Remember to clear REGNO from all basic block's live
|
||||
info. */
|
||||
SET_REGNO_REG_SET (&cleared_regs, regno);
|
||||
clear_regnos++;
|
||||
/* Remember to clear REGNO from all basic block's live
|
||||
info. */
|
||||
SET_REGNO_REG_SET (&cleared_regs, regno);
|
||||
clear_regnos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -530,6 +530,7 @@ branch_prob ()
|
|||
total_num_times_called++;
|
||||
|
||||
flow_call_edges_add (NULL);
|
||||
add_noreturn_fake_exit_edges ();
|
||||
|
||||
/* We can't handle cyclic regions constructed using abnormal edges.
|
||||
To avoid these we replace every source of abnormal edge by a fake
|
||||
|
|
|
@ -1775,6 +1775,12 @@ subst_stack_regs_pat (insn, regstack, pat)
|
|||
case IF_THEN_ELSE:
|
||||
/* This insn requires the top of stack to be the destination. */
|
||||
|
||||
src1 = get_true_reg (&XEXP (pat_src, 1));
|
||||
src2 = get_true_reg (&XEXP (pat_src, 2));
|
||||
|
||||
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
|
||||
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
|
||||
|
||||
/* If the comparison operator is an FP comparison operator,
|
||||
it is handled correctly by compare_for_stack_reg () who
|
||||
will move the destination to the top of stack. But if the
|
||||
|
@ -1782,13 +1788,35 @@ subst_stack_regs_pat (insn, regstack, pat)
|
|||
have to handle it here. */
|
||||
if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG
|
||||
&& REGNO (*dest) != regstack->reg[regstack->top])
|
||||
emit_swap_insn (insn, regstack, *dest);
|
||||
{
|
||||
/* In case one of operands is the top of stack and the operands
|
||||
dies, it is safe to make it the destination operand by reversing
|
||||
the direction of cmove and avoid fxch. */
|
||||
if ((REGNO (*src1) == regstack->reg[regstack->top]
|
||||
&& src1_note)
|
||||
|| (REGNO (*src2) == regstack->reg[regstack->top]
|
||||
&& src2_note))
|
||||
{
|
||||
|
||||
src1 = get_true_reg (&XEXP (pat_src, 1));
|
||||
src2 = get_true_reg (&XEXP (pat_src, 2));
|
||||
/* We know that both sources "dies", as one dies and other
|
||||
is overwriten by the destination. Claim both sources
|
||||
to be dead, as the code bellow will properly pop the
|
||||
non-top-of-stack note and replace top-of-stack by the
|
||||
result by popping source first and then pushing result. */
|
||||
if (!src1_note)
|
||||
src1_note = REG_NOTES (insn)
|
||||
= gen_rtx_EXPR_LIST (REG_DEAD, *src1, REG_NOTES (insn));
|
||||
if (!src2_note)
|
||||
src2_note = REG_NOTES (insn)
|
||||
= gen_rtx_EXPR_LIST (REG_DEAD, *src2, REG_NOTES (insn));
|
||||
|
||||
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
|
||||
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
|
||||
/* i386 do have comparison always reversible. */
|
||||
PUT_CODE (XEXP (pat_src, 0),
|
||||
reversed_comparison_code (XEXP (pat_src, 0), insn));
|
||||
}
|
||||
else
|
||||
emit_swap_insn (insn, regstack, *dest);
|
||||
}
|
||||
|
||||
{
|
||||
rtx src_note [3];
|
||||
|
|
Loading…
Reference in New Issue