Makefile.in (OBJS): Add rtlhooks.o.

2004-05-25  Paolo Bonzini  <bonzini@gnu.org>

	* Makefile.in (OBJS): Add rtlhooks.o.
	(rtlanal.o): Depend on function.h.
	(cse.o): Depend on rtlhooks-def.h.
	(combine.o): Depend on rtlhooks-def.h.
	(rtlhooks.o): New rule.
	* combine.c: Include rtlhooks-def.h.
	(nonzero_bits, cached_nonzero_bits, nonzero_bits1,
	num_sign_bit_copies, cached_num_sign_bit_copies,
	num_sign_bit_copies1): Move most of the code to rtlanal.c.
	(reg_nonzero_bits_for_combine,
	reg_num_sign_bit_copies_for_combine): New functions holding
	the remnants of the above.
	(combine_rtl_hooks): New.
	(combine_instructions): Set rtl_hooks instead of gen_lowpart.
	* cse.c: Include rtlhooks-def.h.
	(cse_rtl_hooks): New.
	(cse_main): Set rtl_hooks instead of gen_lowpart.
	* emit-rtl.c (gen_lowpart): Remove.
	(gen_lowpart_general): Move to rtlhooks.c.
	* rtl.h (nonzero_bits, num_sign_bit_copies,
	struct rtl_hooks, rtl_hooks, general_rtl_hooks): New.
	(gen_lowpart_general): Remove.
	(gen_lowpart): Temporarily redefine as a macro.
	* rtlanal.c: Include function.h.
	(nonzero_bits, cached_nonzero_bits, nonzero_bits1,
	num_sign_bit_copies, cached_num_sign_bit_copies,
	num_sign_bit_copies1): New, from combine.c.
	* rtlhooks.c: New file.

From-SVN: r82234
This commit is contained in:
Paolo Bonzini 2004-05-25 12:04:17 +00:00 committed by Paolo Bonzini
parent 11338cda74
commit 2f93eea861
9 changed files with 1276 additions and 977 deletions

View File

@ -1,3 +1,34 @@
2004-05-25 Paolo Bonzini <bonzini@gnu.org>
* Makefile.in (OBJS): Add rtlhooks.o.
(rtlanal.o): Depend on function.h.
(cse.o): Depend on rtlhooks-def.h.
(combine.o): Depend on rtlhooks-def.h.
(rtlhooks.o): New rule.
* combine.c: Include rtlhooks-def.h.
(nonzero_bits, cached_nonzero_bits, nonzero_bits1,
num_sign_bit_copies, cached_num_sign_bit_copies,
num_sign_bit_copies1): Move most of the code to rtlanal.c.
(reg_nonzero_bits_for_combine,
reg_num_sign_bit_copies_for_combine): New functions holding
the remnants of the above.
(combine_rtl_hooks): New.
(combine_instructions): Set rtl_hooks instead of gen_lowpart.
* cse.c: Include rtlhooks-def.h.
(cse_rtl_hooks): New.
(cse_main): Set rtl_hooks instead of gen_lowpart.
* emit-rtl.c (gen_lowpart): Remove.
(gen_lowpart_general): Move to rtlhooks.c.
* rtl.h (nonzero_bits, num_sign_bit_copies,
struct rtl_hooks, rtl_hooks, general_rtl_hooks): New.
(gen_lowpart_general): Remove.
(gen_lowpart): Temporarily redefine as a macro.
* rtlanal.c: Include function.h.
(nonzero_bits, cached_nonzero_bits, nonzero_bits1,
num_sign_bit_copies, cached_num_sign_bit_copies,
num_sign_bit_copies1): New, from combine.c.
* rtlhooks.c: New file.
2004-05-25 Svein E. Seldal <Svein.Seldal@solidas.com>
* config/avr/avr.h (LONG_LONG_TYPE_SIZE): Changed long long type

View File

@ -901,7 +901,7 @@ OBJS-common = \
targhooks.o timevar.o toplev.o tracer.o tree.o tree-dump.o unroll.o \
varasm.o varray.o version.o vmsdbgout.o xcoffout.o alloc-pool.o \
et-forest.o cfghooks.o bt-load.o pretty-print.o $(GGC) web.o passes.o \
rtl-profile.o tree-profile.o
rtl-profile.o tree-profile.o rtlhooks.o
OBJS-md = $(out_object_file)
OBJS-archive = $(EXTRA_OBJS) $(host_hook_obj) tree-inline.o \
@ -1735,7 +1735,7 @@ print-rtl.o : print-rtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(RTL_H) $(TREE_H) hard-reg-set.h $(BASIC_BLOCK_H) real.h $(TM_P_H)
rtlanal.o : rtlanal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) toplev.h \
$(RTL_H) hard-reg-set.h $(TM_P_H) insn-config.h $(RECOG_H) real.h flags.h \
$(BASIC_BLOCK_H) $(REGS_H) output.h target.h
$(BASIC_BLOCK_H) $(REGS_H) output.h target.h function.h
errors.o : errors.c $(CONFIG_H) $(SYSTEM_H) errors.h
$(CC) -c $(ALL_CFLAGS) -DGENERATOR_FILE $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION)
@ -1837,7 +1837,7 @@ cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_
cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \
output.h function.h $(BASIC_BLOCK_H) $(GGC_H) $(TM_P_H) $(TIMEVAR_H) \
except.h $(TARGET_H) $(PARAMS_H)
except.h $(TARGET_H) $(PARAMS_H) rtlhooks-def.h
web.o : web.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
hard-reg-set.h flags.h $(BASIC_BLOCK_H) function.h output.h toplev.h df.h
gcse.o : gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
@ -1938,7 +1938,7 @@ dominance.o : dominance.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
hard-reg-set.h $(BASIC_BLOCK_H) et-forest.h
et-forest.o : et-forest.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) et-forest.h alloc-pool.h
combine.o : combine.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) flags.h \
function.h insn-config.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) \
function.h insn-config.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) rtlhooks-def.h \
$(BASIC_BLOCK_H) $(RECOG_H) real.h hard-reg-set.h toplev.h $(TM_P_H) $(TREE_H) $(TARGET_H)
regclass.o : regclass.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
hard-reg-set.h flags.h $(BASIC_BLOCK_H) $(REGS_H) insn-config.h $(RECOG_H) reload.h \
@ -1975,6 +1975,8 @@ reload1.o : reload1.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) real.
$(EXPR_H) $(OPTABS_H) reload.h $(REGS_H) hard-reg-set.h insn-config.h \
$(BASIC_BLOCK_H) $(RECOG_H) output.h function.h toplev.h $(TM_P_H) \
except.h $(TREE_H)
rtlhooks.o : rtlhooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
rtlhooks-def.h $(EXPR_H)
postreload.o : postreload.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) real.h flags.h \
$(EXPR_H) $(OPTABS_H) reload.h $(REGS_H) hard-reg-set.h insn-config.h \
$(BASIC_BLOCK_H) $(RECOG_H) output.h function.h toplev.h cselib.h $(TM_P_H) \

