The assembler template contains the assembly instruction sets inserted into the C program. The format is as follows: each instruction is circled with double quotation marks, or the entire instruction group is circled with double quotation marks. At the same time, each command should end with a separator. Valid delimiters include line breaks ("/n") and semicolons (";"). "/N" can be followed by a tab ("/t "). Should we all understand why line breaks or tabs are used )? The operands corresponding to the C expression are represented by % 0, % 1... and so on.
2. operandsThe C expression is used as the number of assembler commands in "asm. Each operand is subject to an operand enclosed by double quotation marks. There is also a constraint modifier in the quotation marks for the output operands, followed by a C expression used to represent the operands. That is, the "operand constraint" (C expression) is a common format. There is an additional modifier for the output operand. The constraint string is mainly used to determine the addressing method of the operands and also to specify the registers used.
If we use more than one operand, each operand is separated by a comma.
In the assembler template, each operand is referenced by a number. The numbering method is as follows. If there are n operands (including input and output operands) in total, the first output operand number is 0, increasing one by one, and the last input operand number is n-1. The maximum number of operands is described in the previous section.
The output operand expression must be a left value. The requirements for input operands are not as strict as they are. They can be expressions. Extended Assembly features are often used for machine commands not known to the compiler ;-). If the output expression cannot be directly addressable (that is, it is a single-bit field), Our constraint string must be given a register. In this case, GCC will use this register as the output of the Assembly, and then store the content of this register to the output.
As stated above, normal output operations must be written only. GCC assumes that the Operation values before the command are dead and do not need to be generated (in advance. Extended Assembly also supports input-output or read-write operations.
So now let's take a look at some examples. We want to request a result of the fifth power of a number. To calculate this value, we use the "lea" command.
asm ("leal (%1,%1,4), %0" : "=r" (five_times_x) : "r" (x) );
Here we enter x. We do not specify the registers used. GCC will select some input registers and one output register for our expected work. If we want to put the input and output in the same register, we can also require GCC to do this. Here we use the read-write operand types. Here we can implement it by specifying the appropriate constraints.
asm ("leal (%0,%0,4), %0" : "=r" (five_times_x) : "0" (x) );
The output and output operations are in the same register. However, we cannot know which register is used. Now, if we want to specify the register where the operand is located, here is a method.
asm ("leal (%%ecx,%%ecx,4), %%ecx" : "=c" (x) : "c" (x) );
In the above three examples, we have not added any registers to the modifier register list. Why? In the first two examples, GCC determines the register and it knows what has changed. In the last example, we do not need to add 'ecx' to the modifier register list (LCTT: The word spelling in the source modifier register list is incorrect, which has been corrected here). gcc knows that it represents x. Therefore, because it can know the value of "ecx", it will not be regarded as a modifier (register.
3. Modify the register listSome commands may corrupt some hardware register content. We have to list these registers in the modifier register, that is, the third in the assembler function'. This notifies gcc that we will use and modify these registers by ourselves, so that gcc will not assume that the values stored in these registers are valid. We do not need to list the input and output registers in this list. Because gcc knows that "asm" uses them (because they are explicitly specified as constraints ). If the instruction uses any other register implicitly or explicitly (and the Register does not appear in the output or output Constraints List), you need to specify these registers in the modifier register list.
If our command can modify the condition code register (cc), we must add "cc" to the modification register list.
If our commands modify the memory in an unpredictable way, you need to add "memory" to the modified register list. This prevents GCC from keeping the memory value cached in the register between Assembly commands. If the affected memory is not in the input or output list of the Assembly, you must add the "volatile" keyword.
We can read and write modifier registers multiple times as needed. Refer to the multi-instruction example in the template. It assumes that the subroutine _ foo accepts the parameters in registers "eax" and "ecx.
asm ("movl %0,%%eax; movl %1,%%ecx; call _foo" : /* no outputs */ : "g" (from), "g" (to) : "eax", "ecx" );
4. Volatile ...?If you are familiar with the kernel source code or similar beautiful code, you must have seen many functions declared as "volatile" or "_ volatile, it is followed by an "asm" or "_ asm __". I have mentioned the keywords "asm" and "_ asm __". So what is volatile?
If our Assembly statement must be executed where we place it (for example, it cannot be removed from the loop statement for optimization), place the keyword "volatile" behind asm ,(). To prevent it from being moved, deleted, or otherwise, we declare it as "asm volatile (...:...:...:...);"
If you are worried about a conflict, use "_ volatile __".
If our compilation is only used for some calculations and has no side effects, it would be better not to use the "volatile" keyword. Without "volatile", gcc can optimize the code and make the code more beautiful.
In the "Some Practical Tips" section, I provide examples of multiple inline assembler functions. We can see the details of the modified register list.
6.