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
A colon that marks the beginning of the list of output __asm operands,
it follows the code format string.
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.
The C expression is the variable x.
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.
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:
The LA instruction is inserted by the C compiler as a result of
processing the “=m”(x)__asm operand.
@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.
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
The absence of a label necessitates that a blank space begin the
code format string.
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).
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:
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).
The constraint "=m" instructs the compiler to
use memory operands for the output variables x and y.
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:
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".
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".
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
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.