On Sat, Jun 20, 2026 at 12:13 PM H.J. Lu <[email protected]> wrote: > > Since LCP stall peepholes are added after register allocation, each > peephole may use a different scratch register. For input: > > extern void bar (void); > > void > foo (short *dst) > { > dst[0] = 3; > asm volatile ("" : : : "memory"); > dst[2] = 3; > bar (); > dst[1] = 3; > asm volatile ("" : : : "memory"); > dst[4] = 3; > } > > with LCP stall peepholes, GCC generates: > > movl $3, %eax > pushq %rbx > movq %rdi, %rbx > movw %ax, (%rdi) > movl $3, %edx > movw %dx, 4(%rdi) > call bar > movl $3, %ecx > movw %cx, 2(%rbx) > movl $3, %esi > movw %si, 8(%rbx) > popq %rbx > > using 4 different scratch registers vs without LCP stall peepholes: > > pushq %rbx > movq %rdi, %rbx > movw $3, (%rdi) > movw $3, 4(%rdi) > call bar > movw $3, 2(%rbx) > movw $3, 8(%rbx) > popq %rbx > > Add X86_CSE_LCP_STALL to x86_cse to run before the cprop_hardreg pass > if there are multiple LCP stall peepholes: > > 1. Collect all LCP stall peepholes along with their scratch register > definitions in the same basic block. Group peepholes together with the > same integer constant in their scratch registers if the first scratch > register definition doesn't become invalid beyond a barrier, which can > be a function call, a definition or a use, before other peepholes. > 2. Track scratch register barriers after scratch register definitions. > 3. Mark a scratch register definition redundant in the group if the first > scratch register doesn't become invalid beyond a barrier. > 4. Replace the redundant scratch register in LCP stall peepholes in the > group with the first scratch register and mark the scratch register > definition redundant for deletion. > 5. Delete redundant scratch register definitions at the end. > > so that the same scratch register can be reused if possible: > > movl $3, %eax > pushq %rbx > movq %rdi, %rbx > movw %ax, (%rdi) > movw %ax, 4(%rdi) > call bar > movl $3, %ecx > movw %cx, 2(%rbx) > movw %cx, 8(%rbx) > popq %rbx > > gcc/ > > PR target/125893 > * config/i386/i386-features.cc (x86_cse_kind): Add > X86_CSE_LCP_STALL. > (replace_vector_const): Skip if SRC == VECTOR_CONST. > (pass_x86_cse::gate): Also run if there are multiple LCP stall > peepholes and reload is completed. > (pass_x86_cse::clone): New. > (pass_x86_cse::candidate_lcp_stall_p): Likewise. > (pass_x86_cse::x86_cse): Replace the redundant scratch register > in LCP stall peepholes in the group with the first scratch > register and mark the scratch register definition redundant for > deletion. Delete redundant scratch register definitions at the > end. > * config/i386/i386-passes.def: Add pass_x86_cse before > pass_cprop_hardreg. > * config/i386/i386.h (machine_function): Add > lcp_stall_peephole_generated. > (ix86_lcp_stall_peephole_generated): New. > * config/i386/i386.md (TARGET_LCP_STALL peepholes): Update > ix86_lcp_stall_peephole_generated. > > gcc/testsuite/ > > PR target/125893 > * gcc.target/i386/pr125893-1.c: New test. > * gcc.target/i386/pr125893-2.c: Likewise. > * gcc.target/i386/pr125893-3.c: Likewise. > * gcc.target/i386/pr125893-4.c: Likewise. > * gcc.target/i386/pr125893-5.c: Likewise. > * gcc.target/i386/pr125893-6.c: Likewise. > > > -- > H.J.
I backported this patch to GCC 16: 1. When bootstrapping GCC 16 with only C and C++ enabled, this optimization triggers 54 times. No regressions. 2. When building glibc 2.44, this optimization triggers 33 times. No regressions. 3. When building Linux kernel 7.1.1, this optimization triggers 2099 times. Kernel boots correctly. -- H.J.
