Output Variables in Inline Assembler (GNAT User’s Guide for Native Platforms)

### 10.3 Output Variables in Inline Assembler ¶

The examples in this section, showing how to access the processor flags, illustrate how to specify the destination operands for assembly language statements.

>     with Interfaces; use Interfaces;
>     with Ada.Text_IO; use Ada.Text_IO;
>     with System.Machine_Code; use System.Machine_Code;
>     procedure Get_Flags is
>        Flags : Unsigned_32;
>        use ASCII;
>     begin
>        Asm ("pushfl"          & LF & HT & -- push flags on stack
>             "popl %%eax"      & LF & HT & -- load eax with flags
>             "movl %%eax, %0",             -- store flags in variable
>             Outputs => Unsigned_32'Asm_Output ("=g", Flags));
>        Put_Line ("Flags register:" & Flags'Img);
>     end Get_Flags;

We have separated multiple assembler statements in the Asm template string with linefeed (ASCII.LF) and horizontal tab (ASCII.HT) characters in order to have a nicely aligned assembly listing. The resulting section of the assembly output file is:

>     #APP
>        pushfl
>        popl %eax
>        movl %eax, -40(%ebp)
>     #NO_APP

It would have been legal to write the Asm invocation as:

>     Asm ("pushfl popl %%eax movl %%eax, %0")

but in the generated assembler file, this would come out as:

>     #APP
>        pushfl popl %eax movl %eax, -40(%ebp)
>     #NO_APP

which is not so convenient for the human reader.

We use Ada comments at the end of each line to explain what the assembler instructions actually do. This is a useful convention.

When writing Inline Assembler instructions, you need to precede each register and variable name with a percent sign. Since the assembler already requires a percent sign at the beginning of a register name, you need two consecutive percent signs for such names in the Asm template string, thus `%%eax`. In the generated assembly code, one of the percent signs will be stripped off.

Names such as `%0`, `%1`, `%2`, etc., denote input or output variables: operands you later define using `Input` or `Output` parameters to `Asm`. An output variable is shown in the third section of the Asm template string:

>     movl %%eax, %0

The intent of this section is to store the contents of the `eax` register in a variable that can be accessed in Ada. Simply writing `movl %%eax, Flags` would not necessarily work, since the compiler might optimize by using a register to hold `Flags`, and the expansion of the `movl` instruction would not be aware of this optimization. The solution is not to store the result directly but rather to advise the compiler to choose the correct operand form; that is the purpose of the `%0` output variable.

Information about the output variable is supplied in the `Outputs` parameter to `Asm`:

>     Outputs => Unsigned_32'Asm_Output ("=g", Flags));

The output is defined by the `Asm_Output` attribute of the target type; the general format is:

>     Type'Asm_Output (constraint_string, variable_name)

The constraint string directs the compiler how to store/access the associated variable. In the example:

>     Unsigned_32'Asm_Output ("=m", Flags);

the `"m"` (memory) constraint tells the compiler that the variable `Flags` should be stored in a memory variable, thus preventing the optimizer from keeping it in a register. In contrast,

>     Unsigned_32'Asm_Output ("=r", Flags);

uses the `"r"` (register) constraint, telling the compiler to store the variable in a register.

If you precede the constraint with the equal character (‘=’), it tells the compiler that the variable will have data stored into it.

In the `Get_Flags` example, we used the `"g"` (global) constraint, allowing the optimizer to choose whatever operand it deems best.

There are a fairly large number of constraints, but the ones that are most useful for the Intel x86 processor are the following:

> ‘=’ | output constraint
> ‘g’ | global (i.e., can be stored anywhere)
> ‘m’ | in memory
> ‘I’ | a constant
> ‘a’ | use eax
> ‘b’ | use ebx
> ‘c’ | use ecx
> ‘d’ | use edx
> ‘S’ | use esi
> ‘D’ | use edi
> ‘r’ | use one of eax, ebx, ecx or edx
> ‘q’ | use one of eax, ebx, ecx, edx, esi or edi

The full set of constraints is described in the `gcc` and `as` documentation; note that you can combine certain constraints into one constraint string.

You specify the association of an output variable with an assembler operand through the `%n` notation, where ‘n’ is a non-negative integer. Thus in

>     Asm ("pushfl"          & LF & HT & -- push flags on stack
>          "popl %%eax"      & LF & HT & -- load eax with flags
>          "movl %%eax, %0",             -- store flags in variable
>          Outputs => Unsigned_32'Asm_Output ("=g", Flags));

`%0` is replaced in the expanded code by the appropriate operand, whatever the compiler chose for the `Flags` variable.

In general, you may have any number of output variables:

- Count the operands starting at 0; thus `%0`, `%1`, etc.
- Specify the `Outputs` parameter as a parenthesized comma-separated list of `Asm_Output` attributes

For example:

>     Asm ("movl %%eax, %0" & LF & HT &
>          "movl %%ebx, %1" & LF & HT &
>          "movl %%ecx, %2",
>          Outputs => (Unsigned_32'Asm_Output ("=g", Var_A),   --  %0 = Var_A
>                      Unsigned_32'Asm_Output ("=g", Var_B),   --  %1 = Var_B
>                      Unsigned_32'Asm_Output ("=g", Var_C))); --  %2 = Var_C

where `Var_A`, `Var_B`, and `Var_C` are variables in the Ada program.

As a variation on the `Get_Flags` example, we can use the constraint string to direct the compiler to store the `eax` register into the `Flags` variable, instead of including the store instruction explicitly in the `Asm` template string:

>     with Interfaces; use Interfaces;
>     with Ada.Text_IO; use Ada.Text_IO;
>     with System.Machine_Code; use System.Machine_Code;
>     procedure Get_Flags_2 is
>        Flags : Unsigned_32;
>        use ASCII;
>     begin
>        Asm ("pushfl"      & LF & HT & -- push flags on stack
>             "popl %%eax",             -- save flags in eax
>             Outputs => Unsigned_32'Asm_Output ("=a", Flags));
>        Put_Line ("Flags register:" & Flags'Img);
>     end Get_Flags_2;

The `"a"` constraint tells the compiler that the `Flags` variable will come from the `eax` register. Here is the resulting code:

>     #APP
>        pushfl
>        popl %eax
>     #NO_APP
>        movl %eax,-40(%ebp)

The compiler generated the store of eax into Flags after expanding the assembler code.

In fact, there was no need to pop the flags into the `eax` register; more simply, we could just pop the flags directly into the program variable:

>     with Interfaces; use Interfaces;
>     with Ada.Text_IO; use Ada.Text_IO;
>     with System.Machine_Code; use System.Machine_Code;
>     procedure Get_Flags_3 is
>        Flags : Unsigned_32;
>        use ASCII;
>     begin
>        Asm ("pushfl"  & LF & HT & -- push flags on stack
>             "pop %0",             -- save flags in Flags
>             Outputs => Unsigned_32'Asm_Output ("=g", Flags));
>        Put_Line ("Flags register:" & Flags'Img);
>     end Get_Flags_3;