Skip to content

Commit 8cd85e6

Browse files
Jorropogopherbot
authored andcommitted
cmd/compile: check domination of loop return in both controls
Fixes #74473 Change-Id: I72ff6b95955ae9407271508aa80f230dcf1b6c74 Reviewed-on: https://go-review.googlesource.com/c/go/+/685816 Reviewed-by: Mark Freeman <[email protected]> Auto-Submit: Jorropo <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent cefaed0 commit 8cd85e6

File tree

2 files changed

+36
-10
lines changed

2 files changed

+36
-10
lines changed

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

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,20 @@ type indVar struct {
3737
// - the minimum bound
3838
// - the increment value
3939
// - the "next" value (SSA value that is Phi'd into the induction variable every loop)
40+
// - the header's edge returning from the body
4041
//
4142
// Currently, we detect induction variables that match (Phi min nxt),
4243
// with nxt being (Add inc ind).
4344
// 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) {
4546
if ind.Op != OpPhi {
4647
return
4748
}
4849

4950
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]
5152
} 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]
5354
} else {
5455
// Not a recognized induction variable.
5556
return
@@ -111,13 +112,13 @@ func findIndVar(f *Func) []indVar {
111112

112113
// See if this is really an induction variable
113114
less := true
114-
init, inc, nxt := parseIndVar(ind)
115+
init, inc, nxt, loopReturn := parseIndVar(ind)
115116
if init == nil {
116117
// We failed to parse the induction variable. Before punting, we want to check
117118
// whether the control op was written with the induction variable on the RHS
118119
// instead of the LHS. This happens for the downwards case, like:
119120
// for i := len(n)-1; i >= 0; i--
120-
init, inc, nxt = parseIndVar(limit)
121+
init, inc, nxt, loopReturn = parseIndVar(limit)
121122
if init == nil {
122123
// No recognized induction variable on either operand
123124
continue
@@ -145,6 +146,20 @@ func findIndVar(f *Func) []indVar {
145146
continue
146147
}
147148

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+
148163
// Increment sign must match comparison direction.
149164
// When incrementing, the termination comparison must be ind </<= limit.
150165
// When decrementing, the termination comparison must be ind >/>= limit.
@@ -172,14 +187,14 @@ func findIndVar(f *Func) []indVar {
172187
// First condition: loop entry has a single predecessor, which
173188
// is the header block. This implies that b.Succs[0] is
174189
// 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.
177192
continue
178193
}
179194

180-
// Second condition: b.Succs[0] dominates nxt so that
195+
// Second condition: startBody.b dominates nxt so that
181196
// nxt is computed when inc < limit.
182-
if !sdom.IsAncestorEq(b.Succs[0].b, nxt.Block) {
197+
if !sdom.IsAncestorEq(startBody.b, nxt.Block) {
183198
// inc+ind can only be reached through the branch that enters the loop.
184199
continue
185200
}
@@ -298,7 +313,7 @@ func findIndVar(f *Func) []indVar {
298313
nxt: nxt,
299314
min: min,
300315
max: max,
301-
entry: b.Succs[0].b,
316+
entry: startBody.b,
302317
flags: flags,
303318
})
304319
b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max)

test/prove.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,6 +2319,17 @@ func resliceBytes(b []byte) byte {
23192319
return 0
23202320
}
23212321

2322+
func issue74473(s []uint) {
2323+
i := 0
2324+
for {
2325+
if i >= len(s) { // ERROR "Induction variable: limits \[0,\?\), increment 1$"
2326+
break
2327+
}
2328+
_ = s[i] // ERROR "Proved IsInBounds$"
2329+
i++
2330+
}
2331+
}
2332+
23222333
//go:noinline
23232334
func useInt(a int) {
23242335
}

0 commit comments

Comments
 (0)