Go语言编译原理之变量捕获


Posted in Golang onAugust 05, 2022

前言

在前边的几篇文章中已经基本分享完了编译器前端的一些工作,后边的几篇主要是关于编译器对抽象语法树进行分析和重构,然后完成一系列的优化,其中包括以下五个部分:

  • 变量捕获
  • 函数内联
  • 逃逸分析
  • 闭包重写
  • 遍历函数

后边的五篇文章主要就是上边这五个主题,本文分享的是变量捕获,变量捕获主要是针对闭包场景的,因为闭包函数中可能引用闭包外的变量,因此变量捕获需要明确在闭包中通过值引用或地址引用的方式来捕获变量

变量捕获概述

下边通过一个示例来看一下什么是变量捕获

package main
import (
	"fmt"
)
func main() {
	a := 1
	b := 2
	go func() {
		//在闭包里对a或b进行了重新赋值,也会改变引用方式
		fmt.Println(a, b)
	}()
	a = 666
}

我们可以看到在闭包中引用了外部的变量a、b,由于变量a在闭包之后进行了其他赋值操作,因此在闭包中,a、b变量的引用方式会有所不同。在闭包中,必须采取地址引用的方式对变量a进行操作,而对变量b的引用将通过直接值传递的方式进行

我们可以通过如下方式查看当前程序闭包变量捕获的情况

go tool compile -m=2 main.go | grep capturing

Go语言编译原理之变量捕获

assign=true代表变量a在闭包完成后又进行了赋值操作

也可以看一个稍微复杂的

func adder() func(int) int {//累加器
	sum := 0 //地址引用
	return func(v int) int {
		sum += v
		return sum
	}
}
func main() {
	a := adder()
	for i:=0;i<10;i++{
		fmt.Printf("0 + 1 + ... + %d = %d\n", i, a(i))
	}
}

上一篇文章分享了类型检查,我们可以继续顺着编译的入口文件中类型检查后边的代码往下看,你会看到如下这段代码

编译入口文件:src/cmd/compile/main.go -> gc.Main(archInit)
// Phase 4: Decide how to capture closed variables.(决定如何捕获闭包变量)
// This needs to run before escape analysis,
// because variables captured by value do not escape.(变量捕获应该在逃逸分析之前进行,因为值类型的变量捕获,不会进行逃逸分析)
	timings.Start("fe", "capturevars")
	for _, n := range xtop {
		if n.Op == ODCLFUNC && n.Func.Closure != nil { //函数需要是闭包类型
			Curfn = n
			capturevars(n)
		}
	}
	capturevarscomplete = true

从上边这段代码及注释中,我们可以得到以下几个信息:

  • 变量捕获应该在逃逸分析之前进行,因为值类型的变量捕获,不会进行逃逸分析
  • 变量捕获是针对闭包函数的
  • 变量捕获的实现主要是调用了:src/cmd/compile/internal/gc/closure.go→capturevars

下边我们就去看capturevars方法的内部实现,了解变量捕获的一些细节

变量捕获底层实现

所有类型检查完成后,capturevars将在单独的阶段调用,它决定闭包捕获的每个变量是通过值还是通过引用捕获

func capturevars(xfunc *Node) {
	......
	clo := xfunc.Func.Closure
	cvars := xfunc.Func.Cvars.Slice()
	out := cvars[:0]
	for _, v := range cvars {
		......
		out = append(out, v)
		......
		outer := v.Name.Param.Outer
		outermost := v.Name.Defn
		// out parameters will be assigned to implicitly upon return.
		if outermost.Class() != PPARAMOUT && !outermost.Name.Addrtaken() && !outermost.Name.Assigned() && v.Type.Width <= 128 {
			v.Name.SetByval(true)
		} else {
			outermost.Name.SetAddrtaken(true)
			outer = nod(OADDR, outer, nil)
		}
		......
		outer = typecheck(outer, ctxExpr)
		clo.Func.Enter.Append(outer)
	}
	xfunc.Func.Cvars.Set(out)
	lineno = lno
}

该方法的代码量很少,大致内容就是,它会先获取到闭包函数内所有变量节点,然后对这些节点进行遍历。确定该闭包需要捕获的变量之后再没有被修改时,该变量小于128字节,则会认为他是值引用。后边它会对外部引用的结点进行类型检查

