80x86汇编与C语言-2 (续)
80x86汇编与C语言-2 (续)
看清C到汇编的每一步翻译,搞懂调用约定、栈帧布局与变量映射
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
80x86汇编与C语言-2 (续)
看清C到汇编的每一步翻译,搞懂调用约定、栈帧布局与变量映射
条件移动指令的体系结构背
上一节我们看到 C 里的 if-else 普遍翻成 cmp + jcc,控制流分叉决定走哪条路。但 x86 还藏着另一类指令:不动跳转,直接「条件搬运」数据——这就是 CMOV 家族。
跳转像红绿灯分流车流,cmov 像两辆车同时发车,乘客坐两辆,最后只让对的那辆下客
条件移动指令的体系结构背
上一页算过分支预测失败的代价:十几到二十个周期白白浪费。硬件工程师的思路是——既然预测难,能不能干脆别猜,把两条路都算出来,再用条件选一个?
厨师并行做A和B,客人最终决定要哪个,直接端对的,不回厨房重做
循环的汇编语言表示-1
CMOV 是 CPU 对 'if-else' 的直接翻译。那 for/while 这种 '重复执行' 呢?答案更古老——依然靠 CMP 加 JMP。
跑到终点线就停,没到就再跑一圈——和 CMP+Jcc 回跳一模一样
循环的汇编语言表示-2
上一页看到 while 是「先测试再执行」的标准翻译。但 C 还有 do-while 这种先斩后奏的,以及 for 这种自带初始化的。当分支多到十几个,编译器搬出另一套武器——跳转表。
查楼层号直接跳过去,不用逐层比对房间号
循环的汇编语言表示-3
前两页看到 while、for、do-while 都被翻译成条件跳转,每次迭代都要「判断+跳转」。这一页看编译器如何用循环展开摊薄这些开销。
一次装多件 vs 一次处理多个元素,摊薄每次的开销
循环表示的体系结构背景
前面几页我们用条件跳转拼出了三种循环。x86 其实藏了一条专用 LOOP 指令,但它在现代代码里几乎绝迹——这条指令的命运,折射出 CPU 微架构二十年来的演进方向。
硬件一键完成'减一+判断+跳转',但电动缝纫机靠微处理器分步控制反而更快
Switch的汇编语言表示-1
前面循环讲的是「跳回去反复执行」。switch 是另一种控制流:判断一次,跳到 N 个分支中的某一个。case 密集时编译器有个核心武器——跳转表,本页拆给你看。
楼层号是下标,牌上写的房间号是目标地址,查一次直达
Switch的汇编语言表示-2
Switch的汇编语言表示-2:定义、要点与典型应用
Switch的汇编语言表示-3
上页讲密集case用跳表。但工程里case经常是稀疏的——比如错误码1、404、500——这种时候GCC怎么决定?
3位客人住1、404、500号;做1~500索引表浪费,不如逐个问
本节要点
- ✓分支预测失误代价高,cmov正是为此设计
- ✓while/for/do-while编译后形态趋同
- ✓Switch跳表用连续存储实现O(1)定位
- ✓稀疏case用决策树,密集case用跳表
- ✓汇编是编译器的策略选择,非唯一翻译
课后思考
先独立思考再看参考答案,三题层层递进:回顾、应用、边界。
参考答案cmov依赖标志位寄存器判断条件。两个立即数做算术后旧标志位已被改写,cmov无法据此判断;必须先算出结果再cmov选择,硬件就无法直接处理两个立即数的情形。
参考答案退回一连串if-else比较。跳转表大小与case取值范围成正比,稀疏时表大到浪费缓存;case数少时分支预测友好、代码更紧凑,编译器倾向于顺序比较。
参考答案当分支预测准确率高时,普通分支代价极低;而cmov无条件执行两条路径,性能反而更差。此时cmov的恒定开销超过分支的偶发错误开销,编译器退回分支。