Skip to content

Commit 87e2b34

Browse files
committed
cmd/compile: in prove, learn facts from OpSliceMake
Now that OpSliceMake is called by runtime.makeslice callers, prove can see and record the actual length and cap of each slice being constructed. This small patch is enough to remove 260 additional bound checks from cmd+std. Thanks to Martin Möhrmann for pointing me to CL141822 that I had missed. Updates #24660 Change-Id: I14556850f285392051f3f07d13b456b608b64eb9 Reviewed-on: https://go-review.googlesource.com/c/go/+/196784 Run-TryBot: Giovanni Bajo <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent 0816593 commit 87e2b34

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,8 @@ func prove(f *Func) {
776776
ft := newFactsTable(f)
777777
ft.checkpoint()
778778

779+
var lensVars map[*Block][]*Value
780+
779781
// Find length and capacity ops.
780782
for _, b := range f.Blocks {
781783
for _, v := range b.Values {
@@ -793,12 +795,24 @@ func prove(f *Func) {
793795
}
794796
ft.lens[v.Args[0].ID] = v
795797
ft.update(b, v, ft.zero, signed, gt|eq)
798+
if v.Args[0].Op == OpSliceMake {
799+
if lensVars == nil {
800+
lensVars = make(map[*Block][]*Value)
801+
}
802+
lensVars[b] = append(lensVars[b], v)
803+
}
796804
case OpSliceCap:
797805
if ft.caps == nil {
798806
ft.caps = map[ID]*Value{}
799807
}
800808
ft.caps[v.Args[0].ID] = v
801809
ft.update(b, v, ft.zero, signed, gt|eq)
810+
if v.Args[0].Op == OpSliceMake {
811+
if lensVars == nil {
812+
lensVars = make(map[*Block][]*Value)
813+
}
814+
lensVars[b] = append(lensVars[b], v)
815+
}
802816
}
803817
}
804818
}
@@ -852,9 +866,22 @@ func prove(f *Func) {
852866
switch node.state {
853867
case descend:
854868
ft.checkpoint()
869+
870+
// Entering the block, add the block-depending facts that we collected
871+
// at the beginning: induction variables and lens/caps of slices.
855872
if iv, ok := indVars[node.block]; ok {
856873
addIndVarRestrictions(ft, parent, iv)
857874
}
875+
if lens, ok := lensVars[node.block]; ok {
876+
for _, v := range lens {
877+
switch v.Op {
878+
case OpSliceLen:
879+
ft.update(node.block, v, v.Args[0].Args[1], signed, eq)
880+
case OpSliceCap:
881+
ft.update(node.block, v, v.Args[0].Args[2], signed, eq)
882+
}
883+
}
884+
}
858885

859886
if branch != unknown {
860887
addBranchRestrictions(ft, parent, branch)

test/prove.go

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,30 @@ func oforuntil(b []int) {
678678
}
679679
}
680680

681+
func atexit(foobar []func()) {
682+
for i := len(foobar) - 1; i >= 0; i-- { // ERROR "Induction variable: limits \[0,\?\], increment 1"
683+
f := foobar[i]
684+
foobar = foobar[:i] // ERROR "IsSliceInBounds"
685+
f()
686+
}
687+
}
688+
689+
func make1(n int) []int {
690+
s := make([]int, n)
691+
for i := 0; i < n; i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1"
692+
s[i] = 1 // ERROR "Proved IsInBounds$"
693+
}
694+
return s
695+
}
696+
697+
func make2(n int) []int {
698+
s := make([]int, n)
699+
for i := range s { // ERROR "Induction variable: limits \[0,\?\), increment 1"
700+
s[i] = 1 // ERROR "Proved IsInBounds$"
701+
}
702+
return s
703+
}
704+
681705
// The range tests below test the index variable of range loops.
682706

683707
// range1 compiles to the "efficiently indexable" form of a range loop.
@@ -862,13 +886,13 @@ func signExtNto64(x []int, j8 int8, j16 int16, j32 int32) int {
862886
return 0
863887
}
864888
if j8 >= 0 && j8 < 22 {
865-
return x[j8] // ERROR "Proved IsInBounds$"
889+
return x[j8] // ERROR "Proved IsInBounds$"
866890
}
867891
if j16 >= 0 && j16 < 22 {
868-
return x[j16] // ERROR "Proved IsInBounds$"
892+
return x[j16] // ERROR "Proved IsInBounds$"
869893
}
870894
if j32 >= 0 && j32 < 22 {
871-
return x[j32] // ERROR "Proved IsInBounds$"
895+
return x[j32] // ERROR "Proved IsInBounds$"
872896
}
873897
return 0
874898
}
@@ -878,13 +902,13 @@ func zeroExtNto64(x []int, j8 uint8, j16 uint16, j32 uint32) int {
878902
return 0
879903
}
880904
if j8 >= 0 && j8 < 22 {
881-
return x[j8] // ERROR "Proved IsInBounds$"
905+
return x[j8] // ERROR "Proved IsInBounds$"
882906
}
883907
if j16 >= 0 && j16 < 22 {
884-
return x[j16] // ERROR "Proved IsInBounds$"
908+
return x[j16] // ERROR "Proved IsInBounds$"
885909
}
886910
if j32 >= 0 && j32 < 22 {
887-
return x[j32] // ERROR "Proved IsInBounds$"
911+
return x[j32] // ERROR "Proved IsInBounds$"
888912
}
889913
return 0
890914
}
@@ -894,7 +918,7 @@ func signExt32to64Fence(x []int, j int32) int {
894918
if x[j] != 0 {
895919
return 1
896920
}
897-
if j > 0 && x[j-1] != 0 { // ERROR "Proved IsInBounds$"
921+
if j > 0 && x[j-1] != 0 { // ERROR "Proved IsInBounds$"
898922
return 1
899923
}
900924
return 0
@@ -904,7 +928,7 @@ func zeroExt32to64Fence(x []int, j uint32) int {
904928
if x[j] != 0 {
905929
return 1
906930
}
907-
if j > 0 && x[j-1] != 0 { // ERROR "Proved IsInBounds$"
931+
if j > 0 && x[j-1] != 0 { // ERROR "Proved IsInBounds$"
908932
return 1
909933
}
910934
return 0

0 commit comments

Comments
 (0)