总结

本部分比较简单,但是挺实用的,特别是我这种一直搞不明包闭包引用外部变量的人。后边的逃逸分析、闭包重写跟变量捕获有一定的联系,介绍的后边内容的时候再提

以上就是Go语言编译原理之变量捕获的详细内容,更多关于Go编译原理变量捕获的资料请关注三水点靠木其它相关文章!

Golang 相关文章推荐
Golang二维切片初始化的实现
Apr 08 Golang
用golang如何替换某个文件中的字符串
Apr 25 Golang
golang 实现对Map进行键值自定义排序
Apr 28 Golang
Golang 空map和未初始化map的注意事项说明
Apr 29 Golang
goland设置颜色和字体的操作
May 05 Golang
Go标准容器之Ring的使用说明
May 05 Golang
Golang: 内建容器的用法
May 05 Golang
go语言使用Casbin实现角色的权限控制
Jun 26 Golang
Go 语言中 20 个占位符的整理
Oct 16 Golang
如何利用golang运用mysql数据库
Mar 13 Golang
Go并发4种方法简明讲解
Apr 06 Golang
Golang 并发编程 SingleFlight模式
Apr 26 Golang
在ubuntu下安装go开发环境的全过程
Aug 05 #Golang
Go语言测试库testify使用学习
Jul 23 #Golang
Go语言怎么使用变长参数函数
Jul 15 #Golang
Go微服务项目配置文件的定义和读取示例详解
Jun 21 #Golang
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
Jun 21 #Golang
Go 内联优化让程序员爱不释手
Jun 21 #Golang
GoFrame框架数据校验之校验结果Error接口对象
Jun 21 #Golang
You might like
php mysql索引问题
2008/06/07 PHP
PHP写UltraEdit插件脚本实现方法
2011/12/26 PHP
php字符编码转换之gb2312转为utf8
2013/10/28 PHP
javascript Discuz代码中的msn聊天小功能
2008/05/25 Javascript
javascript基础第一章 JavaScript与用户端
2010/07/22 Javascript
javascript代码加载优化方法
2011/01/30 Javascript
js中的前绑定和后绑定详解
2013/08/01 Javascript
Bootstrap编写一个在当前网页弹出可关闭的对话框 非弹窗
2016/06/30 Javascript
Angular2使用vscode断点调试ts文件的方法
2017/12/13 Javascript
使用Object.defineProperty如何巧妙找到修改某个变量的准确代码位置
2018/11/02 Javascript
使用taro开发微信小程序遇到的坑总结
2019/04/08 Javascript
Python中使用ConfigParser解析ini配置文件实例
2014/08/30 Python
Python实现包含min函数的栈
2016/04/29 Python
Python实现发送QQ邮件的封装
2017/07/14 Python
用不到50行的Python代码构建最小的区块链
2017/11/16 Python
python自动登录12306并自动点击验证码完成登录的实现源代码
2018/04/25 Python
Python3列表内置方法大全及示例代码小结
2019/05/10 Python
详解PANDAS 数据合并与重塑(join/merge篇)
2019/07/09 Python
centos7之Python3.74安装教程
2019/08/15 Python
python range实例用法分享
2020/02/06 Python
详解python变量与数据类型
2020/08/25 Python
详解java调用python的几种用法(看这篇就够了)
2020/12/10 Python
Python 实现RSA加解密文本文件
2020/12/30 Python
日本酒店、民宿、温泉旅馆、当地旅行团中文预订:e路东瀛
2019/12/09 全球购物
英国奢侈品在线精品店:Hervia
2020/09/03 全球购物
什么是类的返射机制
2016/02/06 面试题
结构和类有什么异同
2012/07/16 面试题
会计自我鉴定
2014/02/04 职场文书
会计专业自我评价
2014/02/12 职场文书
工业设计毕业生自荐信
2014/04/13 职场文书
民事二审代理词
2015/05/25 职场文书
党支部培养考察意见
2015/06/02 职场文书
Spring中bean的生命周期之getSingleton方法
2021/06/30 Java/Android
利用Matlab绘制各类特殊图形的实例代码
2021/07/16 Python
PHP遍历数组的6种方式总结
2021/11/17 PHP
MySQL中EXPLAIN语句及用法
2022/05/20 MySQL