[X86] High byte registers (AH/BH/CH/DH) incur severe penalties on recent Intel P-cores; neither codegen tuning nor scheduler models account for it
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Summary
Since Ice Lake, Intel P-cores execute any ALU operation touching a high byte register (AH/BH/CH/DH) with multi-cycle latency on a restricted execution resource, and the penalty has deepened every generation since.
LLVM currently models these operations identically to their low-byte forms (1 cycle, full ALU port group) on every target, and codegen/regalloc freely produce H-register forms. This is now wrong by up to 4x in latency and ~5x in throughput on current Intel P-cores, while remaining perfectly fine on AMD, Intel E-cores, and pre-Ice Lake P-cores - so the fix needs to be per-target, not global.
## Measured hardware behavior
Data: uops.info (instructions.xml, 2026-03 snapshot), e.g. `ADD AH, imm8` vs `ADD AL, imm8`; Intel ORM 248966-050 §3.5.2.3.
| Microarchitecture | `ADD AH, i8` behavior | Source |
|---|---|---|
| Sandy Bridge - Skylake | 1c, full ALU port group (identical to AL form) | uops.info |
| Ice Lake / Tiger Lake / Rocket Lake | **3c write / 2c read**, TP 0.75, no single-port restriction observed | uops.info |
| Golden Cove / Raptor Cove (ADL/RPL/SPR/EMR) | **1*p1 only, 3c**, TP ~1.0 | uops.info + ORM §3.5.2.3 |
| Redwood Cove (MTL) | 1*p1, 3c | uops.info |
| **Lion Cove (ARL-P)** | **4c, single unit uops.info labels "SLOW"**, TP ~1.0; ops merely *reading* an H register as source (e.g. `ADD R8l, R8h`) are also routed to SLOW | uops.info (2026-03 ARL-P run) |
| Gracemont / Crestmont / Skymont (all E-cores) | 1c, full ALU width - unaffected | uops.info |
| AMD Zen 1-5 | 1c, full ALU width - unaffected | uops.info; AMD SOG is silent on H registers |
Two notes on the Lion Cove data:
(1) the ARL-P measurement campaign uses functional unit classes (ALU/SLOW/SHIFT/...) rather than physical port numbers, since classic per-port dispatch attribution is not available on Lion Cove's PMU - so the model fix there should use an abstract single-capacity resource, not a physical port claim
(2) the trajectory 1c -> 3c (ICL) -> 3c + single port (GLC) -> 4c + dedicated slow unit (LNC) indicates Intel is progressively deprecating H-register performance, so the modeling error grows every generation if left as is.
Documentation status: ORM §3.5.2.3 documents the Golden Cove port 1 / 3c restriction. Its claim that SKL-ICL restrict H-register ops to "ports 1 and 5" is not reproduced by measurement (SKL is unrestricted; ICL shows a latency penalty but no 2-port restriction), which is one more reason to anchor the models on independent measurement.
## Current state in LLVM
All X86 scheduler models assign H-byte and low-byte forms the same sched class - the opcode (e.g. `ADD8ri`) is shared, and nothing downstream distinguishes the operand register class:
- `X86SchedIceLake.td:122` - `WriteALU = [ICXPort0156], 1c` (used by icelake-client/server, tigerlake, rocketlake). Hardware: 3c write for H forms.
- `X86SchedAlderlakeP.td:134` - `WriteALU = [ADLPPort00_01_05_06_11], 1c` (used by ADL and, via current wiring, ARL/PTL and other recent P-core targets). Hardware: 1*p1, 3c.
- `X86SchedSapphireRapids.td:134` - same 5-port 1c claim. Hardware: 1*p1, 3c.
- `X86SchedLunarlakeP.td:159` - `WriteALU = [LNLPPort01_03_05], 1c`. Hardware: single slow unit, 4c.
Consequences:
- **llvm-mca** mispredicts any loop with an H-register dependency chain by up to 4x on these targets. MCA's register tracking already distinguishes AH as a subregister unit, so tracking is fine - only costing is missing.
- ~**Codegen/regalloc** have no tuning knob to avoid H forms where they are expensive: i8 `div`/`rem` lowering reads the remainder from AH; byte-pack patterns can select `movb %r8b, %ah`-style forms; RA can allocate `GR8_ABCD_H` freely. (GCC likewise emits `movzbl %ch`-style idioms; neither compiler currently models the cost.)~
- #45439 proposes *expanding* H-register usage for byte packing. That remains profitable on AMD/E-cores/pre-ICL but is now a 3-4c pessimization on current Intel P-cores - it needs per-target gating, which requires this cost to be modeled first.
## Proposed work
The scheduling side has existing infrastructure: `MCSchedPredicate` over a `TIIPredicate` (same pattern as `CheckLockPrefix` in `X86SchedPredicates.td`) can drive a `SchedWriteVariant` that gives H-operand forms a different timing without splitting opcodes.
- [ ] Add an `IsHReg`-style scheduling predicate (checks whether any GPR8 operand is in `GR8_ABCD_H`) + `SchedWriteVariant` plumbing.
- [ ] Apply per-model timings: IceLake (3c, no port restriction), AlderlakeP + SapphireRapids (3c, single-port resource), LunarlakeP (4c, single slow-unit resource). Leave SNB-SKL, all Atom-line, and all AMD models untouched.
- [ ] llvm-mca regression tests pinned to the measured values above.
- [ ] Decide whether affected targets want a tuning flag (e.g. `TuningSlowHighByteRegs`) so ISel/RA avoid H forms when an equally short alternative exists (shift+or packing, movzx-based extraction); re-evaluate #45439 under that flag.
- [ ] (Stretch) validate with llvm-exegesis runs on GLC and LNC hardware; note the known partial-register measurement caveats in #49797.
## Related issues
- #45439 - proposes more H-register usage for byte packing (pre-dates this data; needs per-target gating)
- #18276, #45210, #158585, #160039, #159823 - H registers as an encoding/correctness problem class (this issue adds the missing performance axis)
- #34055, #23529, #62948 - adjacent partial-register performance issues (low-byte/16-bit false dependencies; distinct mechanism from this one)
- #60043 - precedent: missing "slow LEA" penalty class in Intel models
- #209145 - broader staleness of Intel scheduler models
cc. @phoebewang @RKSimon @adibiagio
Contributor guide
Research direction
Start by reading the X86SchedIceLake.td, X86SchedAlderlakeP.td, X86SchedSapphireRapids.td, and X86SchedLunarlakeP.td entries, then inspect the MCSchedPredicate/TIIPredicate pattern in X86SchedPredicates.td. Add the per-target high-byte scheduling variants and llvm-mca regression tests, with timings matching the measured hardware while leaving unaffected models unchanged.
Written by the indexing model from the issue text.
Assessment
- Domain
- compilers, performance, testing
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100