作者:淡逸幽悠 | 来源:互联网 | 2023-07-18 12:20
开头的碎碎念:对接微信公众平台的时候,开始有个字符串排序,我接触golang毕竟时间尚浅,很多东西都是能从网上找到就直接从网上找,结果就是找了好几个示例代码都不好用,好容易一个好用的,从头开始实现
开头的碎碎念:
对接微信公众平台的时候,开始有个字符串排序,我接触golang毕竟时间尚浅,很多东西都是能从网上找到就直接从网上找,结果就是找了好几个示例代码都不好用,好容易一个好用的,从头开始实现的,代码太多了。我就想,google应该把这些玩意都封装好了吧,不然一个新出的语言只有基础语法,没有强大的标准库,谁用这玩意啊。也就是那时候第一次接触src文件夹,后来发现pkg里的那些go文件是绝好的学习资料。
那么多文件、文件夹从哪开始看呢,我的原则,先找没有依赖性的,也就是没有import的,这么寻摸着就找到了math文件夹。笨方法,从a开始按顺序来呗,这不就碰到了abs.go
难以理解的第12行:
// Copyright 2009 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.
package math
// Abs returns the absolute value of x.
//
// Special cases are:
// Abs(±Inf) = +Inf
// Abs(NaN) = NaN
func Abs(x float64) float64
func abs(x float64) float64 {
switch {
case x <0:
return -x
case x == 0:
return 0 // return correctly abs(-0)
}
return x
}
孤零零的一行,一个方法声明,别的啥都没有,完全不解其意。
下面那个abs()不用我说了吧,很简单的,取绝对值。
不管了,先到math文件夹看看,abs_386.s、abs_amd64.s、abs_arm.s有这三个文件估计跟那行不知道哪来的代码有关系,.s结尾的,汇编语言文件,继续发动google的威力,golang、汇编混编,如此便找到了http://www.mikespook.com/2013/02/%E7%BF%BB%E8%AF%91go-%E5%92%8C%E6%B1%87%E7%BC%96/
让程序跑起来:
先让这段程序跑起来吧,因为我的机器是64位的,所以我把abs.go、abs_amd64.s两个文件拷贝到别处,abs.go的包改成了mymath,另外写了一个简单的测试程序
package main
import(
"fmt"
"mymath"
)
func main(){
fmt.Println(mymath.Abs(-12.00))
}
在/pkg/tool/windows_amd64下有很多有用的工具,6g、6l啥的,不过常用的都被go命令给封装了,直接go build、go install等命令就解决了。
涉及到汇编的,主要是6a,上面的代码按如下顺序编译:
6a abs_amd64.s(生成abs_amd64.6)
6g –o _go_.6 abs.go(生成_go_.6)
pack grc abs.a _go_.6 abs_amd64.6(生成abs.a)
本来是想直接让主程序调用目录下的库的,import “./mymath”,不过,windows下提示出错import path contains invalid characte ‘:’:”c:/xxx/xxx”,所以只得将abs.a扔到pkg/windows_amd64文件夹中。
剩下的就是:
6g test.go(生成test.6)
6l test.6(生成6.out.exe)
从简单的golang编译器自动生成的汇编入手:
先看一个新的命令,golang编译器自动生成汇编中间代码的命令,go tool 6g –S XXX.go,其实上面的那些命令也都可以用go tool XXX来代替,go命令把那些命令都封装进去了。
来个最简单的代码吧:
package asm
func Asm(i int)int{
return i
}
go tool 6g –S asm.go:
--- prog list "Asm" ---
0000 (asm.go:3) TEXT Asm+0(SB),$0-16
0001 (asm.go:3) LOCALS ,$0
0002 (asm.go:3) TYPE i+0(FP){int},$8
0003 (asm.go:3) TYPE ~anon1+8(FP){int},$8
0004 (asm.go:4) MOVQ i+0(FP),BX
0005 (asm.go:4) MOVQ BX,~anon1+8(FP)
0006 (asm.go:4) RET ,
plan9汇编,语法跟AT&T颇为类似,传值是前面是源,后面是目的,这点跟masm、nasm啥的都是反的。
000行:TEXT相当于定义了一个函数,Asm函数名,+0(SB)golang生成的都有这玩意;$0-16,经过我的反复尝试,起码对于int、float64这两者而言,是(参数个数+返回值个数)*8(这都是我自己验证的,没啥科学依据,相关文档我也翻阅过一些,不过鸟语不过关,将把能看懂的东西里没有我需要的,大胆假设,小心论证现在还做不到)。
001行:我估计是执行指令的位置,不过这都不重要,关键是后头的。
002、003行:i是变量名,~anon1其实也是变量名(系统自动生成的)
稍微修改下
func Asm(i int) (j int){
j=i
return
}
则003行,就变成了j+8(FP)
至于0(FP)、8(FP),对于int来说,每个数字占8个字节(64位下),所以传入的参数,第一个就是+0(FP),第二个+8(FP),第三个+16(FP),第四个+24(FP)…
返回值,如果有多个返回值,第一个+(8+最后一个传入参数的数值)(FP),后面都是依次+8
{int}标明了数据类型,$8表明占据8个字节
004行:将参数值传给寄存器BX,MOVQ,传递四字
005行:将BX中的值传给返回值
006行:RET
看看float64又是啥样的:
package asm
func Asm(f float64)float64{
return f
}
--- prog list "Asm" ---
0000 (asm.go:3) TEXT Asm+0(SB),$0-16
0001 (asm.go:3) LOCALS ,$0
0002 (asm.go:3) TYPE i+0(FP){int},$8
0003 (asm.go:3) TYPE ~anon1+8(FP){float64},$8
0004 (asm.go:4) MOVQ i+0(FP),X0
0005 (asm.go:4) MOVQ X0,~anon1+8(FP)
0006 (asm.go:4) RET ,
可以看出与前面用int去尝试大致相同,只是BX寄存器变成了X0,可以推测X0就是浮点数寄存器,有X0,大胆推测会有X1、X2、X3…
试试吧
package asm
func Asm(f1,f2 float64) float64{
return f1+f2
}
--- prog list "Asm" ---
0000 (asm.go:3) TEXT Asm+0(SB),$0-24
0001 (asm.go:3) LOCALS ,$0
0002 (asm.go:3) TYPE f1+0(FP){float64},$8
0003 (asm.go:3) TYPE f2+8(FP){float64},$8
0004 (asm.go:3) TYPE ~anon2+16(FP){float64},$8
0005 (asm.go:4) MOVSD f1+0(FP),X0
0006 (asm.go:4) MOVSD f2+8(FP),X1
0007 (asm.go:4) ADDSD X1,X0
0008 (asm.go:4) MOVSD X0,~anon2+16(FP)
0009 (asm.go:4) RET ,
abs_amd64.s:
// Copyright 2010 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.
// func Abs(x float64) float64
TEXT ·Abs(SB),7,$0
MOVQ $(1<<63), BX
MOVQ BX, X0 // movsd $(-0.0), x0
MOVSD x+0(FP), X1
ANDNPD X1, X0
MOVSD X0, ret+8(FP)
RET
折腾一番终于到这了。
第一行就当固定格式吧,函数名替换下就好。
MOVQ $(1<<63), BX
MOVQ BX, X0
1右移动63位,传给X0,此时X0二进制表示是1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
MOVSD移动标量双精度浮点值,将参数x的值传给X1
ANDNPD压缩双精度浮点值逻辑位与非,将目标操作数的取反,再与源操作数执行逻辑位“与”操作,结果存储到目标操作数
即对X0取反,再与X1相与,最后结果存储到X0中
以上操作所完成的也就是将符号位置0,由此完成取绝对值的任务。