没有合适的资源?快使用搜索试试~ 我知道了~
首页ARM GCC Inline Assembler Cookbook
资源详情
资源评论
资源推荐

1/18/2018 ARM GCC Inline Assembler Cookbook
http://www.ethernut.de/en/documents/arm-inline-asm.html 1/11
Hardware Firmware Tools Download Community
Search | Legals | Deutsch
ARM GCC Inline Assembler Cookbook
About this document
The GNU C compiler for ARM RISC processors offers, to embed assembly language code into C programs. This cool feature
may be used for manually optimizing time critical parts of the software or to use specific processor instruction, which are not
available in the C language.
It's assumed, that you are familiar with writing ARM assembler programs, because this is not an ARM assembler programming
tutorial. It's not a C language tutorial either.
All samples had been tested with GCC version 4, but most of them should work with earlier versions too.
GCC asm statement
Let's start with a simple example. The following statement may be included in your code like any other C statement.
/* NOP example */
asm("mov r0,r0");
It moves the contents of register r0 to register r0. In other words, it doesn't do much more than nothing. It is also known as a
NOP (no operation) statement and is typically used for very short delays.
Stop! Before adding this example right away to your C code, keep on reading and learn, why this may not work as expected.
With inline assembly you can use the same assembler instruction mnemonics as you'd use for writing pure ARM assembly
code. And you can write more than one assembler instruction in a single inline asm statement. To make it more readable, you
can put each instruction on a separate line.
asm(
"mov r0, r0 "
"mov r0, r0 "
"mov r0, r0 "
"mov r0, r0"
);
The special sequence of linefeed and tab characters will keep the assembler listing looking nice. It may seem a bit odd for the
first time, but that's the way the compiler creates its own assembler code while compiling C statements.
So far, the assembler instructions are much the same as they'd appear in pure assembly language programs. However,
registers and constants are specified in a different way, if they refer to C expressions. The general form of an inline assembler
statement is
asm(code : output operand list : input operand list : clobber list);
The connection between assembly language and C operands is provided by an optional second and third part of the asm
statement, the list of output and input operands. We will explain the third optional part, the list of clobbers, later.
The next example of rotating bits passes C variables to assembly language. It takes the value of one integer variable, right
rotates the bits by one and stores the result in a second integer variable.

1/18/2018 ARM GCC Inline Assembler Cookbook
http://www.ethernut.de/en/documents/arm-inline-asm.html 2/11
/* Rotating bits example */
asm("mov %[result], %[value], ror #1" : [result] "=r" (y) : [value] "r" (x));
Each asm statement is divided by colons into up to four parts:
1. The assembler instructions, defined in a single string literal:
"mov %[result], %[value], ror #1"
2. An optional list of output operands, separated by commas. Each entry consists of a symbolic name enclosed in square
brackets, followed by a constraint string, followed by a C expression enclosed in parentheses. Our example uses just
one entry:
[result] "=r" (y)
3. A comma separated list of input operands, which uses the same syntax as the list of output operands. Again, this is
optional and our example uses one operand only:
[value] "r" (x)
4. Optional list of clobbered registers, omitted in our example.
As shown in the initial NOP example, trailing parts of the asm statement may be omitted, if unused. Inline asm statements
containing assembler instruction only are also known as basic inline assembly, while statements containing optional parts are
called extended inline assembly. If an unused part is followed by one which is used, it must be left empty. The following
example sets the current program status register of the ARM CPU. It uses an input, but no output operand.
asm("msr cpsr,%[ps]" : : [ps]"r"(status));
Even the code part may be left empty, though an empty string is required. The next statement creates a special clobber to tell
the compiler, that memory contents may have changed. Again, the clobber list will be explained later, when we take a look to
code optimization.
asm("":::"memory");
You can insert spaces, newlines and even C comments to increase readability.
asm("mov %[result], %[value], ror #1"
: [result]"=r" (y) /* Rotation result. */
: [value]"r" (x) /* Rotated value. */
: /* No clobbers */
);
In the code section, operands are referenced by a percent sign followed by the related symbolic name enclosed in square
brackets. It refers to the entry in one of the operand lists that contains the same symbolic name. From the rotating bits example:
%[result] refers to output operand, the C variable y, and
%[value] refers to the input operand, the C variable x.
Symbolic operand names use a separate name space. That means, that there is no relation to any other symbol table. To put it
simple: You can choose a name without taking care whether the same name already exists in your C code. However, unique
symbols must be used within each asm statement.
If you already looked to some working inline assembler statements written by other authors, you may have noticed a significant
difference. In fact, the GCC compiler supports symbolic names since version 3.1. For earlier releases the rotating bit example
must be written as
asm("mov %0, %1, ror #1" : "=r" (result) : "r" (value));

1/18/2018 ARM GCC Inline Assembler Cookbook
http://www.ethernut.de/en/documents/arm-inline-asm.html 3/11
Operands are referenced by a percent sign followed by a single digit, where %0 refers to the first %1 to the second operand
and so forth. This format is still supported by the latest GCC releases, but quite error-prone and difficult to maintain. Imagine,
that you have written a large number of assembler instructions, where operands have to be renumbered manually after
inserting a new output operand.
If all this stuff still looks a little odd, don't worry. Beside the mysterious clobber list, you have the strong feeling that something
else is missing, right? Indeed, we didn't talk about the constraint strings in the operand lists. I'd like to ask for your patience.
There's something more important to highlight in the next chapter.
C code optimization
There are two possible reasons why you want to use assembly language. First is, that C is limited when we are getting closer to
the hardware. E.g. there's no C statement for directly modifying the processor status register. The second reason is to create
highly optimized code. No doubt, the GNU C code optimizer does a good job, but the results are far away from handcrafted
assembler code.
The subject of the chapter is often overlooked: When adding assembly language code by using inline assembler statements,
this code is also processed by the C compiler's code optimizer. Let's examine the part of a compiler listing which may have
been generated from our rotating bits example:
00309DE5 ldr r3, [sp, #0] @ x, x
E330A0E1 mov r3, r3, ror #1 @ tmp, x
04308DE5 str r3, [sp, #4] @ tmp, y
The compiler selected register r3 for bit rotation. It could have selected any other register or two registers, one for each C
variable. It may not explicitly load the value or store the result. Here is another listing, generated by a different compiler version
with different compile options:
E420A0E1 mov r2, r4, ror #1 @ y, x
The compiler selected a unique register for each operand, using the value already cached in r4 and passing the result to the
following code in r2. Did you get the picture?
Often it becomes worse. The compiler may even decide not to include your assembler code at all. These decisions are part of
the compiler's optimization strategy and depend on the context in which your assembler instructions are used. For example, if
you never use any of the output operands in the remaining part of the C program, the optimizer will most likely remove your
inline assembler statement. The NOP example we presented initially may be such a candidate as well, because to the compiler
this is useless overhead, slowing down program execution.
The solution is to add the volatile attribute to the asm statement to instruct the compiler to exclude your assembler code from
code optimization. Remember, that you have been warned to use the initial example. Here is the revised version:
/* NOP example, revised */
asm volatile("mov r0, r0");
But there is more trouble waiting for us. A sophisticated optimizer will re-arrange the code. The following C snippet had been
left over after several last minute changes:
i++;
if (j == 1)
x += 3;
i++;
The optimizer will recognize, that the two increments do not have any impact on the conditional statement. Furthermore it
knows, that incrementing a value by 2 will cost one ARM instruction only. Thus, it will re-arrange the code to
if (j == 1)
x += 3;
i += 2;
剩余10页未读,继续阅读
















安全验证
文档复制为VIP权益,开通VIP直接复制

评论0