re PR middle-end/31150 (Not promoting an whole array to be static const)

PR middle-end/31150
	* dse.c (struct store_info): Add const_rhs field.
	(clear_rhs_from_active_local_stores): Clear also const_rhs.
	(record_store): Try also cselib_expand_value_rtx to get a constant.
	(find_shift_sequence, get_stored_val): Use const_rhs instead of
	rhs if worthwhile.
	* cselib.c (cselib_record_sets): If !cselib_record_memory and
	there is just one set from read-only MEM, look at REG_EQUAL or
	REG_EQUIV note.

	* dse.c (struct store_info): Add redundant_reason field.
	(record_store): When storing the same constant as has been
	stored by an earlier store, set redundant_reason field
	to the earlier store's insn_info_t.  Don't delete cannot_delete
	insns.
	(find_shift_sequence): Remove read_info argument, add read_mode
	and require_cst arguments.  Return early if require_cst and
	constant wouldn't be returned.
	(get_stored_val): New function.
	(replace_read): Use it.
	(scan_insn): Put even cannot_delete insns with exactly 1 store
	into active_local_stores.
	(dse_step1): Don't delete cannot_delete insns.  Remove redundant
	constant stores if contains_cselib_groups and earlier store storing
	the same value hasn't been eliminated.
	(dse_step6): Renamed to dse_step7.  New function.
	(dse_step7): Renamed from dse_step6.
	(rest_of_handle_dse): Call dse_step6 and dse_step7 at the end.
	* cselib.c (cselib_expand_value_rtx): Don't wrap CONST_INTs
	into CONST unless really necessary.  Handle SUBREG, unary,
	ternary, bitfield and compares specially, to be able to simplify
	operations on constants.
	(expand_loc): Try to optimize LO_SUM.

	* dse.c (get_call_args): New function.
	(scan_insn): Don't handle BUILT_IN_BZERO.  For memset, attempt
	to get call arguments and if successful and both len and val are
	constants, handle the call as (mem:BLK) (const_int) store.

	* dse.c (struct store_info): Add is_large bool field, change
	positions_needed into a union of a bitmask and bitmap + count.
	(free_store_info): Free bitmap if is_large.
	(set_usage_bits): Don't look at stores where
	offset + width >= MAX_OFFSET.
	(set_position_unneeded, set_all_positions_unneeded,
	any_positions_needed_p, all_positions_needed_p): New static inline
	functions.
	(record_store): Handle BLKmode stores of CONST_INT, if
	MEM_SIZE is set on the MEM.  Use the new positions_needed
	accessor inlines.
	(replace_read): Handle reads from BLKmode CONST_INT stores.
	(check_mem_read_rtx): Use all_positions_needed_p function.
	(dse_step1): Free large positions_needed bitmaps and clear is_large.

	* dse.c (struct store_info): Change begin and end types to
	HOST_WIDE_INT.

	* dse.c (record_store): Fix check for unused store.

	* expr.c (block_clear_fn): No longer static.
	* expr.h (block_clear_fn): Declare.
	* dse.c (scan_insn): Memset and bzero can just read their
	arguments.

	* gcc.c-torture/execute/20081218-1.c: New test.

From-SVN: r142892
This commit is contained in:
Jakub Jelinek 2008-12-23 00:41:17 +01:00 committed by Jakub Jelinek
parent 805903b525
commit 8dd5516b4c
7 changed files with 771 additions and 132 deletions

View File

@ -1,5 +1,69 @@
2008-12-23 Jakub Jelinek <jakub@redhat.com>
PR middle-end/31150
* dse.c (struct store_info): Add const_rhs field.
(clear_rhs_from_active_local_stores): Clear also const_rhs.
(record_store): Try also cselib_expand_value_rtx to get a constant.
(find_shift_sequence, get_stored_val): Use const_rhs instead of
rhs if worthwhile.
* cselib.c (cselib_record_sets): If !cselib_record_memory and
there is just one set from read-only MEM, look at REG_EQUAL or
REG_EQUIV note.
* dse.c (struct store_info): Add redundant_reason field.
(record_store): When storing the same constant as has been
stored by an earlier store, set redundant_reason field
to the earlier store's insn_info_t. Don't delete cannot_delete
insns.
(find_shift_sequence): Remove read_info argument, add read_mode
and require_cst arguments. Return early if require_cst and
constant wouldn't be returned.
(get_stored_val): New function.
(replace_read): Use it.
(scan_insn): Put even cannot_delete insns with exactly 1 store
into active_local_stores.
(dse_step1): Don't delete cannot_delete insns. Remove redundant
constant stores if contains_cselib_groups and earlier store storing
the same value hasn't been eliminated.
(dse_step6): Renamed to dse_step7. New function.
(dse_step7): Renamed from dse_step6.
(rest_of_handle_dse): Call dse_step6 and dse_step7 at the end.
* cselib.c (cselib_expand_value_rtx): Don't wrap CONST_INTs
into CONST unless really necessary. Handle SUBREG, unary,
ternary, bitfield and compares specially, to be able to simplify
operations on constants.
(expand_loc): Try to optimize LO_SUM.
* dse.c (get_call_args): New function.
(scan_insn): Don't handle BUILT_IN_BZERO. For memset, attempt
to get call arguments and if successful and both len and val are
constants, handle the call as (mem:BLK) (const_int) store.
* dse.c (struct store_info): Add is_large bool field, change
positions_needed into a union of a bitmask and bitmap + count.
(free_store_info): Free bitmap if is_large.
(set_usage_bits): Don't look at stores where
offset + width >= MAX_OFFSET.
(set_position_unneeded, set_all_positions_unneeded,
any_positions_needed_p, all_positions_needed_p): New static inline
functions.
(record_store): Handle BLKmode stores of CONST_INT, if
MEM_SIZE is set on the MEM. Use the new positions_needed
accessor inlines.
(replace_read): Handle reads from BLKmode CONST_INT stores.
(check_mem_read_rtx): Use all_positions_needed_p function.
(dse_step1): Free large positions_needed bitmaps and clear is_large.
* dse.c (struct store_info): Change begin and end types to
HOST_WIDE_INT.
* dse.c (record_store): Fix check for unused store.
* expr.c (block_clear_fn): No longer static.
* expr.h (block_clear_fn): Declare.
* dse.c (scan_insn): Memset and bzero can just read their
arguments.
* config/i386/i386.c (expand_setmem_via_rep_stos): Add ORIG_VALUE
argument. If ORIG_VALUE is const0_rtx and COUNT is constant,
set MEM_SIZE on DESTMEM.

