Files
Sylpheed/tools/ppc-manual/vmx/lvsr.md
sim 21161154d1
All checks were successful
CI / Native — linux (pull_request) Successful in 2h2m48s
CI / WASM — Web (pull_request) Successful in 29m51s
CI / Formatting (pull_request) Successful in 1m36s
fix(ppc-manual): 543 dead links, from two generator bugs and wrong relative paths
- Category pages linked each family as `<slug>.md`, relative to categories/,
  where no family page lives. They now link `../<category>/<slug>.md`.
- Form pages linked a member into its *own* category directory, so every
  VMX128 sibling (`vsldoi128`) pointed at vmx128/ although its family page is
  under vmx/. They now link into the family's directory.
- Hand-written "Related" and sibling mentions linked other categories' pages
  as if they were in the same directory. 109 are retargeted through the page
  index; 29 that pointed a family page at itself (`vrefp128` on vrefp.md) and
  6 naming instructions the manual has no page for are plain text now.

Regenerated at the existing Canary pin (f21ebd49e): upstream has moved on, and
re-pinning belongs in its own change. The generator reports 0 family pages
changed and is idempotent; the only dead links left are TEMPLATE.md's
placeholders.

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

188 lines
8.0 KiB
Markdown
Raw 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.
# `lvsr` — Load Vector for Shift Right Indexed
> **Category:** [VMX (Altivec)](../categories/vmx.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00004c`
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `lvsr` | `lvsr` | — | Load Vector for Shift Right Indexed |
| `lvsr128` | `lvsr128` | — | Load Vector for Shift Right Indexed 128 |
## Syntax
```asm
lvsr [VD], [RA0], [RB]
lvsr128 [VD], [RA0], [RB]
```
## Encoding
### `lvsr` — form `X`
- **Opcode word:** `0x7c00004c`
- **Primary opcode (bits 05):** `31`
- **Extended opcode:** `38`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `XO` | extended opcode (10 bits) |
| 31 | `Rc` | record-form flag |
### `lvsr128` — form `VX128_1`
- **Opcode word:** `0x10000043`
- **Primary opcode (bits 05):** `4`
- **Extended opcode:** `67`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (4) |
| 610 | `VD128l` | destination low 5 bits |
| 1115 | `RA` | address register |
| 1620 | `RB` | offset register |
| 2127 | `XO` | extended opcode |
| 2829 | `VD128h` | destination high 2 bits |
| 3031 | `—` | reserved |
## Operands
| Field | Role | Description |
| --- | --- | --- |
| `RA0` | lvsr: read; lvsr128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
| `RB` | lvsr: read; lvsr128: read | Source GPR. |
| `VD` | lvsr: write; lvsr128: write | Destination vector register. |
## Register Effects
### `lvsr`
- **Reads (always):** `RA0`, `RB`
- **Reads (conditional):** _none_
- **Writes (always):** `VD`
- **Writes (conditional):** _none_
### `lvsr128`
- **Reads (always):** `RA0`, `RB`
- **Reads (conditional):** _none_
- **Writes (always):** `VD`
- **Writes (conditional):** _none_
## Status-Register Effects
_No condition-register or status-register effects._
## Operation (pseudocode)
```
addr_lo <- ((RA|0) + (RB))[60:63]
for i in 0..15: VD[i] <- 16 addr_lo + i
```
## C Translation Example
```c
/* No hand-written C yet. Translate the Canary emitter snapshot */
/* under Implementation References; its HIR maps directly: */
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
/* wrap them in f.ByteSwap for the big-endian guest value */
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
/* The Register Effects and Status-Register Effects tables above */
/* enumerate every side effect a faithful translation must emit. */
```
## Implementation References
**`lvsr`**
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvsr"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:126`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L126)
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:150`](../../../crates/sylpheed-ppc/src/opcode.rs#L150)
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:877`](../../../crates/sylpheed-ppc/src/decoder.rs#L877)
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
```cpp
int InstrEmit_lvsr(PPCHIRBuilder& f, const InstrData& i) {
return InstrEmit_lvsr_(f, i, i.X.RT, i.X.RA, i.X.RB);
}
// ── delegates to (src/xenia/cpu/ppc/ppc_emit_altivec.cc:118) ──
int InstrEmit_lvsr_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
uint32_t ra, uint32_t rb) {
Value* ea = CalculateEA_0(f, ra, rb);
Value* sh = f.Truncate(f.And(ea, f.LoadConstantInt64(0xF)), INT8_TYPE);
Value* v = f.LoadVectorShr(sh);
f.StoreVR(vd, v);
return 0;
}
```
</details>
**`lvsr128`**
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvsr128"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:129`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L129)
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:151`](../../../crates/sylpheed-ppc/src/opcode.rs#L151)
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:528`](../../../crates/sylpheed-ppc/src/decoder.rs#L528)
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
```cpp
int InstrEmit_lvsr128(PPCHIRBuilder& f, const InstrData& i) {
return InstrEmit_lvsr_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB);
}
// ── delegates to (src/xenia/cpu/ppc/ppc_emit_altivec.cc:118) ──
int InstrEmit_lvsr_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
uint32_t ra, uint32_t rb) {
Value* ea = CalculateEA_0(f, ra, rb);
Value* sh = f.Truncate(f.And(ea, f.LoadConstantInt64(0xF)), INT8_TYPE);
Value* v = f.LoadVectorShr(sh);
f.StoreVR(vd, v);
return 0;
}
```
</details>
<!-- GENERATED: END -->
## Special Cases & Edge Conditions
- **No memory access.** Like [`lvsl`](lvsl.md), `lvsr` does not touch memory: the effective address is consumed solely to extract the low four bits, which then drive the synthesised permute mask in `VD`.
- **Mirror of `lvsl`.** Where `lvsl` produces `{sh, sh+1, …, sh+15}`, `lvsr` produces `{16sh, 17sh, …, 31sh}`. When `EA & 0xF == 0` the output is `{16, 17, …, 31}` — the identity permute that selects all of `VB` (in the `vperm VD, VA, VB, VC` orientation). When `EA & 0xF == 3` the output is `{13, 14, …, 28}`, splitting the `vperm` between the high three bytes of `VA` and the low thirteen of `VB`.
- **Big-endian byte indexing.** `VD[0]` is the most-significant byte (the byte at the lowest address after a `stvx`).
- **Right-shift unaligned-load idiom.** Pair with two aligned `lvx` and a `vperm` when the source data is laid out so the wanted vector starts in the *second* aligned block:
```
lvx vAL, r0, rA ; aligned block at EA & ~0xF
lvx vAH, r0, rA + 16 ; next aligned block
lvsr vC, r0, rA ; right-shift permute mask
vperm vD, vAH, vAL, vC ; note: vAH then vAL — opposite of lvsl
```
The argument flip versus the `lvsl` idiom is the whole reason both masks exist.
- **`RA0` semantics.** When `RA = 0` the base is the literal zero, so `lvsr vD, 0, rB` derives the mask from `rB & 0xF`.
- **Selectors >15 are intentional.** Inside `vperm`, byte selectors with bit 4 set (i.e. `>= 16`) index into the second source vector. `lvsr` deliberately produces values up to `31`, since only the low five bits are honoured by `vperm`.
- **VMX128 sibling (`lvsr128`).** Identical semantics; the extended `VD128l ‖ VD128h` encoding lets `vD` reach `v0..v127`.
- **No flags, no exceptions, trivially reorderable.**
## Related Instructions
- [`lvsl`](lvsl.md) — the mirror: `VD[i] = sh + i`.
- [`vperm`](vperm.md) — consumes the mask to perform arbitrary byte-level permutation across two vectors.
- [`lvx`](../memory/lvx.md), [`lvlx`](../memory/lvlx.md), [`lvrx`](../memory/lvrx.md) — the actual memory loads that supply the two aligned halves.
- [`vsldoi`](vsldoi.md) — when the misalignment is a compile-time constant, the static-offset shift is cheaper than the `lvsr`/`vperm` pair.
## IBM Reference
- [AIX 7.3 — `lvsr` (Load Vector for Shift Right Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvsr-load-vector-shift-right-indexed-instruction)
- [IBM AltiVec Technology Programmer's Interface Manual — unaligned-load idiom](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf)