Associating C language variables with instruction operands based on gcc

Sometimes we want to use embedded assembly in C/C++ code because there is no corresponding function or syntax available in C. For example, when I recently wrote the FIR program on ARM, I needed to saturate the final result, but gcc did not provide a function like ssat, so I had to embed the assembly instructions in the C code.

1. Getting started

The biggest problem with embedding assembly in C is how to associate C language variables with instruction operands. Of course, gcc has helped us think about it. Below is a simple example.

Asm("fsinx %1, %0":"=f"(result):"f"(angle));

Here we don't need to worry about the fsinx instruction being dry; just know that this instruction requires two floating point registers as operands. As a full-time processing of the C language gcc compiler, it is impossible to know what kind of operands fsinx assembly instructions need, which requires the program to tell gcc related information, the method is the "=f" and "f" after the instruction ", indicating that this is two floating-point register operands. This is called the operand constraint. Adding "=" to the rule indicates that this is an output operand, otherwise it is an input operand. Inside the brackets after the constraint are the variables associated with this register. This way gcc knows how to turn this embedded assembly statement into the actual assembly instructions:

Fsinx: assembly instruction name

%1, %0: assembly instruction operands

"=f" (result): The operand %0 is a floating-point register, associated with the variable result (for the output operand, "associated" means that gcc will put the contents of register %0 after executing this assembly instruction. Sent to the variable result)

"f" (angle): operand %1 is a floating-point register, associated with the variable angle (for the input operand, "associated" means that gcc will read the value of the variable angle before executing this assembly instruction. Fetched into register %1)

So this embedded assembly will be converted to at least three assembly instructions (non-optimized):

Load the value of the angle variable into register %1

Fsinx assembly instruction, source register %1, destination register %0

Store the value of register %0 to the variable result

Of course, the above description may not apply at high optimization levels; for example, the source operand may already be in a floating point register.

Here we also see the meaning of the "=" sign before the constraint: gcc needs to know whether this operand is loaded from the variable into the register before executing the embedded assembly, or from the register to the variable after execution.

The following commonly used constraints are available (see the gcc manual for more details):

m memory operand

r register operand

i immediate operand (integer)

f floating point register operand

F immediate operand (floating point)

The basic format of the embedded assembly can also be seen from this chestnut:

Asm ("assembly instruction":" = output operand rule" (associated variable): "input operand rule" (associated variable));

The output operand must be an lvalue; this is obvious.

2. Multiple operands, or no output operands

What if an instruction has multiple input or output operands? For example, arm has many instructions that are three-operand instructions. This time separates multiple rules with commas:

Asm("add %0, %1, %2":"=r"(sum):"r"(a), "r"(b));

Each operand rule corresponds to the operands %0, %1, %2 in order.

In the case of no output operands, there is no output rule after the assembly instruction, so two consecutive colons appear followed by the input rules.

3. Input-output (or read-write) operands

Sometimes an operand is both an input and an output, such as the one under x86:

Add %eax, %ebx

Note that the instructions use the AT&T format instead of the Intel format. Register ebx acts as both an input operand and an output operand. For such operands, use the "+" character before the rule:

Asm("add %1, %0" : "+r"(a) : "r"(b));

Corresponding to the C language statement a=a+b.

Note that such an operand cannot use the "=" symbol, because gcc sees the "=" symbol as a single-output operand, so the value of the variable a is not pre-intended when converting the embedded assembly to true assembly. Loaded into register %0.

Another way is to logically split the read-write operand into two operands:

Asm("add %2, %0" : "=r"(a) : "0" (a), "r" (b));

Specifying the numeric rule "0" for the "logical" input operand 1 indicates that this logical operand occupies the same "position" as the operand 0 (occupying the same register). This method is characterized by the ability to associate two "logical" operands to two different C language variables:

Asm("add %2, %0" : "=r"(c) : "0"(a), "r"(b));

Corresponds to the C program statement c=a+b.

Numeric rules can only be used to enter operands and must be referenced to the output operand. In the example above, the numeric rule "0" is located in the input rule segment and is referenced to the output operand 0, which itself occupies an operand count of one.

It should be noted here that there is no guarantee that two operands occupy the same "location" by the same name C language variable. For example, the following way of writing is not acceptable:

(error writing) asm("add %2, %0":"=r"(a): "r"(a), "r"(b));

4. Specify register

Sometimes we need to use the specified register in the instruction; a typical chestnut is a system call, and the system call code and parameters must be placed in the specified register. To do this, we want to use the extended syntax when declaring variables:

Register int a asm("%eax") = 1; // statement 1

Register int b asm("%ebx") = 2; // statement 2

Asm("add %1, %0" : "+r"(a) : "r"(b)); // statement 3

Note that only when executing the assembly instructions can be determined that a is in eax, b is in ebx, and that the storage locations of a and b are unknown at other times.

In addition, be careful when using this to prevent statement 2 from overwriting eax during execution. For example, statement 2 is changed to the following sentence:

Register int b asm("%ebx") = func();

The function calling convention puts the return value of func() in eax, thus destroying the assignment of statement 1 to a. At this time, you can use a statement to put the func return value in a temporary variable:

Int t = func();

Register int a asm("%eax") = 1; // statement 1

Register int b asm("%ebx") = t; // statement 2

Asm("add %1, %0" : "+r"(a) : "r"(b)); // statement 3

5. Implicitly changing registers

Some assembly instructions implicitly modify some registers that are not in the instruction operand. In order for gcc to know this, the implicit change register rules are listed after the input rules. Here are the chestnuts on the VAX machine:

Asm volatile("movc3 %0,%1,%2"

: /* no outputs */

:"g"(from),"g"(to),"g"(count)

: "r0", "r1", "r2", "r3", "r4", "r5");

(movc3 is a move characters command)

It should be noted here that the registers listed in the input/output rules cannot intersect with the registers in the implicit change rules. For example, in the chestnut above, the rule "g" cannot contain r0-r5. Variables declared with the specified register syntax cannot occupy registers that intersect with implicit change rules. This should be well understood: the implicit change rule tells gcc that there are extra registers to take care of, and naturally there is no intersection with the input/output registers.

In addition, if you explicitly specify a register in an instruction, then this register must also be listed in the implicit change rule (a bit around). We said above that gcc itself does not understand assembly instructions, so the registers you explicitly specify in the instruction are implicit to gcc and must be included in the implicit rules. In addition, an additional % is required before the explicit register in the instruction, such as %%eax.

Volatile

Asm volatile notification gcc your assembly instructions have a side effect, do not give optimization, such as the chestnut above.

If your instructions are just doing some calculations, then you don't need volatile, so gcc can optimize it; otherwise, it's a good idea to add volatile to each asm without brain.

Associating C language variables with instruction operands based on gcc

USB 3.2 Cable

The USB 3.2 specification absorbed all prior 3.x specifications. USB 3.2 identifies three transfer rates – 20Gbps, 10Gbps, and 5Gbps.

Key characteristics of the USB 3.2 specification include:

Defines multi-lane operation for new USB 3.2 hosts and devices, allowing for up to two lanes of 10Gbps operation to realize a 20Gbps data transfer rate, without sacrificing cable length
Delivers compelling performance boosts to meet requirements for demanding USB storage, display, and docking applications
Continued use of existing USB physical layer data rates and encoding techniques
Minor update to hub specification to address increased performance and assure seamless transitions between single and two-lane operation
Improved data encoding for more efficient data transfer leading to higher through-put and improved I/O power efficiency
Backwards compatible with all existing USB products; will operate at lowest common speed capability

Usb 3.2 Cable,Usb Type-C Cable,5Gbps Usb Type-C Cable,10Gbps Usb Type-C Cable

UCOAX , https://www.jsucoax.com