go cmd/compile: 转义分析报告显示,在与常量大小内联后,make()正在使用非常量大小,

2w3rbyxf  于 6个月前  发布在  Go
关注(0)|答案(5)|浏览(64)

你使用的Go版本是什么(go version)?

$ go version
go version go1.16.6 linux/amd64

这个问题在最新版本中是否会重现?

你正在使用什么操作系统和处理器架构(go env)?

go env 输出

$ go env

你做了什么?

package main

// translate translates any byte in s between (lower, upper) to (newLower, newLower + upper - lower)
func translate(s string, cap int, lower, upper, newLower byte) string {
	// code removed
	b := append(make([]byte, 0, cap), s...)
	// code removed
	return string(b)
}

// toLower returns s with all upper case characters replaced with their lower case counterparts.
func toLower(s string, cap int) string {
	return translate(s, cap, 'A', 'Z', 'a')
}

// lookup ASCII case insensitive map lookup.
func lookup(m map[string]string, key string) (string, bool) {
	if len(key) > 32 {
		return "", false
	}
	r, ok := m[toLower(key, 32)]
	return r, ok
}

你期望看到什么?

translate()内联到toLower(),结果toLower()也会内联到它的调用者。因此,在lookup()中,当toLower()被内联时,期望translate()创建的切片容量是常量,因此可以在栈上预留。

你看到了什么?

make([]byte, 0, cap) 在lookup()中转储到堆(非常量大小)。如果在translate()中使用固定容量,如make([]byte, 0, 32),则切片始终可以在栈上预留。

0lvr5msh

0lvr5msh1#

我认为这只是逃逸分析中常传播的限制。内联引入了临时变量,我们不会通过这些临时变量传播。

voase2hg

voase2hg2#

出于好奇,为什么在这种情况下传播常量不会发生?

92vpleto

92vpleto3#

没有人实现它。
在编译器的这个阶段有点棘手,因为你必须证明一个变量的值永远不会改变。这包括查找该变量的其他赋值,包括通过指针。

ezykj2lf

ezykj2lf4#

我的理解可能有些偏差,但持续传播应该能解决这个问题。也就是说,它不应该接触指针或其他类似的东西。在内联后,它应该能在传播32到cap的过程中完成它的工作。当然,如果所有的临时变量都实现为指针,那么我可以看到这个问题。

kq4fsx7k

kq4fsx7k5#

在完美实现的情况下,不需要cap参数。

在lookup()中的len()检查之后,编译器应该知道key的长度可能范围是(0, 32)。那么,就不需要使用显式的固定容量,可以使用make([]byte, 0, len(s)),编译器会确定上限足够小,总是可以栈分配。

通过边界检查消除将较小的非逃逸分配移动到栈上。

相关问题