编译原理之美
宫文学
北京物演科技CEO
立即订阅
8171 人已学习
课程目录
已完结 43 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 为什么你要学习编译原理?
免费
实现一门脚本语言 · 原理篇 (13讲)
01 | 理解代码:编译器的前端技术
02 | 正则文法和有限自动机:纯手工打造词法分析器
03 | 语法分析(一):纯手工打造公式计算器
04 | 语法分析(二):解决二元表达式中的难点
05 | 语法分析(三):实现一门简单的脚本语言
06 | 编译器前端工具(一):用Antlr生成词法、语法分析器
07 | 编译器前端工具(二):用Antlr重构脚本语言
08 | 作用域和生存期:实现块作用域和函数
09 | 面向对象:实现数据和方法的封装
10 | 闭包: 理解了原理,它就不反直觉了
11 | 语义分析(上):如何建立一个完善的类型系统?
12 | 语义分析(下):如何做上下文相关情况的处理?
13 | 继承和多态:面向对象运行期的动态特性
实现一门脚本语言 · 应用篇 (2讲)
14 | 前端技术应用(一):如何透明地支持数据库分库分表?
15 | 前端技术应用(二):如何设计一个报表工具?
实现一门脚本语言 · 算法篇 (3讲)
16 | NFA和DFA:如何自己实现一个正则表达式工具?
17 | First和Follow集合:用LL算法推演一个实例
18 | 移进和规约:用LR算法推演一个实例
实现一门脚本语言 · 热点答疑与用户故事 (2讲)
19 | 案例总结与热点问题答疑:对于左递归的语法,为什么我的推导不是左递归的?
用户故事 | 因为热爱,所以坚持
编译原理 · 期中考试周 (1讲)
期中考试 | 来赴一场100分的约定吧!
免费
实现一门编译型语言 · 原理篇 (12讲)
20 | 高效运行:编译器的后端技术
21 | 运行时机制:突破现象看本质,透过语法看运行时
22 | 生成汇编代码(一):汇编语言其实不难学
加餐 | 汇编代码编程与栈帧管理
23 | 生成汇编代码(二):把脚本编译成可执行文件
24 | 中间代码:兼容不同的语言和硬件
25 | 后端技术的重用:LLVM不仅仅让你高效
26 | 生成IR:实现静态编译的语言
27 | 代码优化:为什么你的代码比他的更高效?
28 | 数据流分析:你写的程序,它更懂
29 | 目标代码的生成和优化(一):如何适应各种硬件架构?
30 | 目标代码的生成和优化(二):如何适应各种硬件架构?
实现一门编译型语言 · 应用篇 (2讲)
31 | 内存计算:对海量数据做计算,到底可以有多快?
32 | 字节码生成:为什么Spring技术很强大?
实现一门编译型语言 · 扩展篇 (3讲)
33 | 垃圾收集:能否不停下整个世界?
34 | 运行时优化:即时编译的原理和作用
35 | 案例总结与热点问题答疑:后端部分真的比前端部分难吗?
面向未来的编程语言 (3讲)
36 | 当前技术的发展趋势以及其对编译技术的影响
37 | 云编程:云计算会如何改变编程模式?
38 | 元编程:一边写程序,一边写语言
结束语 (1讲)
结束语 | 用程序语言,推动这个世界的演化
编译原理之美
登录|注册

22 | 生成汇编代码(一):汇编语言其实不难学

宫文学 2019-10-11
敲黑板:课程用的是 GNU 汇编器,macOS 和 Linux 已内置,本文的汇编语言的写法是 GNU 汇编器规定的写法。Windows 系统可安装 MinGW 或 Linux 虚拟机。
对于静态编译型语言,比如 C 语言和 Go 语言,编译器后端的任务就是生成汇编代码,然后再由汇编器生成机器码,生成的文件叫目标文件,最后再使用链接器就能生成可执行文件或库文件了。
就算像 JavaScript 这样的解释执行的语言,也要在运行时利用类似的机制生成机器码,以便调高执行的速度。Java 的字节码,在运行时通常也会通过 JIT 机制编译成机器码。而汇编语言是完成这些工作的基础。
对你来说,掌握汇编语言是十分有益的,因为哪怕掌握一小点儿汇编技能,就能应用到某项工作中,比如,在 C 语言里嵌入汇编,实现某个特殊功能;或者读懂某些底层类库或驱动程序的代码,因为它可能是用汇编写的。
本节课,我先带你了解一下汇编语言,来个破冰之旅。然后在接下来的课程中再带你基于 AST 手工生成汇编代码,破除你对汇编代码的恐惧,了解编译期后端生成汇编代码的原理。
以后,当你看到高级语言的代码,以及 IR 时,就可以想象出来它对应的汇编代码是什么样子,实现从上层到底层认知的贯通。

