I want to write ARM asm code with GCC toolchain. Previously, I use TI CGT, whose asm syntax is different from GCC.
The assembler is actually part of “Binutils”, not of GCC itself. You can find its manual at: Top (Using as)
For example, the last line has an error with GCC toolchain (bad instruction type).
gvar_a .field gvar, 32
The main issue is the lack of colon after the label gvar_a. This makes the assembler think it is some (unknown) instruction. Also, I’ve never seen the “.field” directive. You probably want “.long gvar” here.
An even easier solution is using a literal load, i.e. “ldr r1, =gvar”.
Also, I should note that “mov pc, lr” is a deprecated way to return from a function, “bx lr” should be used instead.
One way to see how to write “proper” assembler is by having GCC produce some for you (although you may need to weed through some redundant directives and unreadable randomly-generated labels). For example, if I understand your intention correctly, your code aims to do the equivalent of:
extern int gvar;
void asmfunc( int arg ) {
gvar += arg;
}
If I put that in a file “foo.c” and compile it with “arm-linux-gnueabihf-gcc -Og -S -o- foo.c” then several things can be noticed about the output:
- the “.syntax unified” directive, to select UAL syntax (which is the syntax currently used by the ARM Architecture Reference Manual, so selecting it is highly recommended)
- some declarations of the target architecture, with many finer points defined in rather poorly readable “eabi attributes”
- the use of Thumb mode by default, which I’d agree with: since ARMv7 there’s really little reason not to.
- the use of movw+movt to produce the address of gvar, instead of loading it from some location.
It is interesting to note that if I compile with “clang -target arm-linux-gnueabihf -O -S -o- foo.c” then it will generate code essentially identical to yours, however the directives at the top show it is being conservative and targeting arm1136jf-s. If I add “-march=armv7-a” or “-mcpu=cortex-a8” to the commandline options then clang will also use movw+movt, so apparently this really is preferred (and considering the effects on separate L1 instruction and data caches, I can imagine why). Also, clang conveniently includes some comments on what all those eabi-attributes mean.
BTW, you haven’t mentioned what your motivation is to write in assembly, but note that GCC has powerful functionality for including inline assembly into C/C++ source code. For example, if you want to use the “rbit” instruction (for which no intrinsic is available I think) you can easily wrap it in a function:
__attribute__((const))
static inline unsigned bitreverse( unsigned x ) {
unsigned result;
asm( "rbit %0, %1" : "=r"( result ) : "r"( x ) );
return result;
}
(The “const” attribute indicates that the function is free of side-effects and doesn’t depend on global memory, giving the optimizer a lot of liberty to move the instruction around.)