https://gcc.gnu.org/g:850b5135e5ea7e64c7694a4a12c894a052696c1b
commit r17-5451-g850b5135e5ea7e64c7694a4a12c894a052696c1b Author: Roger Sayle <[email protected]> Date: Wed Oct 21 13:17:43 2026 +0100 combine: Missed any-or-plus optimization in make_field_assignment. This simple patch tweaks combine.cc to catch a missed-optimization identified by my recent any-or-plus changes. Currently the logic in make_field_assignment checks for IOR or XOR, this one-line tweak adds PLUS to the list. The motivating example is taken from PR target/15184. On i386, for the code: unsigned int x; void foo(unsigned char c) { x = (x & 0xFFFFFF00) + (unsigned int)c; } current GCC (and earlier GCCs) with -m32 -O2 generates: foo: movl x, %eax movzbl 4(%esp), %edx xorb %al, %al addl %edx, %eax movl %eax, x ret with this patch, we now generate: foo: movzbl 4(%esp), %eax movb %al, x ret which matches what we'd generate if the source code had used "|" or "^" as an operator. 2026-10-21 Roger Sayle <[email protected]> gcc/ChangeLog * combine.cc (make_field_assignment): Treat PLUS like IOR and XOR. gcc/testsuite/ChangeLog * gcc.target/i386/pr15184-3.c: New test case. Diff: --- gcc/combine.cc | 4 +++- gcc/testsuite/gcc.target/i386/pr15184-3.c | 34 +++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/gcc/combine.cc b/gcc/combine.cc index 6cdbdbb6dc62..c9daefc905c3 100644 --- a/gcc/combine.cc +++ b/gcc/combine.cc @@ -9825,7 +9825,9 @@ make_field_assignment (rtx x) && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (src)), &src_mode)) src = SUBREG_REG (src); - if (GET_CODE (src) != IOR && GET_CODE (src) != XOR) + if (GET_CODE (src) != IOR + && GET_CODE (src) != XOR + && GET_CODE (src) != PLUS) return x; rhs = expand_compound_operation (XEXP (src, 0)); diff --git a/gcc/testsuite/gcc.target/i386/pr15184-3.c b/gcc/testsuite/gcc.target/i386/pr15184-3.c new file mode 100644 index 000000000000..dc92275b790e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr15184-3.c @@ -0,0 +1,34 @@ +/* PR 15184 addition forms of pr15184-1.c. */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -march=pentiumpro" } */ +/* { dg-additional-options "-fno-PIE" { target ia32 } } */ +/* { dg-additional-options "-mdynamic-no-pic" { target { *-*-darwin* && ia32 } } } */ + +#define regparm __attribute__((__regparm__(1))) + +extern unsigned int x; + +void regparm f0(unsigned char c) +{ + x = (x & 0xFFFFFF00) + (unsigned int)c; +} + +void regparm f1(unsigned char c) +{ + x = (x & 0xFFFF00FF) + ((unsigned int)c << 8); +} + +void regparm f2(unsigned char c) +{ + x = (x & 0xFF00FFFF) + ((unsigned int)c << 16); +} +void regparm f3(unsigned char c) +{ + x = (x & 0x00FFFFFF) + ((unsigned int)c << 24); +} + + +/* Each function should compile down to a byte move from + the input register into x, possibly at an offset within x. */ +/* { dg-final { scan-assembler-times "movb\[ \\t\]+%al" 4 } } */ +
