aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/ssa/writebarrier.go
diff options
context:
space:
mode:
authorKeith Randall <khr@google.com>2018-11-27 12:40:16 -0800
committerKeith Randall <khr@golang.org>2018-11-29 22:23:02 +0000
commit2140975ebde164ea1eaa70fc72775c03567f2bc9 (patch)
tree132712d538bebb752042e10ddbdf4a7fae7aab68 /src/cmd/compile/internal/ssa/writebarrier.go
parent438b9544a079576c539cdc040cbf337966a0b25d (diff)
downloadgo-2140975ebde164ea1eaa70fc72775c03567f2bc9.tar.gz
go-2140975ebde164ea1eaa70fc72775c03567f2bc9.zip
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>
Diffstat (limited to 'src/cmd/compile/internal/ssa/writebarrier.go')
-rw-r--r--src/cmd/compile/internal/ssa/writebarrier.go57
1 files changed, 55 insertions, 2 deletions
diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go
index 2366e0bfbf..95816d2bda 100644
--- a/src/cmd/compile/internal/ssa/writebarrier.go
+++ b/src/cmd/compile/internal/ssa/writebarrier.go
@@ -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 {