{"id":105295,"date":"2021-06-10T07:00:00","date_gmt":"2021-06-10T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105295"},"modified":"2021-06-10T06:31:20","modified_gmt":"2021-06-10T13:31:20","slug":"20210610-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210610-00\/?p=105295","title":{"rendered":"The ARM processor (Thumb-2), part 9: Sign and zero extension"},"content":{"rendered":"<p>I noted <a title=\"The ARM processor (Thumb-2), part 8: Bit shifting and bitfield access\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210609-00\/?p=105293\"> last time<\/a> that you could use the bitfield extraction instructions to do zero- and sign-extension of bytes and halfwords to words. But there are dedicated instructions for these operations which have smaller encodings if the source and destination registers are low.<\/p>\n<pre>    ; unsigned extend byte to word\r\n    uxtb    Rd, Rm      ; Rd = (uint8_t)Rm\r\n\r\n    ; signed extend byte to word\r\n    sxtb    Rd, Rm      ; Rd = (int8_t)Rm\r\n\r\n    ; unsigned extend halfword to word\r\n    uxth    Rd, Rm      ; Rd = (uint16_t)Rm\r\n\r\n    ; signed extend halfword to word\r\n    sxth    Rd, Rm      ; Rd = (int16_t)Rm\r\n<\/pre>\n<p>You can optionally apply a rotation to the second register so that you can extract a 8-bit or 16-bit value that sits along a byte boundary.<\/p>\n<pre>    ; unsigned\/signed extend byte to word with rotation\r\n    ; rotation must be a multiple of 8\r\n    uxtb    Rd, Rm, #rot ; Rd = (uint8_t)(Rm ROR #rot)\r\n    sxtb    Rd, Rm, #rot ; Rd = ( int8_t)(Rm ROR #rot)\r\n\r\n    ; unsigned\/signed extend halfword to word with rotation\r\n    ; rotation must be a multiple of 8\r\n    uxth    Rd, Rm, #rot ; Rd = (uint16_t)(Rm ROR #rot)\r\n    sxth    Rd, Rm, #rot ; Rd = ( int16_t)(Rm ROR #rot)\r\n<\/pre>\n<p>It&#8217;s kind of weird to apply a 24-bit rotation to extract a halfword, but you can do it if you want to.<\/p>\n<p>You can also zero-extend or sign-extend a word to a doubleword using instructions you already have available:<\/p>\n<pre>    ; zero-extend Rd to Rd\/R(d+1)\r\n    mov     R(d+1), #0          ; set to 0\r\n\r\n    ; sign-extend Rd to Rd\/R(d+1)\r\n    asrs    R(d+1), Rd, #31     ; copy sign bit to all bits\r\n<\/pre>\n<p>The trick is that a signed right-shift by 31 positions ends up filling the entire word with the sign bit. We use the S-version <code>ASRS<\/code> because it allows a compact 16-bit encoding if both the source and destination registers are low.<\/p>\n<p>The <code>ASR #31<\/code> trick can also be used in the <code>op2<\/code> of arithmetic or logical instructions.<\/p>\n<pre>    ; set r0 to zero if r1 is positive or zero\r\n    and     r0, r1, ASR #31\r\n<\/pre>\n<p>The trick here is that <code>r1, ASR #31<\/code> produces <code>0xFFFFFFFF<\/code> if <var>r1<\/var> is negative, but <code>0x00000000<\/code> if <var>r1<\/var> is positive or zero.<\/p>\n<p>In addition to the straight zero- and sign-extension operations, there are other instructions that combine the extension with another operation. Most of them are focused on multimedia scenarios, but the extend-and-add instructions are more general-purpose, and I have seen the compiler generate the versions with no rotation.<\/p>\n<pre>    ; zero\/sign extend and add byte with optional rotation\r\n    ; rotation must be a multiple of 8\r\n    uxtab   Rd, Rn, #rot        ; Rd = Rd + (uint8_t)(Rn ROR #rot)\r\n    sxtab   Rd, Rn, #rot        ; Rd = Rd + ( int8_t)(Rn ROR #rot)\r\n\r\n    ; zero\/sign extend and add halfword with optional rotation\r\n    ; rotation must be a multiple of 8\r\n    sxtah   Rd, Rn, #rot        ; Rd = Rd + ( int16_t)(Rn ROR #rot)\r\n    uxtah   Rd, Rn, #rot        ; Rd = Rd + (uint16_t)(Rn ROR #rot)\r\n<\/pre>\n<p>There&#8217;s another instruction that looks like it&#8217;d come in handy, particularly in Win32 user interface code that has to pack two 16-bit coordinates into a 32-bit integer, but I haven&#8217;t seen any compiler generate it:<\/p>\n<pre>    ; pack halfword bottom-and-top, or top-and-bottom\r\n    ; shift is optional\r\n    pkhbt   Rd, Rn, Rm, LSL #imm ; Rd = ((Rm LSL #imm) &lt;&lt; 16) | (uint16_t)Rn\r\n    pkhtb   Rd, Rn, Rm, ASR #imm ; Rd = (Rn &lt;&lt; 16) | (uint16_t)(Rm ASR #imm)\r\n<\/pre>\n<p>The bottom-and-top version puts the first input register in the bottom part of the output, and the second input parameter goes into the top part. The top-and-bottom version does it the other way. (The top-and-bottom instruction is not redundant because the barrel shifter can be applied only to the second input parameter.)<\/p>\n<p>When the compiler needs to do this, it generates two instructions:<\/p>\n<pre>    ; pack halfword bottom-and-top\r\n    uxth    r12, Rn                 ; r12 = (uint16_t)Rn\r\n    orr     Rd, r12, Rm, LSL #16    ; Rd = r12 | (Rm &lt;&lt; 16)\r\n                                    ;    = (uint16_t)Rn | (Rm &lt;&lt; 16)\r\n<\/pre>\n<p>Even if it didn&#8217;t want to use <code>PKHBT<\/code>, it could have used <code>BFI<\/code> to pack the values in a single instruction:<\/p>\n<pre>    ; pack halfword bottom-and-top (in place)\r\n    bfi     Rd, Rm, #16, #16        ; Rd[31:16] = Rm[15:0]\r\n<\/pre>\n<p>Maybe there&#8217;s some dirty secret about the <code>PKHBT<\/code> and <code>BFI<\/code> instructions that the compiler knows but I don&#8217;t.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Making small things bigger.<\/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-105295","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Making small things bigger.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105295","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=105295"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105295\/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=105295"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105295"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105295"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}