* Integrate alias analysis changes from jfc@mit.edu
* Makefile.in (OBJS): Add alias.o (alias.o): Add dependencies. * alias.c: New file. * sched.c: Remove alias analysis code. It lives in alias.c now. (reg_last_uses_size): Declare. (sched_analyze_2): Add new arguments to true_dependence. (sched_analyze_insn): Use reg_last_uses_size instead of max_reg. (schedule_block): Initialize reg_last_uses_size. (schedule_insns): Always call init_alias_analysis. * calls.c (expand_call): Note calls to malloc, calloc, and realloc; mark return value from such functions as a pointer and keep track of them for alias analysis. If a return value from a function is a pointer, mark it as such. * combine.c (distribute_notes): Handle REG_NOALIAS. * cse.c (struct write_data): Delete. No longer needed. (invalidate): Don't call set_nonvarying_address_components anymore. Use true_dependence to decide if an entry should be removed from the hash table. (invalidate_memory): Remove WRITES argument, simplify appropriately. Fix all callers. (note_mem_written): Similarly for WRITE_PTR argument. (invalidate_from_clobbers): Similarly for W argument. (invalidate_for_call): Remove memory elements from the hash table. (refers_to_mem_p, cse_rtx_addr_varies_p): Deleted. (cse_rtx_varies_p): New function. Derived from old cse_rtx_addr_varies_p. (cse_insn): Remove WRITES_MEMORY and INIT variables and all references. Don't call note_mem_written anymore. Stack pushes invalidate the stack pointer if PUSH_ROUNDING is defined. No longer need to call cse_rtx_addr_varies_p to decide if a MEM should be invalidated. (skipped_writes_memory): Remove variable. (invalidate_skipped_set): Simplify and wewrite to use invalidate_memory. (invalidate_skipped_block): Simplify for new alias analysis code. (cse_set_around_loop): Likewise. (cse_main): Call init_alias_analysis. * flags.h (flag_alias_check, flag_argument_noalias): Declare. * toplev.c (flag_alias_check, flag_argument_noalias): Define. (f_options): Add new alias checking arguments. (main): Set flag_alias_check when optimizing. * local_alloc (validate_equiv_mem_from_store): Add new arguments to true_dependence. (memref_referenced_p): Likewise. * loop.c (NUM_STORES): Increase to 30. (prescan_loop): Only non-constant calls set unknown_address_altered. (invariant_p): Add new arguments to true_dependence. (record_giv): Initialize unrolled and shared fields. (emit_iv_add_mult): Call record_base_value as needed. * loop.h (struct induction): Add unrolled and shared fields. * unroll.c (unroll_loop): Call record_base_value as needed. (copy_loop_body): Likewise. (final_biv_value): Likewise. (final_giv_value): Likewise. (find_splittable_regs): Likewise. Only create one new pseudo if we have multiple address GIVs that were combined with the same dst_reg GIV. Note when a new register is created due to unrolling. * rtl.c (reg_note_name): Add REG_NOALIAS. * rtl.h (enum reg_note): Similarly. (rtx_varies_p, may_trap_p, side_effects_p): Declare. (volatile_refs_p, volatile_insn_p, remove_note): Likewise. (note_stores, refers_to_regno_p, reg_overlap_mentioned_p): Likewise. (true_dependence, read_dependence, anti_dependence): Likewise. (output_dependence, init_alias_analysis, end_alias_analysis): Likewise. (mark_user_reg, mark_reg_pointer): Likewise. jfc's alias analysis code. From-SVN: r14768
This commit is contained in:
parent
5fa39bfeb7
commit
9ae8ffe751
|
@ -1,6 +1,72 @@
|
|||
Mon Aug 11 10:04:49 1997 Jeffrey A Law (law@cygnus.com)
|
||||
|
||||
* Integrate reload bugfix from Wilson which enables the PA port
|
||||
* Integrate alias analysis changes from jfc@mit.edu
|
||||
* Makefile.in (OBJS): Add alias.o
|
||||
(alias.o): Add dependencies.
|
||||
* alias.c: New file.
|
||||
* sched.c: Remove alias analysis code. It lives in alias.c now.
|
||||
(reg_last_uses_size): Declare.
|
||||
(sched_analyze_2): Add new arguments to true_dependence.
|
||||
(sched_analyze_insn): Use reg_last_uses_size instead of max_reg.
|
||||
(schedule_block): Initialize reg_last_uses_size.
|
||||
(schedule_insns): Always call init_alias_analysis.
|
||||
* calls.c (expand_call): Note calls to malloc, calloc, and realloc;
|
||||
mark return value from such functions as a pointer and keep track of
|
||||
them for alias analysis. If a return value from a function is a
|
||||
pointer, mark it as such.
|
||||
* combine.c (distribute_notes): Handle REG_NOALIAS.
|
||||
* cse.c (struct write_data): Delete. No longer needed.
|
||||
(invalidate): Don't call set_nonvarying_address_components anymore.
|
||||
Use true_dependence to decide if an entry should be removed from
|
||||
the hash table.
|
||||
(invalidate_memory): Remove WRITES argument, simplify appropriately.
|
||||
Fix all callers.
|
||||
(note_mem_written): Similarly for WRITE_PTR argument.
|
||||
(invalidate_from_clobbers): Similarly for W argument.
|
||||
(invalidate_for_call): Remove memory elements from the hash table.
|
||||
(refers_to_mem_p, cse_rtx_addr_varies_p): Deleted.
|
||||
(cse_rtx_varies_p): New function. Derived from old
|
||||
cse_rtx_addr_varies_p.
|
||||
(cse_insn): Remove WRITES_MEMORY and INIT variables and all references.
|
||||
Don't call note_mem_written anymore. Stack pushes invalidate the stack
|
||||
pointer if PUSH_ROUNDING is defined. No longer need to call
|
||||
cse_rtx_addr_varies_p to decide if a MEM should be invalidated.
|
||||
(skipped_writes_memory): Remove variable.
|
||||
(invalidate_skipped_set): Simplify and wewrite to use invalidate_memory.
|
||||
(invalidate_skipped_block): Simplify for new alias analysis code.
|
||||
(cse_set_around_loop): Likewise.
|
||||
(cse_main): Call init_alias_analysis.
|
||||
* flags.h (flag_alias_check, flag_argument_noalias): Declare.
|
||||
* toplev.c (flag_alias_check, flag_argument_noalias): Define.
|
||||
(f_options): Add new alias checking arguments.
|
||||
(main): Set flag_alias_check when optimizing.
|
||||
* local_alloc (validate_equiv_mem_from_store): Add new arguments
|
||||
to true_dependence.
|
||||
(memref_referenced_p): Likewise.
|
||||
* loop.c (NUM_STORES): Increase to 30.
|
||||
(prescan_loop): Only non-constant calls set unknown_address_altered.
|
||||
(invariant_p): Add new arguments to true_dependence.
|
||||
(record_giv): Initialize unrolled and shared fields.
|
||||
(emit_iv_add_mult): Call record_base_value as needed.
|
||||
* loop.h (struct induction): Add unrolled and shared fields.
|
||||
* unroll.c (unroll_loop): Call record_base_value as needed.
|
||||
(copy_loop_body): Likewise.
|
||||
(final_biv_value): Likewise.
|
||||
(final_giv_value): Likewise.
|
||||
(find_splittable_regs): Likewise. Only create one new pseudo
|
||||
if we have multiple address GIVs that were combined with the same
|
||||
dst_reg GIV. Note when a new register is created due to unrolling.
|
||||
* rtl.c (reg_note_name): Add REG_NOALIAS.
|
||||
* rtl.h (enum reg_note): Similarly.
|
||||
(rtx_varies_p, may_trap_p, side_effects_p): Declare.
|
||||
(volatile_refs_p, volatile_insn_p, remove_note): Likewise.
|
||||
(note_stores, refers_to_regno_p, reg_overlap_mentioned_p): Likewise.
|
||||
(true_dependence, read_dependence, anti_dependence): Likewise.
|
||||
(output_dependence, init_alias_analysis, end_alias_analysis): Likewise.
|
||||
(mark_user_reg, mark_reg_pointer): Likewise.
|
||||
|
||||
|
||||
* Integrate reload bugfix from Wilon which enables the PA port
|
||||
to bootstrap again.
|
||||
* reload1.c (reload): Sum needs for both OPADDR_ADDR and and
|
||||
OPERAND_ADDRESS when computing how many registers an insn needs.
|
||||
|
|
|
@ -554,7 +554,7 @@ OBJS = toplev.o version.o tree.o print-tree.o stor-layout.o fold-const.o \
|
|||
dbxout.o sdbout.o dwarfout.o dwarf2out.o xcoffout.o bitmap.o \
|
||||
integrate.o jump.o cse.o loop.o unroll.o flow.o stupid.o combine.o \
|
||||
regclass.o local-alloc.o global.o reload.o reload1.o caller-save.o \
|
||||
insn-peep.o reorg.o sched.o final.o recog.o reg-stack.o \
|
||||
insn-peep.o reorg.o alias.o sched.o final.o recog.o reg-stack.o \
|
||||
insn-opinit.o insn-recog.o insn-extract.o insn-output.o insn-emit.o \
|
||||
profile.o insn-attrtab.o $(out_object_file) getpwd.o convert.o $(EXTRA_OBJS)
|
||||
|
||||
|
@ -1321,6 +1321,8 @@ caller-save.o : caller-save.c $(CONFIG_H) $(RTL_H) flags.h \
|
|||
reorg.o : reorg.c $(CONFIG_H) $(RTL_H) conditions.h hard-reg-set.h \
|
||||
$(BASIC_BLOCK_H) regs.h insn-config.h insn-attr.h insn-flags.h recog.h \
|
||||
flags.h output.h
|
||||
alias.o : alias.c $(CONFIG_H) $(RTL_H) flags.h hard-reg-set.h regs.h \
|
||||
insn-codes.h
|
||||
sched.o : $(SCHED_PREFIX)sched.c $(CONFIG_H) $(RTL_H) $(BASIC_BLOCK_H) regs.h hard-reg-set.h \
|
||||
flags.h insn-config.h insn-attr.h
|
||||
final.o : final.c $(CONFIG_H) $(RTL_H) $(TREE_H) flags.h regs.h \
|
||||
|
|
File diff suppressed because it is too large
Load Diff
41
gcc/calls.c
41
gcc/calls.c
|
@ -563,6 +563,8 @@ expand_call (exp, target, ignore)
|
|||
|
||||
/* Nonzero if it is plausible that this is a call to alloca. */
|
||||
int may_be_alloca;
|
||||
/* Nonzero if this is a call to malloc or a related function. */
|
||||
int is_malloc;
|
||||
/* Nonzero if this is a call to setjmp or a related function. */
|
||||
int returns_twice;
|
||||
/* Nonzero if this is a call to `longjmp'. */
|
||||
|
@ -841,6 +843,7 @@ expand_call (exp, target, ignore)
|
|||
|
||||
returns_twice = 0;
|
||||
is_longjmp = 0;
|
||||
is_malloc = 0;
|
||||
|
||||
if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 15)
|
||||
{
|
||||
|
@ -880,6 +883,12 @@ expand_call (exp, target, ignore)
|
|||
else if (tname[0] == 'l' && tname[1] == 'o'
|
||||
&& ! strcmp (tname, "longjmp"))
|
||||
is_longjmp = 1;
|
||||
/* Only recognize malloc when alias analysis is enabled. */
|
||||
else if (flag_alias_check
|
||||
&& ((tname[0] == 'm' && ! strcmp(tname + 1, "alloc"))
|
||||
|| (tname[0] == 'c' && ! strcmp(tname + 1, "alloc"))
|
||||
|| (tname[0] == 'r' && ! strcmp(tname + 1, "ealloc"))))
|
||||
is_malloc = 1;
|
||||
}
|
||||
|
||||
if (may_be_alloca)
|
||||
|
@ -1366,7 +1375,7 @@ expand_call (exp, target, ignore)
|
|||
|
||||
/* Now we are about to start emitting insns that can be deleted
|
||||
if a libcall is deleted. */
|
||||
if (is_const)
|
||||
if (is_const || is_malloc)
|
||||
start_sequence ();
|
||||
|
||||
/* If we have no actual push instructions, or shouldn't use them,
|
||||
|
@ -1948,6 +1957,13 @@ expand_call (exp, target, ignore)
|
|||
rtx temp = gen_reg_rtx (GET_MODE (valreg));
|
||||
rtx insns;
|
||||
|
||||
/* Mark the return value as a pointer if needed. */
|
||||
if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
|
||||
{
|
||||
tree pointed_to = TREE_TYPE (TREE_TYPE (exp));
|
||||
mark_reg_pointer (temp, TYPE_ALIGN (pointed_to) / BITS_PER_UNIT);
|
||||
}
|
||||
|
||||
/* Construct an "equal form" for the value which mentions all the
|
||||
arguments in order as well as the function name. */
|
||||
#ifdef PUSH_ARGS_REVERSED
|
||||
|
@ -1974,6 +1990,29 @@ expand_call (exp, target, ignore)
|
|||
end_sequence ();
|
||||
emit_insns (insns);
|
||||
}
|
||||
else if (is_malloc)
|
||||
{
|
||||
rtx temp = gen_reg_rtx (GET_MODE (valreg));
|
||||
rtx last, insns;
|
||||
|
||||
/* The return value from a malloc-like function is a pointer. */
|
||||
if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
|
||||
mark_reg_pointer (temp, BIGGEST_ALIGNMENT / BITS_PER_UNIT);
|
||||
|
||||
emit_move_insn (temp, valreg);
|
||||
|
||||
/* The return value from a malloc-like function can not alias
|
||||
anything else. */
|
||||
last = get_last_insn ();
|
||||
REG_NOTES (last) =
|
||||
gen_rtx (EXPR_LIST, REG_NOALIAS, temp, REG_NOTES (last));
|
||||
|
||||
/* Write out the sequence. */
|
||||
insns = get_insns ();
|
||||
end_sequence ();
|
||||
emit_insns (insns);
|
||||
valreg = temp;
|
||||
}
|
||||
|
||||
/* For calls to `setjmp', etc., inform flow.c it should complain
|
||||
if nonvolatile values are live. */
|
||||
|
|
|
@ -11058,6 +11058,7 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
|
|||
case REG_EQUAL:
|
||||
case REG_EQUIV:
|
||||
case REG_NONNEG:
|
||||
case REG_NOALIAS:
|
||||
/* These notes say something about results of an insn. We can
|
||||
only support them if they used to be on I3 in which case they
|
||||
remain on I3. Otherwise they are ignored.
|
||||
|
|
432
gcc/cse.c
432
gcc/cse.c
|
@ -519,27 +519,6 @@ static struct table_elt *last_jump_equiv_class;
|
|||
|
||||
static int constant_pool_entries_cost;
|
||||
|
||||
/* Bits describing what kind of values in memory must be invalidated
|
||||
for a particular instruction. If all three bits are zero,
|
||||
no memory refs need to be invalidated. Each bit is more powerful
|
||||
than the preceding ones, and if a bit is set then the preceding
|
||||
bits are also set.
|
||||
|
||||
Here is how the bits are set:
|
||||
Pushing onto the stack invalidates only the stack pointer,
|
||||
writing at a fixed address invalidates only variable addresses,
|
||||
writing in a structure element at variable address
|
||||
invalidates all but scalar variables,
|
||||
and writing in anything else at variable address invalidates everything. */
|
||||
|
||||
struct write_data
|
||||
{
|
||||
int sp : 1; /* Invalidate stack pointer. */
|
||||
int var : 1; /* Invalidate variable addresses. */
|
||||
int nonscalar : 1; /* Invalidate all but scalar variables. */
|
||||
int all : 1; /* Invalidate all memory refs. */
|
||||
};
|
||||
|
||||
/* Define maximum length of a branch path. */
|
||||
|
||||
#define PATHLENGTH 10
|
||||
|
@ -626,9 +605,10 @@ static struct table_elt *insert PROTO((rtx, struct table_elt *, unsigned,
|
|||
static void merge_equiv_classes PROTO((struct table_elt *,
|
||||
struct table_elt *));
|
||||
static void invalidate PROTO((rtx, enum machine_mode));
|
||||
static int cse_rtx_varies_p PROTO((rtx));
|
||||
static void remove_invalid_refs PROTO((int));
|
||||
static void rehash_using_reg PROTO((rtx));
|
||||
static void invalidate_memory PROTO((struct write_data *));
|
||||
static void invalidate_memory PROTO((void));
|
||||
static void invalidate_for_call PROTO((void));
|
||||
static rtx use_related_value PROTO((rtx, struct table_elt *));
|
||||
static unsigned canon_hash PROTO((rtx, enum machine_mode));
|
||||
|
@ -638,9 +618,6 @@ static void set_nonvarying_address_components PROTO((rtx, int, rtx *,
|
|||
HOST_WIDE_INT *,
|
||||
HOST_WIDE_INT *));
|
||||
static int refers_to_p PROTO((rtx, rtx));
|
||||
static int refers_to_mem_p PROTO((rtx, rtx, HOST_WIDE_INT,
|
||||
HOST_WIDE_INT));
|
||||
static int cse_rtx_addr_varies_p PROTO((rtx));
|
||||
static rtx canon_reg PROTO((rtx, rtx));
|
||||
static void find_best_addr PROTO((rtx, rtx *));
|
||||
static enum rtx_code find_comparison_args PROTO((enum rtx_code, rtx *, rtx *,
|
||||
|
@ -656,8 +633,8 @@ static void record_jump_equiv PROTO((rtx, int));
|
|||
static void record_jump_cond PROTO((enum rtx_code, enum machine_mode,
|
||||
rtx, rtx, int));
|
||||
static void cse_insn PROTO((rtx, int));
|
||||
static void note_mem_written PROTO((rtx, struct write_data *));
|
||||
static void invalidate_from_clobbers PROTO((struct write_data *, rtx));
|
||||
static int note_mem_written PROTO((rtx));
|
||||
static void invalidate_from_clobbers PROTO((rtx));
|
||||
static rtx cse_process_notes PROTO((rtx, rtx));
|
||||
static void cse_around_loop PROTO((rtx));
|
||||
static void invalidate_skipped_set PROTO((rtx, rtx));
|
||||
|
@ -1534,8 +1511,6 @@ invalidate (x, full_mode)
|
|||
{
|
||||
register int i;
|
||||
register struct table_elt *p;
|
||||
rtx base;
|
||||
HOST_WIDE_INT start, end;
|
||||
|
||||
/* If X is a register, dependencies on its contents
|
||||
are recorded through the qty number mechanism.
|
||||
|
@ -1627,16 +1602,17 @@ invalidate (x, full_mode)
|
|||
if (full_mode == VOIDmode)
|
||||
full_mode = GET_MODE (x);
|
||||
|
||||
set_nonvarying_address_components (XEXP (x, 0), GET_MODE_SIZE (full_mode),
|
||||
&base, &start, &end);
|
||||
|
||||
for (i = 0; i < NBUCKETS; i++)
|
||||
{
|
||||
register struct table_elt *next;
|
||||
for (p = table[i]; p; p = next)
|
||||
{
|
||||
next = p->next_same_hash;
|
||||
if (refers_to_mem_p (p->exp, base, start, end))
|
||||
/* Invalidate ASM_OPERANDS which reference memory (this is easier
|
||||
than checking all the aliases). */
|
||||
if (p->in_memory
|
||||
&& (GET_CODE (p->exp) != MEM
|
||||
|| true_dependence (x, full_mode, p->exp, cse_rtx_varies_p)))
|
||||
remove_from_table (p, i);
|
||||
}
|
||||
}
|
||||
|
@ -1717,30 +1693,6 @@ rehash_using_reg (x)
|
|||
}
|
||||
}
|
||||
|
||||
/* Remove from the hash table all expressions that reference memory,
|
||||
or some of them as specified by *WRITES. */
|
||||
|
||||
static void
|
||||
invalidate_memory (writes)
|
||||
struct write_data *writes;
|
||||
{
|
||||
register int i;
|
||||
register struct table_elt *p, *next;
|
||||
int all = writes->all;
|
||||
int nonscalar = writes->nonscalar;
|
||||
|
||||
for (i = 0; i < NBUCKETS; i++)
|
||||
for (p = table[i]; p; p = next)
|
||||
{
|
||||
next = p->next_same_hash;
|
||||
if (p->in_memory
|
||||
&& (all
|
||||
|| (nonscalar && p->in_struct)
|
||||
|| cse_rtx_addr_varies_p (p->exp)))
|
||||
remove_from_table (p, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove from the hash table any expression that is a call-clobbered
|
||||
register. Also update their TICK values. */
|
||||
|
||||
|
@ -1778,6 +1730,12 @@ invalidate_for_call ()
|
|||
{
|
||||
next = p->next_same_hash;
|
||||
|
||||
if (p->in_memory)
|
||||
{
|
||||
remove_from_table (p, hash);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GET_CODE (p->exp) != REG
|
||||
|| REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
|
||||
continue;
|
||||
|
@ -2418,105 +2376,31 @@ set_nonvarying_address_components (addr, size, pbase, pstart, pend)
|
|||
*pend = end;
|
||||
}
|
||||
|
||||
/* Return 1 iff any subexpression of X refers to memory
|
||||
at an address of BASE plus some offset
|
||||
such that any of the bytes' offsets fall between START (inclusive)
|
||||
and END (exclusive).
|
||||
|
||||
The value is undefined if X is a varying address (as determined by
|
||||
cse_rtx_addr_varies_p). This function is not used in such cases.
|
||||
|
||||
When used in the cse pass, `qty_const' is nonzero, and it is used
|
||||
to treat an address that is a register with a known constant value
|
||||
as if it were that constant value.
|
||||
In the loop pass, `qty_const' is zero, so this is not done. */
|
||||
/* Return 1 if X has a value that can vary even between two
|
||||
executions of the program. 0 means X can be compared reliably
|
||||
against certain constants or near-constants. */
|
||||
|
||||
static int
|
||||
refers_to_mem_p (x, base, start, end)
|
||||
rtx x, base;
|
||||
HOST_WIDE_INT start, end;
|
||||
{
|
||||
register HOST_WIDE_INT i;
|
||||
register enum rtx_code code;
|
||||
register char *fmt;
|
||||
|
||||
repeat:
|
||||
if (x == 0)
|
||||
return 0;
|
||||
|
||||
code = GET_CODE (x);
|
||||
if (code == MEM)
|
||||
{
|
||||
register rtx addr = XEXP (x, 0); /* Get the address. */
|
||||
rtx mybase;
|
||||
HOST_WIDE_INT mystart, myend;
|
||||
|
||||
set_nonvarying_address_components (addr, GET_MODE_SIZE (GET_MODE (x)),
|
||||
&mybase, &mystart, &myend);
|
||||
|
||||
|
||||
/* refers_to_mem_p is never called with varying addresses.
|
||||
If the base addresses are not equal, there is no chance
|
||||
of the memory addresses conflicting. */
|
||||
if (! rtx_equal_p (mybase, base))
|
||||
return 0;
|
||||
|
||||
return myend > start && mystart < end;
|
||||
}
|
||||
|
||||
/* X does not match, so try its subexpressions. */
|
||||
|
||||
fmt = GET_RTX_FORMAT (code);
|
||||
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
||||
if (fmt[i] == 'e')
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
x = XEXP (x, 0);
|
||||
goto repeat;
|
||||
}
|
||||
else
|
||||
if (refers_to_mem_p (XEXP (x, i), base, start, end))
|
||||
return 1;
|
||||
}
|
||||
else if (fmt[i] == 'E')
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j < XVECLEN (x, i); j++)
|
||||
if (refers_to_mem_p (XVECEXP (x, i, j), base, start, end))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Nonzero if X refers to memory at a varying address;
|
||||
except that a register which has at the moment a known constant value
|
||||
isn't considered variable. */
|
||||
|
||||
static int
|
||||
cse_rtx_addr_varies_p (x)
|
||||
rtx x;
|
||||
cse_rtx_varies_p (x)
|
||||
register rtx x;
|
||||
{
|
||||
/* We need not check for X and the equivalence class being of the same
|
||||
mode because if X is equivalent to a constant in some mode, it
|
||||
doesn't vary in any mode. */
|
||||
|
||||
if (GET_CODE (x) == MEM
|
||||
&& GET_CODE (XEXP (x, 0)) == REG
|
||||
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
|
||||
&& GET_MODE (XEXP (x, 0)) == qty_mode[reg_qty[REGNO (XEXP (x, 0))]]
|
||||
&& qty_const[reg_qty[REGNO (XEXP (x, 0))]] != 0)
|
||||
if (GET_CODE (x) == REG
|
||||
&& REGNO_QTY_VALID_P (REGNO (x))
|
||||
&& GET_MODE (x) == qty_mode[reg_qty[REGNO (x)]]
|
||||
&& qty_const[reg_qty[REGNO (x)]] != 0)
|
||||
return 0;
|
||||
|
||||
if (GET_CODE (x) == MEM
|
||||
&& GET_CODE (XEXP (x, 0)) == PLUS
|
||||
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
|
||||
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
|
||||
&& REGNO_QTY_VALID_P (REGNO (XEXP (XEXP (x, 0), 0)))
|
||||
&& (GET_MODE (XEXP (XEXP (x, 0), 0))
|
||||
== qty_mode[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]])
|
||||
&& qty_const[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]])
|
||||
if (GET_CODE (x) == PLUS
|
||||
&& GET_CODE (XEXP (x, 1)) == CONST_INT
|
||||
&& GET_CODE (XEXP (x, 0)) == REG
|
||||
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
|
||||
&& (GET_MODE (XEXP (x, 0))
|
||||
== qty_mode[reg_qty[REGNO (XEXP (x, 0))]])
|
||||
&& qty_const[reg_qty[REGNO (XEXP (x, 0))]])
|
||||
return 0;
|
||||
|
||||
/* This can happen as the result of virtual register instantiation, if
|
||||
|
@ -2524,21 +2408,20 @@ cse_rtx_addr_varies_p (x)
|
|||
us a three instruction sequence, load large offset into a register,
|
||||
load fp minus a constant into a register, then a MEM which is the
|
||||
sum of the two `constant' registers. */
|
||||
if (GET_CODE (x) == MEM
|
||||
&& GET_CODE (XEXP (x, 0)) == PLUS
|
||||
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
|
||||
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == REG
|
||||
&& REGNO_QTY_VALID_P (REGNO (XEXP (XEXP (x, 0), 0)))
|
||||
&& (GET_MODE (XEXP (XEXP (x, 0), 0))
|
||||
== qty_mode[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]])
|
||||
&& qty_const[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]]
|
||||
&& REGNO_QTY_VALID_P (REGNO (XEXP (XEXP (x, 0), 1)))
|
||||
&& (GET_MODE (XEXP (XEXP (x, 0), 1))
|
||||
== qty_mode[reg_qty[REGNO (XEXP (XEXP (x, 0), 1))]])
|
||||
&& qty_const[reg_qty[REGNO (XEXP (XEXP (x, 0), 1))]])
|
||||
if (GET_CODE (x) == PLUS
|
||||
&& GET_CODE (XEXP (x, 0)) == REG
|
||||
&& GET_CODE (XEXP (x, 1)) == REG
|
||||
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
|
||||
&& (GET_MODE (XEXP (x, 0))
|
||||
== qty_mode[reg_qty[REGNO (XEXP (x, 0))]])
|
||||
&& qty_const[reg_qty[REGNO (XEXP (x, 0))]]
|
||||
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 1)))
|
||||
&& (GET_MODE (XEXP (x, 1))
|
||||
== qty_mode[reg_qty[REGNO (XEXP (x, 1))]])
|
||||
&& qty_const[reg_qty[REGNO (XEXP (x, 1))]])
|
||||
return 0;
|
||||
|
||||
return rtx_addr_varies_p (x);
|
||||
return rtx_varies_p (x);
|
||||
}
|
||||
|
||||
/* Canonicalize an expression:
|
||||
|
@ -6161,8 +6044,6 @@ cse_insn (insn, in_libcall_block)
|
|||
/* Records what this insn does to set CC0. */
|
||||
rtx this_insn_cc0 = 0;
|
||||
enum machine_mode this_insn_cc0_mode;
|
||||
struct write_data writes_memory;
|
||||
static struct write_data init = {0, 0, 0, 0};
|
||||
|
||||
rtx src_eqv = 0;
|
||||
struct table_elt *src_eqv_elt = 0;
|
||||
|
@ -6174,7 +6055,6 @@ cse_insn (insn, in_libcall_block)
|
|||
struct set *sets;
|
||||
|
||||
this_insn = insn;
|
||||
writes_memory = init;
|
||||
|
||||
/* Find all the SETs and CLOBBERs in this instruction.
|
||||
Record all the SETs in the array `set' and count them.
|
||||
|
@ -6276,15 +6156,11 @@ cse_insn (insn, in_libcall_block)
|
|||
}
|
||||
else if (GET_CODE (y) == CLOBBER)
|
||||
{
|
||||
/* If we clobber memory, take note of that,
|
||||
and canon the address.
|
||||
/* If we clobber memory, canon the address.
|
||||
This does nothing when a register is clobbered
|
||||
because we have already invalidated the reg. */
|
||||
if (GET_CODE (XEXP (y, 0)) == MEM)
|
||||
{
|
||||
canon_reg (XEXP (y, 0), NULL_RTX);
|
||||
note_mem_written (XEXP (y, 0), &writes_memory);
|
||||
}
|
||||
canon_reg (XEXP (y, 0), NULL_RTX);
|
||||
}
|
||||
else if (GET_CODE (y) == USE
|
||||
&& ! (GET_CODE (XEXP (y, 0)) == REG
|
||||
|
@ -6303,10 +6179,7 @@ cse_insn (insn, in_libcall_block)
|
|||
else if (GET_CODE (x) == CLOBBER)
|
||||
{
|
||||
if (GET_CODE (XEXP (x, 0)) == MEM)
|
||||
{
|
||||
canon_reg (XEXP (x, 0), NULL_RTX);
|
||||
note_mem_written (XEXP (x, 0), &writes_memory);
|
||||
}
|
||||
canon_reg (XEXP (x, 0), NULL_RTX);
|
||||
}
|
||||
|
||||
/* Canonicalize a USE of a pseudo register or memory location. */
|
||||
|
@ -6964,7 +6837,8 @@ cse_insn (insn, in_libcall_block)
|
|||
&& (src_folded == 0
|
||||
|| (GET_CODE (src_folded) != MEM
|
||||
&& ! src_folded_force_flag))
|
||||
&& GET_MODE_CLASS (mode) != MODE_CC)
|
||||
&& GET_MODE_CLASS (mode) != MODE_CC
|
||||
&& mode != VOIDmode)
|
||||
{
|
||||
src_folded_force_flag = 1;
|
||||
src_folded = trial;
|
||||
|
@ -7087,13 +6961,15 @@ cse_insn (insn, in_libcall_block)
|
|||
|
||||
if (GET_CODE (dest) == MEM)
|
||||
{
|
||||
#ifdef PUSH_ROUNDING
|
||||
/* Stack pushes invalidate the stack pointer. */
|
||||
rtx addr = XEXP (dest, 0);
|
||||
if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
|
||||
|| GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
|
||||
&& XEXP (addr, 0) == stack_pointer_rtx)
|
||||
invalidate (stack_pointer_rtx, Pmode);
|
||||
#endif
|
||||
dest = fold_rtx (dest, insn);
|
||||
|
||||
/* Decide whether we invalidate everything in memory,
|
||||
or just things at non-fixed places.
|
||||
Writing a large aggregate must invalidate everything
|
||||
because we don't know how long it is. */
|
||||
note_mem_written (dest, &writes_memory);
|
||||
}
|
||||
|
||||
/* Compute the hash code of the destination now,
|
||||
|
@ -7338,17 +7214,15 @@ cse_insn (insn, in_libcall_block)
|
|||
so that the destination goes into that class. */
|
||||
sets[i].src_elt = src_eqv_elt;
|
||||
|
||||
invalidate_from_clobbers (&writes_memory, x);
|
||||
invalidate_from_clobbers (x);
|
||||
|
||||
/* Some registers are invalidated by subroutine calls. Memory is
|
||||
invalidated by non-constant calls. */
|
||||
|
||||
if (GET_CODE (insn) == CALL_INSN)
|
||||
{
|
||||
static struct write_data everything = {0, 1, 1, 1};
|
||||
|
||||
if (! CONST_CALL_P (insn))
|
||||
invalidate_memory (&everything);
|
||||
invalidate_memory ();
|
||||
invalidate_for_call ();
|
||||
}
|
||||
|
||||
|
@ -7369,8 +7243,7 @@ cse_insn (insn, in_libcall_block)
|
|||
Needed for memory if this is a nonvarying address, unless
|
||||
we have just done an invalidate_memory that covers even those. */
|
||||
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
|
||||
|| (GET_CODE (dest) == MEM && ! writes_memory.all
|
||||
&& ! cse_rtx_addr_varies_p (dest)))
|
||||
|| GET_CODE (dest) == MEM)
|
||||
invalidate (dest, VOIDmode);
|
||||
else if (GET_CODE (dest) == STRICT_LOW_PART
|
||||
|| GET_CODE (dest) == ZERO_EXTRACT)
|
||||
|
@ -7638,76 +7511,33 @@ cse_insn (insn, in_libcall_block)
|
|||
prev_insn = insn;
|
||||
}
|
||||
|
||||
/* Store 1 in *WRITES_PTR for those categories of memory ref
|
||||
that must be invalidated when the expression WRITTEN is stored in.
|
||||
If WRITTEN is null, say everything must be invalidated. */
|
||||
|
||||
/* Remove from the ahsh table all expressions that reference memory. */
|
||||
static void
|
||||
note_mem_written (written, writes_ptr)
|
||||
rtx written;
|
||||
struct write_data *writes_ptr;
|
||||
invalidate_memory ()
|
||||
{
|
||||
static struct write_data everything = {0, 1, 1, 1};
|
||||
register int i;
|
||||
register struct table_elt *p, *next;
|
||||
|
||||
if (written == 0)
|
||||
*writes_ptr = everything;
|
||||
else if (GET_CODE (written) == MEM)
|
||||
{
|
||||
/* Pushing or popping the stack invalidates just the stack pointer. */
|
||||
rtx addr = XEXP (written, 0);
|
||||
if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
|
||||
|| GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
|
||||
&& GET_CODE (XEXP (addr, 0)) == REG
|
||||
&& REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
|
||||
{
|
||||
writes_ptr->sp = 1;
|
||||
return;
|
||||
}
|
||||
else if (GET_MODE (written) == BLKmode)
|
||||
*writes_ptr = everything;
|
||||
else if (cse_rtx_addr_varies_p (written))
|
||||
{
|
||||
/* A varying address that is a sum indicates an array element,
|
||||
and that's just as good as a structure element
|
||||
in implying that we need not invalidate scalar variables.
|
||||
However, we must allow QImode aliasing of scalars, because the
|
||||
ANSI C standard allows character pointers to alias anything.
|
||||
We must also allow AND addresses, because they may generate
|
||||
accesses outside the object being referenced. This is used to
|
||||
generate aligned addresses from unaligned addresses, for instance,
|
||||
the alpha storeqi_unaligned pattern. */
|
||||
if (! ((MEM_IN_STRUCT_P (written)
|
||||
|| GET_CODE (XEXP (written, 0)) == PLUS)
|
||||
&& GET_MODE (written) != QImode
|
||||
&& GET_CODE (XEXP (written, 0)) != AND))
|
||||
writes_ptr->all = 1;
|
||||
writes_ptr->nonscalar = 1;
|
||||
}
|
||||
writes_ptr->var = 1;
|
||||
}
|
||||
for (i = 0; i < NBUCKETS; i++)
|
||||
for (p = table[i]; p; p = next)
|
||||
{
|
||||
next = p->next_same_hash;
|
||||
if (p->in_memory)
|
||||
remove_from_table (p, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform invalidation on the basis of everything about an insn
|
||||
except for invalidating the actual places that are SET in it.
|
||||
This includes the places CLOBBERed, and anything that might
|
||||
alias with something that is SET or CLOBBERed.
|
||||
|
||||
W points to the writes_memory for this insn, a struct write_data
|
||||
saying which kinds of memory references must be invalidated.
|
||||
X is the pattern of the insn. */
|
||||
|
||||
static void
|
||||
invalidate_from_clobbers (w, x)
|
||||
struct write_data *w;
|
||||
rtx x;
|
||||
/* XXX ??? The name of this function bears little resemblance to
|
||||
what this function actually does. FIXME. */
|
||||
static int
|
||||
note_mem_written (addr)
|
||||
register rtx addr;
|
||||
{
|
||||
/* If W->var is not set, W specifies no action.
|
||||
If W->all is set, this step gets all memory refs
|
||||
so they can be ignored in the rest of this function. */
|
||||
if (w->var)
|
||||
invalidate_memory (w);
|
||||
|
||||
if (w->sp)
|
||||
/* Pushing or popping the stack invalidates just the stack pointer. */
|
||||
if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
|
||||
|| GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
|
||||
&& GET_CODE (XEXP (addr, 0)) == REG
|
||||
&& REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
|
||||
{
|
||||
if (reg_tick[STACK_POINTER_REGNUM] >= 0)
|
||||
reg_tick[STACK_POINTER_REGNUM]++;
|
||||
|
@ -7715,18 +7545,34 @@ invalidate_from_clobbers (w, x)
|
|||
/* This should be *very* rare. */
|
||||
if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM))
|
||||
invalidate (stack_pointer_rtx, VOIDmode);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform invalidation on the basis of everything about an insn
|
||||
except for invalidating the actual places that are SET in it.
|
||||
This includes the places CLOBBERed, and anything that might
|
||||
alias with something that is SET or CLOBBERed.
|
||||
|
||||
X is the pattern of the insn. */
|
||||
|
||||
static void
|
||||
invalidate_from_clobbers (x)
|
||||
rtx x;
|
||||
{
|
||||
if (GET_CODE (x) == CLOBBER)
|
||||
{
|
||||
rtx ref = XEXP (x, 0);
|
||||
|
||||
if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
|
||||
|| (GET_CODE (ref) == MEM && ! w->all))
|
||||
invalidate (ref, VOIDmode);
|
||||
else if (GET_CODE (ref) == STRICT_LOW_PART
|
||||
|| GET_CODE (ref) == ZERO_EXTRACT)
|
||||
invalidate (XEXP (ref, 0), GET_MODE (ref));
|
||||
if (ref)
|
||||
{
|
||||
if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
|
||||
|| GET_CODE (ref) == MEM)
|
||||
invalidate (ref, VOIDmode);
|
||||
else if (GET_CODE (ref) == STRICT_LOW_PART
|
||||
|| GET_CODE (ref) == ZERO_EXTRACT)
|
||||
invalidate (XEXP (ref, 0), GET_MODE (ref));
|
||||
}
|
||||
}
|
||||
else if (GET_CODE (x) == PARALLEL)
|
||||
{
|
||||
|
@ -7737,15 +7583,12 @@ invalidate_from_clobbers (w, x)
|
|||
if (GET_CODE (y) == CLOBBER)
|
||||
{
|
||||
rtx ref = XEXP (y, 0);
|
||||
if (ref)
|
||||
{
|
||||
if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
|
||||
|| (GET_CODE (ref) == MEM && !w->all))
|
||||
invalidate (ref, VOIDmode);
|
||||
else if (GET_CODE (ref) == STRICT_LOW_PART
|
||||
|| GET_CODE (ref) == ZERO_EXTRACT)
|
||||
invalidate (XEXP (ref, 0), GET_MODE (ref));
|
||||
}
|
||||
if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
|
||||
|| GET_CODE (ref) == MEM)
|
||||
invalidate (ref, VOIDmode);
|
||||
else if (GET_CODE (ref) == STRICT_LOW_PART
|
||||
|| GET_CODE (ref) == ZERO_EXTRACT)
|
||||
invalidate (XEXP (ref, 0), GET_MODE (ref));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7907,10 +7750,6 @@ cse_around_loop (loop_start)
|
|||
}
|
||||
}
|
||||
|
||||
/* Variable used for communications between the next two routines. */
|
||||
|
||||
static struct write_data skipped_writes_memory;
|
||||
|
||||
/* Process one SET of an insn that was skipped. We ignore CLOBBERs
|
||||
since they are done elsewhere. This function is called via note_stores. */
|
||||
|
||||
|
@ -7919,14 +7758,21 @@ invalidate_skipped_set (dest, set)
|
|||
rtx set;
|
||||
rtx dest;
|
||||
{
|
||||
if (GET_CODE (dest) == MEM)
|
||||
note_mem_written (dest, &skipped_writes_memory);
|
||||
enum rtx_code code = GET_CODE (dest);
|
||||
|
||||
/* There are times when an address can appear varying and be a PLUS
|
||||
during this scan when it would be a fixed address were we to know
|
||||
the proper equivalences. So promote "nonscalar" to be "all". */
|
||||
if (skipped_writes_memory.nonscalar)
|
||||
skipped_writes_memory.all = 1;
|
||||
if (code == MEM
|
||||
&& ! note_mem_written (dest) /* If this is not a stack push ... */
|
||||
/* There are times when an address can appear varying and be a PLUS
|
||||
during this scan when it would be a fixed address were we to know
|
||||
the proper equivalences. So invalidate all memory if there is
|
||||
a BLKmode or nonscalar memory reference or a reference to a
|
||||
variable address. */
|
||||
&& (MEM_IN_STRUCT_P (dest) || GET_MODE (dest) == BLKmode
|
||||
|| cse_rtx_varies_p (XEXP (dest, 0))))
|
||||
{
|
||||
invalidate_memory ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (GET_CODE (set) == CLOBBER
|
||||
#ifdef HAVE_cc0
|
||||
|
@ -7935,12 +7781,10 @@ invalidate_skipped_set (dest, set)
|
|||
|| dest == pc_rtx)
|
||||
return;
|
||||
|
||||
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
|
||||
|| (! skipped_writes_memory.all && ! cse_rtx_addr_varies_p (dest)))
|
||||
invalidate (dest, VOIDmode);
|
||||
else if (GET_CODE (dest) == STRICT_LOW_PART
|
||||
|| GET_CODE (dest) == ZERO_EXTRACT)
|
||||
if (code == STRICT_LOW_PART || code == ZERO_EXTRACT)
|
||||
invalidate (XEXP (dest, 0), GET_MODE (dest));
|
||||
else if (code == REG || code == SUBREG || code == MEM)
|
||||
invalidate (dest, VOIDmode);
|
||||
}
|
||||
|
||||
/* Invalidate all insns from START up to the end of the function or the
|
||||
|
@ -7952,8 +7796,6 @@ invalidate_skipped_block (start)
|
|||
rtx start;
|
||||
{
|
||||
rtx insn;
|
||||
static struct write_data init = {0, 0, 0, 0};
|
||||
static struct write_data everything = {0, 1, 1, 1};
|
||||
|
||||
for (insn = start; insn && GET_CODE (insn) != CODE_LABEL;
|
||||
insn = NEXT_INSN (insn))
|
||||
|
@ -7961,16 +7803,14 @@ invalidate_skipped_block (start)
|
|||
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
|
||||
continue;
|
||||
|
||||
skipped_writes_memory = init;
|
||||
|
||||
if (GET_CODE (insn) == CALL_INSN)
|
||||
{
|
||||
if (! CONST_CALL_P (insn))
|
||||
invalidate_memory ();
|
||||
invalidate_for_call ();
|
||||
skipped_writes_memory = everything;
|
||||
}
|
||||
|
||||
note_stores (PATTERN (insn), invalidate_skipped_set);
|
||||
invalidate_from_clobbers (&skipped_writes_memory, PATTERN (insn));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8020,10 +7860,6 @@ cse_set_around_loop (x, insn, loop_start)
|
|||
rtx loop_start;
|
||||
{
|
||||
struct table_elt *src_elt;
|
||||
static struct write_data init = {0, 0, 0, 0};
|
||||
struct write_data writes_memory;
|
||||
|
||||
writes_memory = init;
|
||||
|
||||
/* If this is a SET, see if we can replace SET_SRC, but ignore SETs that
|
||||
are setting PC or CC0 or whose SET_SRC is already a register. */
|
||||
|
@ -8083,16 +7919,11 @@ cse_set_around_loop (x, insn, loop_start)
|
|||
}
|
||||
|
||||
/* Now invalidate anything modified by X. */
|
||||
note_mem_written (SET_DEST (x), &writes_memory);
|
||||
note_mem_written (SET_DEST (x));
|
||||
|
||||
if (writes_memory.var)
|
||||
invalidate_memory (&writes_memory);
|
||||
|
||||
/* See comment on similar code in cse_insn for explanation of these
|
||||
tests. */
|
||||
/* See comment on similar code in cse_insn for explanation of these tests. */
|
||||
if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG
|
||||
|| (GET_CODE (SET_DEST (x)) == MEM && ! writes_memory.all
|
||||
&& ! cse_rtx_addr_varies_p (SET_DEST (x))))
|
||||
|| GET_CODE (SET_DEST (x)) == MEM)
|
||||
invalidate (SET_DEST (x), VOIDmode);
|
||||
else if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART
|
||||
|| GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
|
||||
|
@ -8342,6 +8173,7 @@ cse_main (f, nregs, after_loop, file)
|
|||
val.path_size = 0;
|
||||
|
||||
init_recog ();
|
||||
init_alias_analysis ();
|
||||
|
||||
max_reg = nregs;
|
||||
|
||||
|
|
13
gcc/flags.h
13
gcc/flags.h
|
@ -370,6 +370,19 @@ extern int flag_gnu_linker;
|
|||
/* Tag all structures with __attribute__(packed) */
|
||||
extern int flag_pack_struct;
|
||||
|
||||
/* 1 if alias checking is enabled: symbols do not alias each other
|
||||
and parameters do not alias the current stack frame. */
|
||||
extern int flag_alias_check;
|
||||
|
||||
/* This flag is only tested if alias checking is enabled.
|
||||
0 if pointer arguments may alias each other. True in C.
|
||||
1 if pointer arguments may not alias each other but may alias
|
||||
global variables.
|
||||
2 if pointer arguments may not alias each other and may not
|
||||
alias global variables. True in Fortran.
|
||||
The value is ignored if flag_alias_check is 0. */
|
||||
extern int flag_argument_noalias;
|
||||
|
||||
/* Emit code to check for stack overflow; also may cause large objects
|
||||
to be allocated dynamically. */
|
||||
extern int flag_stack_check;
|
||||
|
|
|
@ -543,7 +543,7 @@ validate_equiv_mem_from_store (dest, set)
|
|||
if ((GET_CODE (dest) == REG
|
||||
&& reg_overlap_mentioned_p (dest, equiv_mem))
|
||||
|| (GET_CODE (dest) == MEM
|
||||
&& true_dependence (dest, equiv_mem)))
|
||||
&& true_dependence (dest, VOIDmode, equiv_mem, rtx_varies_p)))
|
||||
equiv_mem_modified = 1;
|
||||
}
|
||||
|
||||
|
@ -632,7 +632,7 @@ memref_referenced_p (memref, x)
|
|||
reg_equiv_replacement[REGNO (x)]));
|
||||
|
||||
case MEM:
|
||||
if (true_dependence (memref, x))
|
||||
if (true_dependence (memref, VOIDmode, x, rtx_varies_p))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
|
|
14
gcc/loop.c
14
gcc/loop.c
|
@ -111,8 +111,7 @@ int *loop_number_exit_count;
|
|||
|
||||
unsigned HOST_WIDE_INT loop_n_iterations;
|
||||
|
||||
/* Nonzero if there is a subroutine call in the current loop.
|
||||
(unknown_address_altered is also nonzero in this case.) */
|
||||
/* Nonzero if there is a subroutine call in the current loop. */
|
||||
|
||||
static int loop_has_call;
|
||||
|
||||
|
@ -160,7 +159,7 @@ static char *moved_once;
|
|||
/* Array of MEMs that are stored in this loop. If there are too many to fit
|
||||
here, we just turn on unknown_address_altered. */
|
||||
|
||||
#define NUM_STORES 20
|
||||
#define NUM_STORES 30
|
||||
static rtx loop_store_mems[NUM_STORES];
|
||||
|
||||
/* Index of first available slot in above array. */
|
||||
|
@ -2199,7 +2198,8 @@ prescan_loop (start, end)
|
|||
}
|
||||
else if (GET_CODE (insn) == CALL_INSN)
|
||||
{
|
||||
unknown_address_altered = 1;
|
||||
if (! CONST_CALL_P (insn))
|
||||
unknown_address_altered = 1;
|
||||
loop_has_call = 1;
|
||||
}
|
||||
else
|
||||
|
@ -2795,7 +2795,7 @@ invariant_p (x)
|
|||
|
||||
/* See if there is any dependence between a store and this load. */
|
||||
for (i = loop_store_mems_idx - 1; i >= 0; i--)
|
||||
if (true_dependence (loop_store_mems[i], x))
|
||||
if (true_dependence (loop_store_mems[i], VOIDmode, x, rtx_varies_p))
|
||||
return 0;
|
||||
|
||||
/* It's not invalidated by a store in memory
|
||||
|
@ -4523,6 +4523,8 @@ record_giv (v, insn, src_reg, dest_reg, mult_val, add_val, benefit,
|
|||
v->final_value = 0;
|
||||
v->same_insn = 0;
|
||||
v->auto_inc_opt = 0;
|
||||
v->unrolled = 0;
|
||||
v->shared = 0;
|
||||
|
||||
/* The v->always_computable field is used in update_giv_derive, to
|
||||
determine whether a giv can be used to derive another giv. For a
|
||||
|
@ -5771,6 +5773,8 @@ emit_iv_add_mult (b, m, a, reg, insert_before)
|
|||
end_sequence ();
|
||||
|
||||
emit_insn_before (seq, insert_before);
|
||||
|
||||
record_base_value (REGNO (reg), b);
|
||||
}
|
||||
|
||||
/* Test whether A * B can be computed without
|
||||
|
|
|
@ -92,6 +92,9 @@ struct induction
|
|||
would probably lose. */
|
||||
unsigned auto_inc_opt : 1; /* 1 if this giv had its increment output next
|
||||
to it to try to form an auto-inc address. */
|
||||
unsigned unrolled : 1; /* 1 if new register has been allocated in
|
||||
unrolled loop. */
|
||||
unsigned shared : 1;
|
||||
int lifetime; /* Length of life of this giv */
|
||||
int times_used; /* # times this giv is used. */
|
||||
rtx derive_adjustment; /* If nonzero, is an adjustment to be
|
||||
|
|
|
@ -181,7 +181,7 @@ char *reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "REG_WAS_0",
|
|||
"REG_NONNEG", "REG_NO_CONFLICT", "REG_UNUSED",
|
||||
"REG_CC_SETTER", "REG_CC_USER", "REG_LABEL",
|
||||
"REG_DEP_ANTI", "REG_DEP_OUTPUT", "REG_BR_PROB",
|
||||
"REG_EXEC_COUNT" };
|
||||
"REG_EXEC_COUNT", "REG_NOALIAS" };
|
||||
|
||||
/* Allocate an rtx vector of N elements.
|
||||
Store the length, and initialize all elements to zero. */
|
||||
|
|
21
gcc/rtl.h
21
gcc/rtl.h
|
@ -317,7 +317,7 @@ enum reg_note { REG_DEAD = 1, REG_INC = 2, REG_EQUIV = 3, REG_WAS_0 = 4,
|
|||
REG_NONNEG = 8, REG_NO_CONFLICT = 9, REG_UNUSED = 10,
|
||||
REG_CC_SETTER = 11, REG_CC_USER = 12, REG_LABEL = 13,
|
||||
REG_DEP_ANTI = 14, REG_DEP_OUTPUT = 15, REG_BR_PROB = 16,
|
||||
REG_EXEC_COUNT = 17 };
|
||||
REG_EXEC_COUNT = 17, REG_NOALIAS = 18 };
|
||||
/* The base value for branch probability notes. */
|
||||
#define REG_BR_PROB_BASE 10000
|
||||
|
||||
|
@ -803,6 +803,16 @@ extern rtx gen_ble PROTO((rtx));
|
|||
extern rtx eliminate_constant_term PROTO((rtx, rtx *));
|
||||
extern rtx expand_complex_abs PROTO((enum machine_mode, rtx, rtx, int));
|
||||
extern enum machine_mode choose_hard_reg_mode PROTO((int, int));
|
||||
extern int rtx_varies_p PROTO((rtx));
|
||||
extern int may_trap_p PROTO((rtx));
|
||||
extern int side_effects_p PROTO((rtx));
|
||||
extern int volatile_refs_p PROTO((rtx));
|
||||
extern int volatile_insn_p PROTO((rtx));
|
||||
extern void remove_note PROTO((rtx, rtx));
|
||||
extern void note_stores PROTO((rtx, void (*)()));
|
||||
extern int refers_to_regno_p PROTO((int, int, rtx, rtx *));
|
||||
extern int reg_overlap_mentioned_p PROTO((rtx, rtx));
|
||||
|
||||
|
||||
/* Maximum number of parallel sets and clobbers in any insn in this fn.
|
||||
Always at least 3, since the combiner could put that many togetherm
|
||||
|
@ -959,3 +969,12 @@ extern char *regno_pointer_align;
|
|||
know what `enum tree_code' means. */
|
||||
|
||||
extern int rtx_to_tree_code PROTO((enum rtx_code));
|
||||
|
||||
extern int true_dependence PROTO((rtx, enum machine_mode, rtx, int (*)()));
|
||||
extern int read_dependence PROTO((rtx, rtx));
|
||||
extern int anti_dependence PROTO((rtx, rtx));
|
||||
extern int output_dependence PROTO((rtx, rtx));
|
||||
extern void init_alias_analysis PROTO((void));
|
||||
extern void end_alias_analysis PROTO((void));
|
||||
extern void mark_user_reg PROTO((rtx));
|
||||
extern void mark_reg_pointer PROTO((rtx, int));
|
||||
|
|
586
gcc/sched.c
586
gcc/sched.c
|
@ -127,6 +127,9 @@ Boston, MA 02111-1307, USA. */
|
|||
#include "insn-config.h"
|
||||
#include "insn-attr.h"
|
||||
|
||||
extern char *reg_known_equiv_p;
|
||||
extern rtx *reg_known_value;
|
||||
|
||||
#ifdef INSN_SCHEDULING
|
||||
/* Arrays set up by scheduling for the same respective purposes as
|
||||
similar-named arrays set up by flow analysis. We work with these
|
||||
|
@ -143,6 +146,7 @@ static int *sched_reg_live_length;
|
|||
such insn. Needed for new registers which may be introduced
|
||||
by splitting insns. */
|
||||
static rtx *reg_last_uses;
|
||||
static int reg_last_uses_size;
|
||||
static rtx *reg_last_sets;
|
||||
static regset reg_pending_sets;
|
||||
static int reg_pending_sets_all;
|
||||
|
@ -295,11 +299,6 @@ struct sometimes
|
|||
};
|
||||
|
||||
/* Forward declarations. */
|
||||
static rtx canon_rtx PROTO((rtx));
|
||||
static int rtx_equal_for_memref_p PROTO((rtx, rtx));
|
||||
static rtx find_symbolic_term PROTO((rtx));
|
||||
static int memrefs_conflict_p PROTO((int, rtx, int, rtx,
|
||||
HOST_WIDE_INT));
|
||||
static void add_dependence PROTO((rtx, rtx, enum reg_note));
|
||||
static void remove_dependence PROTO((rtx, rtx));
|
||||
static rtx find_insn_list PROTO((rtx, rtx));
|
||||
|
@ -349,567 +348,6 @@ void schedule_insns PROTO((FILE *));
|
|||
|
||||
#define SIZE_FOR_MODE(X) (GET_MODE_SIZE (GET_MODE (X)))
|
||||
|
||||
/* Vector indexed by N giving the initial (unchanging) value known
|
||||
for pseudo-register N. */
|
||||
static rtx *reg_known_value;
|
||||
|
||||
/* Vector recording for each reg_known_value whether it is due to a
|
||||
REG_EQUIV note. Future passes (viz., reload) may replace the
|
||||
pseudo with the equivalent expression and so we account for the
|
||||
dependences that would be introduced if that happens. */
|
||||
/* ??? This is a problem only on the Convex. The REG_EQUIV notes created in
|
||||
assign_parms mention the arg pointer, and there are explicit insns in the
|
||||
RTL that modify the arg pointer. Thus we must ensure that such insns don't
|
||||
get scheduled across each other because that would invalidate the REG_EQUIV
|
||||
notes. One could argue that the REG_EQUIV notes are wrong, but solving
|
||||
the problem in the scheduler will likely give better code, so we do it
|
||||
here. */
|
||||
static char *reg_known_equiv_p;
|
||||
|
||||
/* Indicates number of valid entries in reg_known_value. */
|
||||
static int reg_known_value_size;
|
||||
|
||||
static rtx
|
||||
canon_rtx (x)
|
||||
rtx x;
|
||||
{
|
||||
/* Recursively look for equivalences. */
|
||||
if (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER
|
||||
&& REGNO (x) <= reg_known_value_size)
|
||||
return reg_known_value[REGNO (x)] == x
|
||||
? x : canon_rtx (reg_known_value[REGNO (x)]);
|
||||
else if (GET_CODE (x) == PLUS)
|
||||
{
|
||||
rtx x0 = canon_rtx (XEXP (x, 0));
|
||||
rtx x1 = canon_rtx (XEXP (x, 1));
|
||||
|
||||
if (x0 != XEXP (x, 0) || x1 != XEXP (x, 1))
|
||||
{
|
||||
/* We can tolerate LO_SUMs being offset here; these
|
||||
rtl are used for nothing other than comparisons. */
|
||||
if (GET_CODE (x0) == CONST_INT)
|
||||
return plus_constant_for_output (x1, INTVAL (x0));
|
||||
else if (GET_CODE (x1) == CONST_INT)
|
||||
return plus_constant_for_output (x0, INTVAL (x1));
|
||||
return gen_rtx (PLUS, GET_MODE (x), x0, x1);
|
||||
}
|
||||
}
|
||||
/* This gives us much better alias analysis when called from
|
||||
the loop optimizer. Note we want to leave the original
|
||||
MEM alone, but need to return the canonicalized MEM with
|
||||
all the flags with their original values. */
|
||||
else if (GET_CODE (x) == MEM)
|
||||
{
|
||||
rtx copy = copy_rtx (x);
|
||||
XEXP (copy, 0) = canon_rtx (XEXP (copy, 0));
|
||||
x = copy;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Set up all info needed to perform alias analysis on memory references. */
|
||||
|
||||
void
|
||||
init_alias_analysis ()
|
||||
{
|
||||
int maxreg = max_reg_num ();
|
||||
rtx insn;
|
||||
rtx note;
|
||||
rtx set;
|
||||
|
||||
reg_known_value_size = maxreg;
|
||||
|
||||
reg_known_value
|
||||
= (rtx *) oballoc ((maxreg-FIRST_PSEUDO_REGISTER) * sizeof (rtx))
|
||||
- FIRST_PSEUDO_REGISTER;
|
||||
bzero ((char *) (reg_known_value + FIRST_PSEUDO_REGISTER),
|
||||
(maxreg-FIRST_PSEUDO_REGISTER) * sizeof (rtx));
|
||||
|
||||
reg_known_equiv_p
|
||||
= (char *) oballoc ((maxreg -FIRST_PSEUDO_REGISTER) * sizeof (char))
|
||||
- FIRST_PSEUDO_REGISTER;
|
||||
bzero (reg_known_equiv_p + FIRST_PSEUDO_REGISTER,
|
||||
(maxreg - FIRST_PSEUDO_REGISTER) * sizeof (char));
|
||||
|
||||
/* Fill in the entries with known constant values. */
|
||||
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
||||
if ((set = single_set (insn)) != 0
|
||||
&& GET_CODE (SET_DEST (set)) == REG
|
||||
&& REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
|
||||
&& (((note = find_reg_note (insn, REG_EQUAL, 0)) != 0
|
||||
&& REG_N_SETS (REGNO (SET_DEST (set))) == 1)
|
||||
|| (note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) != 0)
|
||||
&& GET_CODE (XEXP (note, 0)) != EXPR_LIST)
|
||||
{
|
||||
int regno = REGNO (SET_DEST (set));
|
||||
reg_known_value[regno] = XEXP (note, 0);
|
||||
reg_known_equiv_p[regno] = REG_NOTE_KIND (note) == REG_EQUIV;
|
||||
}
|
||||
|
||||
/* Fill in the remaining entries. */
|
||||
while (--maxreg >= FIRST_PSEUDO_REGISTER)
|
||||
if (reg_known_value[maxreg] == 0)
|
||||
reg_known_value[maxreg] = regno_reg_rtx[maxreg];
|
||||
}
|
||||
|
||||
/* Return 1 if X and Y are identical-looking rtx's.
|
||||
|
||||
We use the data in reg_known_value above to see if two registers with
|
||||
different numbers are, in fact, equivalent. */
|
||||
|
||||
static int
|
||||
rtx_equal_for_memref_p (x, y)
|
||||
rtx x, y;
|
||||
{
|
||||
register int i;
|
||||
register int j;
|
||||
register enum rtx_code code;
|
||||
register char *fmt;
|
||||
|
||||
if (x == 0 && y == 0)
|
||||
return 1;
|
||||
if (x == 0 || y == 0)
|
||||
return 0;
|
||||
x = canon_rtx (x);
|
||||
y = canon_rtx (y);
|
||||
|
||||
if (x == y)
|
||||
return 1;
|
||||
|
||||
code = GET_CODE (x);
|
||||
/* Rtx's of different codes cannot be equal. */
|
||||
if (code != GET_CODE (y))
|
||||
return 0;
|
||||
|
||||
/* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.
|
||||
(REG:SI x) and (REG:HI x) are NOT equivalent. */
|
||||
|
||||
if (GET_MODE (x) != GET_MODE (y))
|
||||
return 0;
|
||||
|
||||
/* REG, LABEL_REF, and SYMBOL_REF can be compared nonrecursively. */
|
||||
|
||||
if (code == REG)
|
||||
return REGNO (x) == REGNO (y);
|
||||
if (code == LABEL_REF)
|
||||
return XEXP (x, 0) == XEXP (y, 0);
|
||||
if (code == SYMBOL_REF)
|
||||
return XSTR (x, 0) == XSTR (y, 0);
|
||||
|
||||
/* For commutative operations, the RTX match if the operand match in any
|
||||
order. Also handle the simple binary and unary cases without a loop. */
|
||||
if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c')
|
||||
return ((rtx_equal_for_memref_p (XEXP (x, 0), XEXP (y, 0))
|
||||
&& rtx_equal_for_memref_p (XEXP (x, 1), XEXP (y, 1)))
|
||||
|| (rtx_equal_for_memref_p (XEXP (x, 0), XEXP (y, 1))
|
||||
&& rtx_equal_for_memref_p (XEXP (x, 1), XEXP (y, 0))));
|
||||
else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == '2')
|
||||
return (rtx_equal_for_memref_p (XEXP (x, 0), XEXP (y, 0))
|
||||
&& rtx_equal_for_memref_p (XEXP (x, 1), XEXP (y, 1)));
|
||||
else if (GET_RTX_CLASS (code) == '1')
|
||||
return rtx_equal_for_memref_p (XEXP (x, 0), XEXP (y, 0));
|
||||
|
||||
/* Compare the elements. If any pair of corresponding elements
|
||||
fail to match, return 0 for the whole things. */
|
||||
|
||||
fmt = GET_RTX_FORMAT (code);
|
||||
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
||||
{
|
||||
switch (fmt[i])
|
||||
{
|
||||
case 'w':
|
||||
if (XWINT (x, i) != XWINT (y, i))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
case 'i':
|
||||
if (XINT (x, i) != XINT (y, i))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
case 'E':
|
||||
/* Two vectors must have the same length. */
|
||||
if (XVECLEN (x, i) != XVECLEN (y, i))
|
||||
return 0;
|
||||
|
||||
/* And the corresponding elements must match. */
|
||||
for (j = 0; j < XVECLEN (x, i); j++)
|
||||
if (rtx_equal_for_memref_p (XVECEXP (x, i, j), XVECEXP (y, i, j)) == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
if (rtx_equal_for_memref_p (XEXP (x, i), XEXP (y, i)) == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
case 's':
|
||||
if (strcmp (XSTR (x, i), XSTR (y, i)))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
/* These are just backpointers, so they don't matter. */
|
||||
break;
|
||||
|
||||
case '0':
|
||||
break;
|
||||
|
||||
/* It is believed that rtx's at this level will never
|
||||
contain anything but integers and other rtx's,
|
||||
except for within LABEL_REFs and SYMBOL_REFs. */
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Given an rtx X, find a SYMBOL_REF or LABEL_REF within
|
||||
X and return it, or return 0 if none found. */
|
||||
|
||||
static rtx
|
||||
find_symbolic_term (x)
|
||||
rtx x;
|
||||
{
|
||||
register int i;
|
||||
register enum rtx_code code;
|
||||
register char *fmt;
|
||||
|
||||
code = GET_CODE (x);
|
||||
if (code == SYMBOL_REF || code == LABEL_REF)
|
||||
return x;
|
||||
if (GET_RTX_CLASS (code) == 'o')
|
||||
return 0;
|
||||
|
||||
fmt = GET_RTX_FORMAT (code);
|
||||
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
||||
{
|
||||
rtx t;
|
||||
|
||||
if (fmt[i] == 'e')
|
||||
{
|
||||
t = find_symbolic_term (XEXP (x, i));
|
||||
if (t != 0)
|
||||
return t;
|
||||
}
|
||||
else if (fmt[i] == 'E')
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return nonzero if X and Y (memory addresses) could reference the
|
||||
same location in memory. C is an offset accumulator. When
|
||||
C is nonzero, we are testing aliases between X and Y + C.
|
||||
XSIZE is the size in bytes of the X reference,
|
||||
similarly YSIZE is the size in bytes for Y.
|
||||
|
||||
If XSIZE or YSIZE is zero, we do not know the amount of memory being
|
||||
referenced (the reference was BLKmode), so make the most pessimistic
|
||||
assumptions.
|
||||
|
||||
We recognize the following cases of non-conflicting memory:
|
||||
|
||||
(1) addresses involving the frame pointer cannot conflict
|
||||
with addresses involving static variables.
|
||||
(2) static variables with different addresses cannot conflict.
|
||||
|
||||
Nice to notice that varying addresses cannot conflict with fp if no
|
||||
local variables had their addresses taken, but that's too hard now. */
|
||||
|
||||
/* ??? In Fortran, references to a array parameter can never conflict with
|
||||
another array parameter. */
|
||||
|
||||
static int
|
||||
memrefs_conflict_p (xsize, x, ysize, y, c)
|
||||
rtx x, y;
|
||||
int xsize, ysize;
|
||||
HOST_WIDE_INT c;
|
||||
{
|
||||
if (GET_CODE (x) == HIGH)
|
||||
x = XEXP (x, 0);
|
||||
else if (GET_CODE (x) == LO_SUM)
|
||||
x = XEXP (x, 1);
|
||||
else
|
||||
x = canon_rtx (x);
|
||||
if (GET_CODE (y) == HIGH)
|
||||
y = XEXP (y, 0);
|
||||
else if (GET_CODE (y) == LO_SUM)
|
||||
y = XEXP (y, 1);
|
||||
else
|
||||
y = canon_rtx (y);
|
||||
|
||||
if (rtx_equal_for_memref_p (x, y))
|
||||
return (xsize == 0 || ysize == 0
|
||||
|| (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0));
|
||||
|
||||
if (y == frame_pointer_rtx || y == hard_frame_pointer_rtx
|
||||
|| y == stack_pointer_rtx)
|
||||
{
|
||||
rtx t = y;
|
||||
int tsize = ysize;
|
||||
y = x; ysize = xsize;
|
||||
x = t; xsize = tsize;
|
||||
}
|
||||
|
||||
if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
|
||||
|| x == stack_pointer_rtx)
|
||||
{
|
||||
rtx y1;
|
||||
|
||||
if (CONSTANT_P (y))
|
||||
return 0;
|
||||
|
||||
if (GET_CODE (y) == PLUS
|
||||
&& canon_rtx (XEXP (y, 0)) == x
|
||||
&& (y1 = canon_rtx (XEXP (y, 1)))
|
||||
&& GET_CODE (y1) == CONST_INT)
|
||||
{
|
||||
c += INTVAL (y1);
|
||||
return (xsize == 0 || ysize == 0
|
||||
|| (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0));
|
||||
}
|
||||
|
||||
if (GET_CODE (y) == PLUS
|
||||
&& (y1 = canon_rtx (XEXP (y, 0)))
|
||||
&& CONSTANT_P (y1))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (GET_CODE (x) == PLUS)
|
||||
{
|
||||
/* The fact that X is canonicalized means that this
|
||||
PLUS rtx is canonicalized. */
|
||||
rtx x0 = XEXP (x, 0);
|
||||
rtx x1 = XEXP (x, 1);
|
||||
|
||||
if (GET_CODE (y) == PLUS)
|
||||
{
|
||||
/* The fact that Y is canonicalized means that this
|
||||
PLUS rtx is canonicalized. */
|
||||
rtx y0 = XEXP (y, 0);
|
||||
rtx y1 = XEXP (y, 1);
|
||||
|
||||
if (rtx_equal_for_memref_p (x1, y1))
|
||||
return memrefs_conflict_p (xsize, x0, ysize, y0, c);
|
||||
if (rtx_equal_for_memref_p (x0, y0))
|
||||
return memrefs_conflict_p (xsize, x1, ysize, y1, c);
|
||||
if (GET_CODE (x1) == CONST_INT)
|
||||
if (GET_CODE (y1) == CONST_INT)
|
||||
return memrefs_conflict_p (xsize, x0, ysize, y0,
|
||||
c - INTVAL (x1) + INTVAL (y1));
|
||||
else
|
||||
return memrefs_conflict_p (xsize, x0, ysize, y, c - INTVAL (x1));
|
||||
else if (GET_CODE (y1) == CONST_INT)
|
||||
return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1));
|
||||
|
||||
/* Handle case where we cannot understand iteration operators,
|
||||
but we notice that the base addresses are distinct objects. */
|
||||
x = find_symbolic_term (x);
|
||||
if (x == 0)
|
||||
return 1;
|
||||
y = find_symbolic_term (y);
|
||||
if (y == 0)
|
||||
return 1;
|
||||
return rtx_equal_for_memref_p (x, y);
|
||||
}
|
||||
else if (GET_CODE (x1) == CONST_INT)
|
||||
return memrefs_conflict_p (xsize, x0, ysize, y, c - INTVAL (x1));
|
||||
}
|
||||
else if (GET_CODE (y) == PLUS)
|
||||
{
|
||||
/* The fact that Y is canonicalized means that this
|
||||
PLUS rtx is canonicalized. */
|
||||
rtx y0 = XEXP (y, 0);
|
||||
rtx y1 = XEXP (y, 1);
|
||||
|
||||
if (GET_CODE (y1) == CONST_INT)
|
||||
return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1));
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (GET_CODE (x) == GET_CODE (y))
|
||||
switch (GET_CODE (x))
|
||||
{
|
||||
case MULT:
|
||||
{
|
||||
/* Handle cases where we expect the second operands to be the
|
||||
same, and check only whether the first operand would conflict
|
||||
or not. */
|
||||
rtx x0, y0;
|
||||
rtx x1 = canon_rtx (XEXP (x, 1));
|
||||
rtx y1 = canon_rtx (XEXP (y, 1));
|
||||
if (! rtx_equal_for_memref_p (x1, y1))
|
||||
return 1;
|
||||
x0 = canon_rtx (XEXP (x, 0));
|
||||
y0 = canon_rtx (XEXP (y, 0));
|
||||
if (rtx_equal_for_memref_p (x0, y0))
|
||||
return (xsize == 0 || ysize == 0
|
||||
|| (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0));
|
||||
|
||||
/* Can't properly adjust our sizes. */
|
||||
if (GET_CODE (x1) != CONST_INT)
|
||||
return 1;
|
||||
xsize /= INTVAL (x1);
|
||||
ysize /= INTVAL (x1);
|
||||
c /= INTVAL (x1);
|
||||
return memrefs_conflict_p (xsize, x0, ysize, y0, c);
|
||||
}
|
||||
}
|
||||
|
||||
if (CONSTANT_P (x))
|
||||
{
|
||||
if (GET_CODE (x) == CONST_INT && GET_CODE (y) == CONST_INT)
|
||||
{
|
||||
c += (INTVAL (y) - INTVAL (x));
|
||||
return (xsize == 0 || ysize == 0
|
||||
|| (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0));
|
||||
}
|
||||
|
||||
if (GET_CODE (x) == CONST)
|
||||
{
|
||||
if (GET_CODE (y) == CONST)
|
||||
return memrefs_conflict_p (xsize, canon_rtx (XEXP (x, 0)),
|
||||
ysize, canon_rtx (XEXP (y, 0)), c);
|
||||
else
|
||||
return memrefs_conflict_p (xsize, canon_rtx (XEXP (x, 0)),
|
||||
ysize, y, c);
|
||||
}
|
||||
if (GET_CODE (y) == CONST)
|
||||
return memrefs_conflict_p (xsize, x, ysize,
|
||||
canon_rtx (XEXP (y, 0)), c);
|
||||
|
||||
if (CONSTANT_P (y))
|
||||
return (rtx_equal_for_memref_p (x, y)
|
||||
&& (xsize == 0 || ysize == 0
|
||||
|| (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)));
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Functions to compute memory dependencies.
|
||||
|
||||
Since we process the insns in execution order, we can build tables
|
||||
to keep track of what registers are fixed (and not aliased), what registers
|
||||
are varying in known ways, and what registers are varying in unknown
|
||||
ways.
|
||||
|
||||
If both memory references are volatile, then there must always be a
|
||||
dependence between the two references, since their order can not be
|
||||
changed. A volatile and non-volatile reference can be interchanged
|
||||
though.
|
||||
|
||||
A MEM_IN_STRUCT reference at a non-QImode non-AND varying address can never
|
||||
conflict with a non-MEM_IN_STRUCT reference at a fixed address. We must
|
||||
allow QImode aliasing because the ANSI C standard allows character
|
||||
pointers to alias anything. We are assuming that characters are
|
||||
always QImode here. We also must allow AND addresses, because they may
|
||||
generate accesses outside the object being referenced. This is used to
|
||||
generate aligned addresses from unaligned addresses, for instance, the
|
||||
alpha storeqi_unaligned pattern. */
|
||||
|
||||
/* Read dependence: X is read after read in MEM takes place. There can
|
||||
only be a dependence here if both reads are volatile. */
|
||||
|
||||
int
|
||||
read_dependence (mem, x)
|
||||
rtx mem;
|
||||
rtx x;
|
||||
{
|
||||
return MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem);
|
||||
}
|
||||
|
||||
/* True dependence: X is read after store in MEM takes place. */
|
||||
|
||||
int
|
||||
true_dependence (mem, x)
|
||||
rtx mem;
|
||||
rtx x;
|
||||
{
|
||||
/* If X is an unchanging read, then it can't possibly conflict with any
|
||||
non-unchanging store. It may conflict with an unchanging write though,
|
||||
because there may be a single store to this address to initialize it.
|
||||
Just fall through to the code below to resolve the case where we have
|
||||
both an unchanging read and an unchanging write. This won't handle all
|
||||
cases optimally, but the possible performance loss should be
|
||||
negligible. */
|
||||
x = canon_rtx (x);
|
||||
mem = canon_rtx (mem);
|
||||
if (RTX_UNCHANGING_P (x) && ! RTX_UNCHANGING_P (mem))
|
||||
return 0;
|
||||
|
||||
return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem))
|
||||
|| (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0),
|
||||
SIZE_FOR_MODE (x), XEXP (x, 0), 0)
|
||||
&& ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem)
|
||||
&& GET_MODE (mem) != QImode
|
||||
&& GET_CODE (XEXP (mem, 0)) != AND
|
||||
&& ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x))
|
||||
&& ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x)
|
||||
&& GET_MODE (x) != QImode
|
||||
&& GET_CODE (XEXP (x, 0)) != AND
|
||||
&& ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem))));
|
||||
}
|
||||
|
||||
/* Anti dependence: X is written after read in MEM takes place. */
|
||||
|
||||
int
|
||||
anti_dependence (mem, x)
|
||||
rtx mem;
|
||||
rtx x;
|
||||
{
|
||||
/* If MEM is an unchanging read, then it can't possibly conflict with
|
||||
the store to X, because there is at most one store to MEM, and it must
|
||||
have occurred somewhere before MEM. */
|
||||
x = canon_rtx (x);
|
||||
mem = canon_rtx (mem);
|
||||
if (RTX_UNCHANGING_P (mem))
|
||||
return 0;
|
||||
|
||||
return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem))
|
||||
|| (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0),
|
||||
SIZE_FOR_MODE (x), XEXP (x, 0), 0)
|
||||
&& ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem)
|
||||
&& GET_MODE (mem) != QImode
|
||||
&& GET_CODE (XEXP (mem, 0)) != AND
|
||||
&& ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x))
|
||||
&& ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x)
|
||||
&& GET_MODE (x) != QImode
|
||||
&& GET_CODE (XEXP (x, 0)) != AND
|
||||
&& ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem))));
|
||||
}
|
||||
|
||||
/* Output dependence: X is written after store in MEM takes place. */
|
||||
|
||||
int
|
||||
output_dependence (mem, x)
|
||||
rtx mem;
|
||||
rtx x;
|
||||
{
|
||||
x = canon_rtx (x);
|
||||
mem = canon_rtx (mem);
|
||||
return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem))
|
||||
|| (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0),
|
||||
SIZE_FOR_MODE (x), XEXP (x, 0), 0)
|
||||
&& ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem)
|
||||
&& GET_MODE (mem) != QImode
|
||||
&& GET_CODE (XEXP (mem, 0)) != AND
|
||||
&& ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x))
|
||||
&& ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x)
|
||||
&& GET_MODE (x) != QImode
|
||||
&& GET_CODE (XEXP (x, 0)) != AND
|
||||
&& ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem))));
|
||||
}
|
||||
|
||||
/* Helper functions for instruction scheduling. */
|
||||
|
||||
/* Add ELEM wrapped in an INSN_LIST with reg note kind DEP_TYPE to the
|
||||
|
@ -1948,7 +1386,8 @@ sched_analyze_2 (x, insn)
|
|||
{
|
||||
/* If a dependency already exists, don't create a new one. */
|
||||
if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
|
||||
if (true_dependence (XEXP (pending_mem, 0), x))
|
||||
if (true_dependence (XEXP (pending_mem, 0), VOIDmode,
|
||||
x, rtx_varies_p))
|
||||
add_dependence (insn, XEXP (pending, 0), 0);
|
||||
|
||||
pending = XEXP (pending, 1);
|
||||
|
@ -2047,7 +1486,7 @@ sched_analyze_insn (x, insn, loop_notes)
|
|||
{
|
||||
register RTX_CODE code = GET_CODE (x);
|
||||
rtx link;
|
||||
int maxreg = max_reg_num ();
|
||||
int maxreg = reg_last_uses_size;
|
||||
int i;
|
||||
|
||||
if (code == SET || code == CLOBBER)
|
||||
|
@ -2084,7 +1523,7 @@ sched_analyze_insn (x, insn, loop_notes)
|
|||
|
||||
if (loop_notes)
|
||||
{
|
||||
int max_reg = max_reg_num ();
|
||||
int max_reg = reg_last_uses_size;
|
||||
rtx link;
|
||||
|
||||
for (i = 0; i < max_reg; i++)
|
||||
|
@ -2222,8 +1661,7 @@ sched_analyze (head, tail)
|
|||
if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == NOTE
|
||||
&& NOTE_LINE_NUMBER (NEXT_INSN (insn)) == NOTE_INSN_SETJMP)
|
||||
{
|
||||
int max_reg = max_reg_num ();
|
||||
for (i = 0; i < max_reg; i++)
|
||||
for (i = 0; i < reg_last_uses_size; i++)
|
||||
{
|
||||
for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
|
||||
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
|
||||
|
@ -3198,7 +2636,7 @@ schedule_block (b, file)
|
|||
fprintf (file, ";;\t -- basic block number %d from %d to %d --\n",
|
||||
b, INSN_UID (basic_block_head[b]), INSN_UID (basic_block_end[b]));
|
||||
|
||||
i = max_reg_num ();
|
||||
reg_last_uses_size = i = max_reg_num ();
|
||||
reg_last_uses = (rtx *) alloca (i * sizeof (rtx));
|
||||
bzero ((char *) reg_last_uses, i * sizeof (rtx));
|
||||
reg_last_sets = (rtx *) alloca (i * sizeof (rtx));
|
||||
|
@ -4819,7 +4257,6 @@ schedule_insns (dump_file)
|
|||
bb_live_regs = ALLOCA_REG_SET ();
|
||||
bzero ((char *) sched_reg_n_calls_crossed, max_regno * sizeof (int));
|
||||
bzero ((char *) sched_reg_live_length, max_regno * sizeof (int));
|
||||
init_alias_analysis ();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4827,9 +4264,8 @@ schedule_insns (dump_file)
|
|||
sched_reg_live_length = 0;
|
||||
bb_dead_regs = 0;
|
||||
bb_live_regs = 0;
|
||||
if (! flag_schedule_insns)
|
||||
init_alias_analysis ();
|
||||
}
|
||||
init_alias_analysis ();
|
||||
|
||||
if (write_symbols != NO_DEBUG)
|
||||
{
|
||||
|
|
16
gcc/toplev.c
16
gcc/toplev.c
|
@ -616,6 +616,17 @@ int flag_check_memory_usage = 0;
|
|||
-fcheck-memory-usage. */
|
||||
int flag_prefix_function_name = 0;
|
||||
|
||||
/* 1 if alias checking is on (by default, when -O). */
|
||||
int flag_alias_check = 0;
|
||||
|
||||
/* 0 if pointer arguments may alias each other. True in C.
|
||||
1 if pointer arguments may not alias each other but may alias
|
||||
global variables.
|
||||
2 if pointer arguments may not alias each other and may not
|
||||
alias global variables. True in Fortran.
|
||||
This defaults to 0 for C. */
|
||||
int flag_argument_noalias = 0;
|
||||
|
||||
/* Table of language-independent -f options.
|
||||
STRING is the option name. VARIABLE is the address of the variable.
|
||||
ON_VALUE is the value to store in VARIABLE
|
||||
|
@ -672,6 +683,10 @@ struct { char *string; int *variable; int on_value;} f_options[] =
|
|||
{"pack-struct", &flag_pack_struct, 1},
|
||||
{"stack-check", &flag_stack_check, 1},
|
||||
{"bytecode", &output_bytecode, 1},
|
||||
{"alias-check", &flag_alias_check, 1},
|
||||
{"argument-alias", &flag_argument_noalias, 0},
|
||||
{"argument-noalias", &flag_argument_noalias, 1},
|
||||
{"argument-noalias-global", &flag_argument_noalias, 2},
|
||||
{"check-memory-usage", &flag_check_memory_usage, 1},
|
||||
{"prefix-function-name", &flag_prefix_function_name, 1}
|
||||
};
|
||||
|
@ -3672,6 +3687,7 @@ main (argc, argv, envp)
|
|||
#ifdef CAN_DEBUG_WITHOUT_FP
|
||||
flag_omit_frame_pointer = 1;
|
||||
#endif
|
||||
flag_alias_check = 1;
|
||||
}
|
||||
|
||||
if (optimize >= 2)
|
||||
|
|
52
gcc/unroll.c
52
gcc/unroll.c
|
@ -1046,8 +1046,11 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
|
|||
|
||||
for (j = FIRST_PSEUDO_REGISTER; j < max_reg_before_loop; j++)
|
||||
if (local_regno[j])
|
||||
map->reg_map[j] = gen_reg_rtx (GET_MODE (regno_reg_rtx[j]));
|
||||
|
||||
{
|
||||
map->reg_map[j] = gen_reg_rtx (GET_MODE (regno_reg_rtx[j]));
|
||||
record_base_value (REGNO (map->reg_map[j]),
|
||||
regno_reg_rtx[j]);
|
||||
}
|
||||
/* The last copy needs the compare/branch insns at the end,
|
||||
so reset copy_end here if the loop ends with a conditional
|
||||
branch. */
|
||||
|
@ -1191,7 +1194,11 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
|
|||
|
||||
for (j = FIRST_PSEUDO_REGISTER; j < max_reg_before_loop; j++)
|
||||
if (local_regno[j])
|
||||
map->reg_map[j] = gen_reg_rtx (GET_MODE (regno_reg_rtx[j]));
|
||||
{
|
||||
map->reg_map[j] = gen_reg_rtx (GET_MODE (regno_reg_rtx[j]));
|
||||
record_base_value (REGNO (map->reg_map[j]),
|
||||
regno_reg_rtx[j]);
|
||||
}
|
||||
|
||||
/* If loop starts with a branch to the test, then fix it so that
|
||||
it points to the test of the first unrolled copy of the loop. */
|
||||
|
@ -1691,7 +1698,7 @@ copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration,
|
|||
/* Check for shared address givs, and avoid
|
||||
incrementing the shared pseudo reg more than
|
||||
once. */
|
||||
if (! tv->same_insn)
|
||||
if (! tv->same_insn && ! tv->shared)
|
||||
{
|
||||
/* tv->dest_reg may actually be a (PLUS (REG)
|
||||
(CONST)) here, so we must call plus_constant
|
||||
|
@ -1817,6 +1824,7 @@ copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration,
|
|||
tem = gen_reg_rtx (GET_MODE (giv_src_reg));
|
||||
giv_dest_reg = tem;
|
||||
map->reg_map[regno] = tem;
|
||||
record_base_value (REGNO (tem), giv_src_reg);
|
||||
}
|
||||
else
|
||||
map->reg_map[regno] = giv_src_reg;
|
||||
|
@ -2506,6 +2514,7 @@ find_splittable_regs (unroll_type, loop_start, loop_end, end_insert_before,
|
|||
{
|
||||
rtx tem = gen_reg_rtx (bl->biv->mode);
|
||||
|
||||
record_base_value (REGNO (tem), bl->biv->add_val);
|
||||
emit_insn_before (gen_move_insn (tem, bl->biv->src_reg),
|
||||
loop_start);
|
||||
|
||||
|
@ -2562,6 +2571,8 @@ find_splittable_regs (unroll_type, loop_start, loop_end, end_insert_before,
|
|||
this insn will always be executed, no matter how the loop
|
||||
exits. */
|
||||
rtx tem = gen_reg_rtx (bl->biv->mode);
|
||||
record_base_value (REGNO (tem), bl->biv->add_val);
|
||||
|
||||
emit_insn_before (gen_move_insn (tem, bl->biv->src_reg),
|
||||
loop_start);
|
||||
emit_insn_before (gen_move_insn (bl->biv->src_reg,
|
||||
|
@ -2737,6 +2748,7 @@ find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment,
|
|||
{
|
||||
rtx tem = gen_reg_rtx (bl->biv->mode);
|
||||
|
||||
record_base_value (REGNO (tem), bl->biv->add_val);
|
||||
emit_insn_before (gen_move_insn (tem, bl->biv->src_reg),
|
||||
loop_start);
|
||||
biv_initial_value = tem;
|
||||
|
@ -2778,6 +2790,7 @@ find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment,
|
|||
|| GET_CODE (XEXP (value, 1)) != CONST_INT))
|
||||
{
|
||||
rtx tem = gen_reg_rtx (v->mode);
|
||||
record_base_value (REGNO (tem), v->add_val);
|
||||
emit_iv_add_mult (bl->initial_value, v->mult_val,
|
||||
v->add_val, tem, loop_start);
|
||||
value = tem;
|
||||
|
@ -2796,16 +2809,9 @@ find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment,
|
|||
what we want for split addr regs. We always create a new
|
||||
register for the split addr giv, just to be safe. */
|
||||
|
||||
/* ??? If there are multiple address givs which have been
|
||||
combined with the same dest_reg giv, then we may only need
|
||||
one new register for them. Pulling out constants below will
|
||||
catch some of the common cases of this. Currently, I leave
|
||||
the work of simplifying multiple address givs to the
|
||||
following cse pass. */
|
||||
|
||||
/* As a special case, if we have multiple identical address givs
|
||||
within a single instruction, then we do use a single pseudo
|
||||
reg for both. This is necessary in case one is a match_dup
|
||||
/* If we have multiple identical address givs within a
|
||||
single instruction, then use a single pseudo reg for
|
||||
both. This is necessary in case one is a match_dup
|
||||
of the other. */
|
||||
|
||||
v->const_adjust = 0;
|
||||
|
@ -2818,12 +2824,26 @@ find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment,
|
|||
"Sharing address givs in insn %d\n",
|
||||
INSN_UID (v->insn));
|
||||
}
|
||||
/* If multiple address GIVs have been combined with the
|
||||
same dest_reg GIV, do not create a new register for
|
||||
each. */
|
||||
else if (unroll_type != UNROLL_COMPLETELY
|
||||
&& v->giv_type == DEST_ADDR
|
||||
&& v->same && v->same->giv_type == DEST_ADDR
|
||||
&& v->same->unrolled)
|
||||
{
|
||||
v->dest_reg = v->same->dest_reg;
|
||||
v->shared = 1;
|
||||
}
|
||||
else if (unroll_type != UNROLL_COMPLETELY)
|
||||
{
|
||||
/* If not completely unrolling the loop, then create a new
|
||||
register to hold the split value of the DEST_ADDR giv.
|
||||
Emit insn to initialize its value before loop start. */
|
||||
tem = gen_reg_rtx (v->mode);
|
||||
|
||||
rtx tem = gen_reg_rtx (v->mode);
|
||||
record_base_value (REGNO (tem), v->add_val);
|
||||
v->unrolled = 1;
|
||||
|
||||
/* If the address giv has a constant in its new_reg value,
|
||||
then this constant can be pulled out and put in value,
|
||||
|
@ -3130,6 +3150,7 @@ final_biv_value (bl, loop_start, loop_end)
|
|||
case it is needed later. */
|
||||
|
||||
tem = gen_reg_rtx (bl->biv->mode);
|
||||
record_base_value (REGNO (tem), bl->biv->add_val);
|
||||
/* Make sure loop_end is not the last insn. */
|
||||
if (NEXT_INSN (loop_end) == 0)
|
||||
emit_note_after (NOTE_INSN_DELETED, loop_end);
|
||||
|
@ -3228,6 +3249,7 @@ final_giv_value (v, loop_start, loop_end)
|
|||
|
||||
/* Put the final biv value in tem. */
|
||||
tem = gen_reg_rtx (bl->biv->mode);
|
||||
record_base_value (REGNO (tem), bl->biv->add_val);
|
||||
emit_iv_add_mult (increment, GEN_INT (loop_n_iterations),
|
||||
bl->initial_value, tem, insert_before);
|
||||
|
||||
|
|
Loading…
Reference in New Issue