[go] cmd/compile: elide redundant sub-word extensions on arm64

11 views
Skip to first unread message

Frank Kuehnel (Gerrit)

unread,
Aug 27, 2026, 8:13:44 PMAug 27
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Frank Kuehnel has uploaded the change for review

Commit message

cmd/compile: elide redundant sub-word extensions on arm64

On arm64 a MOVBload or MOVHload already produces a correctly sign- or
zero-extended value, so an extension applied to its result is redundant.
The "don't extend after proper load" rules in ARM64latelower.rules
recognise this, but rewrite the extension to MOVDreg, which is a real
register move. MOVDreg only becomes free via

(MOVDreg x) && x.Uses == 1 => (MOVDnop x)

that is, when the loaded value has exactly one use.

A loop that both compares and stores the loaded value gives it two uses,
so the demotion does not fire and a register-to-register MOVD is emitted
every iteration:

MOVB (R0), R3
MOVB R3, (R2)
MOVD R3, R2 // redundant
CMPW R1, R2

32- and 64-bit element types are unaffected, because a 32- or 64-bit
compare consumes the load's result directly and no extension node is ever
created.

Drop the extension node outright rather than turning it into a move. The
same file already does this for other redundant extensions, for instance
(MOVWUreg x) && ZeroUpper32Bits(x, 3) => x, and it is the direction the
existing "TODO: we should be able to get rid of MOVDnop all together"
points at.

Removing the use-count gate instead does not work: MOVDnop is
resultInArg0, so with two live uses the register allocator materialises
the copy anyway and the move survives, merely relocated.

Measured on a c4a-standard-8 (Google Axion, Neoverse-V2), pinned to an
isolated core, GOMAXPROCS=1, 12 rounds after a discarded warmup, with an
A/A control that showed no significant difference. Sorting 100k elements
with a branchless Hoare-Lomuto partition:

int8 -5.7%
int16 -3.5%
int32 ~
int64 +0.1%

An earlier run patching only the 8-bit rules left int16 at -0.1%, which
confirms the int16 result comes from the MOVHreg rules rather than drift.

slices.Sort does not speed up at int8 (+0.1%) even though its partition
loses the same two instructions -- 178 to 175, with reg-to-reg moves
going 5 to 3. That loop is a branchy Hoare scan dominated by branch
mispredictions, so two instructions are not visible in it. A branchless
loop's instruction count is its cost, which is where the gain above comes
from.

Runs of slices.Sort at int32 and int64 moved -2.6% and -2.3%, but
partitionOrdered generates identical code for those shapes with and
without this change, so those deltas are not attributable to it; most
likely code layout.
Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b

Change diff

diff --git a/src/cmd/compile/internal/ssa/_gen/ARM64latelower.rules b/src/cmd/compile/internal/ssa/_gen/ARM64latelower.rules
index d7d2eeb..644d0408 100644
--- a/src/cmd/compile/internal/ssa/_gen/ARM64latelower.rules
+++ b/src/cmd/compile/internal/ssa/_gen/ARM64latelower.rules
@@ -34,43 +34,43 @@
(MOVWUreg x) && ssa.ZeroUpper32Bits(x) => x

