mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: eliminate write barriers when writing non-heap ptrs
We don't need a write barrier if:
1) The location we're writing to doesn't hold a heap pointer, and
2) The value we're writing isn't a heap pointer.
The freshly returned value from runtime.newobject satisfies (1).
Pointers to globals, and the contents of the read-only data section satisfy (2).
This is particularly helpful for code like:
p := []string{"abc", "def", "ghi"}
Where the compiler generates:
a := new([3]string)
move(a, statictmp_) // eliminates write barriers here
p := a[:]
For big slice literals, this makes the code a smaller and faster to
compile.
Update #13554. Reduces the compile time by ~10% and RSS by ~30%.
Change-Id: Icab81db7591c8777f68e5d528abd48c7e44c87eb
Reviewed-on: https://go-review.googlesource.com/c/151498
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
parent
438b9544a0
commit
2140975ebd
2 changed files with 66 additions and 2 deletions
|
|
@ -24,6 +24,14 @@ func needwb(v *Value) bool {
|
|||
if IsStackAddr(v.Args[0]) {
|
||||
return false // write on stack doesn't need write barrier
|
||||
}
|
||||
if v.Op == OpStore && IsGlobalAddr(v.Args[1]) && IsNewObject(v.Args[0], v.MemoryArg()) {
|
||||
// Storing pointers to non-heap locations into a fresh object doesn't need a write barrier.
|
||||
return false
|
||||
}
|
||||
if v.Op == OpMove && IsReadOnlyGlobalAddr(v.Args[1]) && IsNewObject(v.Args[0], v.MemoryArg()) {
|
||||
// Copying data from readonly memory into a fresh object doesn't need a write barrier.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +361,7 @@ func round(o int64, r int64) int64 {
|
|||
return (o + r - 1) &^ (r - 1)
|
||||
}
|
||||
|
||||
// IsStackAddr returns whether v is known to be an address of a stack slot
|
||||
// IsStackAddr reports whether v is known to be an address of a stack slot.
|
||||
func IsStackAddr(v *Value) bool {
|
||||
for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy {
|
||||
v = v.Args[0]
|
||||
|
|
@ -365,6 +373,51 @@ func IsStackAddr(v *Value) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// IsGlobalAddr reports whether v is known to be an address of a global.
|
||||
func IsGlobalAddr(v *Value) bool {
|
||||
return v.Op == OpAddr && v.Args[0].Op == OpSB
|
||||
}
|
||||
|
||||
// IsReadOnlyGlobalAddr reports whether v is known to be an address of a read-only global.
|
||||
func IsReadOnlyGlobalAddr(v *Value) bool {
|
||||
if !IsGlobalAddr(v) {
|
||||
return false
|
||||
}
|
||||
// See TODO in OpAddr case in IsSanitizerSafeAddr below.
|
||||
return strings.HasPrefix(v.Aux.(*obj.LSym).Name, `"".statictmp_`)
|
||||
}
|
||||
|
||||
// IsNewObject reports whether v is a pointer to a freshly allocated & zeroed object at memory state mem.
|
||||
// TODO: Be more precise. We really want "IsNilPointer" for the particular field in question.
|
||||
// Right now, we can only detect a new object before any writes have been done to it.
|
||||
// We could ignore non-pointer writes, writes to offsets which
|
||||
// are known not to overlap the write in question, etc.
|
||||
func IsNewObject(v *Value, mem *Value) bool {
|
||||
if v.Op != OpLoad {
|
||||
return false
|
||||
}
|
||||
if v.MemoryArg() != mem {
|
||||
return false
|
||||
}
|
||||
if mem.Op != OpStaticCall {
|
||||
return false
|
||||
}
|
||||
if !isSameSym(mem.Aux, "runtime.newobject") {
|
||||
return false
|
||||
}
|
||||
if v.Args[0].Op != OpOffPtr {
|
||||
return false
|
||||
}
|
||||
if v.Args[0].Args[0].Op != OpSP {
|
||||
return false
|
||||
}
|
||||
c := v.Block.Func.Config
|
||||
if v.Args[0].AuxInt != c.ctxt.FixedFrameSize()+c.RegSize { // offset of return value
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsSanitizerSafeAddr reports whether v is known to be an address
|
||||
// that doesn't need instrumentation.
|
||||
func IsSanitizerSafeAddr(v *Value) bool {
|
||||
|
|
@ -393,7 +446,7 @@ func IsSanitizerSafeAddr(v *Value) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// isVolatile returns whether v is a pointer to argument region on stack which
|
||||
// isVolatile reports whether v is a pointer to argument region on stack which
|
||||
// will be clobbered by a function call.
|
||||
func isVolatile(v *Value) bool {
|
||||
for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue