Control
%rsp
中存放的是现在的指针栈顶(有元素入栈的时候,栈顶指针是减小的,也就是说栈是从高地址向低地址延申的)
%rip
(instruction pointer)里面放的是当前指令的地址(pc program counter)
旧的有 8 个寄存器,新加的 8 个寄存器是 %r8-%r15
CF
无符号数的进位符(carry)(最高位进位,也就是无符号数产生了溢出)
SF
有符号数的 sign flag,1 表示负数
ZF
zero flag
OF
有符号数的溢出标志位(overflow flag)(两个正数相加小于零 或 两个负数相加大于等于零)
总结:相当于是有符号数溢出看 OF
,无符号数溢出看 CF
cmpq b,a
是计算 a-b
哦,后减前哦。
testq b,a
则是计算 a&b
实际操作:
Conditional branches
cmpq %rsi, %rdi
是后减前 (%rdi
-%rsi
)
movq %rdi, %rax
是前移后(%rdi
移到 %rax
)
subq %rsi, %rax
是后减前 (%rax
- %rsi
)
总结:移动的时候是前移后,算数运算时是后 op 前
Loops
这里的初始判断可以省略,根据视频我感觉他说的意思是:编译器在优化的时候会先判断一下初始的 i 是否满足 test 的这个条件,假如满足的话,那就直接扔掉这个 initial test
Switch Statements
教授说这种设计很糟糕hhhhh
switch 的汇编代码和 多个 if-else 并不一样。
这里是 ja
jump above ,此处用了一个小技巧,ja
是无符号数比较,所以一个负数当作无符号数时会被认为是一个非常大的值,所以这句话的效果是,当 x>6
或者 x
为负数时就跳到 default 处。此处不用 jg
(jump greater than),因为 jg
是有符号数的比较跳转。真的🐂🍺
这里计算好之后直接在 break 处 return ,return 的值存在 %rax
这里很奇特,因为在 case 1,2
的时候是没有用到 w
的初值的(是直接给 w
进行了赋值),所以编译器在整个 switch
之前压根就没进行 w
的初始化(🐂🍺),而在 case 3
处,我突然需要初值了,那就赶紧在前面加了一句…
movl
移的是 32 bits ,可以将 %rax
的高位置零(%rax
是 64 bit,%eax
是 32 bit)
底下有个学生问了个问题:假如 case 只有 1 和 1000000,那中间的都要建表吗?教授说,编译器会聪明地把这个转化成 if-else 。
另一个问题是,假如某些 case
本身就是负值,该怎么办?回答是:会加上一个偏置使他们变成非负的(第一个 case 变成 0)。
真的🐂🍺
总之 switch 语句可以使查找变成 O ( n ) O(n) O(n) ,假如是稀疏大值,编译器会建立一个 if-else 树,使得跳转树到每个 case 都是 O ( log n ) O(\log{n}) O(logn) 的。