View File

@ -1,6 +1,7 @@
/* Common subexpression elimination library for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008
Free Software Foundation, Inc.
This file is part of GCC.
@ -865,12 +866,18 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth)
continue;
else if (!REG_P (p->loc))
{
rtx result;
rtx result, note;
if (dump_file)
{
print_inline_rtx (dump_file, p->loc, 0);
fprintf (dump_file, "\n");
}
if (GET_CODE (p->loc) == LO_SUM
&& GET_CODE (XEXP (p->loc, 1)) == SYMBOL_REF
&& p->setting_insn
&& (note = find_reg_note (p->setting_insn, REG_EQUAL, NULL_RTX))
&& XEXP (note, 0) == XEXP (p->loc, 1))
return XEXP (p->loc, 1);
result = cselib_expand_value_rtx (p->loc, regs_active, max_depth - 1);
if (result)
return result;
@ -928,6 +935,7 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
int i, j;
RTX_CODE code;
const char *format_ptr;
enum machine_mode mode;
code = GET_CODE (orig);
@ -1007,25 +1015,30 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
return orig;
break;
case SUBREG:
{
rtx subreg = cselib_expand_value_rtx (SUBREG_REG (orig), regs_active,
max_depth - 1);
if (!subreg)
return NULL;
scopy = simplify_gen_subreg (GET_MODE (orig), subreg,
GET_MODE (SUBREG_REG (orig)),
SUBREG_BYTE (orig));
if (scopy == NULL
|| (GET_CODE (scopy) == SUBREG
&& !REG_P (SUBREG_REG (scopy))
&& !MEM_P (SUBREG_REG (scopy))))
return shallow_copy_rtx (orig);
return scopy;
}
case VALUE:
{
rtx result;
if (dump_file)
fprintf (dump_file, "expanding value %s into: ", GET_MODE_NAME (GET_MODE (orig)));
result = expand_loc (CSELIB_VAL_PTR (orig)->locs, regs_active, max_depth);
if (result
&& GET_CODE (result) == CONST_INT
&& GET_MODE (orig) != VOIDmode)
{
result = gen_rtx_CONST (GET_MODE (orig), result);
if (dump_file)
fprintf (dump_file, " wrapping const_int result in const to preserve mode %s\n",
GET_MODE_NAME (GET_MODE (orig)));
}
return result;
}
if (dump_file)
fprintf (dump_file, "expanding value %s into: ",
GET_MODE_NAME (GET_MODE (orig)));
return expand_loc (CSELIB_VAL_PTR (orig)->locs, regs_active, max_depth);
default:
break;
}
@ -1036,9 +1049,9 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
us to explicitly document why we are *not* copying a flag. */
copy = shallow_copy_rtx (orig);
format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
format_ptr = GET_RTX_FORMAT (code);
for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
for (i = 0; i < GET_RTX_LENGTH (code); i++)
switch (*format_ptr++)
{
case 'e':
@ -1082,6 +1095,70 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
gcc_unreachable ();
}
mode = GET_MODE (copy);
/* If an operand has been simplified into CONST_INT, which doesn't
have a mode and the mode isn't derivable from whole rtx's mode,
try simplify_*_operation first with mode from original's operand
and as a fallback wrap CONST_INT into gen_rtx_CONST. */
scopy = copy;
switch (GET_RTX_CLASS (code))
{
case RTX_UNARY:
if (CONST_INT_P (XEXP (copy, 0))
&& GET_MODE (XEXP (orig, 0)) != VOIDmode)
{
scopy = simplify_unary_operation (code, mode, XEXP (copy, 0),
GET_MODE (XEXP (orig, 0)));
if (scopy)
return scopy;
}
break;
case RTX_COMM_ARITH:
case RTX_BIN_ARITH:
/* These expressions can derive operand modes from the whole rtx's mode. */
break;
case RTX_TERNARY:
case RTX_BITFIELD_OPS:
if (CONST_INT_P (XEXP (copy, 0))
&& GET_MODE (XEXP (orig, 0)) != VOIDmode)
{
scopy = simplify_ternary_operation (code, mode,
GET_MODE (XEXP (orig, 0)),
XEXP (copy, 0), XEXP (copy, 1),
XEXP (copy, 2));
if (scopy)
return scopy;
}
break;
case RTX_COMPARE:
case RTX_COMM_COMPARE:
if (CONST_INT_P (XEXP (copy, 0))
&& GET_MODE (XEXP (copy, 1)) == VOIDmode
&& (GET_MODE (XEXP (orig, 0)) != VOIDmode
|| GET_MODE (XEXP (orig, 1)) != VOIDmode))
{
scopy = simplify_relational_operation (code, mode,
(GET_MODE (XEXP (orig, 0))
!= VOIDmode)
? GET_MODE (XEXP (orig, 0))
: GET_MODE (XEXP (orig, 1)),
XEXP (copy, 0),
XEXP (copy, 1));
if (scopy)
return scopy;
}
break;
default:
break;
}
if (scopy == NULL_RTX)
{
XEXP (copy, 0)
= gen_rtx_CONST (GET_MODE (XEXP (orig, 0)), XEXP (copy, 0));
if (dump_file)
fprintf (dump_file, " wrapping const_int result in const to preserve mode %s\n",
GET_MODE_NAME (GET_MODE (XEXP (copy, 0))));
}
scopy = simplify_rtx (copy);
if (scopy)
return scopy;
@ -1581,6 +1658,17 @@ cselib_record_sets (rtx insn)
}
}
if (n_sets == 1
&& MEM_P (sets[0].src)
&& !cselib_record_memory
&& MEM_READONLY_P (sets[0].src))
{
rtx note = find_reg_equal_equiv_note (insn);
if (note && CONSTANT_P (XEXP (note, 0)))
sets[0].src = XEXP (note, 0);
}
/* Look up the values that are read. Do this before invalidating the
locations that are written. */
for (i = 0; i < n_sets; i++)

