Files
Sylpheed/tools/ppc-manual/vmx/vaddfp.md
sim f3c512f2ab docs(ppc-manual): check every xenia-rs claim against Canary's source
The hand-written parts of the manual still described how the retired
xenia-rs interpreter behaved: its snapshots, Rust casts and helpers. Each of
those 490 statements is now either restated as what Canary's emitters and
x64 backend actually do (at the pinned canary_experimental commit), or
dropped where it only made sense for xenia-rs.

Checking them turned up claims that were wrong, not just outdated:

- VSCR[SAT] is never modelled in Canary (DID_SATURATE is a stub and mfvscr
  cannot see it); the pages said saturating ops set it stickily.
- Canary does not implement lswi/lswx/stswi/stswx, dcbi, mtfsb0/mtfsb1,
  vmsum*, vmhaddshs, vupkhpx/vupklpx, and most SPRs; pages described them
  as working.
- Traps evaluate TO in Canary; stvebx/stvehx/stvewx store one element, not
  16 bytes; mtmsrd writes only EE; fres/frsqrte/vrsqrtefp precision claims
  and the stfs "rounds under RN / sets FPSCR" claim contradicted the spec.
- Reservations are a 64 KiB block bitmap plus a value compare, not
  per-address tracking.

Claims that neither Canary's source nor a public spec settles are marked
unverified (NI at boot, vmaddcfp128 operand order, estimate bit-exactness).

Generated regions are untouched; re-running the generator changes nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 21:52:38 +02:00

