Hi Uros,
Thanks for the review.  Here's version 2 of this patch implementing your 
suggestions
to use any_QIreg_operand and QIreg_operand predicates.  This revised patch has
been tested on x86_64-pc-linux-gnu with make bootstrap and make -k check, both
with and without --target_board=unix{-m32} with no new failures.


2026-05-13  Roger Sayle  <[email protected]>
            Uros Bizjak  <[email protected]>

gcc/ChangeLog
        PR target/32803
        * config/i386/i386.md (peephole2): Don't transform xorl;movb into
        movzb with -Oz.
        (peephole2): Convert movl into xorl;movb (strict_low_part) with -Oz.
        (peephole2): Likewise, convert movl into xorl;movb [abcd]h with -Oz.

gcc/testsuite/ChangeLog
        PR target/32803
        * gcc.target/i386/pr32803-2.c: New test case.
        * gcc.target/i386/pr32803-3.c: Likewise.


Thanks again,
Roger

> -----Original Message-----
> From: Uros Bizjak <[email protected]>
> Sent: 03 May 2026 19:49
> To: Roger Sayle <[email protected]>
> Cc: GCC Patches <[email protected]>
> Subject: Re: [x86 PATCH] Shorter load immediate constants with -Oz
> 
> On Sat, May 2, 2026 at 10:17 PM Roger Sayle <[email protected]>
> wrote:
> >
> >
> > This patch adds two peephole2 patterns to i386.md to decrease the size
> > of some inteer loads.  These replace "movl $const, %eax" (5 bytes)
> > with "xorl %eax, %eax" followed by either "movb $const,%al" or "movb
> > $const,%ah" (together 4 bytes), for suitable constants and suitable
> > genral registers, when the flags register is dead.
> >
> > Ideally modern Intel and AMD prcoessors can recognize these sequences
> > during instruction decode (avoiding any partial register stall in the
> > same way they avoid the false dependence for the xorl), and internally
> > generate a single uop, treating these bytes like an alternate
> > instruction encoding.
> >
> >
> > This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
> > and make -k check, both with and without --target_board=unix{-m32}
> > with no new failures.  Ok for mainline?
> >
> >
> > 2026-05-02  Roger Sayle  <[email protected]>
> >
> > gcc/ChangeLog
> >         PR target/32803
> >         * config/i386/i386.md (peephole2): Don't transform xorl;movb into
> >         movzb with -Oz.
> >         (peephole2): Convert movl into xorl;movb (strict_low_part) with -Oz.
> >         (peephole2): Likewise, convert movl into xorl;movb [abcd]h with -Oz.
> >
> > gcc/testsuite/ChangeLog
> >         PR target/32803
> >         * gcc.target/i386/pr32803-2.c: New test case.
> >         * gcc.target/i386/pr32803-3.c: Likewise.
> 
> +;; With -Oz, convert mov eax,200 (5 bytes) to xor eax,eax; mov al,200
> +;; (4 bytes) when the flags register is dead.
> +(define_peephole2
> +  [(set (match_operand:SWI48 0 "general_reg_operand")
> 
> Please use any_QIreg_operand predicate here to avoid generating invalid QI
> registers on 32-bit targets.
> 
> +    (match_operand:SWI48 1 "const_int_operand"))]
> +  "optimize_insn_for_size_p () && optimize_size > 1
> +   && IN_RANGE (INTVAL (operands[1]), 128, 255)
> +   && peep2_regno_dead_p (0, FLAGS_REG)"
> +  [(parallel [(set (match_dup 0) (const_int 0))
> +          (clobber (reg:CC FLAGS_REG))])
> +   (set (strict_low_part (match_dup 2)) (match_dup 3))] {
> +  operands[2] = gen_lowpart (QImode, operands[0]);
> +  operands[3] = gen_int_mode (INTVAL (operands[1]), QImode);
> +})
> +
> +;; With -Oz, convert mov eax,512 (5 bytes) to xor eax,eax; mov ah,2 ;;
> +(4 bytes) when the flags register is dead.
> +(define_peephole2
> +  [(set (match_operand:SWI48 0 "general_reg_operand")
> 
> Please use QIreg_operand predicate here instead of QI_REGNO_P predicate in
> insn constraint.
> 
> +    (match_operand:SWI48 1 "const_int_operand"))]
> + "optimize_insn_for_size_p () && optimize_size > 1
> +   && (INTVAL (operands[1]) & ~0xff00) == 0
> +   && QI_REGNO_P (REGNO (operands[0]))
> +   && peep2_regno_dead_p (0, FLAGS_REG)"
> +  [(parallel [(set (match_dup 0) (const_int 0))
> +          (clobber (reg:CC FLAGS_REG))])
> +   (set (zero_extract:SWI48 (match_dup 0) (const_int 8) (const_int 8))
> +        (match_dup 2))]
> +  "operands[2] = gen_int_mode (INTVAL (operands[1]) >> 8, QImode);")
> 
> Uros.
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 648b9b1b951..a2579c9dbf5 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -4948,7 +4948,8 @@
   "REGNO (operands[0]) == REGNO (operands[1])
    && (<SWI48:MODE>mode != SImode
        || !TARGET_ZERO_EXTEND_WITH_AND
-       || !optimize_function_for_speed_p (cfun))"
+       || !optimize_function_for_speed_p (cfun))
+   && !(optimize_insn_for_size_p () && optimize_size > 1)"
   [(set (match_dup 0) (zero_extend:SWI48 (match_dup 2)))])
 
 ;; Likewise, but preserving FLAGS_REG.
@@ -4961,6 +4962,36 @@
        || !TARGET_ZERO_EXTEND_WITH_AND
        || !optimize_function_for_speed_p (cfun))"
   [(set (match_dup 0) (zero_extend:SWI48 (match_dup 2)))])
+
+;; With -Oz, convert mov eax,200 (5 bytes) to xor eax,eax; mov al,200
+;; (4 bytes) when the flags register is dead.
+(define_peephole2
+  [(set (match_operand:SWI48 0 "any_QIreg_operand")
+       (match_operand:SWI48 1 "const_int_operand"))]
+  "optimize_insn_for_size_p () && optimize_size > 1
+   && IN_RANGE (INTVAL (operands[1]), 128, 255)
+   && peep2_regno_dead_p (0, FLAGS_REG)"
+  [(parallel [(set (match_dup 0) (const_int 0))
+             (clobber (reg:CC FLAGS_REG))])
+   (set (strict_low_part (match_dup 2)) (match_dup 3))]
+{
+  operands[2] = gen_lowpart (QImode, operands[0]);
+  operands[3] = gen_int_mode (INTVAL (operands[1]), QImode);
+})
+
+;; With -Oz, convert mov eax,512 (5 bytes) to xor eax,eax; mov ah,2
+;; (4 bytes) when the flags register is dead.
+(define_peephole2
+  [(set (match_operand:SWI48 0 "QIreg_operand")
+       (match_operand:SWI48 1 "const_int_operand"))]
+  "optimize_insn_for_size_p () && optimize_size > 1
+   && (INTVAL (operands[1]) & ~0xff00) == 0
+   && peep2_regno_dead_p (0, FLAGS_REG)"
+  [(parallel [(set (match_dup 0) (const_int 0))
+             (clobber (reg:CC FLAGS_REG))])
+   (set (zero_extract:SWI48 (match_dup 0) (const_int 8) (const_int 8))
+        (match_dup 2))]
+  "operands[2] = gen_int_mode (INTVAL (operands[1]) >> 8, QImode);")
 
 ;; Sign extension instructions
 
diff --git a/gcc/testsuite/gcc.target/i386/pr32803-2.c 
b/gcc/testsuite/gcc.target/i386/pr32803-2.c
new file mode 100644
index 00000000000..7fc8f5aa18e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32803-2.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-Oz" } */
+
+int foo()
+{
+  return 200;
+}
+
+long fool()
+{
+  return 200;
+}
+
+int bar()
+{
+  return 512;
+}
+
+long barl()
+{
+  return 512;
+}
+/* { dg-final { scan-assembler-times "xorl\[ \t\]*%eax, %eax" 4 } } */
+/* { dg-final { scan-assembler-times "movb\[ \t\]*\\\$-56, %al" 2 } } */
+/* { dg-final { scan-assembler-times "movb\[ \t\]*\\\$2, %ah" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32803-3.c 
b/gcc/testsuite/gcc.target/i386/pr32803-3.c
new file mode 100644
index 00000000000..052c7b3c7ed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32803-3.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-Os" } */
+
+int foo()
+{
+  return 200;
+}
+
+long fool()
+{
+  return 200;
+}
+
+int bar()
+{
+  return 512;
+}
+
+long barl()
+{
+  return 512;
+}
+/* { dg-final { scan-assembler-times "movl\[\\t \]*\\\$200, %eax" 2 } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*\\\$512, %eax" 2 } } */

Reply via email to