// don't extend after proper load
-(MOVBreg x:(MOVBload _ _)) => (MOVDreg x)
-(MOVBUreg x:(MOVBUload _ _)) => (MOVDreg x)
-(MOVHreg x:(MOVBload _ _)) => (MOVDreg x)
-(MOVHreg x:(MOVBUload _ _)) => (MOVDreg x)
-(MOVHreg x:(MOVHload _ _)) => (MOVDreg x)
-(MOVHUreg x:(MOVBUload _ _)) => (MOVDreg x)
-(MOVHUreg x:(MOVHUload _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVBload _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVBUload _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVHload _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVHUload _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVWload _ _)) => (MOVDreg x)
-(MOVWUreg x:(MOVBUload _ _)) => (MOVDreg x)
-(MOVWUreg x:(MOVHUload _ _)) => (MOVDreg x)
-(MOVWUreg x:(MOVWUload _ _)) => (MOVDreg x)
-(MOVBreg x:(MOVBloadidx _ _ _)) => (MOVDreg x)
-(MOVBUreg x:(MOVBUloadidx _ _ _)) => (MOVDreg x)
-(MOVHreg x:(MOVBloadidx _ _ _)) => (MOVDreg x)
-(MOVHreg x:(MOVBUloadidx _ _ _)) => (MOVDreg x)
-(MOVHreg x:(MOVHloadidx _ _ _)) => (MOVDreg x)
-(MOVHUreg x:(MOVBUloadidx _ _ _)) => (MOVDreg x)
-(MOVHUreg x:(MOVHUloadidx _ _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVBloadidx _ _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVBUloadidx _ _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVHloadidx _ _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVHUloadidx _ _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVWloadidx _ _ _)) => (MOVDreg x)
-(MOVWUreg x:(MOVBUloadidx _ _ _)) => (MOVDreg x)
-(MOVWUreg x:(MOVHUloadidx _ _ _)) => (MOVDreg x)
-(MOVWUreg x:(MOVWUloadidx _ _ _)) => (MOVDreg x)
-(MOVHreg x:(MOVHloadidx2 _ _ _)) => (MOVDreg x)
-(MOVHUreg x:(MOVHUloadidx2 _ _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVHloadidx2 _ _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVHUloadidx2 _ _ _)) => (MOVDreg x)
-(MOVWreg x:(MOVWloadidx4 _ _ _)) => (MOVDreg x)
-(MOVWUreg x:(MOVHUloadidx2 _ _ _)) => (MOVDreg x)
-(MOVWUreg x:(MOVWUloadidx4 _ _ _)) => (MOVDreg x)
+(MOVBreg x:(MOVBload _ _)) => x
+(MOVBUreg x:(MOVBUload _ _)) => x
+(MOVHreg x:(MOVBload _ _)) => x
+(MOVHreg x:(MOVBUload _ _)) => x
+(MOVHreg x:(MOVHload _ _)) => x
+(MOVHUreg x:(MOVBUload _ _)) => x
+(MOVHUreg x:(MOVHUload _ _)) => x
+(MOVWreg x:(MOVBload _ _)) => x
+(MOVWreg x:(MOVBUload _ _)) => x
+(MOVWreg x:(MOVHload _ _)) => x
+(MOVWreg x:(MOVHUload _ _)) => x
+(MOVWreg x:(MOVWload _ _)) => x
+(MOVWUreg x:(MOVBUload _ _)) => x
+(MOVWUreg x:(MOVHUload _ _)) => x
+(MOVWUreg x:(MOVWUload _ _)) => x
+(MOVBreg x:(MOVBloadidx _ _ _)) => x
+(MOVBUreg x:(MOVBUloadidx _ _ _)) => x
+(MOVHreg x:(MOVBloadidx _ _ _)) => x
+(MOVHreg x:(MOVBUloadidx _ _ _)) => x
+(MOVHreg x:(MOVHloadidx _ _ _)) => x
+(MOVHUreg x:(MOVBUloadidx _ _ _)) => x
+(MOVHUreg x:(MOVHUloadidx _ _ _)) => x
+(MOVWreg x:(MOVBloadidx _ _ _)) => x
+(MOVWreg x:(MOVBUloadidx _ _ _)) => x
+(MOVWreg x:(MOVHloadidx _ _ _)) => x
+(MOVWreg x:(MOVHUloadidx _ _ _)) => x
+(MOVWreg x:(MOVWloadidx _ _ _)) => x
+(MOVWUreg x:(MOVBUloadidx _ _ _)) => x
+(MOVWUreg x:(MOVHUloadidx _ _ _)) => x
+(MOVWUreg x:(MOVWUloadidx _ _ _)) => x
+(MOVHreg x:(MOVHloadidx2 _ _ _)) => x
+(MOVHUreg x:(MOVHUloadidx2 _ _ _)) => x
+(MOVWreg x:(MOVHloadidx2 _ _ _)) => x
+(MOVWreg x:(MOVHUloadidx2 _ _ _)) => x
+(MOVWreg x:(MOVWloadidx4 _ _ _)) => x
+(MOVWUreg x:(MOVHUloadidx2 _ _ _)) => x
+(MOVWUreg x:(MOVWUloadidx4 _ _ _)) => x

