A halfword test takes two instructions, "uxth rD, rS; cmp rD, #0": 0xffff is not an Arm immediate, so TST cannot do it. "lsls rD, rS, #16" sets Z from bits 15:0 in one, at the cost of a scratch. combine already narrows the test to a comparison of (subreg:HI ...) against zero; it wanted only a mode and an insn to match it. Found in the halfword state tests of an emulator, next to the byte tests of the patch below.
Only EQ and NE may use the result -- after the shift N is bit 15 of the operand and C is bit 16 -- hence CC_Zmode, in which get_arm_condition_code maps every other code to ARM_NV. Arm state only: in Thumb-2 uxth and cmp are two bytes each, so there is nothing to win, and with the pattern enabled there the corpus below is byte-identical. This completes the byte case in <[email protected]>, whose COMPARE cost is what lets combine accept the narrowed form; that patch's halfword negative moves to the new test. Over gcc.target/arm and gcc.c-torture/compile at -O2 -marm -march=armv7-a two objects shrink by 84 bytes and none grows; -mthumb -march=armv7-a is unchanged. Regression tested on arm-none-eabi (arm.exp, tree-ssa.exp, dg.exp; default, cortex-a15 and cortex-m0 boards). Assisted-by: Claude Opus 5 (Anthropic) gcc/ChangeLog: * config/arm/arm.cc (arm_select_cc_mode): Select CC_Zmode for an HImode equality comparison against zero, as for QImode. * config/arm/arm.md (*comparehi_eq0): New pattern. gcc/testsuite/ChangeLog: * gcc.target/arm/tst-byte-eq0-2.c: Move the halfword case out. * gcc.target/arm/tst-half-eq0.c: New test. * gcc.target/arm/tst-half-eq0-2.c: New test. Signed-off-by: Dominic P <[email protected]> --- gcc/config/arm/arm.cc | 3 +- gcc/config/arm/arm.md | 12 +++ gcc/testsuite/gcc.target/arm/tst-byte-eq0-2.c | 6 +- gcc/testsuite/gcc.target/arm/tst-half-eq0-2.c | 16 ++++ gcc/testsuite/gcc.target/arm/tst-half-eq0.c | 85 +++++++++++++++++++ 5 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/gcc.target/arm/tst-half-eq0-2.c create mode 100644 gcc/testsuite/gcc.target/arm/tst-half-eq0.c diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc index aa44374d58c..97e94f4dc01 100644 --- a/gcc/config/arm/arm.cc +++ b/gcc/config/arm/arm.cc @@ -16520,7 +16520,8 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y) && CONST_INT_P (y)) return CC_RSBmode; - if (GET_MODE (x) == QImode && (op == EQ || op == NE)) + if ((GET_MODE (x) == QImode || GET_MODE (x) == HImode) + && (op == EQ || op == NE)) return CC_Zmode; if (GET_MODE (x) == SImode && (op == LTU || op == GEU) diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index 4cea71b40dd..acf2b1fb7c8 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -5816,6 +5816,18 @@ (set_attr "type" "logic_imm")] ) +(define_insn "*comparehi_eq0" + [(set (reg:CC_Z CC_REGNUM) + (compare:CC_Z (match_operand:HI 0 "s_register_operand" "r") + (const_int 0))) + (clobber (match_scratch:SI 1 "=r"))] + "TARGET_ARM" + "lsl%.\\t%1, %0, #16" + [(set_attr "conds" "set") + (set_attr "predicable" "yes") + (set_attr "type" "alus_shift_imm")] +) + (define_expand "extendhisi2" [(set (match_operand:SI 0 "s_register_operand") (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand")))] diff --git a/gcc/testsuite/gcc.target/arm/tst-byte-eq0-2.c b/gcc/testsuite/gcc.target/arm/tst-byte-eq0-2.c index 1a793c63899..ea096acc96b 100644 --- a/gcc/testsuite/gcc.target/arm/tst-byte-eq0-2.c +++ b/gcc/testsuite/gcc.target/arm/tst-byte-eq0-2.c @@ -3,14 +3,12 @@ /* { dg-options "-O2" } */ /* { dg-add-options arm_arch_v6 } */ -/* Only an equality may use the flags of TST, and only a byte fits its - immediate, so these keep the extension and the comparison. */ +/* Only an equality may use the flags of TST, so these keep the extension + and the comparison. */ unsigned n1 (unsigned x, unsigned p) { unsigned char v = x; return v < 5 ? p : p | 2; } unsigned n2 (unsigned x, unsigned p) { unsigned char v = x; return v > 5 ? p : p | 2; } -unsigned n3 (unsigned x, unsigned p) { unsigned short v = x; return v == 0 ? p | 2 : p; } /* { dg-final { scan-assembler-times "uxtb" 2 } } */ -/* { dg-final { scan-assembler-times "uxth" 1 } } */ /* { dg-final { scan-assembler-not "tst\[ \t\]" } } */ diff --git a/gcc/testsuite/gcc.target/arm/tst-half-eq0-2.c b/gcc/testsuite/gcc.target/arm/tst-half-eq0-2.c new file mode 100644 index 00000000000..8fa7ccdac61 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/tst-half-eq0-2.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_arm_ok } */ +/* { dg-options "-O2 -marm" } */ + +/* Only an equality may use the flags of the shifted test: after LSLS the N + flag is bit 15 of the operand and the C flag is bit 16. n4 is not a + halfword, and n5 is the byte case, which keeps TST. */ + +unsigned n1 (unsigned x, unsigned p) { unsigned short v = x; return v < 5 ? p : p | 2; } +unsigned n2 (unsigned x, unsigned p) { unsigned short v = x; return v > 5 ? p : p | 2; } +unsigned n3 (unsigned x, unsigned p) { short v = x; return v < 0 ? p : p | 2; } +unsigned n4 (unsigned x, unsigned p) { return (x & 0x3ff) ? p : p | 2; } +unsigned n5 (unsigned x, unsigned p) { unsigned char v = x; return v == 0 ? p : p | 2; } + +/* { dg-final { scan-assembler-times "tst\[ \t\]" 2 } } */ +/* { dg-final { scan-assembler-not "lsls\[ \t\]" } } */ diff --git a/gcc/testsuite/gcc.target/arm/tst-half-eq0.c b/gcc/testsuite/gcc.target/arm/tst-half-eq0.c new file mode 100644 index 00000000000..58ebd739e2a --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/tst-half-eq0.c @@ -0,0 +1,85 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_arm_ok } */ +/* { dg-options "-O2 -marm" } */ +/* { dg-final { check-function-bodies "**" "" "" } } */ + +/* +** f1: +** lsls r0, r0, #16 +** ... +** bx lr +*/ +unsigned +f1 (unsigned x, unsigned p) +{ + return (unsigned short) x ? p : p | 2; +} + +/* +** f2: +** lsls r0, r0, #16 +** ... +** bx lr +*/ +unsigned +f2 (unsigned x, unsigned p) +{ + unsigned short v = x; + return v == 0 ? p | 2 : p; +} + +/* +** f3: +** lsls r0, r0, #16 +** ... +** bx lr +*/ +unsigned +f3 (unsigned x, unsigned p) +{ + return (x & 0xffff) ? p : p | 2; +} + +/* +** f4: +** lsls r0, r0, #16 +** ... +** bx lr +*/ +unsigned +f4 (unsigned x, unsigned p) +{ + short v = x; + return v == 0 ? p | 2 : p; +} + +/* +** f5: +** lsls r0, r0, #16 +** movne r0, #7 +** moveq r0, #9 +** bx lr +*/ +int +f5 (unsigned x) +{ + if ((unsigned short) x) + return 7; + return 9; +} + +/* +** f6: +** tst r0, #255 +** ... +** bx lr +*/ +unsigned +f6 (unsigned x, unsigned p) +{ + unsigned char v = x; + return v == 0 ? p | 2 : p; +} + +/* { dg-final { scan-assembler-not "uxth" } } */ +/* { dg-final { scan-assembler-not "sxth" } } */ -- 2.55.0
