{"id":101036,"date":"2019-02-07T23:00:00","date_gmt":"2019-02-08T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=101036"},"modified":"2019-03-18T11:18:28","modified_gmt":"2019-03-18T18:18:28","slug":"20190208-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190207-00\/?p=101036","title":{"rendered":"The Intel 80386, part 15: Common compiler-generated code sequences"},"content":{"rendered":"<p>The Microsoft compiler employs a few patterns in its code generation that you may want to become familiar with. <\/p>\n<p>As we saw earlier, a call to a <code>__thiscall<\/code> function passes the <code>this<\/code> pointer in the <var>ecx<\/var> register, with the remaining parameters passed on the stack. A typical calling sequence would go something like this: <\/p>\n<pre>\n;   p-&gt;Foo(x, 42);\n\n    push    42                          ; parameter 2\n    push    dword ptr [ebp-24h]         ; parameter 1\n    mov     ecx, dword ptr [ebp-20h]    ; \"this\" for call\n    call    CThing::Foo                 ; call the function directly\n<\/pre>\n<p>If the method is virtual, then there is a vtable lookup: <\/p>\n<pre>\n;   p-&gt;Foo(x, 42);\n\n    push    42                          ; parameter 2\n    push    dword ptr [ebp-24h]         ; parameter 1\n    mov     ecx, dword ptr [ebp-20h]    ; \"this\" for call\n    mov     eax, dword ptr [ecx]        ; fetch vtable\n    call    dword ptr [eax+10h]         ; call through the vtable\n<\/pre>\n<p>If the method uses <code>__stdcall<\/code> instead of <code>__thiscall<\/code> (typically because it is a COM method), then the <var>this<\/var> parameter is passed on the stack rather than in the <var>ecx<\/var> register. <\/p>\n<pre>\n;   p-&gt;Foo(x, 42);\n\n; non-virtual call\n    push    42                          ; parameter 2\n    push    dword ptr [ebp-24h]         ; parameter 1\n    push    dword ptr [ebp-20h]         ; \"this\" for call\n    call    CThing::Foo                 ; call the function directly\n\n; virtual call\n    push    42                          ; parameter 2\n    push    dword ptr [ebp-24h]         ; parameter 1\n    mov     ecx, dword ptr [ebp-20h]    ; \"this\" for call\n    push    ecx                         ; pass as stack parameter\n    mov     eax, dword ptr [ecx]        ; fetch vtable\n    call    dword ptr [eax+10h]         ; call through the vtable\n<\/pre>\n<p>The Microsoft compiler uses a jump table for dense <code>switch<\/code> statements, but it adds a level of indirection so that multiple cases that leads to the same target share the same jump entry. <\/p>\n<p>Consider the following fragment: <\/p>\n<pre>\n    switch (value)\n    {\n    case 2:\n    case 3:\n    case 5:\n    case 7:\n        printf(\"prime\");\n        break;\n\n   case 4:\n   case 9:\n        printf(\"perfect square\");\n        break;\n\n   default:\n        printf(\"I'm sure you're special\");\n        break;\n    }\n<\/pre>\n<p>The resulting code may look like this:<\/p>\n<pre>\n    mov     eax, dword ptr [ebp-30h]    ; load value\n    sub     eax, 2                      ; table starts with \"case 2\"\n    cmp     eax, 8                      ; table has 8 entries\n    jae     case_default                ; not in table, go to default case\n    movzx   eax, byte ptr [eax+level1]  ; get the index into the second table\n    jmp     dword ptr [eax+level2]      ; jump to handler\n\n    ...\n\nlevel2 dd  offset case_prime            ; slot 0 is for cases 2, 3, 5, and 7\n       dd  offset case_square           ; slot 1 is for cases 4 and 9\n       dd  offset case_default          ; slot 2 is for everybody else\nlevel1 db  0, 0, 1, 0, 2, 0, 2, 1       ; generate the slots\n;          2  3  4  5  6  7  8  9       ; corresponding cases\n<\/pre>\n<p>Adding a level of indirection allows the level-2 jump table to be smaller. The trade-off is that you have to pay for a level-1 jump table, but if there are a lot of duplicates (such as those created by all the missing cases that go to <code>default:<\/code>), the trade-off may be worth it. <\/p>\n<p>If you stare at the output and do some reverse-compiling, you can imagine that the compiler internally rewrote it as <\/p>\n<pre>\n    enum SwitchResult { Prime, PerfectSquare, Default };\n    static const unsigned char level1[] =\n      { Prime, Prime, PerfectSquare, Prime,\n        Default, Prime, Default, PerfectSquare };\n    unsigned int index1 = (unsigned)value - 2;\n    switch (index1 &lt; 8 ? level1[index1] : Default)\n    {\n    case Prime:\n        printf(\"prime\");\n        break;\n\n   case PerfectSquare:\n        printf(\"perfect square\");\n        break;\n\n   case Default:\n        printf(\"I'm sure you're special\");\n        break;\n\n    default:\n        __assume(0); \/\/ not reached\n    }\n<\/pre>\n<p><a HREF=\"http:\/\/devblogs.microsoft.com\/oldnewthing\/20190211-00\/?p=101046\">Next time<\/a>, we&#8217;ll wrap up our quick tour of the 80386 by walking through a simple function. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recognizing the patterns.<\/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-101036","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Recognizing the patterns.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/101036","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=101036"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/101036\/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=101036"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=101036"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=101036"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}