查看汇编代码:go tool compile -S asm.go
左边为常见内存布局:
中间为X86寄存器:
Go汇编为了简化汇编代码的编写,引入了PC、FP、SP、SB四个伪寄存器(详见《go汇编ASM简介》)
在AMD64环境:
a(SP)和b-8(SP)
;(SP)和+8(SP)
;Go汇编语言提供了DATA命令用于初始化包变量:
$
开始,如$0xA1)DATA symbol+offset(SB)/width, value
全局变量是包一级的变量,全局变量一般有着较为固定的内存地址,声明周期跨越整个程序运行时间。
GLOBL symbol(SB), width
定义好的变量需要导出后,才可供其他代码引用(在汇编中定义变量,然后在go中使用):
// asm.go
package asm
var count int32 // 声明,会关联到.s中对应变量
var Name string
// asm_amd64.s 是amd64的汇编
#include "textflag.h"
GLOBL ·Id(SB),NOPTR,$4
DATA ·count+0(SB)/1,$1
DATA ·count+1(SB)/1,$2
DATA ·count+2(SB)/1,$3
DATA ·count+3(SB)/1,$4
// 或者(小端)
// DATA ·count+0(SB)/4,$0x04030201
GLOBL ·Name(SB),NOPTR,$24
DATA ·Name+0(SB)/8,$·Name+16(SB)
DATA ·Name+8(SB)/8,$6
DATA ·Name+16(SB)/8,$"gopher"
Go汇编语言中常量以$
美元符号为前缀。常量的类型有整数、浮点数、字符和字符串等几种类型。数值型常量,可通过表达式构造新的常量:
$1 // 十进制
$0x1234ABCD // 十六进制
$1.5 // 浮点数
$'a' // 字符
$"abcd" // 字符串
$2+2 // == $4
$(3&1)<<2 // == $4
Go语言中数组是一种扁平内存结构的基础类型:
var num [2]int
// asm
GLOBL ·num(SB),$16
DATA ·num+0(SB)/8,$0
DATA ·num+8(SB)/8,$0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mOFliZ1F-1670753220575)(pitures/2022-go-asm-array-2int.png)]
注:切片与字符串类似,是一种带Header的结构
字符串(只读类型,要避免在汇编中直接修改字符串底层数据的内容)在go中是一个struct(包含数据地址与长度);amd64环境中StringHeader有16个字节大小,因此在汇编中定义一个16字节大小的变量(·helloworld):
// data of string
GLOBL text<>(SB),NOPTR,$16
DATA text<>+0(SB)/8,$"Hello Wo"
DATA text<>+8(SB)/8,$"rld!"
// header of string
GLOBL ·helloworld(SB),$16
DATA ·helloworld+0(SB)/8,$text<>(SB) // StringHeader.Data
DATA ·helloworld+8(SB)/8,$12 // StringHeader.Len
函数标识符通过TEXT汇编指令定义,其后的指令一般对应函数的实现:
TEXT symbol(SB), [flags,] $framesize[-argsize]
各部分说明:
(SB)
,表示是函数名符号相对于SB伪寄存器的偏移量,二者组合在一起形成最终的绝对地址。Go汇编语言要求,任何通过FP伪寄存器访问的变量必和一个临时标识符前缀组合后才能有效,一般使用参数对应的变量名作为前缀。函数的第一个参数和第一个返回值会分别进行一次地址对齐。
Goroutine是Go中最基本的执行单元。goroutine就是一段代码加一个函数入口,以及在堆上为其分配的堆栈(初始4k,可随需要增长收缩);因此其非常廉价。
goroutine调用与普通函数类似:
MOVL $1, 0(SP)
MOVL $2, 4(SP)
MOVL $3, 8(SP)
PUSHQ $f(SB)
PUSHQ $12
CALL runtime.newproc(SB)
POPQ AX
POPQ AX