1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 20:52:22 +00:00

ch3-05: 完善

This commit is contained in:
chai2010 2018-06-07 06:47:03 +08:00
parent b58f647e2f
commit ced4ed0696

View File

@ -95,7 +95,8 @@ TEXT ·main(SB), $16-0
MOVQ AX, temp-8(SP) // temp = AX MOVQ AX, temp-8(SP) // temp = AX
// 以a为参数调用函数 // 以a为参数调用函数
// ... CALL runtime·printint
CALL runtime·printnl
// 函数调用后, AX 可能被污染, 需要重新加载 // 函数调用后, AX 可能被污染, 需要重新加载
MOVQ temp-8*1(SP), AX // AX = temp MOVQ temp-8*1(SP), AX // AX = temp
@ -110,13 +111,55 @@ TEXT ·main(SB), $16-0
首先是将main函数的栈帧大小从24字节减少到16字节。唯一需要保存的是a变量的值因此在调用runtime·printint函数输出时全部的寄存器都可能被污染我们无法通过寄存器备份a变量的值只有在栈内存中的值才是安全的。然后在BX寄存器并不需要保存到内存。其它部分的代码基本保持不变。 首先是将main函数的栈帧大小从24字节减少到16字节。唯一需要保存的是a变量的值因此在调用runtime·printint函数输出时全部的寄存器都可能被污染我们无法通过寄存器备份a变量的值只有在栈内存中的值才是安全的。然后在BX寄存器并不需要保存到内存。其它部分的代码基本保持不变。
## goto跳转 ## if/goto跳转
TODO 早期的Go虽然提供了goto语句但是并不推荐在编程中使用。有一个和cgo类似的原则如果可以不使用goto语句那么就不要使用goto语句。Go语言中的goto语句是有严格限制的它无法跨越代码块并且在被跨越的代码中不能含义变量定义的语句。虽然Go语言不喜欢goto但是goto确实每个汇编语言码农的最爱。goto近似等价于汇编语言中的无条件跳转指令JMP配合if条件goto就组成了有条件跳转指令而有条件跳转指令正是构建整个汇编代码控制流的基石。
## if分支 为了便于理解我们用Go语言构造一个模拟三元表达式的If函数
TODO ```go
func If(ok bool, a, b int) int {
if ok { return a } else { return b }
}
```
比如求两个数最大值的三元表达式`(a>b)?a:b`用If函数可以这样表达`If(a>b, a, b)`。因为语言的限制用来模拟三元表达式的If函数不支持范型可以将a、b和返回类型改为空接口使用会繁琐一些
这个函数虽然看似只有简单的一行但是包含了if分支语句。在改用汇编实现前我们还是先用汇编的思维来重写If函数。在改写时同样要遵循每个表达式只能有一个运算符的限制同时if语句的条件部分必须只有一个比较符号组成if语句的body部分只能是一个goto语句。
用汇编思维改写后的If函数实现如下
```go
func If(ok int, a, b int) int {
if ok == 0 { goto L }
return a
L: return b
}
```
因为汇编语言中没有bool类型我们改用int类型代替bool类型。当ok参数非0时返回变量a否则返回变量b。我们将ok的逻辑反转下当ok参数为0时表示返回b否则返回变量a。在if语句中当ok参数为0时goto到L标号指定的语句也就是返回变量b。如果if条件不满足也就考试ok残非0执行后门的语句返回变量a。
上述函数的实现已经非常接近汇编语言,下面是改为汇编实现的代码:
```
TEXT ·If(SB), NOSPLIT, $0-32
MOVQ ok+8*0(FP), CX // ok
MOVQ a+8*1(FP), AX // a
MOVQ b+8*2(FP), BX // b
CMPQ CX, $0 // test ok
JZ L // if ok == 0, skip 2 line
MOVQ AX, ret+24(FP) // return a
RET
L:
MOVQ BX, ret+24(FP) // return b
RET
```
首选是将三个参数加载到寄存器中ok参数对应CX寄存器a、b分别对应AX、BX寄存器。然后使用CMPQ比较指令将CX寄存器和常数0进行比较。如果比较的结果为0那么下一条JZ为0时跳转指令将跳转到L标号对应的指令也就是返回变量b的值。如果比较的结果不为0那么JZ指令讲没有效果继续执行后的指令也就是返回变量a的值。
在跳转指令中跳转的目标一般是通过一个标号表示。不过在有些通过宏实现的函数中更希望通过相对位置跳转这时候可以通过PC寄存器的来计算跳转的位置。
## for循环 ## for循环