go cmd/compile: 可能存在潜在的代码生成问题,在amd64上移除零扩展,

3zwjbxry  于 6个月前  发布在  Go
关注(0)|答案(7)|浏览(53)

我不确定现在是否有可能触发这个bug,但我认为可能存在一个问题。
考虑如下代码:

func f(x uint32) uint64 {
	return uint64(x & 0xFFFFFFFF)
}

最外层操作是 ZeroExt32to64 ,它被降低为 MOVLQZX 。最内层操作最终是 (ANDLconst [0xFFFFFFFF] x)
然后这条规则触发: (MOVLQZX x) && zeroUpper32Bits(x,3) -> xANDLconst 是在列表中被列为清零寄存器上32位的操作之一,所以我们消除了零扩展。
然后这条规则触发: (ANDLconst [c] x) && int32(c)==-1 -> x ,消除了 ANDLconst
这使我们只剩下 x ,但没有任何零扩展,这可能会在寄存器的上半部分留下垃圾。
就目前而言,这种情况不会发生在这个函数中,因为 And32 在通用优化过程中被消除。但是有办法让 & -1 通过通用优化,例如使用常数移位,这就是我发现这个问题的方式。在这种情况下,我们也得益于使用 MOVL 从参数加载值。但我们也可以使用计算值代替。所以我还没有说服自己,认为对于正确的输入,这实际上不会导致不良的代码生成。
有两种可能的诊断/修复方法:

  • zeroUpper32Bits 假设内部操作实际上会被执行,这是错误的,在这种情况下,我们需要减少可接受的操作列表,可能只保留加载操作,我认为在那一点上无法消除(?),因为 dse 发生得更早
  • ANDLconst 规则是不可靠的,因为消除它会消除清零寄存器上半部分的副作用

我强烈倾向于第一个诊断和修复方法。你有什么想法吗?

watbbzwu

watbbzwu1#

我从未对我们的整个符号扩展故事感到非常满意。在与机器无关的空间中,我认为我们做得很好,但在与机器相关的空间中,我们玩得非常随意。也许我们可以找到一些不变性,以便保证只使用值中存在的位数。因此,将有两个有效的 ANDLconst ,一个具有32位类型,另一个具有64位类型。前者可以删除 ANDLconst [-1] ,但后者不行。

gv8xihay

gv8xihay2#

同意。同时,我们也未能移除一堆多余的扩展名
这种方法听起来不错,假设细节工作得当。它可能会导致操作和规则数量的爆炸(这已经是一个问题),并且在转换时可能会很棘手。
我不认为我想加入解决这个问题的行列。:)

kqqjbcuj

kqqjbcuj3#

对我来说也是这样。我也不太高兴看到扩展被移除。
(ANDLconst [c] x) && int32(c)==-1 -> x
我认为我们应该只在类型为32位宽或更小时这样做。但是我们如何保持类型的准确性呢?
(MOVLQZX x) && zeroUpper32Bits(x,3) -> x
LHS是64位宽,而RHS是32位宽。这种规则一直让我感到困扰。也许我们可以有一种保留类型的方法来做这件事?比如
(op x:(op2 ...)) -> (op2 <t> ...) ?
(这基本上是在新类型中复制x(不是OpCopy,而是复制x的表达式)。)

puruo6ea

puruo6ea4#

https://golang.org/cl/220499提到了这个问题:cmd/compile: mark Lsyms as readonly earlier

55ooxyrt

55ooxyrt5#

https://golang.org/cl/226957提到了这个问题:internal/lsp/regtest: dump gopls logs on test failure

yxyvkwin

yxyvkwin6#

我猜我们1.15版本没有做任何事情。现在转到1.16里程碑。

5anewei6

5anewei67#

由于目前没有计划处理此问题,将其状态更改为"未计划"。

相关问题