@@ -37,19 +37,20 @@ type indVar struct {
37
37
// - the minimum bound
38
38
// - the increment value
39
39
// - the "next" value (SSA value that is Phi'd into the induction variable every loop)
40
+ // - the header's edge returning from the body
40
41
//
41
42
// Currently, we detect induction variables that match (Phi min nxt),
42
43
// with nxt being (Add inc ind).
43
44
// If it can't parse the induction variable correctly, it returns (nil, nil, nil).
44
- func parseIndVar (ind * Value ) (min , inc , nxt * Value ) {
45
+ func parseIndVar (ind * Value ) (min , inc , nxt * Value , loopReturn Edge ) {
45
46
if ind .Op != OpPhi {
46
47
return
47
48
}
48
49
49
50
if n := ind .Args [0 ]; (n .Op == OpAdd64 || n .Op == OpAdd32 || n .Op == OpAdd16 || n .Op == OpAdd8 ) && (n .Args [0 ] == ind || n .Args [1 ] == ind ) {
50
- min , nxt = ind .Args [1 ], n
51
+ min , nxt , loopReturn = ind .Args [1 ], n , ind . Block . Preds [ 0 ]
51
52
} else if n := ind .Args [1 ]; (n .Op == OpAdd64 || n .Op == OpAdd32 || n .Op == OpAdd16 || n .Op == OpAdd8 ) && (n .Args [0 ] == ind || n .Args [1 ] == ind ) {
52
- min , nxt = ind .Args [0 ], n
53
+ min , nxt , loopReturn = ind .Args [0 ], n , ind . Block . Preds [ 1 ]
53
54
} else {
54
55
// Not a recognized induction variable.
55
56
return
@@ -111,13 +112,13 @@ func findIndVar(f *Func) []indVar {
111
112
112
113
// See if this is really an induction variable
113
114
less := true
114
- init , inc , nxt := parseIndVar (ind )
115
+ init , inc , nxt , loopReturn := parseIndVar (ind )
115
116
if init == nil {
116
117
// We failed to parse the induction variable. Before punting, we want to check
117
118
// whether the control op was written with the induction variable on the RHS
118
119
// instead of the LHS. This happens for the downwards case, like:
119
120
// for i := len(n)-1; i >= 0; i--
120
- init , inc , nxt = parseIndVar (limit )
121
+ init , inc , nxt , loopReturn = parseIndVar (limit )
121
122
if init == nil {
122
123
// No recognized induction variable on either operand
123
124
continue
@@ -145,6 +146,20 @@ func findIndVar(f *Func) []indVar {
145
146
continue
146
147
}
147
148
149
+ // startBody is the edge that eventually returns to the loop header.
150
+ var startBody Edge
151
+ switch {
152
+ case sdom .IsAncestorEq (b .Succs [0 ].b , loopReturn .b ):
153
+ startBody = b .Succs [0 ]
154
+ case sdom .IsAncestorEq (b .Succs [1 ].b , loopReturn .b ):
155
+ // if x { goto exit } else { goto entry } is identical to if !x { goto entry } else { goto exit }
156
+ startBody = b .Succs [1 ]
157
+ less = ! less
158
+ inclusive = ! inclusive
159
+ default :
160
+ continue
161
+ }
162
+
148
163
// Increment sign must match comparison direction.
149
164
// When incrementing, the termination comparison must be ind </<= limit.
150
165
// When decrementing, the termination comparison must be ind >/>= limit.
@@ -172,14 +187,14 @@ func findIndVar(f *Func) []indVar {
172
187
// First condition: loop entry has a single predecessor, which
173
188
// is the header block. This implies that b.Succs[0] is
174
189
// reached iff ind < limit.
175
- if len (b . Succs [ 0 ] .b .Preds ) != 1 {
176
- // b.Succs[1] must exit the loop.
190
+ if len (startBody .b .Preds ) != 1 {
191
+ // the other successor must exit the loop.
177
192
continue
178
193
}
179
194
180
- // Second condition: b.Succs[0] dominates nxt so that
195
+ // Second condition: startBody.b dominates nxt so that
181
196
// nxt is computed when inc < limit.
182
- if ! sdom .IsAncestorEq (b . Succs [ 0 ] .b , nxt .Block ) {
197
+ if ! sdom .IsAncestorEq (startBody .b , nxt .Block ) {
183
198
// inc+ind can only be reached through the branch that enters the loop.
184
199
continue
185
200
}
@@ -298,7 +313,7 @@ func findIndVar(f *Func) []indVar {
298
313
nxt : nxt ,
299
314
min : min ,
300
315
max : max ,
301
- entry : b . Succs [ 0 ] .b ,
316
+ entry : startBody .b ,
302
317
flags : flags ,
303
318
})
304
319
b .Logf ("found induction variable %v (inc = %v, min = %v, max = %v)\n " , ind , inc , min , max )
0 commit comments