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:
Roger Sayle 2022-08-05 21:05:35 +01:00
parent 58a644cfde
commit cc01a27db5
1 changed files with 73 additions and 72 deletions

View File

@ -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;
}