Appendix E. Format of Code Statements

Table of Contents
Format of Code Statements
Constraints for Operands

Format of Code Statements

In XGC Coral 66 the format of a code statement is:


          CODE
          BEGIN  instructions : output operands : input operands END
        

For example, here is how to use the 68881's "fsinx" instruction:


          CODE
          BEGIN  "fsinx   %1,%0" : "=f" (result) : "f" (angle) END
        

Here angle is the Coral expression for the input operand while result is that of the output operand. Each operand 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 =.

Each operand is described by an operand-constraint string followed by the Coral 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 locations; the compiler can check this. 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. Code statements are 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, the compiler will use the register as the output of the code statement, and then store that register into the output location.

The output operands must be write-only; the compiler will assume that the values in these operands before the instruction are dead and need not be generated. Code statements do 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 Coral 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:


          CODE
          BEGIN  "combine %2,%0" : "=r" (foo) : "0" (foo), "g" (bar) END
        

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:


          CODE
          BEGIN  "combine %2,%0" : "=r" (foo) : "r" (foo), "g" (bar) END
        

Various optimizations or reloading could cause operands 0 and 1 to be in different registers; the compiler 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 the compiler 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:


          CODE
          BEGIN  
       "movc3 %0,%1,%2"
       : COMMENT  no outputs ;
       : "g" (from), "g" (to), "g" (count)
       : "r0", "r1", "r2", "r3", "r4", "r5" END
        

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. the compiler 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 the compiler to not keep memory values cached in registers across the assembler instruction.

You can put multiple assembler instructions together in a single code statement, separated either with newlines (written as \n) or with semicolons. 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:


          CODE
          BEGIN  "movl %0,r9;movl %1,r10;call _foo"
          : COMMENT  no outputs;
          : "g" (from), "g" (to)
          : "r9", "r10" END
        

Unless an output operand has the & constraint modifier, the compiler 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.

If you want to test the condition code produced by an assembler instruction, you must include a branch and a label in the code statement, as follows:


          CODE
          BEGIN  
"clr %0;frob %1;beq 0f;mov #1,%0;0:"
  : "g" (result)
  : "g" (input) END
        

Speaking of labels, jumps from one code statement 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.

If a code statement has output operands, the compiler 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.