// fold double extensions
(MOVBreg x:(MOVBreg _)) => (MOVDreg x)
diff --git a/src/cmd/compile/internal/ssarewrite/rewritearm64latelower/rewriteARM64latelower.go b/src/cmd/compile/internal/ssarewrite/rewritearm64latelower/rewriteARM64latelower.go
index 4d53475..fef8ef4 100644
--- a/src/cmd/compile/internal/ssarewrite/rewritearm64latelower/rewriteARM64latelower.go
+++ b/src/cmd/compile/internal/ssarewrite/rewritearm64latelower/rewriteARM64latelower.go
@@ -355,25 +355,23 @@
return true
}
// match: (MOVBUreg x:(MOVBUload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVBUreg x:(MOVBUloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVBUreg x:(MOVBUreg _))
@@ -392,25 +390,23 @@
func rewriteValue_OpARM64MOVBreg(v *ssa.Value) bool {
v_0 := v.Args[0]
// match: (MOVBreg x:(MOVBload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVBreg x:(MOVBloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVBreg x:(MOVBreg _))
@@ -483,58 +479,53 @@
return true
}
// match: (MOVHUreg x:(MOVBUload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHUreg x:(MOVHUload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHUload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHUreg x:(MOVBUloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHUreg x:(MOVHUloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHUloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHUreg x:(MOVHUloadidx2 _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHUloadidx2 {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHUreg x:(MOVBUreg _))
@@ -564,80 +555,73 @@
func rewriteValue_OpARM64MOVHreg(v *ssa.Value) bool {
v_0 := v.Args[0]
// match: (MOVHreg x:(MOVBload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHreg x:(MOVBUload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHreg x:(MOVHload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHreg x:(MOVBloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHreg x:(MOVBUloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHreg x:(MOVHloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHreg x:(MOVHloadidx2 _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHloadidx2 {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVHreg x:(MOVBreg _))
@@ -689,91 +673,83 @@
return true
}
// match: (MOVWUreg x:(MOVBUload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWUreg x:(MOVHUload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHUload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWUreg x:(MOVWUload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVWUload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWUreg x:(MOVBUloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWUreg x:(MOVHUloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHUloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWUreg x:(MOVWUloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVWUloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWUreg x:(MOVHUloadidx2 _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHUloadidx2 {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWUreg x:(MOVWUloadidx4 _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVWUloadidx4 {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWUreg x:(MOVBUreg _))
@@ -814,146 +790,133 @@
func rewriteValue_OpARM64MOVWreg(v *ssa.Value) bool {
v_0 := v.Args[0]
// match: (MOVWreg x:(MOVBload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVBUload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVHload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVHUload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHUload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVWload _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVWload {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVBloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVBUloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVBUloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVHloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVHUloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHUloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVWloadidx _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVWloadidx {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVHloadidx2 _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHloadidx2 {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVHUloadidx2 _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVHUloadidx2 {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVWloadidx4 _ _ _))
- // result: (MOVDreg x)
+ // result: x
for {
x := v_0
if x.Op != ssaop.OpARM64MOVWloadidx4 {
break
}
- v.Reset(ssaop.OpARM64MOVDreg)
- v.AddArg(x)
+ v.CopyOf(x)
return true
}
// match: (MOVWreg x:(MOVBreg _))
diff --git a/test/codegen/noextend.go b/test/codegen/noextend.go
index e2c1982..7033a44 100644
--- a/test/codegen/noextend.go
+++ b/test/codegen/noextend.go
@@ -922,3 +922,36 @@
// arm64/v8.1:"SWPALW" -"MOVWU R[0-9]+, R[0-9]+"
return uint64(p.Swap(n))
}
+
+// A sub-word value that is both compared and stored has two uses, so the
+// MOVDreg -> MOVDnop demotion in ARM64latelower.rules, which is gated on
+// x.Uses == 1, does not fire. The extension after the load must still be
+// elided rather than turned into a register-to-register move.
+
+func noExtTwoUsesMOVBload(p *int8, pivot int8, d *int8) bool {
+ x := *p
+ *d = x
+ // arm64:-`MOVD\tR[0-9]+, R[0-9]+`
+ return pivot <= x
+}
+
+func noExtTwoUsesMOVHload(p *int16, pivot int16, d *int16) bool {
+ x := *p
+ *d = x
+ // arm64:-`MOVD\tR[0-9]+, R[0-9]+`
+ return pivot <= x
+}
+
+func noExtTwoUsesMOVBUload(p *uint8, pivot uint8, d *uint8) bool {
+ x := *p
+ *d = x
+ // arm64:-`MOVD\tR[0-9]+, R[0-9]+`
+ return pivot <= x
+}
+
+func noExtTwoUsesMOVHUload(p *uint16, pivot uint16, d *uint16) bool {
+ x := *p
+ *d = x
+ // arm64:-`MOVD\tR[0-9]+, R[0-9]+`
+ return pivot <= x
+}

