GCC Compiler Option 1 : Optimization Options
全体选项其中一部分是Optimize-Options
1 | # 会列出可选项 |
编译时最好按照其分类有效组织, 例子如下:
1 | g++ |
-Wxxx
对 xxx 启动warning, -fxxx
启动xxx的编译器功能。-fno-xxx
关闭对应选项???-gxxx
debug 相关-mxxx
特定机器架构的选项名称 | 含义 |
---|---|
-Wall | 打开常见的所有warning选项 |
-Werror | 把warning当成error |
-std= | C or C++ language standard. eg ‘c++11’ == ‘c++0x’ ‘c++17’ == ‘c++1z’, which ‘c++0x’,’c++17’ is develop codename |
-Wunknown-pragmas | 未知的pragma会报错(-Wno-unknown-pragmas 应该是相反的) |
-fomit-frame-pointer | 不生成栈帧指针,属于-O1优化 |
-Wstack-protector | 没有防止堆栈崩溃的函数时warning (-fno-stack-protector) |
-MMD | only user header files, not system header files. |
-fexceptions | Enable exception handling. |
-funwind-tables | Unwind tables contain debug frame information which is also necessary for the handling of such exceptions |
-fasynchronous-unwind-tables | Generate unwind table in DWARF format. so it can be used for stack unwinding from asynchronous events |
-fabi-version=n | Use version n of the C++ ABI. The default is version 0.(Version 2 is the version of the C++ ABI that first appeared in G++ 3.4, and was the default through G++ 4.9.) ABI: an application binary interface (ABI) is an interface between two binary program modules. Often, one of these modules is a library or operating system facility, and the other is a program that is being run by a user. |
-fno-rtti | Disable generation of information about every class with virtual functions for use by the C++ run-time type identification features (dynamic_cast and typeid). If you don’t use those parts of the language, you can save some space by using this flag |
-faligned-new | Enable support for C++17 new of types that require more alignment than void* ::operator new(std::size_t) provides. A numeric argument such as -faligned-new=32 can be used to specify how much alignment (in bytes) is provided by that function, but few users will need to override the default of alignof(std::max_align_t) . This flag is enabled by default for -std=c++17 . |
-Wl, xxx | pass xxx option to linker, e.g., -Wl,-R/staff/shaojiemike/github/MultiPIM_icarus0/common/libconfig/lib specify a runtime library search path for dynamic libraries (shared libraries) during the linking process. |
-O3 turns on all optimizations specified by -O2
and also turns on the -finline-functions, -funswitch-loops, -fpredictive-commoning, -fgcse-after-reload, -ftree-loop-vectorize, -ftree-loop-distribute-patterns, -ftree-slp-vectorize, -fvect-cost-model, -ftree-partial-pre and -fipa-cp-clone options
允许使用浮点计算获得更高的性能,但可能会略微降低精度。
更快但是有保证正确
(仅限 GNU)链接时优化,当程序链接时检查文件之间的函数调用的步骤。该标志必须用于编译和链接时。使用此标志的编译时间很长,但是根据应用程序,当与 -O* 标志结合使用时,可能会有明显的性能改进。这个标志和任何优化标志都必须传递给链接器,并且应该调用 gcc/g++/gfortran 进行链接而不是直接调用 ld。
此标志对特定处理器类型进行额外调整,但它不会生成额外的 SIMD 指令,因此不存在体系结构兼容性问题。调整将涉及对处理器缓存大小、首选指令顺序等的优化。
在 AMD Bulldozer 节点上使用的值为 bdver1,在 AMD Epyc 节点上使用的值为 znver2。是zen ver2的简称。
-fprefetch-loop-arrays
-Os
禁用https://zhuanlan.zhihu.com/p/496435946
下面没有特别指明都是O3,默认开启
-ftree-loop-distribution
-ftree-loop-distribute-patterns
-floop-interchange
-floop-unroll-and-jam
(不是计算访问的数据)
-falign-functions=n:m:n2:m2
-freorder-blocks
Unroll loops whose number of iterations can be determined at compile time or upon entry to the loop. -funroll-loops
implies -frerun-cse-after-loop
. This option makes code larger, and may or may not make it run faster.
Unroll all loops, even if their number of iterations is uncertain when the loop is entered. This usually makes programs run more slowly. -funroll-all-loops
implies the same options as -funroll-loops
,
The maximum number of instructions that a loop should have if that loop is unrolled, and if the loop is unrolled, it determines how many times the loop code is unrolled.
如果循环被展开,则循环应具有的最大指令数,如果循环被展开,则它确定循环代码被展开的次数。
The maximum number of instructions biased by probabilities of their execution that a loop should have if that loop is unrolled, and if the loop is unrolled, it determines how many times the loop code is unrolled.
如果一个循环被展开,则根据其执行概率偏置的最大指令数,如果该循环被展开,则确定循环代码被展开的次数。
The maximum number of unrollings of a single loop.
单个循环的最大展开次数。
会自动检测,但有可能检测不对。
这将为特定架构生成 SIMD 指令并应用 -mtune 优化。 arch 的有用值与上面的 -mtune 标志相同。
1 | g++ -march=native -m32 ... -Q --help=target |
position-independent code(PIC)
暂无
暂无
编译是指编译器读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码。
#include
语句以及一些宏插入程序文本中,得到main.i
和sum.i
文件。main.i
和sum.i
编译成文本文件main.s
和sum.c
的汇编语言程序。main.s
和sum.s
翻译成机器语言的二进制指令,并打包成一种叫做可重定位目标程序的格式,并将结果保存在main.o和sum.o两个文件中。这种文件格式就比较接近elf格式了。main.o
和sum.o
,得到可执行目标文件,就是elf格式文件。目标文件有三种形式:
.c
文件转化成 .i
文件.gcc –E filename.cpp -o filename.i
-E Preprocess only; do not compile, assemble or link.
-C
能保留头文件里的注释,如gcc -E -C circle.c -o circle.c
gcc -save-temps -c -o main.o main.c
cpp filename.cpp -o filename.i
命令linemarkers
类似# linenum filename flags
的注释,这些注释是为了让编译器能够定位到源文件的行号,以便于编译器能够在编译错误时给出正确的行号。flags
meaning除开注释被替换成空格,包括代码里的预处理命令:
#error "text"
的作用是在编译时生成一个错误消息,它会导致编译过程中断。 同理有#warning
#define a b
对于这种伪指令,预编译所要做的是将程序中的所有a用b替换,但作为字符串常量的 a则不被替换。还有 #undef,则将取消对某个宏的定义,使以后该串的出现不再被替换。#ifdef SNIPER
,#if defined SNIPER && SNIPER == 0
,#ifndef,#else,#elif,#endif等。 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉-DSNIPER=5
#include "FileName"
或者#include 等。LINE
标识将被解释为当前行号(十进制数),FILE
则被解释为当前被编译的C源程序的名称。#include ""
vs #include <>
区别在于前者会在文件的当前目录寻找,但是后者只会在编译器编译的official路径寻找
通常的搜索顺序是:
包含指定源文件的目录(对于在 #include
命令中以引号包括的文件名)。
采用-iquote
选项指定的目录,依照出现在命令行中的顺序进行搜索。只对 #include
命令中采用引号的头文件名进行搜索。
所有header file的搜寻会从-I
开始, 依照出现在命令行中的顺序进行搜索。(可以使用-I/path/file
只添加一个头文件,尤其是在编译的兼容性修改时)
采用环境变量 CPATH 指定的目录。
采用-isystem
选项指定的目录,依照出现在命令行中的顺序进行搜索。
然后找环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH
指定的路径
再找系统默认目录(/usr/include、/usr/local/include、/usr/lib/gcc-lib/i386-linux/2.95.2/include......)
通过如下命令可以查看头文件搜索目录 gcc -xc -E -v - < /dev/null
或者 g++ -xc++ -E -v - < /dev/null
*. 如果想改,需要重新编译gcc
或者在编译出错时,g++ -H -v
查看是不是项目下的同名头文件优先级高于sys-head-file
.c/.h
或者.i
文件转换成.s
文件,gcc –S filename.cpp -o filename.s
,对应于-S Compile only; do not assemble or link.
gcc –S filename.i -o filename.s
也是可行的。但是我遇到头文件冲突的问题error: declaration for parameter ‘__u_char’ but no such parameter
cc –S filename.cpp -o filename.s
cc1
命令-O3
)如果想把 C 语言变量的名称作为汇编语言语句中的注释,可以加上 -fverbose-asm
选项:
1 | gcc -S -O3 -fverbose-asm ../src/pivot.c -o pivot_O1.s |
请阅读 GNU assembly file一文
汇编器:将.s 文件转化成 .o文件,
gcc –c
,-c Compile and assemble, but do not link.
as
;objdump -Sd ../build/bin/pivot > pivot1.s
-S
以汇编代码的形式显示C++原程序代码,如果有debug信息,会显示源代码。nm file.o
查看目标文件中的符号表注意,这时候的目标文件里的使用的函数可能没定义,需要链接其他目标文件.a .so .o .dll
(Dynamic Link Library的缩写,Windows动态链接库)
List symbol names in object files.
/lib
,/usr/lib
,/lib64
(在64位系统上),/usr/lib64
(在64位系统上)遍历 LD_LIBRARY_PATH
中的每个目录,并查找包括软链接在内的所有 .so 文件。
1 | IFS=':' dirs="$LD_LIBRARY_PATH" |
ldconfig 命令用于配置动态链接器的运行时绑定。你可以使用它来查询系统上已知的库文件的位置()。
ldconfig 会扫描
/lib
和 /usr/lib
,以及 /etc/ld.so.conf
中列出的目录),查找共享库文件(.so 文件),/etc/ld.so.cache
。这个缓存文件会被动态链接器(ld.so 或 ld-linux.so)使用,以加快共享库的查找速度。1 | # 查看所有是path 的库 |
ldd
会显示动态库的链接关系,中间的nm
为U
没关系,只需要最终.so
对应符号是T
即可。ldd
时避免对不可信的可执行文件运行,因为它可能会执行恶意代码。readelf -d
或 objdump -p
来查看库依赖。通过使用ld
命令,将编译好的目标文件连接成一个可执行文件或动态库。
Foo::bar(int,long)
会变成bar__3Fooil
。其中3是名字字符数见 Linux Executable file: Structure & Running
undefined reference to
一旦链接器完成了符号解析这一步,就把代码中的每个符号引用和正好一个符号定义(即它的一个输入目标模块中的一个符号表条目)关联起来。此时,链接器就知道它的输入目标模块中的代码节和数据节的确切大小。现在就可以开始重定位步骤了,在这个步骤中,将合并输入模块,并为每个符号分配运行时地址。重定位由两步组成:
.data
节被全部合并成一个节,这个节成为输出的可执行目标文件的.data
节。当汇编器生成一个目标模块时,它并不知道数据和代码最终将放在内存中的什么位置。它也不知道这个模块引用的任何外部定义的函数或者全局变量的位置。所以,无论何时汇编器遇到对最终位置未知的目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。
代码的重定位条目放在 .rel.text
中。已初始化数据的重定位条目放在 .rel.data
中。
下面 展示了 ELF 重定位条目的格式。
R_X86_64_PC32
。重定位一个使用 32 位 PC 相对地址的引用。回想一下 3.6.3 节,一个 PC 相对地址就是距程序计数器(PC)的当前运行时值的偏移量。当 CPU 执行一条使用 PC 相对寻址的指令时,它就将在指令中编码的 32 位值加上 PC 的当前运行时值,得到有效地址(如 call 指令的目标),PC 值通常是下一条指令在内存中的地址。(将 PC 压入栈中来使用)R_X86_64_32
。重定位一个使用 32 位绝对地址的引用。通过绝对寻址,CPU 直接使用在指令中编码的 32 位值作为有效地址,不需要进一步修改。1 | typedef struct { |
链接器通常从左到右解析依赖项,这意味着如果库 A 依赖于库 B,那么库 B 应该在库 A 之前被链接。
静态库static library就是将相关的目标模块打包形成的单独的文件。使用ar命令。
静态库的优点在于:
问题:
深入理解计算机系统P477,静态库例子
1 | gcc -static -o prog2c main2.o -L. -lvector |
图 7-8 概括了链接器的行为。-static
参数告诉编译器驱动程序,链接器应该构建一个完全链接的可执行目标文件,它可以加载到内存并运行,在加载时无须更进一步的链接。-lvector
参数是 libvector.a
的缩写,-L. 参数告诉链接器在当前目录下查找 libvector.a。
共享库是以两种不同的方式来“共享”的:
如上创建了一个可执行目标文件 prog2l,而此文件的形式使得它在运行时可以和 libvector.so 链接。基本的思路是:
dlopen()
interface.情况:在应用程序被加载后执行前时,动态链接器加载和链接共享库的情景。
核心思想:由动态链接器接管,加载管理和关闭共享库(比如,如果没有其他共享库还在使用这个共享库,dlclose函数就卸载该共享库。)。
.interp
节,这一节包含动态链接器的路径名,动态链接器本身就是一个共享目标(如在 Linux 系统上的 ld-linux.so). 加载器不会像它通常所做地那样将控制传递给应用,而是加载和运行这个动态链接器。然后,动态链接器通过执行下面的重定位完成链接任务:最后,动态链接器将控制传递给应用程序。从这个时刻开始,共享库的位置就固定了,并且在程序执行的过程中都不会改变。
情况:应用程序在运行时要求动态链接器加载和链接某个共享库,而无需在编译时将那些库链接到应用。
实际应用情况:
思路是将每个生成动态内容的函数打包在共享库中。
编译器yasm
的参数-DPIE
如果同一份代码可能被加载到进程空间的任意虚拟地址上执行(如共享库和动态加载代码),那么就需要使用-fPIC生成位置无关代码。
问题:多个进程是如何共享程序的一个副本的呢?
问题。
可以加载而无需重定位的代码称为位置无关代码(Position-Independent Code,PIC)
在一个 x86-64 系统中,对同一个目标模块中符号的引用是不需要特殊处理使之成为 PIC。可以用 PC 相对寻址来编译这些引用,构造目标文件时由静态链接器重定位。
然而,对共享模块定义的外部过程和对全局变量的引用需要一些特殊的技巧,接下来我们会谈到。
解决方法:延迟绑定(lazy binding),将过程地址的绑定推迟到第一次调用该过程时。
动机:使用延迟绑定的动机是对于一个像 libc.so 这样的共享库输出的成百上千个函数中,一个典型的应用程序只会使用其中很少的一部分。把函数地址的解析推迟到它实际被调用的地方,能避免动态链接器在加载时进行成百上千个其实并不需要的重定位。
结果:第一次调用过程的运行时开销很大,但是其后的每次调用都只会花费一条指令和一个间接的内存引用。
实现:延迟绑定是通过两个数据结构之间简洁但又有些复杂的交互来实现的,这两个数据结构是:GOT 和过程链接表(Procedure Linkage Table,PLT)。如果一个目标模块调用定义在共享库中的任何函数,那么它就有自己的 GOT 和 PLT。GOT 是数据段的一部分,而 PLT 是代码段的一部分。
首先,让我们介绍这两个表的内容。
PLT[0]
是一个特殊条目,它跳转到动态链接器中。PLT[1]
(图中未显示)调用系统启动函数(__libc_start_main
),它初始化执行环境,调用 main 函数并处理其返回值从 PLT[2] 开始的条目调用用户代码调用的函数。在我们的例子中,PLT[2] 调用 addvec,PLT[3](图中未显示)调用 printf。上图a 展示了 GOT 和 PLT 如何协同工作,在 addvec 被第一次调用时,延迟解析它的运行时地址:
上图b 给出的是后续再调用 addvec 时的控制流:
静态库
动态库
1 | shaojiemike@snode6 /lib/modules/5.4.0-107-generic/build [06:32:26] |
加载器:将可执行程序加载到内存并进行执行,loader和ld-linux.so。
将可执行文件加载运行
命令 | 描述 |
---|---|
ar | 创建静态库,插入、删除、列出和提取成员; |
stringd | 列出目标文件中所有可以打印的字符串; |
strip | 从目标文件中删除符号表信息; |
nm | 列出目标文件符号表中定义的符号; |
size | 列出目标文件中节的名字和大小; |
readelf | 显示一个目标文件的完整结构,包括ELF 头中编码的所有信息。 |
objdump | 显示目标文件的所有信息,最有用的功能是反汇编.text节中的二进制指令。 |
ldd | 列出可执行文件在运行时需要的共享库。 |
ltrace 跟踪进程调用库函数过程
strace 系统调用的追踪或信号产生的情况
Relyze 图形化收费试用
-g
选项,可以生成调试信息,这样在gdb中可以查看源代码。1 | objdump -g <archive_file>.a |
gcc -E -g testBigExe.cpp -o testDebug.i
相对于无-g
的命令,只会多一行信息# 1 "/staff/shaojiemike/test/OS//"
gcc -S -g testBigExe.cpp -o testDebug.s
,对比之前的汇编文件,由72行变成9760行。具体解析参考 GNU assembly file一文简单的#pragma omp for
,编译后多出汇编代码如下。当前可以创建多少个线程默认汇编并没有显示的汇编指令。
1 | call omp_get_num_threads@PLT |
某些atomic的导语会变成对应汇编
暂无
基础不牢,地动山摇。ya 了。
https://www.cnblogs.com/LiuYanYGZ/p/5574601.html
https://hansimov.gitbook.io/csapp/part2/ch07-linking/7.5-symbols-and-symbol-tables
GCC Compiler Option 2 : Preprocessor Options
-M
option is designed for auto-generate Makefile rules from g++ command.-E
option to STOP after preprocessor during the compilation-w
option to DISABLE/suppress all warnings.Using a complex g++ command as an example:
1 | g++ -Wall -Werror -Wno-unknown-pragmas -DPIN_CRT=1 -fno-stack-protector -fno-exceptions -funwind-tables -fasynchronous-unwind-tables -fno-rtti -DTARGET_IA32E -DHOST_IA32E -fPIC -DTARGET_LINUX -fabi-version=2 -faligned-new -I../../../source/include/pin -I../../../source/include/pin/gen -isystem /staff/shaojiemike/Download/pin-3.28-98749-g6643ecee5-gcc-linux/extras/cxx/include -isystem /staff/shaojiemike/Download/pin-3.28-98749-g6643ecee5-gcc-linux/extras/crt/include -isystem /staff/shaojiemike/Download/pin-3.28-98749-g6643ecee5-gcc-linux/extras/crt/include/arch-x86_64 -isystem /staff/shaojiemike/Download/pin-3.28-98749-g6643ecee5-gcc-linux/extras/crt/include/kernel/uapi -isystem /staff/shaojiemike/Download/pin-3.28-98749-g6643ecee5-gcc-linux/extras/crt/include/kernel/uapi/asm-x86 -I../../../extras/components/include -I../../../extras/xed-intel64/include/xed -I../../../source/tools/Utils -I../../../source/tools/InstLib -O3 -fomit-frame-pointer -fno-strict-aliasing -Wno-dangling-pointer |
In Makefile_bk
1 | inscount0.o: inscount0.cpp \ |
-MM
not include sys header file-MF filename
config the Makefile rules write to which file instead of to stdout.-M -MG
is designed to generate Makefile rules when there is header file missing, treated it as generated in normal.-M -MP
will generated M-rules for dependency between header filesheader1.h
includes header2.h
. So header1.h: header2.h
in Makefile-MD
== -M -MF file
without default option -E
file
has a suffix of .d
, e.g., inscount0.d
for -c inscount0.cpp
-MMD
== -MD
not include sys header file暂无
暂无
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
无
1 | __asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify); |
__asm__
或asm 用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头的,是必不可少的。__volatile__
或volatile 是可选的。如果用了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。__asm__ __volatile__("")
; 或 __asm__ ("")
;都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。__asm__ ("":::"memory")
;就非常有意义,它向GCC 声明:“内存作了改动”,GCC 在编译的时候,会将此因素考虑进去。%0,%1,…,%9
。指令中使用占位符表示的操作数,总被视为long型(4个字节),%h1
。"constraint"(variable)
的列表(逗号分隔)。按照出现的顺序分别与指令操作数”%0”,”%1”对应"=r" (value)
__asm__ ("mov R0, #0x34" : : : "R0");
寄存器R0出现在”Instruction List中”,并且被mov指令修改,但却未被任何Input/Output操作表达式指定,所以你需要在Clobber/Modify域指定”R0”,以让GCC知道这一点。每一个Input和Output表达式都必须指定自己的操作约束Operation Constraint,这里将讨论在80386平台上所可能使用的操作约束。
当前的输入或输出需要借助一个寄存器时,需要为其指定一个寄存器约束,可以直接指定一个寄存器的名字。
常用的寄存器约束的缩写
约束 | 意义 |
---|---|
r | 表示使用一个通用寄存器,由 GCC 在%eax/%ax/%al,%ebx/%bx/%bl,%ecx/%cx/%cl,%edx/%dx/%dl中选取一个GCC认为合适的。 |
g | 表示使用任意一个寄存器,由GCC在所有的可以使用的寄存器中选取一个GCC认为合适的。 |
q | 表示使用一个通用寄存器,和约束r的意义相同。 |
a | 表示使用%eax/%ax/%al |
b | 表示使用%ebx/%bx/%bl |
c | 表示使用%ecx/%cx/%cl |
d | 表示使用%edx/%dx/%dl |
D | 表示使用%edi/%di |
S | 表示使用%esi/%si |
f | 表示使用浮点寄存器 |
t | 表示使用第一个浮点寄存器 |
u | 表示使用第二个浮点寄存器 |
分类 | 限定符 | 描述 |
---|---|---|
通用寄存器 | “a” | 将输入变量放入eax 这里有一个问题:假设eax已经被使用,那怎么办?其实很简单:因为GCC 知道eax 已经被使用,它在这段汇编代码的起始处插入一条语句pushl %eax,将eax 内容保存到堆栈,然 后在这段代码结束处再增加一条语句popl %eax,恢复eax的内容 |
“b” | 将输入变量放入ebx | |
“c” | 将输入变量放入ecx | |
“d” | 将输入变量放入edx | |
“s” | 将输入变量放入esi | |
“d” | 将输入变量放入edi | |
“q” | 将输入变量放入eax,ebx,ecx,edx中的一个 | |
“r” | 将输入变量放入通用寄存器,也就是eax,ebx,ecx,edx,esi,edi中的一个 | |
“A” | 把eax和edx合成一个64 位的寄存器(use long longs) | |
内存 | “m” | 内存变量 |
“o” | 操作数为内存变量,但是其寻址方式是偏移量类型, 也即是基址寻址,或者是基址加变址寻址 | |
“V” | 操作数为内存变量,但寻址方式不是偏移量类型 | |
“ “ | 操作数为内存变量,但寻址方式为自动增量 | |
“p” | 操作数是一个合法的内存地址(指针) | |
寄存器或内存 | “g” | 将输入变量放入eax,ebx,ecx,edx中的一个 或者作为内存变量 |
“X” | 操作数可以是任何类型 | |
立即数 | “I” | 0-31之间的立即数(用于32位移位指令) |
“J” | 0-63之间的立即数(用于64位移位指令) | |
“N” | 0-255之间的立即数(用于out指令) | |
“i” | 立即数 | |
“n” | 立即数,有些系统不支持除字以外的立即数, 这些系统应该使用”n”而不是”i” | |
匹配 | “ 0 “,“1” …“9” | , 表示用它限制的操作数与某个指定的操作数匹配,也即该操作数就是指定的那个操作数,例如”0”去描述”%1”操作数,那么”%1”引用的其实就是”%0”操作数,注意作为限定符字母的0-9 与 指令中的”%0”-“%9”的区别,前者描述操作数,后者代表操作数。 |
&; | 该输出操作数不能使用过和输入操作数相同的寄存器 | |
操作数类型 | “=” | 操作数在指令中是只写的(输出操作数) |
“+” | 操作数在指令中是读写类型的(输入输出操作数) | |
浮点数 | “f” | 浮点寄存器 |
“t” | 第一个浮点寄存器 | |
“u” | 第二个浮点寄存器 | |
“G” | 标准的80387浮点常数 | |
% | 该操作数可以和下一个操作数交换位置.例如addl的两个操作数可以交换顺序 (当然两个操作数都不能是立即数) | |
# | 部分注释,从该字符到其后的逗号之间所有字母被忽略 | |
* | 表示如果选用寄存器,则其后的字母被忽略 |
如果一个Input/Output 操作表达式的C/C++表达式表现为一个内存地址,不想借助于任何寄存器,则可以使用内存约束。比如:
1 | __asm__("lidt%0":"=m"(__idt_addr)); |
修饰符 | 输入/输出 | 意义 |
---|---|---|
= | O | 表示此Output操作表达式是Write-Only的。 |
| O |表示此Output操作表达式是Read-Write的。
& | O |表示此Output操作表达式独占为其指定的寄存器。
% | I |表示此Input 操作表达式中的C/C++表达式可以和下一 个Input操作表达式中的C/C++表达式互换
1 | Static __inline__ void __set_bit(int nr, volatile void * addr) |
第一个占位符%0与C 语言变量ADDR对应,第二个占位符%1与C语言变量nr对应。因此上面的汇编语句代码与下面的伪代码等价:btsl nr, ADDR
使用”memory”是向GCC声明内存发生了变化,而内存发生变化带来的影响并不止这一点。
例如:
1 | int main(int __argc, char* __argv[]) |
本例中,如果没有那条内联汇编语句,那个if语句的判断条件就完全是一句废话。GCC在优化时会意识到这一点,而直接只生成return 5的汇编代码,而不会再生成if语句的相关代码,而不会生成return (*__p)的相关代码。
但你加上了这条内联汇编语句,它除了声明内存变化之外,什么都没有做。
但GCC此时就不能简单的认为它不需要判断都知道 (*__p)一定与9999相等,它只有老老实实生成这条if语句的汇编代码,一起相关的两个return语句相关代码。
另外在linux内核中内存屏障也是基于它实现的include/asm/system.h中
1 | # define barrier() _asm__volatile_("": : :"memory") |
主要是保证程序的执行遵循顺序一致性。呵呵,有的时候你写代码的顺序,不一定是终执行的顺序,这个是处理器有关的。
1 | static inline char * strcpy(char * dest, const char *src) |
暂无
暂无