In an assembler instruction using asm
, you can now specify the
operands of the instruction using C expressions. This means no more
guessing 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 section Constraints for asm
Operands).
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 output operands and separate inputs. The total number of operands is limited to ten or to the maximum number of operands in any instruction pattern in the machine description, whichever is greater.
If there are no output operands, and there are input operands, then there must be two consecutive colons surrounding the place where the output operands would go.
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 whether it is valid assembler input. The
extended asm
feature is most often used for machine instructions
that 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, GNU CC will use
the register as the output of the asm
, and then store that
register into the output.
The output operands must be write-only; GNU CC will assume that the values in these operands before the instruction are dead and need not be generated. Extended asm does not support input-output or read-write operands. For this reason, the constraint character `+', which indicates such an operand, may not be used.
When the assembler instruction has a read-write operand, or an operand
in which only some of the bits are to be changed, you must 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 digit in constraint is allowed only in an input operand, and it must refer to an output operand.
Only a digit 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:
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; GNU CC 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 GNU CC can't tell that.
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");
If you refer to a particular hardware register from the assembler code, then you will probably have to list the register after the third colon to tell the compiler that the register's value is modified. In many 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. GNU CC 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 instruction modifies memory in an unpredictable fashion, add `memory' to the list of clobbered registers. This will cause GNU CC to not keep memory values cached in registers across the assembler instruction.
You can put multiple assembler instructions together in a single asm
template, separated either with newlines (written as `\n') or with
semicolons if the assembler allows such semicolons. The GNU assembler
allows semicolons and all Unix assemblers seem to do so. 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 that the subroutine
_foo
accepts arguments in registers 9 and 10:
asm ("movl %0,r9;movl %1,r10;call _foo" : /* no outputs */ : "g" (from), "g" (to) : "r9", "r10");
Unless an output operand has the `&' constraint modifier, GNU CC may allocate it in the same register as an unrelated input operand, on the assumption that 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 section Constraint Modifier Characters.
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;frob %1;beq 0f;mov #1,%0;0:" : "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, GNU CC assumes for optimization
purposes that the instruction has no side effects except to change the
output operands. This does not mean that 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 set_priority(x) \ asm volatile ("set_priority %0": /* no outputs */ : "g" (x))
An instruction without output operands will not be deleted or moved significantly, regardless, unless it is unreachable.
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
.
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.
If you are writing a header file that should be includable in ANSI C
programs, write __asm__
instead of asm
. See section Alternate Keywords.