What is the difference between =label (equals sign) and [label] (brackets) in ARMv6 assembly?
Asked Answered
H

2

24

I'm following along with the Baking Pi course from Cambridge University, in which a simple operating system is built in the ARMv6 instruction set, targeting the Raspberry Pi.

We've been using two ways of loading data into registers via the ldr instruction so far and I realize now that I'm using them together, I don't fully understand what they both do.

So I've used things like ldr r0,=0x20200000, which I actually understood as "read the data stored at the memory location 0x20200000 into register r0.

Then I've used things like:

ldr r0,[r1,#4]

Which I've understood as being "read the data stored at the memory address pointed to by r1, at an offset of 4 bytes, into register r0".

Then I encounter this:

ldr r0,=pattern
ldr r0,[r0]

pattern here is a .int in the .data section (a bitmap representing a sequence of on/off states for an LED). I realize upon reading this, that my previous understanding of =foo must be wrong, otherwise both of the above instructions would do the same thing.

Is the =x syntax basically more like a pointer in C, while the [x] syntax is as if the memory that is being pointed to by x is actually read?

Let's say ptr in the C below is an int*, do my comments thinking about equivalent assembly (conceptually, not literally) make any sense?

r0 = ptr;     /* equivalent to: ldr r0,=ptr     */
r0 = *ptr;    /* equivalent to: ldr r0,[ptr]    */
r0 = *(ptr+4) /* equivalent to: ldr r0,[ptr,#4] */
Hejira answered 20/6, 2013 at 13:30 Comment(3)
Possible duplicate: https://mcmap.net/q/14735/-why-use-ldr-over-mov-or-vice-versa-in-arm-assembly stackoverflow.com/questions/9374703/… stackoverflow.com/questions/9735169/…Magdalenemagdalenian
Related: Getting an label address to a register on ARM? discusses which ways to do that are efficient.Kareenkarel
If anyone's got the rep for a 1-character edit, there's now an https version of the URL in the question.Enterprise
S
26
ldr r0,=something
...
something:

means load the address of the label something into the register r0. The assembler then adds a word somewhere in reach of the ldr instruction and replaces it with a

ldr r0,[pc,#offset]

instruction

So this shortcut

ldr r0,=0x12345678

means load 0x12345678 into r0.

being mostly fixed length instructions, you cant load a full 32 bit immediate into a register in one instruction, it can take a number of instructions to completely load a register with a 32 bit number. Depends heavily on the number. For example

ldr r0,=0x00010000

will get replaced by the gnu assembler with a single instruction mov r0,#0x00010000 if it is an ARM instruction, for a thumb instruction though it may still have to be ldr r0,[pc,#offset]

These ldr rd,=things are a shortcut, pseudo instructions, not real.

ldr rd,[rm,#offset]
ldr rd,[rm,rn]

are real instructions and mean read from memory at address rm+offset or rm+rn and take the value read and put it in the register rd

the =something is more like &something in C.

unsigned int something;
unsigned int r0;
unsigned int r1;

r0 = &something;
r1 = *(unsigned int *)r0;

and in assembly

something:
    .word 0

ldr r0,=something
ldr r1,[r0]
Stable answered 20/6, 2013 at 13:38 Comment(0)
C
2

It is worth mentioning that there exist the following alternative approaches for those that want to avoid pseudo instructions / the literal pool for some reason:

  • adr r0, label (v7 / v8): single instruction, stores the full address of the label into r0. Refers to label by relative PC addressing it, see also: What are the semantics of ADRP and ADRL instructions in ARM assembly? | Example with asserts.

    In ARMv7, it is not possible however to access labels in different sections with adr, e.g. .data from .text, apparently because there is no relocation that takes care of it. ldr = can do this. If you try, GAS fails with:

     Error: symbol .data is in a different section
    

    Cross section access is however possible in ARMv8, and generates a relocation of type R_AARCH64_ADR_PRE. Example.

  • movw and movt (v7) + GNU GAS #:lower:

    movw r0, #:lower16:myvar
    movt r0, #:upper16:myvar
    

    Example with asserts.

    movk + shifts (v8) + GNU GAS :

    movz x0, #:abs_g2:label     // bits 32-47, overflow check
    movk x0, #:abs_g1_nc:label  // bits 16-31, no overflow check
    movk x0, #:abs_g0_nc:label  // bits  0-15, no overflow check
    

    Example with asserts.

Cortney answered 4/1, 2019 at 17:17 Comment(2)
When tuning for some ARM CPUs, e.g. -mcpu=cortex-a53, gcc will prefer movw/movt for getting a static address into a register instead of a load from a PC-relative literal pool.Kareenkarel
@Downvoters: please explain so I can learn and improve info. I never retaliate.Cortney

© 2022 - 2024 — McMap. All rights reserved.