mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-24 12:32:21 +00:00
ch3-04: 完善
This commit is contained in:
parent
2cfff240b9
commit
deaa708413
@ -2,4 +2,82 @@
|
|||||||
|
|
||||||
终于到函数了!因为Go汇编语言中,可以也建议通过Go语言来定义全局变量,那么剩下的也就是函数了。只有掌握了汇编函数的基本用法,才能真正算是Go汇编语言入门。本章将简单讨论Go汇编中函数的定义和用法。
|
终于到函数了!因为Go汇编语言中,可以也建议通过Go语言来定义全局变量,那么剩下的也就是函数了。只有掌握了汇编函数的基本用法,才能真正算是Go汇编语言入门。本章将简单讨论Go汇编中函数的定义和用法。
|
||||||
|
|
||||||
|
## 基本语法
|
||||||
|
|
||||||
|
函数标识符通过TEXT汇编指令定义,表示该行开始的指令定义在TEXT内存段。TEXT语句后的指令一般对应函数的实现,但是对于TEXT指令本身来说并不关心后面是否有指令。我个人绝对TEXT和LABEL定义的符号是类似的,区别只是LABEL是用于跳转标号,但是本质上他们都是通过标识符映射一个内存地址。
|
||||||
|
|
||||||
|
函数的定义的语法如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
TEXT symbol(SB), [flags,] $framesize[-argsize]
|
||||||
|
```
|
||||||
|
|
||||||
|
函数的定义部分由5个部分组成:TEXT指令、函数名、可选的flags标志、函数帧大小和可选的函数参数大小。
|
||||||
|
|
||||||
|
其中Text用于定义函数符号,函数名中当前包的路径可以省略。函数的名字后面是`(SB)`,表示是相对于的函数名符号对相对于SB伪寄存器的偏移量,二者组合在一起最终是绝对地址。作为全局的标识符的全局变量和全局函数的名字一般都是基于SB伪寄存器的相对地址。标志部分用于指示函数的一些特殊行为,常见的NOSPLIT主要用于指示叶子函数不进行栈分裂。framesize部分表示函数的局部变量需要多少栈空间,其中包含调用其它函数是准备调用参数的隐式栈空间。最后是可以省略的参数大小,之所以可以省略是因为编译器可以从Go语言的函数声明中推导出函数参数的大小。
|
||||||
|
|
||||||
|
下面是在main包中Add在汇编中两种定义方式:
|
||||||
|
|
||||||
|
```
|
||||||
|
// func Add(a, b int) int
|
||||||
|
TEXT main·Add(SB), NOSPLIT, $0-24
|
||||||
|
|
||||||
|
// func Add(a, b int) int
|
||||||
|
TEXT ·Add(SB), $0
|
||||||
|
```
|
||||||
|
|
||||||
|
第一种是最完整的写法:函数名部分包含了当前包的路径,同时指明了函数的参数大小为24个字节(对应参数和返回值的3个int类型)。第二种写法则比较简洁,省略了当前包的路径和参数的大小。需要注意的是,标志参数中的NOSPLIT如果在Go语言函数声明中通过注释指明了标志,应该也是可以省略的(需要确认下)。
|
||||||
|
|
||||||
|
目前可能遇到的函数函数标志有NOSPLIT、WRAPPER和NEEDCTXT几个。其中NOSPLIT不会生成或包含栈分裂代码,这一般用于没有任何其它函数调用的叶子函数,这样可以适当提高性能。WRAPPER标志则表示这个是一个包装函数,在panic或runtime.caller等某项处理函数帧的地方不会增加函数帧计数。最后的NEEDCTXT表示需要一个上下午参数,一般用于闭包函数。
|
||||||
|
|
||||||
|
需要注意的是函数也没有类型,上面定义的Add函数签名可以下面任意一种格式:
|
||||||
|
|
||||||
|
```
|
||||||
|
func Add(a, b int) int
|
||||||
|
func Add(a, b, c int)
|
||||||
|
func Add() (a, b, c int)
|
||||||
|
func Add() (a []int) // reflect.SliceHeader 切片头刚好也是 3 个 int 成员
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
对于汇编函数来说,只要是函数的名字和参数大小一致就可以是相同的函数了。而且在Go汇编语言中,输入参数和返回值参数是没有任何的区别的。
|
||||||
|
|
||||||
|
|
||||||
|
下面几种类型都可以对应到
|
||||||
|
|
||||||
|
。比如上述的Add函数只要函数的名字和参数大小一致,那么从汇编
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
- 以上函数在汇编中是一样的
|
||||||
|
- 汇编中, 函数也没有类型, 类型在Go中定义
|
||||||
|
- 汇编只需要函数的名字和各个参数的偏移位置
|
||||||
|
|
||||||
|
## 函数参数和返回值
|
||||||
|
|
||||||
|
<!-- 代码中有栈的布局注释 -->
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## 函数中的局部变量
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## 函数中的(真)(伪)SP
|
||||||
|
|
||||||
|
<!-- 伪SP是当前func栈的起点,不会发生变化,便于局部变量定位 -->
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## 调用其它函数
|
||||||
|
|
||||||
|
<!-- 基于栈传参和返回值,性能稍低,但是栈帧非常规整,好分析 -->
|
||||||
|
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## 宏函数
|
||||||
|
|
||||||
|
<!-- swap 方法,几个版本 -->
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
Loading…
x
Reference in New Issue
Block a user