File diff suppressed because it is too large Load Diff

View File

@ -43,6 +43,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "except.h"
#include "target.h"
#include "params.h"
#include "rtlhooks-def.h"
/* The basic idea of common subexpression elimination is to go
through the code, keeping a record of expressions that would
@ -664,6 +665,12 @@ static int cse_change_cc_mode (rtx *, void *);
static void cse_change_cc_mode_insns (rtx, rtx, rtx);
static enum machine_mode cse_cc_succs (basic_block, rtx, rtx, bool);
#undef RTL_HOOKS_GEN_LOWPART
#define RTL_HOOKS_GEN_LOWPART gen_lowpart_if_possible
static const struct rtl_hooks cse_rtl_hooks = RTL_HOOKS_INITIALIZER;
/* Nonzero if X has the form (PLUS frame-pointer integer). We check for
virtual regs here because the simplify_*_operation routines are called
by integrate.c, which is called before virtual register instantiation. */
@ -6881,7 +6888,7 @@ cse_main (rtx f, int nregs, int after_loop, FILE *file)
constant_pool_entries_cost = 0;
constant_pool_entries_regcost = 0;
val.path_size = 0;
gen_lowpart = gen_lowpart_if_possible;
rtl_hooks = cse_rtl_hooks;
init_recog ();
init_alias_analysis ();
@ -7001,7 +7008,7 @@ cse_main (rtx f, int nregs, int after_loop, FILE *file)
free (uid_cuid);
free (reg_eqv_table);
free (val.path);
gen_lowpart = gen_lowpart_general;
rtl_hooks = general_rtl_hooks;
return cse_jumps_altered || recorded_label_ref;
}

View File

