> On 4/21/2026 2:29 AM, Jin Ma wrote:
> > The restrict_cost_classes function builds a "narrow" list of register
> > classes for IRA cost computation.  When iterating over classes, it skips
> > a class if it is a subset of an already-added class.  However, it does
> > not handle the reverse case: when a newly encountered class is a
> > superset of an already-added class.
> >
> > Per GCC internals Section 19.8, subclasses should have lower class
> > numbers than their parent classes ("Order the classes so that if class x
> > is contained in class y then x has a lower class number than y").  This
> > means subclasses are enumerated first and may be added to the narrow
> > list before their parent class is encountered.  When the parent class
> > arrives, it is not a subset of the subclass, so it is also added,
> > resulting in both classes coexisting in the narrow list.
> >
> > This causes problems for targets with constrained register subsets.
> > For example, on RISC-V, RVC_FP_REGS (8 registers, f8-f15) appears
> > before FP_REGS (32 registers) in enum reg_class.  Both end up in the
> > narrow list, and since their register move costs are equal, the
> > allocator may choose the smaller RVC_FP_REGS as the cost class.  This
> > overestimates register pressure (8 vs 32 available registers), leading
> > to unnecessary spills and suboptimal instruction scheduling.  This was
> > observed as a 6.3% performance regression in lmbench's double bogo
> > benchmark on the C907 rv32 platform.
> >
> > Similarly, on AArch64, FP_LO8_REGS (8 registers) appears before
> > FP_LO_REGS (16 registers) and FP_REGS (32 registers), and could
> > exhibit the same issue.
> >
> > Fix this by removing existing subset classes from the narrow list when
> > a superset class is added.  This ensures the narrow list result is
> > independent of the enumeration order of reg_class, and the allocator
> > always uses the largest applicable class for pressure estimation.
> >
> > gcc/ChangeLog:
> >
> >     * ira-costs.cc (restrict_cost_classes): When adding a new class
> >     to the narrow list, remove any existing classes that are subsets
> >     of the new class.  Update the map array to reflect the removal.
> >
> > gcc/testsuite/ChangeLog:
> >
> >     * gcc.target/riscv/ira-cost-class-subset.c: New test.
> >     * gcc.target/aarch64/ira-cost-class-subset.c: New test.
>
> Has this been bootstrapped and regression tested?  I threw it into my
> tester and I see a large number of failures across a variety of
> platforms.  Now it's possible some other change in the last 12 hours is
> the root cause, the most likely scenario based on what I'm seeing is
> it's this patch.
>
> I would start by verifying this bootstraps and regression tests on x86
> which it seems to have introduced ~1400 regressions, many with the
> strub tests, but also various assembler scanning tests. When x86 is
> clean I would suggest regression testing riscv64-elf where I see:
>
> > Tests that now fail, but worked before (610 tests):
> >
> > unix/-march=rv64gc_zba_zbb_zbs_zicond: gcc: 
> > gcc.dg/hoist-register-pressure-1.c scan-rtl-dump hoist "PRE/HOIST: end of 
> > bb .* copying expression"
> > unix/-march=rv64gc_zba_zbb_zbs_zicond: gcc: 
> > gcc.dg/hoist-register-pressure-2.c scan-rtl-dump hoist "PRE/HOIST: end of 
> > bb .* copying expression"
> > unix/-march=rv64gc_zba_zbb_zbs_zicond: gcc: 
> > gcc.dg/hoist-register-pressure-3.c scan-rtl-dump hoist "PRE/HOIST: end of 
> > bb .* copying expression"
> > unix/-march=rv64gc_zba_zbb_zbs_zicond: gcc: gcc.target/riscv/fle-ieee.c   
> > -O0   scan-assembler 
> > \tfrflags\t([^\n]*)\n\tfle\\.d\t[^\n]*\n\tfsflags\t\\1\n
> > unix/-march=rv64gc_zba_zbb_zbs_zicond: gcc: gcc.target/riscv/fle-snan.c   
> > -O0   scan-assembler 
> > \tfrflags\t([^\n]*)\n\tfle\\.d\t[^,]*,([^,]*),([^,]*)\n\tfsflags\t\\1\n\tfeq\\.d\tzero,\\2,\\3\n
> > unix/-march=rv64gc_zba_zbb_zbs_zicond: gcc: gcc.target/riscv/flef-ieee.c   
> > -O0   scan-assembler 
> > \tfrflags\t([^\n]*)\n\tfle\\.s\t[^\n]*\n\tfsflags\t\\1\n
> > unix/-march=rv64gc_zba_zbb_zbs_zicond: gcc: gcc.target/riscv/flef-snan.c   
> > -O0   scan-assembler 
> > \tfrflags\t([^\n]*)\n\tfle\\.s\t[^,]*,([^,]*),([^,]*)\n\tfsflags\t\\1\n\tfeq\\.s\tzero,\\2,\\3\n
> > unix/-march=rv64gc_zba_zbb_zbs_zicond: gcc: gcc.target/riscv/flt-ieee.c   
> > -O0   scan-assembler 
> > \tfrflags\t([^\n]*)\n\tflt\\.d\t[^\n]*\n\tfsflags\t\\1\n
> > unix/-march=rv64gc_zba_zbb_zbs_zicond: gcc: gcc.target/riscv/flt-snan.c   
> > -O0   scan-assembler 
> > \tfrflags\t([^\n]*)\n\tflt\\.d\t[^,]*,([^,]*),([^,]*)\n\tfsflags\t\\1\n\tfeq\\.d\tzero,\\2,\\3\n
>
> [ ... ] And so-on.  Those hoist-register-pressure-?.c tests are failing
> on other targets as well.
>
> Once both of those are working as we want, let me know and I'll repeat
> my broader target testing.

Hi Jeff,

Thank you for the testing and the detailed report. I sincerely apologize
for the inconvenience caused — I only tested on RISC-V before submitting
and did not catch regressions on other architectures. That was an
oversight on my part.

I should clarify that this patch was dropped by me intentionally.
You can see it here:
https://patchwork.sourceware.org/project/gcc/patch/[email protected]/

The reason is that I discovered a deeper issue that requires more careful
analysis. Changes touching register allocation need to be handled with
great caution, and I need more time to investigate the root cause
properly.  ):

BR,
Jin

Reply via email to