Next: Constraints, Previous: Inline, Up: C Extensions
In an assembler instruction using asm
, you can specify the
operands of the instruction using C expressions. This means you need not
guess which registers or memory locations will contain the data you want
to use.
You must specify an assembler instruction template much like what appears in a machine description, plus an operand constraint string for each operand.
For example, here is how to use the 68881's fsinx
instruction:
asm ("fsinx %1,%0" : "=f" (result) : "f" (angle));
Here angle
is the C expression for the input operand while
result
is that of the output operand. Each has `"f"' as its
operand constraint, saying that a floating point register is required.
The `=' in `=f' indicates that the operand is an output; all
output operands' constraints must use `='. The constraints use the
same language used in the machine description (see Constraints).
Each operand is described by an operand-constraint string followed by the C expression in parentheses. A colon separates the assembler template from the first output operand and another separates the last output operand from the first input, if any. Commas separate the operands within each group. The total number of operands is currently limited to 30; this limitation may be lifted in some future version of GCC.
If there are no output operands but there are input operands, you must place two consecutive colons surrounding the place where the output operands would go.
As of GCC version 3.1, it is also possible to specify input and output
operands using symbolic names which can be referenced within the
assembler code. These names are specified inside square brackets
preceding the constraint string, and can be referenced inside the
assembler code using %[
name]
instead of a percentage sign
followed by the operand number. Using named operands the above example
could look like:
asm ("fsinx %[angle],%[output]" : [output] "=f" (result) : [angle] "f" (angle));
Note that the symbolic operand names have no relation whatsoever to other C identifiers. You may use any name you like, even those of existing C symbols, but you must ensure that no two operands within the same assembler construct use the same symbolic name.
Output operand expressions must be lvalues; the compiler can check this.
The input operands need not be lvalues. The compiler cannot check
whether the operands have data types that are reasonable for the
instruction being executed. It does not parse the assembler instruction
template and does not know what it means or even whether it is valid
assembler input. The extended asm
feature is most often used for
machine instructions the compiler itself does not know exist. If
the output expression cannot be directly addressed (for example, it is a
bit-field), your constraint must allow a register. In that case, GCC
will use the register as the output of the asm
, and then store
that register into the output.
The ordinary output operands must be write-only; GCC will assume that the values in these operands before the instruction are dead and need not be generated. Extended asm supports input-output or read-write operands. Use the constraint character `+' to indicate such an operand and list it with the output operands. You should only use read-write operands when the constraints for the operand (or the operand in which only some of the bits are to be changed) allow a register.
You may, as an alternative, logically split its function into two
separate operands, one input operand and one write-only output
operand. The connection between them is expressed by constraints
which say they need to be in the same location when the instruction
executes. You can use the same C expression for both operands, or
different expressions. For example, here we write the (fictitious)
`combine' instruction with bar
as its read-only source
operand and foo
as its read-write destination:
asm ("combine %2,%0" : "=r" (foo) : "0" (foo), "g" (bar));
The constraint `"0"' for operand 1 says that it must occupy the same location as operand 0. A number in constraint is allowed only in an input operand and it must refer to an output operand.
Only a number in the constraint can guarantee that one operand will be in
the same place as another. The mere fact that foo
is the value
of both operands is not enough to guarantee that they will be in the
same place in the generated assembler code. The following would not
work reliably:
asm ("combine %2,%0" : "=r" (foo) : "r" (foo), "g" (bar));
Various optimizations or reloading could cause operands 0 and 1 to be in
different registers; GCC knows no reason not to do so. For example, the
compiler might find a copy of the value of foo
in one register and
use it for operand 1, but generate the output operand 0 in a different
register (copying it afterward to foo
's own address). Of course,
since the register for operand 1 is not even mentioned in the assembler
code, the result will not work, but GCC can't tell that.
As of GCC version 3.1, one may write [
name]
instead of
the operand number for a matching constraint. For example:
asm ("cmoveq %1,%2,%[result]" : [result] "=r"(result) : "r" (test), "r"(new), "[result]"(old));
Some instructions clobber specific hard registers. To describe this, write a third colon after the input operands, followed by the names of the clobbered hard registers (given as strings). Here is a realistic example for the VAX:
asm volatile ("movc3 %0,%1,%2" : /* no outputs */ : "g" (from), "g" (to), "g" (count) : "r0", "r1", "r2", "r3", "r4", "r5");
You may not write a clobber description in a way that overlaps with an
input or output operand. For example, you may not have an operand
describing a register class with one member if you mention that register
in the clobber list. Variables declared to live in specific registers
(see Explicit Reg Vars), and used as asm input or output operands must
have no part mentioned in the clobber description.
There is no way for you to specify that an input
operand is modified without also specifying it as an output
operand. Note that if all the output operands you specify are for this
purpose (and hence unused), you will then also need to specify
volatile
for the asm
construct, as described below, to
prevent GCC from deleting the asm
statement as unused.
If you refer to a particular hardware register from the assembler code, you will probably have to list the register after the third colon to tell the compiler the register's value is modified. In some assemblers, the register names begin with `%'; to produce one `%' in the assembler code, you must write `%%' in the input.
If your assembler instruction can alter the condition code register, add `cc' to the list of clobbered registers. GCC on some machines represents the condition codes as a specific hardware register; `cc' serves to name this register. On other machines, the condition code is handled differently, and specifying `cc' has no effect. But it is valid no matter what the machine.
If your assembler instructions access memory in an unpredictable
fashion, add `memory' to the list of clobbered registers. This
will cause GCC to not keep memory values cached in registers across the
assembler instruction and not optimize stores or loads to that memory.
You will also want to add the volatile
keyword if the memory
affected is not listed in the inputs or outputs of the asm
, as
the `memory' clobber does not count as a side-effect of the
asm
. If you know how large the accessed memory is, you can add
it as input or output but if this is not known, you should add
`memory'. As an example, if you access ten bytes of a string, you
can use a memory input like:
{"m"( ({ struct { char x[10]; } *p = (void *)ptr ; *p; }) )}.
Note that in the following example the memory input is necessary,
otherwise GCC might optimize the store to x
away:
int foo () { int x = 42; int *y = &x; int result; asm ("magic stuff accessing an 'int' pointed to by '%1'" "=&d" (r) : "a" (y), "m" (*y)); return result; }
You can put multiple assembler instructions together in a single
asm
template, separated by the characters normally used in assembly
code for the system. A combination that works in most places is a newline
to break the line, plus a tab character to move to the instruction field
(written as `\n\t'). Sometimes semicolons can be used, if the
assembler allows semicolons as a line-breaking character. Note that some
assembler dialects use semicolons to start a comment.
The input operands are guaranteed not to use any of the clobbered
registers, and neither will the output operands' addresses, so you can
read and write the clobbered registers as many times as you like. Here
is an example of multiple instructions in a template; it assumes the
subroutine _foo
accepts arguments in registers 9 and 10:
asm ("movl %0,r9\n\tmovl %1,r10\n\tcall _foo" : /* no outputs */ : "g" (from), "g" (to) : "r9", "r10");
Unless an output operand has the `&' constraint modifier, GCC may allocate it in the same register as an unrelated input operand, on the assumption the inputs are consumed before the outputs are produced. This assumption may be false if the assembler code actually consists of more than one instruction. In such a case, use `&' for each output operand that may not overlap an input. See Modifiers.
If you want to test the condition code produced by an assembler
instruction, you must include a branch and a label in the asm
construct, as follows:
asm ("clr %0\n\tfrob %1\n\tbeq 0f\n\tmov #1,%0\n0:" : "g" (result) : "g" (input));
This assumes your assembler supports local labels, as the GNU assembler and most Unix assemblers do.
Speaking of labels, jumps from one asm
to another are not
supported. The compiler's optimizers do not know about these jumps, and
therefore they cannot take account of them when deciding how to
optimize.
Usually the most convenient way to use these asm
instructions is to
encapsulate them in macros that look like functions. For example,
#define sin(x) \ ({ double __value, __arg = (x); \ asm ("fsinx %1,%0": "=f" (__value): "f" (__arg)); \ __value; })
Here the variable __arg
is used to make sure that the instruction
operates on a proper double
value, and to accept only those
arguments x
which can convert automatically to a double
.
Another way to make sure the instruction operates on the correct data
type is to use a cast in the asm
. This is different from using a
variable __arg
in that it converts more different types. For
example, if the desired type were int
, casting the argument to
int
would accept a pointer with no complaint, while assigning the
argument to an int
variable named __arg
would warn about
using a pointer unless the caller explicitly casts it.
If an asm
has output operands, GCC assumes for optimization
purposes the instruction has no side effects except to change the output
operands. This does not mean instructions with a side effect cannot be
used, but you must be careful, because the compiler may eliminate them
if the output operands aren't used, or move them out of loops, or
replace two with one if they constitute a common subexpression. Also,
if your instruction does have a side effect on a variable that otherwise
appears not to change, the old value of the variable may be reused later
if it happens to be found in a register.
You can prevent an asm
instruction from being deleted, moved
significantly, or combined, by writing the keyword volatile
after
the asm
. For example:
#define get_and_set_priority(new) \ ({ int __old; \ asm volatile ("get_and_set_priority %0, %1" \ : "=g" (__old) : "g" (new)); \ __old; })
If you write an asm
instruction with no outputs, GCC will know
the instruction has side-effects and will not delete the instruction or
move it outside of loops.
The volatile
keyword indicates that the instruction has
important side-effects. GCC will not delete a volatile asm
if
it is reachable. (The instruction can still be deleted if GCC can
prove that control-flow will never reach the location of the
instruction.) In addition, GCC will not reschedule instructions
across a volatile asm
instruction. For example:
*(volatile int *)addr = foo; asm volatile ("eieio" : : );
Assume addr
contains the address of a memory mapped device
register. The PowerPC eieio
instruction (Enforce In-order
Execution of I/O) tells the CPU to make sure that the store to that
device register happens before it issues any other I/O.
Note that even a volatile asm
instruction can be moved in ways
that appear insignificant to the compiler, such as across jump
instructions. You can't expect a sequence of volatile asm
instructions to remain perfectly consecutive. If you want consecutive
output, use a single asm
. Also, GCC will perform some
optimizations across a volatile asm
instruction; GCC does not
“forget everything” when it encounters a volatile asm
instruction the way some other compilers do.
An asm
instruction without any operands or clobbers (an “old
style” asm
) will be treated identically to a volatile
asm
instruction.
It is a natural idea to look for a way to give access to the condition code left by the assembler instruction. However, when we attempted to implement this, we found no way to make it work reliably. The problem is that output operands might need reloading, which would result in additional following “store” instructions. On most machines, these instructions would alter the condition code before there was time to test it. This problem doesn't arise for ordinary “test” and “compare” instructions because they don't have any output operands.
For reasons similar to those described above, it is not possible to give an assembler instruction access to the condition code left by previous instructions.
If you are writing a header file that should be includable in ISO C
programs, write __asm__
instead of asm
. See Alternate Keywords.
asm
Some targets require that GCC track the size of each instruction used in
order to generate correct code. Because the final length of an
asm
is only known by the assembler, GCC must make an estimate as
to how big it will be. The estimate is formed by counting the number of
statements in the pattern of the asm
and multiplying that by the
length of the longest instruction on that processor. Statements in the
asm
are identified by newline characters and whatever statement
separator characters are supported by the assembler; on most processors
this is the `;
' character.
Normally, GCC's estimate is perfectly adequate to ensure that correct code is generated, but it is possible to confuse the compiler if you use pseudo instructions or assembler macros that expand into multiple real instructions or if you use assembler directives that expand to more space in the object file than would be needed for a single instruction. If this happens then the assembler will produce a diagnostic saying that a label is unreachable.
There are several rules on the usage of stack-like regs in asm_operands insns. These rules apply only to the operands that are stack-like regs:
An input reg that is implicitly popped by the asm must be explicitly clobbered, unless it is constrained to match an output operand.
All implicitly popped input regs must be closer to the top of the reg-stack than any input that is not implicitly popped.
It is possible that if an input dies in an insn, reload might use the input reg for an output reload. Consider this example:
asm ("foo" : "=t" (a) : "f" (b));
This asm says that input B is not popped by the asm, and that the asm pushes a result onto the reg-stack, i.e., the stack is one deeper after the asm than it was before. But, it is possible that reload will think that it can use the same reg for both the input and the output, if input B dies in this insn.
If any input operand uses the f
constraint, all output reg
constraints must use the &
earlyclobber.
The asm above would be written as
asm ("foo" : "=&t" (a) : "f" (b));
Output operands must specifically indicate which reg an output
appears in after an asm. =f
is not allowed: the operand
constraints must select a class with a single reg.
Output operands must start at the top of the reg-stack: output operands may not “skip” a reg.
Here are a couple of reasonable asms to want to write. This asm takes one input, which is internally popped, and produces two outputs.
asm ("fsincos" : "=t" (cos), "=u" (sin) : "0" (inp));
This asm takes two inputs, which are popped by the fyl2xp1
opcode,
and replaces them with one output. The user must code the st(1)
clobber for reg-stack.c to know that fyl2xp1
pops both inputs.
asm ("fyl2xp1" : "=t" (result) : "0" (x), "u" (y) : "st(1)");