662
gcc/dse.c
View File

@ -91,7 +91,10 @@ along with GCC; see the file COPYING3. If not see
5) Delete the insns that the global analysis has indicated are
unnecessary.
6) Cleanup.
6) Delete insns that store the same value as preceeding store
where the earlier store couldn't be eliminated.
7) Cleanup.
This step uses cselib and canon_rtx to build the largest expression
possible for each address. This pass is a forwards pass through
@ -205,6 +208,9 @@ struct store_info
/* False means this is a clobber. */
bool is_set;
/* False if a single HOST_WIDE_INT bitmap is used for positions_needed. */
bool is_large;
/* The id of the mem group of the base address. If rtx_varies_p is
true, this is -1. Otherwise, it is the index into the group
table. */
@ -224,12 +230,26 @@ struct store_info
/* The offset of the first and byte before the last byte associated
with the operation. */
int begin, end;
HOST_WIDE_INT begin, end;
/* An bitmask as wide as the number of bytes in the word that
contains a 1 if the byte may be needed. The store is unused if
all of the bits are 0. */
unsigned HOST_WIDE_INT positions_needed;
union
{
/* A bitmask as wide as the number of bytes in the word that
contains a 1 if the byte may be needed. The store is unused if
all of the bits are 0. This is used if IS_LARGE is false. */
unsigned HOST_WIDE_INT small_bitmask;
struct
{
/* A bitmap with one bit per byte. Cleared bit means the position
is needed. Used if IS_LARGE is false. */
bitmap bitmap;
/* Number of set bits (i.e. unneeded bytes) in BITMAP. If it is
equal to END - BEGIN, the whole store is unused. */
int count;
} large;
} positions_needed;
/* The next store info for this insn. */
struct store_info *next;
@ -237,7 +257,16 @@ struct store_info
/* The right hand side of the store. This is used if there is a
subsequent reload of the mems address somewhere later in the
basic block. */
rtx rhs;
rtx rhs;
/* If rhs is or holds a constant, this contains that constant,
otherwise NULL. */
rtx const_rhs;
/* Set if this store stores the same constant value as REDUNDANT_REASON
insn stored. These aren't eliminated early, because doing that
might prevent the earlier larger store to be eliminated. */
struct insn_info *redundant_reason;
};
/* Return a bitmask with the first N low bits set. */
@ -760,6 +789,8 @@ free_store_info (insn_info_t insn_info)
while (store_info)
{
store_info_t next = store_info->next;
if (store_info->is_large)
BITMAP_FREE (store_info->positions_needed.large.bitmap);
if (store_info->cse_base)
pool_free (cse_store_info_pool, store_info);
else
@ -911,7 +942,7 @@ set_usage_bits (group_info_t group, HOST_WIDE_INT offset, HOST_WIDE_INT width)
{
HOST_WIDE_INT i;
if ((offset > -MAX_OFFSET) && (offset < MAX_OFFSET))
if (offset > -MAX_OFFSET && offset + width < MAX_OFFSET)
for (i=offset; i<offset+width; i++)
{
bitmap store1;
@ -1167,12 +1198,86 @@ clear_rhs_from_active_local_stores (void)
store_info = store_info->next;
store_info->rhs = NULL;
store_info->const_rhs = NULL;
ptr = ptr->next_local_store;
}
}
/* Mark byte POS bytes from the beginning of store S_INFO as unneeded. */
static inline void
set_position_unneeded (store_info_t s_info, int pos)
{
if (__builtin_expect (s_info->is_large, false))
{
if (!bitmap_bit_p (s_info->positions_needed.large.bitmap, pos))
{
s_info->positions_needed.large.count++;
bitmap_set_bit (s_info->positions_needed.large.bitmap, pos);
}
}
else
s_info->positions_needed.small_bitmask
&= ~(((unsigned HOST_WIDE_INT) 1) << pos);
}
/* Mark the whole store S_INFO as unneeded. */
static inline void
set_all_positions_unneeded (store_info_t s_info)
{
if (__builtin_expect (s_info->is_large, false))
{
int pos, end = s_info->end - s_info->begin;
for (pos = 0; pos < end; pos++)
bitmap_set_bit (s_info->positions_needed.large.bitmap, pos);
s_info->positions_needed.large.count = end;
}
else
s_info->positions_needed.small_bitmask = (unsigned HOST_WIDE_INT) 0;
}
/* Return TRUE if any bytes from S_INFO store are needed. */
static inline bool
any_positions_needed_p (store_info_t s_info)
{
if (__builtin_expect (s_info->is_large, false))
return (s_info->positions_needed.large.count
< s_info->end - s_info->begin);
else
return (s_info->positions_needed.small_bitmask
!= (unsigned HOST_WIDE_INT) 0);
}
/* Return TRUE if all bytes START through START+WIDTH-1 from S_INFO
store are needed. */
static inline bool
all_positions_needed_p (store_info_t s_info, int start, int width)
{
if (__builtin_expect (s_info->is_large, false))
{
int end = start + width;
while (start < end)
if (bitmap_bit_p (s_info->positions_needed.large.bitmap, start++))
return false;
return true;
}
else
{
unsigned HOST_WIDE_INT mask = lowpart_bitmask (width) << start;
return (s_info->positions_needed.small_bitmask & mask) == mask;
}
}
static rtx get_stored_val (store_info_t, enum machine_mode, HOST_WIDE_INT,
HOST_WIDE_INT, basic_block, bool);
/* BODY is an instruction pattern that belongs to INSN. Return 1 if
there is a candidate store, after adding it to the appropriate
local store group if so. */
@ -1180,7 +1285,7 @@ clear_rhs_from_active_local_stores (void)
static int
record_store (rtx body, bb_info_t bb_info)
{
rtx mem;
rtx mem, rhs, const_rhs;
HOST_WIDE_INT offset = 0;
HOST_WIDE_INT width = 0;
alias_set_type spill_alias_set;
@ -1188,20 +1293,21 @@ record_store (rtx body, bb_info_t bb_info)
store_info_t store_info = NULL;
int group_id;
cselib_val *base = NULL;
insn_info_t ptr, last;
insn_info_t ptr, last, redundant_reason;
bool store_is_unused;
if (GET_CODE (body) != SET && GET_CODE (body) != CLOBBER)
return 0;
mem = SET_DEST (body);
/* If this is not used, then this cannot be used to keep the insn
from being deleted. On the other hand, it does provide something
that can be used to prove that another store is dead. */
store_is_unused
= (find_reg_note (insn_info->insn, REG_UNUSED, body) != NULL);
= (find_reg_note (insn_info->insn, REG_UNUSED, mem) != NULL);
/* Check whether that value is a suitable memory location. */
mem = SET_DEST (body);
if (!MEM_P (mem))
{
/* If the set or clobber is unused, then it does not effect our
@ -1220,20 +1326,31 @@ record_store (rtx body, bb_info_t bb_info)
fprintf (dump_file, " adding wild read for (clobber (mem:BLK (scratch))\n");
add_wild_read (bb_info);
insn_info->cannot_delete = true;
return 0;
}
else if (!store_is_unused)
/* Handle (set (mem:BLK (addr) [... S36 ...]) (const_int 0))
as memset (addr, 0, 36); */
else if (!MEM_SIZE (mem)
|| !CONST_INT_P (MEM_SIZE (mem))
|| GET_CODE (body) != SET
|| INTVAL (MEM_SIZE (mem)) <= 0
|| INTVAL (MEM_SIZE (mem)) > MAX_OFFSET
|| !CONST_INT_P (SET_SRC (body)))
{
/* If the set or clobber is unused, then it does not effect our
ability to get rid of the entire insn. */
insn_info->cannot_delete = true;
clear_rhs_from_active_local_stores ();
if (!store_is_unused)
{
/* If the set or clobber is unused, then it does not effect our
ability to get rid of the entire insn. */
insn_info->cannot_delete = true;
clear_rhs_from_active_local_stores ();
}
return 0;
}
return 0;
}
/* We can still process a volatile mem, we just cannot delete it. */
if (MEM_VOLATILE_P (mem))
insn_info->cannot_delete = true;
insn_info->cannot_delete = true;
if (!canon_address (mem, &spill_alias_set, &group_id, &offset, &base))
{
@ -1241,12 +1358,20 @@ record_store (rtx body, bb_info_t bb_info)
return 0;
}
width = GET_MODE_SIZE (GET_MODE (mem));
if (GET_MODE (mem) == BLKmode)
width = INTVAL (MEM_SIZE (mem));
else
{
width = GET_MODE_SIZE (GET_MODE (mem));
gcc_assert ((unsigned) width <= HOST_BITS_PER_WIDE_INT);
}
if (spill_alias_set)
{
bitmap store1 = clear_alias_group->store1_p;
bitmap store2 = clear_alias_group->store2_p;
gcc_assert (GET_MODE (mem) != BLKmode);
if (bitmap_bit_p (store1, spill_alias_set))
bitmap_set_bit (store2, spill_alias_set);
@ -1295,10 +1420,41 @@ record_store (rtx body, bb_info_t bb_info)
(int)offset, (int)(offset+width));
}
const_rhs = rhs = NULL_RTX;
if (GET_CODE (body) == SET
/* No place to keep the value after ra. */
&& !reload_completed
&& (REG_P (SET_SRC (body))
|| GET_CODE (SET_SRC (body)) == SUBREG
|| CONSTANT_P (SET_SRC (body)))
&& !MEM_VOLATILE_P (mem)
/* Sometimes the store and reload is used for truncation and
rounding. */
&& !(FLOAT_MODE_P (GET_MODE (mem)) && (flag_float_store)))
{
rhs = SET_SRC (body);
if (CONSTANT_P (rhs))
const_rhs = rhs;
else if (body == PATTERN (insn_info->insn))
{
rtx tem = find_reg_note (insn_info->insn, REG_EQUAL, NULL_RTX);
if (tem && CONSTANT_P (XEXP (tem, 0)))
const_rhs = XEXP (tem, 0);
}
if (const_rhs == NULL_RTX && REG_P (rhs))
{
rtx tem = cselib_expand_value_rtx (rhs, scratch, 5);
if (tem && CONSTANT_P (tem))
const_rhs = tem;
}
}
/* Check to see if this stores causes some other stores to be
dead. */
ptr = active_local_stores;
last = NULL;
redundant_reason = NULL;
while (ptr)
{
@ -1327,7 +1483,7 @@ record_store (rtx body, bb_info_t bb_info)
&& (GET_MODE (mem) == entry->mode))
{
del = true;
s_info->positions_needed = (unsigned HOST_WIDE_INT) 0;
set_all_positions_unneeded (s_info);
}
if (dump_file)
fprintf (dump_file, " trying spill store in insn=%d alias_set=%d\n",
@ -1341,10 +1497,46 @@ record_store (rtx body, bb_info_t bb_info)
fprintf (dump_file, " trying store in insn=%d gid=%d[%d..%d)\n",
INSN_UID (ptr->insn), s_info->group_id,
(int)s_info->begin, (int)s_info->end);
for (i = offset; i < offset+width; i++)
if (i >= s_info->begin && i < s_info->end)
s_info->positions_needed
&= ~(((unsigned HOST_WIDE_INT) 1) << (i - s_info->begin));
/* Even if PTR won't be eliminated as unneeded, if both
PTR and this insn store the same constant value, we might
eliminate this insn instead. */
if (s_info->const_rhs
&& const_rhs
&& offset >= s_info->begin
&& offset + width <= s_info->end
&& all_positions_needed_p (s_info, offset - s_info->begin,
width))
{
if (GET_MODE (mem) == BLKmode)
{
if (GET_MODE (s_info->mem) == BLKmode
&& s_info->const_rhs == const_rhs)
redundant_reason = ptr;
}
else if (s_info->const_rhs == const0_rtx
&& const_rhs == const0_rtx)
redundant_reason = ptr;
else
{
rtx val;
start_sequence ();
val = get_stored_val (s_info, GET_MODE (mem),
offset, offset + width,
BLOCK_FOR_INSN (insn_info->insn),
true);
if (get_insns () != NULL)
val = NULL_RTX;
end_sequence ();
if (val && rtx_equal_p (val, const_rhs))
redundant_reason = ptr;
}
}
for (i = MAX (offset, s_info->begin);
i < offset + width && i < s_info->end;
i++)
set_position_unneeded (s_info, i - s_info->begin);
}
else if (s_info->rhs)
/* Need to see if it is possible for this store to overwrite
@ -1355,14 +1547,18 @@ record_store (rtx body, bb_info_t bb_info)
GET_MODE (s_info->mem),
s_info->mem_addr,
mem, rtx_varies_p))
s_info->rhs = NULL;
{
s_info->rhs = NULL;
s_info->const_rhs = NULL;
}
}
/* An insn can be deleted if every position of every one of
its s_infos is zero. */
if (s_info->positions_needed != (unsigned HOST_WIDE_INT) 0)
if (any_positions_needed_p (s_info)
|| ptr->cannot_delete)
del = false;
if (del)
{
insn_info_t insn_to_delete = ptr;
@ -1380,8 +1576,6 @@ record_store (rtx body, bb_info_t bb_info)
ptr = next;
}
gcc_assert ((unsigned) width <= HOST_BITS_PER_WIDE_INT);
/* Finish filling in the store_info. */
store_info->next = insn_info->store_rec;
insn_info->store_rec = store_info;
@ -1389,25 +1583,25 @@ record_store (rtx body, bb_info_t bb_info)
store_info->alias_set = spill_alias_set;
store_info->mem_addr = get_addr (XEXP (mem, 0));
store_info->cse_base = base;
store_info->positions_needed = lowpart_bitmask (width);
if (width > HOST_BITS_PER_WIDE_INT)
{
store_info->is_large = true;
store_info->positions_needed.large.count = 0;
store_info->positions_needed.large.bitmap = BITMAP_ALLOC (NULL);
}
else
{
store_info->is_large = false;
store_info->positions_needed.small_bitmask = lowpart_bitmask (width);
}
store_info->group_id = group_id;
store_info->begin = offset;
store_info->end = offset + width;
store_info->is_set = GET_CODE (body) == SET;
store_info->rhs = rhs;
store_info->const_rhs = const_rhs;
store_info->redundant_reason = redundant_reason;
if (store_info->is_set
/* No place to keep the value after ra. */
&& !reload_completed
&& (REG_P (SET_SRC (body))
|| GET_CODE (SET_SRC (body)) == SUBREG
|| CONSTANT_P (SET_SRC (body)))
/* Sometimes the store and reload is used for truncation and
rounding. */
&& !(FLOAT_MODE_P (GET_MODE (mem)) && (flag_float_store)))
store_info->rhs = SET_SRC (body);
else
store_info->rhs = NULL;
/* If this is a clobber, we return 0. We will only be able to
delete this insn if there is only one store USED store, but we
can use the clobber to delete other stores earlier. */
@ -1435,12 +1629,10 @@ dump_insn_info (const char * start, insn_info_t insn_info)
static rtx
find_shift_sequence (int access_size,
store_info_t store_info,
read_info_t read_info,
int shift,
bool speed)
enum machine_mode read_mode,
int shift, bool speed, bool require_cst)
{
enum machine_mode store_mode = GET_MODE (store_info->mem);
enum machine_mode read_mode = GET_MODE (read_info->mem);
enum machine_mode new_mode;
rtx read_reg = NULL;
@ -1462,11 +1654,11 @@ find_shift_sequence (int access_size,
/* If a constant was stored into memory, try to simplify it here,
otherwise the cost of the shift might preclude this optimization
e.g. at -Os, even when no actual shift will be needed. */
if (CONSTANT_P (store_info->rhs))
if (store_info->const_rhs)
{
unsigned int byte = subreg_lowpart_offset (new_mode, store_mode);
rtx ret = simplify_subreg (new_mode, store_info->rhs, store_mode,
byte);
rtx ret = simplify_subreg (new_mode, store_info->const_rhs,
store_mode, byte);
if (ret && CONSTANT_P (ret))
{
ret = simplify_const_binary_operation (LSHIFTRT, new_mode,
@ -1482,6 +1674,9 @@ find_shift_sequence (int access_size,
}
}
if (require_cst)
return NULL_RTX;
/* Try a wider mode if truncating the store mode to NEW_MODE
requires a real instruction. */
if (GET_MODE_BITSIZE (new_mode) < GET_MODE_BITSIZE (store_mode)
@ -1563,6 +1758,79 @@ look_for_hardregs (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
}
}
/* Helper function for replace_read and record_store.
Attempt to return a value stored in STORE_INFO, from READ_BEGIN
to one before READ_END bytes read in READ_MODE. Return NULL
if not successful. If REQUIRE_CST is true, return always constant. */
static rtx
get_stored_val (store_info_t store_info, enum machine_mode read_mode,
HOST_WIDE_INT read_begin, HOST_WIDE_INT read_end,
basic_block bb, bool require_cst)
{
enum machine_mode store_mode = GET_MODE (store_info->mem);
int shift;
int access_size; /* In bytes. */
rtx read_reg;
/* To get here the read is within the boundaries of the write so
shift will never be negative. Start out with the shift being in
bytes. */
if (store_mode == BLKmode)
shift = 0;
else if (BYTES_BIG_ENDIAN)
shift = store_info->end - read_end;
else
shift = read_begin - store_info->begin;
access_size = shift + GET_MODE_SIZE (read_mode);
/* From now on it is bits. */
shift *= BITS_PER_UNIT;
if (shift)
read_reg = find_shift_sequence (access_size, store_info, read_mode, shift,
optimize_bb_for_speed_p (bb),
require_cst);
else if (store_mode == BLKmode)
{
/* The store is a memset (addr, const_val, const_size). */
gcc_assert (CONST_INT_P (store_info->rhs));
store_mode = int_mode_for_mode (read_mode);
if (store_mode == BLKmode)
read_reg = NULL_RTX;
else if (store_info->rhs == const0_rtx)
read_reg = extract_low_bits (read_mode, store_mode, const0_rtx);
else if (GET_MODE_BITSIZE (store_mode) > HOST_BITS_PER_WIDE_INT
|| BITS_PER_UNIT >= HOST_BITS_PER_WIDE_INT)
read_reg = NULL_RTX;
else
{
unsigned HOST_WIDE_INT c
= INTVAL (store_info->rhs)
& (((HOST_WIDE_INT) 1 << BITS_PER_UNIT) - 1);
int shift = BITS_PER_UNIT;
while (shift < HOST_BITS_PER_WIDE_INT)
{
c |= (c << shift);
shift <<= 1;
}
read_reg = GEN_INT (trunc_int_for_mode (c, store_mode));
read_reg = extract_low_bits (read_mode, store_mode, read_reg);
}
}
else if (store_info->const_rhs
&& (require_cst
|| GET_MODE_CLASS (read_mode) != GET_MODE_CLASS (store_mode)))
read_reg = extract_low_bits (read_mode, store_mode,
copy_rtx (store_info->const_rhs));
else
read_reg = extract_low_bits (read_mode, store_mode,
copy_rtx (store_info->rhs));
if (require_cst && read_reg && !CONSTANT_P (read_reg))
read_reg = NULL_RTX;
return read_reg;
}
/* Take a sequence of:
A <- r1
@ -1597,30 +1865,17 @@ look_for_hardregs (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
static bool
replace_read (store_info_t store_info, insn_info_t store_insn,
read_info_t read_info, insn_info_t read_insn, rtx *loc, bitmap regs_live)
read_info_t read_info, insn_info_t read_insn, rtx *loc,
bitmap regs_live)
{
enum machine_mode store_mode = GET_MODE (store_info->mem);
enum machine_mode read_mode = GET_MODE (read_info->mem);
int shift;
int access_size; /* In bytes. */
rtx insns, this_insn, read_reg;
basic_block bb;
if (!dbg_cnt (dse))
return false;
/* To get here the read is within the boundaries of the write so
shift will never be negative. Start out with the shift being in
bytes. */
if (BYTES_BIG_ENDIAN)
shift = store_info->end - read_info->end;
else
shift = read_info->begin - store_info->begin;
access_size = shift + GET_MODE_SIZE (read_mode);
/* From now on it is bits. */
shift *= BITS_PER_UNIT;
/* Create a sequence of instructions to set up the read register.
This sequence goes immediately before the store and its result
is read by the load.
@ -1636,12 +1891,10 @@ replace_read (store_info_t store_info, insn_info_t store_insn,
GET_MODE_NAME (read_mode), INSN_UID (read_insn->insn),
GET_MODE_NAME (store_mode), INSN_UID (store_insn->insn));
start_sequence ();
if (shift)
read_reg = find_shift_sequence (access_size, store_info, read_info, shift,
optimize_bb_for_speed_p (BLOCK_FOR_INSN (read_insn->insn)));
else
read_reg = extract_low_bits (read_mode, store_mode,
copy_rtx (store_info->rhs));
bb = BLOCK_FOR_INSN (read_insn->insn);
read_reg = get_stored_val (store_info,
read_mode, read_info->begin, read_info->end,
bb, false);
if (read_reg == NULL_RTX)
{
end_sequence ();
@ -1892,18 +2145,15 @@ check_mem_read_rtx (rtx *loc, void *data)
else
{
if (store_info->rhs
&& (offset >= store_info->begin)
&& (offset + width <= store_info->end))
{
unsigned HOST_WIDE_INT mask
= (lowpart_bitmask (width)
<< (offset - store_info->begin));
&& offset >= store_info->begin
&& offset + width <= store_info->end
&& all_positions_needed_p (store_info,
offset - store_info->begin,
width)
&& replace_read (store_info, i_ptr, read_info,
insn_info, loc, bb_info->regs_live))
return 0;
if ((store_info->positions_needed & mask) == mask
&& replace_read (store_info, i_ptr,
read_info, insn_info, loc, bb_info->regs_live))
return 0;
}
/* The bases are the same, just see if the offsets
overlap. */
if ((offset < store_info->end)
@ -1961,18 +2211,13 @@ check_mem_read_rtx (rtx *loc, void *data)
if (store_info->rhs
&& store_info->group_id == -1
&& store_info->cse_base == base
&& (offset >= store_info->begin)
&& (offset + width <= store_info->end))
{
unsigned HOST_WIDE_INT mask
= (lowpart_bitmask (width)
<< (offset - store_info->begin));
if ((store_info->positions_needed & mask) == mask
&& replace_read (store_info, i_ptr,
read_info, insn_info, loc, bb_info->regs_live))
return 0;
}
&& offset >= store_info->begin
&& offset + width <= store_info->end
&& all_positions_needed_p (store_info,
offset - store_info->begin, width)
&& replace_read (store_info, i_ptr, read_info, insn_info, loc,
bb_info->regs_live))
return 0;
if (!store_info->alias_set)
remove = canon_true_dependence (store_info->mem,
@ -2008,6 +2253,67 @@ check_mem_read_use (rtx *loc, void *data)
for_each_rtx (loc, check_mem_read_rtx, data);
}
/* Get arguments passed to CALL_INSN. Return TRUE if successful.
So far it only handles arguments passed in registers. */
static bool
get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
{
CUMULATIVE_ARGS args_so_far;
tree arg;
int idx;
INIT_CUMULATIVE_ARGS (args_so_far, TREE_TYPE (fn), NULL_RTX, 0, 3);
arg = TYPE_ARG_TYPES (TREE_TYPE (fn));
for (idx = 0;
arg != void_list_node && idx < nargs;
arg = TREE_CHAIN (arg), idx++)
{
enum machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
rtx reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1), link, tmp;
if (!reg || !REG_P (reg) || GET_MODE (reg) != mode
|| GET_MODE_CLASS (mode) != MODE_INT)
return false;
for (link = CALL_INSN_FUNCTION_USAGE (call_insn);
link;
link = XEXP (link, 1))
if (GET_CODE (XEXP (link, 0)) == USE)
{
args[idx] = XEXP (XEXP (link, 0), 0);
if (REG_P (args[idx])
&& REGNO (args[idx]) == REGNO (reg)
&& (GET_MODE (args[idx]) == mode
|| (GET_MODE_CLASS (GET_MODE (args[idx])) == MODE_INT
&& (GET_MODE_SIZE (GET_MODE (args[idx]))
<= UNITS_PER_WORD)
&& (GET_MODE_SIZE (GET_MODE (args[idx]))
> GET_MODE_SIZE (mode)))))
break;
}
if (!link)
return false;
tmp = cselib_expand_value_rtx (args[idx], scratch, 5);
if (GET_MODE (args[idx]) != mode)
{
if (!tmp || !CONST_INT_P (tmp))
return false;
tmp = GEN_INT (trunc_int_for_mode (INTVAL (tmp), mode));
}
if (tmp)
args[idx] = tmp;
FUNCTION_ARG_ADVANCE (args_so_far, mode, NULL_TREE, 1);
}
if (arg != void_list_node || idx != nargs)
return false;
return true;
}
/* Apply record_store to all candidate stores in INSN. Mark INSN
if some part of it is not a candidate store and assigns to a
non-register target. */
@ -2045,18 +2351,48 @@ scan_insn (bb_info_t bb_info, rtx insn)
if (CALL_P (insn))
{
bool const_call;
tree memset_call = NULL_TREE;
insn_info->cannot_delete = true;
/* Const functions cannot do anything bad i.e. read memory,
however, they can read their parameters which may have
been pushed onto the stack. */
if (RTL_CONST_CALL_P (insn))
been pushed onto the stack.
memset and bzero don't read memory either. */
const_call = RTL_CONST_CALL_P (insn);
if (!const_call)
{
rtx call = PATTERN (insn);
if (GET_CODE (call) == PARALLEL)
call = XVECEXP (call, 0, 0);
if (GET_CODE (call) == SET)
call = SET_SRC (call);
if (GET_CODE (call) == CALL
&& MEM_P (XEXP (call, 0))
&& GET_CODE (XEXP (XEXP (call, 0), 0)) == SYMBOL_REF)
{
rtx symbol = XEXP (XEXP (call, 0), 0);
if (SYMBOL_REF_DECL (symbol)
&& TREE_CODE (SYMBOL_REF_DECL (symbol)) == FUNCTION_DECL)
{
if ((DECL_BUILT_IN_CLASS (SYMBOL_REF_DECL (symbol))
== BUILT_IN_NORMAL
&& (DECL_FUNCTION_CODE (SYMBOL_REF_DECL (symbol))
== BUILT_IN_MEMSET))
|| SYMBOL_REF_DECL (symbol) == block_clear_fn)
memset_call = SYMBOL_REF_DECL (symbol);
}
}
}
if (const_call || memset_call)
{
insn_info_t i_ptr = active_local_stores;
insn_info_t last = NULL;
if (dump_file)
fprintf (dump_file, "const call %d\n", INSN_UID (insn));
fprintf (dump_file, "%s call %d\n",
const_call ? "const" : "memset", INSN_UID (insn));
/* See the head comment of the frame_read field. */
if (reload_completed)
@ -2102,6 +2438,28 @@ scan_insn (bb_info_t bb_info, rtx insn)
i_ptr = i_ptr->next_local_store;
}
if (memset_call)
{
rtx args[3];
if (get_call_args (insn, memset_call, args, 3)
&& CONST_INT_P (args[1])
&& CONST_INT_P (args[2])
&& INTVAL (args[2]) > 0)
{
rtx mem = gen_rtx_MEM (BLKmode, args[0]);
set_mem_size (mem, args[2]);
body = gen_rtx_SET (VOIDmode, mem, args[1]);
mems_found += record_store (body, bb_info);
if (dump_file)
fprintf (dump_file, "handling memset as BLKmode store\n");
if (mems_found == 1)
{
insn_info->next_local_store = active_local_stores;
active_local_stores = insn_info;
}
}
}
}
else
@ -2134,11 +2492,11 @@ scan_insn (bb_info_t bb_info, rtx insn)
fprintf (dump_file, "mems_found = %d, cannot_delete = %s\n",
mems_found, insn_info->cannot_delete ? "true" : "false");
/* If we found some sets of mems, and the insn has not been marked
cannot delete, add it into the active_local_stores so that it can
be locally deleted if found dead. Otherwise mark it as cannot
delete. This simplifies the processing later. */
if (mems_found == 1 && !insn_info->cannot_delete)
/* If we found some sets of mems, add it into the active_local_stores so
that it can be locally deleted if found dead or used for
replace_read and redundant constant store elimination. Otherwise mark
it as cannot delete. This simplifies the processing later. */
if (mems_found == 1)
{
insn_info->next_local_store = active_local_stores;
active_local_stores = insn_info;
@ -2263,14 +2621,14 @@ dse_step1 (void)
/* Skip the clobbers. */
while (!store_info->is_set)
store_info = store_info->next;
if (store_info->alias_set)
if (store_info->alias_set && !i_ptr->cannot_delete)
delete_dead_store_insn (i_ptr);
else
if (store_info->group_id >= 0)
{
group_info_t group
= VEC_index (group_info_t, rtx_group_vec, store_info->group_id);
if (group->frame_related)
if (group->frame_related && !i_ptr->cannot_delete)
delete_dead_store_insn (i_ptr);
}
@ -2298,7 +2656,40 @@ dse_step1 (void)
while (ptr)
{
if (ptr->contains_cselib_groups)
free_store_info (ptr);
{
store_info_t s_info = ptr->store_rec;
while (s_info && !s_info->is_set)
s_info = s_info->next;
if (s_info
&& s_info->redundant_reason
&& s_info->redundant_reason->insn
&& !ptr->cannot_delete)
{
if (dump_file)
fprintf (dump_file, "Locally deleting insn %d "
"because insn %d stores the "
"same value and couldn't be "
"eliminated\n",
INSN_UID (ptr->insn),
INSN_UID (s_info->redundant_reason->insn));
delete_dead_store_insn (ptr);
}
if (s_info)
s_info->redundant_reason = NULL;
free_store_info (ptr);
}
else
{
store_info_t s_info;
/* Free at least positions_needed bitmaps. */
for (s_info = ptr->store_rec; s_info; s_info = s_info->next)
if (s_info->is_large)
{
BITMAP_FREE (s_info->positions_needed.large.bitmap);
s_info->is_large = false;
}
}
ptr = ptr->prev_insn;
}
@ -3236,11 +3627,61 @@ dse_step5_spill (void)
/*----------------------------------------------------------------------------
Sixth step.
Delete stores made redundant by earlier stores (which store the same
value) that couldn't be eliminated.
----------------------------------------------------------------------------*/
static void
dse_step6 (void)
{
basic_block bb;
FOR_ALL_BB (bb)
{
bb_info_t bb_info = bb_table[bb->index];
insn_info_t insn_info = bb_info->last_insn;
while (insn_info)
{
/* There may have been code deleted by the dce pass run before
this phase. */
if (insn_info->insn
&& INSN_P (insn_info->insn)
&& !insn_info->cannot_delete)
{
store_info_t s_info = insn_info->store_rec;
while (s_info && !s_info->is_set)
s_info = s_info->next;
if (s_info
&& s_info->redundant_reason
&& s_info->redundant_reason->insn
&& INSN_P (s_info->redundant_reason->insn))
{
rtx rinsn = s_info->redundant_reason->insn;
if (dump_file)
fprintf (dump_file, "Locally deleting insn %d "
"because insn %d stores the "
"same value and couldn't be "
"eliminated\n",
INSN_UID (insn_info->insn),
INSN_UID (rinsn));
delete_dead_store_insn (insn_info);
}
}
insn_info = insn_info->prev_insn;
}
}
}
/*----------------------------------------------------------------------------
Seventh step.
Destroy everything left standing.
----------------------------------------------------------------------------*/
static void
dse_step6 (bool global_done)
dse_step7 (bool global_done)
{
unsigned int i;
group_info_t group;
@ -3346,8 +3787,9 @@ rest_of_handle_dse (void)
dse_step4 ();
dse_step5_spill ();
}
dse_step6 (did_global);
dse_step6 ();
dse_step7 (did_global);
if (dump_file)
fprintf (dump_file, "dse: local deletions = %d, global deletions = %d, spill deletions = %d\n",

View File

@ -2698,7 +2698,7 @@ set_storage_via_libcall (rtx object, rtx size, rtx val, bool tailcall)
for the function we use for block clears. The first time FOR_CALL
is true, we call assemble_external. */
static GTY(()) tree block_clear_fn;
tree block_clear_fn;
void
init_block_clear_fn (const char *asmspec)

View File

@ -404,6 +404,7 @@ enum block_op_methods
BLOCK_OP_TAILCALL
};
extern tree GTY(()) block_clear_fn;
extern void init_block_move_fn (const char *);
extern void init_block_clear_fn (const char *);

View File

@ -1,3 +1,8 @@
2008-12-18 Jakub Jelinek <jakub@redhat.com>
PR middle-end/31150
* gcc.c-torture/execute/20081218-1.c: New test.
2008-12-22 Paul Thomas <pault@gcc.gnu.org>
PR fortran/35780

View File

@ -0,0 +1,39 @@
struct A { int i, j; char pad[512]; } a;
int __attribute__((noinline))
foo (void)
{
__builtin_memset (&a, 0x26, sizeof a);
return a.i;
}
void __attribute__((noinline))
bar (void)
{
__builtin_memset (&a, 0x36, sizeof a);
a.i = 0x36363636;
a.j = 0x36373636;
}
int
main (void)
{
int i;
if (sizeof (int) != 4 || __CHAR_BIT__ != 8)
return 0;
if (foo () != 0x26262626)
__builtin_abort ();
for (i = 0; i < sizeof a; i++)
if (((char *)&a)[i] != 0x26)
__builtin_abort ();
bar ();
if (a.j != 0x36373636)
__builtin_abort ();
a.j = 0x36363636;
for (i = 0; i < sizeof a; i++)
if (((char *)&a)[i] != 0x36)
__builtin_abort ();
return 0;
}