asm is a keyword in gcc, and the asm expression is a nested assembly instruction in C code. This expression simply replaces the assembly code and does not parse the meaning of the assembly code.
There are two forms of asm expression, the secondasm-qualifiers
Includedgoto
Statement.
The first form is a common usage, and AssemblerTemplate and OutputOperands must exist, where Clobbers exist requires InputOperands also appears.
asm asm-qualifiers ( AssemblerTemplate : OutputOperands [ : InputOperands [ : Clobbers ] ]) asm asm-qualifiers ( AssemblerTemplate : : InputOperands : Clobbers : GotoLabels)
Types of Qualifiers
volatile, avoid compiler optimization inline, constrained qualifier, minimum volume goto, including jump instructions
parameter
AssemblerTemplate
- The assembly instruction template is a literal string containing the assembler instruction. The editor replaces the reference input, and the compiler will not parse the meaning of the instruction. OutputOperands
- Comma-separated list of C variables modified by directives in AssemblerTemplate, allowing use of empty lists. InputOperands
- Comma-separated list of C variables read by directives in AssemblerTemplate, allowing use of empty lists. Clobbers
- Comma-separated register lists or values modified by AssemblerTemplate cannot appear in OutputOperands and InputOperands and are allowed to use empty lists. GotoLabels
- When using the goto form of asm, this section contains a list of all C tags that the code in AssemblerTemplate may jump to.
AssemblerTemplate
The assembly instruction is given by a string. When multiple assembly instructions are combined, the\r\t
Separate, such as
asm("inc %0\n\tinc %0" : "=r"(res) : "0"(res)); /APP # 11 "" 1 inc %rax inc %rax # 0 "" 2 /NO_APPs
Characters that need to be escaped:%
, =
, {
, }
, |
Therefore, in ATT assembly, the operation of registers requires double %%, such asinc %%rax
.
OutputOperands
Operands are separated by commas. Each operand has the following format:
[ [asmSymbolicName] ] constraint (cvariablename)
asmSymbolicName
- Specify a name for the operand, in the format%[name]
c // res = num asm("movq %[num], %[res]" : [res] "=r"(res) : [num] "m"(num));
- If the name is not specified, start from the output field, the first parameter is %0, and so on, here res is %0, num is %1c // res = num asm("movq %1, %0" : "=r"(res) : "m"(num));
constraint
- A string constant that specifies the storage of operandsconstraint, need to start with "=" or "+" cvariablename
- Specifies a C lvalue expression to hold the output, usually a variable name. Brackets are a necessary part of the syntax
The first parameter is used to increase readability. Now we have the code as follows
int64_t res; int64_t num = 1; asm("movq %[num], %[res]" : [res] "=r"(res) : [num] "m"(num)); asm("movq %1, %0" : "=r"(res) : "m"(num)); asm("movq %1, %0" : "=m"(res) : "m"(num)); asm("movq %1, %0" : "=r"(res) : "r"(num)); // The corresponding assembly code only retains the code in the asm expression# 13 "" 1 movq -16(%rbp), %rax // asm-1 # 0 "" 2 /NO_APP /APP # 15 "" 1 movq -16(%rbp), %rax // asm-2 # 0 "" 2 /NO_APP /APP # 17 "" 1 movq -16(%rbp), -8(%rbp) // asm-3 # 0 "" 2 /NO_APP /APP # 19 "" 1 movq %rax, %rax // asm-4 # 0 "" 2 /NO_APP
- Use name replacement and number replacement effects, see the usage of asm-1 and asm-2 constraints. Here are two simple and common situations.
r
For register addressing operations,m
Through memory addressing operations, so see - Constrained
r
It corresponds to the operation of the register. - The result is saved in res, that is, cvariablename
InputOperands
Enter operands to make the values in C variables and expressions available for assembly code.
[ [asmSymbolicName] ] constraint (cexpression)
The usage of asmSymbolicName and the output list are exactly the same
constraint constraint cannot be used=
and+
. You can use "0", which indicates that the specified input must be in the same position as the output constraints in the list of (from zero) of the output constraints.
int64_t res = 3; int64_t num = 1; asm("addq %1, %0" : "=g"(res) : "0"(num)); // The input and output positions are the same movq $3, -8(%rbp) movq $1, -16(%rbp) movq -16(%rbp), %rax /APP # 32 "" 1 addq %rax, %rax # 0 "" 2 /NO_APP
- cexpression can be not an lvalue, but can be used as the input value of the assembly expression
- Clobbers
Destroy the list, mainly used to instruct the compiler generated assembly instructions.
It is OK to see from the asm expression that changes the entries listed in the output operand compiler is visible, but the inline assembly code may not only modify the output. For example, the calculation may require additional registers, or the processor may destroy the register's value due to a specific assembler instruction. To notify the compiler of these changes, list these entries that have side effects in the Clobber list. The corrupt list entry can be a register name or a special corrupt list entry (listed below). Each content list entry is a string constant, enclosed in double quotes and separated by commas.
register
```c asm volatile("movc3 %0, %1, %2" : /* No outputs. */ : "r"(from), "r"(to), "g"(count) : "%rbx", "%rcx", "%rdx", "memory"); /APP # 25 "" 1 movc3 %rax, %r8, -72(%rbp) # 0 "" 2 /NO_APP ``` You can see that it has been used rax register,Then modify the program Clobbers Increase %rax, The results are as follows ```c asm volatile("movc3 %0, %1, %2" : /* No outputs. */ : "r"(from), "r"(to), "g"(count) : "%rax", "%rbx", "%rcx", "%rdx", "memory"); /APP # 25 "" 1 movc3 %r8, %r9, -72(%rbp) # 0 "" 2 /NO_APP ```
Special damage list item
- "cc", means that the assembly code has modified the flag register
- "memory", To ensure that the correct value is included in the memory, the compiler may need to flush a specific register value into memory before executing asm
In order to destroy the value of the list item, the compiler is not used when these entries are registers; when they are special parameters, it is refreshed to get the latest value.
constraint
Some basic constraints
Constraint name | illustrate |
---|---|
whitespace | Whitespace characters are ignored |
m | Allows the use of memory operands, as well as any type of address that the machine usually supports |
o | Memory operands are allowed, but only if the address is offset |
V | Allows the use of memory operands, non-offset memory addresses, mutually exclusive to "o' |
r | Register operands allowed in general registers, where registers can be specified such as a(%rax), b(%rbx) |
i | Allows immediate integer operands |
n | Allows the use of immediate integer operands with known values, ‘I’, ‘J’, ‘K’, … ‘P’, more should use n |
F | Allows floating point counting immediately |
g | Allows any register, memory, or instant integer operands, except for non-generic registers |
X | Allow any operand, ‘0’, ‘1’, ‘2’, … ‘9’ |
p | Operands that allow valid memory addresses |
Identifier constraints
Identifier | illustrate |
---|---|
= | Indicates that this operand is written by the instruction: the previous value will be discarded and replaced by new data |
+ | Indicates that the operand is read and written by the instruction |
& | Indicates (in a specific alternative) this operand is an early instruction operand, which is written before the instruction is completed with the input operand, so the input operand part cannot be allocated the same register as the output operand |
% | Interchangeable instruction indicating the operand and subsequent operands |
Kernel Example
x86's memory barrier instruction.
// Avoid compiler optimization and declare that memory may be damaged here#define barrier() asm volatile("" ::: "memory") // In a 32-bit CPU, the lock instruction is the lock bus, and adding a memory operation instruction to achieve the role of a memory barrier. The 64-bit CPU has a new *fence instruction that can be used.// mb() executes a memory barrier instruction to specify CPU operation; destroy list declaration cc memory indicates to avoid the compiler's optimization#ifdef CONFIG_X86_32 #define mb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "mfence", \ X86_FEATURE_XMM2) ::: "memory", "cc") #define rmb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "lfence", \ X86_FEATURE_XMM2) ::: "memory", "cc") #define wmb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "sfence", \ X86_FEATURE_XMM2) ::: "memory", "cc") #else #define mb() asm volatile("mfence":::"memory") #define rmb() asm volatile("lfence":::"memory") #define wmb() asm volatile("sfence" ::: "memory") #endif
Get the current value under x86
DECLARE_PER_CPU(struct task_struct *, current_task); #define this_cpu_read_stable(var) percpu_stable_op("mov", var) static __always_inline struct task_struct *get_current(void) { return this_cpu_read_stable(current_task); } #define percpu_stable_op(op, var) \ ({ \ typeof(var) pfo_ret__; \ switch (sizeof(var)) { \ case 8: \ asm(op "q "__percpu_arg(P1)",%0" \ : "=r" (pfo_ret__) \ : "p" (&(var))); \ break; \ } \ pfo_ret__; \ })
Current_task is a pointer of type struct task_struct, tracking macro calls, hitting the assembly code of case 8: under x86-64, and the expanded code is
asm("mov" "q ""%%""gs" ":" "%" "P1"",%0" : "=r" (pfo_ret__) : "p" (&(current_task))); // Change it toasm("movq %%gs:%P1, %0" : "=r"(pfo_ret__) : "p"(&(current_task)));
The meaning of this line of code is to assign the constraint input part to a valid address (p constraint), and assign the CPU id (obtained through segment register gs and offset through GDT, as analyzed here later) to pfo_ret__ through register (r constraint).
refer to
GCC Documentation
C language ASM assembly embedded syntax zz
Summarize
The above is the assembly instructions in the C expression introduced to you by the editor. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. Thank you very much for your support for my website!
If you think this article is helpful to you, please reprint it. Please indicate the source, thank you!