GCC _ ASM _ example

Source: Internet
Author: User

_ ASM _ is the macro definition of the GCC keyword ASM:

# DEFINE _ ASM

_ ASM _ or ASM is used to declare an inline assembly expression. Therefore, any inline assembly expression starts with it and is indispensable.

2. Instruction List

The Instruction List is a sequence of Assembly commands. It can be empty, for example, __asm _ volatile _ (""); or _ ASM __(""); they are all completely legal inline assembly expressions, but these two statements have no significance. But not all Instruction List empty inline assembly expressions are meaningless, such as :__ ASM _ ("": "Memory"); it makes sense, it declares to GCC: "I have modified the memory". GCC will take this factor into consideration during compilation.

Let's take a look at the following example:

$ Cat example1.c

Int main (INT _ argc, char * _ argv [])
{
Int * _ p = (int *) _ argc;

(* _ P) = 9999;

// _ ASM _ ("": "Memory ");

If (* _ p) = 9999)
Return 5;

Return (* _ P );
}

In this Code, the inline assembly is commented out. Before this inline assembly, the memory pointer _ P points to 9999, and then after the inline assembly, an if statement determines whether the memory pointed to by _ p is equal to 9999. Obviously, they are equal. GCC is able to intelligently find this point when optimizing compilation. We use the following command line to compile it:

$ Gcc-o-s example1.c

Option-O indicates optimized compilation. We can also specify the optimization level. For example,-O2 indicates that the optimization level is 2. Option-s indicates that the C/C ++ source file is compiled as an assembly file, the file name is the same as the C/C ++ file, except that the extension is. c. s.

Let's take a look at the compilation results put in example1.s. Here we only list the compilation code of the relevant functions compiled on Redhat 2.96 using gcc 7.3. Other unrelated code is not listed for clarity.

$ Cat example1.s

Main:
Pushl % EBP
Movl % ESP, % EBP
Movl 8 (% EBP), % eax # int * _ p = (int *) _ argc
Movl $9999, (% eax) # (* _ p) = 9999
Movl $5, % eax # Return 5
Popl % EBP
RET

Take a look at the C source code and compiled assembly code. We will find that there is no if statement-related code in the assembly code, but the value assignment statement (* _ p) = return 5 directly after 9999; this is because GCC considers that after (* _ p) is assigned a value, there is no operation to change (* _ p) content before the if statement, therefore, the IF statement's judgment condition (* _ p) = 9999 must be true, so GCC will not generate relevant code, instead, the return 5 assembly code is directly generated based on the true condition (GCC uses eax as the Register to save the return value ).

Now we will remove the comments of inline assembly in example1.c, re-compile, and take a look at the relevant compilation results.

$ Gcc-o-s example1.c

$ Cat example1.s

Main:
Pushl % EBP
Movl % ESP, % EBP
Movl 8 (% EBP), % eax # int * _ p = (int *) _ argc
Movl $9999, (% eax) # (* _ p) = 9999
# App

# _ ASM _ ("": "Memory ")
# No_app
CMPL $9999, (% eax) # (* _ p) = 9999?
JNE. l3 # False
Movl $5, % eax # True, return 5
JMP. l2
. P2align 2
. L3:
Movl (% eax), % eax
. L2:
Popl % EBP
RET

Since the inline assembly statement _ ASM _ ("": "Memory") declares to GCC, the memory content may change in the position where the inline assembly statement appears, therefore, GCC cannot be compiled as it did just now. This time, GCC honestly generates assembly code for the IF statement.

Some people may question: why do we use _ ASM _ ("": "Memory") to declare to GCC that memory has changed? Obviously, the "Instruction List" is empty and there is no operation on the memory. This will only increase the number of compiled code generated by GCC.