182 lines
8.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `vaddfp` — Vector Add Floating Point
> **Category:** [VMX (Altivec)](../categories/vmx.md) · **Form:** [VX](../forms/VX.md) · **Opcode:** `0x1000000a`
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `vaddfp` | `vaddfp` | — | Vector Add Floating Point |
| `vaddfp128` | `vaddfp128` | — | Vector128 Add Floating Point |
## Syntax
```asm
vaddfp [VD], [VA], [VB]
vaddfp128 [VD], [VA], [VB]
```
## Encoding
### `vaddfp` — form `VX`
- **Opcode word:** `0x1000000a`
- **Primary opcode (bits 05):** `4`
- **Extended opcode:** `10`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (4) |
| 610 | `VRT/VD` | destination vector register |
| 1115 | `VRA/VA` | source A vector register |
| 1620 | `VRB/VB` | source B vector register |
| 2131 | `XO` | extended opcode (11 bits) |
### `vaddfp128` — form `VX128`
- **Opcode word:** `0x14000010`
- **Primary opcode (bits 05):** `5`
- **Extended opcode:** `16`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (4 or 5) |
| 610 | `VD128l` | destination low 5 bits |
| 1115 | `VA128l` | source A low 5 bits |
| 1620 | `VB128l` | source B low 5 bits |
| 21 | `VA128H` | source A high bit |
| 22 | `—` | reserved |
| 2325 | `VC` | optional VC / XO sub-field |
| 26 | `VA128h` | source A middle bit |
| 27 | `—` | reserved |
| 2829 | `VD128h` | destination high 2 bits |
| 3031 | `VB128h` | source B high 2 bits |
## Operands
| Field | Role | Description |
| --- | --- | --- |
| `VA` | vaddfp: read; vaddfp128: read | Source A vector register. |
| `VB` | vaddfp: read; vaddfp128: read | Source B vector register. |
| `VD` | vaddfp: write; vaddfp128: write | Destination vector register. |
## Register Effects
### `vaddfp`
- **Reads (always):** `VA`, `VB`
- **Reads (conditional):** _none_
- **Writes (always):** `VD`
- **Writes (conditional):** _none_
### `vaddfp128`
- **Reads (always):** `VA`, `VB`
- **Reads (conditional):** _none_
- **Writes (always):** `VD`
- **Writes (conditional):** _none_
## Status-Register Effects
_No condition-register or status-register effects._
## Operation (pseudocode)
```
for each 32-bit float lane i in 0..3:
VD[i] <- VA[i] + VB[i]
```
## C Translation Example
```c
/* vaddfp VD, VA, VB — lane-wise float add */
for (int i = 0; i < 4; ++i) v[insn.VD].f[i] = v[insn.VA].f[i] + v[insn.VB].f[i];
```
## Implementation References
**`vaddfp`**
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="vaddfp"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:341`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L341)
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:296`](../../../crates/sylpheed-ppc/src/opcode.rs#L296)
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:553`](../../../crates/sylpheed-ppc/src/decoder.rs#L553)
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
```cpp
int InstrEmit_vaddfp(PPCHIRBuilder& f, const InstrData& i) {
return InstrEmit_vaddfp_(f, i.VX.VD, i.VX.VA, i.VX.VB);
}
// ── delegates to (src/xenia/cpu/ppc/ppc_emit_altivec.cc:335) ──
int InstrEmit_vaddfp_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) {
// (VD) <- (VA) + (VB) (4 x fp)
Value* v = f.VectorAdd(f.LoadVR(va), f.LoadVR(vb), FLOAT32_TYPE);
f.StoreVR(vd, v);
return 0;
}
```
</details>
**`vaddfp128`**
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="vaddfp128"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:344`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L344)
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:297`](../../../crates/sylpheed-ppc/src/opcode.rs#L297)
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:725`](../../../crates/sylpheed-ppc/src/decoder.rs#L725)
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
```cpp
int InstrEmit_vaddfp128(PPCHIRBuilder& f, const InstrData& i) {
return InstrEmit_vaddfp_(f, VX128_VD128, VX128_VA128, VX128_VB128);
}
// ── delegates to (src/xenia/cpu/ppc/ppc_emit_altivec.cc:335) ──
int InstrEmit_vaddfp_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) {
// (VD) <- (VA) + (VB) (4 x fp)
Value* v = f.VectorAdd(f.LoadVR(va), f.LoadVR(vb), FLOAT32_TYPE);
f.StoreVR(vd, v);
return 0;
}
```
</details>
<!-- GENERATED: END -->
## Extended Pseudocode
```
; Four independent lane-wise IEEE-754 single-precision adds
for i in 0..3:
VD[i] <- VA[i] + VB[i] ; binary32, rounded to nearest
; No FPSCR update (VMX uses VSCR, which only has NJ / SAT — and vaddfp doesn't saturate)
```
## Special Cases & Edge Conditions
- **Lane indexing is big-endian.** Lane 0 is the **most significant** 4 bytes of the 128-bit register (the one that appears at the lowest byte offset after a `stvx`). Canary keeps lanes in PPC order: `lvx`/`stvx` byte-swap each 32-bit lane in place (`vpshufb` with `XMMByteSwapMask`), so host element `i` is PPC lane `i`. When writing C that manipulates individual lanes, index `v.f[0]` as "the byte 0..3" of the big-endian layout.
- **Flush-denormals ("NJ") mode.** Altivec is independent of FPSCR — it has its own 2-bit VSCR (`NJ` for non-Java mode + `SAT` sticky-saturation). VMX float operations honour `VSCR[NJ]`: when set, denormal inputs and outputs are flushed to zero. Canary starts every thread with `NJ` set (`vscr_vec` low word `0x00010000`) and its VMX MXCSR in flush-to-zero + denormals-are-zero mode, while its scalar FPU starts in IEEE mode; `mtvscr` switches the VMX side off when the guest clears `NJ`. Whether Xenon boots the same way is unverified.
- **No exception, no trap.** Altivec floats never raise exceptions. NaN inputs produce NaN outputs; `±∞ ±∞` yields a NaN; there is no VXISI-style status bit. `VSCR[SAT]` is **not** touched by `vaddfp` (it saturates integer ops, not floats).
- **Four independent lanes.** Each lane's operation is unaffected by the others. Aliasing between `VA`, `VB`, and `VD` is legal and common (`vaddfp v3, v3, v4`).
- **VMX128 sibling (`vaddfp128`).** Semantics identical; only the register encoding differs. VMX128 uses a 7-bit operand ID per source (and destination) built from two or three non-contiguous bit fields — see [`categories/vmx128.md`](../categories/vmx128.md). Any bit pattern encodable as a 32-register VX-form is also encodable as a VMX128 form, so compilers picked the more compact form that reached the needed register range.
- **On x86-64 hosts.** Canary emits `vaddps`. Because its `lvx`/`stvx` swap each 32-bit lane in place rather than reversing all 16 bytes, PPC lane `i` is host lane `i`, and `vaddps` gives the right per-lane result.
## Related Instructions
- [`vsubfp`](vsubfp.md) — lane-wise float subtract.
- [`vmaddfp`](vmaddfp.md) — lane-wise `(VA × VC) + VB` (fused multiply-add with single rounding).
- [`vnmsubfp`](vnmsubfp.md) — `((VA × VC) VB)`.
- [`vmaxfp`](vmaxfp.md), [`vminfp`](vminfp.md) — IEEE-754-aware max/min (NaN propagation).
- [`vcmpeqfp`](vcmpeqfp.md), [`vcmpgtfp`](vcmpgtfp.md), [`vcmpgefp`](vcmpgefp.md), [`vcmpbfp`](vcmpbfp.md) — compares producing per-lane all-ones / all-zero masks.
- [`vrfin`](vrfin.md), [`vrfim`](vrfim.md), [`vrfip`](vrfip.md), [`vrfiz`](vrfiz.md) — round to integer (to-nearest / down / up / toward-zero).
- [`vmulfp128`](../vmx128/vmulfp128.md) — the VMX128-only lane-wise multiply (Canary emits a plain `Mul`); standard Altivec has no `vmulfp`, and code there uses `vmaddfp v, va, vc, v0_zero` instead.
## IBM Reference
- [AIX 7.3 — `vaddfp` (Vector Add Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vaddfp-vector-add-floating-point-instruction)
- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 — Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf)