Go | 1.17正式版本之初印象

开发 后端
8月17日凌晨,Go 1.17 正式发布!增加了slice对象直接强制类型转换为数组指针的能力。在unsafe中增加了Add函数。在unsafe中增加了Slice函数。

[[420250]]

8月17日凌晨,Go 1.17 正式发布!

迫不及待的阅读了版本说明:https://golang.google.cn/doc/go1.17。

语言变化

该版本主要包含三个小小的语法(糖)增强:

  1. 增加了slice对象直接强制类型转换为数组指针的能力。
  2. 在unsafe中增加了Add函数。
  3. 在unsafe中增加了Slice函数。

slice转数组指针

这是Go语言规范中新添加的内容:https://golang.google.cn/ref/spec#Conversions_from_slice_to_array_pointer。

直接上用例:

从上图代码可以看出,有了这个新的语法功能,类型转换确实方便了很多。

但是,如果转换的目标数组长度(len)大于slice的长度(len),编译虽然成功,可是运行时必定panic。

这是因为:Go编译器知道slice的长度是4,目标数组长度是5,这是数组越界访问,是错误的,于是将以下源代码:

  1. a5 := (*[5]int)(slice) 
  2. fmt.Println("a5 =", *a5) 

直接替换为以下runtime.panicSliceConvert函数调用,使进程异常退出。

这是Go语言中的一个很奇怪现象:即使在编译时期发现了代码异常,但是编译成功,把异常编码成运行时panic。

已经遇到过几次这种情况。

如果在 Go 1.17 版本之前实现slice转数组指针的功能,实现如下,稍微复杂一点:

Go 1.17版本完全兼容老版本的语法,该代码在Go 1.17运行是完全没有问题的。

只不过数组越界问题,需要开发者自己谨慎处理。

unsafe.Add

这是在unsafe/unsafe.go源码文件中新增加的一个内置函数,该函数没有函数体,是由Go编译器负责实现的。

其实现等同于以下代码:

  1. func Add(ptr Pointer, len IntegerType) Pointer { 
  2.     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编译器负责实现的。

该函数像是一个泛型函数。

  1. func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType { 
  2.   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 版本中,发现这项检查发现了变更:检查是通过R14寄存器与RSP寄存器比较实现的。

该检查由4条指令精简为2条指令,效率绝对提高许多,因为该检查几乎覆盖所有开发者实现的Go函数。

这是一项重大更新。

因为时间问题,尚未对其细节做进一步研究。

调用约定

在简单的调试过程中,发现Go 1.17版本的函数调用,返回值竟然使用的RAX寄存器,而且参数与使用了寄存器。

在Go 1.17之前的版本,所有开发者实现的Go函数,参数和返回值全部使用栈内存传递;只有少数汇编实现的函数、某些特殊函数、系统调用使用了寄存器传递参数和返回值。

而在该版本中,参数和返回值都使用了寄存器。似乎在向UNIX环境下的函数调用约定靠拢。

这是一项重大更新。

毕竟寄存器数量是有限的,具体使用哪些寄存器传递参数、返回值,哪些参数需要通过栈内存传递,需要找空闲时间探索一番。

该变更在版本说明的编译器部分有记录:https://golang.google.cn/doc/go1.17#compiler。

其他

可移植性方面,增加了新系统和处理器架构的支持。

在工具链方面,也有一些变更。

本文转载自微信公众号「Golang In Memory」

 

责任编辑:姜华 来源: Golang In Memory
相关推荐

2012-03-30 14:15:50

GoogleGo

2021-08-19 09:37:06

Go 1.17语言架构

2014-06-19 10:11:54

GoGo语言

2011-11-16 10:10:47

Go

2011-12-20 10:50:29

Firefox发布

2014-12-17 09:26:26

GoAndroid

2021-09-05 18:25:30

Go命令仓库

2012-10-10 16:27:39

StartOS 5.0Ylmf OS

2011-03-04 13:47:15

IE9

2020-06-04 18:17:44

PCIe 6.0带宽数据

2012-06-18 15:35:53

Win8 RTM版本号

2023-07-25 16:49:12

2013-04-26 17:24:40

Ubuntu

2009-04-09 17:09:32

LinuxVirtualBox

2019-09-04 09:26:42

谷歌Android开发者

2020-05-21 15:51:40

iOS 13.5GM升级

2021-08-19 09:43:09

Fedora 35LLVMGo 1.17

2011-12-07 10:26:11

2012-07-02 10:40:24

GroovyJavaJVM
点赞
收藏

51CTO技术栈公众号