Go | 1.17正式版本之初印象
8月17日凌晨,Go 1.17 正式發(fā)布!
迫不及待的閱讀了版本說明:https://golang.google.cn/doc/go1.17。
語言變化
該版本主要包含三個小小的語法(糖)增強:
- 增加了slice對象直接強制類型轉換為數組指針的能力。
- 在unsafe中增加了Add函數。
- 在unsafe中增加了Slice函數。
slice轉數組指針
這是Go語言規(guī)范中新添加的內容:https://golang.google.cn/ref/spec#Conversions_from_slice_to_array_pointer。
直接上用例:
從上圖代碼可以看出,有了這個新的語法功能,類型轉換確實方便了很多。
但是,如果轉換的目標數組長度(len)大于slice的長度(len),編譯雖然成功,可是運行時必定panic。
這是因為:Go編譯器知道slice的長度是4,目標數組長度是5,這是數組越界訪問,是錯誤的,于是將以下源代碼:
- a5 := (*[5]int)(slice)
- fmt.Println("a5 =", *a5)
直接替換為以下runtime.panicSliceConvert函數調用,使進程異常退出。
這是Go語言中的一個很奇怪現象:即使在編譯時期發(fā)現了代碼異常,但是編譯成功,把異常編碼成運行時panic。
已經遇到過幾次這種情況。
如果在 Go 1.17 版本之前實現slice轉數組指針的功能,實現如下,稍微復雜一點:
Go 1.17版本完全兼容老版本的語法,該代碼在Go 1.17運行是完全沒有問題的。
只不過數組越界問題,需要開發(fā)者自己謹慎處理。
unsafe.Add
這是在unsafe/unsafe.go源碼文件中新增加的一個內置函數,該函數沒有函數體,是由Go編譯器負責實現的。
其實現等同于以下代碼:
- func Add(ptr Pointer, len IntegerType) Pointer {
- return Pointer(uintptr(ptr) + uintptr(len))
- }
相關源碼鏈接:
- https://github.com/golang/go/blob/go1.17/src/unsafe/unsafe.go#L217
- https://github.com/golang/go/blob/go1.17/src/go/types/builtins.go#L589
unsafe.Slice
這是在unsafe/unsafe.go源碼文件中新增加的一個內置函數,該函數沒有函數體,是由Go編譯器負責實現的。
該函數像是一個泛型函數。
- func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType {
- return (*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
- }
相關源碼鏈接:
- https://github.com/golang/go/blob/go1.17/src/unsafe/unsafe.go#L233
- https://github.com/golang/go/blob/go1.17/src/go/types/builtins.go#L690
調用棧邊界檢查
如果沒有特殊標記,Go編譯器會在函數的入口處自動添加檢查棧是否需要擴增的指令。
在 Go 1.17 之前的版本中,檢查是通過FS寄存器讀取線程本地存儲(TLS)中的棧保護標記(runtime.g.stackguard0)與RSP寄存器比較實現的。
在 Go 1.17 版本中,發(fā)現這項檢查發(fā)現了變更:檢查是通過R14寄存器與RSP寄存器比較實現的。
該檢查由4條指令精簡為2條指令,效率絕對提高許多,因為該檢查幾乎覆蓋所有開發(fā)者實現的Go函數。
這是一項重大更新。
因為時間問題,尚未對其細節(jié)做進一步研究。
調用約定
在簡單的調試過程中,發(fā)現Go 1.17版本的函數調用,返回值竟然使用的RAX寄存器,而且參數與使用了寄存器。
在Go 1.17之前的版本,所有開發(fā)者實現的Go函數,參數和返回值全部使用棧內存?zhèn)鬟f;只有少數匯編實現的函數、某些特殊函數、系統(tǒng)調用使用了寄存器傳遞參數和返回值。
而在該版本中,參數和返回值都使用了寄存器。似乎在向UNIX環(huán)境下的函數調用約定靠攏。
這是一項重大更新。
畢竟寄存器數量是有限的,具體使用哪些寄存器傳遞參數、返回值,哪些參數需要通過棧內存?zhèn)鬟f,需要找空閑時間探索一番。
該變更在版本說明的編譯器部分有記錄:https://golang.google.cn/doc/go1.17#compiler。
其他
可移植性方面,增加了新系統(tǒng)和處理器架構的支持。
在工具鏈方面,也有一些變更。
本文轉載自微信公眾號「Golang In Memory」