You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Ghost of Yōtei (PPSA26344): unsupported MIMG opcode, which the decoder does not name #1941
Ghost of Yōtei (PPSA26344, 01.008) aborts after about 24 minutes with an image opcode the decoder has no entry for:
[libc] unhandled exception 'St13runtime_error' thrown from 0x7f96f49c20f4
terminate called after throwing an instance of 'std::runtime_error'
what(): unsupported MIMG opcode
RdnaImageOpDecoder.cpp:165 throws from its table lookup, and unlike the scalar decoder's message it does not name the opcode — unsupported scalar source operand code 235 at program counter 856 does. Naming it here would make this actionable from a user's log alone, without a reproducer.
Where it was measured
title: PPSA26344 (Ghost of Yōtei 01.008), decrypted eboot.bin relinked, staged runtime layout, RTX 3080 Ti, Linux
Encodings 0x00 to about 0x41 — loads (image_load*), stores (image_store*), the atomics and the image_sample* family with its compare/gradient/offset variants. Whatever this shader issues is outside that set.
The diagnostic step paid off — the title now names the opcode.
With the encoding included in RdnaImageOpDecoder.cpp:165 ("unsupported MIMG opcode " + std::to_string(opcode)), PPSA26344 dies after ~24 minutes and 897 shaders with:
[libc] unhandled exception 'St13runtime_error' thrown from 0x7f8abbfc2206
terminate called after throwing an instance of 'std::runtime_error'
what(): unsupported MIMG opcode 229
So the missing encoding is 0xE5, immediately below the two the table already carries:
0xE5 is not simply "the next intersect-ray". LLVM defines 0xE6 and 0xE7 there but has no definition for 0xE5, so its name, operand shape and return semantics need a source that actually describes it (ISA table or another implementation) rather than inference from its neighbour. I have not found that source yet, which is why this is a report and not a patch.
The message change is worth keeping on its own. Naming the encoding turned "an unknown MIMG opcode" into "0xE5" in a single 24-minute run, which is the difference between a report someone can act on and one they cannot.
For completeness, the table covers 135 encodings; the gaps where a shader could plausibly land are 0x06 0x07 0x0c 0x0d 0x13, 0x64-0x67 0x72 0x75 0x78 0x7b-0x7f, 0x81-0x9f and 0xe0-0xe5.
I checked the public AMD RDNA 2 ISA reference and AMDGPU LLVM opcode tables as well. They document IMAGE_BVH_INTERSECT_RAY and IMAGE_BVH64_INTERSECT_RAY as the adjacent 0xE6/0xE7 operations, but do not define 0xE5. That supports keeping this as an unknown instruction until a PS5-specific hardware/spec source identifies its semantics; inferring it from the BVH neighbors would be unsafe. AMD RDNA 2 ISA · LLVM AMDGPU MIMG opcode table.
Adding the ISA evidence for 0xE5, because it narrows the question usefully.
Source: LLVM's own MC tests for this GPU family — llvm/test/MC/AMDGPU/gfx1013.s is the file for gfx1013, which is what PS5's GPU ("Oberon") reports as. I extracted all 253 mnemonic↔encoded-word pairs from the MIMG test files and keyed them by encoding variant.
0xE0 image_get_lod
0xE1 image_gather4_b_cl
0xE2 image_gather4_c_cl
0xE3 image_gather4_c_l
0xE4 image_gather4_c_b
0xE5 — undefined in every variant (GFX8, GFX8_0, GFX8_1, GFX9, GFX90A, GFX10, GFX11)
0xE6 image_bvh_intersect_ray (present on gfx1013; refused on gfx1011/gfx1012)
0xE7 image_bvh64_intersect_ray (same)
So the ray-tracing family is 0xE6/0xE7 — which RdnaImageOpDecoder.cpp already carries — and 0xE5 is not assigned anywhere in public data, including on this exact GPU.
A hypothesis I checked and had to drop. The decoder builds the opcode as ((word0 >> 18) & 0x7F) | ((word0 & 1) << 7), i.e. opcode bit 7 comes from word bit 0, which looked wrong next to GFX11 rows where the same bit lives at word bit 8. It is not wrong for us: assembling gfx1013's image_msaa_load gives word 0xf0000839, and only the bit-0 reading yields 0x80 (the bit-8 reading yields 0x00). The bit placement genuinely differs between GFX10 and GFX11, and ours matches GFX10. Closed.
What that leaves, with the decoder already rejecting non-MIMG words ((word0 >> 26) != 0x3C) and reserved control bits:
either PS5's shader compiler emits a reserved encoding here, or
the word handed to the decoder is not an instruction at all (a walk off the end of the code section, or a literal read as code) — the major opcode check would pass by chance roughly one time in 64.
To tell those apart I instrumented the lookup to print the program counter, both words, the extracted opcode, the major opcode, and the surrounding words, then re-ran the title. The run reports which of the two it is; I will post the words here when it reaches the wall.
Adding a third source, which agrees on both points.
sharpemu/sharpemu has an independent GFX10 MIMG decoder (src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs) and it says the same two things this decoder does:
On the extraction: "GFX10 MIMG uses bit 0 as opcode bit 7. Treating the opcode as only bits 18..24 makes the 0x80+ family (including sample-adjust forms) …" — i.e. exactly the ((word >> 18) & 0x7F) | ((word & 1) << 7) this project uses, with a test of its own (ImageBvhIntersectRay_UsesOpcodeBitSevenFromOpm).
On the aliasing: "Ignoring bit 0 aliases 0xE6/0xE7 BVH operations to 0x66/0x67" — which is why the bit matters at all.
Their opcode table then lists the image ops they need, and its window above 0xA0 is: 0xE6 => ImageBvhIntersectRay, 0xE7 => ImageBvh64IntersectRay, then the default unknown-mimg op=0x{opcode:X2}. No 0xE5.
So three implementations now agree it is not a defined MIMG opcode on this family: LLVM's MC tables (every gfx variant), sharpemu's decoder, and ours (0xE6/0xE7 present, 0xE5 absent). That leaves the two possibilities in the previous comment; the diagnostic run is underway and I will post the raw words here.
Checked against the decoder's own set of valid major opcodes (32 33 35 36 37 38 3a 3c 3d 3e 3f 7d 7e 7f), by (word >> 26) & 0x3F:
361a6e87 0x0d not an instruction
beea0f7e 0x2f not an instruction
8126a026 0x20 not an instruction
8125256a 0x20 not an instruction
f1949f05 0x3c <- the only word that passes, and it is not code
00060018 0x00 not an instruction
121b1a19 0x04 not an instruction
00003715 0x00 not an instruction
7d8a1af9 0x1f not an instruction
0686b086 0x01 not an instruction
Nine of the ten words are not valid instructions at all, and the tenth only passes because a random word has roughly one chance in sixty-four of carrying 0x3C in bits 31:26 — after which its opcode field happened to land on the one gap in the table. So unsupported MIMG opcode 229 is a coincidence, not a missing ISA operation. Two supporting points: 0xE5 is also absent from LLVM's gfx1013 tables and from sharpemu's decoder, and ray-intersection instructions would be meaningless here anyway — Oberon has no ray-tracing hardware, so a PS5 title never emits them.
The defect is therefore a desync: the decode walked into non-code. Either the code span handed to the decoder extends past the shader's instruction section, or an earlier instruction was consumed with the wrong size and every word after it started at the wrong boundary. The ten-word window cannot tell those apart, so I instrumented the failure to dump the whole code span once and am re-running; locating the last valid instruction boundary in that dump should name the instruction whose size we get wrong.
I will post the desync point here and narrow this issue accordingly — its subject changes from "missing opcode" to "the walk leaves the code section". Leaving it open in the meantime.
Correction to my previous comment, before anyone acts on it.
I claimed "nine of the ten words are not valid instructions" using (word >> 26) & 0x3F against the major
opcodes I found by grepping the decoder. That test was wrong. The decoder's own family rule
(GetRdnaInstructionFamily) has three branches, not one:
bit31 == 0 → the short VOP forms (VOP2 by default, VOP1/VOPC by bits 25:30);
bits 31:30 == 10 → the SOP family, selected by (word >> 23) & 0x7F;
Re-evaluated with that rule, all ten words are legal instruction starts (361a6e87 VOP2, beea0f7e SOP1, 8126a026 SOP2, 8125256a SOP2, f1949f05 MIMG, then VOP2/SOP-family words). So the window is not evidence
of reading non-code.
Worse, that test has almost no power either way: any word with bit31 clear is accepted as VOP2, which is
roughly half of all random words. It cannot separate code from data.
What still stands, unchanged:
0xE5 is not a defined MIMG opcode in LLVM's tables for any gfx variant, nor in sharpemu's decoder.
This project's table carries 0xE6/0xE7 (and sharpemu names them) but has no 0xE5.
The title reached program counter 0x410 with word f1949f05, whose encoded word is the same shape as image_bvh64_intersect_ray (f19c9f01, opcode 0xE7) and image_bvh_intersect_ray (f1989f01, 0xE6) —
one step below them in that consecutive group.
So the open question is still "reserved encoding, or a walk that is off", and the test that separates them is
a walk trace (index, family, opcode and word count per instruction), not a window of raw words. I have the
code-span dump in flight and will post the trace next.
llvm-mc from ROCm 7.2.4 (-mcpu=gfx1013, gfx1030, gfx1010) also rejects these words as invalid instruction encoding, so I agree it's best kept unknown until there's a PS5-specific source.
Tested and written by Claude (AI assistant) on a contributor's machine.
Status update: the affected shaders no longer abort the title. #2257 defers them (the opcode is named in the reason, and they still throw when a draw needs them), measured on PPSA26344 — before: terminate … unsupported MIMG opcode 229; after: could not be prepared at registration (unsupported MIMG opcode 229) and the title carries on.
That does not close this issue: 0xE5 remains unidentified, so the shaders are still not compiled. Also worth recording from the offline work: the failing word is reproducible from the title's own shader store (cache_ps5/all_shaders.xpps, code word 260), which makes this cheap to re-check without running the game.
527 implemented · 39 stub · 136 provided by the title's own modules · 0 absent
0 missing libraries, 0 library mismatches
So nothing the executable imports is unresolved. The residual risk in the import path is the 39 NIDs that are only stubs — they abort when called. The ones that bear on rendering all live in this project's own libSceAgc.prx:
sceAgcGetDefaultCxStateFlat — AAeX-U5-P3M
sceAgcAcbSetFlip — ebixW91gpPw
sceAgcAcbWaitUntilSafeForRendering — GPbUp9jXQa8
sceAgcDebugRaiseException — T6xuVw0KUJo
Corroborated independently: all four are exactly 23-byte exports in the relinked module (readelf --dyn-syms over its 281 exported FUNCs: min 5, median 39, p90 231, max 3061), i.e. one shared generated stub body — matching import_audit's stub classification from a different direction.
The other 35 are libScePsml (MFSR, 10), libSceVoiceQoS (11), five sceCoredump* in libkernel, sceAjmBatchJobControl, and a few audio/NP/system-service NIDs.
Expectation, not a measured fact: once this title gets past the shader decode above and reaches presentation, sceAgcAcbSetFlip / sceAgcAcbWaitUntilSafeForRendering are the next things it can trip over, since it does import them. No run of ours has reached a draw yet, and the logs show no stub abort — consistent with those stubs sitting after this wall rather than before it.
Follow-up: the classification above is now confirmed at the source level, not only by export size.
Decompiling that export in our own relinked module with Ghidra (address = dynsym value + image base 0x100000; passing the raw NID as a procedure selector is rejected, the address works):
That is exactly the stub class as docs/user/USAGE.md defines it — the whole function calls NotImplemented_nid_no_patch and throws when the title calls it. So for AAeX-U5-P3M the audit's classification and the object code agree; the remaining three AGC stubs are byte-identical 23-byte exports, i.e. the same body.
For the two flip stubs in that list: #1585 implements sceAgcAcbSetFlip and sceAgcAcbWaitUntilSafeForRendering, with the compute-queue flip and rendering-wait packets and tests. So if Yōtei reaches them, #1585 is the PR to stack.
Written by Claude (AI assistant) on a contributor's machine.
Correction to my last comment: #1585 was closed this morning, because no title was shown to reach those two functions on main. It can be reopened with a title ID and log line. So if Yōtei reaches sceAgcAcbSetFlip or sceAgcAcbWaitUntilSafeForRendering, that log line is what it needs.
Written by Claude (AI assistant) on a contributor's machine.
The two decoders agree on the span, and the runtime's own dumped window pins where the span starts — so the "walk that is off" horn is not supported.
Method (offline; the title's own shader store, no game run). The log for this wall prints pc=0x410 wordIndex=260 and the words at indices 256..265. That ten-word sequence occurs exactly once in cache_ps5/all_shaders.xpps, at offset 3235660 = 3234636 + 256*4. The runtime's wordIndex is by construction the index into the code array (pc = wordIndex*4, code[0] = the decode start), so matching that window to a unique file offset pins the decode start at file offset 3234636 and the failing word f1949f05 at index 260 (offset 3235676). (The span's word 0 is bfa00003 = s_inst_prefetch 0x3, a normal prologue.)
I walked that 320-word span two ways:
the project's own RdnaInstructionDecoder, built standalone, calling DecodeRdnaInstruction in a loop; and
The two walks are position-identical for all 196 instructions up to the failure: comparing the ordered boundary sequences (LLVM's per-instruction byte counts from --show-encoding against the project's per-instruction indices) gives zero mismatches, and LLVM's last instruction ends exactly at word 260. Mnemonics agree at the tail:
word 257 s_bcnt1_i32_b32 vcc_lo, exec_lo
word 258 s_add_i32 s38, s38, 32
word 259 s_add_i32 s37, vcc_lo, s37 <- last instruction before the failure
word 260 f1949f05 <- the failure
No unconditional branch and no s_endpgm precedes word 260 — every branch before it is conditional — so 260 sits on the fall-through path of valid code.
What 260 is. MIMG-major ((w>>26) == 0x3C) with opcode ((w>>18)&0x7f)|((w&1)<<7) = 0xE5, which the project's own table does not define (RdnaImageOpDecoder.cpp carries 0xE6/0xE7 but not 0xE5), and which llvm-mc rejects as invalid instruction encoding. Not a one-off: the store carries 38 MIMG-major 0xE5 words (f1949f05 ×34, f1962969 ×4).
Why it cannot be a size bug. MIMG's width is 2 + nsa, and nsa = (word0 >> 1) & 3 is read from word0 independently of the opcode lookup — so an unknown opcode can only throw, never change the width the walk consumes.
Where that leaves #1941. The failure is consistent with a reserved/undocumented MIMG opcode, not a decoder that consumed the wrong number of words; identifying 0xE5 still needs a hardware or spec source, as before. No change is posted for this.
Reproduce (Linux, g++ and llvm-mc):
# the failing shader's span: 320 words from the decode start
dd if=all_shaders.xpps of=span.bin bs=1 skip=3234636 count=1280
# the project's decoder, in a loop over DecodeRdnaInstruction
g++ -std=c++20 -O0 \
-I core/shader/recompiler -I core/shader/recompiler/RdnaDecoder/include \
-I core/shader/recompiler/IntermediateRepresentation/include \
-I core/shader/recompiler/ControlFlow/include -I core/shader/recompiler/Optimization/include \
-I core/shader/recompiler/SpirvBackend/include -I core/shader/recompiler/Translation/include \
walk.cpp core/shader/recompiler/RdnaDecoder/src/*.cpp -o walk && ./walk span.bin 0 320
# LLVM's boundaries + encodings over the same bytes
python3 -c "d=open('span.bin','rb').read()[:260*4]; print(' '.join('0x%02x'%b for b in d))" \
| llvm-mc -disassemble -arch=amdgcn -mcpu=gfx1013 --show-encoding
This one can be closed out: 0xE5 is the fifth member of the gather4 family the enumeration above already lists.
Reading the title's own shader store (cache_ps5/all_shaders.xpps), all 38 4-byte-aligned sites whose extracted opcode is 0xE5 decode on gfx1100 as image_gather4_c_b_cl — the raw bytes as they sit in the file, not a reconstruction:
34 sites are the first word, 4 are the second. gfx1013 and gfx1030 reject the same bytes — the same "encoding absent from the table" situation as the rest of this family, which is why nothing in LLVM's MIMG tables names it.
It lines up exactly with the enumeration in the comment above:
Those four are, in order, AMD's RDNA3 numbering for 97–100, and 101 there is IMAGE_GATHER4_C_B_CL. The whole get_lod-plus-five-gathers block sits at 0x60–0x65 on RDNA3 and at 0xE0–0xE5 on this enumeration, so 0xE5 is that block's last member.
So the correction to my own earlier "undefined in every variant": undefined in LLVM's tables, yes, but the instruction is identifiable — it is not an anonymous gap beside the BVH pair.
Caveat worth keeping: no table defines 0xE5 for gfx1013 itself, so this rests on the family ordering plus the gfx1100 decode. Whoever implements it should validate the semantics rather than trust the neighbour.
Correction to my comment above, before anyone acts on it.
I said 0xE5 is the fifth member of the gather4 sequence listed there. The ordering is what does not hold up, and the check is cheap:
constructed MIMG words, disassembled with llvm-mc:
0xE0-0xE5 -> v_illegal on gfx1010, gfx1013, gfx1030
0x60 -> image_get_lod on gfx1010, gfx1013, gfx1030
0x61 -> image_gather4h on gfx1010, gfx1013, gfx1030
0x4E -> image_gather4_c_b_cl on gfx1010, gfx1013, gfx1030
The known encodings decode, so the words are well formed — and every 0xE0–0xE5 value is illegal on this GPU family.
It is not illegal everywhere, though: gfx1100 decodes all six, as image_gather4_cl at 0xE0 and the five gathers image_gather4_b_cl, _c_cl, _c_l, _c_b, _c_b_cl at 0xE1–0xE5. So the block is a real gfx11 block, not a gfx1013 one, and my "fifth member" reading borrowed gfx11's numbering for a gfx1013 shader. That borrowed numbering is the unsupported step.
It matters because the sourced gfx1013 encoding of the same operation is different: LLVM's assembler encodes image_gather4_c_b_cl on gfx1013 as word 0xf138010a, opcode 0x4E — which this table already carries, and main has it. So the operation is implemented and its gfx1013 encoding is known. 0xE5 is something else.
The title's bytes are gfx1100-shaped: all 38 sites decode on gfx1100 as image_gather4_c_b_cl, and gfx1010/gfx1013/gfx1030 reject the same raw bytes.
On the trial build, corrected: with a local mapping for 0xE0–0xE5 the refusals stopped being unsupported MIMG opcode 229 (9 → 0) and became unsupported or reserved MIMG control bits (0 → 9) — the same nine shaders, and the deferred total was unchanged at 121, so nothing was actually unblocked. The check that fails there is word0 against the class's reserved mask (bit 12 for f1949f05, bit 6 for f1962969) rather than word1: word1's mask is bits 26–29, and bits 16–18 are the resource field. Bit 12 is the gradient-count field, which a gather must not set — evidence that the word is not a gather at all. That mapping is an unconfirmed local experiment; I am not proposing it here.
So 0xE5 is still unidentified, and the open question is now sharper: why does a gfx1013 title emit a gfx11-shaped MIMG word instead of the gfx1013 encoding of the operation? The project's hardware oracle needs an AMD device and my box is NVIDIA, so I cannot run it — that is where I would look next.
Leaving the next step here so the issue stays actionable rather than parked on my inference.
docs/dev/HW_ORACLE.md says other RDNA2 parts (gfx103x) share the MIMG encodings with this GPU, so tools/hw-oracle/ on any AMD RDNA2 card under Linux can answer what a table cannot: whether 0xE5 is accepted on real silicon and what it does. It needs the amdgpu driver with read access to /dev/kfd and a ROCm runtime, so my NVIDIA box cannot run it and I am not asking anyone on this thread to.
The question worth putting to the oracle is the one the title raises: a gfx1013 title emits words that only gfx1100's tables decode (as image_gather4_c_b_cl), while gfx1013's own encoding of that operation is 0x4E and is already implemented here. Until something outside LLVM's tables says what 0xE5 means, this stays open.
Following the comment above with the measurement I should have led with, plus two corrections to it.
This store is gfx10-family, and 0xE5 is an unassigned opcode inside it. Disassembling each encoding's real sites on both families (llvm-mc, -triple=amdgcn, raw bytes, up to six aligned sites each):
The decisive rows are 0xE6/0xE7. In LLVM's tables IMAGE_BVH_INTERSECT_RAY is mimgopc<gfx11=0x19, gfx10m=0xe6> and IMAGE_BVH64_INTERSECT_RAY is <0x1a, 0xe7>: the gfx10 family uses 0xE6/0xE7, gfx11 moved them to 0x19/0x1A. Those words decode only on gfx1030 and gfx1013, never on gfx1100 — so this is a gfx10-family store.
That also explains the 0xE5 words, and why the gfx1100 name is a coincidence rather than an identification. The gfx10 opcode space is 8-bit with the top bit carried in word0 bit 0 — which is how 0xE6/0xE7 exist at all — so these words are opcode 0xE5. gfx11 reads a clean 8-bit field at bits 25:18, where the same bits are 0x65, and gfx11's IMAGE_GATHER4_C_B_CL is 0x65. Same bit pattern, two field positions, different answers. The store is gfx10-family, so 0xE5 is the reading that applies — and nothing assigns it. It sits immediately below the only two opcodes that are gfx1013-specific.
Two corrections to the comment above, since it is the one that will be read first:
It says the trial build's failure "is word0 bit 12, the gradient-count field, which a gather must not set". That is wrong. This store contains words that genuinely decode as image_gather4* on gfx1030 with bit 12 set, so bit 12 is not a field gathers are barred from; the failing check is word0 against the reserved mask for the op class, and the meaning I put on that bit does not hold. Withdrawn.
"The title's bytes are gfx1100-shaped" is true only of the 0xE5 words. It reads as though it covers the whole store, and the store is gfx10-family.
So 0xE5 is an unassigned gfx10-family opcode sitting beside the two gfx1013-only BVH ops — the shape of a gfx1013-specific extension that the public tables do not carry. Settling it needs the hardware oracle on an AMD RDNA2 device rather than another reading of the bits.
Activity
Dextroid17 commentedon Oct 9, 2026
The diagnostic step paid off — the title now names the opcode.
With the encoding included in
RdnaImageOpDecoder.cpp:165("unsupported MIMG opcode " + std::to_string(opcode)), PPSA26344 dies after ~24 minutes and 897 shaders with:So the missing encoding is 0xE5, immediately below the two the table already carries:
MIMGInstructions.td)ImageBvhIntersectRayIMAGE_BVH_INTERSECT_RAY, gfx12 encoding 0xE6 (line 2039)ImageBvh64IntersectRayIMAGE_BVH64_INTERSECT_RAY, gfx12 encoding 0xE7 (line 2041)Two notes for whoever implements it:
For completeness, the table covers 135 encodings; the gaps where a shader could plausibly land are
0x06 0x07 0x0c 0x0d 0x13,0x64-0x67 0x72 0x75 0x78 0x7b-0x7f,0x81-0x9fand0xe0-0xe5.Written with AI assistance.
MarcZX10 commentedon Oct 9, 2026
I checked the public AMD RDNA 2 ISA reference and AMDGPU LLVM opcode tables as well. They document
IMAGE_BVH_INTERSECT_RAYandIMAGE_BVH64_INTERSECT_RAYas the adjacent0xE6/0xE7operations, but do not define0xE5. That supports keeping this as an unknown instruction until a PS5-specific hardware/spec source identifies its semantics; inferring it from the BVH neighbors would be unsafe. AMD RDNA 2 ISA · LLVM AMDGPU MIMG opcode table.Dextroid17 commentedon Oct 9, 2026
Adding the ISA evidence for
0xE5, because it narrows the question usefully.Source: LLVM's own MC tests for this GPU family —
llvm/test/MC/AMDGPU/gfx1013.sis the file for gfx1013, which is what PS5's GPU ("Oberon") reports as. I extracted all 253 mnemonic↔encoded-word pairs from the MIMG test files and keyed them by encoding variant.So the ray-tracing family is
0xE6/0xE7— whichRdnaImageOpDecoder.cppalready carries — and0xE5is not assigned anywhere in public data, including on this exact GPU.A hypothesis I checked and had to drop. The decoder builds the opcode as
((word0 >> 18) & 0x7F) | ((word0 & 1) << 7), i.e. opcode bit 7 comes from word bit 0, which looked wrong next to GFX11 rows where the same bit lives at word bit 8. It is not wrong for us: assembling gfx1013'simage_msaa_loadgives word0xf0000839, and only the bit-0 reading yields0x80(the bit-8 reading yields0x00). The bit placement genuinely differs between GFX10 and GFX11, and ours matches GFX10. Closed.What that leaves, with the decoder already rejecting non-MIMG words (
(word0 >> 26) != 0x3C) and reserved control bits:To tell those apart I instrumented the lookup to print the program counter, both words, the extracted opcode, the major opcode, and the surrounding words, then re-ran the title. The run reports which of the two it is; I will post the words here when it reaches the wall.
Written with AI assistance.
Dextroid17 commentedon Oct 9, 2026
Adding a third source, which agrees on both points.
sharpemu/sharpemu has an independent GFX10 MIMG decoder (
src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs) and it says the same two things this decoder does:((word >> 18) & 0x7F) | ((word & 1) << 7)this project uses, with a test of its own (ImageBvhIntersectRay_UsesOpcodeBitSevenFromOpm).Their opcode table then lists the image ops they need, and its window above
0xA0is:0xE6 => ImageBvhIntersectRay,0xE7 => ImageBvh64IntersectRay, then the defaultunknown-mimg op=0x{opcode:X2}. No0xE5.So three implementations now agree it is not a defined MIMG opcode on this family: LLVM's MC tables (every gfx variant), sharpemu's decoder, and ours (
0xE6/0xE7present,0xE5absent). That leaves the two possibilities in the previous comment; the diagnostic run is underway and I will post the raw words here.Written with AI assistance.
Dextroid17 commentedon Oct 9, 2026
The diagnostic answers it, and the answer is not an opcode.
Checked against the decoder's own set of valid major opcodes (
32 33 35 36 37 38 3a 3c 3d 3e 3f 7d 7e 7f), by(word >> 26) & 0x3F:Nine of the ten words are not valid instructions at all, and the tenth only passes because a random word has roughly one chance in sixty-four of carrying
0x3Cin bits 31:26 — after which its opcode field happened to land on the one gap in the table. Sounsupported MIMG opcode 229is a coincidence, not a missing ISA operation. Two supporting points:0xE5is also absent from LLVM's gfx1013 tables and from sharpemu's decoder, and ray-intersection instructions would be meaningless here anyway — Oberon has no ray-tracing hardware, so a PS5 title never emits them.The defect is therefore a desync: the decode walked into non-code. Either the code span handed to the decoder extends past the shader's instruction section, or an earlier instruction was consumed with the wrong size and every word after it started at the wrong boundary. The ten-word window cannot tell those apart, so I instrumented the failure to dump the whole code span once and am re-running; locating the last valid instruction boundary in that dump should name the instruction whose size we get wrong.
I will post the desync point here and narrow this issue accordingly — its subject changes from "missing opcode" to "the walk leaves the code section". Leaving it open in the meantime.
Written with AI assistance.
Dextroid17 commentedon Oct 9, 2026
Correction to my previous comment, before anyone acts on it.
I claimed "nine of the ten words are not valid instructions" using
(word >> 26) & 0x3Fagainst the majoropcodes I found by grepping the decoder. That test was wrong. The decoder's own family rule
(
GetRdnaInstructionFamily) has three branches, not one:bit31 == 0→ the short VOP forms (VOP2 by default, VOP1/VOPC by bits 25:30);bits 31:30 == 10→ the SOP family, selected by(word >> 23) & 0x7F;VINTRP VOP3P VOP3 DS FLAT MUBUF MTBUF MIMG SMEM EXP).Re-evaluated with that rule, all ten words are legal instruction starts (
361a6e87VOP2,beea0f7eSOP1,8126a026SOP2,8125256aSOP2,f1949f05MIMG, then VOP2/SOP-family words). So the window is not evidenceof reading non-code.
Worse, that test has almost no power either way: any word with bit31 clear is accepted as VOP2, which is
roughly half of all random words. It cannot separate code from data.
What still stands, unchanged:
0xE5is not a defined MIMG opcode in LLVM's tables for any gfx variant, nor in sharpemu's decoder.0xE6/0xE7(and sharpemu names them) but has no0xE5.0x410with wordf1949f05, whose encoded word is the same shape asimage_bvh64_intersect_ray(f19c9f01, opcode0xE7) andimage_bvh_intersect_ray(f1989f01,0xE6) —one step below them in that consecutive group.
So the open question is still "reserved encoding, or a walk that is off", and the test that separates them is
a walk trace (index, family, opcode and word count per instruction), not a window of raw words. I have the
code-span dump in flight and will post the trace next.
Written with AI assistance.
EdouardSence commentedon Oct 9, 2026
Same instruction here on AMD, reached about 15 s after boot instead of about 24 min. That makes it quick to reproduce for whoever looks at it.
main43972e6 + fix(agc): defer shaders that cannot be prepared at registration to their first use #1836, fix(agc): accept bound copies of a registered shader header that differ only in user_data #1837, fix(shader): decode the scalar aperture sources 235-238 instead of failing the shader #1940, perf(recompiler): compute post-dominators visiting successors first #2105, fix(agc): skip registration-time preparation for unsupported shader types #1893, feat(agc): implement deferred faults for reads through null SRT pointers #759 and a few unrelated fixes.pc=0x410 words=f1949f05 00060018 121b1a19 00003715.computePostDominators, as measured on Rise of the Ronin there.llvm-mcfrom ROCm 7.2.4 (-mcpu=gfx1013,gfx1030,gfx1010) also rejects these words asinvalid instruction encoding, so I agree it's best kept unknown until there's a PS5-specific source.Tested and written by Claude (AI assistant) on a contributor's machine.
Dextroid17 commentedon Oct 9, 2026
Status update: the affected shaders no longer abort the title. #2257 defers them (the opcode is named in the reason, and they still throw when a draw needs them), measured on PPSA26344 — before:
terminate … unsupported MIMG opcode 229; after:could not be prepared at registration (unsupported MIMG opcode 229)and the title carries on.That does not close this issue:
0xE5remains unidentified, so the shaders are still not compiled. Also worth recording from the offline work: the failing word is reproducible from the title's own shader store (cache_ps5/all_shaders.xpps, code word 260), which makes this cheap to re-check without running the game.Dextroid17 commentedon Oct 9, 2026
Import-coverage audit for this title, for anyone picking up the wall above.
Reproduced with the documented pipeline (
docs/user/USAGE.md):Result for PPSA26344's executable:
So nothing the executable imports is unresolved. The residual risk in the import path is the 39 NIDs that are only stubs — they abort when called. The ones that bear on rendering all live in this project's own
libSceAgc.prx:sceAgcGetDefaultCxStateFlat—AAeX-U5-P3MsceAgcAcbSetFlip—ebixW91gpPwsceAgcAcbWaitUntilSafeForRendering—GPbUp9jXQa8sceAgcDebugRaiseException—T6xuVw0KUJoCorroborated independently: all four are exactly 23-byte exports in the relinked module (
readelf --dyn-symsover its 281 exported FUNCs: min 5, median 39, p90 231, max 3061), i.e. one shared generated stub body — matchingimport_audit'sstubclassification from a different direction.The other 35 are
libScePsml(MFSR, 10),libSceVoiceQoS(11), fivesceCoredump*in libkernel,sceAjmBatchJobControl, and a few audio/NP/system-service NIDs.Expectation, not a measured fact: once this title gets past the shader decode above and reaches presentation,
sceAgcAcbSetFlip/sceAgcAcbWaitUntilSafeForRenderingare the next things it can trip over, since it does import them. No run of ours has reached a draw yet, and the logs show no stub abort — consistent with those stubs sitting after this wall rather than before it.Written with AI assistance.
Dextroid17 commentedon Oct 9, 2026
Follow-up: the classification above is now confirmed at the source level, not only by export size.
Decompiling that export in our own relinked module with Ghidra (address = dynsym value + image base
0x100000; passing the raw NID as a procedure selector is rejected, the address works):That is exactly the
stubclass asdocs/user/USAGE.mddefines it — the whole function callsNotImplemented_nid_no_patchand throws when the title calls it. So forAAeX-U5-P3Mthe audit's classification and the object code agree; the remaining three AGC stubs are byte-identical 23-byte exports, i.e. the same body.Written with AI assistance.
EdouardSence commentedon Oct 9, 2026
For the two flip stubs in that list: #1585 implements
sceAgcAcbSetFlipandsceAgcAcbWaitUntilSafeForRendering, with the compute-queue flip and rendering-wait packets and tests. So if Yōtei reaches them, #1585 is the PR to stack.Written by Claude (AI assistant) on a contributor's machine.
EdouardSence commentedon Oct 9, 2026
Correction to my last comment: #1585 was closed this morning, because no title was shown to reach those two functions on
main. It can be reopened with a title ID and log line. So if Yōtei reachessceAgcAcbSetFliporsceAgcAcbWaitUntilSafeForRendering, that log line is what it needs.Written by Claude (AI assistant) on a contributor's machine.
fix(agc): defer a shader the decoder does not implement instead of ab…
Dextroid17 commentedon Oct 9, 2026
The two decoders agree on the span, and the runtime's own dumped window pins where the span starts — so the "walk that is off" horn is not supported.
Method (offline; the title's own shader store, no game run). The log for this wall prints
pc=0x410 wordIndex=260and the words at indices 256..265. That ten-word sequence occurs exactly once incache_ps5/all_shaders.xpps, at offset 3235660 =3234636 + 256*4. The runtime'swordIndexis by construction the index into the code array (pc = wordIndex*4,code[0]= the decode start), so matching that window to a unique file offset pins the decode start at file offset 3234636 and the failing wordf1949f05at index 260 (offset 3235676). (The span's word 0 isbfa00003=s_inst_prefetch 0x3, a normal prologue.)I walked that 320-word span two ways:
RdnaInstructionDecoder, built standalone, callingDecodeRdnaInstructionin a loop; andllvm-mc -disassemble -arch=amdgcn -mcpu=gfx1013 --show-encoding(LLVM 23.1.1).The two walks are position-identical for all 196 instructions up to the failure: comparing the ordered boundary sequences (LLVM's per-instruction byte counts from
--show-encodingagainst the project's per-instruction indices) gives zero mismatches, and LLVM's last instruction ends exactly at word 260. Mnemonics agree at the tail:No unconditional branch and no
s_endpgmprecedes word 260 — every branch before it is conditional — so 260 sits on the fall-through path of valid code.What 260 is. MIMG-major (
(w>>26) == 0x3C) with opcode((w>>18)&0x7f)|((w&1)<<7)=0xE5, which the project's own table does not define (RdnaImageOpDecoder.cppcarries0xE6/0xE7but not0xE5), and whichllvm-mcrejects asinvalid instruction encoding. Not a one-off: the store carries 38 MIMG-major0xE5words (f1949f05×34,f1962969×4).Why it cannot be a size bug. MIMG's width is
2 + nsa, andnsa = (word0 >> 1) & 3is read fromword0independently of the opcode lookup — so an unknown opcode can only throw, never change the width the walk consumes.Where that leaves #1941. The failure is consistent with a reserved/undocumented MIMG opcode, not a decoder that consumed the wrong number of words; identifying
0xE5still needs a hardware or spec source, as before. No change is posted for this.Reproduce (Linux,
g++andllvm-mc):Written with AI assistance.
Dextroid17 commentedon Oct 10, 2026
This one can be closed out:
0xE5is the fifth member of the gather4 family the enumeration above already lists.Reading the title's own shader store (
cache_ps5/all_shaders.xpps), all 38 4-byte-aligned sites whose extracted opcode is0xE5decode ongfx1100asimage_gather4_c_b_cl— the raw bytes as they sit in the file, not a reconstruction:34 sites are the first word, 4 are the second.
gfx1013andgfx1030reject the same bytes — the same "encoding absent from the table" situation as the rest of this family, which is why nothing in LLVM's MIMG tables names it.It lines up exactly with the enumeration in the comment above:
Those four are, in order, AMD's RDNA3 numbering for 97–100, and 101 there is
IMAGE_GATHER4_C_B_CL. The whole get_lod-plus-five-gathers block sits at 0x60–0x65 on RDNA3 and at 0xE0–0xE5 on this enumeration, so0xE5is that block's last member.So the correction to my own earlier "undefined in every variant": undefined in LLVM's tables, yes, but the instruction is identifiable — it is not an anonymous gap beside the BVH pair.
Caveat worth keeping: no table defines
0xE5for gfx1013 itself, so this rests on the family ordering plus the gfx1100 decode. Whoever implements it should validate the semantics rather than trust the neighbour.Written with AI assistance.
Dextroid17 commentedon Oct 10, 2026
Correction to my comment above, before anyone acts on it.
I said
0xE5is the fifth member of the gather4 sequence listed there. The ordering is what does not hold up, and the check is cheap:The known encodings decode, so the words are well formed — and every
0xE0–0xE5value is illegal on this GPU family.It is not illegal everywhere, though: gfx1100 decodes all six, as
image_gather4_clat 0xE0 and the five gathersimage_gather4_b_cl,_c_cl,_c_l,_c_b,_c_b_clat 0xE1–0xE5. So the block is a real gfx11 block, not a gfx1013 one, and my "fifth member" reading borrowed gfx11's numbering for a gfx1013 shader. That borrowed numbering is the unsupported step.It matters because the sourced gfx1013 encoding of the same operation is different: LLVM's assembler encodes
image_gather4_c_b_clon gfx1013 as word0xf138010a, opcode0x4E— which this table already carries, andmainhas it. So the operation is implemented and its gfx1013 encoding is known.0xE5is something else.The title's bytes are gfx1100-shaped: all 38 sites decode on gfx1100 as
image_gather4_c_b_cl, and gfx1010/gfx1013/gfx1030 reject the same raw bytes.On the trial build, corrected: with a local mapping for 0xE0–0xE5 the refusals stopped being
unsupported MIMG opcode 229(9 → 0) and becameunsupported or reserved MIMG control bits(0 → 9) — the same nine shaders, and the deferred total was unchanged at 121, so nothing was actually unblocked. The check that fails there is word0 against the class's reserved mask (bit 12 forf1949f05, bit 6 forf1962969) rather than word1: word1's mask is bits 26–29, and bits 16–18 are the resource field. Bit 12 is the gradient-count field, which a gather must not set — evidence that the word is not a gather at all. That mapping is an unconfirmed local experiment; I am not proposing it here.So
0xE5is still unidentified, and the open question is now sharper: why does a gfx1013 title emit a gfx11-shaped MIMG word instead of the gfx1013 encoding of the operation? The project's hardware oracle needs an AMD device and my box is NVIDIA, so I cannot run it — that is where I would look next.Written with AI assistance.
Dextroid17 commentedon Oct 10, 2026
Leaving the next step here so the issue stays actionable rather than parked on my inference.
docs/dev/HW_ORACLE.mdsays other RDNA2 parts (gfx103x) share the MIMG encodings with this GPU, sotools/hw-oracle/on any AMD RDNA2 card under Linux can answer what a table cannot: whether0xE5is accepted on real silicon and what it does. It needs theamdgpudriver with read access to/dev/kfdand a ROCm runtime, so my NVIDIA box cannot run it and I am not asking anyone on this thread to.The question worth putting to the oracle is the one the title raises: a gfx1013 title emits words that only gfx1100's tables decode (as
image_gather4_c_b_cl), while gfx1013's own encoding of that operation is0x4Eand is already implemented here. Until something outside LLVM's tables says what0xE5means, this stays open.Written with AI assistance.
Dextroid17 commentedon Oct 10, 2026
Following the comment above with the measurement I should have led with, plus two corrections to it.
This store is gfx10-family, and
0xE5is an unassigned opcode inside it. Disassembling each encoding's real sites on both families (llvm-mc,-triple=amdgcn, raw bytes, up to six aligned sites each):The decisive rows are
0xE6/0xE7. In LLVM's tablesIMAGE_BVH_INTERSECT_RAYismimgopc<gfx11=0x19, gfx10m=0xe6>andIMAGE_BVH64_INTERSECT_RAYis<0x1a, 0xe7>: the gfx10 family uses0xE6/0xE7, gfx11 moved them to0x19/0x1A. Those words decode only on gfx1030 and gfx1013, never on gfx1100 — so this is a gfx10-family store.That also explains the
0xE5words, and why the gfx1100 name is a coincidence rather than an identification. The gfx10 opcode space is 8-bit with the top bit carried in word0 bit 0 — which is how0xE6/0xE7exist at all — so these words are opcode0xE5. gfx11 reads a clean 8-bit field at bits 25:18, where the same bits are0x65, and gfx11'sIMAGE_GATHER4_C_B_CLis0x65. Same bit pattern, two field positions, different answers. The store is gfx10-family, so0xE5is the reading that applies — and nothing assigns it. It sits immediately below the only two opcodes that are gfx1013-specific.Two corrections to the comment above, since it is the one that will be read first:
image_gather4*on gfx1030 with bit 12 set, so bit 12 is not a field gathers are barred from; the failing check is word0 against the reserved mask for the op class, and the meaning I put on that bit does not hold. Withdrawn.0xE5words. It reads as though it covers the whole store, and the store is gfx10-family.So
0xE5is an unassigned gfx10-family opcode sitting beside the two gfx1013-only BVH ops — the shape of a gfx1013-specific extension that the public tables do not carry. Settling it needs the hardware oracle on an AMD RDNA2 device rather than another reading of the bits.Written with AI assistance.
fix(agc): defer a shader the decoder does not implement instead of ab…
fix(agc): defer a shader the decoder does not implement instead of ab…
fix(agc): defer a shader the decoder does not implement instead of ab…