1.内联汇编格式

asm volatile ( 这里写指令 : 输出操作数                   /* 可选 */: 输入操作数                   /* 可选 */: 可能被破坏的寄存器列表         /* 可选 */);
或者
__asm__ __volatile__ ( 这里写指令 : 输出操作数                   /* 可选 */: 输入操作数                   /* 可选 */: 可能被破坏的寄存器列表         /* 可选 */);

_ asm _ 修饰这个是一段汇编语言,它是GCC定义的关键字asm的宏定义(#define _ asm _ asm),它用来声明一个内嵌汇编表达式。所以,任何一个内嵌汇编表达式都以它开头,它是必不可少的;如果要编写符合ANSI C标准的代码(即:与ANSI C兼容),那就要使用_ asm _;

_ volatile _ 修饰这段代码不被编译器优化,保持代码原样。这个volatile正是我们需要的,如果经过编译器优化,很有可能将我们写的程序修改,并达不到预期的执行效果了。如果要编写符合ANSI C标准的代码(即:与ANSI C兼容),那就要使用_ volatile _;

然后,该介绍内嵌汇编的语言了。 一般而言,C语言里嵌入汇编代码片段都要比纯汇编语言写的代码复杂得多。因为这里有个怎样分配寄存器、怎样与C代码中的变量融合的问题。为了这个目的,必须要对所用的汇编语言做更多的扩充,增加对汇编语言的明确指示。

1.1 指令部分

第一部分就是汇编语言的语句本身,其格式与在汇编语言程序中使用的格式基本相同,但也有不同之处。这一部分被称为“指令部分”说明他是必须有的,而其它各部分则视具体情况而定,如果不需要的话是可以忽略的,所以在最简单的情况下就与常规的汇编语句基本相同。

指令部分的编写规则:

当指令列表里面有多条指令时,可以在一对双引号中全部写出,也可将一条或多条指令放在一对双引号中,所有指令放在多对双引号中;

如果是将所有指令写在一对双引号中,那么,相邻俩条指令之间必须用分号”;“或换行符(\n)隔开,如果使用换行符(\n),通常\n后面还要跟一个\t;或者是相邻两条指令分别单独写在两行中;
如果将指令放在多对双引号中,除了最后一对双引号之外,前面的所有双引号里的最后一条指令后面都要有一个分号(;)或(\n)或(\n\t);
在涉及到具体的寄存器时就要在寄存器名前面加上两个”%”号,以免混淆。

1.2 输出部分

第二部分,紧接在指令部分后面的是“输出部分”,用来指定当前内嵌汇编语句的输出表达式。
格式为:“操作约束”(输出表达式)

用括号括起来的部分,它用于保存当前内嵌汇编语句的一个输出值。在输出表达式内需要用(=)或(+)来进行修饰。 等号(=)加号(+)的区别:等号(=)表示当前表达式是一个纯粹的输出操作,而加号(+)则表示当前表达式不仅仅是一个输出操作,还是一个输入操作;但无论是等号(=)还是加号(+),所表示的都是可写,只能用于输出,只能出现在输出部分,而不能出现在输入部分;在输出部分可以出现多个输出操作表达式,多个输出操作表达式之间必须用逗号(,)隔开;;

用双引号括起来的部分,被称作是:”输出操作约束“,也可以称为”输出约束“;关于约束部分将在后面一起进行讲解。

1.3 输入部分

第三部分用来指定当前内嵌汇编语句的输入;称为输入表达式;
格式为:”操作约束“(输入表达式)

输入部分同样也由两部分组成:由双引号括起来的部分和由圆括号括起来的部分;这两个部分对于当前内嵌汇编语句的输入来说,是必不可少的;用于约束当前内嵌汇编语句中的当前输入;这个部分也成为”输入操作约束“,也可以成为是”输入约束“;与输出表达式中的操作约束不同的是,输入表达式中的操作约束不允许指定等号(=)约束或加号(+)约束,也就是说,它只能是只读的;约束中必须指定一个寄存器约束;

操作约束
每一个输入和输出表达式都必须指定自己的操作约束;约束的类型有:寄存器约束、内存约束、立即数约束、通用约束;

寄存器约束
当你的输入或输出需要借助于一个寄存器时,你需要为其指定一个寄存器约束;

可以直接指定一个寄存器名字;比如:

__asm__ __volatile__("movl %0,%%cr0"::"eax"(cr0));

也可以指定寄存器的缩写名称;比如:

__asm__ __volatile__("movl %0,%%cr0"::"a"(cr0));

如果指定的是寄存器的缩写名称,比如:字母a;那么,GCC将会根据当前操作表达式的宽度来决定使用%rax、%eax、%ax还是%al;

常用的寄存器约束的缩写:

r:I/O,表示使用一个通用寄存器,由GCC在%rax/%eax/%ax/%al、%rbx/%ebx/%bx/%bl、%rcx/%ecx/%cx/%cl、%rdx/%edx/%dx/%dl中选取一个GCC认为是合适的;
q:I/O,表示使用一个通用寄存器,与r的意义相同;
g:I/O,表示使用寄存器或内存地址;
m:I/O,表示使用内存地址;
a:I/O,表示使用%rax/%eax/%ax/%al;
b:I/O,表示使用%rbx/%ebx/%bx/%bl;
c:I/O,表示使用%rcx/%ecx/%cx/%cl;
d:I/O,表示使用%rdx/%edx/%dx/%dl;
D:I/O,表示使用%rdi/%edi/%di;
S:I/O,表示使用%rsi/%esi/%si;
f:I/O,表示使用浮点寄存器;
t:I/O,表示使用第一个浮点寄存器;
u:I/O,表示使用第二个浮点寄存器;
A:I/O,表示把%eax与%edx组合成一个64位的整数值;
o:I/O,表示使用一个内存位置的偏移量;
V:I/O,表示仅仅使用一个直接内存位置;
i:I/O,表示使用一个整数类型的立即数;
n:I/O,表示使用一个带有已知整数值的立即数;
F:I/O,表示使用一个浮点类型的立即数;

内存约束
如果一个输入/输出操作表达式,表现为一个内存地址(指针变量),不想借助于任何寄存器,则可以使用内存约束;

例如:

__asm__ __volatile__ ("lgdt %0":"=m"(__gdt_addr) : : );
__asm__ __volatile__ ("lgdt %0"::"m"(__gdt_addr));

内存约束使用约束名“m”,表示的是使用系统支持的任何一种内存方式,不需要借助于寄存器;

立即数约束
如果一个输入/输出操作表达式是一个数字常数,不想借助于任何寄存器或内存,则可以使用立即数约束;
由于立即数在表达式中只能作为右值使用,所以,对于使用立即数约束的表达式而言,只能放在输入部分;

比如:

__asm__ __volatile__("movl %0,%%eax"::"i"(100));

立即数约束使用约束名“i”表示输入表达式是一个整数类型的立即数,不需要借助于任何寄存器,只能用于输入部分;使用约束名“F”表示输入表达式是一个浮点数类型的立即数,不需要借助于任何寄存器,只能用于输入部分;

通用约束
约束名“g”可以用于输入和输出,表示可以使用通用寄存器、内存、立即数等任何一种处理方式;通用约束“g”是一个非常灵活的约束,当程序员认为一个表达式在实际操作中,无论使用寄存器方式、内存方式还是立即数方式都无所谓时,或者让GCC可以根据不同的表达式生成不同的访问方式时,就可以使用通用约束“g”。
约束名“0,1,2,3,4,5,6,7,8,9”只能用于输入,表示与第n个操作表达式使用相同的寄存器/内存;

修饰符
等号(=)和加号(+)作为修饰符,已经在输出部分讲解过了,这里主要讲解“&”符。

符号“&”也只能写在输出表达式的约束部分,用于约束寄存器的分配,但是只能写在约束部分的第二个字符的位置上。因为,第一个字符的位置我们要写(=)或(+)。

用符号“&”进行修饰时,等于向GCC声明:“GCC不得为任何输入操作表达式分配与此输出操作表达式相同的寄存器”;其原因是,GCC会先使用输出值对被修饰符“&”修饰的输出操作表达式进行赋值,然后,才对输入操作表达式进行赋值。这样的话,如果不使用修饰符“&”对输出操作表达式进行修饰,一旦后面的输入操作表达式使用了与输出操作表达式相同的寄存器,就会产生输入和输出数据混乱的情况;

值得注意的是:如果一个输出操作表达式的寄存器约束被指定为某个寄存器,只有当至少存在一个输入操作表达式的寄存器约束为可选约束(意思是GCC可以从多个寄存器中选取一个,或使用非寄存器方式)时,比如“r”或“g”时,此输出操作表达式使用符号“&”修饰才有意义!如果你为所有的输入操作表达式指定了固定的寄存器,或使用内存/立即数约束时,则此输出操作表达式使用符号“&”修饰没有任何意义;

如果没有使用修饰符“&”修饰输出操作表达式会是什么样子呢?那就意味着GCC会先把输入操作表达式的值输入到选定的寄存器中,然后经过处理,最后才用输出值填充对应的输出操作表达式;

占位符
每一个占位符对应一个输入/输出操作表达式,内嵌汇编中有两种占位符:序号占位符和名称占位符;

1、序号占位符
GCC规定:一个内嵌汇编语句中最多只能有10个输入/输出操作表达式,这些操作表达式按照他们被列出来的顺序依次赋予编号0到9;对于占位符中的数字而言,与这些编号是对应的;比如:占位符%0对应编号为0的操作表达式,占位符%1对应编号为1的操作表达式,依次类推;

由于占位符前面要有一个百分号“%”,为了区别占位符与寄存器,GCC规定:在内嵌汇编语句的指令列表里列出的寄存器名称前面必须使用两个百分号(%%),以区别于占位符语法。GCC对占位符进行编译的时候,会将每一个占位符替换为对应的输入/输出操作表达式所指定的寄存器/内存/立即数;

2、名称占位符
由于GCC中限制这种占位符的个数最多只能由这10个,这也就限制了输入/输出操作表达式的数量做多只能有10个;如果需要的表达式的数量超过10个,那么,这些需要占位符就不够用了;GCC内嵌汇编提供了名称占位符来解决这个问题;即:使用一个名字字符串与一个表达式对应;这个名字字符串就称为名称占位符;而这个名字通常使用与表达式中的变量完全相同的名字;

使用名字占位符时,内嵌汇编的输入/输出操作表达式中的格式如下:

[name] "constraint"(变量)

此时,指令列表中的占位符的书写格式如下:

%[name]

这个格式等价于序号占位符中的%0,%1,%2等等,使用名称占位符时,一个name对应一个变量;

例如:

__asm__("imull %[value1],%[value2]":[value2] "=r"(data2):[value1] "r"(data1),"0"(data2));

此例中,名称占位符value1就对应变量data1,名称占位符value2对应变量data2;GCC编译的时候,同样会把这两个占位符分别替换成对应的变量所使用的寄存器/内存地址/立即数;而且也增强了代码的可读性;
这个例子,使用序号占位符的写法如下:

__asm__("imull %1,%0":"=r"(data2):"r"(data1),"0"(data2));
1.4 损坏部分

有的时候,当您想通知GCC当前内嵌汇编语句可能会对某些寄存器或内存进行修改,希望GCC在编译时能够将这一点考虑进去;那么您就可以在损坏部分声明这些寄存器或内存;

寄存器修改通知
这种情况一般发生在一个寄存器出现在指令列表中,但又不是输入/输出操作表达式所指定的,也不是在一些输入/输出操作表达式中使用“r”或“g”约束时由GCC选择的。同时,此寄存器被指令列表中的指令所修改,而这个寄存器只供当前内嵌汇编语句使用;比如:

__asm__("movl %0,%%ebx"::"a"(__foo):"bx");

这个内嵌汇编语句中,%ebx出现在指令列表中,并且被指令修改了,但是却未被任何输入/输出操作表达式所指定。所以,您需要在损坏部分指定“bx”,以让GCC知道这一点。

在损坏部分声明这些寄存器的方法很简单,只需要将寄存器的名字用双引号括起来就可以了;如果要声明多个寄存器,则相邻两个寄存器名字之间用逗号隔开。

注意:因为你在输入/输出操作表达式中指定寄存器,或当你为一些输入/输出操作表达式使用“r”/“g”约束,让GCC为你选择一个寄存器时,GCC对这些寄存器的状态是非常清楚的,它知道这些寄存器是被修改的,你根本不需要在损坏部分声明它们;但除此之外,GCC对剩下的寄存器中哪些会被当前内嵌汇编语句所修改却一无所知;所以,如果你真的在当前内嵌汇编指令中修改了它们,那么就最好在损坏部分声明它们,让GCC针对这些寄存器做相应的处理;否则,有可能会造成寄存器不一致,从而造成程序执行错误;

寄存器名称串:
“al”/“ax”/“eax”:代表寄存器%eax
“bl”/“bx”/“ebx”:代表寄存器%ebx
“cl”/“cx”/“ecx”:代表寄存器%ecx
“dl”/“dx”/“edx”:代表寄存器%edx
“si”/“esi”:代表寄存器%esi
“di”/“edi”:代表寄存器%edi

所以,只需要使用“ax”,“bx”,“cx”,“dx”,“si”,“di”就可以了,因为他们都代表对应的寄存器;

如果你在一个内嵌汇编语句的损坏部分向GCC声明了某个寄存器会发生改变。那么,在GCC编译时,如果发现这个被声明的寄存器的内容在此内嵌汇编之后还要继续使用,GCC会首先将此寄存器的内容保存起来,然后在此内嵌汇编语句的相关代码生成之后,再将其内容恢复。

另外需要注意的是,如果你在损坏部分声明了一个寄存器,那么这个寄存器将不能再被用作当前内嵌汇编语句的输入/输出操作表达式的寄存器约束,如果输入/输出操作表达式的寄存器约束被指定为“r”/“g”,GCC也不会选择已经被声明在损坏部分中的寄存器;

摘抄网站:https://blog.csdn.net/tyhh2001/article/details/44351797

附录A

https://www.cnblogs.com/scu-cjx/p/6878222.html

https://blog.csdn.net/qq_29343201/article/details/52199533

http://blog.chinaunix.net/uid-21602837-id-1823631.html

https://www.cnblogs.com/rain-blog/p/gnu-gcc-insert-asm.html

http://www.ethernut.de/en/documents/arm-inline-asm.html

https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Extended-Asm

【汇编优化】之内联汇编相关推荐

  1. 【汇编优化】之汇编中的指针操作

    序 汇编中,对于指针的操作都是以byte为单位进行指针偏移,示例: void func(int *parrayi, char *parrayc) {//STATEMENT } 纯汇编中操作,不管是对p ...

  2. linux gcc 内联汇编入门

    目录 2. 概览(Overview of the whole thing.) 3.GCC汇编语法(GCC Assembler Syntax.) 3.1. 源-目标顺序(Source-Destinati ...

  3. 32位汇编第七讲,混合编程,内联汇编

    32位汇编第七讲,混合编程 博客园IBinary原创 QQ:2510908331  博客连接:http://www.cnblogs.com/iBinary/ 转载请注明出处,谢谢 混合编程的概念,有时 ...

  4. (转)GCC内联汇编入门

    转自:http://blog.csdn.net/wuyao721/article/details/3573598 原文为GCC-Inline-Assembly-HOWTO,在google上可以找到原文 ...

  5. linux c内联汇编popl,ShiYanLou/汇编语言.md at master · Ewenwan/ShiYanLou · GitHub

    汇编语言简述 内联汇编 G++中的内联汇编分为基本形式的内联汇编与扩展形式的内联汇编:毫无疑问,扩展形式的内联汇编更加复杂,也更加强大 __asm__与asm 两者是一样的,只不过ANSI C标准将a ...

  6. 在Visual C++ 中使用内联汇编

    一. 优点 使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤.在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工 ...

  7. 须使用visual c 内联汇编语言开发,在VisualC 中使用内联汇编

    在VisualC 中使用内联汇编 2008-04-09 04:08:57来源:互联网 阅读 () 一.内联汇编的优缺点 因为在Visual C 中使用内联汇编不需要额外的编译器和联接器,且可以处理Vi ...

  8. 用VC写Assembly代码(7)--在Visual C++中使用内联汇编

    在Visual C++中使用内联汇编 一. 优点 使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤.在 Visual C++ 中,内联汇编是内置的编译器,因此不需 ...

  9. 在 Visual C++ 中使用内联汇编

    在 Visual C++ 中使用内联汇编 一. 优点 使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤.在 Visual C++ 中,内联汇编是内置的编译器,因此 ...

最新文章

  1. Python 之 Matplotlib (四)图例
  2. 跟我学雨林木风系统制作——2.涉及的技术及用到的工具介绍
  3. 成就解锁:BCH修复了所有常见的第三方交易延展性矢量
  4. 三星台积电大笑 LG弃自研CPU英特尔代工遭重创
  5. “全能”选手—Django 1.10文档中文版Part2
  6. sitecore系统教程之架构概述
  7. SQL Server物化视图学习笔记
  8. Django之ORM操作
  9. 在Window10上使用Ubuntu终端
  10. python查询最高分_精通 Oracle+Python,第 1 部分:查询最佳应践
  11. Linux下架设邮件服务器全攻略(二)
  12. VB得到指定文件夹下的文件列表
  13. 蓝桥杯2014c++真题:扑克序列(next_permutation)
  14. 【ccpc网络赛】Tree and Permutation【1009】【树上+组合数学】
  15. emq mysql消息存储_EMQ X 规则引擎系列(二)存储消息到 MySQL 数据库
  16. [渝粤教育] 深圳职业技术学院 安全教育与应急处理训练 参考 资料
  17. ps4html5播放器,PS4迎来全新媒体播放器 支持多种视频音频格式
  18. 认识CodeSmith
  19. MPU6050读取实验
  20. windows系统笔记本连接外部显示器进行分屏

热门文章

  1. polycom安卓手机客户端_Spark,安卓上最好用的邮件客户端来了!附App下载
  2. gateway中的局部过滤器_vue 过滤器
  3. Keil5的仿真调试
  4. Python中使用xpath结合contains
  5. python linux 命令_Python Linux 命令行 sudo
  6. python输出100到200的素数_python输出2到100之间的素数
  7. 打印php错误日志,php怎样打印错误日志
  8. 未来计算机的两个发展方向是巨型化和,【填空题】未来的计算机将以超大规模集成电路为基础,向巨型化、( )、多媒体化、网络化和智能化的方向发展。...
  9. 选下拉框的的值对应上传相应的图片_vue.js如何拿到多种类型表单值提交到后台,包含上传图片、单选、复选、文本框、下拉列表框...
  10. vue 两个日期比较大小