From d9bd83a2ff0bb5dcac37307b06ac94d82331ca1e Mon Sep 17 00:00:00 2001 From: chai2010 Date: Fri, 15 Jun 2018 14:24:06 +0800 Subject: [PATCH] =?UTF-8?q?ch3-04:=20=E5=A2=9E=E5=8A=A0=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ch3-asm/ch3-04-func.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ch3-asm/ch3-04-func.md b/ch3-asm/ch3-04-func.md index 4d09c52..c8ad237 100644 --- a/ch3-asm/ch3-04-func.md +++ b/ch3-asm/ch3-04-func.md @@ -6,6 +6,9 @@ 函数标识符通过TEXT汇编指令定义,表示该行开始的指令定义在TEXT内存段。TEXT语句后的指令一般对应函数的实现,但是对于TEXT指令本身来说并不关心后面是否有指令。我个人绝对TEXT和LABEL定义的符号是类似的,区别只是LABEL是用于跳转标号,但是本质上他们都是通过标识符映射一个内存地址。 +![](../images/ch3-func-decl-01.ditaa.png) + + 函数的定义的语法如下: ``` @@ -46,6 +49,8 @@ func Add() (a []int) // reflect.SliceHeader 切片头刚好也是 3 个 int 成 对于函数来说,最重要是是函数对外提供的API约定,包含函数的名称、参数和返回值。当名称和参数返回都确定之后,如何精确计算参数和返回值的大小是第一个需要解决的问题。 +![](../images/ch3-func-decl-02.ditaa.png) + 比如有一个Foo函数的签名如下: ```go @@ -129,8 +134,14 @@ func SomeFunc(FP *SomeFunc_args_and_returns) { 代码完全和Foo函数参数的方式类似。唯一的差异是每个函数的偏移量,这有`unsafe.Offsetof`函数自动计算生成。因为Go结构体中的每个成员已经满足了对齐要求,因此采用通用方式得到每个参数的偏移量也是满足对齐要求的。 +![](../images/ch3-func-arg-01.ditaa.png) + + + ## 函数中的局部变量 + + 从Go语言函数角度讲,局部变量是函数内明确定义的变量,同时也包含函数的参数和返回值变量。但是从Go汇编角度看,局部变量是指函数运行时,在当前函数栈帧所对应的内存内的变量,不包含函数的参数和返回值(因为访问方式有差异)。函数栈帧的空间主要由函数参数和返回值、局部变量和被调用其它函数的参数和返回值空间组成。为了便于理解,我们可以将汇编函数的局部变量类比为Go语言函数中显式定义的变量,不包含参数和返回值部分。 为了便于访问局部变量,Go汇编语言引入了伪SP寄存器,对应当前栈帧的底部。因为在当前栈帧时间栈的底部是固定不变的,因此局部变量的相对于伪SP的偏移量也就是固定的,这可以简化局部变量的维护工作。SP真伪区分只有一个原则:如果使用SP时有一个临时标识符前缀就是伪SP,否则就是真SP寄存器。比如`a(SP)`和`b+8(SP)`有a和b临时前缀,这里都是伪SP,而前缀部分一般用于表示局部变量的名字。而`(SP)`和`+8(SP)`没有临时标识符作为前缀,它们都是真SP寄存器。