cmd/compile: get more bounds info from logic operators in prove pass

Currently, the prove pass can get knowledge from some specific logic
operators only before the CFG is explored, which means that the bounds
information of the branch will be ignored.

This CL updates the facts table by the logic operators in every
branch. Combined with the branch information, this will be helpful for
BCE in some circumstances.

Fixes #57243

Change-Id: I0bd164f1b47804ccfc37879abe9788740b016fd5
Reviewed-on: https://go-review.googlesource.com/c/go/+/419555
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Eric Fang <eric.fang@arm.com>
Reviewed-by: Keith Randall <khr@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
ruinan 2022-07-12 04:05:41 +00:00 committed by Eric Fang
parent 4f4a9c7fff
commit 9be533a8ee
3 changed files with 46 additions and 1 deletions

View file

@ -802,6 +802,7 @@ func prove(f *Func) {
ft.checkpoint()
var lensVars map[*Block][]*Value
var logicVars map[*Block][]*Value
// Find length and capacity ops.
for _, b := range f.Blocks {
@ -856,6 +857,16 @@ func prove(f *Func) {
case OpAnd64, OpAnd32, OpAnd16, OpAnd8:
ft.update(b, v, v.Args[1], unsigned, lt|eq)
ft.update(b, v, v.Args[0], unsigned, lt|eq)
for i := 0; i < 2; i++ {
if isNonNegative(v.Args[i]) {
ft.update(b, v, v.Args[i], signed, lt|eq)
ft.update(b, v, ft.zero, signed, gt|eq)
}
}
if logicVars == nil {
logicVars = make(map[*Block][]*Value)
}
logicVars[b] = append(logicVars[b], v)
case OpDiv64u, OpDiv32u, OpDiv16u, OpDiv8u,
OpRsh8Ux64, OpRsh8Ux32, OpRsh8Ux16, OpRsh8Ux8,
OpRsh16Ux64, OpRsh16Ux32, OpRsh16Ux16, OpRsh16Ux8,
@ -982,6 +993,21 @@ func prove(f *Func) {
if branch != unknown {
addBranchRestrictions(ft, parent, branch)
// After we add the branch restriction, re-check the logic operations in the parent block,
// it may give us more info to omit some branches
if logic, ok := logicVars[parent]; ok {
for _, v := range logic {
// we only have OpAnd for now
ft.update(parent, v, v.Args[1], unsigned, lt|eq)
ft.update(parent, v, v.Args[0], unsigned, lt|eq)
for i := 0; i < 2; i++ {
if isNonNegative(v.Args[i]) {
ft.update(parent, v, v.Args[i], signed, lt|eq)
ft.update(parent, v, ft.zero, signed, gt|eq)
}
}
}
}
if ft.unsat {
// node.block is unreachable.
// Remove it and don't visit

View file

@ -235,7 +235,7 @@ func CmpToZero(a, b, d int32, e, f int64, deOptC0, deOptC1 bool) int32 {
// arm64:`CMN\sR[0-9]+<<3,\sR[0-9]+`
c8 := e+(f<<3) < 0
// arm64:`TST\sR[0-9],\sR[0-9]+`
c9 := e&17 < 0
c9 := e&(-19) < 0
if c0 {
return 1
} else if c1 {

View file

@ -1038,6 +1038,25 @@ func divShiftClean32(n int32) int32 {
return n / int32(16) // ERROR "Proved Rsh32x64 shifts to zero"
}
// Bounds check elimination
func sliceBCE1(p []string, h uint) string {
if len(p) == 0 {
return ""
}
i := h & uint(len(p)-1)
return p[i] // ERROR "Proved IsInBounds$"
}
func sliceBCE2(p []string, h int) string {
if len(p) == 0 {
return ""
}
i := h & (len(p) - 1)
return p[i] // ERROR "Proved IsInBounds$"
}
func and(p []byte) ([]byte, []byte) { // issue #52563
const blocksize = 16
fullBlocks := len(p) &^ (blocksize - 1)