Change information

Files:
  • M src/cmd/compile/internal/ssa/_gen/ARM64latelower.rules
  • M src/cmd/compile/internal/ssarewrite/rewritearm64latelower/rewriteARM64latelower.go
  • M test/codegen/noextend.go
Change size: L
Delta: 3 files changed, 144 insertions(+), 148 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
Gerrit-Change-Number: 823424
Gerrit-PatchSet: 1
Gerrit-Owner: Frank Kuehnel <kueh...@gmail.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Keith Randall (Gerrit)

unread,
Aug 28, 2026, 5:58:01 PMAug 28
to Frank Kuehnel, goph...@pubsubhelper.golang.org, Robert Griesemer, Keith Randall, Martin Möhrmann, Gopher Robot, golang-co...@googlegroups.com
Attention needed from Frank Kuehnel, Martin Möhrmann and Robert Griesemer

Keith Randall added 1 comment

Patchset-level comments
File-level comment, Patchset 1 (Latest):
Keith Randall . unresolved

Please take a look at the description in https://go-review.googlesource.com/c/go/+/568616

I think before we futz with this code some more, we really need to establish invariants on what a spill/restore pair does. I do worry generally about the fact that we re-extend using the type when a value is spilled/restored, and that re-extension might not match the outer extension op. I can't see any obvious way to make a bug using that mismatch, but I'd be much happier with:

```
(MOVWreg x:(MOVWload ...)) && x.Type.IsSigned() => x
```
because that test is exactly what cmd/compile/internal/arm64/ssa.go:loadByType uses to decide how to extend on spill+restore.

Open in Gerrit

Related details

