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. */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
#include "config.h"
rtx libfunc_table[LTI_MAX];
/* Tables of patterns for converting one mode to another. */
-convert_optab convert_optab_table[CTI_MAX];
+convert_optab convert_optab_table[COI_MAX];
/* Contains the optab used for each rtx code. */
optab code_to_optab[NUM_RTX_CODE + 1];
#ifndef HAVE_conditional_trap
#define HAVE_conditional_trap 0
-#define gen_conditional_trap(a,b) (abort (), NULL_RTX)
+#define gen_conditional_trap(a,b) (gcc_unreachable (), NULL_RTX)
#endif
\f
/* Add a REG_EQUAL note to the last insn in INSNS. TARGET is being set to
rtx last_insn, insn, set;
rtx note;
- if (! insns
- || ! INSN_P (insns)
- || NEXT_INSN (insns) == NULL_RTX)
- abort ();
+ gcc_assert (insns && INSN_P (insns) && NEXT_INSN (insns));
if (GET_RTX_CLASS (code) != RTX_COMM_ARITH
&& GET_RTX_CLASS (code) != RTX_BIN_ARITH
case REALIGN_LOAD_EXPR:
return vec_realign_load_optab;
+ case REDUC_MAX_EXPR:
+ return TYPE_UNSIGNED (type) ? reduc_umax_optab : reduc_smax_optab;
+
+ case REDUC_MIN_EXPR:
+ return TYPE_UNSIGNED (type) ? reduc_umin_optab : reduc_smin_optab;
+
+ case REDUC_PLUS_EXPR:
+ return TYPE_UNSIGNED (type) ? reduc_uplus_optab : reduc_splus_optab;
+
+ case VEC_LSHIFT_EXPR:
+ return vec_shl_optab;
+
+ case VEC_RSHIFT_EXPR:
+ return vec_shr_optab;
+
default:
break;
}
this may or may not be TARGET. */
rtx
-expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0,
- rtx op1, rtx op2, rtx target, int unsignedp)
+expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0,
+ rtx op1, rtx op2, rtx target, int unsignedp)
{
int icode = (int) ternary_optab->handlers[(int) mode].insn_code;
enum machine_mode mode0 = insn_data[icode].operand[1].mode;
rtx pat;
rtx xop0 = op0, xop1 = op1, xop2 = op2;
- if (ternary_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
- abort ();
+ gcc_assert (ternary_optab->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing);
- if (!target
- || ! (*insn_data[icode].operand[0].predicate) (target, mode))
+ if (!target || !insn_data[icode].operand[0].predicate (target, mode))
temp = gen_reg_rtx (mode);
else
temp = target;
if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
xop0 = convert_modes (mode0,
GET_MODE (op0) != VOIDmode
- ? GET_MODE (op0)
+ ? GET_MODE (op0)
: mode,
xop0, unsignedp);
/* Now, if insn's predicates don't allow our operands, put them into
pseudo regs. */
-
- if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0)
- && mode0 != VOIDmode)
+
+ if (!insn_data[icode].operand[1].predicate (xop0, mode0)
+ && mode0 != VOIDmode)
xop0 = copy_to_mode_reg (mode0, xop0);
-
- if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1)
+
+ if (!insn_data[icode].operand[2].predicate (xop1, mode1)
&& mode1 != VOIDmode)
xop1 = copy_to_mode_reg (mode1, xop1);
-
- if (! (*insn_data[icode].operand[3].predicate) (xop2, mode2)
+
+ if (!insn_data[icode].operand[3].predicate (xop2, mode2)
&& mode2 != VOIDmode)
xop2 = copy_to_mode_reg (mode2, xop2);
-
+
pat = GEN_FCN (icode) (temp, xop0, xop1, xop2);
-
+
emit_insn (pat);
- return temp;
+ return temp;
}
return true;
}
+/* Generate insns for VEC_LSHIFT_EXPR, VEC_RSHIFT_EXPR. */
+
+rtx
+expand_vec_shift_expr (tree vec_shift_expr, rtx target)
+{
+ enum insn_code icode;
+ rtx rtx_op1, rtx_op2;
+ enum machine_mode mode1;
+ enum machine_mode mode2;
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (vec_shift_expr));
+ tree vec_oprnd = TREE_OPERAND (vec_shift_expr, 0);
+ tree shift_oprnd = TREE_OPERAND (vec_shift_expr, 1);
+ optab shift_optab;
+ rtx pat;
+
+ switch (TREE_CODE (vec_shift_expr))
+ {
+ case VEC_RSHIFT_EXPR:
+ shift_optab = vec_shr_optab;
+ break;
+ case VEC_LSHIFT_EXPR:
+ shift_optab = vec_shl_optab;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ icode = (int) shift_optab->handlers[(int) mode].insn_code;
+ gcc_assert (icode != CODE_FOR_nothing);
+
+ mode1 = insn_data[icode].operand[1].mode;
+ mode2 = insn_data[icode].operand[2].mode;
+
+ rtx_op1 = expand_expr (vec_oprnd, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+ if (!(*insn_data[icode].operand[1].predicate) (rtx_op1, mode1)
+ && mode1 != VOIDmode)
+ rtx_op1 = force_reg (mode1, rtx_op1);
+
+ rtx_op2 = expand_expr (shift_oprnd, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+ if (!(*insn_data[icode].operand[2].predicate) (rtx_op2, mode2)
+ && mode2 != VOIDmode)
+ rtx_op2 = force_reg (mode2, rtx_op2);
+
+ if (!target
+ || ! (*insn_data[icode].operand[0].predicate) (target, mode))
+ target = gen_reg_rtx (mode);
+
+ /* Emit instruction */
+ pat = GEN_FCN (icode) (target, rtx_op1, rtx_op2);
+ gcc_assert (pat);
+ emit_insn (pat);
+
+ return target;
+}
+
/* 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
return true;
}
\f
+/* Subroutine of expand_binop. Perform a double word multiplication of
+ operands OP0 and OP1 both of mode MODE, which is exactly twice as wide
+ as the target's word_mode. This function return NULL_RTX if anything
+ goes wrong, in which case it may have already emitted instructions
+ which need to be deleted.
+
+ If we want to multiply two two-word values and have normal and widening
+ multiplies of single-word values, we can do this with three smaller
+ multiplications. Note that we do not make a REG_NO_CONFLICT block here
+ because we are not operating on one word at a time.
+
+ The multiplication proceeds as follows:
+ _______________________
+ [__op0_high_|__op0_low__]
+ _______________________
+ * [__op1_high_|__op1_low__]
+ _______________________________________________
+ _______________________
+ (1) [__op0_low__*__op1_low__]
+ _______________________
+ (2a) [__op0_low__*__op1_high_]
+ _______________________
+ (2b) [__op0_high_*__op1_low__]
+ _______________________
+ (3) [__op0_high_*__op1_high_]
+
+
+ This gives a 4-word result. Since we are only interested in the
+ lower 2 words, partial result (3) and the upper words of (2a) and
+ (2b) don't need to be calculated. Hence (2a) and (2b) can be
+ calculated using non-widening multiplication.
+
+ (1), however, needs to be calculated with an unsigned widening
+ multiplication. If this operation is not directly supported we
+ try using a signed widening multiplication and adjust the result.
+ This adjustment works as follows:
+
+ If both operands are positive then no adjustment is needed.
+
+ If the operands have different signs, for example op0_low < 0 and
+ op1_low >= 0, the instruction treats the most significant bit of
+ op0_low as a sign bit instead of a bit with significance
+ 2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low
+ with 2**BITS_PER_WORD - op0_low, and two's complements the
+ result. Conclusion: We need to add op1_low * 2**BITS_PER_WORD to
+ the result.
+
+ Similarly, if both operands are negative, we need to add
+ (op0_low + op1_low) * 2**BITS_PER_WORD.
+
+ We use a trick to adjust quickly. We logically shift op0_low right
+ (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to
+ op0_high (op1_high) before it is used to calculate 2b (2a). If no
+ logical shift exists, we do an arithmetic right shift and subtract
+ the 0 or -1. */
+
+static rtx
+expand_doubleword_mult (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+ bool umulp, enum optab_methods methods)
+{
+ int low = (WORDS_BIG_ENDIAN ? 1 : 0);
+ int high = (WORDS_BIG_ENDIAN ? 0 : 1);
+ rtx wordm1 = umulp ? NULL_RTX : GEN_INT (BITS_PER_WORD - 1);
+ rtx product, adjust, product_high, temp;
+
+ rtx op0_high = operand_subword_force (op0, high, mode);
+ rtx op0_low = operand_subword_force (op0, low, mode);
+ rtx op1_high = operand_subword_force (op1, high, mode);
+ rtx op1_low = operand_subword_force (op1, low, mode);
+
+ /* If we're using an unsigned multiply to directly compute the product
+ of the low-order words of the operands and perform any required
+ adjustments of the operands, we begin by trying two more multiplications
+ and then computing the appropriate sum.
+
+ We have checked above that the required addition is provided.
+ Full-word addition will normally always succeed, especially if
+ it is provided at all, so we don't worry about its failure. The
+ multiplication may well fail, however, so we do handle that. */
+
+ if (!umulp)
+ {
+ /* ??? This could be done with emit_store_flag where available. */
+ temp = expand_binop (word_mode, lshr_optab, op0_low, wordm1,
+ NULL_RTX, 1, methods);
+ if (temp)
+ op0_high = expand_binop (word_mode, add_optab, op0_high, temp,
+ NULL_RTX, 0, OPTAB_DIRECT);
+ else
+ {
+ temp = expand_binop (word_mode, ashr_optab, op0_low, wordm1,
+ NULL_RTX, 0, methods);
+ if (!temp)
+ return NULL_RTX;
+ op0_high = expand_binop (word_mode, sub_optab, op0_high, temp,
+ NULL_RTX, 0, OPTAB_DIRECT);
+ }
+
+ if (!op0_high)
+ return NULL_RTX;
+ }
+
+ adjust = expand_binop (word_mode, smul_optab, op0_high, op1_low,
+ NULL_RTX, 0, OPTAB_DIRECT);
+ if (!adjust)
+ return NULL_RTX;
+
+ /* OP0_HIGH should now be dead. */
+
+ if (!umulp)
+ {
+ /* ??? This could be done with emit_store_flag where available. */
+ temp = expand_binop (word_mode, lshr_optab, op1_low, wordm1,
+ NULL_RTX, 1, methods);
+ if (temp)
+ op1_high = expand_binop (word_mode, add_optab, op1_high, temp,
+ NULL_RTX, 0, OPTAB_DIRECT);
+ else
+ {
+ temp = expand_binop (word_mode, ashr_optab, op1_low, wordm1,
+ NULL_RTX, 0, methods);
+ if (!temp)
+ return NULL_RTX;
+ op1_high = expand_binop (word_mode, sub_optab, op1_high, temp,
+ NULL_RTX, 0, OPTAB_DIRECT);
+ }
+
+ if (!op1_high)
+ return NULL_RTX;
+ }
+
+ temp = expand_binop (word_mode, smul_optab, op1_high, op0_low,
+ NULL_RTX, 0, OPTAB_DIRECT);
+ if (!temp)
+ return NULL_RTX;
+
+ /* OP1_HIGH should now be dead. */
+
+ adjust = expand_binop (word_mode, add_optab, adjust, temp,
+ adjust, 0, OPTAB_DIRECT);
+
+ if (target && !REG_P (target))
+ target = NULL_RTX;
+
+ if (umulp)
+ product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
+ target, 1, OPTAB_DIRECT);
+ else
+ product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
+ target, 1, OPTAB_DIRECT);
+
+ if (!product)
+ return NULL_RTX;
+
+ product_high = operand_subword (product, high, 1, mode);
+ adjust = expand_binop (word_mode, add_optab, product_high, adjust,
+ REG_P (product_high) ? product_high : adjust,
+ 0, OPTAB_DIRECT);
+ emit_move_insn (product_high, adjust);
+ return product;
+}
+\f
/* Wrapper around expand_binop which takes an rtx code to specify
the operation to perform, not an optab pointer. All other
arguments are the same. */
enum optab_methods methods)
{
optab binop = code_to_optab[(int) code];
- if (binop == 0)
- abort ();
+ gcc_assert (binop);
return expand_binop (mode, binop, op0, op1, target, unsignedp, methods);
}
+
+/* Return whether OP0 and OP1 should be swapped when expanding a commutative
+ binop. Order them according to commutative_operand_precedence and, if
+ possible, try to put TARGET first. */
+static bool
+swap_commutative_operands_with_target (rtx target, rtx op0, rtx op1)
+{
+ int op0_prec = commutative_operand_precedence (op0);
+ int op1_prec = commutative_operand_precedence (op1);
+
+ if (op0_prec < op1_prec)
+ return true;
+
+ if (op0_prec > op1_prec)
+ return false;
+
+ /* With equal precedence, both orders are ok, but try to put the
+ target first. */
+ return target && rtx_equal_p (op1, target);
+}
+
+
/* Generate code to perform an operation specified by BINOPTAB
on operands OP0 and OP1, with result having machine-mode MODE.
class = GET_MODE_CLASS (mode);
- if (flag_force_mem)
- {
- /* Load duplicate non-volatile operands once. */
- if (rtx_equal_p (op0, op1) && ! volatile_refs_p (op0))
- {
- op0 = force_not_mem (op0);
- op1 = op0;
- }
- else
- {
- op0 = force_not_mem (op0);
- op1 = force_not_mem (op1);
- }
- }
-
/* If subtracting an integer constant, convert this into an addition of
the negated constant. */
/* Record where to delete back to if we backtrack. */
last = get_last_insn ();
- /* If operation is commutative,
- try to make the first operand a register.
- Even better, try to make it the same as the target.
- Also try to make the last operand a constant. */
+ /* If operation is commutative, canonicalize the order of the operands. */
if (GET_RTX_CLASS (binoptab->code) == RTX_COMM_ARITH
|| binoptab == smul_widen_optab
|| binoptab == umul_widen_optab
|| binoptab == umul_highpart_optab)
{
commutative_op = 1;
-
- if (((target == 0 || REG_P (target))
- ? ((REG_P (op1)
- && !REG_P (op0))
- || target == op1)
- : rtx_equal_p (op1, target))
- || GET_CODE (op0) == CONST_INT)
+ if (swap_commutative_operands_with_target (target, op0, op1))
{
temp = op1;
op1 = op0;
/* Now, if insn's predicates don't allow our operands, put them into
pseudo regs. */
- if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0)
+ if (!insn_data[icode].operand[1].predicate (xop0, mode0)
&& mode0 != VOIDmode)
xop0 = copy_to_mode_reg (mode0, xop0);
- if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1)
+ if (!insn_data[icode].operand[2].predicate (xop1, mode1)
&& mode1 != VOIDmode)
xop1 = copy_to_mode_reg (mode1, xop1);
- if (! (*insn_data[icode].operand[0].predicate) (temp, mode))
+ if (!insn_data[icode].operand[0].predicate (temp, mode))
temp = gen_reg_rtx (mode);
pat = GEN_FCN (icode) (temp, xop0, xop1);
if (temp != 0)
{
- if (GET_MODE_CLASS (mode) == MODE_INT)
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+ GET_MODE_BITSIZE (GET_MODE (temp))))
return gen_lowpart (mode, temp);
else
return convert_to_mode (mode, temp, unsignedp);
unsignedp, OPTAB_DIRECT);
if (temp)
{
- if (class != MODE_INT)
+ if (class != MODE_INT
+ || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+ GET_MODE_BITSIZE (wider_mode)))
{
if (target == 0)
target = gen_reg_rtx (mode);
}
emit_move_insn (target_piece, newx);
}
+ else
+ {
+ if (x != target_piece)
+ emit_move_insn (target_piece, x);
+ }
carry_in = carry_out;
}
delete_insns_since (last);
}
- /* If we want to multiply two two-word values and have normal and widening
- multiplies of single-word values, we can do this with three smaller
- multiplications. Note that we do not make a REG_NO_CONFLICT block here
- because we are not operating on one word at a time.
-
- The multiplication proceeds as follows:
- _______________________
- [__op0_high_|__op0_low__]
- _______________________
- * [__op1_high_|__op1_low__]
- _______________________________________________
- _______________________
- (1) [__op0_low__*__op1_low__]
- _______________________
- (2a) [__op0_low__*__op1_high_]
- _______________________
- (2b) [__op0_high_*__op1_low__]
- _______________________
- (3) [__op0_high_*__op1_high_]
-
-
- This gives a 4-word result. Since we are only interested in the
- lower 2 words, partial result (3) and the upper words of (2a) and
- (2b) don't need to be calculated. Hence (2a) and (2b) can be
- calculated using non-widening multiplication.
-
- (1), however, needs to be calculated with an unsigned widening
- multiplication. If this operation is not directly supported we
- try using a signed widening multiplication and adjust the result.
- This adjustment works as follows:
-
- If both operands are positive then no adjustment is needed.
-
- If the operands have different signs, for example op0_low < 0 and
- op1_low >= 0, the instruction treats the most significant bit of
- op0_low as a sign bit instead of a bit with significance
- 2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low
- with 2**BITS_PER_WORD - op0_low, and two's complements the
- result. Conclusion: We need to add op1_low * 2**BITS_PER_WORD to
- the result.
-
- Similarly, if both operands are negative, we need to add
- (op0_low + op1_low) * 2**BITS_PER_WORD.
-
- We use a trick to adjust quickly. We logically shift op0_low right
- (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to
- op0_high (op1_high) before it is used to calculate 2b (2a). If no
- logical shift exists, we do an arithmetic right shift and subtract
- the 0 or -1. */
+ /* Attempt to synthesize double word multiplies using a sequence of word
+ mode multiplications. We first attempt to generate a sequence using a
+ more efficient unsigned widening multiply, and if that fails we then
+ try using a signed widening multiply. */
if (binoptab == smul_optab
&& class == MODE_INT
&& GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
&& smul_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
- && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
- && ((umul_widen_optab->handlers[(int) mode].insn_code
- != CODE_FOR_nothing)
- || (smul_widen_optab->handlers[(int) mode].insn_code
- != CODE_FOR_nothing)))
+ && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
{
- int low = (WORDS_BIG_ENDIAN ? 1 : 0);
- int high = (WORDS_BIG_ENDIAN ? 0 : 1);
- rtx op0_high = operand_subword_force (op0, high, mode);
- rtx op0_low = operand_subword_force (op0, low, mode);
- rtx op1_high = operand_subword_force (op1, high, mode);
- rtx op1_low = operand_subword_force (op1, low, mode);
- rtx product = 0;
- rtx op0_xhigh = NULL_RTX;
- rtx op1_xhigh = NULL_RTX;
-
- /* If the target is the same as one of the inputs, don't use it. This
- prevents problems with the REG_EQUAL note. */
- if (target == op0 || target == op1
- || (target != 0 && !REG_P (target)))
- target = 0;
-
- /* Multiply the two lower words to get a double-word product.
- If unsigned widening multiplication is available, use that;
- otherwise use the signed form and compensate. */
-
- if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
- {
- product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
- target, 1, OPTAB_DIRECT);
+ rtx product = NULL_RTX;
- /* If we didn't succeed, delete everything we did so far. */
- if (product == 0)
+ if (umul_widen_optab->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing)
+ {
+ product = expand_doubleword_mult (mode, op0, op1, target,
+ true, methods);
+ if (!product)
delete_insns_since (last);
- else
- op0_xhigh = op0_high, op1_xhigh = op1_high;
}
- if (product == 0
+ if (product == NULL_RTX
&& smul_widen_optab->handlers[(int) mode].insn_code
- != CODE_FOR_nothing)
+ != CODE_FOR_nothing)
{
- rtx wordm1 = GEN_INT (BITS_PER_WORD - 1);
- product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
- target, 1, OPTAB_DIRECT);
- op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1,
- NULL_RTX, 1, next_methods);
- if (op0_xhigh)
- op0_xhigh = expand_binop (word_mode, add_optab, op0_high,
- op0_xhigh, op0_xhigh, 0, next_methods);
- else
- {
- op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1,
- NULL_RTX, 0, next_methods);
- if (op0_xhigh)
- op0_xhigh = expand_binop (word_mode, sub_optab, op0_high,
- op0_xhigh, op0_xhigh, 0,
- next_methods);
- }
-
- op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1,
- NULL_RTX, 1, next_methods);
- if (op1_xhigh)
- op1_xhigh = expand_binop (word_mode, add_optab, op1_high,
- op1_xhigh, op1_xhigh, 0, next_methods);
- else
- {
- op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1,
- NULL_RTX, 0, next_methods);
- if (op1_xhigh)
- op1_xhigh = expand_binop (word_mode, sub_optab, op1_high,
- op1_xhigh, op1_xhigh, 0,
- next_methods);
- }
+ product = expand_doubleword_mult (mode, op0, op1, target,
+ false, methods);
+ if (!product)
+ delete_insns_since (last);
}
- /* If we have been able to directly compute the product of the
- low-order words of the operands and perform any required adjustments
- of the operands, we proceed by trying two more multiplications
- and then computing the appropriate sum.
-
- We have checked above that the required addition is provided.
- Full-word addition will normally always succeed, especially if
- it is provided at all, so we don't worry about its failure. The
- multiplication may well fail, however, so we do handle that. */
-
- if (product && op0_xhigh && op1_xhigh)
+ if (product != NULL_RTX)
{
- rtx product_high = operand_subword (product, high, 1, mode);
- rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh,
- NULL_RTX, 0, OPTAB_DIRECT);
-
- if (!REG_P (product_high))
- product_high = force_reg (word_mode, product_high);
-
- if (temp != 0)
- temp = expand_binop (word_mode, add_optab, temp, product_high,
- product_high, 0, next_methods);
-
- if (temp != 0 && temp != product_high)
- emit_move_insn (product_high, temp);
-
- if (temp != 0)
- temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh,
- NULL_RTX, 0, OPTAB_DIRECT);
-
- if (temp != 0)
- temp = expand_binop (word_mode, add_optab, temp,
- product_high, product_high,
- 0, next_methods);
-
- if (temp != 0 && temp != product_high)
- emit_move_insn (product_high, temp);
-
- emit_move_insn (operand_subword (product, high, 1, mode), product_high);
-
- if (temp != 0)
+ if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
{
- if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
- {
- temp = emit_move_insn (product, product);
- set_unique_reg_note (temp,
- REG_EQUAL,
- gen_rtx_fmt_ee (MULT, mode,
- copy_rtx (op0),
- copy_rtx (op1)));
- }
-
- return product;
+ temp = emit_move_insn (target ? target : product, product);
+ set_unique_reg_note (temp,
+ REG_EQUAL,
+ gen_rtx_fmt_ee (MULT, mode,
+ copy_rtx (op0),
+ copy_rtx (op1)));
}
+ return product;
}
-
- /* If we get here, we couldn't do it for some reason even though we
- originally thought we could. Delete anything we've emitted in
- trying to do it. */
-
- delete_insns_since (last);
}
/* It can't be open-coded in this mode.
unsignedp, methods);
if (temp)
{
- if (class != MODE_INT)
+ if (class != MODE_INT
+ || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+ GET_MODE_BITSIZE (wider_mode)))
{
if (target == 0)
target = gen_reg_rtx (mode);
class = GET_MODE_CLASS (mode);
- if (flag_force_mem)
- op0 = force_not_mem (op0);
-
if (!targ0)
targ0 = gen_reg_rtx (mode);
if (!targ1)
xop0 = convert_to_mode (mode0, xop0, unsignedp);
/* Now, if insn doesn't accept these operands, put them into pseudos. */
- if (! (*insn_data[icode].operand[2].predicate) (xop0, mode0))
+ if (!insn_data[icode].operand[2].predicate (xop0, mode0))
xop0 = copy_to_mode_reg (mode0, xop0);
/* We could handle this, but we should always be called with a pseudo
for our targets and all insns should take them as outputs. */
- if (! (*insn_data[icode].operand[0].predicate) (targ0, mode)
- || ! (*insn_data[icode].operand[1].predicate) (targ1, mode))
- abort ();
+ gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
+ gcc_assert (insn_data[icode].operand[1].predicate (targ1, mode));
pat = GEN_FCN (icode) (targ0, targ1, xop0);
if (pat)
class = GET_MODE_CLASS (mode);
- if (flag_force_mem)
- {
- op0 = force_not_mem (op0);
- op1 = force_not_mem (op1);
- }
-
/* If we are inside an appropriately-short loop and we are optimizing,
force expensive constants into a register. */
if (CONSTANT_P (op0) && optimize
xop1, unsignedp);
/* Now, if insn doesn't accept these operands, put them into pseudos. */
- if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
+ if (!insn_data[icode].operand[1].predicate (xop0, mode0))
xop0 = copy_to_mode_reg (mode0, xop0);
- if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1))
+ if (!insn_data[icode].operand[2].predicate (xop1, mode1))
xop1 = copy_to_mode_reg (mode1, xop1);
/* We could handle this, but we should always be called with a pseudo
for our targets and all insns should take them as outputs. */
- if (! (*insn_data[icode].operand[0].predicate) (targ0, mode)
- || ! (*insn_data[icode].operand[3].predicate) (targ1, mode))
- abort ();
+ gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
+ gcc_assert (insn_data[icode].operand[3].predicate (targ1, mode));
pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1);
if (pat)
rtx insns;
/* Exactly one of TARG0 or TARG1 should be non-NULL. */
- if (!((targ0 != NULL_RTX) ^ (targ1 != NULL_RTX)))
- abort ();
+ gcc_assert (!targ0 != !targ1);
mode = GET_MODE (op0);
if (!binoptab->handlers[(int) mode].libfunc)
rtx target, int unsignedp)
{
optab unop = code_to_optab[(int) code];
- if (unop == 0)
- abort ();
+ gcc_assert (unop);
return expand_unop (mode, unop, op0, target, unsignedp);
}
return 0;
}
-/* Extract the OMODE lowpart from VAL, which has IMODE. Under certain
+/* Extract the OMODE lowpart from VAL, which has IMODE. Under certain
conditions, VAL may already be a SUBREG against which we cannot generate
a further SUBREG. In this case, we expect forcing the value into a
register will work around the situation. */
if (fmt == NULL)
return NULL_RTX;
- bitpos = fmt->signbit;
+ bitpos = fmt->signbit_rw;
if (bitpos < 0)
return NULL_RTX;
{
rtx targ_piece = operand_subword (target, i, 1, mode);
rtx op0_piece = operand_subword_force (op0, i, mode);
-
+
if (i == word)
{
temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
class = GET_MODE_CLASS (mode);
- if (flag_force_mem)
- op0 = force_not_mem (op0);
-
if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
{
int icode = (int) unoptab->handlers[(int) mode].insn_code;
/* Now, if insn doesn't accept our operand, put it into a pseudo. */
- if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
+ if (!insn_data[icode].operand[1].predicate (xop0, mode0))
xop0 = copy_to_mode_reg (mode0, xop0);
- if (! (*insn_data[icode].operand[0].predicate) (temp, mode))
+ if (!insn_data[icode].operand[0].predicate (temp, mode))
temp = gen_reg_rtx (mode);
pat = GEN_FCN (icode) (temp, xop0);
is that we have a split register file, and leaving op0 in fp registers,
and not playing with subregs so much, will help the register allocator. */
-rtx
+static rtx
expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target,
int bitpos, bool op0_is_abs)
{
{
rtx targ_piece = operand_subword (target, i, 1, mode);
rtx op0_piece = operand_subword_force (op0, i, mode);
-
+
if (i == word)
{
if (!op0_is_abs)
return target;
}
-/* Expand the C99 copysign operation. OP0 and OP1 must be the same
+/* Expand the C99 copysign operation. OP0 and OP1 must be the same
scalar floating point mode. Return NULL if we do not know how to
expand the operation inline. */
{
enum machine_mode mode = GET_MODE (op0);
const struct real_format *fmt;
- int bitpos;
bool op0_is_abs;
rtx temp;
if (fmt == NULL || !fmt->has_signed_zero)
return NULL_RTX;
- bitpos = fmt->signbit;
- if (bitpos < 0)
- return NULL_RTX;
-
op0_is_abs = false;
if (GET_CODE (op0) == CONST_DOUBLE)
{
op0_is_abs = true;
}
- if (GET_CODE (op0) == CONST_DOUBLE
- || (neg_optab->handlers[mode].insn_code != CODE_FOR_nothing
- && abs_optab->handlers[mode].insn_code != CODE_FOR_nothing))
+ if (fmt->signbit_ro >= 0
+ && (GET_CODE (op0) == CONST_DOUBLE
+ || (neg_optab->handlers[mode].insn_code != CODE_FOR_nothing
+ && abs_optab->handlers[mode].insn_code != CODE_FOR_nothing)))
{
temp = expand_copysign_absneg (mode, op0, op1, target,
- bitpos, op0_is_abs);
+ fmt->signbit_ro, op0_is_abs);
if (temp)
return temp;
}
- return expand_copysign_bit (mode, op0, op1, target, bitpos, op0_is_abs);
+ if (fmt->signbit_rw < 0)
+ return NULL_RTX;
+ return expand_copysign_bit (mode, op0, op1, target,
+ fmt->signbit_rw, op0_is_abs);
}
\f
/* Generate an instruction whose insn-code is INSN_CODE,
temp = target;
- /* Sign and zero extension from memory is often done specially on
- RISC machines, so forcing into a register here can pessimize
- code. */
- if (flag_force_mem && code != SIGN_EXTEND && code != ZERO_EXTEND)
- op0 = force_not_mem (op0);
-
/* Now, if insn does not accept our operands, put them into pseudos. */
- if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+ if (!insn_data[icode].operand[1].predicate (op0, mode0))
op0 = copy_to_mode_reg (mode0, op0);
- if (! (*insn_data[icode].operand[0].predicate) (temp, GET_MODE (temp))
- || (flag_force_mem && MEM_P (temp)))
+ if (!insn_data[icode].operand[0].predicate (temp, GET_MODE (temp)))
temp = gen_reg_rtx (GET_MODE (temp));
pat = GEN_FCN (icode) (temp, op0);
emit_move_insn (target, temp);
}
\f
+struct no_conflict_data
+{
+ rtx target, first, insn;
+ bool must_stay;
+};
+
+/* Called via note_stores by emit_no_conflict_block and emit_libcall_block.
+ Set P->must_stay if the currently examined clobber / store has to stay
+ in the list of insns that constitute the actual no_conflict block /
+ libcall block. */
+static void
+no_conflict_move_test (rtx dest, rtx set, void *p0)
+{
+ struct no_conflict_data *p= p0;
+
+ /* If this inns directly contributes to setting the target, it must stay. */
+ if (reg_overlap_mentioned_p (p->target, dest))
+ p->must_stay = true;
+ /* If we haven't committed to keeping any other insns in the list yet,
+ there is nothing more to check. */
+ else if (p->insn == p->first)
+ return;
+ /* If this insn sets / clobbers a register that feeds one of the insns
+ already in the list, this insn has to stay too. */
+ else if (reg_overlap_mentioned_p (dest, PATTERN (p->first))
+ || (CALL_P (p->first) && (find_reg_fusage (p->first, USE, dest)))
+ || reg_used_between_p (dest, p->first, p->insn)
+ /* Likewise if this insn depends on a register set by a previous
+ insn in the list, or if it sets a result (presumably a hard
+ register) that is set or clobbered by a previous insn.
+ N.B. the modified_*_p (SET_DEST...) tests applied to a MEM
+ SET_DEST perform the former check on the address, and the latter
+ check on the MEM. */
+ || (GET_CODE (set) == SET
+ && (modified_in_p (SET_SRC (set), p->first)
+ || modified_in_p (SET_DEST (set), p->first)
+ || modified_between_p (SET_SRC (set), p->first, p->insn)
+ || modified_between_p (SET_DEST (set), p->first, p->insn))))
+ p->must_stay = true;
+}
+
/* Emit code to perform a series of operations on a multi-word quantity, one
word at a time.
these from the list. */
for (insn = insns; insn; insn = next)
{
- rtx set = 0, note;
- int i;
+ rtx note;
+ struct no_conflict_data data;
next = NEXT_INSN (insn);
if ((note = find_reg_note (insn, REG_RETVAL, NULL)) != NULL)
remove_note (insn, note);
- if (GET_CODE (PATTERN (insn)) == SET || GET_CODE (PATTERN (insn)) == USE
- || GET_CODE (PATTERN (insn)) == CLOBBER)
- set = PATTERN (insn);
- else if (GET_CODE (PATTERN (insn)) == PARALLEL)
- {
- for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
- if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
- {
- set = XVECEXP (PATTERN (insn), 0, i);
- break;
- }
- }
-
- if (set == 0)
- abort ();
-
- if (! reg_overlap_mentioned_p (target, SET_DEST (set)))
+ data.target = target;
+ data.first = insns;
+ data.insn = insn;
+ data.must_stay = 0;
+ note_stores (PATTERN (insn), no_conflict_move_test, &data);
+ if (! data.must_stay)
{
if (PREV_INSN (insn))
NEXT_INSN (PREV_INSN (insn)) = next;
next = NEXT_INSN (insn);
if (set != 0 && REG_P (SET_DEST (set))
- && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
- && (insn == insns
- || ((! INSN_P(insns)
- || ! reg_mentioned_p (SET_DEST (set), PATTERN (insns)))
- && ! reg_used_between_p (SET_DEST (set), insns, insn)
- && ! modified_in_p (SET_SRC (set), insns)
- && ! modified_between_p (SET_SRC (set), insns, insn))))
+ && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER)
{
- if (PREV_INSN (insn))
- NEXT_INSN (PREV_INSN (insn)) = next;
- else
- insns = next;
+ struct no_conflict_data data;
+
+ data.target = const0_rtx;
+ data.first = insns;
+ data.insn = insn;
+ data.must_stay = 0;
+ note_stores (PATTERN (insn), no_conflict_move_test, &data);
+ if (! data.must_stay)
+ {
+ if (PREV_INSN (insn))
+ NEXT_INSN (PREV_INSN (insn)) = next;
+ else
+ insns = next;
- if (next)
- PREV_INSN (next) = PREV_INSN (insn);
+ if (next)
+ PREV_INSN (next) = PREV_INSN (insn);
- add_insn (insn);
+ add_insn (insn);
+ }
}
/* Some ports use a loop to copy large arguments onto the stack.
comparison or emitting a library call to perform the comparison if no insn
is available to handle it.
The values which are passed in through pointers can be modified; the caller
- should perform the comparison on the modified values. */
+ should perform the comparison on the modified values. Constant
+ comparisons must have already been folded. */
static void
prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
class = GET_MODE_CLASS (mode);
- /* They could both be VOIDmode if both args are immediate constants,
- but we should fold that at an earlier stage.
- With no special code here, this will call abort,
- reminding the programmer to implement such folding. */
-
- if (mode != BLKmode && flag_force_mem)
- {
- /* Load duplicate non-volatile operands once. */
- if (rtx_equal_p (x, y) && ! volatile_refs_p (x))
- {
- x = force_not_mem (x);
- y = x;
- }
- else
- {
- x = force_not_mem (x);
- y = force_not_mem (y);
- }
- }
-
/* If we are inside an appropriately-short loop and we are optimizing,
force expensive constants into a register. */
if (CONSTANT_P (x) && optimize
y = force_reg (mode, y);
#ifdef HAVE_cc0
- /* Abort if we have a non-canonical comparison. The RTL documentation
- states that canonical comparisons are required only for targets which
- have cc0. */
- if (CONSTANT_P (x) && ! CONSTANT_P (y))
- abort ();
+ /* Make sure if we have a canonical comparison. The RTL
+ documentation states that canonical comparisons are required only
+ for targets which have cc0. */
+ gcc_assert (!CONSTANT_P (x) || CONSTANT_P (y));
#endif
/* Don't let both operands fail to indicate the mode. */
rtx opalign
= GEN_INT (MIN (MEM_ALIGN (x), MEM_ALIGN (y)) / BITS_PER_UNIT);
- if (size == 0)
- abort ();
+ gcc_assert (size);
/* Try to use a memory block compare insn - either cmpstr
or cmpmem will do. */
if (cmp_code == CODE_FOR_nothing)
cmp_code = cmpstr_optab[cmp_mode];
if (cmp_code == CODE_FOR_nothing)
+ cmp_code = cmpstrn_optab[cmp_mode];
+ if (cmp_code == CODE_FOR_nothing)
continue;
/* Must make sure the size fits the insn's mode. */
return;
}
- if (class == MODE_FLOAT)
- prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
-
- else
- abort ();
+ gcc_assert (class == MODE_FLOAT);
+ prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
}
/* Before emitting an insn with code ICODE, make sure that X, which is going
if (mode != wider_mode)
x = convert_modes (wider_mode, mode, x, unsignedp);
- if (! (*insn_data[icode].operand[opnum].predicate)
+ if (!insn_data[icode].operand[opnum].predicate
(x, insn_data[icode].operand[opnum].mode))
{
if (no_new_pseudos)
icode = cbranch_optab->handlers[(int) wider_mode].insn_code;
if (icode != CODE_FOR_nothing
- && (*insn_data[icode].operand[0].predicate) (test, wider_mode))
+ && insn_data[icode].operand[0].predicate (test, wider_mode))
{
x = prepare_operand (icode, x, 1, mode, wider_mode, unsignedp);
y = prepare_operand (icode, y, 2, mode, wider_mode, unsignedp);
x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp);
emit_insn (GEN_FCN (icode) (x));
if (label)
- emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
+ emit_jump_insn (bcc_gen_fctn[(int) comparison] (label));
return;
}
y = prepare_operand (icode, y, 1, mode, wider_mode, unsignedp);
emit_insn (GEN_FCN (icode) (x, y));
if (label)
- emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
+ emit_jump_insn (bcc_gen_fctn[(int) comparison] (label));
return;
}
}
while (wider_mode != VOIDmode);
- abort ();
+ gcc_unreachable ();
}
/* Generate code to compare X with Y so that the condition codes are
{
/* If we're not emitting a branch, this means some caller
is out of sync. */
- if (! label)
- abort ();
+ gcc_assert (label);
op0 = y, op1 = x;
comparison = swap_condition (comparison);
}
#ifdef HAVE_cc0
- /* If OP0 is still a constant, then both X and Y must be constants. Force
- X into a register to avoid aborting in emit_cmp_insn due to non-canonical
- RTL. */
+ /* If OP0 is still a constant, then both X and Y must be constants.
+ Force X into a register to create canonical RTL. */
if (CONSTANT_P (op0))
op0 = force_reg (mode, op0);
#endif
}
}
- if (mode == VOIDmode)
- abort ();
+ gcc_assert (mode != VOIDmode);
if (mode != orig_mode)
{
break;
default:
- abort ();
+ gcc_unreachable ();
}
equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode, word_mode,
equiv, true_rtx, false_rtx);
void
emit_indirect_jump (rtx loc)
{
- if (! ((*insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate)
- (loc, Pmode)))
+ if (!insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate
+ (loc, Pmode))
loc = copy_to_mode_reg (Pmode, loc);
emit_jump_insn (gen_indirect_jump (loc));
if (icode == CODE_FOR_nothing)
return 0;
- if (flag_force_mem)
- {
- op2 = force_not_mem (op2);
- op3 = force_not_mem (op3);
- }
-
if (!target)
target = gen_reg_rtx (mode);
/* If the insn doesn't accept these operands, put them in pseudos. */
- if (! (*insn_data[icode].operand[0].predicate)
+ if (!insn_data[icode].operand[0].predicate
(subtarget, insn_data[icode].operand[0].mode))
subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
- if (! (*insn_data[icode].operand[2].predicate)
+ if (!insn_data[icode].operand[2].predicate
(op2, insn_data[icode].operand[2].mode))
op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
- if (! (*insn_data[icode].operand[3].predicate)
+ if (!insn_data[icode].operand[3].predicate
(op3, insn_data[icode].operand[3].mode))
op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
if (icode == CODE_FOR_nothing)
return 0;
- if (flag_force_mem)
- {
- op2 = force_not_mem (op2);
- op3 = force_not_mem (op3);
- }
-
if (!target)
target = gen_reg_rtx (mode);
/* If the insn doesn't accept these operands, put them in pseudos. */
- if (! (*insn_data[icode].operand[0].predicate)
+ if (!insn_data[icode].operand[0].predicate
(target, insn_data[icode].operand[0].mode))
subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
else
subtarget = target;
- if (! (*insn_data[icode].operand[2].predicate)
+ if (!insn_data[icode].operand[2].predicate
(op2, insn_data[icode].operand[2].mode))
op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
- if (! (*insn_data[icode].operand[3].predicate)
+ if (!insn_data[icode].operand[3].predicate
(op3, insn_data[icode].operand[3].mode))
op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
{
int icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code;
- if (! ((*insn_data[icode].operand[0].predicate)
- (x, insn_data[icode].operand[0].mode))
- || ! ((*insn_data[icode].operand[1].predicate)
- (x, insn_data[icode].operand[1].mode))
- || ! ((*insn_data[icode].operand[2].predicate)
- (y, insn_data[icode].operand[2].mode)))
- abort ();
+ gcc_assert (insn_data[icode].operand[0].predicate
+ (x, insn_data[icode].operand[0].mode));
+ gcc_assert (insn_data[icode].operand[1].predicate
+ (x, insn_data[icode].operand[1].mode));
+ gcc_assert (insn_data[icode].operand[2].predicate
+ (y, insn_data[icode].operand[2].mode));
- return (GEN_FCN (icode) (x, x, y));
+ return GEN_FCN (icode) (x, x, y);
}
/* Generate and return an insn body to add r1 and c,
int icode = (int) add_optab->handlers[(int) GET_MODE (r0)].insn_code;
if (icode == CODE_FOR_nothing
- || ! ((*insn_data[icode].operand[0].predicate)
- (r0, insn_data[icode].operand[0].mode))
- || ! ((*insn_data[icode].operand[1].predicate)
- (r1, insn_data[icode].operand[1].mode))
- || ! ((*insn_data[icode].operand[2].predicate)
- (c, insn_data[icode].operand[2].mode)))
+ || !(insn_data[icode].operand[0].predicate
+ (r0, insn_data[icode].operand[0].mode))
+ || !(insn_data[icode].operand[1].predicate
+ (r1, insn_data[icode].operand[1].mode))
+ || !(insn_data[icode].operand[2].predicate
+ (c, insn_data[icode].operand[2].mode)))
return NULL_RTX;
- return (GEN_FCN (icode) (r0, r1, c));
+ return GEN_FCN (icode) (r0, r1, c);
}
int
{
int icode;
- if (GET_MODE (x) == VOIDmode)
- abort ();
+ gcc_assert (GET_MODE (x) != VOIDmode);
icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code;
if (icode == CODE_FOR_nothing)
return 0;
- if (! ((*insn_data[icode].operand[0].predicate)
- (x, insn_data[icode].operand[0].mode))
- || ! ((*insn_data[icode].operand[1].predicate)
- (x, insn_data[icode].operand[1].mode))
- || ! ((*insn_data[icode].operand[2].predicate)
- (y, insn_data[icode].operand[2].mode)))
+ if (!(insn_data[icode].operand[0].predicate
+ (x, insn_data[icode].operand[0].mode))
+ || !(insn_data[icode].operand[1].predicate
+ (x, insn_data[icode].operand[1].mode))
+ || !(insn_data[icode].operand[2].predicate
+ (y, insn_data[icode].operand[2].mode)))
return 0;
return 1;
{
int icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code;
- if (! ((*insn_data[icode].operand[0].predicate)
- (x, insn_data[icode].operand[0].mode))
- || ! ((*insn_data[icode].operand[1].predicate)
- (x, insn_data[icode].operand[1].mode))
- || ! ((*insn_data[icode].operand[2].predicate)
- (y, insn_data[icode].operand[2].mode)))
- abort ();
+ gcc_assert (insn_data[icode].operand[0].predicate
+ (x, insn_data[icode].operand[0].mode));
+ gcc_assert (insn_data[icode].operand[1].predicate
+ (x, insn_data[icode].operand[1].mode));
+ gcc_assert (insn_data[icode].operand[2].predicate
+ (y, insn_data[icode].operand[2].mode));
- return (GEN_FCN (icode) (x, x, y));
+ return GEN_FCN (icode) (x, x, y);
}
/* Generate and return an insn body to subtract r1 and c,
int icode = (int) sub_optab->handlers[(int) GET_MODE (r0)].insn_code;
if (icode == CODE_FOR_nothing
- || ! ((*insn_data[icode].operand[0].predicate)
- (r0, insn_data[icode].operand[0].mode))
- || ! ((*insn_data[icode].operand[1].predicate)
- (r1, insn_data[icode].operand[1].mode))
- || ! ((*insn_data[icode].operand[2].predicate)
- (c, insn_data[icode].operand[2].mode)))
+ || !(insn_data[icode].operand[0].predicate
+ (r0, insn_data[icode].operand[0].mode))
+ || !(insn_data[icode].operand[1].predicate
+ (r1, insn_data[icode].operand[1].mode))
+ || !(insn_data[icode].operand[2].predicate
+ (c, insn_data[icode].operand[2].mode)))
return NULL_RTX;
- return (GEN_FCN (icode) (r0, r1, c));
+ return GEN_FCN (icode) (r0, r1, c);
}
int
{
int icode;
- if (GET_MODE (x) == VOIDmode)
- abort ();
+ gcc_assert (GET_MODE (x) != VOIDmode);
icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code;
if (icode == CODE_FOR_nothing)
return 0;
- if (! ((*insn_data[icode].operand[0].predicate)
- (x, insn_data[icode].operand[0].mode))
- || ! ((*insn_data[icode].operand[1].predicate)
- (x, insn_data[icode].operand[1].mode))
- || ! ((*insn_data[icode].operand[2].predicate)
- (y, insn_data[icode].operand[2].mode)))
+ if (!(insn_data[icode].operand[0].predicate
+ (x, insn_data[icode].operand[0].mode))
+ || !(insn_data[icode].operand[1].predicate
+ (x, insn_data[icode].operand[1].mode))
+ || !(insn_data[icode].operand[2].predicate
+ (y, insn_data[icode].operand[2].mode)))
return 0;
return 1;
enum machine_mode fmode, imode;
/* Crash now, because we won't be able to decide which mode to use. */
- if (GET_MODE (from) == VOIDmode)
- abort ();
+ gcc_assert (GET_MODE (from) != VOIDmode);
/* Look for an insn to do the conversion. Do it in the specified
modes if possible; otherwise convert either input, output or both to
rtx temp;
REAL_VALUE_TYPE offset;
- if (flag_force_mem)
- from = force_not_mem (from);
-
/* Look for a usable floating mode FMODE wider than the source and at
least as wide as the target. Using FMODE will avoid rounding woes
with unsigned values greater than the signed maximum value. */
if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode))
from = convert_to_mode (SImode, from, unsignedp);
- if (flag_force_mem)
- from = force_not_mem (from);
-
libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc;
- if (!libfunc)
- abort ();
+ gcc_assert (libfunc);
start_sequence ();
lab1 = gen_label_rtx ();
lab2 = gen_label_rtx ();
- if (flag_force_mem)
- from = force_not_mem (from);
-
if (fmode != GET_MODE (from))
from = convert_to_mode (fmode, from, 0);
convert_optab tab = unsignedp ? ufix_optab : sfix_optab;
libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc;
- if (!libfunc)
- abort ();
-
- if (flag_force_mem)
- from = force_not_mem (from);
+ gcc_assert (libfunc);
start_sequence ();
parity_optab = init_optab (PARITY);
sqrt_optab = init_optab (SQRT);
floor_optab = init_optab (UNKNOWN);
+ lfloor_optab = init_optab (UNKNOWN);
ceil_optab = init_optab (UNKNOWN);
+ lceil_optab = init_optab (UNKNOWN);
round_optab = init_optab (UNKNOWN);
btrunc_optab = init_optab (UNKNOWN);
nearbyint_optab = init_optab (UNKNOWN);
rint_optab = init_optab (UNKNOWN);
+ lrint_optab = init_optab (UNKNOWN);
sincos_optab = init_optab (UNKNOWN);
sin_optab = init_optab (UNKNOWN);
asin_optab = init_optab (UNKNOWN);
exp10_optab = init_optab (UNKNOWN);
exp2_optab = init_optab (UNKNOWN);
expm1_optab = init_optab (UNKNOWN);
+ ldexp_optab = init_optab (UNKNOWN);
logb_optab = init_optab (UNKNOWN);
ilogb_optab = init_optab (UNKNOWN);
log_optab = init_optab (UNKNOWN);
cstore_optab = init_optab (UNKNOWN);
push_optab = init_optab (UNKNOWN);
+ reduc_smax_optab = init_optab (UNKNOWN);
+ reduc_umax_optab = init_optab (UNKNOWN);
+ reduc_smin_optab = init_optab (UNKNOWN);
+ reduc_umin_optab = init_optab (UNKNOWN);
+ reduc_splus_optab = init_optab (UNKNOWN);
+ reduc_uplus_optab = init_optab (UNKNOWN);
+
vec_extract_optab = init_optab (UNKNOWN);
vec_set_optab = init_optab (UNKNOWN);
vec_init_optab = init_optab (UNKNOWN);
+ vec_shl_optab = init_optab (UNKNOWN);
+ vec_shr_optab = init_optab (UNKNOWN);
vec_realign_load_optab = init_optab (UNKNOWN);
movmisalign_optab = init_optab (UNKNOWN);
for (i = 0; i < NUM_MACHINE_MODES; i++)
{
movmem_optab[i] = CODE_FOR_nothing;
- clrmem_optab[i] = CODE_FOR_nothing;
cmpstr_optab[i] = CODE_FOR_nothing;
+ cmpstrn_optab[i] = CODE_FOR_nothing;
cmpmem_optab[i] = CODE_FOR_nothing;
+ setmem_optab[i] = CODE_FOR_nothing;
+
+ sync_add_optab[i] = CODE_FOR_nothing;
+ sync_sub_optab[i] = CODE_FOR_nothing;
+ sync_ior_optab[i] = CODE_FOR_nothing;
+ sync_and_optab[i] = CODE_FOR_nothing;
+ sync_xor_optab[i] = CODE_FOR_nothing;
+ sync_nand_optab[i] = CODE_FOR_nothing;
+ sync_old_add_optab[i] = CODE_FOR_nothing;
+ sync_old_sub_optab[i] = CODE_FOR_nothing;
+ sync_old_ior_optab[i] = CODE_FOR_nothing;
+ sync_old_and_optab[i] = CODE_FOR_nothing;
+ sync_old_xor_optab[i] = CODE_FOR_nothing;
+ sync_old_nand_optab[i] = CODE_FOR_nothing;
+ sync_new_add_optab[i] = CODE_FOR_nothing;
+ sync_new_sub_optab[i] = CODE_FOR_nothing;
+ sync_new_ior_optab[i] = CODE_FOR_nothing;
+ sync_new_and_optab[i] = CODE_FOR_nothing;
+ sync_new_xor_optab[i] = CODE_FOR_nothing;
+ sync_new_nand_optab[i] = CODE_FOR_nothing;
+ sync_compare_and_swap[i] = CODE_FOR_nothing;
+ sync_compare_and_swap_cc[i] = CODE_FOR_nothing;
+ sync_lock_test_and_set[i] = CODE_FOR_nothing;
+ sync_lock_release[i] = CODE_FOR_nothing;
#ifdef HAVE_SECONDARY_RELOADS
reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
memset_libfunc = init_one_libfunc ("memset");
setbits_libfunc = init_one_libfunc ("__setbits");
- unwind_resume_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
- ? "_Unwind_SjLj_Resume"
- : "_Unwind_Resume");
#ifndef DONT_USE_BUILTIN_SETJMP
setjmp_libfunc = init_one_libfunc ("__builtin_setjmp");
longjmp_libfunc = init_one_libfunc ("__builtin_longjmp");
h = &o->handlers[j];
if (h->libfunc)
{
- if (GET_CODE (h->libfunc) != SYMBOL_REF)
- abort ();
+ gcc_assert (GET_CODE (h->libfunc) = SYMBOL_REF);
fprintf (stderr, "%s\t%s:\t%s\n",
GET_RTX_NAME (o->code),
GET_MODE_NAME (j),
}
/* Dump the conversion optabs. */
- for (i = 0; i < (int) CTI_MAX; ++i)
+ for (i = 0; i < (int) COI_MAX; ++i)
for (j = 0; j < NUM_MACHINE_MODES; ++j)
for (k = 0; k < NUM_MACHINE_MODES; ++k)
{
h = &o->handlers[j][k];
if (h->libfunc)
{
- if (GET_CODE (h->libfunc) != SYMBOL_REF)
- abort ();
+ gcc_assert (GET_CODE (h->libfunc) = SYMBOL_REF);
fprintf (stderr, "%s\t%s\t%s:\t%s\n",
GET_RTX_NAME (o->code),
GET_MODE_NAME (j),
emit_insn (GEN_FCN (icode) (op1, op2));
PUT_CODE (trap_rtx, code);
+ gcc_assert (HAVE_conditional_trap);
insn = gen_conditional_trap (trap_rtx, tcode);
if (insn)
{
case GE_EXPR:
code = unsignedp ? GEU : GE;
break;
-
+
case UNORDERED_EXPR:
code = UNORDERED;
break;
break;
default:
- abort ();
+ gcc_unreachable ();
}
return code;
}
tree t_op0, t_op1;
rtx rtx_op0, rtx_op1;
- if (!COMPARISON_CLASS_P (cond))
- {
- /* This is unlikely. While generating VEC_COND_EXPR,
- auto vectorizer ensures that condition is a relational
- operation. */
- abort ();
- }
- else
- {
- rcode = get_rtx_code (TREE_CODE (cond), unsignedp);
- t_op0 = TREE_OPERAND (cond, 0);
- t_op1 = TREE_OPERAND (cond, 1);
- }
+ /* This is unlikely. While generating VEC_COND_EXPR, auto vectorizer
+ ensures that condition is a relational operation. */
+ gcc_assert (COMPARISON_CLASS_P (cond));
+
+ rcode = get_rtx_code (TREE_CODE (cond), unsignedp);
+ t_op0 = TREE_OPERAND (cond, 0);
+ t_op1 = TREE_OPERAND (cond, 1);
/* Expand operands. */
rtx_op0 = expand_expr (t_op0, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op0)), 1);
rtx_op1 = expand_expr (t_op1, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op1)), 1);
- if (!(*insn_data[icode].operand[4].predicate) (rtx_op0, GET_MODE (rtx_op0))
+ if (!insn_data[icode].operand[4].predicate (rtx_op0, GET_MODE (rtx_op0))
&& GET_MODE (rtx_op0) != VOIDmode)
rtx_op0 = force_reg (GET_MODE (rtx_op0), rtx_op0);
-
- if (!(*insn_data[icode].operand[5].predicate) (rtx_op1, GET_MODE (rtx_op1))
+
+ if (!insn_data[icode].operand[5].predicate (rtx_op1, GET_MODE (rtx_op1))
&& GET_MODE (rtx_op1) != VOIDmode)
rtx_op1 = force_reg (GET_MODE (rtx_op1), rtx_op1);
}
/* Return insn code for VEC_COND_EXPR EXPR. */
-
-static inline enum insn_code
+
+static inline enum insn_code
get_vcond_icode (tree expr, enum machine_mode mode)
{
enum insn_code icode = CODE_FOR_nothing;
if (icode == CODE_FOR_nothing)
return 0;
- if (!target)
+ if (!target || !insn_data[icode].operand[0].predicate (target, mode))
target = gen_reg_rtx (mode);
/* Get comparison rtx. First expand both cond expr operands. */
- comparison = vector_compare_rtx (TREE_OPERAND (vec_cond_expr, 0),
+ comparison = vector_compare_rtx (TREE_OPERAND (vec_cond_expr, 0),
unsignedp, icode);
cc_op0 = XEXP (comparison, 0);
cc_op1 = XEXP (comparison, 1);
/* Expand both operands and force them in reg, if required. */
rtx_op1 = expand_expr (TREE_OPERAND (vec_cond_expr, 1),
NULL_RTX, VOIDmode, 1);
- if (!(*insn_data[icode].operand[1].predicate) (rtx_op1, mode)
+ if (!insn_data[icode].operand[1].predicate (rtx_op1, mode)
&& mode != VOIDmode)
rtx_op1 = force_reg (mode, rtx_op1);
rtx_op2 = expand_expr (TREE_OPERAND (vec_cond_expr, 2),
NULL_RTX, VOIDmode, 1);
- if (!(*insn_data[icode].operand[2].predicate) (rtx_op2, mode)
+ if (!insn_data[icode].operand[2].predicate (rtx_op2, mode)
&& mode != VOIDmode)
rtx_op2 = force_reg (mode, rtx_op2);
/* Emit instruction! */
- emit_insn (GEN_FCN (icode) (target, rtx_op1, rtx_op2,
+ emit_insn (GEN_FCN (icode) (target, rtx_op1, rtx_op2,
comparison, cc_op0, cc_op1));
return target;
}
+
+\f
+/* This is an internal subroutine of the other compare_and_swap expanders.
+ MEM, OLD_VAL and NEW_VAL are as you'd expect for a compare-and-swap
+ operation. TARGET is an optional place to store the value result of
+ the operation. ICODE is the particular instruction to expand. Return
+ the result of the operation. */
+
+static rtx
+expand_val_compare_and_swap_1 (rtx mem, rtx old_val, rtx new_val,
+ rtx target, enum insn_code icode)
+{
+ enum machine_mode mode = GET_MODE (mem);
+ rtx insn;
+
+ if (!target || !insn_data[icode].operand[0].predicate (target, mode))
+ target = gen_reg_rtx (mode);
+
+ if (GET_MODE (old_val) != VOIDmode && GET_MODE (old_val) != mode)
+ old_val = convert_modes (mode, GET_MODE (old_val), old_val, 1);
+ if (!insn_data[icode].operand[2].predicate (old_val, mode))
+ old_val = force_reg (mode, old_val);
+
+ if (GET_MODE (new_val) != VOIDmode && GET_MODE (new_val) != mode)
+ new_val = convert_modes (mode, GET_MODE (new_val), new_val, 1);
+ if (!insn_data[icode].operand[3].predicate (new_val, mode))
+ new_val = force_reg (mode, new_val);
+
+ insn = GEN_FCN (icode) (target, mem, old_val, new_val);
+ if (insn == NULL_RTX)
+ return NULL_RTX;
+ emit_insn (insn);
+
+ return target;
+}
+
+/* Expand a compare-and-swap operation and return its value. */
+
+rtx
+expand_val_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target)
+{
+ enum machine_mode mode = GET_MODE (mem);
+ enum insn_code icode = sync_compare_and_swap[mode];
+
+ if (icode == CODE_FOR_nothing)
+ return NULL_RTX;
+
+ return expand_val_compare_and_swap_1 (mem, old_val, new_val, target, icode);
+}
+
+/* Expand a compare-and-swap operation and store true into the result if
+ the operation was successful and false otherwise. Return the result.
+ Unlike other routines, TARGET is not optional. */
+
+rtx
+expand_bool_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target)
+{
+ enum machine_mode mode = GET_MODE (mem);
+ enum insn_code icode;
+ rtx subtarget, label0, label1;
+
+ /* If the target supports a compare-and-swap pattern that simultaneously
+ sets some flag for success, then use it. Otherwise use the regular
+ compare-and-swap and follow that immediately with a compare insn. */
+ icode = sync_compare_and_swap_cc[mode];
+ switch (icode)
+ {
+ default:
+ subtarget = expand_val_compare_and_swap_1 (mem, old_val, new_val,
+ NULL_RTX, icode);
+ if (subtarget != NULL_RTX)
+ break;
+
+ /* FALLTHRU */
+ case CODE_FOR_nothing:
+ icode = sync_compare_and_swap[mode];
+ if (icode == CODE_FOR_nothing)
+ return NULL_RTX;
+
+ /* Ensure that if old_val == mem, that we're not comparing
+ against an old value. */
+ if (MEM_P (old_val))
+ old_val = force_reg (mode, old_val);
+
+ subtarget = expand_val_compare_and_swap_1 (mem, old_val, new_val,
+ NULL_RTX, icode);
+ if (subtarget == NULL_RTX)
+ return NULL_RTX;
+
+ emit_cmp_insn (subtarget, old_val, EQ, const0_rtx, mode, true);
+ }
+
+ /* If the target has a sane STORE_FLAG_VALUE, then go ahead and use a
+ setcc instruction from the beginning. We don't work too hard here,
+ but it's nice to not be stupid about initial code gen either. */
+ if (STORE_FLAG_VALUE == 1)
+ {
+ icode = setcc_gen_code[EQ];
+ if (icode != CODE_FOR_nothing)
+ {
+ enum machine_mode cmode = insn_data[icode].operand[0].mode;
+ rtx insn;
+
+ subtarget = target;
+ if (!insn_data[icode].operand[0].predicate (target, cmode))
+ subtarget = gen_reg_rtx (cmode);
+
+ insn = GEN_FCN (icode) (subtarget);
+ if (insn)
+ {
+ emit_insn (insn);
+ if (GET_MODE (target) != GET_MODE (subtarget))
+ {
+ convert_move (target, subtarget, 1);
+ subtarget = target;
+ }
+ return subtarget;
+ }
+ }
+ }
+
+ /* Without an appropriate setcc instruction, use a set of branches to
+ get 1 and 0 stored into target. Presumably if the target has a
+ STORE_FLAG_VALUE that isn't 1, then this will get cleaned up by ifcvt. */
+
+ label0 = gen_label_rtx ();
+ label1 = gen_label_rtx ();
+
+ emit_jump_insn (bcc_gen_fctn[EQ] (label0));
+ emit_move_insn (target, const0_rtx);
+ emit_jump_insn (gen_jump (label1));
+ emit_barrier ();
+ emit_label (label0);
+ emit_move_insn (target, const1_rtx);
+ emit_label (label1);
+
+ return target;
+}
+
+/* This is a helper function for the other atomic operations. This function
+ emits a loop that contains SEQ that iterates until a compare-and-swap
+ operation at the end succeeds. MEM is the memory to be modified. SEQ is
+ a set of instructions that takes a value from OLD_REG as an input and
+ produces a value in NEW_REG as an output. Before SEQ, OLD_REG will be
+ set to the current contents of MEM. After SEQ, a compare-and-swap will
+ attempt to update MEM with NEW_REG. The function returns true when the
+ loop was generated successfully. */
+
+static bool
+expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq)
+{
+ enum machine_mode mode = GET_MODE (mem);
+ enum insn_code icode;
+ rtx label, cmp_reg, subtarget;
+
+ /* The loop we want to generate looks like
+
+ cmp_reg = mem;
+ label:
+ old_reg = cmp_reg;
+ seq;
+ cmp_reg = compare-and-swap(mem, old_reg, new_reg)
+ if (cmp_reg != old_reg)
+ goto label;
+
+ Note that we only do the plain load from memory once. Subsequent
+ iterations use the value loaded by the compare-and-swap pattern. */
+
+ label = gen_label_rtx ();
+ cmp_reg = gen_reg_rtx (mode);
+
+ emit_move_insn (cmp_reg, mem);
+ emit_label (label);
+ emit_move_insn (old_reg, cmp_reg);
+ if (seq)
+ emit_insn (seq);
+
+ /* If the target supports a compare-and-swap pattern that simultaneously
+ sets some flag for success, then use it. Otherwise use the regular
+ compare-and-swap and follow that immediately with a compare insn. */
+ icode = sync_compare_and_swap_cc[mode];
+ switch (icode)
+ {
+ default:
+ subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg,
+ cmp_reg, icode);
+ if (subtarget != NULL_RTX)
+ {
+ gcc_assert (subtarget == cmp_reg);
+ break;
+ }
+
+ /* FALLTHRU */
+ case CODE_FOR_nothing:
+ icode = sync_compare_and_swap[mode];
+ if (icode == CODE_FOR_nothing)
+ return false;
+
+ subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg,
+ cmp_reg, icode);
+ if (subtarget == NULL_RTX)
+ return false;
+ if (subtarget != cmp_reg)
+ emit_move_insn (cmp_reg, subtarget);
+
+ emit_cmp_insn (cmp_reg, old_reg, EQ, const0_rtx, mode, true);
+ }
+
+ /* ??? Mark this jump predicted not taken? */
+ emit_jump_insn (bcc_gen_fctn[NE] (label));
+
+ return true;
+}
+
+/* This function generates the atomic operation MEM CODE= VAL. In this
+ case, we do not care about any resulting value. Returns NULL if we
+ cannot generate the operation. */
+
+rtx
+expand_sync_operation (rtx mem, rtx val, enum rtx_code code)
+{
+ enum machine_mode mode = GET_MODE (mem);
+ enum insn_code icode;
+ rtx insn;
+
+ /* Look to see if the target supports the operation directly. */
+ switch (code)
+ {
+ case PLUS:
+ icode = sync_add_optab[mode];
+ break;
+ case IOR:
+ icode = sync_ior_optab[mode];
+ break;
+ case XOR:
+ icode = sync_xor_optab[mode];
+ break;
+ case AND:
+ icode = sync_and_optab[mode];
+ break;
+ case NOT:
+ icode = sync_nand_optab[mode];
+ break;
+
+ case MINUS:
+ icode = sync_sub_optab[mode];
+ if (icode == CODE_FOR_nothing)
+ {
+ icode = sync_add_optab[mode];
+ if (icode != CODE_FOR_nothing)
+ {
+ val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1);
+ code = PLUS;
+ }
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* Generate the direct operation, if present. */
+ if (icode != CODE_FOR_nothing)
+ {
+ if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
+ val = convert_modes (mode, GET_MODE (val), val, 1);
+ if (!insn_data[icode].operand[1].predicate (val, mode))
+ val = force_reg (mode, val);
+
+ insn = GEN_FCN (icode) (mem, val);
+ if (insn)
+ {
+ emit_insn (insn);
+ return const0_rtx;
+ }
+ }
+
+ /* Failing that, generate a compare-and-swap loop in which we perform the
+ operation with normal arithmetic instructions. */
+ if (sync_compare_and_swap[mode] != CODE_FOR_nothing)
+ {
+ rtx t0 = gen_reg_rtx (mode), t1;
+
+ start_sequence ();
+
+ t1 = t0;
+ if (code == NOT)
+ {
+ t1 = expand_simple_unop (mode, NOT, t1, NULL_RTX, true);
+ code = AND;
+ }
+ t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX,
+ true, OPTAB_LIB_WIDEN);
+
+ insn = get_insns ();
+ end_sequence ();
+
+ if (t1 != NULL && expand_compare_and_swap_loop (mem, t0, t1, insn))
+ return const0_rtx;
+ }
+
+ return NULL_RTX;
+}
+
+/* This function generates the atomic operation MEM CODE= VAL. In this
+ case, we do care about the resulting value: if AFTER is true then
+ return the value MEM holds after the operation, if AFTER is false
+ then return the value MEM holds before the operation. TARGET is an
+ optional place for the result value to be stored. */
+
+rtx
+expand_sync_fetch_operation (rtx mem, rtx val, enum rtx_code code,
+ bool after, rtx target)
+{
+ enum machine_mode mode = GET_MODE (mem);
+ enum insn_code old_code, new_code, icode;
+ bool compensate;
+ rtx insn;
+
+ /* Look to see if the target supports the operation directly. */
+ switch (code)
+ {
+ case PLUS:
+ old_code = sync_old_add_optab[mode];
+ new_code = sync_new_add_optab[mode];
+ break;
+ case IOR:
+ old_code = sync_old_ior_optab[mode];
+ new_code = sync_new_ior_optab[mode];
+ break;
+ case XOR:
+ old_code = sync_old_xor_optab[mode];
+ new_code = sync_new_xor_optab[mode];
+ break;
+ case AND:
+ old_code = sync_old_and_optab[mode];
+ new_code = sync_new_and_optab[mode];
+ break;
+ case NOT:
+ old_code = sync_old_nand_optab[mode];
+ new_code = sync_new_nand_optab[mode];
+ break;
+
+ case MINUS:
+ old_code = sync_old_sub_optab[mode];
+ new_code = sync_new_sub_optab[mode];
+ if (old_code == CODE_FOR_nothing && new_code == CODE_FOR_nothing)
+ {
+ old_code = sync_old_add_optab[mode];
+ new_code = sync_new_add_optab[mode];
+ if (old_code != CODE_FOR_nothing || new_code != CODE_FOR_nothing)
+ {
+ val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1);
+ code = PLUS;
+ }
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* If the target does supports the proper new/old operation, great. But
+ if we only support the opposite old/new operation, check to see if we
+ can compensate. In the case in which the old value is supported, then
+ we can always perform the operation again with normal arithmetic. In
+ the case in which the new value is supported, then we can only handle
+ this in the case the operation is reversible. */
+ compensate = false;
+ if (after)
+ {
+ icode = new_code;
+ if (icode == CODE_FOR_nothing)
+ {
+ icode = old_code;
+ if (icode != CODE_FOR_nothing)
+ compensate = true;
+ }
+ }
+ else
+ {
+ icode = old_code;
+ if (icode == CODE_FOR_nothing
+ && (code == PLUS || code == MINUS || code == XOR))
+ {
+ icode = new_code;
+ if (icode != CODE_FOR_nothing)
+ compensate = true;
+ }
+ }
+
+ /* If we found something supported, great. */
+ if (icode != CODE_FOR_nothing)
+ {
+ if (!target || !insn_data[icode].operand[0].predicate (target, mode))
+ target = gen_reg_rtx (mode);
+
+ if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
+ val = convert_modes (mode, GET_MODE (val), val, 1);
+ if (!insn_data[icode].operand[2].predicate (val, mode))
+ val = force_reg (mode, val);
+
+ insn = GEN_FCN (icode) (target, mem, val);
+ if (insn)
+ {
+ emit_insn (insn);
+
+ /* If we need to compensate for using an operation with the
+ wrong return value, do so now. */
+ if (compensate)
+ {
+ if (!after)
+ {
+ if (code == PLUS)
+ code = MINUS;
+ else if (code == MINUS)
+ code = PLUS;
+ }
+
+ if (code == NOT)
+ target = expand_simple_unop (mode, NOT, target, NULL_RTX, true);
+ target = expand_simple_binop (mode, code, target, val, NULL_RTX,
+ true, OPTAB_LIB_WIDEN);
+ }
+
+ return target;
+ }
+ }
+
+ /* Failing that, generate a compare-and-swap loop in which we perform the
+ operation with normal arithmetic instructions. */
+ if (sync_compare_and_swap[mode] != CODE_FOR_nothing)
+ {
+ rtx t0 = gen_reg_rtx (mode), t1;
+
+ if (!target || !register_operand (target, mode))
+ target = gen_reg_rtx (mode);
+
+ start_sequence ();
+
+ if (!after)
+ emit_move_insn (target, t0);
+ t1 = t0;
+ if (code == NOT)
+ {
+ t1 = expand_simple_unop (mode, NOT, t1, NULL_RTX, true);
+ code = AND;
+ }
+ t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX,
+ true, OPTAB_LIB_WIDEN);
+ if (after)
+ emit_move_insn (target, t1);
+
+ insn = get_insns ();
+ end_sequence ();
+
+ if (t1 != NULL && expand_compare_and_swap_loop (mem, t0, t1, insn))
+ return target;
+ }
+
+ return NULL_RTX;
+}
+
+/* This function expands a test-and-set operation. Ideally we atomically
+ store VAL in MEM and return the previous value in MEM. Some targets
+ may not support this operation and only support VAL with the constant 1;
+ in this case while the return value will be 0/1, but the exact value
+ stored in MEM is target defined. TARGET is an option place to stick
+ the return value. */
+
+rtx
+expand_sync_lock_test_and_set (rtx mem, rtx val, rtx target)
+{
+ enum machine_mode mode = GET_MODE (mem);
+ enum insn_code icode;
+ rtx insn;
+
+ /* If the target supports the test-and-set directly, great. */
+ icode = sync_lock_test_and_set[mode];
+ if (icode != CODE_FOR_nothing)
+ {
+ if (!target || !insn_data[icode].operand[0].predicate (target, mode))
+ target = gen_reg_rtx (mode);
+
+ if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
+ val = convert_modes (mode, GET_MODE (val), val, 1);
+ if (!insn_data[icode].operand[2].predicate (val, mode))
+ val = force_reg (mode, val);
+
+ insn = GEN_FCN (icode) (target, mem, val);
+ if (insn)
+ {
+ emit_insn (insn);
+ return target;
+ }
+ }
+
+ /* Otherwise, use a compare-and-swap loop for the exchange. */
+ if (sync_compare_and_swap[mode] != CODE_FOR_nothing)
+ {
+ if (!target || !register_operand (target, mode))
+ target = gen_reg_rtx (mode);
+ if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
+ val = convert_modes (mode, GET_MODE (val), val, 1);
+ if (expand_compare_and_swap_loop (mem, target, val, NULL_RTX))
+ return target;
+ }
+
+ return NULL_RTX;
+}
+
#include "gt-optabs.h"