Commit 273a2526 by Richard Sandiford Committed by Richard Sandiford

md.texi (shift patterns): New anchor.

	* doc/md.texi (shift patterns): New anchor.  Add reference to
	TARGET_SHIFT_TRUNCATION_MASK.
	* doc/tm.texi (TARGET_SHIFT_TRUNCATION_MASK): Document.
	* target.h (shift_truncation_mask): New target hook.
	* targhook.h (default_shift_truncation_mask): Declare.
	* targhook.c (default_shift_truncation_mask): Define.
	* target-def.h (TARGET_SHIFT_TRUNCATION_MASK): Define.
	(TARGET_INITIALIZER): Include it.
	* simplify-rtx.c (simplify_binary_operation): Combine ASHIFT, ASHIFTRT
	and LSHIFTRT cases.  Truncate arg1 if SHIFT_COUNT_TRUNCATED, otherwise
	reject all out-of-range values.  Fix sign-extension code for modes
	whose width is smaller than HOST_BITS_PER_WIDE_INT.
	* optabs.c (simplify_expand_binop, force_expand_binop): New functions.
	(expand_superword_shift, expand_subword_shift): Likewise.
	(expand_doubleword_shift_condmove, expand_doubleword_shift): Likewise.
	(expand_binop): Use them to implement double-word shifts.
	* config/arm/arm.c (arm_shift_truncation_mask): New function.
	(TARGET_SHIFT_TRUNCATION_MASK): Define.

