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"
#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;
}
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;
/* 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[3].predicate) (xop2, mode2)
+ if (!insn_data[icode].operand[3].predicate (xop2, mode2)
&& mode2 != VOIDmode)
xop2 = copy_to_mode_reg (mode2, xop2);
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
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);
}
/* 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);
}
emit_move_insn (target_piece, newx);
}
+ else
+ {
+ if (x != target_piece)
+ emit_move_insn (target_piece, x);
+ }
carry_in = carry_out;
}
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)
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);
}
/* 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);
/* 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))
+ if (!insn_data[icode].operand[0].predicate (temp, GET_MODE (temp))
|| (flag_force_mem && MEM_P (temp)))
temp = gen_reg_rtx (GET_MODE (temp));
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. 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. */
+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_mentioned_p (dest, PATTERN (p->first))
+ || 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. */
+ || (GET_CODE (set) == SET
+ && (modified_in_p (SET_SRC (set), p->first)
+ || modified_between_p (SET_SRC (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;
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. */
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. */
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 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 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
from = force_not_mem (from);
libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc;
- if (!libfunc)
- abort ();
+ gcc_assert (libfunc);
start_sequence ();
convert_optab tab = unsignedp ? ufix_optab : sfix_optab;
libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc;
- if (!libfunc)
- abort ();
+ gcc_assert (libfunc);
if (flag_force_mem)
from = force_not_mem (from);
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);
- llrint_optab = init_optab (UNKNOWN);
sincos_optab = init_optab (UNKNOWN);
sin_optab = init_optab (UNKNOWN);
asin_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;
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),
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)
{
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);
/* 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);
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"