]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: in prove, learn facts from OpSliceMake
authorGiovanni Bajo <rasky@develer.com>
Sun, 22 Sep 2019 13:39:16 +0000 (15:39 +0200)
committerGiovanni Bajo <rasky@develer.com>
Thu, 26 Sep 2019 18:27:38 +0000 (18:27 +0000)
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 <rasky@develer.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/ssa/prove.go
test/prove.go

index 697862f986d334fcbf9af056bbf0f47b2bc8020e..014535c0a4e5ce3aad7bbebcbe1e99578a53b7e3 100644 (file)
@@ -776,6 +776,8 @@ func prove(f *Func) {
        ft := newFactsTable(f)
        ft.checkpoint()
 
+       var lensVars map[*Block][]*Value
+
        // Find length and capacity ops.
        for _, b := range f.Blocks {
                for _, v := range b.Values {
@@ -793,12 +795,24 @@ func prove(f *Func) {
                                }
                                ft.lens[v.Args[0].ID] = v
                                ft.update(b, v, ft.zero, signed, gt|eq)
+                               if v.Args[0].Op == OpSliceMake {
+                                       if lensVars == nil {
+                                               lensVars = make(map[*Block][]*Value)
+                                       }
+                                       lensVars[b] = append(lensVars[b], v)
+                               }
                        case OpSliceCap:
                                if ft.caps == nil {
                                        ft.caps = map[ID]*Value{}
                                }
                                ft.caps[v.Args[0].ID] = v
                                ft.update(b, v, ft.zero, signed, gt|eq)
+                               if v.Args[0].Op == OpSliceMake {
+                                       if lensVars == nil {
+                                               lensVars = make(map[*Block][]*Value)
+                                       }
+                                       lensVars[b] = append(lensVars[b], v)
+                               }
                        }
                }
        }
@@ -852,9 +866,22 @@ func prove(f *Func) {
                switch node.state {
                case descend:
                        ft.checkpoint()
+
+                       // Entering the block, add the block-depending facts that we collected
+                       // at the beginning: induction variables and lens/caps of slices.
                        if iv, ok := indVars[node.block]; ok {
                                addIndVarRestrictions(ft, parent, iv)
                        }
+                       if lens, ok := lensVars[node.block]; ok {
+                               for _, v := range lens {
+                                       switch v.Op {
+                                       case OpSliceLen:
+                                               ft.update(node.block, v, v.Args[0].Args[1], signed, eq)
+                                       case OpSliceCap:
+                                               ft.update(node.block, v, v.Args[0].Args[2], signed, eq)
+                                       }
+                               }
+                       }
 
                        if branch != unknown {
                                addBranchRestrictions(ft, parent, branch)
index 7643031c6220749a2af175e0ce02ab3e109f802a..6629982ba8558b5af7068c176bc28113a6f91ad8 100644 (file)
@@ -678,6 +678,30 @@ func oforuntil(b []int) {
        }
 }
 
+func atexit(foobar []func()) {
+       for i := len(foobar) - 1; i >= 0; i-- { // ERROR "Induction variable: limits \[0,\?\], increment 1"
+               f := foobar[i]
+               foobar = foobar[:i] // ERROR "IsSliceInBounds"
+               f()
+       }
+}
+
+func make1(n int) []int {
+       s := make([]int, n)
+       for i := 0; i < n; i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1"
+               s[i] = 1 // ERROR "Proved IsInBounds$"
+       }
+       return s
+}
+
+func make2(n int) []int {
+       s := make([]int, n)
+       for i := range s { // ERROR "Induction variable: limits \[0,\?\), increment 1"
+               s[i] = 1 // ERROR "Proved IsInBounds$"
+       }
+       return s
+}
+
 // The range tests below test the index variable of range loops.
 
 // 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 {
                return 0
        }
        if j8 >= 0 && j8 < 22 {
-               return x[j8]  // ERROR "Proved IsInBounds$"
+               return x[j8] // ERROR "Proved IsInBounds$"
        }
        if j16 >= 0 && j16 < 22 {
-               return x[j16]  // ERROR "Proved IsInBounds$"
+               return x[j16] // ERROR "Proved IsInBounds$"
        }
        if j32 >= 0 && j32 < 22 {
-               return x[j32]  // ERROR "Proved IsInBounds$"
+               return x[j32] // ERROR "Proved IsInBounds$"
        }
        return 0
 }
@@ -878,13 +902,13 @@ func zeroExtNto64(x []int, j8 uint8, j16 uint16, j32 uint32) int {
                return 0
        }
        if j8 >= 0 && j8 < 22 {
-               return x[j8]  // ERROR "Proved IsInBounds$"
+               return x[j8] // ERROR "Proved IsInBounds$"
        }
        if j16 >= 0 && j16 < 22 {
-               return x[j16]  // ERROR "Proved IsInBounds$"
+               return x[j16] // ERROR "Proved IsInBounds$"
        }
        if j32 >= 0 && j32 < 22 {
-               return x[j32]  // ERROR "Proved IsInBounds$"
+               return x[j32] // ERROR "Proved IsInBounds$"
        }
        return 0
 }
@@ -894,7 +918,7 @@ func signExt32to64Fence(x []int, j int32) int {
        if x[j] != 0 {
                return 1
        }
-       if j > 0 && x[j-1] != 0 {  // ERROR "Proved IsInBounds$"
+       if j > 0 && x[j-1] != 0 { // ERROR "Proved IsInBounds$"
                return 1
        }
        return 0
@@ -904,7 +928,7 @@ func zeroExt32to64Fence(x []int, j uint32) int {
        if x[j] != 0 {
                return 1
        }
-       if j > 0 && x[j-1] != 0 {  // ERROR "Proved IsInBounds$"
+       if j > 0 && x[j-1] != 0 { // ERROR "Proved IsInBounds$"
                return 1
        }
        return 0