Indeed, the inline assembly statement does not perform any operations on the memory. In fact, it does not. However, the memory content is not only the program you are currently running. For example, if the memory you are operating on is a memory ing, the ing content is the peripheral I/O device register. Then the operation of this memory is not only the current program, but also the I/O device will operate this memory. Since both of them operate on the same memory, neither party can take the content of this memory for granted at any time. Therefore, when you use C/C ++ to write such programs, you must let the compiler understand this. After all, high-level languages will eventually be compiled into assembly code.

You may have noticed that the output of this compilation result contains two symbols: # app and # no_app, GCC places the commands listed in "Instruction List" in the inline assembly sentence between # app and # no_app, because _ ASM _ ("": "Memory ") "Instruction List" is empty, so no content exists between # app and # no_app. However, we will be more clear about this in future examples.

We will discuss in detail why inline assembly _ ASM _ ("": "Memory") is a statement that declares Memory changes.

We spent a lot of time discussing the case that "Instruction List" is empty, but in actual programming, "Instruction List" is not empty in most cases. It can have one or more Assembly commands.

When there are multiple commands in "Instruction List", you can list all the commands in a pair of quotation marks, or put one or more commands in a pair of quotation marks, all commands are enclosed in multiple pair quotation marks. If it is the former, you can put each command in one line. If you want to put multiple commands in one line, you must use a semicolon (;) or line break (\ n, in most cases, \ n is followed by a \ t, where \ n is for line feed, \ t is to empty a tab width space) to separate them. For example:

