llvm / llvm/llvm-project

[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

Open
#210,321 3 comments 0 reactions 0 assignees View on GitHub
backend:X86 backend:X86 Scheduler Models
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.