mirror of https://github.com/golang/go.git
cmd/compile: use len(s)<=cap(s) to remove more bounds checks
When we discover a relation x <= len(s), also discover the relation x <= cap(s). That way, in situations like: a := s[x:] // tests 0 <= x <= len(s) b := s[:x] // tests 0 <= x <= cap(s) the second check can be eliminated. Fixes #16813 Change-Id: Ifc037920b6955e43bac1a1eaf6bac63a89cfbd44 Reviewed-on: https://go-review.googlesource.com/33633 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Alexandru Moșoi <alexandru@mosoi.ro> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
6317f92f6e
commit
73f92f9b04
|
|
@ -97,6 +97,12 @@ type factsTable struct {
|
||||||
// known lower and upper bounds on individual values.
|
// known lower and upper bounds on individual values.
|
||||||
limits map[ID]limit
|
limits map[ID]limit
|
||||||
limitStack []limitFact // previous entries
|
limitStack []limitFact // previous entries
|
||||||
|
|
||||||
|
// For each slice s, a map from s to a len(s)/cap(s) value (if any)
|
||||||
|
// TODO: check if there are cases that matter where we have
|
||||||
|
// more than one len(s) for a slice. We could keep a list if necessary.
|
||||||
|
lens map[ID]*Value
|
||||||
|
caps map[ID]*Value
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkpointFact is an invalid value used for checkpointing
|
// checkpointFact is an invalid value used for checkpointing
|
||||||
|
|
@ -432,7 +438,8 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// prove removes redundant BlockIf controls that can be inferred in a straight line.
|
// prove removes redundant BlockIf branches that can be inferred
|
||||||
|
// from previous dominating comparisons.
|
||||||
//
|
//
|
||||||
// By far, the most common redundant pair are generated by bounds checking.
|
// By far, the most common redundant pair are generated by bounds checking.
|
||||||
// For example for the code:
|
// For example for the code:
|
||||||
|
|
@ -455,6 +462,31 @@ var (
|
||||||
// else branch of the first comparison is executed, we already know that i < len(a).
|
// else branch of the first comparison is executed, we already know that i < len(a).
|
||||||
// The code for the second panic can be removed.
|
// The code for the second panic can be removed.
|
||||||
func prove(f *Func) {
|
func prove(f *Func) {
|
||||||
|
ft := newFactsTable()
|
||||||
|
|
||||||
|
// Find length and capacity ops.
|
||||||
|
for _, b := range f.Blocks {
|
||||||
|
for _, v := range b.Values {
|
||||||
|
if v.Uses == 0 {
|
||||||
|
// We don't care about dead values.
|
||||||
|
// (There can be some that are CSEd but not removed yet.)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch v.Op {
|
||||||
|
case OpSliceLen:
|
||||||
|
if ft.lens == nil {
|
||||||
|
ft.lens = map[ID]*Value{}
|
||||||
|
}
|
||||||
|
ft.lens[v.Args[0].ID] = v
|
||||||
|
case OpSliceCap:
|
||||||
|
if ft.caps == nil {
|
||||||
|
ft.caps = map[ID]*Value{}
|
||||||
|
}
|
||||||
|
ft.caps[v.Args[0].ID] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// current node state
|
// current node state
|
||||||
type walkState int
|
type walkState int
|
||||||
const (
|
const (
|
||||||
|
|
@ -472,7 +504,6 @@ func prove(f *Func) {
|
||||||
state: descend,
|
state: descend,
|
||||||
})
|
})
|
||||||
|
|
||||||
ft := newFactsTable()
|
|
||||||
idom := f.Idom()
|
idom := f.Idom()
|
||||||
sdom := f.sdom()
|
sdom := f.sdom()
|
||||||
|
|
||||||
|
|
@ -559,8 +590,34 @@ func updateRestrictions(parent *Block, ft *factsTable, t domain, v, w *Value, r
|
||||||
r = (lt | eq | gt) ^ r
|
r = (lt | eq | gt) ^ r
|
||||||
}
|
}
|
||||||
for i := domain(1); i <= t; i <<= 1 {
|
for i := domain(1); i <= t; i <<= 1 {
|
||||||
if t&i != 0 {
|
if t&i == 0 {
|
||||||
ft.update(parent, v, w, i, r)
|
continue
|
||||||
|
}
|
||||||
|
ft.update(parent, v, w, i, r)
|
||||||
|
|
||||||
|
// Additional facts we know given the relationship between len and cap.
|
||||||
|
if i != signed && i != unsigned {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v.Op == OpSliceLen && r< == 0 && ft.caps[v.Args[0].ID] != nil {
|
||||||
|
// len(s) > w implies cap(s) > w
|
||||||
|
// len(s) >= w implies cap(s) >= w
|
||||||
|
// len(s) == w implies cap(s) >= w
|
||||||
|
ft.update(parent, ft.caps[v.Args[0].ID], w, i, r|gt)
|
||||||
|
}
|
||||||
|
if w.Op == OpSliceLen && r> == 0 && ft.caps[w.Args[0].ID] != nil {
|
||||||
|
// same, length on the RHS.
|
||||||
|
ft.update(parent, v, ft.caps[w.Args[0].ID], i, r|lt)
|
||||||
|
}
|
||||||
|
if v.Op == OpSliceCap && r> == 0 && ft.lens[v.Args[0].ID] != nil {
|
||||||
|
// cap(s) < w implies len(s) < w
|
||||||
|
// cap(s) <= w implies len(s) <= w
|
||||||
|
// cap(s) == w implies len(s) <= w
|
||||||
|
ft.update(parent, ft.lens[v.Args[0].ID], w, i, r|lt)
|
||||||
|
}
|
||||||
|
if w.Op == OpSliceCap && r< == 0 && ft.lens[w.Args[0].ID] != nil {
|
||||||
|
// same, capacity on the RHS.
|
||||||
|
ft.update(parent, v, ft.lens[w.Args[0].ID], i, r|gt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ func f5(a []int) {
|
||||||
if len(a) > 5 {
|
if len(a) > 5 {
|
||||||
useInt(a[5])
|
useInt(a[5])
|
||||||
useSlice(a[6:])
|
useSlice(a[6:])
|
||||||
useSlice(a[:6]) // ERROR "Found IsSliceInBounds$"
|
useSlice(a[:6])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -451,6 +451,18 @@ func f14(p, q *int, a []int) {
|
||||||
useInt(a[i2+j]) // ERROR "Proved boolean IsInBounds$"
|
useInt(a[i2+j]) // ERROR "Proved boolean IsInBounds$"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func f15(s []int, x int) {
|
||||||
|
useSlice(s[x:])
|
||||||
|
useSlice(s[:x]) // ERROR "Proved IsSliceInBounds$"
|
||||||
|
}
|
||||||
|
|
||||||
|
func f16(s []int) []int {
|
||||||
|
if len(s) >= 10 {
|
||||||
|
return s[:10] // ERROR "Proved non-negative bounds IsSliceInBounds$"
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func useInt(a int) {
|
func useInt(a int) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue