Skip to content

Apx add ndd - #13916

Draft
jlb6740 wants to merge 9 commits into
bytecodealliance:mainfrom
jlb6740:apx-add-ndd
Draft

Apx add ndd#13916
jlb6740 wants to merge 9 commits into
bytecodealliance:mainfrom
jlb6740:apx-add-ndd

Conversation

@jlb6740

@jlb6740 jlb6740 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Adds the APX NDD (new data destination) form of 64-bit add, as an initial
end-to-end slice through the stack: DSL entry, extended-EVEX (map 4) encoding,
an ISLE lowering rule gated on has_apx, and a filetest.

This is deliberately one instruction rather than a batch to make sure of the generic
mechanisms definining APX, not necessarily addq itself:

  • Operand order is derived from the EVEX ND bit, since with ND = 1 the
    destination lives in vvvv and prints last in AT&T order. Keying off the bit
    rather than the operand shape means future NDD instructions get this for free.
  • disp8 scaling is disabled for ApxClass::LegacyGpr. Map-4 instructions are
    promoted legacy instructions and keep plain byte displacements rather than
    EVEX compressed displacement.

Two things:

  • APX is test-only right now. Nothing enables has_apx automatically, because
    is_x86_feature_detected!("apxf") is still unstable (Tracking Issue for APX target feature rust-lang/rust#139284).
    Detection can follow once that lands.
  • The NDD rule sits below the load-sinking rules, so the memory-operand form
    isn't selected yet. It's covered by an assembler unit test instead.

Still a draft: it's stacked on #14136, so that commit currently shows up in the
diff here. I'll rebase and mark this ready once #14136 merges.

@github-actions github-actions Bot added cranelift Issues related to the Cranelift code generator cranelift:area:x64 Issues related to x64 codegen cranelift:meta Everything related to the meta-language. isle Related to the ISLE domain-specific language labels Jul 21, 2026
@github-actions

Copy link
Copy Markdown

Subscribe to Label Action

cc @cfallin, @fitzgen

Details This issue or pull request has been labeled: "cranelift", "cranelift:area:x64", "cranelift:meta", "isle"

Thus the following users have been cc'd because of the following labels:

  • cfallin: isle
  • fitzgen: isle

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

Capstone's `disasm_all` stops at the first instruction it can't decode and
returns success with whatever it managed to decode. We never compared that
against the block length, so that instruction and everything after it
vanished from the listing. For `precise-output` filetests the truncated
output gets blessed, and the test then passes without asserting anything
about those bytes.

This is reachable today: the bundled capstone can't decode AVX-VNNI, so a
function containing `vpdpbusd` was silently dropping five instructions.
It is also a precursor to the APX work, whose EVEX map 4 encodings hit the
same path; those filetests were being blessed as truncated output and so
verified nothing about the instructions they were added to cover.

Print the leftover bytes as `.byte`, keeping the reloc and trap
annotations. s390x expectations already look like this. Resyncing after
the bad instruction isn't possible on x86 without knowing its length, and
guessing yields plausible but wrong instructions.

The updated expectations only gain lines; nothing already printed changed.
Those blocks end in constant pool data that capstone was already rendering
as nonsense.

prtest:full
Add the runtime EVEX map-4 prefix builder (ND/NF/EGPR bits per APX
spec rev 8, Fig. 3.3), wire it into the meta code generator, and enable
the NDD `addq` instruction. Verified with a byte-exact encoding test
(62 F4 F4 18 01 C2).

Instruction selection (ISLE lowering) and EGPR register allocation are
not part of this change; the `.clif` filetest cannot select these
instructions yet.
@jlb6740
jlb6740 force-pushed the apx-add-ndd branch 2 times, most recently from 4df6421 to 74c42a8 Compare August 21, 2026 23:34
Add a `use_apx` ISLE predicate and lower 64-bit `iadd` to the NDD form of
`addq` (EVEX map 4) when the `has_apx` target flag is enabled. Gate the
instruction on the `apx` assembler feature and align the NDD destination
with the EVEX `vvvv` operand so the result is written to a fresh register
without clobbering either input. Add filetests exercising the new lowering
and the `has_apx` flag parsing.
The assembler's generated `Display` impl derives AT&T operand order by
reversing the DSL's Intel-style order. That works when the destination is
the ModRM.reg operand, but APX "new data destination" (NDD) forms encode
their destination in `vvvv`, which the DSL must list in the middle so that
the positional slot assignment in `generate_vex_or_evex_prefix` maps
operands to `reg`/`vvvv`/`rm` correctly. Reversing therefore left the
destination in the middle: `addq` printed as `addq %rsi, %rax, %rdi` where
both AT&T convention and XED expect the destination last.

Identify these forms from the `ND` bit rather than from the operand shape.
`[Reg, Reg, RegMem]` is also used by ordinary VEX instructions, and
`[Reg, RegMem, Reg]` is used by the BMI2 `bzhi`/`sarx`/`shlx`/`shrx` family
with the slots assigned the other way around, so the shape alone cannot
identify NDD. When `ND = 1`, sources keep their DSL order and the
destination is printed last. Any future NDD instruction then gets the
correct order without a per-instruction `Display` customization.

Only printing changes here; the emitted encodings are untouched.
The bundled capstone build cannot decode the APX extended-EVEX ("map 4")
encodings at all, returning zero instructions, which tripped the "not a
single instruction" assertion in the roundtrip oracle. Skip them the same
way AVX-VNNI instructions are already skipped.

APX stays in `ALL_FEATURES` so that the XED oracle, which does understand
map 4, keeps exercising these instructions rather than losing coverage.
EVEX normally stores an 8-bit displacement as a multiple of a tuple-derived
factor N, so a 128-bit `Full` tuple divides the byte offset by 16. APX
promotes legacy GPR instructions into extended-EVEX "map 4", and those keep
legacy displacement semantics: disp8 is a plain byte offset.

We were applying the vector scaling to them anyway, so `0x50(%rdi)` encoded
as disp8 = 0x50 / 16 = 0x05 and the CPU would read it as `0x5(%rdi)`. Found
by the XED fuzz oracle; capstone cannot decode map 4 at all, and the
memory-operand form is not reachable from lowering yet, so nothing else
would have caught it.

Key the exemption off `ApxClass::LegacyGpr` rather than the map number.
Promoted vector instructions and APX-extended AVX-512 instructions are still
vector encodings and do use compressed displacements.
Adding the `two_op`/`three_op` parameters to `generate_vex_or_evex_prefix`
pushed both call sites past the width limit, so rustfmt wants them broken
across lines. No functional change.
@github-actions github-actions Bot added the cranelift:area:machinst Issues related to instruction selection and the new MachInst backend. label Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cranelift:area:machinst Issues related to instruction selection and the new MachInst backend. cranelift:area:x64 Issues related to x64 codegen cranelift:meta Everything related to the meta-language. cranelift Issues related to the Cranelift code generator isle Related to the ISLE domain-specific language

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant