mirror of
https://github.com/golang/go
synced 2024-10-02 22:25:08 +00:00
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.
|
||||
limits map[ID]limit
|
||||
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
|
||||
|
@ -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.
|
||||
// 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).
|
||||
// The code for the second panic can be removed.
|
||||
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
|
||||
type walkState int
|
||||
const (
|
||||
|
@ -472,7 +504,6 @@ func prove(f *Func) {
|
|||
state: descend,
|
||||
})
|
||||
|
||||
ft := newFactsTable()
|
||||
idom := f.Idom()
|
||||
sdom := f.sdom()
|
||||
|
||||
|
@ -559,8 +590,34 @@ func updateRestrictions(parent *Block, ft *factsTable, t domain, v, w *Value, r
|
|||
r = (lt | eq | gt) ^ r
|
||||
}
|
||||
for i := domain(1); i <= t; i <<= 1 {
|
||||
if t&i != 0 {
|
||||
ft.update(parent, v, w, i, r)
|
||||
if t&i == 0 {
|
||||
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 {
|
||||
useInt(a[5])
|
||||
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$"
|
||||
}
|
||||
|
||||
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
|
||||
func useInt(a int) {
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue