C expressions as __asm operands

You can use substitution specifiers in a code format string just as you can in a printf format string. The substitution specifier tells the compiler to substitute the specified C expression into the corresponding __asm operand when it embeds the assembly statement in the HLASM source code. You must ensure that the substitution converts the code format string into a valid assembler instruction.
Note: In this document, operands used in a code format string are referred to as __asm operands.

An embedded assembly statement can use any C-language expression that is in scope as an __asm operand. The constraint tells the compiler what to do with the C expression that follows it.

Substitution of a C variable into an __asm operand

Figure 1 shows an __asm statement that substitutes a C variable into an output __asm operand. Figure 2 shows those assembly instructions.
Figure 1. Substitution of a C variable into an output __asm operand
   void foo() {
     int x;
                           1   2   3 
     __asm ( " ST 12,%0\n" : "=m"(x) :: "r12" );
               4     5 
   }
Notes:
  1. A colon that marks the beginning of the list of output __asm operands, it follows the code format string.
  2. The output __asm operand is "=m"(x). The constraint "m" communicates the syntactic requirement to the compiler:
    • The symbol "=" means the (output) __asm operand will be modified.
    • The letter "m" means that the output __asm operand is a memory operand.
  3. The C expression is the variable x.
  4. The compiler does not know that the embedded assembly instruction is ST, nor does it know the HLASM syntactic requirement of the second ST operand.
  5. The variable x is the first __asm operand in the example, and therefore corresponds to %0 in the code format string.
From the __asm statement used in Figure 1, the compiler embeds the instructions shown in Figure 2 in the generated HLASM source code.
Figure 2. HLASM source code embedded by the compiler
    1       2 
   LA    1,@3x
   ST    12,0(1) 
             3    
Notes:
  1. The LA instruction is inserted by the C compiler as a result of processing the “=m”(x) __asm operand.
  2. @3x is the HLASM symbol name that the compiler assigned to the local variable x. Local C symbol names are mapped to HLASM symbol names so that each local variable has a unique name in the HLASM source file.
  3. 0(1) is substituted into "%0", which specified the first __asm operand in the code format string in Figure 1 (ST 12,%0).

Substitution of a C pointer into an __asm operand

The code format string in Figure 3 invokes the WTO macro by using the execute form of the macro with a user-defined buffer.

In general, you do not control which registers are used during the operand substitution, as illustrated in Figure 3. For an example that allows you to specify registers, see Figure 7.
Figure 3. Substitution of a C pointer into an __asm operand
   int main() {
       struct WTO_PARM {
          unsigned short len;
          unsigned short code;
          char text[80];
       } wto_buff = { 4+11, 0, "hello world" };

               1                    2        3        
       __asm( " WTO  MF=(E,(%0)) " : : "r"(&wto_buff)); 
       return 0;
   }
Notes:
  1. The absence of a label necessitates that a blank space begin the code format string.
  2. There are no output __asm operands. The end of the output __asm operands list is marked by a colon, which is then followed by a comma-separated list of input __asm operands. The colon starting the list of input __asm operands is not necessary if there are no input operands (which is the case in Figure 1).
  3. The input __asm operand consists of two components:
    • A constraint "r" that tells the compiler that the operand will be stored in a GPR.
    • An expression (&wto_buff) that states that the operand is the address of the message text in the C structure wto_buff.

Definition of multiple __asm operands

In Figure 4, the compiler is instructed to store the third defined C variable (z) in a register, and then substitute that register into the third __asm operand %2.
Figure 4. __asm operand lists
   void foo() {
        int x, y, z;
        __asm ( " ST 12,%0\n"
                " ST 12,%1\n"
                 " AR 12,%2" : "=m"(x), "=m"(y) : "r"(z) : "r12" );
                         1         2                  3 
   }
Notes:
  1. The code format string instructs the compiler to embed an assembly statement that substitutes the register (with contents of the C variable z) into the third __asm operand (%2).
  2. The constraint "=m" instructs the compiler to use memory operands for the output variables x and y.
  3. The constraint "r" instructs the compiler to use a register for the input variable z.
Figure 5 shows the compiler-generated HLASM code from the __asm statement in Figure 4. GPR 4 is assigned to the variable z.
Figure 5. Compiler-generated HLASM code from the __asm statement
   L  4,@5z        1 
   LA 2,@4y        2 
   LA 1,@3x        
   ST 12,0(1)      3 
   ST 12,0(2)      
   AR 12,4         
Notes:
  1. The first assembly statement L 4,@5z is added by the compiler to get z into the form specified by the input __asm operand constraint "r".
  2. The next two instructions are added by the compiler to get the variables x and y into the form specified by the output __asm operand constraints "=m".
  3. The contents of the code format string are appended in the last three instructions.

Register specification

In general you do not have control over which registers are used during operand substitutions. The register assignment might change when you use different options or optimization levels, or when the surrounding C code is changed.

In cases where you specify explicit registers to be used in the embedded instructions, you should code a clobber list, as shown in Figure 7. Without the clobber list, the __asm statement embeds incorrect assembly statements, as shown in Figure 6.
Figure 6. Unsuccessful attempt to specify registers
   __asm ( " LR 0,%0\n"       /* load &pl  */
         " LR 1,%1\n"         /* load &dcb */
         " SVC 21"     
                    : :  "r"(&pl), "r"(&dcb));   1 
Note: The output and input __asm operand lists are positional. If there are no output __asm operands, the colons separating the output and input operand list are still needed. Because the compiler has no knowledge of assembly instructions and does not understand the LR instruction, it does not know that the registers GPR 0 and GPR 1 are being used in the statement. Any connection between the __asm statement and the rest of the C code must be specified via the __asm operand lists. The information provided in the lists should prevent the compiler from incorrectly moving the other references surrounding the __asm statement. In this example, because the compiler doesn't know that GPR 0 and GPR 1 are being used, it will embed incorrect assembly statements.
To prevent the compiler from incorrectly moving the other references surrounding the __asm statement, add a clobber list after a colon that follows the input __asm operands, as shown in Figure 7.
Note: Do not try to use the __asm statement to embed a long piece of assembly code with many operand specifiers and stringent register requirements. There is a limited number of registers available for the compiler to use in the operand specifiers, and in the surrounding code generation. If too many registers are clobbered, there may not be enough registers left for the __asm statement. The same applies if there are too many specifiers.
Figure 7. Register specification with clobbers
   __asm ( " LR 0,%0\n"       /* load &pl  */
         " LR 1,%1\n"         /* load &dcb */
         " SVC 21"     
                    : :  "r"(&pl), "r"(&dcb) : “r0”,“r1”);
                                             1     2 
Notes:
  1. This colon is not needed if there is no clobber list.
  2. The clobber list specifies the registers that can be modified by the assembly instructions.