ch3: 规范化图像名字
@ -65,7 +65,7 @@
|
||||
|
||||
下图是某一层的任务:将输入数据的0剔除,非0的数据依次输出,右边部分是解决方案。
|
||||
|
||||

|
||||

|
||||
|
||||
整个程序只有一个输入指令、一个输出指令和两个跳转指令共四个指令:
|
||||
|
||||
@ -86,7 +86,7 @@ X86其实是是80X86的简称(后面三个字母),包括Intel 8086、80286
|
||||
|
||||
在使用汇编语言之前必须要了解对应的CPU体系结构。下面是X86/AMD架构图:
|
||||
|
||||

|
||||

|
||||
|
||||
左边是内存部分是常见的内存布局。其中text一般对应代码段,用于存储要执行指令数据,代码段一般是只读的。然后是rodata和data数据段,数据段一般用于存放全局的数据,其中rodata是只读的数据段。而heap段则用于管理动态的数据,stack段用于管理每个函数调用时相关的数据。在汇编语言中一般重点关注text代码段和data数据段,因此Go汇编语言中专门提供了对应TEXT和DATA命令用于定义代码和数据。
|
||||
|
||||
@ -101,7 +101,7 @@ Go汇编为了简化汇编代码的编写,引入了PC、FP、SP、SB四个伪
|
||||
|
||||
四个伪寄存器和X86/AMD64的内存和寄存器的相互关系如下图:
|
||||
|
||||

|
||||

|
||||
|
||||
在AMD64环境,伪PC寄存器其实是IP指令计数器寄存器的别名。伪FP寄存器对应的是函数的帧指针,一般用来访问函数的参数和返回值。伪SP栈指针对应的是当前函数栈帧的底部(不包括参数和返回值部分),一般用于定位局部变量。伪SP是一个比较特殊的寄存器,因为还存在一个同名的SP真寄存器。真SP寄存器对应的是栈的顶部,一般用于定位调用其它函数的参数和返回值。
|
||||
|
||||
|
@ -110,7 +110,7 @@ DATA ·num+8(SB)/8,$0
|
||||
|
||||
下图是Go语句和汇编语句定义变量时的对应关系:
|
||||
|
||||

|
||||

|
||||
|
||||
汇编代码中并不需要NOPTR标志,因为Go编译器会从Go语言语句声明的`[2]int`类型中推导出该变量内部没有指针数据。
|
||||
|
||||
@ -171,7 +171,7 @@ Go汇编语言通常无法区分变量是否是浮点数类型,与之相关的
|
||||
|
||||
IEEE754标准中,最高位1bit为符号位,然后是指数位(指数为采用移码格式表示),然后是有效数部分(其中小数点左边的一个bit位被省略)。下图是IEEE754中float32类型浮点数的bit布局:
|
||||
|
||||

|
||||

|
||||
|
||||
IEEE754浮点数还有一些奇妙的特性:比如有正负两个0;除了无穷大和无穷小Inf还有非数NaN;同时如果两个浮点数有序那么对应的有符号整数也是有序的(反之则不一定成立,因为浮点数中存在的非数是不可排序的)。浮点数是程序中最难琢磨的角落,因为程序中很多手写的浮点数字面值常量根本无法精确表达,浮点数计算涉及到的误差舍入方式可能也的随机的。
|
||||
|
||||
@ -300,13 +300,13 @@ func makechan(chanType *byte, size int) (hchan chan any)
|
||||
|
||||
首先查看前面已经见过的`[2]int`类型数组的内存布局:
|
||||
|
||||

|
||||

|
||||
|
||||
变量在data段分配空间,数组的元素地址依次从低向高排列。
|
||||
|
||||
然后再查看下标准库图像包中`image.Point`结构体类型变量的内存布局:
|
||||
|
||||

|
||||

|
||||
|
||||
变量也时在data段分配空间,变量结构体成员的地址也是依次从低向高排列。
|
||||
|
||||
|
@ -38,7 +38,7 @@ TEXT ·Swap(SB), NOSPLIT, $0
|
||||
|
||||
下图是Swap函数几种不同写法的对比关系图:
|
||||
|
||||

|
||||