_ ASM _ ("movl % eax, % EBX
STI
Popl % EDI
Subl % ECx, % EBX ");

_ ASM _ ("movl % eax, % EBX; STI
Popl % EDI; subl % ECx, % EBX ");

_ ASM _ ("movl % eax, % EBX; STI \ n \ t popl % EDI
Subl % ECx, % EBX ");

All are legal statements. If you place the instruction in multiple pair quotation marks, a semicolon (;) or (\ n) must be followed by the last pair of quotation marks except the last pair of quotation marks) or (\ n \ t ). For example:

_ ASM _ ("movl % eax, % EBX
STI \ n"
"Popl % EDI ;"
"Subl % ECx, % EBX ");

_ ASM _ ("movl % eax, % EBX; STI \ n \ t"
"Popl % EDI; subl % ECx, % EBX ");

_ ASM _ ("movl % eax, % EBX; STI \ n \ t popl % EDI \ n"
"Subl % ECx, % EBX ");

_ ASM _ ("movl % eax, % EBX; STI \ n \ t popl % EDI;" "subl % ECx, % EBX ");

Are valid.

The preceding principles can be summarized as follows:

Any two commands are either separated by semicolons (;) or placed in two rows;
The two rows can be implemented either through the \ n method or in two rows;
You can use one or more pair of quotation marks. Each pair of quotation marks can contain more than one instruction. All instructions must be placed in quotation marks.
In basic inline assembly, the format of "Instruction List" is no different from that of writing non-inline assembly directly in the Assembly file. You can define label and define alignment (. align N), define a segment (. section name ). For example:

_ ASM _ (". Align 2 \ n \ t"
"Movl % eax, % EBX \ n \ t"
"Test % EBX, % ECx \ n \ t"
"JNE error \ n \ t"
"Sti \ n \ t"
"Error: popl % EDI \ n \ t"
"Subl % ECx, % EBX ");

The format in the preceding example is commonly used in Linux Inline code and is very neat. We also recommend that you use this format to write inline assembly code.

3. _ volatile __

_ Volatile _ is the macro definition of the GCC keyword volatile:

# DEFINE _ volatile

_ Volatile _ or volatile is optional. You can use it or not. If you use it, declare to GCC that "do not touch the Instruction List I wrote, I need to keep every instruction intact ", otherwise, when you use the optimization option (-O) for compilation, GCC will decide whether to optimize the pointer in this inline assembly expression based on its own judgment.

So what is the GCC Judgment Principle? I don't know (if anyone knows this, please let me know ). I tested it and found that if an inline assembly statement is a basic inline assembly Statement (that is, there is only "Instruction List" and there is no inline assembly of input/output/clobber, we will discuss this later). Whether you use _ volatile _ to modify it or not, GCC 2.96 will keep the Instruction List in the inline assembly intact during compilation optimization ". However, I may not have a sufficient example of the experiment, so this is not guaranteed.

For the sake of security, if you don't want GCC optimization to affect your inline assembly code, you 'd better add _ volatile __in front of it, instead of relying on the Compiler Principles, even if you are familiar with the optimization principles of the current compiler, you cannot guarantee that these principles will not change in the future. The meaning of _ volatile _ is constant.

2. inline assembly with C/C ++ expressions

GCC allows you to use C/C ++ expressions to specify the input and output of commands in the "getting cuction list" inline assembly. You can even ignore which register is used, it is arranged and specified by GCC. This allows programmers to avoid using limited registers and improve the efficiency of the target code.

Let's look at several examples:

_ ASM _ ("": "Memory"); //

_ ASM _ ("mov % eax, % EBX": "= B" (RV): "A" (FOO): "eax ", "EBX ");

_ ASM _ volatile _ ("LIDT % 0": "= m" (idt_descr ));

_ ASM _ ("subl % 2, % 0 \ n \ t"
"Sbbl % 3, % 1"
: "= A" (endlow), "= D" (endhigh)
: "G" (startlow), "G" (starthweigh), "0" (endlow), "1" (endhweigh ));

How about it? I'm a little impressed, isn't it? It doesn't matter. You won't be dizzy after the discussion. (Of course, it may also be more dizzy ^_^ ). Discussion started --

The inline assembly format with C/C ++ expressions is:

_ ASM _ volatile _ ("Instruction List": Output: input: clobber/modify );

We can see that it is different from the basic inline assembly: it has three more parts (input, output, clobber/modify ). The four parts in brackets are separated by colons.

These four parts are not required. Any part can be empty. The rule is as follows:

If clobber/modify is empty, the colon (:) before it must be omitted. For example, _ ASM _ ("mov % eax, % EBX": "= B" (FOO): "A" (indium):) is invalid; while _ ASM _ ("mov % eax, % EBX": "= B" (FOO): "A" (indium) is correct.
If the Instruction List is empty, input, output, clobber/modify can be either empty or empty. For example, _ ASM _ ("": "Memory"); and _ ASM _ ("":); are both legal statements.
If output, input, clobber/modify are empty, the colon (:) before output and input can be omitted or not. If all are omitted, the Assembly is degraded into a basic inline assembly. Otherwise, it is still an inline assembly with a C/C ++ expression, in this case, the register syntax in "Instruction List" must comply with relevant regulations. For example, two percentage signs (%) must be used before the register ), instead of using only one percentage sign (%) before the register, as in the basic Assembly format ). For example, _ ASM _ ("mov % eax, % EBX":) ;__ ASM _ ("mov % eax, % EBX ":) and _ ASM _ ("mov % eax, % EBX") are both correct statements, while _ ASM _ ("mov % eax, % EBX "::); __asm _ ("mov % eax, % EBX":) and _ ASM _ ("mov % eax, % EBX") are both incorrect statements.
If the input, clobber/modify is empty but the output is not empty, the colon (:) before the input can be omitted or not omitted. For example, _ ASM _ ("mov % eax, % EBX": "= B" (FOO):) ;__ ASM _ ("mov % eax, % EBX ":" = B "(FOO) is correct.
If the latter part is not empty, and the former part is empty, the colon (:) must be retained. Otherwise, it cannot be specified which part is not empty. For example, if clobber/modify and output are empty but input is not empty, the colon Before clobber/modify must be omitted (the previous rule), and the colon Before output must be reserved. If clobber/modify is not empty, and input and output are empty, the colons before input and output must be retained. For example, _ ASM _ ("mov % eax, % EBX": "A" (FOO) and _ ASM _ ("mov % eax, % EBX ":" EBX ").
From the above rule, we can see another fact, which distinguishes whether an inline assembly is in the basic format or with a C/C ++ expression format, the rule is to check whether a colon (:) exists after "Instruction List". If not, it is in the basic format. Otherwise, it is in the C/C ++ expression format.

The two formats have different requirements on register Syntax: the basic format requires that only one percentage sign (%) can be used before the Register, which is the same as non-inline assembly; the C/C ++ expression format requires that two percent signs (%) must be used before the Register. The reason will be discussed later.

1. Output

Output is used to specify the output of the current inline assembly statement. Let's take a look at this example:

_ ASM _ ("movl % Cr0, % 0": "= A" (Cr0 ));

The output part of this inline assembly statement is "= r" (Cr0), which is an "Operation expression" that specifies an output operation. We can clearly see that this output operation consists of two parts: the section enclosed by parentheses (Cr0) and the section enclosed by quotation marks "= ". These two parts are essential for each output operation. The Section enclosed in parentheses is a C/C ++ expression used to save an output value of inline assembly. The operation is equal to the equal value of C/C ++ Cr0 = output_value, therefore, the output expression in parentheses can only be the left value expression of C/C ++, that is, it can only be a valid medium number (=) that can be put in the C/C ++ assignment operation) expression on the left. So where does the right value output_value come from?

The expression CPU> db7 is a C/C ++ language expression. It does not have to be a left-value expression, that is to say, it can not only be an expression placed on the left of the C/C ++ assignment operation, but also an expression placed on the right of the C/C ++ assignment operation. Therefore, it can be a variable, a number, or a complex expression (such as a + B/c * D ). For example, the preceding example can be changed to: _ ASM _ ("movl % 0, % db7": "A" (FOO) ,__ ASM _ ("movl % 0, % db7 ":" A "(0x1000) or _ ASM _ (" movl % 0, % db7 ":: "A" (VA * Vb/VC )).

The part in the quotation mark is the constraint part. Unlike the output expression constraint, it does not allow specifying the plus sign (+) and equal sign (=) constraints, that is to say, it can only be the default read-only. A register constraint must be specified. In this example, the letter A indicates that the current input variable CPU-> db7 should be input to the current inline assembly through the Register eax.

Let's look at an example:

$ Cat example4.c

Int main (INT _ argc, char * _ argv [])
{
Int Cr0 = 5;

_ ASM _ volatile _ ("movl % 0, % Cr0": "A" (Cr0 ));

Return 0;
}

$ Gcc-s example4.c

$ Cat example4.s

Main:
Pushl % EBP
Movl % ESP, % EBP
Subl $4, % ESP
Movl $5,-4 (% EBP) # Cr0 = 5
Movl-4 (% EBP), % eax # % eax = Cr0
# App
Movl % eax, % Cr0
# No_app
Movl $0, % eax
Leave
RET

We can see from the compiled assembly code that before "Instruction List", GCC loads the content of the Cr0 variable into the eax register according to our input constraint ".

3. Operation Constraint

Each input and output expression must specify its own operation constraint. Here we will discuss the possible operation constraints used on the 80386 platform.

1. Register Constraints

When you need to use a register for your current input or input, you need to specify a register constraint for it. You can directly specify the name of a register, for example:

_ ASM _ volatile _ ("movl % 0, % Cr0": "eax" (Cr0 ));

You can also specify an abbreviation, for example:

_ ASM _ volatile _ ("movl % 0, % Cr0": "A" (Cr0 ));

If you specify an abbreviation, for example, a, GCC determines whether to use % eax, % ax, or % Al based on the width of the C/C ++ expression in the current operation expression. For example:

Unsigned short _ shrt;

_ ASM _ ("mov % 0, % BX": "A" (_ shrt ));

Because the Variable _ shrt is of the 16-bit short type, the compiled assembly code will make the variable use the % ex register. The compilation result is:

Movw-2 (% EBP), % ax # % AX = _ shrt
# App
Movl % ax, % BX
# No_app

Register constraints can be used for both input and output operation expression constraints.

The following table lists the abbreviations of common register constraints.

Constraints on input/output
R I, O indicates that a general register is used by GCC in % eax/% ax/% Al, % EBX/% BX/% BL, % ECx/% CX/% Cl, select a GCC in % edX/% dx/% DL that is appropriate.
Q I, O indicates that a general register is used, which has the same meaning as R.
A I, O indicates % eax/% ax/% Al
B I, O indicates % EBX/% BX/% BL
C I, O indicates using % ECx/% CX/% Cl
D I, O indicates % edX/% dx/% DL is used
D I, O indicates % EDI/% di
S I, O indicates % ESI/% Si
F I, O indicates the use of floating-point registers
T I, O indicates that the first floating point register is used
U I, O indicates that the second floating point register is used

2. Memory Constraints
If the C/C ++ expression of an input/output operation expression represents a memory address and you do not want to use any registers, you can use the memory constraints. For example:

_ ASM _ ("LIDT % 0": "= m" (_ idt_addr); or _ ASM _ ("LIDT % 0 ":: "M" (_ idt_addr ));

Let's take a look at the results that are put in a C source file respectively and then compiled by GCC:

$ Cat example5.c

// In this example, the variable SH is used as a memory input.

Int main (INT _ argc, char * _ argv [])
{
Char * Sh = (char *) & __ argc;

_ ASM _ volatile _ ("LIDT % 0": "M" (SH ));

Return 0;
}

$ Gcc-s example5.c

$ Cat example5.s

Main:
Pushl % EBP
Movl % ESP, % EBP
Subl $4, % ESP
Leal 8 (% EBP), % eax
Movl % eax,-4 (% EBP) # SH = (char *) & __ argc
# App
LIDT-4 (% EBP)
# No_app
Movl $0, % eax
Leave
RET

$ Cat example6.c

// In this example, the variable SH is output as a memory

Int main (INT _ argc, char * _ argv [])
{
Char * Sh = (char *) & __ argc;

_ ASM _ volatile _ ("LIDT % 0": "= m" (SH ));

Return 0;
}

$ Gcc-s example6.c

$ Cat example6.s

Main:
Pushl % EBP
Movl % ESP, % EBP
Subl $4, % ESP
Leal 8 (% EBP), % eax
Movl % eax,-4 (% EBP) # SH = (char *) & __ argc
# App
LIDT-4 (% EBP)
# No_app
Movl $0, % eax
Leave
RET
First, you will notice that in the two examples, the variable SH is directly involved in the LIDT operation without using any registers.

Secondly, after careful observation, you will find an amazing fact that the compilation Code Compiled by the two examples is the same! Although, in one example, the variable SH is used as the input, and in another example, the variable SH is used as the output. What's going on?

Originally, when using memory for input and output, GCC will not process any input and output according to your statement because it does not use registers. GCC will only be used directly. Whether it is input or output for this C/C ++ expression depends entirely on the instructions you write in "Instruction List" to operate on it.

In the above example, the Operation Command is LIDT, and the operation of the LIDT command is an input-type operation. Therefore, the operation on the variable SH is actually an input operation, this is not changed even if you put it in the output field. Therefore, in this example, the syntax that fully complies with the semantics should be to place SH in the input field, even if it is placed in the output field, there will be correct execution results.

Therefore, for memory-constrained operation expressions, whether put in the input field or output field has no effect on the Compilation result, because we put an operation expression in the input field or in the output field, we hope that GCC can automatically input or output the expression value through registers. Since GCC does not automatically do anything for the Operation expression of the memory constraint type, it doesn't matter where it is placed. But from the programmer's point of view, in order to enhance the readability of the Code, it is best to put it in a place that fits the actual situation.

Constraints on input/output
M I, O indicates using any memory mode supported by the system.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.