middle-end: Allow backend to expand/split double word compare to 0/-1.
This patch to the middle-end's RTL expansion reorders the code in emit_store_flag_1 so that the backend has more control over how best to expand/split double word equality/inequality comparisons against zero or minus one. With the current implementation, the middle-end always decides to lower this idiom during RTL expansion using SUBREGs and word mode instructions, without ever consulting the backend's machine description. Hence on x86_64, a TImode comparison against zero is always expanded as: (parallel [ (set (reg:DI 91) (ior:DI (subreg:DI (reg:TI 88) 0) (subreg:DI (reg:TI 88) 8))) (clobber (reg:CC 17 flags))]) (set (reg:CCZ 17 flags) (compare:CCZ (reg:DI 91) (const_int 0 [0]))) This patch, which makes no changes to the code itself, simply reorders the clauses in emit_store_flag_1 so that the middle-end first attempts expansion using the target's doubleword mode cstore optab/expander, and only if this fails, falls back to lowering to word mode operations. On x86_64, this allows the expander to produce: (set (reg:CCZ 17 flags) (compare:CCZ (reg:TI 88) (const_int 0 [0]))) which is a candidate for scalar-to-vector transformations (and combine simplifications etc.). On targets that don't define a cstore pattern for doubleword integer modes, there should be no change in behaviour. For those that do, the current behaviour can be restored (if desired) by restricting the expander/insn to not apply when the comparison is EQ or NE, and operand[2] is either const0_rtx or constm1_rtx. This change just keeps RTL expansion more consistent (in philosophy). For other doubleword comparisons, such as with operators LT and GT, or with constants other than zero or -1, the wishes of the backend are respected, and only if the optab expansion fails are the default fall-back implementations using narrower integer mode operations (and conditional jumps) used. 2022-08-05 Roger Sayle <roger@nextmovesoftware.com> gcc/ChangeLog * expmed.cc (emit_store_flag_1): Move code to expand double word equality and inequality against zero or -1, using word operations, to after trying to use the backend's cstore<mode>4 optab/expander.
This commit is contained in:
parent
58a644cfde
commit
cc01a27db5
145
gcc/expmed.cc
145
gcc/expmed.cc
|
@ -5662,9 +5662,81 @@ emit_store_flag_1 (rtx target, enum rtx_code code, rtx op0, rtx op1,
|
|||
break;
|
||||
}
|
||||
|
||||
/* If this is A < 0 or A >= 0, we can do this by taking the ones
|
||||
complement of A (for GE) and shifting the sign bit to the low bit. */
|
||||
scalar_int_mode int_mode;
|
||||
if (op1 == const0_rtx && (code == LT || code == GE)
|
||||
&& is_int_mode (mode, &int_mode)
|
||||
&& (normalizep || STORE_FLAG_VALUE == 1
|
||||
|| val_signbit_p (int_mode, STORE_FLAG_VALUE)))
|
||||
{
|
||||
scalar_int_mode int_target_mode;
|
||||
subtarget = target;
|
||||
|
||||
if (!target)
|
||||
int_target_mode = int_mode;
|
||||
else
|
||||
{
|
||||
/* If the result is to be wider than OP0, it is best to convert it
|
||||
first. If it is to be narrower, it is *incorrect* to convert it
|
||||
first. */
|
||||
int_target_mode = as_a <scalar_int_mode> (target_mode);
|
||||
if (GET_MODE_SIZE (int_target_mode) > GET_MODE_SIZE (int_mode))
|
||||
{
|
||||
op0 = convert_modes (int_target_mode, int_mode, op0, 0);
|
||||
int_mode = int_target_mode;
|
||||
}
|
||||
}
|
||||
|
||||
if (int_target_mode != int_mode)
|
||||
subtarget = 0;
|
||||
|
||||
if (code == GE)
|
||||
op0 = expand_unop (int_mode, one_cmpl_optab, op0,
|
||||
((STORE_FLAG_VALUE == 1 || normalizep)
|
||||
? 0 : subtarget), 0);
|
||||
|
||||
if (STORE_FLAG_VALUE == 1 || normalizep)
|
||||
/* If we are supposed to produce a 0/1 value, we want to do
|
||||
a logical shift from the sign bit to the low-order bit; for
|
||||
a -1/0 value, we do an arithmetic shift. */
|
||||
op0 = expand_shift (RSHIFT_EXPR, int_mode, op0,
|
||||
GET_MODE_BITSIZE (int_mode) - 1,
|
||||
subtarget, normalizep != -1);
|
||||
|
||||
if (int_mode != int_target_mode)
|
||||
op0 = convert_modes (int_target_mode, int_mode, op0, 0);
|
||||
|
||||
return op0;
|
||||
}
|
||||
|
||||
/* Next try expanding this via the backend's cstore<mode>4. */
|
||||
mclass = GET_MODE_CLASS (mode);
|
||||
FOR_EACH_MODE_FROM (compare_mode, mode)
|
||||
{
|
||||
machine_mode optab_mode = mclass == MODE_CC ? CCmode : compare_mode;
|
||||
icode = optab_handler (cstore_optab, optab_mode);
|
||||
if (icode != CODE_FOR_nothing)
|
||||
{
|
||||
do_pending_stack_adjust ();
|
||||
rtx tem = emit_cstore (target, icode, code, mode, compare_mode,
|
||||
unsignedp, op0, op1, normalizep, target_mode);
|
||||
if (tem)
|
||||
return tem;
|
||||
|
||||
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
|
||||
{
|
||||
tem = emit_cstore (target, icode, scode, mode, compare_mode,
|
||||
unsignedp, op1, op0, normalizep, target_mode);
|
||||
if (tem)
|
||||
return tem;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are comparing a double-word integer with zero or -1, we can
|
||||
convert the comparison into one involving a single word. */
|
||||
scalar_int_mode int_mode;
|
||||
if (is_int_mode (mode, &int_mode)
|
||||
&& GET_MODE_BITSIZE (int_mode) == BITS_PER_WORD * 2
|
||||
&& (!MEM_P (op0) || ! MEM_VOLATILE_P (op0)))
|
||||
|
@ -5717,77 +5789,6 @@ emit_store_flag_1 (rtx target, enum rtx_code code, rtx op0, rtx op1,
|
|||
}
|
||||
}
|
||||
|
||||
/* If this is A < 0 or A >= 0, we can do this by taking the ones
|
||||
complement of A (for GE) and shifting the sign bit to the low bit. */
|
||||
if (op1 == const0_rtx && (code == LT || code == GE)
|
||||
&& is_int_mode (mode, &int_mode)
|
||||
&& (normalizep || STORE_FLAG_VALUE == 1
|
||||
|| val_signbit_p (int_mode, STORE_FLAG_VALUE)))
|
||||
{
|
||||
scalar_int_mode int_target_mode;
|
||||
subtarget = target;
|
||||
|
||||
if (!target)
|
||||
int_target_mode = int_mode;
|
||||
else
|
||||
{
|
||||
/* If the result is to be wider than OP0, it is best to convert it
|
||||
first. If it is to be narrower, it is *incorrect* to convert it
|
||||
first. */
|
||||
int_target_mode = as_a <scalar_int_mode> (target_mode);
|
||||
if (GET_MODE_SIZE (int_target_mode) > GET_MODE_SIZE (int_mode))
|
||||
{
|
||||
op0 = convert_modes (int_target_mode, int_mode, op0, 0);
|
||||
int_mode = int_target_mode;
|
||||
}
|
||||
}
|
||||
|
||||
if (int_target_mode != int_mode)
|
||||
subtarget = 0;
|
||||
|
||||
if (code == GE)
|
||||
op0 = expand_unop (int_mode, one_cmpl_optab, op0,
|
||||
((STORE_FLAG_VALUE == 1 || normalizep)
|
||||
? 0 : subtarget), 0);
|
||||
|
||||
if (STORE_FLAG_VALUE == 1 || normalizep)
|
||||
/* If we are supposed to produce a 0/1 value, we want to do
|
||||
a logical shift from the sign bit to the low-order bit; for
|
||||
a -1/0 value, we do an arithmetic shift. */
|
||||
op0 = expand_shift (RSHIFT_EXPR, int_mode, op0,
|
||||
GET_MODE_BITSIZE (int_mode) - 1,
|
||||
subtarget, normalizep != -1);
|
||||
|
||||
if (int_mode != int_target_mode)
|
||||
op0 = convert_modes (int_target_mode, int_mode, op0, 0);
|
||||
|
||||
return op0;
|
||||
}
|
||||
|
||||
mclass = GET_MODE_CLASS (mode);
|
||||
FOR_EACH_MODE_FROM (compare_mode, mode)
|
||||
{
|
||||
machine_mode optab_mode = mclass == MODE_CC ? CCmode : compare_mode;
|
||||
icode = optab_handler (cstore_optab, optab_mode);
|
||||
if (icode != CODE_FOR_nothing)
|
||||
{
|
||||
do_pending_stack_adjust ();
|
||||
rtx tem = emit_cstore (target, icode, code, mode, compare_mode,
|
||||
unsignedp, op0, op1, normalizep, target_mode);
|
||||
if (tem)
|
||||
return tem;
|
||||
|
||||
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
|
||||
{
|
||||
tem = emit_cstore (target, icode, scode, mode, compare_mode,
|
||||
unsignedp, op1, op0, normalizep, target_mode);
|
||||
if (tem)
|
||||
return tem;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue