I'm currently reading a tutorial on Raspberry Pi OS development and was wondering about the way local labels are used in this code snippet (GCC ARM Assembly):
...
b 2f
1:
stmia r4!, {r5-r8}
2:
cmp r4, r9
blo 1b
...
If you use 1:
as a label you have to specify either f
or b
after the jump instruction to make the assembler know in which direction the jump is aimed. As far as I know you could also use this:
...
b .2
.1:
stmia r4!, {r5-r8}
.2:
cmp r4, r9
blo .1
...
I think this option is a lot less confusing (local labels are also marked with a dot in x86 assembly), because there is no additional letter after the label reference. I have tested the resulting machine code and it's the same. So my questions:
Why would you use the one variant over the other?
Why is it necessary to specify the direction of the jump with either f
or b
?
A label is a symbol that represents the memory address of an instruction or data. The address can be PC-relative, register-relative, or absolute. Labels are local to the source file unless you make them global using the EXPORT directive. The address given by a label is calculated during assembly.
A "LABEL" is something so that you don't have to manually work out the addresses to jump to. It is not an instruction and takes up no space (excepting as the assembler might align code to make it valid).
A data label identifies the location of a variable, providing a convenient way to reference the variable in code. The following, for example, defines a variable named count: count DWORD 100. The assembler assigns a numeric address to each label.
The important difference is that the numbered local labels can be reused without worry and that is why you need to specify the direction too. You can jump to preceding or following one, but not the ones beyond them.
1: foo
...
1: bar
...
jmp 1b # jumps to bar
...
jmp 1f # jumps to baz
...
1: baz
...
1: qux
...
jmp 1b # jumps to qux
As long as you only use them within a single block only, you can be sure they will work as intended and not conflict with anything else.
One of the main benefits of local labels is that since the same identifier can appear multiple times, they can be used in macros. Consider some hypothetical local label usage like this, though:
.macro dothething rega regb ptr
ldrex \regb, [\ptr]
cmp \rega, \regb
beq 1
2: <more instructions>
...
strex \regb, \rega, [ptr]
cmp \regb, #0
bne 2
1:
.endm
myfunction:
dothething r0 r1 r2
dothething r0 r1 r3
bx lr
That's actually allowed in armasm (albeit with slightly different syntax), where the behaviour in the absence of a specified direction is "search backwards, then forwards", but still under any reasonable default behaviour at least one of the jumps in the above code is going to end up targeting the wrong instance of a label. Explicitly calling out the direction with beq 1f
and bne 2b
in the macro resolves the ambiguity and generates the right jumps in both invocations of the macro.
If you choose to use something that isn't a true local label, then not only do you potentially clutter up your symbol table with junk, but you also rob yourself of being able to use loops or conditional branching in macros since you'd generate non-unique symbols. My example might seem a bit contrived, but switch from assembler macros to inline asm blocks in C functions which get inlined all over your complex codebase, and things get a lot more real.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With