内容简介:最近在阅读 Go1.5 的源码,发现源码中多处调用了了源文件:在此期间参阅了大量的资料,最大的坑就是 Go 使用的汇编是 Plan9而非常见的 x86 汇编,参考资料如下:
Mon 11 February 2019
最近在阅读 Go 1.5 的源码,发现源码中多处调用了了 memclr ,进一步深入了解发现原来 Go 为了实现内存复用会将已经回收到 cache 的内存清除,清除逻辑使用汇编实现,本文是基于自己的理解进行的注释。
注意:
- 最新版的 Go 中(1.11)该函数已经改名为 memclrNoHeapPointers
- 我自己添加的注释以 //# 开始
- Go 针对不同的平台实现了不同的内存清除,这里仅对 amd64 平台下的实现进行注释:
源文件: https://github.com/golang/go/blob/release-branch.go1.5/src/runtime/memclr_amd64.s
// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build !plan9 #include "textflag.h" // NOTE: Windows externalthreadhandler expects memclr to preserve DX. // void runtime·memclr(void* ptr, uintptr n) //# - TEXT 在 Plan9 中用于声明函数 //# - runtime·memclr 为函数名,中间的·不是普通的点在 Mac 下通过 Option+Shift+9 打出,· 前是包名后面是函数名 //# - SB(伪寄存器)全局静态基指针,用来声明函数或全局变量(此处用来声明函数) //# - NOSPLIT 定义在 https://github.com/golang/go/blob/master/src/runtime/textflag.h //# - $0-16: //# + $0 栈帧大小为0(局部变量+可能需要的额外调用函数的参数空间总大小,不包括调用其他函数时的 ret address 的大小) //# + 16 参数基返回值的大小(16 表示两个双四字的参数) TEXT runtime·memclr(SB), NOSPLIT, $0-16 //# FP(伪寄存器):通过 `symbol+offset(FP)` 的方式引用输入参数 //# symbol 没有任何用,只是增加可读性,但不能省略 //# FP 指向整个栈帧的底部的 BP 寄存器 MOVQ ptr+0(FP), DI //# 第一个参数移动到 DI 寄存器(DI 目标索引寄存器) MOVQ n+8(FP), BX //# 第二个参数移动到 BX 寄存器(BX 为基址寄存器,用于内存寻址) XORQ AX, AX //# 清零 AX 寄存器(AX 为累加寄存器) // MOVOU seems always faster than REP STOSQ. tail: TESTQ BX, BX // set ZF to 1 if n is 0 JEQ _0 // jump to _0 if ZF == 1(returns) CMPQ BX, $2 JBE _1or2 // jump to _1or2 if n <= 2 CMPQ BX, $4 JBE _3or4 // jump to _3or4 if n > 2 and n <= 4 CMPQ BX, $8 // ... JBE _5through8 CMPQ BX, $16 JBE _9through16 //# 大于 16 开始使用 128 位寄存器 X0, //# PXOR 将 X0 寄存器置为 0 PXOR X0, X0 CMPQ BX, $32 JBE _17through32 CMPQ BX, $64 JBE _33through64 CMPQ BX, $128 JBE _65through128 CMPQ BX, $256 JBE _129through256 // TODO: use branch table and BSR to make this just a single dispatch // TODO: for really big clears, use MOVNTDQ. //# 大于 256 则通过循环 loop: //# MOVOU 相当于 AT&T/Intel 的 MOVDQU -- 移动非对齐的双四字 //# X0 相当与 AT&T/Intel 的 SSE 新增的 %xmm0(128位元暂存器) //# 参见 https://zh.wikipedia.org/wiki/SSE MOVOU X0, 0(DI) MOVOU X0, 16(DI) MOVOU X0, 32(DI) MOVOU X0, 48(DI) MOVOU X0, 64(DI) MOVOU X0, 80(DI) MOVOU X0, 96(DI) MOVOU X0, 112(DI) MOVOU X0, 128(DI) MOVOU X0, 144(DI) MOVOU X0, 160(DI) MOVOU X0, 176(DI) MOVOU X0, 192(DI) MOVOU X0, 208(DI) MOVOU X0, 224(DI) MOVOU X0, 240(DI) SUBQ $256, BX //# 递减 BX ADDQ $256, DI //# 递增 DI CMPQ BX, $256 JAE loop //# 如果 BX 依然大于 256 则继续循环 JMP tail //# 否则进入 tail _1or2: MOVB AX, (DI) MOVB AX, -1(DI)(BX*1) RET _0: RET _3or4: MOVW AX, (DI) MOVW AX, -2(DI)(BX*1) RET _5through8: MOVL AX, (DI) MOVL AX, -4(DI)(BX*1) RET _9through16: MOVQ AX, (DI) MOVQ AX, -8(DI)(BX*1) RET _17through32: MOVOU X0, (DI) MOVOU X0, -16(DI)(BX*1) RET _33through64: MOVOU X0, (DI) MOVOU X0, 16(DI) MOVOU X0, -32(DI)(BX*1) MOVOU X0, -16(DI)(BX*1) RET _65through128: MOVOU X0, (DI) MOVOU X0, 16(DI) MOVOU X0, 32(DI) MOVOU X0, 48(DI) MOVOU X0, -64(DI)(BX*1) MOVOU X0, -48(DI)(BX*1) MOVOU X0, -32(DI)(BX*1) MOVOU X0, -16(DI)(BX*1) RET _129through256: MOVOU X0, (DI) MOVOU X0, 16(DI) MOVOU X0, 32(DI) MOVOU X0, 48(DI) MOVOU X0, 64(DI) MOVOU X0, 80(DI) MOVOU X0, 96(DI) MOVOU X0, 112(DI) MOVOU X0, -128(DI)(BX*1) MOVOU X0, -112(DI)(BX*1) MOVOU X0, -96(DI)(BX*1) MOVOU X0, -80(DI)(BX*1) MOVOU X0, -64(DI)(BX*1) MOVOU X0, -48(DI)(BX*1) MOVOU X0, -32(DI)(BX*1) MOVOU X0, -16(DI)(BX*1) RET
在此期间参阅了大量的资料,最大的坑就是 Go 使用的汇编是 Plan9而非常见的 x86 汇编,参考资料如下:
以上所述就是小编给大家介绍的《Go 1.5 内存清除汇编源码注释》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- iOS 注释方法大全 代码块加快捷键注释
- 让 MyBatis Generator 用数据库注释作 Java 注释,并支持附加注解
- iOS汇编入门教程(一)ARM64汇编基础
- iOS 汇编入门教程(一):ARM64 汇编基础
- 请停止代码注释
- iOS汇编入门教程(三)汇编中的 Section 与数据存取
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。