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>
8.3 KiB
8.3 KiB
vaddfp — Vector Add Floating Point
Category: VMX (Altivec) · Form: VX · Opcode:
0x1000000a
Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
|---|---|---|---|
vaddfp |
vaddfp |
— | Vector Add Floating Point |
vaddfp128 |
vaddfp128 |
— | Vector128 Add Floating Point |
Syntax
vaddfp [VD], [VA], [VB]
vaddfp128 [VD], [VA], [VB]
Encoding
vaddfp — form VX
- Opcode word:
0x1000000a - Primary opcode (bits 0–5):
4 - Extended opcode:
10 - Synchronising: no
| Bits | Field | Meaning |
|---|---|---|
| 0–5 | OPCD |
primary opcode (4) |
| 6–10 | VRT/VD |
destination vector register |
| 11–15 | VRA/VA |
source A vector register |
| 16–20 | VRB/VB |
source B vector register |
| 21–31 | XO |
extended opcode (11 bits) |
vaddfp128 — form VX128
- Opcode word:
0x14000010 - Primary opcode (bits 0–5):
5 - Extended opcode:
16 - Synchronising: no
| Bits | Field | Meaning |
|---|---|---|
| 0–5 | OPCD |
primary opcode (4 or 5) |
| 6–10 | VD128l |
destination low 5 bits |
| 11–15 | VA128l |
source A low 5 bits |
| 16–20 | VB128l |
source B low 5 bits |
| 21 | VA128H |
source A high bit |
| 22 | — |
reserved |
| 23–25 | VC |
optional VC / XO sub-field |
| 26 | VA128h |
source A middle bit |
| 27 | — |
reserved |
| 28–29 | VD128h |
destination high 2 bits |
| 30–31 | 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
/* 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 formnem="vaddfp" - Canary emitter:
src/xenia/cpu/ppc/ppc_emit_altivec.cc:341 - Sylpheed opcode:
crates/sylpheed-ppc/src/opcode.rs:296 - Sylpheed decoder:
crates/sylpheed-ppc/src/decoder.rs:553
Canary emitter (frozen snapshot @ f21ebd49e9)
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;
}
vaddfp128
- Canary XML:
tools/ppc-instructions.xml— search formnem="vaddfp128" - Canary emitter:
src/xenia/cpu/ppc/ppc_emit_altivec.cc:344 - Sylpheed opcode:
crates/sylpheed-ppc/src/opcode.rs:297 - Sylpheed decoder:
crates/sylpheed-ppc/src/decoder.rs:725
Canary emitter (frozen snapshot @ f21ebd49e9)
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;
}
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/stvxbyte-swap each 32-bit lane in place (vpshufbwithXMMByteSwapMask), so host elementiis PPC lanei. When writing C that manipulates individual lanes, indexv.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 (
NJfor non-Java mode +SATsticky-saturation). VMX float operations honourVSCR[NJ]: when set, denormal inputs and outputs are flushed to zero. Canary starts every thread withNJset (vscr_veclow word0x00010000) and its VMX MXCSR in flush-to-zero + denormals-are-zero mode, while its scalar FPU starts in IEEE mode;mtvscrswitches the VMX side off when the guest clearsNJ. 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 byvaddfp(it saturates integer ops, not floats). - Four independent lanes. Each lane's operation is unaffected by the others. Aliasing between
VA,VB, andVDis 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 — seecategories/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 itslvx/stvxswap each 32-bit lane in place rather than reversing all 16 bytes, PPC laneiis host lanei, andvaddpsgives the right per-lane result.
Related Instructions
vsubfp— lane-wise float subtract.vmaddfp— lane-wise(VA × VC) + VB(fused multiply-add with single rounding).vnmsubfp—−((VA × VC) − VB).vmaxfp,vminfp— IEEE-754-aware max/min (NaN propagation).vcmpeqfp,vcmpgtfp,vcmpgefp,vcmpbfp— compares producing per-lane all-ones / all-zero masks.vrfin,vrfim,vrfip,vrfiz— round to integer (to-nearest / down / up / toward-zero).vmulfp128— the VMX128-only lane-wise multiply (Canary emits a plainMul); standard Altivec has novmulfp, and code there usesvmaddfp v, va, vc, v0_zeroinstead.