How to use the assembly instruction template of inline assembly in C code

Assembly instruction template
Assembly template: An
assembler template is a text string containing assembler instructions.
The compiler replaces the tags that reference input, output, and goto tags in the template, and then outputs the generated string to the assembler.
The string can contain any instructions recognized by the assembler, including indicators.
GCC does not analyze the assembler instructions themselves, nor does it know their meaning, or even whether they are valid assembler input.
You can put multiple assembler instructions in an asm string, separated by the characters usually used in the system assembly code.
The combination that works in most places is a newline character, plus a tab to move to the instruction field (written as'\n\t').
Some assemblers allow semicolons as line separators. However, please note that some assembly languages ​​use semicolons to start comments.
Do not expect the sequence of asm statements to remain completely continuous after assembly, even if the volatile qualifier is used.
If some instructions need to be continuous in the output, please put them in a multi-instruction asm statement.
If you do not use input/output operands (for example, directly use the global symbols in the assembler template), you can access data from the C program. It does not work as expected.
Similarly, calling functions directly from the assembler template requires a detailed understanding of the target assembler and ABI.
Since GCC does not parse assembler templates, it is not visible to any symbols referenced.
This may cause GCC to discard these symbols as unreferenced unless they are also listed as input, output, or goto operands.

Special format string:
In addition to the tokens described by the input, output and goto operands, the following tokens have special meanings in the assembler template:
'%%':
output a single'%' to assembly In the program code.
'%=':
Output the number unique to each instance of the asm statement throughout the compilation process
. This option is very useful when creating local tags in a single template that generates multiple assembler instructions and quoting them multiple times.
'%{ ':
'%|':
'%}':
correspondingly output '{','| and'}' to the assembler code.

Multiple assembly languages ​​in asm templates:

Output operands: The
asm statement has zero or more output operands, which represent the names of C variables modified by assembly code.
Example:
In this i386 example, old (called %0 in the template string) and *Base (as %1) are the output, and Offset (%2) is the input:
bool old;

		__asm__ ("btsl %2,%1\n\t" // Turn on zero-based bit #Offset in Base.
				 "sbb %0,%0"      // Use the CF to calculate old.
		   : "=r" (old), "+rm" (*Base)
		   : "Ir" (Offset)
		   : "cc");

		return old;

The syntax format of output operands:
Operands are separated by commas.
Constraints
|
V
[[asmSymbolicName]] constraint (cvariablename)

asmSymbolicName:

Specify a symbolic name for the operand.
Refer to the name in the assembler template by enclosing the name in square brackets (ie, '%[Value]').
The scope of the name is to accommodate the asm statement that defines it.
Any valid C variable name is acceptable, including names already defined in surrounding code.
No two operands in the same asm statement can use the same symbolic name.
When asmSymbolicName is not used, please use the (zero-based) position of the operand in the operand list in the assembler template.
For example, if there are three output operands, use'%0' to refer to the first operand,'%1' to refer to the second operand, and'%2' to refer to the third operand in the template.

constraint:

A string constant specifying the position constraint of the operand.
Output constraints must start with '=' (variables that overwrite existing values) or '+' (for reading and writing).
When using '=', do not assume that the position contains the existing value at the asm entry, unless the operand is bound to the input.
After the prefix, there must be one or more additional constraints to describe the position of the value.
Common constraints include r: for register and m: for memory.
When you list multiple possible locations (for example, "=rm"), the compiler will choose the most effective location based on the current context.
If List as many alternatives as possible within the scope of the asm statement, allowing the optimizer to generate the best possible code.
If you must use a specific register, but your machine constraints do not provide enough control to select the specific register you want, then local register variables can provide a solution

cvariablename:

Specify a C lvalue expression to save the output, usually a variable name.
Brackets are a required part of the grammar

输出操作数表达式必须是左值。使用“+”约束修饰符的操作数被计算为两个操作数(即同时作为输入和输出),每个asm语句的操作数最多为30个

Use the '&' constraint modifier for all output operands that cannot overlap with the input.
Otherwise, GCC can allocate the output operand as an unrelated input operand in the same register, provided that the assembly code uses its input before generating the output. If the assembler code actually contains more than one instruction, then this assumption may be wrong.
If one output parameter (a) allows register constraints, and another output parameter (b) allows memory constraints, the same problem will occur.
The code generated by GCC to access the memory address in b can include registers that may be shared by a, and GCC considers these registers to be the input of asm.
As mentioned above, GCC assumes that these input registers must be used before writing any output.
This assumption may lead to incorrect behavior if the asm statement is written using b. Combine the "&" modifier and register constraints to ensure that the modification does not affect the address reference b. Otherwise, b is an undefined position if you modify b before use.
asm supports operand modifiers on operands (such as "%k2" instead of simply "%2"). Usually, these qualifiers depend on the hardware.
If the C code behind asm does not use any output operands, use the volatile for asm statement to prevent the optimizer from discarding the asm statement when it is not needed.
This code does not use the optional asmSymbolicName. Therefore, it references the first output operand as %0 (or %1 if there is a second operand, and so on). The number of first input operands is greater than the number of last output operands. In this i386 example, it makes the Mask reference %1:
uint32_t Mask = 1234;
uint32_t Index;

	  asm ("bsfl %1, %0"
		 : "=r" (Index)
		 : "r" (Mask)
		 : "cc");

