How can undefined opcodes ud0 and ud1 have parameters? How undefined were they?

The Old New Thing (Raymond Chen) News

Summary

Raymond Chen explains how x86 opcodes ud0 and ud1 could appear to take parameters despite being 'undefined' — they occupied decoder slots that had no assigned meaning yet still went through operand parsing before an invalid-opcode fault was raised.

<p>Gunnar Dalsnes wondered <a href="https://devblogs.microsoft.com/oldnewthing/20260910-00/?p=112689/&amp;commentid=144727#comment-144727"> how <code>ud0</code> and <code>ud1</code> could have parameters if they were undefined</a>? &#8220;Does it mean they were not completely undefined, just undocumented and not completely implemented?&#8221;</p> <p>The opcodes <code>ud0</code> and <code>ud1</code> have no definition, but that&#8217;s not the same as being architecturally an &#8220;undefined instruction&#8221;. They live in a purgatory where they were not assigned a meaning, but were also not officially declared to be meaningless.</p> <p>Originally, these byte sequences went through the instruction decoder and happened to slip through a few cracks before somebody finally noticed, &#8220;Wait a second, I don&#8217;t know how to execute this.&#8221;</p> <p>You can see this when you look at the instructions that are encoded as <code>00001111 11111xxx</code>, as of the Pentium III.</p> <table class="cp3" style="border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3"> <tbody> <tr> <th>Bits</th> <th>Bytes</th> <th>Opcode</th> <th>Operand 1</th> <th>Operand 2</th> <th>Meaning</th> </tr> <tr> <td><tt>00001111 11111000</tt></td> <td><tt>0F F8</tt></td> <td><tt>PSUBB</tt></td> <td>mm</td> <td>mm/m64</td> <td>Subtract packed bytes</td> </tr> <tr> <td><tt>00001111 11111001</tt></td> <td><tt>0F F9</tt></td> <td><tt>PSUBW</tt></td> <td>mm</td> <td>mm/m64</td> <td>Subtract packed words</td> </tr> <tr> <td><tt>00001111 11111010</tt></td> <td><tt>0F FA</tt></td> <td><tt>PSUBD</tt></td> <td>mm</td> <td>mm/m64</td> <td>Subtract packed dwords</td> </tr> <tr> <td><tt>00001111 11111011</tt></td> <td><tt>0F FB</tt></td> <td colspan="4" align="center">No meaning assigned</td> </tr> <tr> <td><tt>00001111 11111100</tt></td> <td><tt>0F FC</tt></td> <td><tt>PADDB</tt></td> <td>mm</td> <td>mm/m64</td> <td>Add packed bytes</td> </tr> <tr> <td><tt>00001111 11111101</tt></td> <td><tt>0F FD</tt></td> <td><tt>PADDW</tt></td> <td>mm</td> <td>mm/m64</td> <td>Add packed words</td> </tr> <tr> <td><tt>00001111 11111110</tt></td> <td><tt>0F FE</tt></td> <td><tt>PADDD</tt></td> <td>mm</td> <td>mm/m64</td> <td>Add packed dwords</td> </tr> <tr> <td><tt>00001111 11111111</tt></td> <td><tt>0F FF</tt></td> <td colspan="4" align="center">No meaning assigned</td> </tr> </tbody> </table> <p>The byte sequences <code>0F FB</code> and <code>0F FF</code> had yet to be assigned a meaning. You can see how the instruction decoder could take a shortcut and say, &#8220;Well, all the instructions in this range, or at least all the ones that I care about, take an mm registers and an mm/m64 operand, so I&#8217;ll just save myself some transistors and decode all of them with two parameters (mm, mm/m64).&#8221; And then after decoding, it would use bit 3 to decide whether to set up the arithmetic unit for an add or subtract, and it would use bits 0 and 1 to decide how to subdivide the bits into saturating units.</p> <p>And if you gave it a <code>0F FF</code>, it would be only in that last step that the decoder would realize &#8220;Oh dear, I don&#8217;t know what to do with a bit combination of <tt>11</tt>. I&#8217;ll raise an invalid opcode instruction.&#8221;</p> <p>The invalid opcode instruction got raised <i>after</i> the operands were parsed.</p> <p>You can see the trouble that <code>0F FF</code> created when those empty slots started to get filled in by the SSE instructions.</p> <table class="cp3" style="border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3"> <tbody> <tr> <th>Bits</th> <th>Bytes</th> <th>Opcode</th> <th>Operand 1</th> <th>Operand 2</th> <th>Meaning</th> </tr> <tr> <td><tt>00001111 11111000</tt></td> <td><tt>0F F8</tt></td> <td><tt>PSUBB</tt></td> <td>mm</td> <td>mm/m64</td> <td>Subtract packed bytes</td> </tr> <tr> <td><tt>00001111 11111001</tt></td> <td><tt>0F F9</tt></td> <td><tt>PSUBW</tt></td> <td>mm</td> <td>mm/m64</td> <td>Subtract packed words</td> </tr> <tr> <td><tt>00001111 11111010</tt></td> <td><tt>0F FA</tt></td> <td><tt>PSUBD</tt></td> <td>mm</td> <td>mm/m64</td> <td>Subtract packed dwords</td> </tr> <tr style="background-color: color(from currentcolor srgb r g b /15%);"> <td><tt>00001111 11111011</tt></td> <td><tt>0F FB</tt></td> <td><tt>PSUBQ</tt></td> <td>mm</td> <td>mm/m64</td> <td>Subtract packed qwords</td> </tr> <tr> <td><tt>00001111 11111100</tt></td> <td><tt>0F FC</tt></td> <td><tt>PADDB</tt></td> <td>mm</td> <td>mm/m64</td> <td>Add packed bytes</td> </tr> <tr> <td><tt>00001111 11111101</tt></td> <td><tt>0F FD</tt></td> <td><tt>PADDW</tt></td> <td>mm</td> <td>mm/m64</td> <td>Add packed words</td> </tr> <tr> <td><tt>00001111 11111110</tt></td> <td><tt>0F FE</tt></td> <td><tt>PADDD</tt></td> <td>mm</td> <td>mm/m64</td> <td>Add packed dwords</td> </tr> <tr style="background-color: color(from currentcolor srgb r g b /15%);"> <td><tt>00001111 11111111</tt></td> <td><tt>0F FF</tt></td> <td colspan="4" align="center">I want to put <tt>PADDQ</tt> here but I can&#8217;t</td> </tr> </tbody> </table> <p>the natural place to put the <code>PADDQ</code> instruction is <code>0F FF</code>, but people had already been using <code>0F FF</code> with the expectation that it raises an illegal instruction exception. Making it a valid instruction would break those programs, so Intel had to move <code>PADDQ</code> to the rather awkward location <code>0F D4</code>.</p> <p><b>Bonus chatter</b>: Undefined instructions with parameters are actually not uncommon. For example, on AArch64, <a title="The AArch64 processor (aka arm64), part 19: Miscellaneous instructions" href="https://devblogs.microsoft.com/oldnewthing/20220822-00/?p=107032"> there is a range of 65,536 instructions set aside as permanently undefined</a>, so the <code>udf</code> instruction takes a 16-bit immediate to specify which invalid opcode you want. The PDP-10 reserved opcode 000 as a permanently illegal instruction, and it carries a register and a memory address as parameters. (Because <i>all</i> PDP-10 instructions carry a register and a memory address as parameters.)</p> <p>There are also so-called &#8220;unofficial opcodes&#8221; which are instructions that are not part of the instruction set architecture, but for which people reverse-engineered a consistent behavior and began to rely on it. (The 6502 processor is <a title="How MOS 6502 Illegal Opcodes really work" href="https://www.pagetable.com/31"> well-known in nerd circles for having undergone this type of analysis</a>.) The <code>0F FF</code> is one of these &#8220;unofficial opcodes&#8221; that was popular enough that Intel felt pressure to maintain backward compatibility with it, even though it was never architecturally documented or supported.</p> <p><b>Bonus bonus chatter</b>: It appears that the mnemonic <code>ud1</code> was <a href="https://github.com/netwide-assembler/nasm/blob/nasm-0.98.x/CHANGES#L827-L831"> introduced by the nasm assembler</a>:</p> <pre>* Added the following new instructions: SYSENTER, SYSEXIT, FXSAVE, FXRSTOR, UD1, UD2 (the latter two are two opcodes that Intel guarantee will never be used; one of them is documented as UD2 in Intel documentation, the other one just as "Undefined Opcode" -- <span style="border: solid 1px currentcolor;">calling it UD1 seemed to make sense</span>.) </pre> <p>It seems obvious that <code>ud1</code> is also the name that Intel gave internally to that legacy instruction. Otherwise, there would be no need to call the new one <code>ud2</code>!</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20261007-00/?p=112759/">How can undefined opcodes &lt;CODE&gt;ud0&lt;/CODE&gt; and &lt;CODE&gt;ud1&lt;/CODE&gt; have parameters? How undefined were they?</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
Original Article

Similar Articles

Why is the x86 undefined instruction called ud2? Why 2?

The Old New Thing (Raymond Chen)

The article explains the history and reasoning behind the naming of the x86 undefined instruction 'ud2', tracing its evolution from unofficial opcodes to Intel's official implementation due to Hyrum's Law.

Why do we need ud2 when int 6 is right there?

The Old New Thing (Raymond Chen)

Raymond Chen 解释了为什么 x86 需要专用的 ud2 指令而不是用 int 6 来触发未定义指令异常:int 6 是陷阱(报告下一条指令地址)而 #UD 是故障(报告出错指令地址),且操作系统会禁止用户态调用 int 6 以免混淆 #UD 处理程序。

Who Is Vfmaddrnd231pd?

Hacker News Top

一篇博客调查 Intel SDM 手册中记载、但实际 CPU 上会触发 #UD 的幽灵指令 vfmaddrnd231pd 的来源之谜,推测 Intel 在最后一刻移除了该指令但未同步更新文档。