|
||||
|
||||
第一种是最完整的写法:函数名部分包含了当前包的路径,同时指明了函数的参数大小为32个字节(对应参数和返回值的4个int类型)。第二种写法则比较简洁,省略了当前包的路径和参数的大小。如果有NOSPLIT标注,会禁止汇编器为汇编函数插入栈分裂的代码。NOSPLIT对应Go语言中的`//go:nosplit`注释。
|
||||
|
||||
@ -79,7 +79,7 @@ TEXT ·Swap(SB), $0-32
|
||||
|
||||
下图是Swap函数中参数和返回值在内存中的布局图:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
下面的代码演示了如何在汇编函数中使用参数和返回值:
|
||||
@ -136,7 +136,7 @@ func Foo(FP *SomeFunc_args_and_returns) {
|
||||
|
||||
Foo函数的参数和返回值的大小和内存布局:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
下面的代码演示了Foo汇编函数参数和返回值的定位:
|
||||
@ -208,7 +208,7 @@ func Foo() {
|
||||
|
||||
下面是Foo函数的局部变量的大小和内存布局:
|
||||
|
||||

|
||||

|
||||
|
||||
从图中可以看出Foo函数局部变量和前一个例子中参数和返回值的内存布局是完全一样的,这也是我们故意设计的结果。但是参数和返回值是通过伪FP寄存器定位的,FP寄存器对应第一个参数的开始地址(第一个参数地址较低),因此每个变量的偏移量是正数。而局部变量是通过伪SP寄存器定位的,而伪SP寄存器对应的是第一个局部变量的结束地址(第一个局部变量地址较大),因此每个局部变量的便宜量都是负数。
|
||||
|
||||
@ -239,7 +239,7 @@ func sum(a, b int) int {
|
||||
|
||||
下图展示了三个函数逐级调用时内存中函数参数和返回值的布局:
|
||||
|
||||

|
||||

|
||||
|
||||
为了便于理解,我们对真实的内存布局进行了简化。要记住的是调用函数时,被调用函数的参数和返回值内存空间都必须由调用者提供。因此函数的局部变量和为调用其它函数准备的栈空间总和就确定了函数帧的大小。调用其它函数前调用方要选择保存相关寄存器到栈中,并在调用函数返回后选择要恢复的寄存器进行保存。最终通过CALL指令调用函数的过程和调用我们熟悉的调用println函数输出的过程类似。
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
和C语言函数不同,Go语言函数的参数和返回值完全通过栈传递。下面是Go函数调用时栈的布局图:
|
||||
|
||||

|
||||

|
||||
|
||||
首先是调用函数前准备的输入参数和返回值空间。然后CALL指令将首先触发返回地址入栈操作。在进入到被调用函数内之后,汇编器自动插入了BP寄存器相关的指令,因此BP寄存器和返回地址是紧挨着的。再下面就是当前函数的局部变量的空间,包含再次调用其它函数需要准备的调用参数空间。被调用的函数执行RET返回指令时,先从栈恢复BP和SP寄存器,接着取出的返回地址跳转到对应的指令执行。
|
||||
|
||||
|
@ -1,88 +0,0 @@
|
||||
' Copyright 2017 <chaishushan{AT}gmail.com>. All rights reserved.
|
||||
' Use of this source code is governed by a Apache
|
||||
' license that can be found in the LICENSE file.
|
||||
|
||||
@startuml
|
||||
|
||||
title qsort
|
||||
|
||||
participant go
|
||||
participant c
|
||||
|
||||
[--> go: qsort
|
||||
activate go
|
||||
|
||||
go -> go
|
||||
activate go #DarkSalmon
|
||||
note left
|
||||
go_qsort_compare_info.elemsize = sv.Type().Elem().Size()
|
||||
go_qsort_compare_info.fn = fn
|
||||
end note
|
||||
|
||||
deactivate go
|
||||
|
||||
go -> c: C.qsort_proxy(cmp=go_qsort_compare)
|
||||
activate c
|
||||
note right
|
||||
void qsort_proxy(
|
||||
void* base, size_t num, size_t size,
|
||||
int (*compare)(const void* a, const void* b)
|
||||
) {
|
||||
go_qsort_compare_save_base(base);
|
||||
qsort(base, num, size, compare);
|
||||
}
|
||||
end note
|
||||
|
||||
' begin
|
||||
c -> c: C.go_qsort_compare_save_base
|
||||
activate c #DarkSalmon
|
||||
note right: callback go func
|
||||
|
||||
go <- c: go_qsort_compare_save_base
|
||||
activate go #DarkSalmon
|
||||
note left
|
||||
//export go_qsort_compare_save_base
|
||||
func go_qsort_compare_save_base(base unsafe.Pointer) {
|
||||
go_qsort_compare_info.base = uintptr(base)
|
||||
}
|
||||
var go_qsort_compare_info struct {
|
||||
base uintptr
|
||||
elemsize uintptr
|
||||
fn func(a, b int) int
|
||||
sync.RWMutex
|
||||
}
|
||||
end note
|
||||
|
||||
go -> c: go_qsort_compare_save_base done
|
||||
deactivate go
|
||||
|
||||
deactivate c
|
||||
' end
|
||||
|
||||
loop
|
||||
c -> c: C.go_qsort_compare
|
||||
activate c #DarkSalmon
|
||||
note right: callback go func
|
||||
|
||||
c -> go: go_qsort_compare
|
||||
activate go #DarkSalmon
|
||||
note left
|
||||
//export go_qsort_compare_save_base
|
||||
func go_qsort_compare_save_base(base unsafe.Pointer) {
|
||||
go_qsort_compare_info.base = uintptr(base)
|
||||
}
|
||||
end note
|
||||
|
||||
go -> c: go_qsort_compare done
|
||||
deactivate go
|
||||
|
||||
deactivate c
|
||||
end
|
||||
|
||||
go <- c: C.qsort_proxy done
|
||||
deactivate c
|
||||
|
||||
[<-- go: done
|
||||
deactivate go
|
||||
|
||||
@enduml
|
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |