Add the following patch to the runtime:
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -194,9 +194,14 @@ func wbMove(typ *_type, dst, src unsafe.Pointer) {
// pass a type here.
//
// See the comment on bulkBarrierPreWrite.
+ nop()
bulkBarrierPreWrite(uintptr(dst), uintptr(src), typ.PtrBytes, typ)
}
+//go:noinline
+//go:nosplit
+func nop() {}
+
The patch should not do anything.
But this program starts panicing:
package main
type S [5]*byte
//go:noinline
func f() S {
return S{}
}
var sink *S
//go:noinline
func g() (s S) {
sink = &s
s = f()
return
}
func main() {
for i := range 1000000 {
s := g()
if s != (S{}) {
println(s[0], s[1], s[2], s[3], s[4])
panic(i)
}
}
}
The reason is we introduced a spill of wbMove's arguments at the start of wbMove. That spill uses its arg area as the spill location. At the write barrier for the s = f() statement, the results of the f() call are stored in that same argument area. We end up clobbering the results before they can be copied out to the heap.
An unwritten rule is that wbMove (and probably wbZero also) can't spill to its argument area. it is used when its argument area is currently being used for other things.
There is code to handle this situation, but I (mistakenly) thought we didn't need it for archs with register calling convention. But that only avoids the problem when marshaling the args to wbMove. It does not avoid the problem of spills inside wbMove.
We should either disable the register calling convention hack, or move wbMove to assembly. I think probably the former.
Note that this is not currently a problem for any of our releases, including tip. I just ran into it when developing a CL. Mostly filing this just as a marker to add a test off of.
Add the following patch to the runtime:
The patch should not do anything.
But this program starts panicing:
The reason is we introduced a spill of
wbMove's arguments at the start ofwbMove. That spill uses its arg area as the spill location. At the write barrier for thes = f()statement, the results of thef()call are stored in that same argument area. We end up clobbering the results before they can be copied out to the heap.An unwritten rule is that
wbMove(and probablywbZeroalso) can't spill to its argument area. it is used when its argument area is currently being used for other things.There is code to handle this situation, but I (mistakenly) thought we didn't need it for archs with register calling convention. But that only avoids the problem when marshaling the args to
wbMove. It does not avoid the problem of spills insidewbMove.We should either disable the register calling convention hack, or move
wbMoveto assembly. I think probably the former.Note that this is not currently a problem for any of our releases, including tip. I just ran into it when developing a CL. Mostly filing this just as a marker to add a test off of.