{"id":106912,"date":"2022-07-28T07:00:00","date_gmt":"2022-07-28T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106912"},"modified":"2022-11-29T10:18:37","modified_gmt":"2022-11-29T18:18:37","slug":"20220728-00-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220728-00\/?p=106912\/","title":{"rendered":"The AArch64 processor (aka arm64), part 3: Addressing modes"},"content":{"rendered":"<p>Every addressing mode on AArch64 begins with a base register, which can be any numbered register or <var>sp<\/var>. On top of that, you can add various sprinkles.<\/p>\n<p>In the discussion, the term <var>size<\/var> refers to the size of the data being transferred, and <var>sizeshift<\/var> is the base-2 logarithm of that size:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Operand<\/th>\n<th>size<\/th>\n<th>sizeshift<\/th>\n<\/tr>\n<tr>\n<td>byte<\/td>\n<td style=\"text-align: center;\">1<\/td>\n<td style=\"text-align: center;\">0<\/td>\n<\/tr>\n<tr>\n<td>halfword<\/td>\n<td style=\"text-align: center;\">2<\/td>\n<td style=\"text-align: center;\">1<\/td>\n<\/tr>\n<tr>\n<td>word<\/td>\n<td style=\"text-align: center;\">4<\/td>\n<td style=\"text-align: center;\">2<\/td>\n<\/tr>\n<tr>\n<td>doubleword<\/td>\n<td style=\"text-align: center;\">8<\/td>\n<td style=\"text-align: center;\">3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>For illustration purposes, I&#8217;ll use the <code>LDR<\/code> instruction, which loads a register.<\/p>\n<p><b>Register indirect with offset<\/b><\/p>\n<pre>    ldr     x0, [Xn\/sp, #imm]\r\n    ldr     x0, [Xn\/sp]         ; #0 is implied if omitted\r\n<\/pre>\n<p>This loads a value from the address calculated by adding the immediate to the value in the <var>Xn<\/var> register or <var>sp<\/var>.<\/p>\n<p>The immediate can be a signed integer offset in the range \u2212256 to +255, or an unsigned multiple of the operand size up to 4095 \u00d7 <var>size<\/var>.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Size<\/th>\n<th>Signed reach<\/th>\n<th>Unsigned reach<\/th>\n<\/tr>\n<tr>\n<td>byte<\/td>\n<td rowspan=\"4\">\u2212256 to +255<\/td>\n<td style=\"text-align: center;\">0 to \u20074095<\/td>\n<\/tr>\n<tr>\n<td>halfword<\/td>\n<td style=\"text-align: center;\">0 to \u20078190<\/td>\n<\/tr>\n<tr>\n<td>word<\/td>\n<td style=\"text-align: center;\">0 to 16380<\/td>\n<\/tr>\n<tr>\n<td>doubleword<\/td>\n<td style=\"text-align: center;\">0 to 32760<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><b>Register indirect with pre-increment<\/b><\/p>\n<p>Putting an exclamation point after the close-bracket means that the calculated effective address is written back to the base register.<\/p>\n<pre>    ; load from (Xn\/sp + imm)\r\n    ; then set Xn\/sp = Xn\/sp + imm\r\n    ldr     x0, [Xn\/sp, #imm]!\r\n<\/pre>\n<p><b>Register indirect with post-increment<\/b><\/p>\n<p>Putting the immediate offset outside the close-bracket means that the base register is adjusted <i>after<\/i> the memory is read.<\/p>\n<pre>    ; load from Xn\/sp\r\n    ; then set Xn\/sp = Xn\/sp + imm\r\n    ldr     x0, [Xn\/sp], #imm\r\n<\/pre>\n<p><b>PC-relative with offset<\/b><\/p>\n<pre>    ldr     x0, [pc, #imm]\r\n<\/pre>\n<p>The PC-relative addressing mode reads memory from a position given as a signed offset from the current instruction. The offset must be a multiple of 4, and the reach is \u00b11MB.<\/p>\n<p>This instruction is typically used to load large constants from memory, and the disassembler does the math for you and decodes it as<\/p>\n<pre>    ldr     x0, =imm\r\n<\/pre>\n<p>by calculating the effective address and fetching the value from that location.<\/p>\n<p>The assembler typically generates literals into the code segment between subroutines, and the large reach of this instruction means that the need to dump literals prematurely is largely a thing of the past. (By comparison, AArch32&#8217;s PC-relative addressing mode had a reach of only \u00b14KB, so it was not uncommon to dump literals in the middle of a function.)<\/p>\n<p><b>Register indirect with index<\/b><\/p>\n<pre>    ldr     x0, [Xn\/sp, Rn\/zr, extend]\r\n<\/pre>\n<p>This addressing mode takes the <var>Rn\/zr<\/var>, transforms it according to the extended register operation <i>extend<\/i>, and adds the result to the <var>Xn\/sp<\/var> register to form the final address.<\/p>\n<p>For memory access, the following extended register operations are available:<\/p>\n<ul>\n<li><code>UXTW<\/code><\/li>\n<li><code>UXTX<\/code> (aka <code>LSL<\/code>)<\/li>\n<li><code>SXTW<\/code><\/li>\n<li><code>SXTX<\/code><\/li>\n<\/ul>\n<p>The only acceptable shifts are zero and <var>sizeshift<\/var>. This means that the index register can be treated either as a byte offset or as an element index, where the element is the size of the operand. For example, if you are loading a halfword, then the index register is either a byte offset of a halfword index.<\/p>\n<p>Writing out all the possibilities produces these possible extended registers:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th style=\"border: solid 1px gray;\">Extended<\/th>\n<th style=\"border: solid 1px gray;\">Effective address<\/th>\n<th style=\"border: solid 1px gray;\" colspan=\"3\">Index format<\/th>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\"><code>[a, b, UXTW #0]<\/code><br \/>\n<code>[a, b, UXTW]<\/code><\/td>\n<td style=\"border: solid 1px gray;\"><code>a + (uint32_t)b<\/code><\/td>\n<td style=\"border: solid 1px gray; border-right: none;\">32-bit<\/td>\n<td style=\"border: 1px gray; border-style: solid none; text-align: right;\">unsigned<\/td>\n<td style=\"border: solid 1px gray; border-left: none; text-align: right;\">byte offset.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\"><code>[a, b, UXTW #sizeshift]<\/code><\/td>\n<td style=\"border: solid 1px gray;\"><code>a + (uint32_t)b * size<\/code><\/td>\n<td style=\"border: solid 1px gray; border-right: none;\">32-bit<\/td>\n<td style=\"border: 1px gray; border-style: solid none; text-align: right;\">unsigned<\/td>\n<td style=\"border: solid 1px gray; border-left: none; text-align: right;\">element offset.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\"><code>[a, b, SXTW #0]<\/code><br \/>\n<code>[a, b, SXTW]<\/code><\/td>\n<td style=\"border: solid 1px gray;\"><code>a + \u00a0(int32_t)b<\/code><\/td>\n<td style=\"border: solid 1px gray; border-right: none;\">32-bit<\/td>\n<td style=\"border: 1px gray; border-style: solid none; text-align: right;\">signed<\/td>\n<td style=\"border: solid 1px gray; border-left: none; text-align: right;\">byte offset.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\"><code>[a, b, SXTW #sizeshift]<\/code><\/td>\n<td style=\"border: solid 1px gray;\"><code>a + \u00a0(int32_t)b * size<\/code><\/td>\n<td style=\"border: solid 1px gray; border-right: none;\">32-bit<\/td>\n<td style=\"border: 1px gray; border-style: solid none; text-align: right;\">signed<\/td>\n<td style=\"border: solid 1px gray; border-left: none; text-align: right;\">element offset.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\"><code>[a, b, UXTX #0]<\/code><br \/>\n<code>[a, b, UXTX]<\/code><br \/>\n<code>[a, b, LSL #0]<\/code><br \/>\n<code>[a, b]<\/code><\/td>\n<td style=\"border: solid 1px gray;\"><code>a + (uint64_t)b<\/code><\/td>\n<td style=\"border: solid 1px gray; border-right: none;\">64-bit<\/td>\n<td style=\"border: 1px gray; border-style: solid none; text-align: right;\">unsigned<\/td>\n<td style=\"border: solid 1px gray; border-left: none; text-align: right;\">byte offset.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\"><code>[a, b, UXTX #sizeshift]<\/code><br \/>\n<code>[a, b, LSL #sizeshift]<\/code><\/td>\n<td style=\"border: solid 1px gray;\"><code>a + (uint64_t)b * size<\/code><\/td>\n<td style=\"border: solid 1px gray; border-right: none;\">64-bit<\/td>\n<td style=\"border: 1px gray; border-style: solid none; text-align: right;\">unsigned<\/td>\n<td style=\"border: solid 1px gray; border-left: none; text-align: right;\">element offset.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\"><code>[a, b, SXTX #0]<\/code><\/td>\n<td style=\"border: solid 1px gray;\"><code>a + \u00a0(int64_t)b<\/code><\/td>\n<td style=\"border: solid 1px gray; border-right: none;\">64-bit<\/td>\n<td style=\"border: 1px gray; border-style: solid none; text-align: right;\">signed<\/td>\n<td style=\"border: solid 1px gray; border-left: none; text-align: right;\">byte offset.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\"><code>[a, b, SXTX #sizeshift]<\/code><\/td>\n<td style=\"border: solid 1px gray;\"><code>a + \u00a0(int64_t)b * size<\/code><\/td>\n<td style=\"border: solid 1px gray; border-right: none;\">64-bit<\/td>\n<td style=\"border: 1px gray; border-style: solid none; text-align: right;\">signed<\/td>\n<td style=\"border: solid 1px gray; border-left: none; text-align: right;\">element offset.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If no extended operation is provided, it defaults to <code>UXTX #0<\/code>, which means &#8220;use the whole register, no shift.&#8221;<\/p>\n<p>There is no pre-increment or post-increment option for the indexed addressing modes.<\/p>\n<p>Okay, so those are the addressing modes. Quite a lot to choose from. Next time, we&#8217;ll start doing arithmetic.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The ways of accessing memory.<\/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-106912","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>The ways of accessing memory.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106912","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=106912"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106912\/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=106912"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106912"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106912"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}