Attention is currently required from:
  • Frank Kuehnel
  • Martin Möhrmann
  • Robert Griesemer
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 1
    Gerrit-Owner: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Reviewer: Keith Randall <k...@golang.org>
    Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
    Gerrit-Attention: Robert Griesemer <g...@golang.org>
    Gerrit-Attention: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Comment-Date: Fri, 28 Aug 2026 21:57:55 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Frank Kuehnel (Gerrit)

    unread,
    Aug 29, 2026, 2:39:41 PMAug 29
    to goph...@pubsubhelper.golang.org, Robert Griesemer, Keith Randall, Martin Möhrmann, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from Keith Randall, Martin Möhrmann and Robert Griesemer

    Frank Kuehnel added 1 comment

    Patchset-level comments
    Keith Randall . unresolved

    Please take a look at the description in https://go-review.googlesource.com/c/go/+/568616

    I think before we futz with this code some more, we really need to establish invariants on what a spill/restore pair does. I do worry generally about the fact that we re-extend using the type when a value is spilled/restored, and that re-extension might not match the outer extension op. I can't see any obvious way to make a bug using that mismatch, but I'd be much happier with:

    ```
    (MOVWreg x:(MOVWload ...)) && x.Type.IsSigned() => x
    ```
    because that test is exactly what cmd/compile/internal/arm64/ssa.go:loadByType uses to decide how to extend on spill+restore.

    Frank Kuehnel

    Read 568616. The paragraph that matters is the last one -- spill/restore does not
    preserve the high bits, it re-derives them, and you left the question open. I
    think it is answerable, and that the answer is close to what you already landed
    for the other direction. Below is the invariant I believe we need, what this CL
    does and does not change about it, and what I propose for the next patch set.

    ### The spill/restore contract

    On arm64 (cmd/compile/internal/arm64/ssa.go):

    - a spill writes storeByType(x.Type), i.e. the low x.Type.Size() bytes of the
    register;
    - a restore rewrites the *whole* 64-bit register with loadByType(x.Type): the
    low Size() bytes from the slot, upper bits re-extended according to
    x.Type.IsSigned() (identity at size 8).

    So the only upper-bit pattern that survives an arbitrary spill/restore of x is
    extend(x.Type). Nothing else about the defining op is preserved.

    ### The invariant

    An extension op E on top of x may be deleted only if both hold:

      (a) x's defining op puts E's result bits in the register at the def. This is
    what the rules already test: "proper load", ZeroUpperNBits, the
    boolean-producing ops.
      (b) loadByType(x.Type) performs the same extension E does. Otherwise a
    spill/restore anywhere between the def and a use silently rewrites the
    upper bits and (a) stops being true at that use.

    (b) is the half that is not written down anywhere -- but it is already enforced
    for the zero-extension direction. ssacore/rewrite.go:zeroUpperBits opens with

    	if x.Type.IsSigned() && 8*x.Type.Size() <= 64-bits {
    // A spill/restore sign-extends from the type's width (issue 68227).
    return false
    }

    which is exactly (b) for MOVBUreg/MOVHUreg/MOVWUreg, from CL 595675 (#68227).
    The "don't extend after proper load" block never got the mirror test. That is
    the gap you are pointing at, and I would like to close it in this CL.

    ### The exposure is not introduced by this CL

    MOVDreg compiles to a plain 64-bit MOVD (arm64/ssa.go, the
    `case ssaop.OpCopy, ssaop.OpARM64MOVDreg` arm) -- it does not re-extend. So

    	(MOVWreg x:(MOVWload _ _)) => (MOVDreg x)

    already asserts that x's register holds the sign-extended value at the point of
    the move, i.e. it already depends on (b) holding across every spill/restore
    between the load and the extension, which may sit in another block with calls in
    between. Deleting the node does not create the hazard.

    It does widen the window: after `=> x`, x's live range reaches every consumer
    instead of ending at the move, so there are strictly more points at which a
    spill could occur. To me that argues for settling (b) now rather than later, not
    for keeping the MOVDreg form, which is exposed to the same mismatch and merely
    harder to reach.

    ### Why I expect the guard to be free on arm64

    Every MOVxload takes its type from exactly one place, ARM64.rules:321-327:

    	(Load <t> ptr mem) && t.IsBoolean()                     => (MOVBUload ptr mem)
    (Load <t> ptr mem) && ssa.Is8BitInt(t) && t.IsSigned() => (MOVBload ptr mem)
    ... etc

    The predicate that picks the flavour is the same size+signedness test that
    loadByType uses, so op and type agree at origin. Every later rule that rewrites a
    load (offset folding, the loadidx/idx2/idx4 forms, symbol merging) does v.reset
    and keeps v.Type; arm64 has no `@x.Block (MOVxload <v.Type> ...)` retyping rules
    of the kind amd64 has. Between "lower" and "late lower" only "addressing modes"
    runs, and it preserves types. The "pair" pass afterwards keeps the flavour
    (MOVWload -> LDPSW, MOVWUload -> LDPW) and threads x.Type through the tuple.

    So I expect op flavour and x.Type to agree at every one of these match sites,
    and the guard to change zero instructions. I would rather measure that than
    assert it.

    ### Proposed next patch set

    1. Guard all 37 rules in the form you wrote, spelled out at each rule. The one
    thing worth flagging is that the test follows the *defining load*, not the
    outer extension op. 7 of the 37 are sign-extensions over an unsigned load,
    where the load already zeroes the bits the extension would set:
    	(MOVWreg  x:(MOVWload  _ _)) &&  x.Type.IsSigned() => x
    (MOVWreg x:(MOVBUload _ _)) && !x.Type.IsSigned() => x
    (MOVWUreg x:(MOVHUload _ _)) && !x.Type.IsSigned() => x
       i.e. the condition is "loadByType(x.Type) is the instruction that defined x,
    so a restore re-executes the load". (The converse pairing, an unsigned
    extension over a signed load, does not appear in these rules -- it would not
    be a no-op.)
       If 37 copies of the guard reads badly, I suggest putting the load-op-to-signedness
    mapping in one helper in arm64latelower_helpers.go, next to isARM64addcon,
    so every rule line becomes the same token.
    2. Write (a)/(b) above as a comment over that rule section, next to the existing
    "they occur in late lower because ..." note. Three things fall out of it for
    free and are worth documenting so the next reader does not re-derive them:
    the boolean rule is safe because bool is size 1 unsigned, so the restore is
    MOVBU; the double-extension folds are safe because MOVxreg results are size
    8, so the restore is MOVD; and the rules assume x.Type.Size() equals the
    load's width. I considered testing that width too, but a narrower type on a
    wider load is already broken before these rules see it -- the spill drops the
    bytes above the type, so the value itself is truncated whether or not an
    extension sits on top. That is an SSA well-formedness assumption, so it reads
    better as a sentence in the comment than as a condition on 37 rules.
    3. Census, to answer "can this actually happen": instrument the guard to log
    every match where it fails, build std+cmd for arm64, report the count. If it
    is zero, the guard is free and the invariant is enforced locally instead of
    holding by an accident spread over three files. If it is not zero, that is a
    live bug in the current tree with MOVDreg, and I will file it separately.
    4. Verify the guarded compiler emits byte-identical code to the unguarded one
    across std+cmd, then re-run the benchmark under the same A/A gate as before.
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Keith Randall
    • Martin Möhrmann
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 1
    Gerrit-Owner: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Reviewer: Keith Randall <k...@golang.org>
    Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-Attention: Keith Randall <k...@golang.org>
    Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
    Gerrit-Attention: Robert Griesemer <g...@golang.org>
    Gerrit-Comment-Date: Sat, 29 Aug 2026 18:39:36 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Keith Randall <k...@golang.org>
    unsatisfied_requirement
    open
    diffy

    Keith Randall (Gerrit)

    unread,
    Aug 30, 2026, 12:09:56 PMAug 30
    to Frank Kuehnel, goph...@pubsubhelper.golang.org, Robert Griesemer, Keith Randall, Martin Möhrmann, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from Frank Kuehnel, Martin Möhrmann and Robert Griesemer

    Keith Randall added 1 comment

    Patchset-level comments
    Keith Randall

    Note to also look at issue 80577. It is for riscv64, but it demonstrates how tricky this is to get right.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Frank Kuehnel
    • Martin Möhrmann
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 1
    Gerrit-Owner: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Reviewer: Keith Randall <k...@golang.org>
    Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
    Gerrit-Attention: Robert Griesemer <g...@golang.org>
    Gerrit-Attention: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Comment-Date: Sun, 30 Aug 2026 16:09:50 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Keith Randall <k...@golang.org>
    Comment-In-Reply-To: Frank Kuehnel <kueh...@gmail.com>
    unsatisfied_requirement
    open
    diffy

    Frank Kuehnel (Gerrit)

    unread,
    Aug 31, 2026, 1:09:52 PMAug 31
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Frank Kuehnel, Martin Möhrmann and Robert Griesemer

    Frank Kuehnel uploaded new patchset

    Frank Kuehnel uploaded patch set #2 to this change.
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Frank Kuehnel
    • Martin Möhrmann
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: newpatchset
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 2
    unsatisfied_requirement
    open
    diffy

    Frank Kuehnel (Gerrit)

    unread,
    Aug 31, 2026, 1:26:52 PMAug 31
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Frank Kuehnel, Martin Möhrmann and Robert Griesemer

    Frank Kuehnel uploaded new patchset

    Frank Kuehnel uploaded patch set #3 to this change.
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Frank Kuehnel
    • Martin Möhrmann
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: newpatchset
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 3
    unsatisfied_requirement
    open
    diffy

    Frank Kuehnel (Gerrit)

    unread,
    Aug 31, 2026, 1:31:16 PMAug 31
    to goph...@pubsubhelper.golang.org, Robert Griesemer, Keith Randall, Martin Möhrmann, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from Martin Möhrmann and Robert Griesemer

    Frank Kuehnel added 1 comment

    Patchset-level comments
    File-level comment, Patchset 3 (Latest):
    Frank Kuehnel . resolved

    Thanks — 80577 was the right thing to read, and it changed what I think this CL
    should be.

    What I found across the backends is not really about arm64, so I put it on the
    issue: https://github.com/golang/go/issues/80577#issuecomment-5481715626

    The part that matters here is why this is tricky, which looks structural rather
    than incidental. Deleting an extension `E` over a value `x` needs two things:
    **(a)** `x`'s defining op already leaves `E`'s result in the register, and
    **(b)** restoring `x` from a spill slot produces that same result. Every rule in
    the tree tests (a). Nothing names (b). And of the two:

    - **(a) is half-declarable.** `zeroUpperBits` is a declared per-op attribute in
    the `_gen` op definitions, validated by the generator — exactly the right
    shape. But it only covers zero extension. There is no attribute for "this op
    sign-extends from bit N", so arm64 can declare `MULW` as `zeroUpperBits: 32`
    while riscv64's sign-extending `MULW` has nowhere to say so — and that is the
    one that got elided unsafely.
    - **(b) is not declarable at all.** What a restore puts above `t.Size()` lives
    inside `loadByType`, or `loadByRegWidth` on amd64, and is never exposed to the
    rules.
    - **(a)'s durability is a filing convention.** CL 568616 fixed it by moving
    these rules to late lower. Nothing enforces that placement, and s390x has no
    late lower pass at all, so the one backend that already tests (b) cannot have
    it.

    The visible result is six different spellings of (b) across the tree, and four
    backends with none.

    **So I have taken patch set 2 back down.** It added (b) to the 37 arm64 rules
    behind a local helper, and it measured well — the check is evaluated 10504 times
    over std+cmd and blocks nothing, both compilers emit identical code across
    std+cmd, all.bash passes, and loong64's copy of these rules sits at 20087 sites
    with none failing. So cost is not the issue. But free is not the same as right:
    a local `isARM64loadMatchesType` would be the seventh spelling of a condition
    that ought to exist once, and adding it makes the proliferation worse rather
    than better. Patch set 3 is patch set 1 again; patch set 2 is still in the
    history if you want to see what it looked like.

    What this needs first is an architectural direction — one place that says what a
    restore does to the bits above `t.Size()`, that `zeroUpperBits` and the backend
    rules can both consult, and ideally a way for an op to declare sign extension
    the way it can already declare zero extension. That is a call for the people who
    own this area rather than something to settle inside an arm64 CL.

    Once there is a direction I will shape this CL to it, including dropping the
    optimisation from it if the safety condition should land on its own.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Martin Möhrmann
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 3
    Gerrit-Owner: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Reviewer: Keith Randall <k...@golang.org>
    Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
    Gerrit-Attention: Robert Griesemer <g...@golang.org>
    Gerrit-Comment-Date: Mon, 31 Aug 2026 17:31:09 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Keith Randall (Gerrit)

    unread,
    Aug 31, 2026, 5:55:38 PMAug 31
    to Frank Kuehnel, goph...@pubsubhelper.golang.org, Robert Griesemer, Keith Randall, Martin Möhrmann, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from Frank Kuehnel, Martin Möhrmann and Robert Griesemer

    Keith Randall added 1 comment

    Patchset-level comments
    Frank Kuehnel . unresolved
    Keith Randall

    I agree that it would be good to have the various arch backends report whether restores do sign or zero extend, and then we can use that info in the arch-independent parts (like zeroUpperBits).

    Adding a `signExtendedUpperBits` field to ops that guarantee sign extension would also be good. (Although there is nowhere that uses such info currently?)

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Frank Kuehnel
    • Martin Möhrmann
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 3
    Gerrit-Owner: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Reviewer: Keith Randall <k...@golang.org>
    Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
    Gerrit-Attention: Robert Griesemer <g...@golang.org>
    Gerrit-Attention: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Comment-Date: Mon, 31 Aug 2026 21:55:34 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Frank Kuehnel <kueh...@gmail.com>
    unsatisfied_requirement
    open
    diffy

    Frank Kuehnel (Gerrit)

    unread,
    Aug 31, 2026, 8:34:57 PMAug 31
    to goph...@pubsubhelper.golang.org, Robert Griesemer, Keith Randall, Martin Möhrmann, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from Keith Randall, Martin Möhrmann and Robert Griesemer

    Frank Kuehnel added 1 comment

    Patchset-level comments
    Frank Kuehnel

    Makes sense. Since that refactoring affects multiple backends, should we scope the architectural changes to a separate issue/proposal so this fix isn't blocked, or would you prefer to agree on the signExtendedUpperBits API design first?

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Keith Randall
    • Martin Möhrmann
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 3
    Gerrit-Owner: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Reviewer: Keith Randall <k...@golang.org>
    Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-Attention: Keith Randall <k...@golang.org>
    Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
    Gerrit-Attention: Robert Griesemer <g...@golang.org>
    Gerrit-Comment-Date: Tue, 01 Sep 2026 00:34:53 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Keith Randall (Gerrit)

    unread,
    Sep 1, 2026, 1:39:42 AMSep 1
    to Frank Kuehnel, goph...@pubsubhelper.golang.org, Robert Griesemer, Keith Randall, Martin Möhrmann, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from Frank Kuehnel, Martin Möhrmann and Robert Griesemer

    Keith Randall added 1 comment

    Patchset-level comments
    Keith Randall

    The refactoring/architectural change should be first, hopefully with no semantic changes, just a reorg of existing code. Then we can make this CL on top of that CL.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Frank Kuehnel
    • Martin Möhrmann
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 3
    Gerrit-Owner: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Reviewer: Keith Randall <k...@golang.org>
    Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
    Gerrit-Attention: Robert Griesemer <g...@golang.org>
    Gerrit-Attention: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Comment-Date: Tue, 01 Sep 2026 05:39:35 +0000
    unsatisfied_requirement
    open
    diffy

    Frank Kuehnel (Gerrit)

    unread,
    Sep 1, 2026, 2:39:52 PMSep 1
    to goph...@pubsubhelper.golang.org, Robert Griesemer, Keith Randall, Martin Möhrmann, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from Keith Randall, Martin Möhrmann and Robert Griesemer

    Frank Kuehnel added 1 comment

    Patchset-level comments
    Attention is currently required from:
    • Keith Randall
    • Martin Möhrmann
    • Robert Griesemer
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I49ce8bb2dbaae403d026de6b173130cfd0bed46b
    Gerrit-Change-Number: 823424
    Gerrit-PatchSet: 3
    Gerrit-Owner: Frank Kuehnel <kueh...@gmail.com>
    Gerrit-Reviewer: Keith Randall <k...@golang.org>
    Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
    Gerrit-Reviewer: Robert Griesemer <g...@golang.org>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-Attention: Keith Randall <k...@golang.org>
    Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
    Gerrit-Attention: Robert Griesemer <g...@golang.org>
    Gerrit-Comment-Date: Tue, 01 Sep 2026 18:39:46 +0000
    unsatisfied_requirement
    open
    diffy
    Reply all
    Reply to author
    Forward
    0 new messages