SIMD Support Design (SSE/MMX)

1. Overview

Transitioning the emulator to support SIMD involves mapping x86 vector registers to WebAssembly v128 types and handling the architectural aliasing between MMX and the FPU.

2. Register Mapping

2.1 SSE (XMM0-XMM7)

2.2 MMX (MM0-MM7)

3. Instruction Mapping Table

x86 Opcode Description WASM SIMD Equivalent
ADDPD / ADDPS Packed Double/Single Float Add f64x2.add / f32x4.add
PADD[B/W/D] Packed Integer Add i8x16.add / i16x8.add / i32x4.add
ANDPS / ORPS Bitwise Logic v128.and / v128.or
MOV[A/U]PS Move Aligned/Unaligned v128.load / v128.store

4. Implementation Phases

Phase 1: Infrastructure

Phase 2: Integer MMX

Phase 3: Floating Point SSE

5. Performance Considerations

6. What MMX Actually Buys — Measured

Headline: 1.2x–1.5x on real applications, not the 7x the microbenchmark shows.

tools/mmx-bench.js reports 7.24x wall / 10.9x fewer x86 instructions on a saturating byte blend. That is the vector loop in isolation. A real frame also spends time on file I/O, palette work and the message pump, none of which MMX touches, so Amdahl caps the app-level win far below the kernel-level one. When quoting a number for what MMX support is worth, quote this section, not the bench.

App Workload Ratio
Jazz Jackrabbit 2 logo.j2v intro video decoder 1.53x
StarCraft (shareware) SMACKW32 Smacker decoder 1.20x
jazz2      --exe=test/binaries/candidates/jazz-jackrabbit-2-demo-installer/installed/jazz2.exe \
           --vfs-include='*.j2*,*.lst,*.txt'
           20000 batches: 2160 vs 1409 frames   (frame = SelectPalette)
           2.21M MMX instructions retired vs 0

starcraft  --exe=test/binaries/candidates/starcraft-shareware/installed/starcraft.exe \
           --vfs-include='*.dll,*.mpq,*.snp'
            5000 batches:   93 vs   85 frames   (frame = IDirectDrawSurface_Unlock)
            9000 batches:  234 vs  203 frames
            marginal:      141 vs  118 frames -> 1.20x
           11.2M MMX instructions retired vs 0

6.1 Why only these two apps

--no-mmx flips only the CPUID leaf-1 EDX bit 23; the MMX handlers stay live. So a ratio requires a guest that picks its own path at runtime, and most of the corpus does not. tools/mmx-census.js finds the ones that do. Two binaries look dual-path and are not — AVS and in_mod.dll each compute a feature flag and then run MMX regardless (AVS 2.6.1 is the MMX-only build and simply refuses to start with the bit off). jazz2 is the real shape:

00490e6c  test eax, 0x800000            ; EDX from CPUID leaf 1
00490e73  mov byte [0x4f7d78], 1        ; the MMX flag ...
00452bb4  mov al, [0x4f7d78]            ; ... read at 14 sites
00452bbb  jz 0x452c83                   ; scalar fallback
00452bc4  movq mm6, [0x4c4698]          ; vector leaf

6.2 Measuring one of these without fooling yourself

Four traps, each of which produced a wrong number before the ones above:

6.3 Remaining candidates

From tools/mmx-census.js, by biggest cluster: ScummVM (1062), VirtualDub (1045, dies in its MSVC runtime startup), MilkDrop vis_milk.dll (722, needs Direct3D 8 — the project has DirectDraw, D3D IM v2/3/7, D3DRM and D3D9, but no D3D8), msvbvm60.dll (522), TWorld's SDL.dll (200). Caesar III ships the same SMACKW32 as StarCraft, so it should behave like it.

Trust the tool's density column, not its raw count: RollerCoaster Tycoon scores 588 "MMX instructions" and retires zero at runtime, because they are 249 scattered linear-sweep misdecodes at 2.4 instructions per cluster. Under 4 is noise.