-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
package main
import "runtime"
func f(m map[int]int) {
outer:
for i := 0; i < 10; i++ {
for k := range m {
if k == 5 {
continue outer
}
}
runtime.GC()
break
}
runtime.GC()
}
$ go tool compile -live tmp1.go
tmp1.go:5:18: live at entry to f: m
tmp1.go:8:12: live at call to mapiterinit: m .autotmp_4
tmp1.go:8:12: live at call to mapiternext: m .autotmp_4
tmp1.go:16:12: f: .autotmp_4 (type map.iter[int]int) is ambiguously live
tmp1.go:16:12: live at call to GC: .autotmp_4
The hiter for the inner loop (.autotmp_4
) should not be live at the outer runtime.GC
. It is live because liveness analysis sort of gives up on it (the "ambiguously live" comment there).
That's not so bad in and of itself; it's just imprecise.
But at the end of the inner loop we issue a VARKILL of .autotmp_4
, so it definitely isn't live at the first runtime.GC
(nothing with a pointer is live at that call, so it isn't even listed by -live
). That means .autotmp_4
is live, then it isn't, then it is again. The first runtime.GC
will free m
, as nothing is holding m
live at this point. The second runtime.GC
finds m
inside .autotmp_4
and tries to scan an already freed map. Boom.
This seems like a bad interaction between ambiguously live and VARKILL. Maybe fundamentally bad. Time to put my pondering cap on...