mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: teach prove about min/max phi operations
If there is a phi that is computing the minimum of its two inputs, then we know the result of the phi is smaller than or equal to both of its inputs. Similarly for maxiumum (although max seems less useful). This pattern happens for the case n := copy(a, b) n is the minimum of len(a) and len(b), so with this optimization we know both n <= len(a) and n <= len(b). That extra information is helpful for subsequent slicing of a or b. Fixes #16833 Change-Id: Ib4238fd1edae0f2940f62a5516a6b363bbe7928c Reviewed-on: https://go-review.googlesource.com/c/go/+/622240 Reviewed-by: Carlos Amedee <carlos@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
f5526b56db
commit
4dcbb00be2
3 changed files with 128 additions and 0 deletions
|
|
@ -2026,10 +2026,87 @@ func addLocalFacts(ft *factsTable, b *Block) {
|
|||
if v.Args[0].Op == OpSliceMake {
|
||||
ft.update(b, v, v.Args[0].Args[2], signed, eq)
|
||||
}
|
||||
case OpPhi:
|
||||
addLocalFactsPhi(ft, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addLocalFactsPhi(ft *factsTable, v *Value) {
|
||||
// Look for phis that implement min/max.
|
||||
// z:
|
||||
// c = Less64 x y (or other Less/Leq operation)
|
||||
// If c -> bx by
|
||||
// bx: <- z
|
||||
// -> b ...
|
||||
// by: <- z
|
||||
// -> b ...
|
||||
// b: <- bx by
|
||||
// v = Phi x y
|
||||
// Then v is either min or max of x,y.
|
||||
// If it is the min, then we deduce v <= x && v <= y.
|
||||
// If it is the max, then we deduce v >= x && v >= y.
|
||||
// The min case is useful for the copy builtin, see issue 16833.
|
||||
if len(v.Args) != 2 {
|
||||
return
|
||||
}
|
||||
b := v.Block
|
||||
x := v.Args[0]
|
||||
y := v.Args[1]
|
||||
bx := b.Preds[0].b
|
||||
by := b.Preds[1].b
|
||||
var z *Block // branch point
|
||||
switch {
|
||||
case bx == by: // bx == by == z case
|
||||
z = bx
|
||||
case by.uniquePred() == bx: // bx == z case
|
||||
z = bx
|
||||
case bx.uniquePred() == by: // by == z case
|
||||
z = by
|
||||
case bx.uniquePred() == by.uniquePred():
|
||||
z = bx.uniquePred()
|
||||
}
|
||||
if z == nil || z.Kind != BlockIf {
|
||||
return
|
||||
}
|
||||
c := z.Controls[0]
|
||||
if len(c.Args) != 2 {
|
||||
return
|
||||
}
|
||||
var isMin bool // if c, a less-than comparison, is true, phi chooses x.
|
||||
if bx == z {
|
||||
isMin = b.Preds[0].i == 0
|
||||
} else {
|
||||
isMin = bx.Preds[0].i == 0
|
||||
}
|
||||
if c.Args[0] == x && c.Args[1] == y {
|
||||
// ok
|
||||
} else if c.Args[0] == y && c.Args[1] == x {
|
||||
// Comparison is reversed from how the values are listed in the Phi.
|
||||
isMin = !isMin
|
||||
} else {
|
||||
// Not comparing x and y.
|
||||
return
|
||||
}
|
||||
var dom domain
|
||||
switch c.Op {
|
||||
case OpLess64, OpLess32, OpLess16, OpLess8, OpLeq64, OpLeq32, OpLeq16, OpLeq8:
|
||||
dom = signed
|
||||
case OpLess64U, OpLess32U, OpLess16U, OpLess8U, OpLeq64U, OpLeq32U, OpLeq16U, OpLeq8U:
|
||||
dom = unsigned
|
||||
default:
|
||||
return
|
||||
}
|
||||
var rel relation
|
||||
if isMin {
|
||||
rel = lt | eq
|
||||
} else {
|
||||
rel = gt | eq
|
||||
}
|
||||
ft.update(b, v, x, dom, rel)
|
||||
ft.update(b, v, y, dom, rel)
|
||||
}
|
||||
|
||||
var ctzNonZeroOp = map[Op]Op{OpCtz8: OpCtz8NonZero, OpCtz16: OpCtz16NonZero, OpCtz32: OpCtz32NonZero, OpCtz64: OpCtz64NonZero}
|
||||
var mostNegativeDividend = map[Op]int64{
|
||||
OpDiv16: -1 << 15,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue