Skip to content

Commit

Permalink
[arm] Early split simple DImode equality comparisons
Browse files Browse the repository at this point in the history
This is the first step of early splitting all the DImode comparison
operations.  We start by factoring the DImode handling out of
arm_gen_compare_reg into its own function.

Simple DImode equality comparisions (such as equality with zero, or
equality with a constant that is zero in one of the two word values
that it comprises) can be done using a single subtract followed by an
ORRS instruction.  This avoids the need for conditional execution.

For example, (r0 != 5) can be written as

	SUB	Rt, R0, gcc-mirror#5
	ORRS	Rt, Rt, R1

The ORRS is now expanded using an SImode pattern that already exists
in the MD file and this gives the register allocator more freedom to
select registers (consecutive pairs are no-longer required).
Furthermore, we can then delete the arm_cmpdi_zero pattern as it is
no-longer required.  We use SUB for the value adjustment as this has a
generally more flexible range of immediates than XOR and what's more
has the opportunity to be relaxed in thumb2 to a 16-bit SUBS
instruction.

	* config/arm/arm.c (arm_select_cc_mode): For DImode equality tests
	return CC_Zmode if comparing against a constant where one word is
	zero.
	(arm_gen_compare_reg): Split DImode handling to ...
	(arm_gen_dicompare_reg): ... here.  Handle equality comparisons
	against simple constants.
	* config/arm/arm.md (arm_cmpdi_zero): Delete pattern.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@277177 138bc75d-0d04-0410-961f-82ee72b054a4
  • Loading branch information
rearnsha authored and emsr committed Nov 2, 2019
1 parent 02c37af commit bb9a709
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 30 deletions.
10 changes: 10 additions & 0 deletions gcc/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
2019-10-18 Richard Earnshaw <rearnsha@arm.com>

* config/arm/arm.c (arm_select_cc_mode): For DImode equality tests
return CC_Zmode if comparing against a constant where one word is
zero.
(arm_gen_compare_reg): Split DImode handling to ...
(arm_gen_dicompare_reg): ... here. Handle equality comparisons
against simple constants.
* config/arm/arm.md (arm_cmpdi_zero): Delete pattern.

2019-10-18 Richard Earnshaw <rearnsha@arm.com>

* config/arm/arm.md (subsi3_carryin_shift_alt): New pattern.
Expand Down
87 changes: 68 additions & 19 deletions gcc/config/arm/arm.c
Original file line number Diff line number Diff line change
Expand Up @@ -15350,8 +15350,14 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
case EQ:
case NE:
/* A DImode comparison against zero can be implemented by
or'ing the two halves together. */
if (y == const0_rtx)
or'ing the two halves together. We can also handle
immediates where one word of that value is zero by
subtracting the non-zero word from the corresponding word
in the other register and then ORRing it with the other
word. */
if (CONST_INT_P (y)
&& ((UINTVAL (y) & 0xffffffff) == 0
|| (UINTVAL (y) >> 32) == 0))
return CC_Zmode;

/* We can do an equality test in three Thumb instructions. */
Expand Down Expand Up @@ -15393,37 +15399,64 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
return CCmode;
}

/* X and Y are two things to compare using CODE. Emit the compare insn and
return the rtx for register 0 in the proper mode. FP means this is a
floating point compare: I don't think that it is needed on the arm. */
rtx
arm_gen_compare_reg (enum rtx_code code, rtx x, rtx y, rtx scratch)
/* X and Y are two (DImode) things to compare for the condition CODE. Emit
the sequence of instructions needed to generate a suitable condition
code register. Return the CC register result. */
static rtx
arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
{
machine_mode mode;
rtx cc_reg;
int dimode_comparison = GET_MODE (x) == DImode || GET_MODE (y) == DImode;
/* We don't currently handle DImode in thumb1, but rely on libgcc. */
gcc_assert (TARGET_32BIT);

/* We might have X as a constant, Y as a register because of the predicates
used for cmpdi. If so, force X to a register here. */
if (dimode_comparison && !REG_P (x))
if (!REG_P (x))
x = force_reg (DImode, x);

mode = SELECT_CC_MODE (code, x, y);
cc_reg = gen_rtx_REG (mode, CC_REGNUM);
machine_mode mode = SELECT_CC_MODE (code, x, y);
rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM);

if (dimode_comparison
&& mode != CC_CZmode)
if (mode != CC_CZmode)
{
rtx clobber, set;

/* To compare two non-zero values for equality, XOR them and
then compare against zero. Not used for ARM mode; there
CC_CZmode is cheaper. */
if (mode == CC_Zmode && y != const0_rtx)
if (mode == CC_Zmode)
{
gcc_assert (!reload_completed);
x = expand_binop (DImode, xor_optab, x, y, NULL_RTX, 0, OPTAB_WIDEN);
y = const0_rtx;
mode = CC_NOOVmode;
PUT_MODE (cc_reg, mode);
if (y != const0_rtx)
{
gcc_assert (CONST_INT_P (y));
rtx xlo, xhi, ylo, yhi;
arm_decompose_di_binop (x, y, &xlo, &xhi, &ylo, &yhi);
if (!scratch)
scratch = gen_reg_rtx (SImode);
if (ylo == const0_rtx)
{
yhi = GEN_INT (-INTVAL(yhi));
if (!arm_add_operand (yhi, SImode))
yhi = force_reg (SImode, yhi);
emit_insn (gen_addsi3 (scratch, xhi, yhi));
y = xlo;
}
else
{
gcc_assert (yhi == const0_rtx);
ylo = GEN_INT (-INTVAL(ylo));
if (!arm_add_operand (ylo, SImode))
ylo = force_reg (SImode, ylo);
emit_insn (gen_addsi3 (scratch, xlo, ylo));
y = xhi;
}
x = gen_rtx_IOR (SImode, scratch, y);
y = const0_rtx;
}
else
x = gen_rtx_IOR (SImode, gen_lowpart (SImode, x),
gen_highpart (SImode, x));
}

/* A scratch register is required. */
Expand All @@ -15442,6 +15475,22 @@ arm_gen_compare_reg (enum rtx_code code, rtx x, rtx y, rtx scratch)
return cc_reg;
}

/* X and Y are two things to compare using CODE. Emit the compare insn and
return the rtx for register 0 in the proper mode. */
rtx
arm_gen_compare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
{
if (GET_MODE (x) == DImode || GET_MODE (y) == DImode)
return arm_gen_dicompare_reg (code, x, y, scratch);

machine_mode mode = SELECT_CC_MODE (code, x, y);
rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM);

emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y));

return cc_reg;
}

/* Generate a sequence of insns that will generate the correct return
address mask depending on the physical architecture that the program
is running on. */
Expand Down
11 changes: 0 additions & 11 deletions gcc/config/arm/arm.md
Original file line number Diff line number Diff line change
Expand Up @@ -6518,17 +6518,6 @@
(set_attr "type" "multiple")]
)

(define_insn "*arm_cmpdi_zero"
[(set (reg:CC_Z CC_REGNUM)
(compare:CC_Z (match_operand:DI 0 "s_register_operand" "r")
(const_int 0)))
(clobber (match_scratch:SI 1 "=r"))]
"TARGET_32BIT"
"orrs%?\\t%1, %Q0, %R0"
[(set_attr "conds" "set")
(set_attr "type" "logics_reg")]
)

; This insn allows redundant compares to be removed by cse, nothing should
; ever appear in the output file since (set (reg x) (reg x)) is a no-op that
; is deleted later on. The match_dup will match the mode here, so that
Expand Down

0 comments on commit bb9a709

Please sign in to comment.