了解汇编语言

取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《编译原理之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(6)

  • 沉淀的梦想
    没太看懂文稿中的"leaq L_.str(%rip), %rdi"里面的"L_.str(%rip)"的含义,能再解释一下吗?

    作者回复: 就是一个间接内存访问。基数是%rip,也就是指令寄存器中的值,也就是leaq这行代码的下一行代码的地址。L_.str会被汇编器算出一个直接数来,比如0x20,也就是32个字节。这行指令就等价于:0x20(%rip)。
    0x20是什么意思?就是这个字符串常量的地址与%rip值的差。这种寻址方式叫做rip相对寻址。因为代码区在下面,静态数据区在上面,所以静态数据的地址一定大于指令的地址。在这里,是多0x20个字节,也就是32个字节。
    为什么要这么麻烦呢?为什么不使用这个字符串的确定地址呢?这主要是为了让程序代码更加位置无关。在使用动态库之类的场景的时候有好处。

    另外,lea这个指令除了获取地址以外,还有一个“奇怪”的用法,它可以用计算地址的方法,实际上对寄存器的值同时完成加法和乘法的计算,相当于一个三元计算,并且只使用一个时钟周期。比如:lea 0x20(,%eax,2) %eax,本来是用来算一个地址,结果被用来一次性的做了一个乘法和一个加法计算。

    提供3篇文章作为参考:
    https://stackoverflow.com/questions/40329260/why-is-the-address-of-static-variables-relative-to-the-instruction-pointer

    https://stackoverflow.com/questions/3250277/how-to-use-rip-relative-addressing-in-a-64-bit-assembly-program

    https://zhuanlan.zhihu.com/p/58774036

    2019-10-12
    2
  • 权华
    刚刚的问题,xorl %eax, %eax,将返回值置为0。

    作者回复: 对的。

    2019-11-12
  • 权华
    leaq L_.str(%rip), %rdi
    leaq L_.str.1(%rip), %rsi
    xorl %eax, %eax
    callq _printf
    xorl %eax, %eax

    宫老师你好,这是文章前面的汇编代码的一部分,我不明白 xorl %eax, %eax 这两行代码,它的作用是什么?为什么没有给寄存器 eax 就直接 xorl 操作了。

    作者回复: 修昂党羽给eax赋值为0,但用xorl使用的时钟周期更少。

    2019-11-12
  • 不的
    老师,为啥要设计成区分调用者、被调用者保护的寄存器,统一被调用者或者调用者保护,有啥问题么

    作者回复: 这里有一个效率的问题。

    如果全部都是调用者保护,那么其实你调用的对象根本不会破坏你的寄存器,你也要保护起来,那就增加了成本,对于逻辑比较简单的被调用者,是用不到几个寄存器的。

    如果全部都是被调用者保护,也是一样的逻辑。如果调用者用了很少几个寄存器,被调用者却要保护很多,也不划算。

    所以最优的方法,其实是比较中庸主义的。我觉得这可以用概率的方法做比较严谨的证明。

    2019-10-23
  • 沉淀的梦想
    看了老师的回答与讲解"RIP相对寻址"的文章,但是还是不太理解:%rip存储的是下一条要执行的代码的地址,他存储值应该在不停地变,所以他和静态数据的偏移应该也在不停地变,为什么这里的偏移(L_.str)看起来是个常量啊?

    作者回复: 对的。
    如果你在不同的代码里调用使用L_.str(%rip),前面的L_.str对应的立即数是不同的。这个计算工作是由汇编器来算,不影响我们编程的效率。

    2019-10-14
  • 感受是写了一大坨,天都黑了,还没写完。。。

    作者回复: 就是搬砖的工作太多,创造性的工作太少:-)

    2019-10-12
收起评论
6
返回
顶部