Interpreter: fix the ins mask when the encoded msb is below pos

ins derived its width as (_SIZE + 1) - pos, which is zero or negative when the
encoded msb is below pos: the following shift is then 32 or more, undefined, and
on x86 produces an all-ones mask that writes bits the JITs don't touch. Build
the mask from msb and shift it down instead, which is what the JITs do and can't
shift out of range. Hardware calls that encoding unpredictable, so consistency
is all that's wanted here.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01SfY7iFJEjmRXf1XGrTs4MF
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-08-27 19:01:23 +02:00
1 parent 484898385f
commit d79117146d
1 file changed
+10 -4
+10 -4
View File
@@ -1088,10 +1088,16 @@ namespace MIPSInt {
break;
case 0x4: //ins
{
int size = (_SIZE + 1) - pos;
u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);
u32 destmask = sourcemask << pos;
R(rt) = (R(rt) & ~destmask) | ((R(rs)&sourcemask) << pos);
// The size field actually holds msb (= pos + size - 1), so build the mask from
// that and shift it down, the way the JITs do. Computing the width as
// (_SIZE + 1) - pos instead would shift by 32 or more when msb < pos - undefined
// behavior, and on x86 it yields an all-ones mask that writes bits the JITs leave
// alone. Hardware calls that encoding unpredictable, so all we need is to be
// consistent and not invoke UB.
const u32 mask = 0xFFFFFFFFUL >> (31 - _SIZE);
const u32 sourcemask = mask >> pos;
const u32 destmask = sourcemask << pos;
R(rt) = (R(rt) & ~destmask) | ((R(rs) & sourcemask) << pos);
}
break;
}