Skip to content

Commit 0202ad0

Browse files
committed
cmd/compile: prevent IsNewObject from taking quadratic time
As part of IsNewObject, we need to go from the SelectN[0] use of a call to the SelectN[1] use of a call. The current code does this by just looking through the block. If the block is very large, this ends up taking quadratic time. Instead, prepopulate a map from call -> SelectN[1] user of that call. That lets us find the SelectN[1] user in constant time. Fixes #57657 Change-Id: Ie2e0b660af5c080314f4f17ba2838510a1147f9e Reviewed-on: https://go-review.googlesource.com/c/go/+/461080 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Run-TryBot: Keith Randall <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]>
1 parent 64519ba commit 0202ad0

File tree

1 file changed

+24
-14
lines changed

1 file changed

+24
-14
lines changed

src/cmd/compile/internal/ssa/writebarrier.go

+24-14
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type ZeroRegion struct {
2727
// needwb reports whether we need write barrier for store op v.
2828
// v must be Store/Move/Zero.
2929
// zeroes provides known zero information (keyed by ID of memory-type values).
30-
func needwb(v *Value, zeroes map[ID]ZeroRegion) bool {
30+
func needwb(v *Value, zeroes map[ID]ZeroRegion, select1 []*Value) bool {
3131
t, ok := v.Aux.(*types.Type)
3232
if !ok {
3333
v.Fatalf("store aux is not a type: %s", v.LongString())
@@ -39,7 +39,7 @@ func needwb(v *Value, zeroes map[ID]ZeroRegion) bool {
3939
return false // write on stack doesn't need write barrier
4040
}
4141
if v.Op == OpMove && IsReadOnlyGlobalAddr(v.Args[1]) {
42-
if mem, ok := IsNewObject(v.Args[0]); ok && mem == v.MemoryArg() {
42+
if mem, ok := IsNewObject(v.Args[0], select1); ok && mem == v.MemoryArg() {
4343
// Copying data from readonly memory into a fresh object doesn't need a write barrier.
4444
return false
4545
}
@@ -99,15 +99,30 @@ func writebarrier(f *Func) {
9999
var sset *sparseSet
100100
var storeNumber []int32
101101

102-
zeroes := f.computeZeroMap()
102+
// Compute map from a value to the SelectN [1] value that uses it.
103+
select1 := f.Cache.allocValueSlice(f.NumValues())
104+
defer func() { f.Cache.freeValueSlice(select1) }()
105+
for _, b := range f.Blocks {
106+
for _, v := range b.Values {
107+
if v.Op != OpSelectN {
108+
continue
109+
}
110+
if v.AuxInt != 1 {
111+
continue
112+
}
113+
select1[v.Args[0].ID] = v
114+
}
115+
}
116+
117+
zeroes := f.computeZeroMap(select1)
103118
for _, b := range f.Blocks { // range loop is safe since the blocks we added contain no stores to expand
104119
// first, identify all the stores that need to insert a write barrier.
105120
// mark them with WB ops temporarily. record presence of WB ops.
106121
nWBops := 0 // count of temporarily created WB ops remaining to be rewritten in the current block
107122
for _, v := range b.Values {
108123
switch v.Op {
109124
case OpStore, OpMove, OpZero:
110-
if needwb(v, zeroes) {
125+
if needwb(v, zeroes, select1) {
111126
switch v.Op {
112127
case OpStore:
113128
v.Op = OpStoreWB
@@ -376,7 +391,8 @@ func writebarrier(f *Func) {
376391

377392
// computeZeroMap returns a map from an ID of a memory value to
378393
// a set of locations that are known to be zeroed at that memory value.
379-
func (f *Func) computeZeroMap() map[ID]ZeroRegion {
394+
func (f *Func) computeZeroMap(select1 []*Value) map[ID]ZeroRegion {
395+
380396
ptrSize := f.Config.PtrSize
381397
// Keep track of which parts of memory are known to be zero.
382398
// This helps with removing write barriers for various initialization patterns.
@@ -386,7 +402,7 @@ func (f *Func) computeZeroMap() map[ID]ZeroRegion {
386402
// Find new objects.
387403
for _, b := range f.Blocks {
388404
for _, v := range b.Values {
389-
if mem, ok := IsNewObject(v); ok {
405+
if mem, ok := IsNewObject(v, select1); ok {
390406
// While compiling package runtime itself, we might see user
391407
// calls to newobject, which will have result type
392408
// unsafe.Pointer instead. We can't easily infer how large the
@@ -584,20 +600,14 @@ func IsReadOnlyGlobalAddr(v *Value) bool {
584600

585601
// IsNewObject reports whether v is a pointer to a freshly allocated & zeroed object,
586602
// if so, also returns the memory state mem at which v is zero.
587-
func IsNewObject(v *Value) (mem *Value, ok bool) {
603+
func IsNewObject(v *Value, select1 []*Value) (mem *Value, ok bool) {
588604
f := v.Block.Func
589605
c := f.Config
590606
if f.ABIDefault == f.ABI1 && len(c.intParamRegs) >= 1 {
591607
if v.Op != OpSelectN || v.AuxInt != 0 {
592608
return nil, false
593609
}
594-
// Find the memory
595-
for _, w := range v.Block.Values {
596-
if w.Op == OpSelectN && w.AuxInt == 1 && w.Args[0] == v.Args[0] {
597-
mem = w
598-
break
599-
}
600-
}
610+
mem = select1[v.Args[0].ID]
601611
if mem == nil {
602612
return nil, false
603613
}

0 commit comments

Comments
 (0)