MMX (Single-cycle processing of packed integer data)

MMX (MultiMedia eXtensions) is a set of SIMD instructions built into Intel Pentium processors. The technology allows a single arithmetic or logical operation to be performed on multiple numbers packed into 64-bit registers, accelerating graphics and sound processing without additional coprocessors.

The technology was primarily used in multimedia applications of the 1990s: software video players (MPEG-1 and MPEG-2 decoding), two-dimensional and basic three-dimensional graphics, image processing in editors (filter application, layer blending), and audio codecs. It was actively used in early games with software rendering to speed up polygon and texture calculations on processors without a hardware 3D accelerator.

The main problem with MMX is register sharing with the x87 math coprocessor. Switching between MMX integer calculations and floating-point operations caused costly pipeline stalls and required mandatory state clearing with the EMMS instruction. Failure to issue EMMS after a block of MMX code led to hangs or incorrect coprocessor operation. Additional limitations include integer-only operation and small register width (64 bits) compared to later standards.

How MMX works

The operating principle is based on the SIMD paradigm and uses eight 64-bit registers (MM0–MM7), physically mapped onto the x87 register stack. Unlike scalar instructions operating on a single pair of numbers, MMX commands load a packed vector of several integers into a register: for example, eight bytes or four 16-bit words. The instruction performs an action in parallel on all vector elements, preventing carry between them (saturation). The key advantage over the newer SSE extension is support for saturating arithmetic for unsigned and signed integer types, which is critically important for correct pixel processing (brightness does not wrap to negative values on overflow). Unlike SSE, which operates on independent 128-bit XMM registers, MMX rigidly shares resources with the FPU, reducing efficiency in code where integer and real calculations alternate; however, at the time of its release, the technology was a compromise solution for rapidly introducing parallelism on the existing architecture.

MMX functionality

  1. Unsigned saturating subtraction. The PSUBSB instruction performs subtraction of packed unsigned bytes. If the result is less than zero, the destination is forced to zero, preventing wraparound and artifacts at brightness boundaries.
  2. Multiply and accumulate low parts. The PMADDWD command multiplies four signed 16-bit words, forming two 32-bit products, and then sums these products pairwise. The result is two doublewords with accumulated signal energy, critically important for convolutions.
  3. Parallel packed byte equality comparison. The PCMPEQB instruction compares bytes in operands, forming a mask: on a match, the destination byte is filled with ones (0xFF), otherwise zeros. This logic is used for alpha blending and sprite collision without conditional branches.
  4. Sum of absolute byte differences. PSADBW computes the Sum of Absolute Differences of eight byte pairs. The intermediate absolute differences are accumulated in the low word of the 64-bit register. This is the core of motion estimation in video codecs at the assembly level.
  5. Logical packing of doublewords into words. PACKSSDW converts four signed 32-bit doublewords into four 16-bit words with saturation. If the original values exceed the int16 boundaries, they are clamped to the maximum or minimum, eliminating overflow during signal downsampling.
  6. Unpacking high bytes into words. The PUNPCKHBW instruction extracts the upper four bytes from each operand and interleaves them into 16-bit words. The processor arranges the high-order bits of the source paired with the destination, forming interleaving without additional shifts and masks.
  7. Bitwise shift of a packed quadword to the right. PSRLQ performs a logical shift of all 64 bits as a single entity by the specified immediate value. Unlike the scalar approach, bits are not borrowed from neighboring mantissas; instead, the high-order bits of the entire vector are simply zeroed.
  8. Saving a mask from a byte stream. PMOVMSKB extracts the most significant bit of each of the eight packed bytes and places them in the low-order bits of a general-purpose integer register. The resulting index serves as a compact feature descriptor for fast branch switching in pixel classification algorithms.
  9. Unsigned arithmetic byte averaging. The PAVGB command computes the rounded average for each pair of packed bytes, adding one to the sum before shifting right. This method eliminates systematic bias towards zero during dithering and image filtering.
  10. Extracting a word from a multimedia register. PEXTRW pushes a specific 16-bit word, indexed by the imm8 field, from the 64-bit MMX container directly into a general-purpose register. The E-register addressing scheme eliminates expensive store-to-memory operations for scalar processing.
  11. Inserting a word into a multimedia register. PINSRW loads a 16-bit operand from the low word of an integer register and places it at a specified index in the MMX register. The remaining vector fields stay intact, allowing precise pixel parameter adjustments in a loop.
  12. Byte swapping in a general-purpose register. Although BSWAP is not part of the MMX set, its logic precedes the endianness reversal of multimedia operands. It mirrors the byte order in a 32-bit register, preparing data for the specifics of RGB graphics formats in OGL/DirectDraw.
  13. Determining the maximum of packed unsigned bytes. PMAXUB compares the unsigned bytes of the source and destination element-wise, writing the larger value into the destination. This hardware clipping is used in morphological dilation operations and volumetric fog rendering.
  14. Determining the minimum of packed signed words. PMINSW analyzes pairs of signed 16-bit words and keeps the smallest value. The instruction is in demand in physics engines for calculating convex hulls and limiting audio sample amplitude during editing.
  15. Multiplying high parts of signed words. PMULHW multiplies four pairs of signed 16-bit numbers and saves the upper 16 bits of each 32-bit product. This approach is used in fixed-point scaling, discarding the fractional part without a cycle spent on Q15 format conversion.
  16. Packing with unsigned saturation. The PACKUSWB command compresses four signed 16-bit words into unsigned bytes. If a word is negative, it saturates to zero; if it exceeds 255, to 255. The conversion corresponds to the transition from a high-precision shader range to an LDR framebuffer.
  17. Forced clearing of multimedia state. The EMMS (Empty MMX State) instruction resets the floating-point status word (FP Tag Word), marking registers as empty. Without this call, subsequent x87 FPU operations will interpret dirty tags, causing undefined behavior or slow exceptions.
  18. Masked byte write from logical comparison. PMASKMOVQ performs a selective write of data from the source to memory, controlled by the sign bit of each mask byte. If the most significant bit of the mask is one, the data is passed through; otherwise, zero is written to memory, forming occlusion without gradient transitions.
  19. Arithmetic averaging with rounding up for words. PAVGW works similarly to PAVGB but operates on 16-bit grids. The instruction accelerates low-pass image filtering, where accumulating truncation error at the byte level is unacceptable, ensuring correct calculation of color subsampling.
  20. Packed data conversion with interleaving. PUNPCKLWD unpacks and interleaves the low 16-bit words of the source and destination, generating an array of doublewords. The mechanism prepares matrices for 32-bit vector multiplication in affine texture transformation operations in MMX mode.

Comparisons

  • MMX vs x87 FPU. The MMX set uses the x87 coprocessor registers as an alias for integer vector operations, applying the SIMD methodology, while x87 performs scalar floating-point calculations. Context switching between MMX and x87 required the costly EMMS instruction, creating a resource conflict and limiting joint use within a single instruction stream.
  • MMX vs SSE. The SSE extension eliminated the fundamental drawback of MMX by introducing a separate architectural XMM register file, removing the conflict with the coprocessor. SSE operates primarily on single-precision floating-point numbers, whereas MMX is oriented exclusively towards integer calculations with saturation, offering packing of various data types in a 64-bit vector.
  • MMX vs 3DNow!. AMD’s 3DNow! technology extended the MMX paradigm by processing floating-point numbers in the same physical 64-bit MMX registers without introducing new state. Unlike integer MMX, 3DNow! provided parallelism for two 32-bit real numbers but retained the incompatibility problem with x87 and required state clearing with the FEMMS instruction.
  • MMX vs SSE2. SSE2 integrates MMX functionality into the 128-bit XMM architecture, supporting both extended integer operations and double-precision calculations. Unlike the archaic MMX aliasing, SSE2 uses an independent register set, allowing vector operations to be freely mixed with traditional scalar math without performance losses on context switching.
  • MMX vs AVX. The modern AVX standard, with its three-address non-destructive operation syntax, radically surpasses MMX, which works with a destructive two-operand format. AVX vector width reaches 256 and 512 bits versus the fixed 64 bits of MMX. Meanwhile, MMX is fully emulated in the AVX environment through microarchitectural command translation for compatibility purposes but is completely devoid of hardware priority.
  • AVX (SIMD data processing with 256/512-bit registers)

OS and driver support

MMX support is implemented at the operating system kernel level through saving and restoring the eight 64-bit MM0–MM7 registers (mapped onto the FPU mantissas) in the task context structure during thread switching, for which a kernel-mode driver must explicitly call wrapper macros (e.g., KeSaveFloatingPointState in Windows), while the application level operates with instructions without OS intervention, relying on a correct driver not causing an inconsistent state between MMX and FPU.

Security

Security issues when using MMX are caused by the lack of hardware isolation of MMX registers from x87 FPU registers: a malicious thread can read residual data after another thread’s operations if the context switch did not perform a forced clear via the EMMS instruction, and integrity control errors are also possible when incorrect insertion of assembly instructions without fixing the FPU tag state leads to computation corruption and exceptions in critical code.

Logging

Logging of MMX code execution is implemented through binary code instrumentation under a debugger with breakpoint trapping on target instructions (e.g., PADDUSB or PMADDW) and dumping of packed 64-bit values to a log file with their preliminary copying through memory into scalar general-purpose registers, whereas during kernel-level tracing, recording is performed into a non-pageable circular buffer linked to the thread ID and a TSC timestamp.

Limitations

The main limitation is that MMX arithmetic works only with packed integer types and saturating operations without floating-point number support; a restriction is also imposed on the simultaneous use of MMX registers and the x87 coprocessor stack without explicit separation via the EMMS instruction, and operations are carried out exclusively on 64-bit vectors without the ability to scale to 128-bit operands, which appeared only in the SSE extension.

History and development

MMX technology was first introduced in Intel Pentium P55C processors in 1997 as 57 additional SIMD instructions for accelerating multimedia integer processing by reusing eight 80-bit coprocessor registers as 64-bit shadow registers, after which AMD architecture implemented full compatibility in the K6, and subsequently development moved towards the displacement of MMX by SSE and AVX instructions with their own dedicated XMM/YMM registers, leaving MMX in the status of a legacy but supported instruction set for backward compatibility.