Assembler Instructions with C Expression Operands 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's 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. 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 the maximum number of operands in any instruction pattern in the machine description. 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 doesn't parse the assembler instruction template and doesn't know what it means, or whether it's valid assembler input. The extended asm feature is most often used for machine instructions that the compiler itself doesn't know exist. 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 doesn't 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 the 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 isn't enough to guarantee that they will be in the same place in the generated assembler code. The following wouldn't 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 isn't even mentioned in the assembler code, the result won't work, but GNU CC can't tell that. 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. 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's 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"); 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 that your assembler supports local labels, as the GNU assembler and most UNIX assemblers do. 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 doesn't 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 won't be deleted or moved significantly, regardless, unless it's 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's 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.