From-SVN: r87079
parent caf29de7
2004-09-04 Richard Sandiford <rsandifo@redhat.com>
* doc/md.texi (shift patterns): New anchor. Add reference to
TARGET_SHIFT_TRUNCATION_MASK.
* doc/tm.texi (TARGET_SHIFT_TRUNCATION_MASK): Document.
* target.h (shift_truncation_mask): New target hook.
* targhook.h (default_shift_truncation_mask): Declare.
* targhook.c (default_shift_truncation_mask): Define.
* target-def.h (TARGET_SHIFT_TRUNCATION_MASK): Define.
(TARGET_INITIALIZER): Include it.
* simplify-rtx.c (simplify_binary_operation): Combine ASHIFT, ASHIFTRT
and LSHIFTRT cases. Truncate arg1 if SHIFT_COUNT_TRUNCATED, otherwise
reject all out-of-range values. Fix sign-extension code for modes
whose width is smaller than HOST_BITS_PER_WIDE_INT.
* optabs.c (simplify_expand_binop, force_expand_binop): New functions.
(expand_superword_shift, expand_subword_shift): Likewise.
(expand_doubleword_shift_condmove, expand_doubleword_shift): Likewise.
(expand_binop): Use them to implement double-word shifts.
* config/arm/arm.c (arm_shift_truncation_mask): New function.
(TARGET_SHIFT_TRUNCATION_MASK): Define.
2004-09-04 Jan Hubicka <jh@suse.cz>
* tree.c (iterate_hash_expr): Optimize, avoid use of iterative_hash_object.
......
......@@ -175,7 +175,7 @@ static bool arm_cxx_cdtor_returns_this (void);
static bool arm_cxx_key_method_may_be_inline (void);
static bool arm_cxx_export_class_data (void);
static void arm_init_libfuncs (void);
static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode);
/* Initialize the GCC target structure. */
#if TARGET_DLLIMPORT_DECL_ATTRIBUTES
......@@ -248,6 +248,8 @@ static void arm_init_libfuncs (void);
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST arm_address_cost
#undef TARGET_SHIFT_TRUNCATION_MASK
#define TARGET_SHIFT_TRUNCATION_MASK arm_shift_truncation_mask
#undef TARGET_VECTOR_MODE_SUPPORTED_P
#define TARGET_VECTOR_MODE_SUPPORTED_P arm_vector_mode_supported_p
......@@ -14334,3 +14336,14 @@ arm_vector_mode_supported_p (enum machine_mode mode)
return false;
}
/* Implement TARGET_SHIFT_TRUNCATION_MASK. SImode shifts use normal
ARM insns and therefore guarantee that the shift count is modulo 256.
DImode shifts (those implemented by lib1funcs.asm or by optabs.c)
guarantee no particular behavior for out-of-range counts. */
static unsigned HOST_WIDE_INT
arm_shift_truncation_mask (enum machine_mode mode)
{
return mode == SImode ? 255 : 0;
}
......@@ -2884,13 +2884,16 @@ quotient or remainder and generate the appropriate instruction.
@item @samp{udivmod@var{m}4}
Similar, but does unsigned division.
@anchor{shift patterns}
@cindex @code{ashl@var{m}3} instruction pattern
@item @samp{ashl@var{m}3}
Arithmetic-shift operand 1 left by a number of bits specified by operand
2, and store the result in operand 0. Here @var{m} is the mode of
operand 0 and operand 1; operand 2's mode is specified by the
instruction pattern, and the compiler will convert the operand to that
mode before generating the instruction.
mode before generating the instruction. The meaning of out-of-range shift
counts can optionally be specified by @code{TARGET_SHIFT_TRUNCATION_MASK}.
@xref{TARGET_SHIFT_TRUNCATION_MASK}.
@cindex @code{ashr@var{m}3} instruction pattern
@cindex @code{lshr@var{m}3} instruction pattern
......
......@@ -8749,6 +8749,31 @@ the implied truncation of the shift instructions.
You need not define this macro if it would always have the value of zero.
@end defmac
@anchor{TARGET_SHIFT_TRUNCATION_MASK}
@deftypefn {Target Hook} int TARGET_SHIFT_TRUNCATION_MASK (enum machine_mode @var{mode})
This function describes how the standard shift patterns for @var{mode}
deal with shifts by negative amounts or by more than the width of the mode.
@xref{shift patterns}.
On many machines, the shift patterns will apply a mask @var{m} to the
shift count, meaning that a fixed-width shift of @var{x} by @var{y} is
equivalent to an arbitrary-width shift of @var{x} by @var{y & m}. If
this is true for mode @var{mode}, the function should return @var{m},
otherwise it should return 0. A return value of 0 indicates that no
particular behavior is guaranteed.
Note that, unlike @code{SHIFT_COUNT_TRUNCATED}, this function does
@emph{not} apply to general shift rtxes; it applies only to instructions
that are generated by the named shift patterns.
The default implementation of this function returns
@code{GET_MODE_BITSIZE (@var{mode}) - 1} if @code{SHIFT_COUNT_TRUNCATED}
and 0 otherwise. This definition is always safe, but if
@code{SHIFT_COUNT_TRUNCATED} is false, and some shift patterns
nevertheless truncate the shift count, you may get better code
by overriding it.
@end deftypefn
@defmac TRULY_NOOP_TRUNCATION (@var{outprec}, @var{inprec})
A C expression which is nonzero if on this machine it is safe to
``convert'' an integer of @var{inprec} bits to one of @var{outprec}
......
......@@ -312,7 +312,356 @@ optab_for_tree_code (enum tree_code code, tree type)
return NULL;
}
}
/* Like expand_binop, but return a constant rtx if the result can be
calculated at compile time. The arguments and return value are
otherwise the same as for expand_binop. */
static rtx
simplify_expand_binop (enum machine_mode mode, optab binoptab,
rtx op0, rtx op1, rtx target, int unsignedp,
enum optab_methods methods)
{
if (CONSTANT_P (op0) && CONSTANT_P (op1))
return simplify_gen_binary (binoptab->code, mode, op0, op1);
else
return expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods);
}
/* Like simplify_expand_binop, but always put the result in TARGET.
Return true if the expansion succeeded. */
static bool
force_expand_binop (enum machine_mode mode, optab binoptab,
rtx op0, rtx op1, rtx target, int unsignedp,
enum optab_methods methods)
{
rtx x = simplify_expand_binop (mode, binoptab, op0, op1,
target, unsignedp, methods);
if (x == 0)
return false;
if (x != target)
emit_move_insn (target, x);
return true;
}
/* This subroutine of expand_doubleword_shift handles the cases in which
the effective shift value is >= BITS_PER_WORD. The arguments and return
value are the same as for the parent routine, except that SUPERWORD_OP1
is the shift count to use when shifting OUTOF_INPUT into INTO_TARGET.
INTO_TARGET may be null if the caller has decided to calculate it. */
static bool
expand_superword_shift (optab binoptab, rtx outof_input, rtx superword_op1,
rtx outof_target, rtx into_target,
int unsignedp, enum optab_methods methods)
{
if (into_target != 0)
if (!force_expand_binop (word_mode, binoptab, outof_input, superword_op1,
into_target, unsignedp, methods))
return false;
if (outof_target != 0)
{
/* For a signed right shift, we must fill OUTOF_TARGET with copies
of the sign bit, otherwise we must fill it with zeros. */
if (binoptab != ashr_optab)
emit_move_insn (outof_target, CONST0_RTX (word_mode));
else
if (!force_expand_binop (word_mode, binoptab,
outof_input, GEN_INT (BITS_PER_WORD - 1),
outof_target, unsignedp, methods))
return false;
}
return true;
}
/* This subroutine of expand_doubleword_shift handles the cases in which
the effective shift value is < BITS_PER_WORD. The arguments and return
value are the same as for the parent routine. */
static bool
expand_subword_shift (enum machine_mode op1_mode, optab binoptab,
rtx outof_input, rtx into_input, rtx op1,
rtx outof_target, rtx into_target,
int unsignedp, enum optab_methods methods,
unsigned HOST_WIDE_INT shift_mask)
{
optab reverse_unsigned_shift, unsigned_shift;
rtx tmp, carries;
reverse_unsigned_shift = (binoptab == ashl_optab ? lshr_optab : ashl_optab);
unsigned_shift = (binoptab == ashl_optab ? ashl_optab : lshr_optab);
/* The low OP1 bits of INTO_TARGET come from the high bits of OUTOF_INPUT.
We therefore need to shift OUTOF_INPUT by (BITS_PER_WORD - OP1) bits in
the opposite direction to BINOPTAB. */
if (CONSTANT_P (op1) || shift_mask >= BITS_PER_WORD)
{
carries = outof_input;
tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1,
0, true, methods);
}
else
{
/* We must avoid shifting by BITS_PER_WORD bits since that is either
the same as a zero shift (if shift_mask == BITS_PER_WORD - 1) or
has unknown behaviour. Do a single shift first, then shift by the
remainder. It's OK to use ~OP1 as the remainder if shift counts
are truncated to the mode size. */
carries = expand_binop (word_mode, reverse_unsigned_shift,
outof_input, const1_rtx, 0, unsignedp, methods);
if (shift_mask == BITS_PER_WORD - 1)
{
tmp = immed_double_const (-1, -1, op1_mode);
tmp = simplify_expand_binop (op1_mode, xor_optab, op1, tmp,
0, true, methods);
}
else
{
tmp = immed_double_const (BITS_PER_WORD - 1, 0, op1_mode);
tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1,
0, true, methods);
}
}
if (tmp == 0 || carries == 0)
return false;
carries = expand_binop (word_mode, reverse_unsigned_shift,
carries, tmp, 0, unsignedp, methods);
if (carries == 0)
return false;
/* Shift INTO_INPUT logically by OP1. This is the last use of INTO_INPUT
so the result can go directly into INTO_TARGET if convenient. */
tmp = expand_binop (word_mode, unsigned_shift, into_input, op1,
into_target, unsignedp, methods);
if (tmp == 0)
return false;
/* Now OR in the bits carried over from OUTOF_INPUT. */
if (!force_expand_binop (word_mode, ior_optab, tmp, carries,
into_target, unsignedp, methods))
return false;
/* Use a standard word_mode shift for the out-of half. */
if (outof_target != 0)
if (!force_expand_binop (word_mode, binoptab, outof_input, op1,
outof_target, unsignedp, methods))
return false;
return true;
}
#ifdef HAVE_conditional_move
/* Try implementing expand_doubleword_shift using conditional moves.
The shift is by < BITS_PER_WORD if (CMP_CODE CMP1 CMP2) is true,
otherwise it is by >= BITS_PER_WORD. SUBWORD_OP1 and SUPERWORD_OP1
are the shift counts to use in the former and latter case. All other
arguments are the same as the parent routine. */
static bool
expand_doubleword_shift_condmove (enum machine_mode op1_mode, optab binoptab,
enum rtx_code cmp_code, rtx cmp1, rtx cmp2,
rtx outof_input, rtx into_input,
rtx subword_op1, rtx superword_op1,
rtx outof_target, rtx into_target,
int unsignedp, enum optab_methods methods,
unsigned HOST_WIDE_INT shift_mask)
{
rtx outof_superword, into_superword;
/* Put the superword version of the output into OUTOF_SUPERWORD and
INTO_SUPERWORD. */
outof_superword = outof_target != 0 ? gen_reg_rtx (word_mode) : 0;
if (outof_target != 0 && subword_op1 == superword_op1)
{
/* The value INTO_TARGET >> SUBWORD_OP1, which we later store in
OUTOF_TARGET, is the same as the value of INTO_SUPERWORD. */
into_superword = outof_target;
if (!expand_superword_shift (binoptab, outof_input, superword_op1,
outof_superword, 0, unsignedp, methods))
return false;
}
else
{
into_superword = gen_reg_rtx (word_mode);
if (!expand_superword_shift (binoptab, outof_input, superword_op1,
outof_superword, into_superword,
unsignedp, methods))
return false;
}
/* Put the subword version directly in OUTOF_TARGET and INTO_TARGET. */
if (!expand_subword_shift (op1_mode, binoptab,
outof_input, into_input, subword_op1,
outof_target, into_target,
unsignedp, methods, shift_mask))
return false;
/* Select between them. Do the INTO half first because INTO_SUPERWORD
might be the current value of OUTOF_TARGET. */
if (!emit_conditional_move (into_target, cmp_code, cmp1, cmp2, op1_mode,
into_target, into_superword, word_mode, false))
return false;
if (outof_target != 0)
if (!emit_conditional_move (outof_target, cmp_code, cmp1, cmp2, op1_mode,
outof_target, outof_superword,
word_mode, false))
return false;
return true;
}
#endif
/* Expand a doubleword shift (ashl, ashr or lshr) using word-mode shifts.
OUTOF_INPUT and INTO_INPUT are the two word-sized halves of the first
input operand; the shift moves bits in the direction OUTOF_INPUT->
INTO_TARGET. OUTOF_TARGET and INTO_TARGET are the equivalent words
of the target. OP1 is the shift count and OP1_MODE is its mode.
If OP1 is constant, it will have been truncated as appropriate
and is known to be nonzero.
If SHIFT_MASK is zero, the result of word shifts is undefined when the
shift count is outside the range [0, BITS_PER_WORD). This routine must
avoid generating such shifts for OP1s in the range [0, BITS_PER_WORD * 2).
If SHIFT_MASK is nonzero, all word-mode shift counts are effectively
masked by it and shifts in the range [BITS_PER_WORD, SHIFT_MASK) will
fill with zeros or sign bits as appropriate.
If SHIFT_MASK is BITS_PER_WORD - 1, this routine will synthesise
a doubleword shift whose equivalent mask is BITS_PER_WORD * 2 - 1.
Doing this preserves semantics required by SHIFT_COUNT_TRUNCATED.
In all other cases, shifts by values outside [0, BITS_PER_UNIT * 2)
are undefined.
BINOPTAB, UNSIGNEDP and METHODS are as for expand_binop. This function
may not use INTO_INPUT after modifying INTO_TARGET, and similarly for
OUTOF_INPUT and OUTOF_TARGET. OUTOF_TARGET can be null if the parent
function wants to calculate it itself.
Return true if the shift could be successfully synthesized. */
static bool
expand_doubleword_shift (enum machine_mode op1_mode, optab binoptab,
rtx outof_input, rtx into_input, rtx op1,
rtx outof_target, rtx into_target,
int unsignedp, enum optab_methods methods,
unsigned HOST_WIDE_INT shift_mask)
{
rtx superword_op1, tmp, cmp1, cmp2;
rtx subword_label, done_label;
enum rtx_code cmp_code;
/* See if word-mode shifts by BITS_PER_WORD...BITS_PER_WORD * 2 - 1 will
fill the result with sign or zero bits as appropriate. If so, the value
of OUTOF_TARGET will always be (SHIFT OUTOF_INPUT OP1). Recursively call
this routine to calculate INTO_TARGET (which depends on both OUTOF_INPUT
and INTO_INPUT), then emit code to set up OUTOF_TARGET.
This isn't worthwhile for constant shifts since the optimizers will
cope better with in-range shift counts. */
if (shift_mask >= BITS_PER_WORD
&& outof_target != 0
&& !CONSTANT_P (op1))
{
if (!expand_doubleword_shift (op1_mode, binoptab,
outof_input, into_input, op1,
0, into_target,
unsignedp, methods, shift_mask))
return false;
if (!force_expand_binop (word_mode, binoptab, outof_input, op1,
outof_target, unsignedp, methods))
return false;
return true;
}
/* Set CMP_CODE, CMP1 and CMP2 so that the rtx (CMP_CODE CMP1 CMP2)
is true when the effective shift value is less than BITS_PER_WORD.
Set SUPERWORD_OP1 to the shift count that should be used to shift
OUTOF_INPUT into INTO_TARGET when the condition is false. */
tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
if (!CONSTANT_P (op1) && shift_mask == BITS_PER_WORD - 1)
{
/* Set CMP1 to OP1 & BITS_PER_WORD. The result is zero iff OP1
is a subword shift count. */
cmp1 = simplify_expand_binop (op1_mode, and_optab, op1, tmp,
0, true, methods);
cmp2 = CONST0_RTX (op1_mode);
cmp_code = EQ;
superword_op1 = op1;
}
else
{
/* Set CMP1 to OP1 - BITS_PER_WORD. */
cmp1 = simplify_expand_binop (op1_mode, sub_optab, op1, tmp,
0, true, methods);
cmp2 = CONST0_RTX (op1_mode);
cmp_code = LT;
superword_op1 = cmp1;
}
if (cmp1 == 0)
return false;
/* If we can compute the condition at compile time, pick the
appropriate subroutine. */
tmp = simplify_relational_operation (cmp_code, SImode, op1_mode, cmp1, cmp2);
if (tmp != 0 && GET_CODE (tmp) == CONST_INT)
{
if (tmp == const0_rtx)
return expand_superword_shift (binoptab, outof_input, superword_op1,
outof_target, into_target,
unsignedp, methods);
else
return expand_subword_shift (op1_mode, binoptab,
outof_input, into_input, op1,
outof_target, into_target,
unsignedp, methods, shift_mask);
}
#ifdef HAVE_conditional_move
/* Try using conditional moves to generate straight-line code. */
{
rtx start = get_last_insn ();
if (expand_doubleword_shift_condmove (op1_mode, binoptab,
cmp_code, cmp1, cmp2,
outof_input, into_input,
op1, superword_op1,
outof_target, into_target,
unsignedp, methods, shift_mask))
return true;
delete_insns_since (start);
}
#endif
/* As a last resort, use branches to select the correct alternative. */
subword_label = gen_label_rtx ();
done_label = gen_label_rtx ();
do_compare_rtx_and_jump (cmp1, cmp2, cmp_code, false, op1_mode,
0, 0, subword_label);
if (!expand_superword_shift (binoptab, outof_input, superword_op1,
outof_target, into_target,
unsignedp, methods))
return false;
emit_jump_insn (gen_jump (done_label));
emit_barrier ();
emit_label (subword_label);
if (!expand_subword_shift (op1_mode, binoptab,
outof_input, into_input, op1,
outof_target, into_target,
unsignedp, methods, shift_mask))
return false;
emit_label (done_label);
return true;
}
/* Wrapper around expand_binop which takes an rtx code to specify
the operation to perform, not an optab pointer. All other
......@@ -638,118 +987,71 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
if ((binoptab == lshr_optab || binoptab == ashl_optab
|| binoptab == ashr_optab)
&& class == MODE_INT
&& GET_CODE (op1) == CONST_INT
&& (GET_CODE (op1) == CONST_INT || !optimize_size)
&& GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
&& binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
&& ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
&& lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
{
rtx insns, inter, equiv_value;
rtx into_target, outof_target;
rtx into_input, outof_input;
int shift_count, left_shift, outof_word;
unsigned HOST_WIDE_INT shift_mask, double_shift_mask;
enum machine_mode op1_mode;
/* If TARGET is the same as one of the operands, the REG_EQUAL note
won't be accurate, so use a new target. */
if (target == 0 || target == op0 || target == op1)
target = gen_reg_rtx (mode);
start_sequence ();
double_shift_mask = targetm.shift_truncation_mask (mode);
shift_mask = targetm.shift_truncation_mask (word_mode);
op1_mode = GET_MODE (op1) != VOIDmode ? GET_MODE (op1) : word_mode;
shift_count = INTVAL (op1);
/* Apply the truncation to constant shifts. */
if (double_shift_mask > 0 && GET_CODE (op1) == CONST_INT)
op1 = GEN_INT (INTVAL (op1) & double_shift_mask);
/* OUTOF_* is the word we are shifting bits away from, and
INTO_* is the word that we are shifting bits towards, thus
they differ depending on the direction of the shift and
WORDS_BIG_ENDIAN. */
left_shift = binoptab == ashl_optab;
outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
outof_target = operand_subword (target, outof_word, 1, mode);
into_target = operand_subword (target, 1 - outof_word, 1, mode);
outof_input = operand_subword_force (op0, outof_word, mode);
into_input = operand_subword_force (op0, 1 - outof_word, mode);
if (op1 == CONST0_RTX (op1_mode))
return op0;
if (shift_count >= BITS_PER_WORD)
/* Make sure that this is a combination that expand_doubleword_shift
can handle. See the comments there for details. */
if (double_shift_mask == 0
|| (shift_mask == BITS_PER_WORD - 1
&& double_shift_mask == BITS_PER_WORD * 2 - 1))
{
inter = expand_binop (word_mode, binoptab,
outof_input,
GEN_INT (shift_count - BITS_PER_WORD),
into_target, unsignedp, next_methods);
rtx insns, equiv_value;
rtx into_target, outof_target;
rtx into_input, outof_input;
int left_shift, outof_word;
if (inter != 0 && inter != into_target)
emit_move_insn (into_target, inter);
/* For a signed right shift, we must fill the word we are shifting
out of with copies of the sign bit. Otherwise it is zeroed. */
if (inter != 0 && binoptab != ashr_optab)
inter = CONST0_RTX (word_mode);
else if (inter != 0)
inter = expand_binop (word_mode, binoptab,
outof_input,
GEN_INT (BITS_PER_WORD - 1),
outof_target, unsignedp, next_methods);
if (inter != 0 && inter != outof_target)
emit_move_insn (outof_target, inter);
}
else
{
rtx carries;
optab reverse_unsigned_shift, unsigned_shift;
/* For a shift of less then BITS_PER_WORD, to compute the carry,
we must do a logical shift in the opposite direction of the
desired shift. */
reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab);
/* For a shift of less than BITS_PER_WORD, to compute the word
shifted towards, we need to unsigned shift the orig value of
that word. */
unsigned_shift = (left_shift ? ashl_optab : lshr_optab);
carries = expand_binop (word_mode, reverse_unsigned_shift,
outof_input,
GEN_INT (BITS_PER_WORD - shift_count),
0, unsignedp, next_methods);
if (carries == 0)
inter = 0;
else
inter = expand_binop (word_mode, unsigned_shift, into_input,
op1, 0, unsignedp, next_methods);
/* If TARGET is the same as one of the operands, the REG_EQUAL note
won't be accurate, so use a new target. */
if (target == 0 || target == op0 || target == op1)
target = gen_reg_rtx (mode);
if (inter != 0)
inter = expand_binop (word_mode, ior_optab, carries, inter,
into_target, unsignedp, next_methods);
start_sequence ();
if (inter != 0 && inter != into_target)
emit_move_insn (into_target, inter);
/* OUTOF_* is the word we are shifting bits away from, and
INTO_* is the word that we are shifting bits towards, thus
they differ depending on the direction of the shift and
WORDS_BIG_ENDIAN. */
if (inter != 0)
inter = expand_binop (word_mode, binoptab, outof_input,
op1, outof_target, unsignedp, next_methods);
left_shift = binoptab == ashl_optab;
outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
if (inter != 0 && inter != outof_target)
emit_move_insn (outof_target, inter);
}
outof_target = operand_subword (target, outof_word, 1, mode);
into_target = operand_subword (target, 1 - outof_word, 1, mode);
insns = get_insns ();
end_sequence ();
outof_input = operand_subword_force (op0, outof_word, mode);
into_input = operand_subword_force (op0, 1 - outof_word, mode);
if (inter != 0)
{
if (binoptab->code != UNKNOWN)
equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1);
else
equiv_value = 0;
if (expand_doubleword_shift (op1_mode, binoptab,
outof_input, into_input, op1,
outof_target, into_target,
unsignedp, methods, shift_mask))
{
insns = get_insns ();
end_sequence ();
emit_no_conflict_block (insns, target, op0, op1, equiv_value);
return target;
equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1);
emit_no_conflict_block (insns, target, op0, op1, equiv_value);
return target;
}
end_sequence ();
}
}
......
......@@ -2343,41 +2343,26 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
break;
case LSHIFTRT:
/* If shift count is undefined, don't fold it; let the machine do
what it wants. But truncate it if the machine will do that. */
if (arg1 < 0)
return 0;
if (SHIFT_COUNT_TRUNCATED)
arg1 %= width;
val = ((unsigned HOST_WIDE_INT) arg0) >> arg1;
break;
case ASHIFT:
if (arg1 < 0)
return 0;
if (SHIFT_COUNT_TRUNCATED)
arg1 %= width;
val = ((unsigned HOST_WIDE_INT) arg0) << arg1;
break;
case ASHIFTRT:
if (arg1 < 0)
return 0;
/* Truncate the shift if SHIFT_COUNT_TRUNCATED, otherwise make sure the
value is in range. We can't return any old value for out-of-range
arguments because either the middle-end (via shift_truncation_mask)
or the back-end might be relying on target-specific knowledge.
Nor can we rely on shift_truncation_mask, since the shift might
not be part of an ashlM3, lshrM3 or ashrM3 instruction. */
if (SHIFT_COUNT_TRUNCATED)
arg1 %= width;
val = arg0s >> arg1;
arg1 = (unsigned HOST_WIDE_INT) arg1 % width;
else if (arg1 < 0 || arg1 >= GET_MODE_BITSIZE (mode))
return 0;
/* Bootstrap compiler may not have sign extended the right shift.
Manually extend the sign to insure bootstrap cc matches gcc. */
if (arg0s < 0 && arg1 > 0)
val |= ((HOST_WIDE_INT) -1) << (HOST_BITS_PER_WIDE_INT - arg1);
val = (code == ASHIFT
? ((unsigned HOST_WIDE_INT) arg0) << arg1
: ((unsigned HOST_WIDE_INT) arg0) >> arg1);
/* Sign-extend the result for arithmetic right shifts. */
if (code == ASHIFTRT && arg0s < 0 && arg1 > 0)
val |= ((HOST_WIDE_INT) -1) << (width - arg1);
break;
case ROTATERT:
......
......@@ -301,6 +301,10 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define TARGET_BINDS_LOCAL_P default_binds_local_p
#endif
#ifndef TARGET_SHIFT_TRUNCATION_MASK
#define TARGET_SHIFT_TRUNCATION_MASK default_shift_truncation_mask
#endif
#ifndef TARGET_VALID_POINTER_MODE
#define TARGET_VALID_POINTER_MODE default_valid_pointer_mode
#endif
......@@ -488,6 +492,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
TARGET_BINDS_LOCAL_P, \
TARGET_ENCODE_SECTION_INFO, \
TARGET_STRIP_NAME_ENCODING, \
TARGET_SHIFT_TRUNCATION_MASK, \
TARGET_VALID_POINTER_MODE, \
TARGET_SCALAR_MODE_SUPPORTED_P, \
TARGET_VECTOR_MODE_SUPPORTED_P, \
......
......@@ -378,6 +378,10 @@ struct gcc_target
/* Undo the effects of encode_section_info on the symbol string. */
const char * (* strip_name_encoding) (const char *);
/* If shift optabs for MODE are known to always truncate the shift count,
return the mask that they apply. Return 0 otherwise. */
unsigned HOST_WIDE_INT (* shift_truncation_mask) (enum machine_mode mode);
/* True if MODE is valid for a pointer in __attribute__((mode("MODE"))). */
bool (* valid_pointer_mode) (enum machine_mode mode);
......
......@@ -135,6 +135,14 @@ default_eh_return_filter_mode (void)
return word_mode;
}
/* The default implementation of TARGET_SHIFT_TRUNCATION_MASK. */
unsigned HOST_WIDE_INT
default_shift_truncation_mask (enum machine_mode mode)
{
return SHIFT_COUNT_TRUNCATED ? GET_MODE_BITSIZE (mode) - 1 : 0;
}
/* Generic hook that takes a CUMULATIVE_ARGS pointer and returns true. */
bool
......
......@@ -32,6 +32,8 @@ extern bool hook_bool_CUMULATIVE_ARGS_false (CUMULATIVE_ARGS *);
extern bool default_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *);
extern enum machine_mode default_eh_return_filter_mode (void);
extern unsigned HOST_WIDE_INT default_shift_truncation_mask
(enum machine_mode);
extern bool hook_bool_CUMULATIVE_ARGS_true (CUMULATIVE_ARGS *);
extern tree default_cxx_guard_type (void);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment