{"id":107077,"date":"2022-08-31T07:00:00","date_gmt":"2022-08-31T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107077"},"modified":"2022-09-10T06:29:54","modified_gmt":"2022-09-10T13:29:54","slug":"20220831-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220831-00\/?p=107077","title":{"rendered":"The x86-64 processor (aka amd64, x64): Whirlwind tour"},"content":{"rendered":"<p>I figure I&#8217;d tidy up the processor overview series by covering the last\u00b9 processor on my list of &#8220;processors Windows has supported in its history,&#8221; namely, the x86-64. Other names for this architecture are amd64 (because AMD invented it) and x64 (which is super-confusing because it doesn&#8217;t correspond with x86, a common nickname for the x86-32).<\/p>\n<p>This is going to be a quick overview because the x86-64 is a natural extension of the i386, <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190120-00\/?p=100745\"> which we covered some time ago<\/a>. I&#8217;ll just highlight the differences.<\/p>\n<p>Each existing 32-bit general-purpose register has been extended from 32 bits to 64. The name of the 64-bit register is based on the name of the 32-bit register, but with the leading <var>e<\/var> changed to a leading <var>r<\/var>. Eight new 64-bit registers were introduced, bring the total to 16. Instead of giving quirky names to the new registers, they are just numbered: <var>r8<\/var> through <var>r15<\/var>. To match the existing classic registers, the new registers also have aliases for referring to partial registers, and partial register aliases were invented for some of the classic registers that lacked them.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th rowspan=\"2\">Register<\/th>\n<th colspan=\"4\">Aliases<\/th>\n<th rowspan=\"2\">Preserved?<\/th>\n<th rowspan=\"2\">Notes<\/th>\n<\/tr>\n<tr>\n<th>Bits 31:0<\/th>\n<th>Bits 15:0<\/th>\n<th>Bits 15:8<\/th>\n<th>Bits 7:0<\/th>\n<\/tr>\n<tr>\n<td><var>rax<\/var><\/td>\n<td><var>eax<\/var><\/td>\n<td><var>ax<\/var><\/td>\n<td><var>ah<\/var><\/td>\n<td><var>al<\/var><\/td>\n<td>No<\/td>\n<td style=\"text-align: left;\">Return value<\/td>\n<\/tr>\n<tr>\n<td><var>rbx<\/var><\/td>\n<td><var>ebx<\/var><\/td>\n<td><var>bx<\/var><\/td>\n<td><var>bh<\/var><\/td>\n<td><var>bl<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><var>rcx<\/var><\/td>\n<td><var>ecx<\/var><\/td>\n<td><var>cx<\/var><\/td>\n<td><var>ch<\/var><\/td>\n<td><var>cl<\/var><\/td>\n<td>No<\/td>\n<td style=\"text-align: left;\">Parameter 1<\/td>\n<\/tr>\n<tr>\n<td><var>rdx<\/var><\/td>\n<td><var>edx<\/var><\/td>\n<td><var>dx<\/var><\/td>\n<td><var>dh<\/var><\/td>\n<td><var>dl<\/var><\/td>\n<td>No<\/td>\n<td style=\"text-align: left;\">Parameter 2<\/td>\n<\/tr>\n<tr>\n<td><var>rsi<\/var><\/td>\n<td><var>esi<\/var><\/td>\n<td><var>si<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>sil<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><var>rdi<\/var><\/td>\n<td><var>edi<\/var><\/td>\n<td><var>di<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>dil<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><var>rsp<\/var><\/td>\n<td><var>esp<\/var><\/td>\n<td><var>sp<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>spl<\/var><\/td>\n<td>Yes<\/td>\n<td style=\"text-align: left;\">Stack pointer<\/td>\n<\/tr>\n<tr>\n<td><var>rbp<\/var><\/td>\n<td><var>ebp<\/var><\/td>\n<td><var>bp<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>bpl<\/var><\/td>\n<td>Yes<\/td>\n<td style=\"text-align: left;\">Frame pointer<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r8<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r8d<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r8w<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r8b<\/var><\/td>\n<td>No<\/td>\n<td style=\"text-align: left;\">Parameter 3<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r9<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r9d<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r9w<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r9b<\/var><\/td>\n<td>No<\/td>\n<td style=\"text-align: left;\">Parameter 4<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r10<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r10d<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r10w<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r10b<\/var><\/td>\n<td>No<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r11<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r11d<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r11w<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r11b<\/var><\/td>\n<td>No<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r12<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r12d<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r12w<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r12b<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r13<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r13d<var><\/var><\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r13w<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r13b<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r14<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r14d<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r14w<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r14b<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r15<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r15d<\/var><\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r15w<\/var><\/td>\n<td>&nbsp;<\/td>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>r15b<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The <var>eip<\/var> and <var>eflags<\/var> registers are correspondingly expanded to 64-bit registers <var>rip<\/var> and <var>rflags<\/var>.<\/p>\n<p>Additional restrictions have been imposed on the use of the <var>ah<\/var>, <var>bh<\/var>, <var>ch<\/var>, and <var>dh<\/var> registers. The details aren&#8217;t important for reading code, so I won&#8217;t bother digging into them.<\/p>\n<p>Windows requires that the stack be 16-byte aligned at function call boundaries, and there is no red zone. Calling a function pushes the 8-byte return address onto the stack, so on entry to a function, the stack is misaligned. Functions typically realign the stack in their prologue.<\/p>\n<p>The old 8087-based floating point registers are not used.\u00b2 Instead, the SIMD XMM registers are used for floating point calculations. These registers are 128 bits wide and can be viewed as four single-precision floating point values or as two double-precision floating point values. When used to pass parameters or return floating point values, only the bottom lane is used.<\/p>\n<p>Eight more XMM registers have been added, bringing the total to 16.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Register<\/th>\n<th>Preserved?<\/th>\n<th>Notes<\/th>\n<\/tr>\n<tr>\n<td><var>XMM0<\/var><\/td>\n<td>No<\/td>\n<td style=\"text-align: left;\">Parameter 1 and return value<\/td>\n<\/tr>\n<tr>\n<td><var>XMM1<\/var><\/td>\n<td>No<\/td>\n<td style=\"text-align: left;\">Parameter 2 and second return value<\/td>\n<\/tr>\n<tr>\n<td><var>XMM2<\/var><\/td>\n<td>No<\/td>\n<td style=\"text-align: left;\">Parameter 3<\/td>\n<\/tr>\n<tr>\n<td><var>XMM3<\/var><\/td>\n<td>No<\/td>\n<td style=\"text-align: left;\">Parameter 4<\/td>\n<\/tr>\n<tr>\n<td><var>XMM4<\/var><\/td>\n<td>No<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><var>XMM5<\/var><\/td>\n<td>No<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><var>XMM6<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><var>XMM7<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>XMM8<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>XMM9<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>XMM10<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>XMM11<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>XMM12<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>XMM13<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>XMM14<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"color: #383d41; background-color: #d6d8db;\"><var>XMM15<\/var><\/td>\n<td>Yes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><b>Calling convention<\/b><\/p>\n<p>The calling convention is register-based for the first four parameters, with remaining parameters on the stack. In practice, the stack-based parameters are not <code>push<\/code>&#8216;d, but rather the values are <code>mov<\/code>&#8216;d into the preallocated stack space.<\/p>\n<p>For register-based parameters, integer parameters go into the general-purpose registers and floating point parameters go into the floating point registers. When a register is used to hold a parameter, its counterpart register goes unused. For example, a function that takes an integer and a double will pass the integer in <var>rcx<\/var> and the double in <var>xmm1<\/var>.<\/p>\n<p>There are always 4 \u00d7 8 = 32 bytes of home space for the register-based parameters, even if the function has fewer than four formal parameters. (If this bothers you, then you can reinterpret the home space as a 32-byte red zone that resides <var>above<\/var> the return address.)<\/p>\n<p>Integer return values up to 64 bits go into <var>rax<\/var> If the return value is a 128-bit value, then the <var>rdx<\/var> register holds the upper 64 bits. Floating point return values are returned in <var>xmm0<\/var>.<\/p>\n<p>The caller is responsible for cleaning the stack. In practice, the caller does not clean the stack after every call, but rather preallocates the stack space in the prologue, reuses the stack space for multiple calls, and then cleans it all up in the epilogue.<\/p>\n<p>Exception handling is done by unwind tables, not by threading exception handlers through the stack at runtime.<\/p>\n<p><b>Partial registers<\/b><\/p>\n<p>When a 32-bit partial register is the destination of an operation, the upper 32 bits are set to zero. For example, consider<\/p>\n<pre>    add     eax, ecx\r\n<\/pre>\n<p>On the 32-bit 80386, this adds the value of <var>ecx<\/var> to <var>eax<\/var> and puts the result back into <var>eax<\/var>. On x86-64, this performs the same calculation, but since the destination is the 32-bit partial register <var>eax<\/var>, the operation also zeroes out the upper 32 bits of <var>rax<\/var>.<\/p>\n<p>Another way of looking at this is that writes to 32-bit partial registers are <var>zero-extended<\/var> to 64-bit values.\u00b3<\/p>\n<p>Note, however, that operations on 16-bit and 8-bit partial registers leave the unused bits unchanged.<\/p>\n<p><b>Addressing modes<\/b><\/p>\n<p>The 32-bit addressing modes carry over to 64-bit, with these exceptions:<\/p>\n<ul>\n<li>Absolute addressing mode is limited to signed 32-bit addresses.<\/li>\n<li>There is a new <var>rip<\/var>-relative addressing mode.<\/li>\n<\/ul>\n<p>The offsets in the memory addressing modes are 32-bit <a title=\"Adventures in application compatibility: The case of the wild instruction pointer that, upon closer inspection, might not be so wild after all\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210917-00\/?p=105704\"> signed<\/a> values, for a reach of \u00b12GB.<\/p>\n<p>The <var>rip<\/var>-relative addressing mode greatly reduces the number of fixups required to relocate a module. The enormous \u00b12GB reach means that any reasonably-sized module can use it to access all of its static data, be it a read-only table embedded in the code segment or read-write data in the data segment.<\/p>\n<p>The disassembler automatically performs the necessary calculations to convert the <var>rip<\/var>-relative address to an absolute one at disassembly time, so you are unlikely even to realize that anything has changed.<\/p>\n<p><b>Immediates<\/b><\/p>\n<p>In general, immediates are capped at 32 bits. The exception is that you can use a 64-bit immediate in the <code>mov reg, imm64<\/code> instruction.<\/p>\n<p><b>Segments<\/b><\/p>\n<p>Segmentation is architecturally dead. The processor is always in flat mode. The <var>fs<\/var> and <var>gs<\/var> selectors have been repurposed as two additional registers that add an operating-system-defined value to the effective address.<\/p>\n<pre>    mov   rax, qword ptr gs:[rcx*8+1480h]\r\n<\/pre>\n<p>The <i>base address<\/i> assigned to the <var>gs<\/var> register is added to the effective address <var>rcx * 8 + 0x1480<\/var>, producing a final address that is the target of the memory operation.<\/p>\n<p>Windows sets the <var>gs<\/var> register&#8217;s base address to a block of per-thread data. During context switches, the base address of the <var>gs<\/var> register is updated to point to the per-thread data of the incoming thread. The <var>fs<\/var> register has not yet been assigned a meaning and should not be used.\u2074 The Windows ABI forbids modifying either of these segment registers.<\/p>\n<p><b>Instruction set changes<\/b><\/p>\n<p>Some rarely-used instructions have been removed, primarily the binary-coded decimal instructions, <code>BOUND<\/code>, and <code>PUSHAD<\/code>\/<code>POPAD<\/code> instructions.<\/p>\n<p>New instructions for dealing with 64-bit registers:<\/p>\n<pre>    ; sign-extend 32-bit to 64-bit\r\n    movsxd  r64, r32\/m32\r\n<\/pre>\n<p>There is no need for a zero-extend instruction because operations on 32-bit registers automatically zero-extend to 64-bit values, so if the value was the result of a calculation, you probably got the zero-extended value anyeway. If you want to wipe out the top 32 bits of an existing 64-bit value, you could do<\/p>\n<pre>    ; zero-extend 32-bit to 64-bit\r\n    mov     r32, r32\r\n<\/pre>\n<p>This can result in some odd-looking instructions like<\/p>\n<pre>    mov     eax, eax        ; zero-extend eax to rax\r\n<\/pre>\n<p>On its face, the instruction looks pointless, but we&#8217;re performing for the zero-extending side effect.\u2075<\/p>\n<p>There are also specialized instruction for certain sign-extending scenarios:<\/p>\n<pre>    cwqe                    ; sign-extend eax to rax\r\n    cqd                     ; sign-extend rax to rdx:rax\r\n<\/pre>\n<p><b>Lightweight leaf functions and exception handling<\/b><\/p>\n<p>A lightweight leaf function is one which can perform all of its work using only non-preserved registers, the inbound parameter home space, and stack space occupied by stack-based inbound parameters (if any). Preserved registers and the stack pointer must remain unchanged for the entire lifetime of the function, and the return address must remain at the top of the stack.<\/p>\n<p>The inability to move the stack pointer means that the stack pointer is <i>not<\/i> at a multiple of 16 for the lifetime of a lightweight leaf function.<\/p>\n<p>The x86-64 ABI abandons the stack-based exception handling model of its 32-bit older brother and joins the RISC crowd by using table-based exception handling. With the exception of lightweight leaf functions, all functions must declare unwind codes that allow the exception unwinder to restore registers from the stack and find the return address. Any function that does not have unwind codes is assumed to be a lightweight leaf function.<\/p>\n<p><b>Annotated disassembly<\/b><\/p>\n<p>I&#8217;ll defer to the <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows-hardware\/drivers\/debugger\/annotated-x64-disassembly\"> existing documentation<\/a> (which I wrote).<\/p>\n<p><b>Encoding notes<\/b><\/p>\n<p>Instructions that operate on the classic 32-bit or 8-bit registers tend to have the most compact encodings. Using any of the new registers (<var>r8<\/var> through <var>r15<\/var>, or <var>xmm8<\/var> through <var>xmm15<\/var>, or the new aliases <var>sil<\/var>, <var>dil<\/var>, <var>spl<\/var> or <var>bpl<\/var>) typically requires a one-byte prefix. An instruction that operates on word-sized data typically incurs an additional byte encoding. And fancy addressing modes (involving scaling or multiple registers contributing to the effective address) also require yet another byte for the encoding.<\/p>\n<p>I&#8217;m not sure how aggressively the compiler allocates registers and chooses instructions which have compact encodings. It certainly didn&#8217;t stand out to me.<\/p>\n<p><b>Bonus reading<\/b>: <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/x64-software-conventions?view=msvc-160\"> x64 software conventions<\/a>.<\/p>\n<p><b>Bonus chatter<\/b>: Now that I&#8217;ve exhausted my list of processors that Windows has supported over the years, I&#8217;ll have to start branching out into other processors. I&#8217;m open to suggestions. Though I probably won&#8217;t be as detailed as these processor overviews have been, since the original goal of these overviews was to give you enough information to get started debugging on Windows. For other processors, I&#8217;ll probably just focus on the one or two things that make them interesting, like SPARC register windows, or 68000&#8217;s separate data and address registers.<\/p>\n<p>\u00b9 Early versions of Windows CE allegedly supported the StrongARM and possibly even M32R and other architectures, but I can&#8217;t find any binaries for those versions, so I have nothing to investigate.<\/p>\n<p>\u00b2 They are still physically present and usable, but in practice, nobody uses them,\u2076 and they are not part of the calling convention.<\/p>\n<p>\u00b3 I strongly suspect this design decision was made to avoid introduce spurious register dependencies due to partial register operations.<\/p>\n<p>\u2074 On x86-32, the <var>fs<\/var> register is used to access the per-thread data. Why did Windows switch to using <var>gs<\/var> on x86-64? One theory is that there is a special instruction on x86-64 called <code>SWAPGS<\/code> that lets the kernel exchange the <var>gs<\/var> base address with another internal register. This instruction is used on transitions to and from user mode, so the kernel can quickly switch from user-mode thread data to kernel-mode thread data on entry and to switch it back on exit. No such courtesy instruction exists for the <var>fs<\/var> register. Another theory is that <code>fs<\/code> is reserved for the 32-bit emulation layer.<\/p>\n<p>\u2075 It also means that the x86-32 pun of interpreting <code>nop<\/code> as <code>xchg eax, eax<\/code> does not work in x86-64. The self-exchange zeroes out the high 32 bits as a side effect. The Windows debugger doesn&#8217;t realize this, and if you ask it to assemble <code>xchg eax, eax<\/code>, it encodes it as <code>90<\/code>, using the one-byte encoding of <code>xchg eax, r32<\/code>, unaware that this doesn&#8217;t work if the other register is <i>also<\/i> <var>eax<\/var>. The correct encoding of <code>xchg eax, eax<\/code> is <code>87 c0<\/code>, using the larger two-byte encoding.<\/p>\n<p>\u2076 Apparently, gcc and clang do use them for the 80-bit floating point <code>long double<\/code> type.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Pretty much a 64-bit-ification of the i386.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[2],"class_list":["post-107077","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Pretty much a 64-bit-ification of the i386.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107077","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=107077"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107077\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=107077"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107077"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107077"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}