diff options
author | rsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4> | 2004-09-04 08:50:36 +0000 |
---|---|---|
committer | rsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4> | 2004-09-04 08:50:36 +0000 |
commit | c49547c45490b7a0c0281d90f79848aa3191ba88 (patch) | |
tree | 4320b41f468e28f9439feab3f57dff953085081c /gcc | |
parent | c068056a5c1064f44c6ad502c60981420b4a4562 (diff) | |
download | gcc-c49547c45490b7a0c0281d90f79848aa3191ba88.tar.gz |
* 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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@87079 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 21 | ||||
-rw-r--r-- | gcc/config/arm/arm.c | 15 | ||||
-rw-r--r-- | gcc/doc/md.texi | 5 | ||||
-rw-r--r-- | gcc/doc/tm.texi | 25 | ||||
-rw-r--r-- | gcc/optabs.c | 490 | ||||
-rw-r--r-- | gcc/simplify-rtx.c | 45 | ||||
-rw-r--r-- | gcc/target-def.h | 5 | ||||
-rw-r--r-- | gcc/target.h | 4 | ||||
-rw-r--r-- | gcc/targhooks.c | 8 | ||||
-rw-r--r-- | gcc/targhooks.h | 2 |
10 files changed, 494 insertions, 126 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 93ed7fde8d7..19d3faef681 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,24 @@ +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. diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index c2617052d7c..38ead5d3c35 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -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; +} diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index 8404707db27..0fe7c50eb8d 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -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 diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 26724858e99..e21f72747ed 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -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} diff --git a/gcc/optabs.c b/gcc/optabs.c index de7f4dc2caa..22c0b347946 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -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 (); } } diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index a0f17696cea..285f898de80 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -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: diff --git a/gcc/target-def.h b/gcc/target-def.h index 3b4c11b0694..01837efd604 100644 --- a/gcc/target-def.h +++ b/gcc/target-def.h @@ -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, \ diff --git a/gcc/target.h b/gcc/target.h index 035aeed4426..735a85d11dd 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -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); diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 6aa2e07117b..5ecb6d8370d 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -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 diff --git a/gcc/targhooks.h b/gcc/targhooks.h index b91e51dec84..8745f7eb5b7 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -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); |