该代码覆盖变量Index(' = '),将该值放在寄存器(' r ')中。

Using generic "r" constraints instead of specific register constraints allows the compiler to choose which registers to use, which can produce more efficient code.
If the assembler instructions require specific registers, this may not be possible.
The i386 example below uses the asmSymbolicName syntax.
It produces the same result as the code above, but some people may think it is easier to read or easier to maintain, because there is no need to reorder the index numbers when adding or removing operands.
The names aIndex and aMask are only used in this example to emphasize which names are used where. The name index and mask can be reused.
uint32_t Mask = 1234;
uint32_t Index;

	  asm ("bsfl %[aMask], %[aIndex]"
		 : [aIndex] "=r" (Index)
		 : [aMask] "r" (Mask)
		 : "cc");



下面是一些输出操作数的示例。
	uint32_t c = 1;
	uint32_t d;
	uint32_t *e = &c;

	asm ("mov %[e], %[d]"
	   : [d] "=rm" (d)
	   : [e] "rm" (*e));		

这里,d可以在寄存器中,也可以在内存中。
由于编译器可能已经在寄存器中有e所指向的uint32_t位置的当前值,所以可以通过指定这两个约束来让它为d选择最佳位置。

Input operand: The
input operand makes the values ​​of variables and expressions from C available to assembly code.
Use commas to separate operands.
The syntax format of the input operand:
[[asmSymbolicName]] constraint (cexpression)

asmSymbolicName:

Specify symbolic names for the operands.
Refer to the name in the assembler template by enclosing the name in square brackets (ie,'%[Value]').
The scope of the name is to include the asm statement that defines it.
Any valid C variable name is acceptable, including names already defined in surrounding code.
No two operands in the same asm statement can use the same symbolic name.
When asmSymbolicName is not used, please use the (zero-based) position of the operand in the operand list in the assembler template.
For example, if there are two output operands and three inputs, use '%2' in the template to refer to the first input operand, '%3' to refer to the second, and '%4' to refer to the third.
constraint:
A string constant specifying the position constraint of the operand. The
input constraint string cannot start with'=' or'+'.
When you list multiple possible positions (for example, "irm"), the compiler will choose according to the current context The most effective location.
If you must use specific registers, but your machine constraints do not provide enough control to select the specific registers you want, then local register variables can provide a solution.
Input constraints can also be numbers (for example, "0"). This means that the specified input must be in the same position as the output constraint at the index (starting from zero) in the output constraint list. When using asmSymbolicName syntax for output operands, you can use these names (enclosed in brackets "[]") instead of numbers.
cexpression:
This is the C variable or expression passed as input to the asm statement. Brackets are a necessary part of the grammar.
If there is no output operand, but there is an input operand, place two consecutive colons in the position of the output operand:
asm (“some instructions”
: /* No outputs. */
: “r” (Offset / 8));

警告:不要修改仅输入操作数的内容(与输出绑定的输入除外)。编译器假设从asm语句中退出时,这些操作数包含与执行语句之前相同的值。
不可能使用clobbers通知编译器这些输入中的值正在更改。一种常见的解决方法是将正在更改的输入变量绑定到从未使用过的输出变量。

However, please note that if the code following the asm statement does not use any output operands, the GCC optimizer may discard the asm statement as an unnecessary statement (see Volatile).
In this example, a fictitious combine instruction is used for input operations The constraint "0" of number 1 means that it must occupy the same position as the output operand 0. Only input operands can use numbers in constraints, and they must each refer to an output operand.
Only one number (or symbolic assembler name) in the constraint can guarantee that one operand is in the same position as the other operand.
The fact that foo is the value of the two operands is not enough to guarantee that they are in the same position in the generated assembly code.
asm ("combine %2, %0"
: "=r" (foo)
: "0" (foo), "g" (bar));

下面是一个使用符号名称的示例
	asm ("cmoveq %1, %2, %[result]" 
	   : [result] "=r"(result) 
	   : "r" (test), "r" (new), "[result]" (old));

Clobbers and Scratch Registers:
Although the compiler is aware of changes to the entries listed in the output operands, inline asm code may modify more than just the output.
For example, calculations may require additional registers or as a side effect of specific assembly instructions. The processor may overwrite registers.
In order to notify the compiler of these changes, please list them in the clobber list.
The Clobber list item can be a register name or a special Clobber (as shown below). Each clobber list item is a string constant, enclosed in double quotes and separated by commas.
Clobber descriptions cannot overlap with input or output operands in any way.
Another limitation is that the clobber list should not contain stack pointer registers.
This is because the compiler requires the value of the stack pointer to be the same after the asm statement as at the statement entry.
However, the previous version of GCC did not enforce this rule, allowing stack pointers to appear in the list, and the semantics were unclear. This behavior is deprecated, and listing the stack pointer may become an error in future versions of GCC.

