{"id":105314,"date":"2021-06-16T07:00:00","date_gmt":"2021-06-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105314"},"modified":"2021-06-17T07:36:24","modified_gmt":"2021-06-17T14:36:24","slug":"20210616-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210616-00\/?p=105314","title":{"rendered":"The ARM processor (Thumb-2), part 13: Trampolines"},"content":{"rendered":"<p>As we noted last time, <a title=\"The ARM processor (Thumb-2), part 12: Control transfer\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210615-00\/?p=105311\"> the relative branch instructions have a limited reach<\/a>. In particular, the <code>bl<\/code> instruction, which is used for intra-module direct calls, has a reach of around \u00b116MB. But what happens if the call target is too far away? Or if the function is a na\u00efvely-imported function?<\/p>\n<p>In the case of a faraway call target, the linker injects a trampoline, called a <i>veneer<\/i> in the ARM documentation.<\/p>\n<pre>    bl      toofar_trampoline\r\n...\r\n\r\ntoofar_trampoline:\r\n    mov     r12, #lo(|toofar|+1)\r\n    movt    r12, #hi(|toofar|+1)\r\n    bx      r12             ; jump to r12\r\n<\/pre>\n<p>The <var>r12<\/var> register, known as the <i>intraprocedure call<\/i> register, is a register that the linker is permitted to use for the purpose of generating trampolines and function prologues. From the compiler&#8217;s point of view, it is super-volatile: Any branch instruction could damage the <var>r12<\/var> register.<\/p>\n<p>In practice, the compiler doesn&#8217;t use <var>r12<\/var> for anything at all.<\/p>\n<p>In the case of a na\u00efvely-imported function, the actual call target is stored in the import address table, and the linker must generate a trampoline that jumps to the imported function:<\/p>\n<pre>    bl      imported_trampoline\r\n...\r\n\r\nimported_trampoline:\r\n    mov     r12, #lo(iat_imported)\r\n    movt    r12, #hi(iat_imported)\r\n    ldr     pc, [r12]\r\n<\/pre>\n<p>Here, we take advantage of the overly-uniform <var>pc<\/var> register: Loading a value into it acts as a jump instruction. It saves an instruction, because we don&#8217;t have to load the jump target into a register and then <code>BX<\/code> to it.<\/p>\n<p>Next time, we&#8217;ll look at a few miscellaneous instructions.<\/p>\n<p><b>Bonus chatter<\/b>: I don&#8217;t know why the linker prefers to use a <code>MOV<\/code> + <code>MOVT<\/code> instruction pair instead of a single <var>pc<\/var>-relative <code>LDR<\/code>. My guess is that it avoids memory latency.<\/p>\n<p><b>Bonus chatter 2<\/b>: You might think that trampolines can never be deployed for jumps within a function. However, that&#8217;s not true: Code motion due to profile-guided optimization can cause rarely-executed code blocks to be relocated to faraway locations in the module. The most likely case is that a relative short jump becomes long and has to be converted to a jump-to-a-jump. In rare cases, the destination could end up more than 16MB away, in which case you would need a full trampoline.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The overly-uniform program counter register strikes 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":[2],"class_list":["post-105314","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>The overly-uniform program counter register strikes again.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105314","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=105314"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105314\/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=105314"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105314"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105314"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}