Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Assembly (x86-64, AArch64, RISC-V)  Module 4 – Moving data: registers, memory and addresses

Addressing modes for arrays and structs

Walk arrays and structs with base, index, scale and displacement on x86-64, pre- and post-index on AArch64, and base-plus-offset on RISC-V.

  • Beginner
  • 5 minutes
  • Examples run with Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 5 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Use base, index, scale and displacement addressing to walk arrays and structs
  • Use AArch64 pre- and post-indexed addressing and RISC-V base-plus-offset loads
  • Map textbook addressing modes onto the real instructions of three ISAs

Before you start

What you’ll learn · Before you start

What you will learn

  • Use base, index, scale and displacement addressing to walk arrays and structs
  • Use AArch64 pre- and post-indexed addressing and RISC-V base-plus-offset loads
  • Map textbook addressing modes onto the real instructions of three ISAs

Before you start

On this page

An array index or a struct field is an address computed from a few parts: where the array starts, which element you want, how big each element is, and how far into the element the field sits. An addressing mode: The rule an instruction uses to compute the memory address of an operand, such as a register plus a constant offset, or a base plus an index times a scale plus a displacement. Each ISA offers its own set of modes. is the rule an instruction uses to build that address. x86-64 can fold all four parts into one operand; AArch64 and RISC-V split the work differently, and the difference is why the same a[i].y compiles to one memory operand on one ISA and three instructions on another.

This lesson walks an array of struct point { int x; int y; }, summing the y fields, on all three ISAs. The AArch64 version is built and run, and returns 60; the x86-64 and RISC-V versions are disassembled, so you can compare the operands instruction by instruction. By the end you will read a [rdi + rcx*8 + 4] operand at sight and map the textbook addressing modes onto the instructions that implement them.

x86-64: base, index, scale and displacement

x86-64’s memory operand is the most capable of the three. One operand can be [base + index*scale + displacement], where the scale is 1, 2, 4 or 8 and the displacement is a signed constant. That is exactly the shape of an array-of-structs access: the base is the array, the index times the scale reaches element i, and the displacement picks the field.

Interactive · stage by stage base + index*scale + displacement = &points[i].y Watch base, scaled index and displacement combine into the effective address of point[i].y on x86-64.
One operand, four parts, one address, step 4 of 4. Showing base rdi, index rcx x 8, displacement 4, effective address. In focus: effective address.base rdi&points[0]index rcx x 8i times struct sizedisplacement 4offset of y in pointeffective address&points[i].y

The drawing shows the whole scene; its 4 steps are listed below.

Every step as text
  1. The base register. rdi holds the address of the array's first element. Every element's address is measured from here.
  2. The scaled index. rcx is the element number i; times the scale 8 (the struct size) it is the byte offset to point[i].
  3. The displacement. The constant 4 is the offset of the y field inside a point. It is fixed, so it rides in the instruction.
  4. The effective address. base + index times scale + displacement is &points[i].y. x86-64 computes it inside one operand; the add reads from there.

The loop below sums a[i].y for 8-byte points. The whole address computation lives in the one operand [rdi + rcx*8 + 4], so the add reads a[i].y directly. The same arithmetic in lea writes the address to a register without touching memory, which is why compilers reach for lea as a general “multiply-and-add” instruction.

x86-64: scaled-index operand and lea S · x86_64/sum_y.s
// x86-64 addressing: [base + index*scale + displacement] reaches point[i].y in one
// operand. lea computes such an address without touching memory. Build with
// clang -c -target x86_64-linux-gnu sum_y.s and link the output on an x86-64 Linux host.
.intel_syntax noprefix
.text
// long sum_y(struct point *a, long n): sum the y field of each 8-byte point.
.globl sum_y
sum_y:
    xor eax, eax                    // sum = 0
    xor rcx, rcx                    // i = 0
.Lloop:
    add eax, [rdi + rcx*8 + 4]      // sum += a[i].y: base + index*scale + displacement
    inc rcx
    cmp rcx, rsi
    jne .Lloop
    ret

// lea computes an address with the same arithmetic but writes it to a register.
.globl addr_of_y
addr_of_y:
    lea rax, [rdi + rsi*8 + 4]      // rax = &a[rsi].y, no memory touched
    ret
