{"id":98415,"date":"2018-04-02T07:00:00","date_gmt":"2018-04-02T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=98415"},"modified":"2019-03-13T00:44:48","modified_gmt":"2019-03-13T07:44:48","slug":"20180402-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20180402-00\/?p=98415","title":{"rendered":"The MIPS R4000, part 1: Introduction"},"content":{"rendered":"<p>Continuing in the &#8220;Raymond introduces you to a CPU architecture that Windows once supported but no longer does&#8221; sort-of series, here we go with the MIPS R4000. <\/p>\n<p>The MIPS R4000 implements the MIPS III architecture. It is a 64-bit processor, but Windows NT used it in 32-bit mode. I&#8217;ll be focusing on the aspects of the processor relevant to debugging user-mode programs on Windows NT. This means that I may skip over various technical details on the assumption that the compiler knows what the rules are and won&#8217;t (intentionally) generate code that violates them. <\/p>\n<p>Throughout, I will say &#8220;MIPS&#8221; instead of &#8220;MIPS III architecture&#8221;. Some of the issues do not apply to later versions of the architecture family, but I am focusing on MIPS III since that&#8217;s what Windows NT used. <\/p>\n<p>The MIPS is a RISC-style load-store processor: The only operations you can perform with memory are load and store. There is no &#8220;add value to memory&#8221; instruction, for example. Each instruction is 32 bits wide, and the program counter must be on an exact multiple of 4. <\/p>\n<p>The processor can operate in either little-endian or big-endian mode; Windows NT uses little-endian mode, and even though some instructions change behavior depending on whether the processor is in big-endian or little-endian mode, I will discuss only the little-endian case. <\/p>\n<p>The architectural terminology for a 32-bit value is a <i>word<\/i> (w), and a 16-bit value is a <i>halfword<\/i> (h). There&#8217;s also <i>doubleword<\/i> (d) for 64-bit values, but we won&#8217;t see it here because we are focusing on the 32-bit mode of the processor. <\/p>\n<p>The MIPS has 32 general-purpose integer registers, formally known as registers <var>$0<\/var> through <var>$31<\/var>, but which conventionally go by these names: <\/p>\n<table BORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"3\" STYLE=\"border: solid 1px black;border-collapse: collapse\">\n<tr>\n<th>Register<\/th>\n<th>Mnemonic<\/th>\n<th>Meaning<\/th>\n<th>Preserved?<\/th>\n<th>Notes<\/th>\n<\/tr>\n<tr>\n<td><var>$0<\/var><\/td>\n<td><var>zero<\/var><\/td>\n<td>reads as zero<\/td>\n<td>Immutable<\/td>\n<td>Writes are ignored<\/td>\n<\/tr>\n<tr>\n<td><var>$1<\/var><\/td>\n<td><var>at<\/var><\/td>\n<td>assembler temporary<\/td>\n<td>Volatile<\/td>\n<td>Helper for synthesized instructions<\/td>\n<\/tr>\n<tr>\n<td><var>$2<\/var><\/td>\n<td><var>v0<\/var><\/td>\n<td>value<\/td>\n<td>No<\/td>\n<td>On function exit, contains the return value<\/td>\n<\/tr>\n<tr>\n<td><var>$3<\/var><\/td>\n<td><var>v1<\/var><\/td>\n<td>value<\/td>\n<td>No<\/td>\n<td>High 32 bits of return value (for 64-bit values)<\/td>\n<\/tr>\n<tr>\n<td><var>$4<\/var>&hellip;<var>$7<\/var><\/td>\n<td><var>a0<\/var>&hellip;<var>a3<\/var><\/td>\n<td>argument<\/td>\n<td>No<\/td>\n<td>On function entry, contains function parameters<\/td>\n<\/tr>\n<tr>\n<td><var>$8<\/var>&hellip;<var>$15<\/var><\/td>\n<td><var>t0<\/var>&hellip;<var>t7<\/var><\/td>\n<td>temporary<\/td>\n<td>No<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><var>$16<\/var>&hellip;<var>$23<\/var><\/td>\n<td><var>s0<\/var>&hellip;<var>s7<\/var><\/td>\n<td>saved<\/td>\n<td>Yes<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><var>$24<\/var>&hellip;<var>$25<\/var><\/td>\n<td><var>t8<\/var>&hellip;<var>t9<\/var><\/td>\n<td>temporary<\/td>\n<td>No<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><var>$26<\/var>&hellip;<var>$27<\/var><\/td>\n<td><var>k0<\/var>&hellip;<var>k1<\/var><\/td>\n<td>kernel<\/td>\n<td>No access<\/td>\n<td>Reserved for kernel use<\/td>\n<\/tr>\n<tr>\n<td><var>$28<\/var><\/td>\n<td><var>gp<\/var><\/td>\n<td>global pointer<\/td>\n<td>Yes<\/td>\n<td>Not used by 32-bit code<\/td>\n<\/tr>\n<tr>\n<td><var>$29<\/var><\/td>\n<td><var>sp<\/var><\/td>\n<td>stack pointer<\/td>\n<td>Yes<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><var>$30<\/var><\/td>\n<td><var>s8<\/var><\/td>\n<td>frame pointer<\/td>\n<td>Yes<\/td>\n<td>For functions with variable-sized stacks<\/td>\n<\/tr>\n<tr>\n<td><var>$31<\/var><\/td>\n<td><var>ra<\/var><\/td>\n<td>return address<\/td>\n<td>Maybe<\/td>\n<td><\/td>\n<\/tr>\n<\/table>\n<p>The <var>zero<\/var> register reads as zero, and writes to it are ignored. <\/p>\n<p>The <var>k0<\/var> and <var>k1<\/var> registers are reserved for kernel use, and no well-written user-mode program will use them.&sup1; <\/p>\n<p>Win32 requires that the <var>sp<\/var> and <var>s8<\/var> registers be used for their stated purpose throughout the entire function. If a function does not have a variable-sized stack frame, then it can use <var>s8<\/var> for any purpose (which is why the disassembler calls it <var>s8<\/var> instead of <var>fp<\/var>, I guess). And since 32-bit code doesn&#8217;t ascribe special meaning to <var>gp<\/var>, then it too can be used for any purpose, provided its value is preserved across the call. In practice the Microsoft compiler merely avoids the <var>gp<\/var> register completely, and it uses the <var>s8<\/var> register only as a frame pointer. <\/p>\n<p>The stack is always aligned on an 8-byte boundary, and there is no <a HREF=\"https:\/\/en.wikipedia.org\/wiki\/Red_zone_(computing)\">red zone<\/a>. <\/p>\n<p>Some registers have stated purposes only at entry to a function or exit from a function. When not at the function boundary, those registers may be used for any purpose. <\/p>\n<p>Register marked with &#8220;Yes&#8221; in the &#8220;Preserved&#8221; column must be preserved across the call; those marked &#8220;No&#8221; do not. <\/p>\n<p>The <var>ra<\/var> register is marked &#8220;Maybe&#8221; because you don&#8217;t normally need to preserve it. However, if you are a leaf function that does not modify any preserved registers (not even <var>sp<\/var>), then you can skip the generation of unwind codes for the leaf function, but you must keep the return address in <var>ra<\/var> for the duration of your function so that the operating system can unwind out of the function should an exception occur. (Special rules for lightweight leaf functions also exist for <a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/\">Itanium<\/a>, <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20170807-00\/?p=96766\">Alpha AXP<\/a>, and x64.) <\/p>\n<p>The <var>at<\/var> register is volatile because the assembler can use it for various invisible purposes, primarily for synthesizing missing instructions. We&#8217;ll see examples of this as we go. <\/p>\n<p>There are also two special-purpose integer registers, called <var>HI<\/var> and <var>LO<\/var>. These are used by multiplication and division instructions, and we&#8217;ll cover them when we get to multiplication and division. <\/p>\n<p>There are 32 single-precision (32-bit) floating point registers, which can be paired up to form 16 double-precision (64-bit) floating point registers. When a pair is used to operate on a single-precision value, the lower-numbered register holds the value, and the higher-numbered register is not used. (Indeed, the value in the higher-numbered register will be garbage.) So I guess you really have just 16 single-precision floating point registers, since the odd-numbered ones are basically useless. <\/p>\n<table BORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"3\" STYLE=\"border: solid 1px black;border-collapse: collapse\">\n<tr>\n<th>Register(s)<\/th>\n<th>Meaning<\/th>\n<th>Preserved?<\/th>\n<th>Notes<\/th>\n<\/tr>\n<tr>\n<td><var>$f0<\/var>\/<var>$f1<\/var><\/td>\n<td>return value<\/td>\n<td>No<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><var>$f2<\/var>\/<var>$f3<\/var><\/td>\n<td>second return value<\/td>\n<td>No<\/td>\n<td>For imaginary component of complex number.<\/td>\n<\/tr>\n<tr>\n<td><var>$f4<\/var>\/<var>$f5<\/var>&hellip;<var>$f10<\/var>\/<var>$f11<\/var><\/td>\n<td>temporary<\/td>\n<td>No<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><var>$f12<\/var>\/<var>$f13<\/var>&hellip;<var>$f14<\/var>\/<var>$f15<\/var><\/td>\n<td>arguments<\/td>\n<td>No<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><var>$f16<\/var>\/<var>$f17<\/var>&hellip;<var>$f18<\/var>\/<var>$f19<\/var><\/td>\n<td>temporary<\/td>\n<td>No<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><var>$f20<\/var>\/<var>$f21<\/var>&hellip;<var>$f30<\/var>\/<var>$f31<\/var><\/td>\n<td>saved<\/td>\n<td>Yes<\/td>\n<td><\/td>\n<\/tr>\n<\/table>\n<p><p>Floating point support is optional. If not supported, floating point instructions will trap into the kernel, and the kernel is expected to emulate the instruction. <\/p>\n<p>There is not a lot of floating point in typical systems programming, so I won&#8217;t cover it except when discussing the calling convention later. <\/p>\n<p>There is no flags register. Hopefully you don&#8217;t find this weird any more, seeing as <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20170811-00\/?p=96805\">we already encountered this with the Alpha AXP<\/a>. <\/p>\n<p>The 32-bit address space is split down the middle between user-mode and kernel-mode. The kernel-mode space is further split: Half of the kernel-mode address space is dedicated to mapping physical addresses (the lowest 512<a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20090611-00\/?p=17933\">MB<\/a>&sup2; gets mapped twice, once cached and once uncached), leaving only 1GB for the operating system. This partitioning is architectural; you don&#8217;t get a choice in the matter. <\/p>\n<p>Okay, we&#8217;ll begin next time by looking at 32-bit integer calculations. <\/p>\n<p>&sup1; I know you&#8217;re wondering what happens if poorly-written user-mode code tries to use them. The answer is that user-mode code can modify the register all it wants, but the value read back may not be equal to value last written. As far as user mode is concerned, it&#8217;s basically a black hole register that reads as garbage. This makes it even more useless than the <var>zero<\/var> register, which is a black hole register that at least reads as zero. (Internally, the registers are used by kernel mode as scratch variables during interrupt and exception handling.) <\/p>\n<p>&sup2; I guess they figured that if you had more than 512MB of RAM, you&#8217;d have switched to a 64-bit operating system. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here we go again.<\/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":[25],"class_list":["post-98415","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Here we go again.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/98415","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=98415"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/98415\/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=98415"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=98415"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=98415"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}