@ -97,8 +97,6 @@ rtx global_rtl[GR_MAX];
at the beginning of each function. */
static GTY(()) rtx static_regno_reg_rtx[FIRST_PSEUDO_REGISTER];
rtx (*gen_lowpart) (enum machine_mode mode, rtx x) = gen_lowpart_general;
/* We record floating-point CONST_DOUBLEs in each floating-point mode for
the values of 0, 1, and 2. For the integer entries and VOIDmode, we
record a copy of const[012]_rtx. */
@ -1124,62 +1122,6 @@ gen_imagpart (enum machine_mode mode, rtx x)
return gen_highpart (mode, x);
}
/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a value,
return an rtx (MEM, SUBREG, or CONST_INT) that refers to the
least-significant part of X.
MODE specifies how big a part of X to return;
it usually should not be larger than a word.
If X is a MEM whose address is a QUEUED, the value may be so also. */
rtx
gen_lowpart_general (enum machine_mode mode, rtx x)
{
rtx result = gen_lowpart_common (mode, x);
if (result)
return result;
else if (GET_CODE (x) == REG)
{
/* Must be a hard reg that's not valid in MODE. */
result = gen_lowpart_common (mode, copy_to_reg (x));
if (result == 0)
abort ();
return result;
}
else if (GET_CODE (x) == MEM)
{
/* The only additional case we can do is MEM. */
int offset = 0;
/* The following exposes the use of "x" to CSE. */
if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD
&& SCALAR_INT_MODE_P (GET_MODE (x))
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
GET_MODE_BITSIZE (GET_MODE (x)))
&& ! no_new_pseudos)
return gen_lowpart (mode, force_reg (GET_MODE (x), x));
if (WORDS_BIG_ENDIAN)
offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
- MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
if (BYTES_BIG_ENDIAN)
/* Adjust the address so that the address-after-the-data
is unchanged. */
offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
- MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
return adjust_address (x, mode, offset);
}
else if (GET_CODE (x) == ADDRESSOF)
return gen_lowpart (mode, force_reg (GET_MODE (x), x));
else
abort ();
}
/* Like `gen_lowpart', but refer to the most significant part.
This is used to access the imaginary part of a complex number. */
rtx
gen_highpart (enum machine_mode mode, rtx x)
{

View File

@ -1195,6 +1195,9 @@ extern unsigned int subreg_regno_offset (unsigned int, enum machine_mode,
extern bool subreg_offset_representable_p (unsigned int, enum machine_mode,
unsigned int, enum machine_mode);
extern unsigned int subreg_regno (rtx);
extern unsigned HOST_WIDE_INT nonzero_bits (rtx, enum machine_mode);
extern unsigned int num_sign_bit_copies (rtx, enum machine_mode);
/* 1 if RTX is a subreg containing a reg that is already known to be
sign- or zero-extended from the mode of the subreg to the mode of
@ -1598,9 +1601,6 @@ extern rtx gen_rtx_REG_offset (rtx, enum machine_mode, unsigned int, int);
extern rtx gen_label_rtx (void);
extern int subreg_hard_regno (rtx, int);
extern rtx gen_lowpart_common (enum machine_mode, rtx);
extern rtx gen_lowpart_general (enum machine_mode, rtx);
extern rtx (*gen_lowpart) (enum machine_mode mode, rtx x);
/* In cse.c */
extern rtx gen_lowpart_if_possible (enum machine_mode, rtx);
@ -2461,4 +2461,25 @@ extern void simplify_using_condition (rtx, rtx *, struct bitmap_head_def *);
/* In ra.c. */
extern void reg_alloc (void);
struct rtl_hooks
{
rtx (*gen_lowpart) (enum machine_mode, rtx);
rtx (*reg_nonzero_bits) (rtx, enum machine_mode, rtx, enum machine_mode,
unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT *);
rtx (*reg_num_sign_bit_copies) (rtx, enum machine_mode, rtx, enum machine_mode,
unsigned int, unsigned int *);
/* Whenever you add entries here, make sure you adjust hosthooks-def.h. */
};
/* Each pass can provide its own. */
extern struct rtl_hooks rtl_hooks;
/* ... but then it has to restore these. */
extern const struct rtl_hooks general_rtl_hooks;
/* Keep this for the nonce. */
#define gen_lowpart rtl_hooks.gen_lowpart
#endif /* ! GCC_RTL_H */

View File

@ -36,6 +36,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "basic-block.h"
#include "real.h"
#include "regs.h"
#include "function.h"
/* Forward declarations */
static int global_reg_mentioned_p_1 (rtx *, void *);
@ -47,6 +48,18 @@ static void parms_set (rtx, rtx, void *);
static bool hoist_test_store (rtx, rtx, regset);
static void hoist_update_store (rtx, rtx *, rtx, rtx);
static unsigned HOST_WIDE_INT cached_nonzero_bits (rtx, enum machine_mode,
rtx, enum machine_mode,
unsigned HOST_WIDE_INT);
static unsigned HOST_WIDE_INT nonzero_bits1 (rtx, enum machine_mode, rtx,
enum machine_mode,
unsigned HOST_WIDE_INT);
static unsigned int cached_num_sign_bit_copies (rtx, enum machine_mode, rtx,
enum machine_mode,
unsigned int);
static unsigned int num_sign_bit_copies1 (rtx, enum machine_mode, rtx,
enum machine_mode, unsigned int);
/* Bit flags that specify the machine subtype we are compiling for.
Bits are tested using macros TARGET_... defined in the tm.h file
and set by `-m...' switches. Must be defined in rtlanal.c. */
@ -3849,3 +3862,946 @@ default_address_cost (rtx x)
{
return rtx_cost (x, MEM);
}
unsigned HOST_WIDE_INT
nonzero_bits (rtx x, enum machine_mode mode)
{
return cached_nonzero_bits (x, mode, NULL_RTX, VOIDmode, 0);
}
unsigned int
num_sign_bit_copies (rtx x, enum machine_mode mode)
{
return cached_num_sign_bit_copies (x, mode, NULL_RTX, VOIDmode, 0);
}
/* The function cached_nonzero_bits is a wrapper around nonzero_bits1.
It avoids exponential behavior in nonzero_bits1 when X has
identical subexpressions on the first or the second level. */
static unsigned HOST_WIDE_INT
cached_nonzero_bits (rtx x, enum machine_mode mode, rtx known_x,
enum machine_mode known_mode,
unsigned HOST_WIDE_INT known_ret)
{
if (x == known_x && mode == known_mode)
return known_ret;
/* Try to find identical subexpressions. If found call
nonzero_bits1 on X with the subexpressions as KNOWN_X and the
precomputed value for the subexpression as KNOWN_RET. */
if (ARITHMETIC_P (x))
{
rtx x0 = XEXP (x, 0);
rtx x1 = XEXP (x, 1);
/* Check the first level. */
if (x0 == x1)
return nonzero_bits1 (x, mode, x0, mode,
cached_nonzero_bits (x0, mode, known_x,
known_mode, known_ret));
/* Check the second level. */
if (ARITHMETIC_P (x0)
&& (x1 == XEXP (x0, 0) || x1 == XEXP (x0, 1)))
return nonzero_bits1 (x, mode, x1, mode,
cached_nonzero_bits (x1, mode, known_x,
known_mode, known_ret));
if (ARITHMETIC_P (x1)
&& (x0 == XEXP (x1, 0) || x0 == XEXP (x1, 1)))
return nonzero_bits1 (x, mode, x0, mode,
cached_nonzero_bits (x0, mode, known_x,
known_mode, known_ret));
}
return nonzero_bits1 (x, mode, known_x, known_mode, known_ret);
}
/* We let num_sign_bit_copies recur into nonzero_bits as that is useful.
We don't let nonzero_bits recur into num_sign_bit_copies, because that
is less useful. We can't allow both, because that results in exponential
run time recursion. There is a nullstone testcase that triggered
this. This macro avoids accidental uses of num_sign_bit_copies. */
#define cached_num_sign_bit_copies sorry_i_am_preventing_exponential_behavior
/* Given an expression, X, compute which bits in X can be nonzero.
We don't care about bits outside of those defined in MODE.
For most X this is simply GET_MODE_MASK (GET_MODE (MODE)), but if X is
an arithmetic operation, we can do better. */
static unsigned HOST_WIDE_INT
nonzero_bits1 (rtx x, enum machine_mode mode, rtx known_x,
enum machine_mode known_mode,
unsigned HOST_WIDE_INT known_ret)
{
unsigned HOST_WIDE_INT nonzero = GET_MODE_MASK (mode);
unsigned HOST_WIDE_INT inner_nz;
enum rtx_code code;
unsigned int mode_width = GET_MODE_BITSIZE (mode);
/* For floating-point values, assume all bits are needed. */
if (FLOAT_MODE_P (GET_MODE (x)) || FLOAT_MODE_P (mode))
return nonzero;
/* If X is wider than MODE, use its mode instead. */
if (GET_MODE_BITSIZE (GET_MODE (x)) > mode_width)
{
mode = GET_MODE (x);
nonzero = GET_MODE_MASK (mode);
mode_width = GET_MODE_BITSIZE (mode);
}
if (mode_width > HOST_BITS_PER_WIDE_INT)
/* Our only callers in this case look for single bit values. So
just return the mode mask. Those tests will then be false. */
return nonzero;
#ifndef WORD_REGISTER_OPERATIONS
/* If MODE is wider than X, but both are a single word for both the host
and target machines, we can compute this from which bits of the
object might be nonzero in its own mode, taking into account the fact
that on many CISC machines, accessing an object in a wider mode
causes the high-order bits to become undefined. So they are
not known to be zero. */
if (GET_MODE (x) != VOIDmode && GET_MODE (x) != mode
&& GET_MODE_BITSIZE (GET_MODE (x)) <= BITS_PER_WORD
&& GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
&& GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (GET_MODE (x)))
{
nonzero &= cached_nonzero_bits (x, GET_MODE (x),
known_x, known_mode, known_ret);
nonzero |= GET_MODE_MASK (mode) & ~GET_MODE_MASK (GET_MODE (x));
return nonzero;
}
#endif
code = GET_CODE (x);
switch (code)
{
case REG:
#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
/* If pointers extend unsigned and this is a pointer in Pmode, say that
all the bits above ptr_mode are known to be zero. */
if (POINTERS_EXTEND_UNSIGNED && GET_MODE (x) == Pmode
&& REG_POINTER (x))
nonzero &= GET_MODE_MASK (ptr_mode);
#endif
/* Include declared information about alignment of pointers. */
/* ??? We don't properly preserve REG_POINTER changes across
pointer-to-integer casts, so we can't trust it except for
things that we know must be pointers. See execute/960116-1.c. */
if ((x == stack_pointer_rtx
|| x == frame_pointer_rtx
|| x == arg_pointer_rtx)
&& REGNO_POINTER_ALIGN (REGNO (x)))
{
unsigned HOST_WIDE_INT alignment
= REGNO_POINTER_ALIGN (REGNO (x)) / BITS_PER_UNIT;
#ifdef PUSH_ROUNDING
/* If PUSH_ROUNDING is defined, it is possible for the
stack to be momentarily aligned only to that amount,
so we pick the least alignment. */
if (x == stack_pointer_rtx && PUSH_ARGS)
alignment = MIN ((unsigned HOST_WIDE_INT) PUSH_ROUNDING (1),
alignment);
#endif
nonzero &= ~(alignment - 1);
}
{
unsigned HOST_WIDE_INT nonzero_for_hook = nonzero;
rtx new = rtl_hooks.reg_nonzero_bits (x, mode, known_x,
known_mode, known_ret,
&nonzero_for_hook);
if (new)
nonzero_for_hook &= cached_nonzero_bits (new, mode, known_x,
known_mode, known_ret);
return nonzero_for_hook;
}
case CONST_INT:
#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
/* If X is negative in MODE, sign-extend the value. */
if (INTVAL (x) > 0 && mode_width < BITS_PER_WORD
&& 0 != (INTVAL (x) & ((HOST_WIDE_INT) 1 << (mode_width - 1))))
return (INTVAL (x) | ((HOST_WIDE_INT) (-1) << mode_width));
#endif
return INTVAL (x);
case MEM:
#ifdef LOAD_EXTEND_OP
/* In many, if not most, RISC machines, reading a byte from memory
zeros the rest of the register. Noticing that fact saves a lot
of extra zero-extends. */
if (LOAD_EXTEND_OP (GET_MODE (x)) == ZERO_EXTEND)
nonzero &= GET_MODE_MASK (GET_MODE (x));
#endif
break;
case EQ: case NE:
case UNEQ: case LTGT:
case GT: case GTU: case UNGT:
case LT: case LTU: case UNLT:
case GE: case GEU: case UNGE:
case LE: case LEU: case UNLE:
case UNORDERED: case ORDERED:
/* If this produces an integer result, we know which bits are set.
Code here used to clear bits outside the mode of X, but that is
now done above. */
if (GET_MODE_CLASS (mode) == MODE_INT
&& mode_width <= HOST_BITS_PER_WIDE_INT)
nonzero = STORE_FLAG_VALUE;
break;
case NEG:
#if 0
/* Disabled to avoid exponential mutual recursion between nonzero_bits
and num_sign_bit_copies. */
if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x))
== GET_MODE_BITSIZE (GET_MODE (x)))
nonzero = 1;
#endif
if (GET_MODE_SIZE (GET_MODE (x)) < mode_width)
nonzero |= (GET_MODE_MASK (mode) & ~GET_MODE_MASK (GET_MODE (x)));
break;
case ABS:
#if 0
/* Disabled to avoid exponential mutual recursion between nonzero_bits
and num_sign_bit_copies. */
if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x))
== GET_MODE_BITSIZE (GET_MODE (x)))
nonzero = 1;
#endif
break;
case TRUNCATE:
nonzero &= (cached_nonzero_bits (XEXP (x, 0), mode,
known_x, known_mode, known_ret)
& GET_MODE_MASK (mode));
break;
case ZERO_EXTEND:
nonzero &= cached_nonzero_bits (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
if (GET_MODE (XEXP (x, 0)) != VOIDmode)
nonzero &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
break;
case SIGN_EXTEND:
/* If the sign bit is known clear, this is the same as ZERO_EXTEND.
Otherwise, show all the bits in the outer mode but not the inner
may be nonzero. */
inner_nz = cached_nonzero_bits (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
if (GET_MODE (XEXP (x, 0)) != VOIDmode)
{
inner_nz &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
if (inner_nz
& (((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1))))
inner_nz |= (GET_MODE_MASK (mode)
& ~GET_MODE_MASK (GET_MODE (XEXP (x, 0))));
}
nonzero &= inner_nz;
break;
case AND:
nonzero &= cached_nonzero_bits (XEXP (x, 0), mode,
known_x, known_mode, known_ret)
& cached_nonzero_bits (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
break;
case XOR: case IOR:
case UMIN: case UMAX: case SMIN: case SMAX:
{
unsigned HOST_WIDE_INT nonzero0 =
cached_nonzero_bits (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
/* Don't call nonzero_bits for the second time if it cannot change
anything. */
if ((nonzero & nonzero0) != nonzero)
nonzero &= nonzero0
| cached_nonzero_bits (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
}
break;
case PLUS: case MINUS:
case MULT:
case DIV: case UDIV:
case MOD: case UMOD:
/* We can apply the rules of arithmetic to compute the number of
high- and low-order zero bits of these operations. We start by
computing the width (position of the highest-order nonzero bit)
and the number of low-order zero bits for each value. */
{
unsigned HOST_WIDE_INT nz0 =
cached_nonzero_bits (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
unsigned HOST_WIDE_INT nz1 =
cached_nonzero_bits (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
int sign_index = GET_MODE_BITSIZE (GET_MODE (x)) - 1;
int width0 = floor_log2 (nz0) + 1;
int width1 = floor_log2 (nz1) + 1;
int low0 = floor_log2 (nz0 & -nz0);
int low1 = floor_log2 (nz1 & -nz1);
HOST_WIDE_INT op0_maybe_minusp
= (nz0 & ((HOST_WIDE_INT) 1 << sign_index));
HOST_WIDE_INT op1_maybe_minusp
= (nz1 & ((HOST_WIDE_INT) 1 << sign_index));
unsigned int result_width = mode_width;
int result_low = 0;
switch (code)
{
case PLUS:
result_width = MAX (width0, width1) + 1;
result_low = MIN (low0, low1);
break;
case MINUS:
result_low = MIN (low0, low1);
break;
case MULT:
result_width = width0 + width1;
result_low = low0 + low1;
break;
case DIV:
if (width1 == 0)
break;
if (! op0_maybe_minusp && ! op1_maybe_minusp)
result_width = width0;
break;
case UDIV:
if (width1 == 0)
break;
result_width = width0;
break;
case MOD:
if (width1 == 0)
break;
if (! op0_maybe_minusp && ! op1_maybe_minusp)
result_width = MIN (width0, width1);
result_low = MIN (low0, low1);
break;
case UMOD:
if (width1 == 0)
break;
result_width = MIN (width0, width1);
result_low = MIN (low0, low1);
break;
default:
abort ();
}
if (result_width < mode_width)
nonzero &= ((HOST_WIDE_INT) 1 << result_width) - 1;
if (result_low > 0)
nonzero &= ~(((HOST_WIDE_INT) 1 << result_low) - 1);
#ifdef POINTERS_EXTEND_UNSIGNED
/* If pointers extend unsigned and this is an addition or subtraction
to a pointer in Pmode, all the bits above ptr_mode are known to be
zero. */
if (POINTERS_EXTEND_UNSIGNED > 0 && GET_MODE (x) == Pmode
&& (code == PLUS || code == MINUS)
&& GET_CODE (XEXP (x, 0)) == REG && REG_POINTER (XEXP (x, 0)))
nonzero &= GET_MODE_MASK (ptr_mode);
#endif
}
break;
case ZERO_EXTRACT:
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
nonzero &= ((HOST_WIDE_INT) 1 << INTVAL (XEXP (x, 1))) - 1;
break;
case SUBREG:
/* If this is a SUBREG formed for a promoted variable that has
been zero-extended, we know that at least the high-order bits
are zero, though others might be too. */
if (SUBREG_PROMOTED_VAR_P (x) && SUBREG_PROMOTED_UNSIGNED_P (x) > 0)
nonzero = GET_MODE_MASK (GET_MODE (x))
& cached_nonzero_bits (SUBREG_REG (x), GET_MODE (x),
known_x, known_mode, known_ret);
/* If the inner mode is a single word for both the host and target
machines, we can compute this from which bits of the inner
object might be nonzero. */
if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) <= BITS_PER_WORD
&& (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))
<= HOST_BITS_PER_WIDE_INT))
{
nonzero &= cached_nonzero_bits (SUBREG_REG (x), mode,
known_x, known_mode, known_ret);
#if defined (WORD_REGISTER_OPERATIONS) && defined (LOAD_EXTEND_OP)
/* If this is a typical RISC machine, we only have to worry
about the way loads are extended. */
if ((LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (x))) == SIGN_EXTEND
? (((nonzero
& (((unsigned HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) - 1))))
!= 0))
: LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (x))) != ZERO_EXTEND)
|| GET_CODE (SUBREG_REG (x)) != MEM)
#endif
{
/* On many CISC machines, accessing an object in a wider mode
causes the high-order bits to become undefined. So they are
not known to be zero. */
if (GET_MODE_SIZE (GET_MODE (x))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
nonzero |= (GET_MODE_MASK (GET_MODE (x))
& ~GET_MODE_MASK (GET_MODE (SUBREG_REG (x))));
}
}
break;
case ASHIFTRT:
case LSHIFTRT:
case ASHIFT:
case ROTATE:
/* The nonzero bits are in two classes: any bits within MODE
that aren't in GET_MODE (x) are always significant. The rest of the
nonzero bits are those that are significant in the operand of
the shift when shifted the appropriate number of bits. This
shows that high-order bits are cleared by the right shift and
low-order bits by left shifts. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= 0
&& INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
{
enum machine_mode inner_mode = GET_MODE (x);
unsigned int width = GET_MODE_BITSIZE (inner_mode);
int count = INTVAL (XEXP (x, 1));
unsigned HOST_WIDE_INT mode_mask = GET_MODE_MASK (inner_mode);
unsigned HOST_WIDE_INT op_nonzero =
cached_nonzero_bits (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
unsigned HOST_WIDE_INT inner = op_nonzero & mode_mask;
unsigned HOST_WIDE_INT outer = 0;
if (mode_width > width)
outer = (op_nonzero & nonzero & ~mode_mask);
if (code == LSHIFTRT)
inner >>= count;
else if (code == ASHIFTRT)
{
inner >>= count;
/* If the sign bit may have been nonzero before the shift, we
need to mark all the places it could have been copied to
by the shift as possibly nonzero. */
if (inner & ((HOST_WIDE_INT) 1 << (width - 1 - count)))
inner |= (((HOST_WIDE_INT) 1 << count) - 1) << (width - count);
}
else if (code == ASHIFT)
inner <<= count;
else
inner = ((inner << (count % width)
| (inner >> (width - (count % width)))) & mode_mask);
nonzero &= (outer | inner);
}
break;
case FFS:
case POPCOUNT:
/* This is at most the number of bits in the mode. */
nonzero = ((HOST_WIDE_INT) 2 << (floor_log2 (mode_width))) - 1;
break;
case CLZ:
/* If CLZ has a known value at zero, then the nonzero bits are
that value, plus the number of bits in the mode minus one. */
if (CLZ_DEFINED_VALUE_AT_ZERO (mode, nonzero))
nonzero |= ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width))) - 1;
else
nonzero = -1;
break;
case CTZ:
/* If CTZ has a known value at zero, then the nonzero bits are
that value, plus the number of bits in the mode minus one. */
if (CTZ_DEFINED_VALUE_AT_ZERO (mode, nonzero))
nonzero |= ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width))) - 1;
else
nonzero = -1;
break;
case PARITY:
nonzero = 1;
break;
case IF_THEN_ELSE:
{
unsigned HOST_WIDE_INT nonzero_true =
cached_nonzero_bits (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
/* Don't call nonzero_bits for the second time if it cannot change
anything. */
if ((nonzero & nonzero_true) != nonzero)
nonzero &= nonzero_true
| cached_nonzero_bits (XEXP (x, 2), mode,
known_x, known_mode, known_ret);
}
break;
default:
break;
}
return nonzero;
}
/* See the macro definition above. */
#undef cached_num_sign_bit_copies
/* The function cached_num_sign_bit_copies is a wrapper around
num_sign_bit_copies1. It avoids exponential behavior in
num_sign_bit_copies1 when X has identical subexpressions on the
first or the second level. */
static unsigned int
cached_num_sign_bit_copies (rtx x, enum machine_mode mode, rtx known_x,
enum machine_mode known_mode,
unsigned int known_ret)
{
if (x == known_x && mode == known_mode)
return known_ret;
/* Try to find identical subexpressions. If found call
num_sign_bit_copies1 on X with the subexpressions as KNOWN_X and
the precomputed value for the subexpression as KNOWN_RET. */
if (ARITHMETIC_P (x))
{
rtx x0 = XEXP (x, 0);
rtx x1 = XEXP (x, 1);
/* Check the first level. */
if (x0 == x1)
return
num_sign_bit_copies1 (x, mode, x0, mode,
cached_num_sign_bit_copies (x0, mode, known_x,
known_mode,
known_ret));
/* Check the second level. */
if (ARITHMETIC_P (x0)
&& (x1 == XEXP (x0, 0) || x1 == XEXP (x0, 1)))
return
num_sign_bit_copies1 (x, mode, x1, mode,
cached_num_sign_bit_copies (x1, mode, known_x,
known_mode,
known_ret));
if (ARITHMETIC_P (x1)
&& (x0 == XEXP (x1, 0) || x0 == XEXP (x1, 1)))
return
num_sign_bit_copies1 (x, mode, x0, mode,
cached_num_sign_bit_copies (x0, mode, known_x,
known_mode,
known_ret));
}
return num_sign_bit_copies1 (x, mode, known_x, known_mode, known_ret);
}
/* Return the number of bits at the high-order end of X that are known to
be equal to the sign bit. X will be used in mode MODE; if MODE is
VOIDmode, X will be used in its own mode. The returned value will always
be between 1 and the number of bits in MODE. */
static unsigned int
num_sign_bit_copies1 (rtx x, enum machine_mode mode, rtx known_x,
enum machine_mode known_mode,
unsigned int known_ret)
{
enum rtx_code code = GET_CODE (x);
unsigned int bitwidth = GET_MODE_BITSIZE (mode);
int num0, num1, result;
unsigned HOST_WIDE_INT nonzero;
/* If we weren't given a mode, use the mode of X. If the mode is still
VOIDmode, we don't know anything. Likewise if one of the modes is
floating-point. */
if (mode == VOIDmode)
mode = GET_MODE (x);
if (mode == VOIDmode || FLOAT_MODE_P (mode) || FLOAT_MODE_P (GET_MODE (x)))
return 1;
/* For a smaller object, just ignore the high bits. */
if (bitwidth < GET_MODE_BITSIZE (GET_MODE (x)))
{
num0 = cached_num_sign_bit_copies (x, GET_MODE (x),
known_x, known_mode, known_ret);
return MAX (1,
num0 - (int) (GET_MODE_BITSIZE (GET_MODE (x)) - bitwidth));
}
if (GET_MODE (x) != VOIDmode && bitwidth > GET_MODE_BITSIZE (GET_MODE (x)))
{
#ifndef WORD_REGISTER_OPERATIONS
/* If this machine does not do all register operations on the entire
register and MODE is wider than the mode of X, we can say nothing
at all about the high-order bits. */
return 1;
#else
/* Likewise on machines that do, if the mode of the object is smaller
than a word and loads of that size don't sign extend, we can say
nothing about the high order bits. */
if (GET_MODE_BITSIZE (GET_MODE (x)) < BITS_PER_WORD
#ifdef LOAD_EXTEND_OP
&& LOAD_EXTEND_OP (GET_MODE (x)) != SIGN_EXTEND
#endif
)
return 1;
#endif
}
switch (code)
{
case REG:
#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
/* If pointers extend signed and this is a pointer in Pmode, say that
all the bits above ptr_mode are known to be sign bit copies. */
if (! POINTERS_EXTEND_UNSIGNED && GET_MODE (x) == Pmode && mode == Pmode
&& REG_POINTER (x))
return GET_MODE_BITSIZE (Pmode) - GET_MODE_BITSIZE (ptr_mode) + 1;
#endif
{
unsigned int copies_for_hook = 1, copies = 1;
rtx new = rtl_hooks.reg_num_sign_bit_copies (x, mode, known_x,
known_mode, known_ret,
&copies_for_hook);
if (new)
copies = cached_num_sign_bit_copies (new, mode, known_x,
known_mode, known_ret);
if (copies > 1 || copies_for_hook > 1)
return MAX (copies, copies_for_hook);
/* Else, use nonzero_bits to guess num_sign_bit_copies (see below). */
}
break;
case MEM:
#ifdef LOAD_EXTEND_OP
/* Some RISC machines sign-extend all loads of smaller than a word. */
if (LOAD_EXTEND_OP (GET_MODE (x)) == SIGN_EXTEND)
return MAX (1, ((int) bitwidth
- (int) GET_MODE_BITSIZE (GET_MODE (x)) + 1));
#endif
break;
case CONST_INT:
/* If the constant is negative, take its 1's complement and remask.
Then see how many zero bits we have. */
nonzero = INTVAL (x) & GET_MODE_MASK (mode);
if (bitwidth <= HOST_BITS_PER_WIDE_INT
&& (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
nonzero = (~nonzero) & GET_MODE_MASK (mode);
return (nonzero == 0 ? bitwidth : bitwidth - floor_log2 (nonzero) - 1);
case SUBREG:
/* If this is a SUBREG for a promoted object that is sign-extended
and we are looking at it in a wider mode, we know that at least the
high-order bits are known to be sign bit copies. */
if (SUBREG_PROMOTED_VAR_P (x) && ! SUBREG_PROMOTED_UNSIGNED_P (x))
{
num0 = cached_num_sign_bit_copies (SUBREG_REG (x), mode,
known_x, known_mode, known_ret);
return MAX ((int) bitwidth
- (int) GET_MODE_BITSIZE (GET_MODE (x)) + 1,
num0);
}
/* For a smaller object, just ignore the high bits. */
if (bitwidth <= GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))))
{
num0 = cached_num_sign_bit_copies (SUBREG_REG (x), VOIDmode,
known_x, known_mode, known_ret);
return MAX (1, (num0
- (int) (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))
- bitwidth)));
}
#ifdef WORD_REGISTER_OPERATIONS
#ifdef LOAD_EXTEND_OP
/* For paradoxical SUBREGs on machines where all register operations
affect the entire register, just look inside. Note that we are
passing MODE to the recursive call, so the number of sign bit copies
will remain relative to that mode, not the inner mode. */
/* This works only if loads sign extend. Otherwise, if we get a
reload for the inner part, it may be loaded from the stack, and
then we lose all sign bit copies that existed before the store
to the stack. */
if ((GET_MODE_SIZE (GET_MODE (x))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
&& LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (x))) == SIGN_EXTEND
&& GET_CODE (SUBREG_REG (x)) == MEM)
return cached_num_sign_bit_copies (SUBREG_REG (x), mode,
known_x, known_mode, known_ret);
#endif
#endif
break;
case SIGN_EXTRACT:
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
return MAX (1, (int) bitwidth - INTVAL (XEXP (x, 1)));
break;
case SIGN_EXTEND:
return (bitwidth - GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
+ cached_num_sign_bit_copies (XEXP (x, 0), VOIDmode,
known_x, known_mode, known_ret));
case TRUNCATE:
/* For a smaller object, just ignore the high bits. */
num0 = cached_num_sign_bit_copies (XEXP (x, 0), VOIDmode,
known_x, known_mode, known_ret);
return MAX (1, (num0 - (int) (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
- bitwidth)));
case NOT:
return cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
case ROTATE: case ROTATERT:
/* If we are rotating left by a number of bits less than the number
of sign bit copies, we can just subtract that amount from the
number. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= 0
&& INTVAL (XEXP (x, 1)) < (int) bitwidth)
{
num0 = cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
return MAX (1, num0 - (code == ROTATE ? INTVAL (XEXP (x, 1))
: (int) bitwidth - INTVAL (XEXP (x, 1))));
}
break;
case NEG:
/* In general, this subtracts one sign bit copy. But if the value
is known to be positive, the number of sign bit copies is the
same as that of the input. Finally, if the input has just one bit
that might be nonzero, all the bits are copies of the sign bit. */
num0 = cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
if (bitwidth > HOST_BITS_PER_WIDE_INT)
return num0 > 1 ? num0 - 1 : 1;
nonzero = nonzero_bits (XEXP (x, 0), mode);
if (nonzero == 1)
return bitwidth;
if (num0 > 1
&& (((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero))
num0--;
return num0;
case IOR: case AND: case XOR:
case SMIN: case SMAX: case UMIN: case UMAX:
/* Logical operations will preserve the number of sign-bit copies.
MIN and MAX operations always return one of the operands. */
num0 = cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
num1 = cached_num_sign_bit_copies (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
return MIN (num0, num1);
case PLUS: case MINUS:
/* For addition and subtraction, we can have a 1-bit carry. However,
if we are subtracting 1 from a positive number, there will not
be such a carry. Furthermore, if the positive number is known to
be 0 or 1, we know the result is either -1 or 0. */
if (code == PLUS && XEXP (x, 1) == constm1_rtx
&& bitwidth <= HOST_BITS_PER_WIDE_INT)
{
nonzero = nonzero_bits (XEXP (x, 0), mode);
if ((((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero) == 0)
return (nonzero == 1 || nonzero == 0 ? bitwidth
: bitwidth - floor_log2 (nonzero) - 1);
}
num0 = cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
num1 = cached_num_sign_bit_copies (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
result = MAX (1, MIN (num0, num1) - 1);
#ifdef POINTERS_EXTEND_UNSIGNED
/* If pointers extend signed and this is an addition or subtraction
to a pointer in Pmode, all the bits above ptr_mode are known to be
sign bit copies. */
if (! POINTERS_EXTEND_UNSIGNED && GET_MODE (x) == Pmode
&& (code == PLUS || code == MINUS)
&& GET_CODE (XEXP (x, 0)) == REG && REG_POINTER (XEXP (x, 0)))
result = MAX ((int) (GET_MODE_BITSIZE (Pmode)
- GET_MODE_BITSIZE (ptr_mode) + 1),
result);
#endif
return result;
case MULT:
/* The number of bits of the product is the sum of the number of
bits of both terms. However, unless one of the terms if known
to be positive, we must allow for an additional bit since negating
a negative number can remove one sign bit copy. */
num0 = cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
num1 = cached_num_sign_bit_copies (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
result = bitwidth - (bitwidth - num0) - (bitwidth - num1);
if (result > 0
&& (bitwidth > HOST_BITS_PER_WIDE_INT
|| (((nonzero_bits (XEXP (x, 0), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
&& ((nonzero_bits (XEXP (x, 1), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0))))
result--;
return MAX (1, result);
case UDIV:
/* The result must be <= the first operand. If the first operand
has the high bit set, we know nothing about the number of sign
bit copies. */
if (bitwidth > HOST_BITS_PER_WIDE_INT)
return 1;
else if ((nonzero_bits (XEXP (x, 0), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
return 1;
else
return cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
case UMOD:
/* The result must be <= the second operand. */
return cached_num_sign_bit_copies (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
case DIV:
/* Similar to unsigned division, except that we have to worry about
the case where the divisor is negative, in which case we have
to add 1. */
result = cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
if (result > 1
&& (bitwidth > HOST_BITS_PER_WIDE_INT
|| (nonzero_bits (XEXP (x, 1), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0))
result--;
return result;
case MOD:
result = cached_num_sign_bit_copies (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
if (result > 1
&& (bitwidth > HOST_BITS_PER_WIDE_INT
|| (nonzero_bits (XEXP (x, 1), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0))
result--;
return result;
case ASHIFTRT:
/* Shifts by a constant add to the number of bits equal to the
sign bit. */
num0 = cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) > 0)
num0 = MIN ((int) bitwidth, num0 + INTVAL (XEXP (x, 1)));
return num0;
case ASHIFT:
/* Left shifts destroy copies. */
if (GET_CODE (XEXP (x, 1)) != CONST_INT
|| INTVAL (XEXP (x, 1)) < 0
|| INTVAL (XEXP (x, 1)) >= (int) bitwidth)
return 1;
num0 = cached_num_sign_bit_copies (XEXP (x, 0), mode,
known_x, known_mode, known_ret);
return MAX (1, num0 - INTVAL (XEXP (x, 1)));
case IF_THEN_ELSE:
num0 = cached_num_sign_bit_copies (XEXP (x, 1), mode,
known_x, known_mode, known_ret);
num1 = cached_num_sign_bit_copies (XEXP (x, 2), mode,
known_x, known_mode, known_ret);
return MIN (num0, num1);
case EQ: case NE: case GE: case GT: case LE: case LT:
case UNEQ: case LTGT: case UNGE: case UNGT: case UNLE: case UNLT:
case GEU: case GTU: case LEU: case LTU:
case UNORDERED: case ORDERED:
/* If the constant is negative, take its 1's complement and remask.
Then see how many zero bits we have. */
nonzero = STORE_FLAG_VALUE;
if (bitwidth <= HOST_BITS_PER_WIDE_INT
&& (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
nonzero = (~nonzero) & GET_MODE_MASK (mode);
return (nonzero == 0 ? bitwidth : bitwidth - floor_log2 (nonzero) - 1);
default:
break;
}
/* If we haven't been able to figure it out by one of the above rules,
see if some of the high-order bits are known to be zero. If so,
count those bits and return one less than that amount. If we can't
safely compute the mask for this mode, always return BITWIDTH. */
bitwidth = GET_MODE_BITSIZE (mode);
if (bitwidth > HOST_BITS_PER_WIDE_INT)
return 1;
nonzero = nonzero_bits (x, mode);
return nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))
? 1 : bitwidth - floor_log2 (nonzero) - 1;
}

46
gcc/rtlhooks-def.h Normal file
View File

@ -0,0 +1,46 @@
/* Default macros to initialize an rtl_hooks data structure.
Copyright 2004 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef GCC_RTL_HOOKS_DEF_H
#define GCC_RTL_HOOKS_DEF_H
#include "rtl.h"
#define RTL_HOOKS_GEN_LOWPART gen_lowpart_general
#define RTL_HOOKS_REG_NONZERO_REG_BITS reg_nonzero_bits_general
#define RTL_HOOKS_REG_NUM_SIGN_BIT_COPIES reg_num_sign_bit_copies_general
/* The structure is defined in rtl.h. */
#define RTL_HOOKS_INITIALIZER { \
RTL_HOOKS_GEN_LOWPART, \
RTL_HOOKS_REG_NONZERO_REG_BITS, \
RTL_HOOKS_REG_NUM_SIGN_BIT_COPIES, \
}
extern rtx gen_lowpart_general (enum machine_mode, rtx);
extern rtx reg_nonzero_bits_general (rtx, enum machine_mode, rtx,
enum machine_mode,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT *);
extern rtx reg_num_sign_bit_copies_general (rtx, enum machine_mode, rtx,
enum machine_mode,
unsigned int, unsigned int *);
#endif /* GCC_RTL_HOOKS_DEF_H */

105
gcc/rtlhooks.c Normal file
View File

@ -0,0 +1,105 @@
/* Generic hooks for the RTL middle-end.
Copyright (C) 2004 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "rtlhooks-def.h"
#include "expr.h"
/* For speed, we will copy the RTX hooks struct member-by-member
instead of doing indirect calls. For these reason, we initialize
*two* struct rtl_hooks globals: rtl_hooks is the one that is used
to actually call the hooks, while general_rtl_hooks is used
to restore the hooks by passes that modify them. */
const struct rtl_hooks general_rtl_hooks = RTL_HOOKS_INITIALIZER;
struct rtl_hooks rtl_hooks = RTL_HOOKS_INITIALIZER;
rtx
gen_lowpart_general (enum machine_mode mode, rtx x)
{
rtx result = gen_lowpart_common (mode, x);
if (result)
return result;
else if (GET_CODE (x) == REG)
{
/* Must be a hard reg that's not valid in MODE. */
result = gen_lowpart_common (mode, copy_to_reg (x));
if (result == 0)
abort ();
return result;
}
else if (GET_CODE (x) == MEM)
{
/* The only additional case we can do is MEM. */
int offset = 0;
/* The following exposes the use of "x" to CSE. */
if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD
&& SCALAR_INT_MODE_P (GET_MODE (x))
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
GET_MODE_BITSIZE (GET_MODE (x)))
&& ! no_new_pseudos)
return gen_lowpart_general (mode, force_reg (GET_MODE (x), x));
if (WORDS_BIG_ENDIAN)
offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
- MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
if (BYTES_BIG_ENDIAN)
/* Adjust the address so that the address-after-the-data
is unchanged. */
offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
- MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
return adjust_address (x, mode, offset);
}
else if (GET_CODE (x) == ADDRESSOF)
return gen_lowpart_general (mode, force_reg (GET_MODE (x), x));
else
abort ();
}
rtx
reg_num_sign_bit_copies_general (rtx x ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED,
rtx known_x ATTRIBUTE_UNUSED,
enum machine_mode known_mode ATTRIBUTE_UNUSED,
unsigned int known_ret ATTRIBUTE_UNUSED,
unsigned int *result ATTRIBUTE_UNUSED)
{
return NULL;
}
rtx
reg_nonzero_bits_general (rtx x ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED,
rtx known_x ATTRIBUTE_UNUSED,
enum machine_mode known_mode ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT known_ret ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT *nonzero ATTRIBUTE_UNUSED)
{
return NULL;
}