Disassembling the scaled-index operand and lea Python · x86_64_addr_session.py
"""Assemble the x86-64 struct-array code and disassemble the scaled-index operand and lea.

The source is an x86-64 Linux file: clang -c -target x86_64-linux-gnu turns it into an ELF object, and
llvm-objdump shows the bytes of each instruction beside it. Link the object on an x86-64 Linux machine to run it.
"""
import pathlib, sys
sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import session
session.run("""
clang -c -target x86_64-linux-gnu x86_64/sum_y.s -o x86_64/sum_y.o
/opt/homebrew/opt/llvm/bin/llvm-objdump -d x86_64/sum_y.o
""")

Output

$ clang -c -target x86_64-linux-gnu x86_64/sum_y.s -o x86_64/sum_y.o
$ /opt/homebrew/opt/llvm/bin/llvm-objdump -d x86_64/sum_y.o

x86_64/sum_y.o:	file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <sum_y>:
       0: 31 c0                        	xorl	%eax, %eax
       2: 48 31 c9                     	xorq	%rcx, %rcx
       5: 03 44 cf 04                  	addl	0x4(%rdi,%rcx,8), %eax
       9: 48 ff c1                     	incq	%rcx
       c: 48 39 f1                     	cmpq	%rsi, %rcx
       f: 75 f4                        	jne	0x5 <sum_y+0x5>
      11: c3                           	retq

0000000000000012 <addr_of_y>:
      12: 48 8d 44 f7 04               	leaq	0x4(%rdi,%rsi,8), %rax
      17: c3                           	retq

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 x86_64_addr_session.py

The disassembly shows addl 0x4(%rdi,%rcx,8), %eax: displacement 0x4, base %rdi, index %rcx, scale 8. The addr_of_y function shows leaq 0x4(%rdi,%rsi,8), %rax — the identical address arithmetic, but the result goes into a register instead of being dereferenced.

AArch64: offset, pre-index and post-index

AArch64 has no four-part operand, but it has something x86-64 lacks: addressing modes that update the base register as a side effect. The forms are [xn, #imm] (base plus a fixed offset), [xn, xm, lsl #3] (base plus a scaled register), pre-index [xn, #8]! (add 8 to xn, then access) and post-index [xn], #8 (access, then add 8 to xn).

Form Meaning Side effect
[x1, #4] address is x1 + 4 none
[x1, x2, lsl #3] address is x1 + x2*8 none
[x1, #8]! (pre-index) address is x1 + 8 x1 = x1 + 8 first
[x1], #8 (post-index) address is x1 x1 = x1 + 8 after

Post-index is the natural fit for walking an array: load the current element and advance the pointer in one instruction. The AArch64 program sums the y fields with exactly that, ldr w3, [x1], #8, and returns 60.

The two families differ in how far they reach, which decides which one a compiler can use. The plain offset form encodes a 12-bit unsigned immediate that is scaled by the access width, so a 64-bit ldr x0, [x1, #imm] reaches 0 to 32760 bytes in steps of 8 — far enough for any field of a normal structure. The pre- and post-index forms encode an unscaled 9-bit signed immediate instead, −256 to 255, because the instruction must also say that the base register is written back. A stride larger than that cannot be folded into the load, and the pointer has to be advanced by a separate add.

AArch64: post-indexed walk over the points S · aarch64/sum_y.s
// An array of struct point { int x; int y; } (8 bytes each). Sum the y fields and
// return the total from _main. y values 10 + 20 + 30 = 60 becomes the exit status.
.section __DATA,__data
.p2align 2
points:
    .word 1, 10        // point[0]: x=1, y=10
    .word 2, 20        // point[1]: x=2, y=20
    .word 3, 30        // point[2]: x=3, y=30
.text
.globl _main
.p2align 2
_main:
    adrp x1, points@PAGE
    add  x1, x1, points@PAGEOFF
    add  x1, x1, #4            // point x1 at the first y field (offset 4)
    mov  w0, #0               // running sum
    mov  x2, #3               // points left
Lloop:
    ldr  w3, [x1], #8          // load y, then advance x1 by the 8-byte struct (post-index)
    add  w0, w0, w3
    subs x2, x2, #1
    b.ne Lloop
    ret                        // exit status = low byte of sum = 60
Running the struct sum and reading the post-indexed load Python · aarch64_addr_session.py
"""Build and run the AArch64 struct-array sum, then read its post-indexed load.

sum_y.s walks an array of 8-byte points with ldr w3, [x1], #8, summing the y fields; the total 60 is the exit status.
"""
import pathlib, sys
sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import session
session.run("""
clang -o aarch64/sum_y aarch64/sum_y.s
./aarch64/sum_y; echo "exit=$?"
/opt/homebrew/opt/llvm/bin/llvm-objdump -d aarch64/sum_y | sed -n '/<_main>:/,/ret/p'
""")

Output

$ clang -o aarch64/sum_y aarch64/sum_y.s
$ ./aarch64/sum_y; echo "exit=$?"
exit=60
$ /opt/homebrew/opt/llvm/bin/llvm-objdump -d aarch64/sum_y | sed -n '/<_main>:/,/ret/p'
0000000100000370 <_main>:
100000370: 90000021    	adrp	x1, 0x100004000 <points>
100000374: 91000021    	add	x1, x1, #0x0
100000378: 91001021    	add	x1, x1, #0x4
10000037c: 52800000    	mov	w0, #0x0                ; =0
100000380: d2800062    	mov	x2, #0x3                ; =3
100000384: b8408423    	ldr	w3, [x1], #0x8
100000388: 0b030000    	add	w0, w0, w3
10000038c: f1000442    	subs	x2, x2, #0x1
100000390: 54ffffa1    	b.ne	0x100000384 <_main+0x14>
100000394: d65f03c0    	ret

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 aarch64_addr_session.py

Checkpoint

Check: x86-64 base, index, scale, displacement · AArch64 pre- and post-index

  1. Question 1 of 2 In add eax, [rdi + rcx*8 + 4], which part reaches the y field inside an 8-byte struct point?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The displacement 4, the byte offset of y within each point

    base (rdi) is the array, index*scale (rcx*8) reaches element i, and the displacement (4) is the offset of the y field inside the 8-byte struct. Together they form &points[i].y.

  2. Question 2 of 2 What does AArch64 ldr w3, [x1], #8 do?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Loads from the address in x1, then adds 8 to x1 (post-index).

    Post-index [x1], #8 reads from the current x1, then adds 8 to x1. It walks an array in one instruction: load the element and advance the pointer.

RISC-V: one mode, and the shNadd shortcut

RISC-V keeps its addressing deliberately small: the only mode is a base register plus a 12-bit signed offset, offset(rs1). There is no index, no scale, no auto-increment. Everything else is computed with ordinary instructions, so a[i].y needs a shift to scale the index, an add to reach the element, and then the base-plus-offset load for the field.

The standard extension Zba (address generation) adds sh1add, sh2add and sh3add, which shift one operand left by 1, 2 or 3 and add in a single instruction. The disassembly shows the plain loop using slli then add, and the Zba loop folding both into one sh3add.

Two limits follow from that single mode. The offset is a 12-bit signed field, −2048 to 2047, so a field further than 2 KiB into a structure or an object needs its own add before the load; and the shortcut only exists where the hardware has Zba, so the assembler has to be told the extension is allowed — the Zba variant here is assembled for rv64gc_zba, while the same source assembled for plain rv64gc is rejected, as the session’s first command shows. That is the RISC-V trade-off in one line: a small, regular instruction set, with the arithmetic that other ISAs hide inside an operand written out where you can see it.

RISC-V: slli + add, and the Zba sh3add S · riscv/sum_y.s
# RISC-V has one addressing mode: a base register plus a 12-bit signed offset. Scaling an
# index by the struct size is a separate instruction (slli), or a single sh3add with the
# Zba extension. Build with clang -c -target riscv64-linux-gnu -march=rv64gc_zba sum_y.s.
.text
# long sum_y(struct point *a, long n): base plus offset, index scaled with slli.
.globl sum_y
sum_y:
    li   a2, 0            # sum = 0
    li   a3, 0            # i = 0
.Lloop:
    slli a4, a3, 3        # a4 = i * 8 (no scaled addressing mode, so compute it)
    add  a4, a0, a4       # a4 = base + i*8
    lw   a5, 4(a4)        # a[i].y: the only mode is base + offset
    addw a2, a2, a5
    addi a3, a3, 1
    bne  a3, a1, .Lloop
    mv   a0, a2
    ret

# The Zba extension folds the shift-and-add into one sh3add (a4 = a0 + a3*8).
.globl sum_y_zba
sum_y_zba:
    li   a2, 0
    li   a3, 0
.Lz:
    sh3add a4, a3, a0     # a4 = base + i*8 in one instruction (Zba)
    lw   a5, 4(a4)
    addw a2, a2, a5
    addi a3, a3, 1
    bne  a3, a1, .Lz
    mv   a0, a2
    ret
Disassembling the plain loop and the Zba variant Python · riscv_addr_session.py
"""Assemble the RISC-V struct-array code twice, then disassemble the Zba variant.

A plain rv64gc assembler rejects sh3add with a Zba-required error; adding the Zba extension
(-march=rv64gc_zba) lets both loops assemble. The disassembly contrasts slli+add with sh3add.
"""
import pathlib, sys
sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import session
session.run("""
# A plain rv64gc assembler refuses sh3add: the extension is not in the base.
/opt/homebrew/opt/llvm/bin/clang -c -target riscv64-linux-gnu -march=rv64gc riscv/sum_y.s -o riscv/sum_y.o
# Adding the Zba extension lets both loops assemble.
/opt/homebrew/opt/llvm/bin/clang -c -target riscv64-linux-gnu -march=rv64gc_zba riscv/sum_y.s -o riscv/sum_y.o
/opt/homebrew/opt/llvm/bin/llvm-objdump -d riscv/sum_y.o
""")

Output

# A plain rv64gc assembler refuses sh3add: the extension is not in the base.
$ /opt/homebrew/opt/llvm/bin/clang -c -target riscv64-linux-gnu -march=rv64gc riscv/sum_y.s -o riscv/sum_y.o
riscv/sum_y.s:26:5: error: instruction requires the following: 'Zba' (Address Generation Instructions)
    sh3add a4, a3, a0     # a4 = base + i*8 in one instruction (Zba)
    ^
[exit status 1]
# Adding the Zba extension lets both loops assemble.
$ /opt/homebrew/opt/llvm/bin/clang -c -target riscv64-linux-gnu -march=rv64gc_zba riscv/sum_y.s -o riscv/sum_y.o
$ /opt/homebrew/opt/llvm/bin/llvm-objdump -d riscv/sum_y.o

riscv/sum_y.o:	file format elf64-littleriscv

Disassembly of section .text:

0000000000000000 <sum_y>:
       0: 4601         	li	a2, 0x0
       2: 4681         	li	a3, 0x0
       4: 00369713     	slli	a4, a3, 0x3
       8: 972a         	add	a4, a4, a0
       a: 435c         	lw	a5, 0x4(a4)
       c: 9e3d         	addw	a2, a2, a5
       e: 0685         	addi	a3, a3, 0x1
      10: feb69ae3     	bne	a3, a1, 0x4 <sum_y+0x4>
      14: 8532         	mv	a0, a2
      16: 8082         	ret

0000000000000018 <sum_y_zba>:
      18: 4601         	li	a2, 0x0
      1a: 4681         	li	a3, 0x0
      1c: 20a6e733     	sh3add	a4, a3, a0
      20: 435c         	lw	a5, 0x4(a4)
      22: 9e3d         	addw	a2, a2, a5
      24: 0685         	addi	a3, a3, 0x1
      26: feb69be3     	bne	a3, a1, 0x1c <sum_y_zba+0x4>
      2a: 8532         	mv	a0, a2
      2c: 8082         	ret

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 riscv_addr_session.py

Key idea

x86-64 folds base, scaled index and displacement into one operand; AArch64 adds pre- and post-index that update the base; RISC-V has only base-plus-offset and computes the rest, with Zba’s shNadd as a shortcut.

Textbook addressing modes, mapped to real forms

Textbooks and course syllabi list addressing modes as a vocabulary — immediate, register, direct, register indirect, indexed, base-plus-displacement, PC-relative, auto-increment. The table maps each name onto a real form you have now seen; the one exception, direct, follows it.

Textbook mode What it means Real example
Immediate operand is a constant in the instruction mov x0, #42, addi a0, a0, 4
Register operand is a register add x0, x1, x2
Register indirect address is in a register ldr x0, [x1], lw a0, 0(a1)
Base plus displacement register plus a constant offset ldr w3, [x1, #4], lw a5, 4(a4)
Indexed (scaled) base plus index times scale [rdi + rcx*8], [x1, x2, lsl #3]
Base, index, scale, disp all four at once [rdi + rcx*8 + 4] (x86-64 only)
PC-relative address measured from the program counter lea [rip + msg], adrp/add, auipc/addi
Auto-increment base updates as a side effect [x1], #8 (post-index), [x1, #8]! (pre-index)

The effective address: The final memory address an instruction computes from the parts of its addressing mode (base, index, scale, displacement or PC-relative offset) just before it reads or writes memory. is whatever the mode finally computes before the memory access. “Direct” (the operand names a fixed absolute address) barely appears in modern 64-bit code, because position-independent executables form every address relative to the PC instead; the previous lesson showed why.

GATE CS 2027

IIT Madras runs GATE 2027. In its Computer Science paper, Section 3 (Computer Organization and Architecture) opens with the topic “Instruction set and addressing modes”. The table above maps those textbook names onto the real x86-64, AArch64 and RISC-V instructions this lesson traced.

Checkpoint

Check: RISC-V base-plus-offset and shNadd · Textbook addressing modes

  1. Question 1 of 2 Why does summing a[i].y on RISC-V take a slli and an add before the load, when x86-64 needs none?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: RISC-V's only addressing mode is base + 12-bit offset, so scaling the index (slli) and reaching the element (add) are separate instructions.

    RISC-V offers only offset(base). To reach a[i] it must scale i by the struct size (slli) and add the base (add), then load the field with an offset. The Zba sh3add folds the shift-and-add into one instruction.

  2. Question 2 of 2 Which real form implements the textbook "auto-increment" addressing mode?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: AArch64 post-index [x1], #8 (and pre-index [x1, #8]!)

    Auto-increment updates the base as a side effect of the access. AArch64's post-index [x1], #8 and pre-index [x1, #8]! are the real forms; x86-64 and RISC-V have no auto-increment addressing mode.

Practise

Each question gives its reason, right or wrong, from the modes this lesson named: the four-part x86-64 operand, the auto-indexing AArch64 forms, and RISC-V’s single base-plus-offset.

Practice that adapts to you

Practise this lesson

8 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 8 Why do compilers use lea rax, [rdi + rsi*8 + 4] even when no memory is accessed?

    Choose one answer.

    Show the answer: Question 1 of 8

    Answer: lea runs the address arithmetic (a multiply by 8 and two adds) and writes the result to a register, without touching memory.

    lea computes base + index*scale + displacement and stores that value in a register without a memory access. Because the scale can be 2, 4 or 8, compilers use it as a one-instruction multiply-and-add.

  2. Question 2 of 8 You want to push a value: lower the stack pointer by 16, then store at the new address. Which AArch64 form?

    Choose one answer.

    Show the answer: Question 2 of 8

    Answer: Pre-index, str x0, [sp, #-16]!

    Pre-index [sp, #-16]! lowers sp by 16 and writes it back before the store, so the value lands on the freshly reserved slot. That is the push order; post-index would access first and adjust afterwards.

  3. Question 3 of 8 How does the RISC-V Zba extension shorten a4 = base + i*8 in the array loop?

    Choose one answer.

    Show the answer: Question 3 of 8

    Answer: sh3add a4, i, base shifts i left by 3 and adds base in one instruction, replacing a separate slli and add.

    Zba's sh3add a4, a3, a0 computes a0 + (a3 << 3) in one instruction, folding the slli by 3 and the add of the base. The load is still offset(base); only the address computation shrinks.

  4. Question 4 of 8 lw a5, 4(a4) on RISC-V is which textbook addressing mode?

    Choose one answer.

    Show the answer: Question 4 of 8

    Answer: Base plus displacement (register indirect with an offset)

    4(a4) is base register a4 plus the constant displacement 4 — base-plus-displacement addressing. RISC-V offers only this mode for loads and stores.

  5. Question 5 of 8 A loop walks an array of 12-byte structs on x86-64. Why can [rdi + rcx*12] not be encoded, and what does a compiler write instead?

    Choose one answer.

    Show the answer: Question 5 of 8

    Answer: The scale may only be 1, 2, 4 or 8, so the index is multiplied first — often with an lea that computes i*3, then a scale of 4.

    The scale field of an x86-64 memory operand holds 1, 2, 4 or 8 and nothing else. For a 12-byte element the index has to be scaled by a separate instruction first, for example lea rax, [rcx + rcx*2] to get i*3, after which a scale of 4 finishes the job.

  6. Question 6 of 8 Which AArch64 load leaves the base register x1 unchanged?

    Choose one answer.

    Show the answer: Question 6 of 8

    Answer: ldr w3, [x1, #4]

    Only the plain offset form [x1, #4] leaves the base alone. Both the pre-index [x1, #4]! and the post-index [x1], #4 write an updated address back into x1, which is what makes them useful for walking an array.

  7. Question 7 of 8 Which memory-operand form do RISC-V base-ISA loads and stores accept?

    Choose one answer.

    Show the answer: Question 7 of 8

    Answer: A base register plus a 12-bit signed offset, written off(reg)

    RISC-V loads and stores take one register and a 12-bit signed offset, −2048 to 2047. Every other addressing need, scaled indexes included, is met with ordinary arithmetic into that base register.

  8. Question 8 of 8 Which textbook addressing mode is lea rsi, [rip + msg] on x86-64?

    Choose one answer.

    Show the answer: Question 8 of 8

    Answer: PC-relative

    [rip + msg] adds a displacement to the address of the next instruction, which is the textbook PC-relative mode. Position-independent executables reach their data this way, which is why direct addressing is rare in 64-bit code.

Interview questions

Warm-up: what is an addressing mode, and why does a[i].y take one x86-64 operand but three RISC-V instructions?

An addressing mode is the rule an instruction uses to compute the memory address of an operand — for example “a register plus a constant offset”, or “a base plus an index times a scale plus a displacement”. The address it produces is the effective address. x86-64 offers a rich mode, [base + index*scale + displacement], which maps directly onto a[i].y: the base is the array, index*scale reaches element i (scale 8 for an 8-byte struct), and the displacement 4 is the offset of the y field. So one memory operand computes the whole address and the load or add reads from it. RISC-V deliberately provides only offset(base), so the same access is a shift (slli to scale the index), an add (to reach the element), and a base-plus-offset load for the field — three instructions, unless the Zba extension folds the shift-and-add into one sh3add. The trade-off is x86-64’s decoding complexity against RISC-V’s regularity.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    Reading optimised x86-64, you see lea used where no memory is accessed, sometimes instead of a multiply or a chain of adds. Why do compilers do this, and what are the limits?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    On RISC-V an array-of-structs loop has extra instructions per iteration compared with x86-64. A teammate asks you to justify or remove them. What do you say?

    Model answer, Pro. First step and follow-ups

Flashcards · 6 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What is an addressing mode, and what is the effective address?

    An addressing mode is the rule an instruction uses to compute the memory address of an operand, such as a register plus an offset. The effective address is the address that rule finally produces, just before the access.

  2. How does one x86-64 operand reach a[i].y in an array of 8-byte structs?

    [base + index*8 + 4]: the base register holds the array, the index scaled by 8 steps to element i, and the displacement 4 is the offset of field y. One memory operand computes the whole address.

  3. What does x86-64 lea do with a memory operand?

    It computes the effective address and puts it in a register without touching memory, so it is also used for quick arithmetic such as a + b*4 + 8.

  4. What is the difference between AArch64 pre-index and post-index addressing?

    Pre-index, written with an exclamation mark after the brackets, adds the offset to the base register before the access and keeps the new value. Post-index, with the offset after the brackets, accesses the old address and then updates the base, which makes it the natural way to walk an array.

  5. RISC-V has only offset(base) addressing. How does it index an array of 8-byte elements?

    It shifts the index left by 3 with slli, adds the base, then loads at offset 0; with the Zba extension one sh3add does the shift and the add together.

  6. Which textbook addressing modes map onto real instructions of the three ISAs?

    Immediate, register, register indirect, indexed, base plus displacement, PC-relative and auto-increment all appear: auto-increment as AArch64 post-index, PC-relative as rip, adr and auipc forms.

Key takeaways

  • x86-64’s [base + index*scale + displacement] operand computes an array-of-structs address in one step, and lea runs the same arithmetic to produce an address without a memory access.
  • AArch64 adds pre-index ([xn, #8]!) and post-index ([xn], #8) forms that update the base register, which makes ldr w3, [x1], #8 the natural way to walk an array.
  • RISC-V has a single offset(base) mode and computes scaling with slli plus add, or with one sh3add when the Zba extension is present.
  • The textbook addressing modes — immediate, register, indirect, indexed, base-plus-displacement, PC-relative, auto-increment — each map onto real instructions across the three ISAs, and the effective address is what the mode finally computes.

Terms in this lesson

addressing mode
The rule an instruction uses to compute the memory address of an operand, such as a register plus a constant offset, or a base plus an index times a scale plus a displacement. Each ISA offers its own set of modes.
effective address
The final memory address an instruction computes from the parts of its addressing mode (base, index, scale, displacement or PC-relative offset) just before it reads or writes memory.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.