@@ -38,11 +38,14 @@ func nilcheckelim(f *Func) {
3838 work := make ([]bp , 0 , 256 )
3939 work = append (work , bp {block : f .Entry })
4040
41- // map from value ID to bool indicating if value is known to be non-nil
42- // in the current dominator path being walked. This slice is updated by
41+ // map from value ID to known non-nil version of that value ID
42+ // ( in the current dominator path being walked) . This slice is updated by
4343 // walkStates to maintain the known non-nil values.
44- nonNilValues := f .Cache .allocBoolSlice (f .NumValues ())
45- defer f .Cache .freeBoolSlice (nonNilValues )
44+ // If there is extrinsic information about non-nil-ness, this map
45+ // points a value to itself. If a value is known non-nil because we
46+ // already did a nil check on it, it points to the nil check operation.
47+ nonNilValues := f .Cache .allocValueSlice (f .NumValues ())
48+ defer f .Cache .freeValueSlice (nonNilValues )
4649
4750 // make an initial pass identifying any non-nil values
4851 for _ , b := range f .Blocks {
@@ -54,7 +57,7 @@ func nilcheckelim(f *Func) {
5457 // We assume that SlicePtr is non-nil because we do a bounds check
5558 // before the slice access (and all cap>0 slices have a non-nil ptr). See #30366.
5659 if v .Op == OpAddr || v .Op == OpLocalAddr || v .Op == OpAddPtr || v .Op == OpOffPtr || v .Op == OpAdd32 || v .Op == OpAdd64 || v .Op == OpSub32 || v .Op == OpSub64 || v .Op == OpSlicePtr {
57- nonNilValues [v .ID ] = true
60+ nonNilValues [v .ID ] = v
5861 }
5962 }
6063 }
@@ -68,16 +71,16 @@ func nilcheckelim(f *Func) {
6871 if v .Op == OpPhi {
6972 argsNonNil := true
7073 for _ , a := range v .Args {
71- if ! nonNilValues [a .ID ] {
74+ if nonNilValues [a .ID ] == nil {
7275 argsNonNil = false
7376 break
7477 }
7578 }
7679 if argsNonNil {
77- if ! nonNilValues [v .ID ] {
80+ if nonNilValues [v .ID ] == nil {
7881 changed = true
7982 }
80- nonNilValues [v .ID ] = true
83+ nonNilValues [v .ID ] = v
8184 }
8285 }
8386 }
@@ -103,8 +106,8 @@ func nilcheckelim(f *Func) {
103106 if len (b .Preds ) == 1 {
104107 p := b .Preds [0 ].b
105108 if p .Kind == BlockIf && p .Controls [0 ].Op == OpIsNonNil && p .Succs [0 ].b == b {
106- if ptr := p .Controls [0 ].Args [0 ]; ! nonNilValues [ptr .ID ] {
107- nonNilValues [ptr .ID ] = true
109+ if ptr := p .Controls [0 ].Args [0 ]; nonNilValues [ptr .ID ] == nil {
110+ nonNilValues [ptr .ID ] = ptr
108111 work = append (work , bp {op : ClearPtr , ptr : ptr })
109112 }
110113 }
@@ -117,14 +120,11 @@ func nilcheckelim(f *Func) {
117120 pendingLines .clear ()
118121
119122 // Next, process values in the block.
120- i := 0
121123 for _ , v := range b .Values {
122- b .Values [i ] = v
123- i ++
124124 switch v .Op {
125125 case OpIsNonNil :
126126 ptr := v .Args [0 ]
127- if nonNilValues [ptr .ID ] {
127+ if nonNilValues [ptr .ID ] != nil {
128128 if v .Pos .IsStmt () == src .PosIsStmt { // Boolean true is a terrible statement boundary.
129129 pendingLines .add (v .Pos )
130130 v .Pos = v .Pos .WithNotStmt ()
@@ -135,7 +135,7 @@ func nilcheckelim(f *Func) {
135135 }
136136 case OpNilCheck :
137137 ptr := v .Args [0 ]
138- if nonNilValues [ptr .ID ] {
138+ if nilCheck := nonNilValues [ptr .ID ]; nilCheck != nil {
139139 // This is a redundant implicit nil check.
140140 // Logging in the style of the former compiler -- and omit line 1,
141141 // which is usually in generated code.
@@ -145,14 +145,13 @@ func nilcheckelim(f *Func) {
145145 if v .Pos .IsStmt () == src .PosIsStmt { // About to lose a statement boundary
146146 pendingLines .add (v .Pos )
147147 }
148- v .reset (OpUnknown )
149- f .freeValue (v )
150- i --
148+ v .Op = OpCopy
149+ v .SetArgs1 (nilCheck )
151150 continue
152151 }
153152 // Record the fact that we know ptr is non nil, and remember to
154153 // undo that information when this dominator subtree is done.
155- nonNilValues [ptr .ID ] = true
154+ nonNilValues [ptr .ID ] = v
156155 work = append (work , bp {op : ClearPtr , ptr : ptr })
157156 fallthrough // a non-eliminated nil check might be a good place for a statement boundary.
158157 default :
@@ -163,7 +162,7 @@ func nilcheckelim(f *Func) {
163162 }
164163 }
165164 // This reduces the lost statement count in "go" by 5 (out of 500 total).
166- for j := 0 ; j < i ; j ++ { // is this an ordering problem?
165+ for j := range b . Values { // is this an ordering problem?
167166 v := b .Values [j ]
168167 if v .Pos .IsStmt () != src .PosNotStmt && ! isPoorStatementOp (v .Op ) && pendingLines .contains (v .Pos ) {
169168 v .Pos = v .Pos .WithIsStmt ()
@@ -174,15 +173,14 @@ func nilcheckelim(f *Func) {
174173 b .Pos = b .Pos .WithIsStmt ()
175174 pendingLines .remove (b .Pos )
176175 }
177- b .truncateValues (i )
178176
179177 // Add all dominated blocks to the work list.
180178 for w := sdom [node .block .ID ].child ; w != nil ; w = sdom [w .ID ].sibling {
181179 work = append (work , bp {op : Work , block : w })
182180 }
183181
184182 case ClearPtr :
185- nonNilValues [node .ptr .ID ] = false
183+ nonNilValues [node .ptr .ID ] = nil
186184 continue
187185 }
188186 }
0 commit comments