下面是一个实际的VAX例子,展示了如何使用Clobber寄存器:
	asm volatile ("movc3 %0, %1, %2"
					   : /* No outputs. */
					   : "g" (from), "g" (to), "g" (count)
					   : "r0", "r1", "r2", "r3", "r4", "r5", "memory");

此外,还有两个特殊的clobber参数:

"Cc":
"cc" clobber means assembler code modification flag register.
On some machines, GCC represents the condition code as a specific hardware register; "cc" is used to name this register. On other machines, condition code processing is different, and specifying "cc" has no effect. But no matter what the goal is, it is effective.
"Memory":
"memory" clobber tells the compiler that the assembly code performs memory reads and writes for items other than those listed in the input and output operands (for example, access to the memory pointed to by one of the input parameters).
In order to ensure that the memory contains the correct value, GCC may need to flush specific register values ​​to the memory before executing asm.
In addition, the compiler does not assume that any values ​​read from memory before asm remain unchanged after asm; it will reload them as needed. Using "memory" clobber can effectively form a read/write memory barrier for the compiler.
Note that this clobber does not prevent speculative reads after the processor executes the asm statement. To prevent this, you need processor-specific fence instructions.
Flushing registers into memory affects performance and can be a problem for time-sensitive code. You can provide better information to GCC to avoid this situation, as shown in the example below.
At the very least, the aliasing rules allow GCC to know which memory does not need to be flushed.
Here is a fictitious sum-of-squares instruction, which accepts two pointers to floating-point values ​​in memory and generates a floating-point register output. Note that both x and y appear in the asm parameter twice, once to specify the memory to be accessed, and once to specify the base register used by asm.
This usually does not waste registers, because GCC can use the same registers for both purposes. However, if you use both %1 and %3 to represent x in asm and expect them to be the same, it would be stupid.
In fact, %3 is probably not a register. It may be a symbolic memory reference to the object pointed to by x.
asm (“sumsq %0, %1, %2”
: “+f” (result)
: “r” (x), “r” (y), “m” (*x), “m” (*y));

这里是一个虚构的*z++ = *x++ * *y++指令。注意,必须将x、y和z指针寄存器指定为输入/输出,因为asm会修改它们。
	asm ("vecmul %0, %1, %2"
		 : "+r" (z), "+r" (x), "+r" (y), "=m" (*z)
		 : "m" (*x), "m" (*y));	

一个x86示例,其中字符串内存参数的长度未知。
	asm("repne scasb"
		: "=c" (count), "+D" (p)
		: "m" (*(const char (*)[]) p), "0" (-1), "a" (0));


如果您知道上面只读取一个10字节数组,那么您可以使用类似于这样的内存输入:
		"m" (*(const char (*)[10]) p)

Goto Label:
asm goto allows assembly code to jump to one or more C labels.
The GotoLabels part of the asm goto statement contains a comma-separated list of all C labels that the assembly code may jump to.
GCC assumes that the execution of asm will continue to the next statement (if this is not the case, please consider using __builtin_unreachable after the asm statement)
The optimization of asm goto can be improved by using hot and cold tag attributes to improve the
asm goto statement without output.
It is due to the internal limitation of the compiler: control transmission instructions cannot have output.
If the assembly code does modify anything, then use the "memory" clobber to force the optimizer to refresh all register values ​​into memory, and reload as needed after the asm statement they.
Also note that asm goto statements are always implicitly considered volatile statements.
To quote a label in the assembler template, add "%l" (lowercase "l")
in front of it, and add its (zero-based) position and input operand in GotoLabels.
For example, if asm has three inputs and references two tags, the first tag is referenced as "%l3" and the second tag is referenced as "%l4").
In addition, you can refer to tags using the actual C tag name in parentheses. For example, to quote a tag named carry, you can use "%l[carry]".
When using this method, the labels must still be listed in the GotoLabels section.
The following is an example asm goto for i386:
asm goto (
"btl %1, %0\n\t"
"jc %l2"
: /* No outputs. */
: “r” (p1), “r” (p2)
: “cc”
: carry);

	return 0;

	carry:
	return 1;


下面的例子显示了一个使用内存clobber的asm goto。	
	int frob(int x)
	{
	  int y;
	  asm goto ("frob %%r5, %1; jc %l[error]; mov (%2), %%r5"
				: /* No outputs. */
				: "r"(x), "r"(&y)
				: "r5", "memory" 
				: error);
	  return y;
	error:
	  return -1;
	}

Guess you like

Origin blog.csdn.net/wzc18743083828/article/details/100543968