From: ruinan Date: Tue, 12 Jul 2022 04:05:41 +0000 (+0000) Subject: cmd/compile: get more bounds info from logic operators in prove pass X-Git-Tag: go1.21rc1~1006 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=9be533a8ee7673cbf16699d05517d9c6aebed297;p=gostls13.git 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 Run-TryBot: Eric Fang Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Heschi Kreinick --- diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index 550eb90bed..2ca2466086 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -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 diff --git a/test/codegen/comparisons.go b/test/codegen/comparisons.go index 99589c4ce8..6ffc73482a 100644 --- a/test/codegen/comparisons.go +++ b/test/codegen/comparisons.go @@ -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 { diff --git a/test/prove.go b/test/prove.go index 00bc0a315f..abc7bfaa21 100644 --- a/test/prove.go +++ b/test/prove.go @@ -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)