mirror of
https://github.com/golang/go.git
synced 2026-06-27 19:30:52 +00:00
cmd/compile: add Lsh support to known bits
I've got a bit carried away with the exhaustive implementation. I have one private real world code where adding support for x << aConst to knownBits helps the compilation output. However the « you can just think about it like a phi of all the shift amounts » looked pretty easy to implement and it worked first try so here it is. Uniqued by LOC this adds 5 known bits hits when building the std. Updates #78633 Change-Id: I3c4cafe9907004296129a07b2d13086660747a3d Reviewed-on: https://go-review.googlesource.com/c/go/+/766042 Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Keith Randall <khr@google.com> Auto-Submit: Jorropo <jorropo.pgm@gmail.com> Reviewed-by: Carlos Amedee <carlos@golang.org>
This commit is contained in:
parent
9c0a8a2b46
commit
977041b065
2 changed files with 113 additions and 0 deletions
|
|
@ -117,6 +117,11 @@ func (kb *knownBitsState) fold(v *Value) (value, known int64) {
|
|||
srcSize := v.Args[0].Type.Size() * 8
|
||||
mask := int64(1<<srcSize - 1)
|
||||
return x & mask, k | ^mask
|
||||
case OpLsh8x8, OpLsh16x8, OpLsh32x8, OpLsh64x8,
|
||||
OpLsh8x16, OpLsh16x16, OpLsh32x16, OpLsh64x16,
|
||||
OpLsh8x32, OpLsh16x32, OpLsh32x32, OpLsh64x32,
|
||||
OpLsh8x64, OpLsh16x64, OpLsh32x64, OpLsh64x64:
|
||||
return kb.computeKnownBitsForLsh(v)
|
||||
default:
|
||||
return 0, 0
|
||||
}
|
||||
|
|
@ -219,3 +224,66 @@ func (kb *knownBitsState) isLiveOutEdge(b *Block, index uint) bool {
|
|||
panic("unreachable; unknown block kind")
|
||||
}
|
||||
}
|
||||
|
||||
// computeKnownBitsForLsh computes the known bits for a left shift.
|
||||
// Considering the following piece of code x = x << uint8(i)
|
||||
// The algorithm is based on two observations:
|
||||
//
|
||||
// 1. computing a shift of a lattice by a constant (i) is easy:
|
||||
// value, known = x<<i, xk<<i|(1<<i-1)
|
||||
// each point in the lattice is shifted by the constant, all new shifted in bits are known zeros.
|
||||
//
|
||||
// 2. x = uint8(x) << i is equivalent to
|
||||
//
|
||||
// switch i {
|
||||
// case 0: x0 = x << 0
|
||||
// case 1: x1 = x << 1
|
||||
// case 2: x2 = x << 2
|
||||
// case 3: x3 = x << 3
|
||||
// case 4: x4 = x << 4
|
||||
// case 5: x5 = x << 5
|
||||
// case 6: x6 = x << 6
|
||||
// case 7: x7 = x << 7
|
||||
// default: xd = x << 8
|
||||
// }
|
||||
// x = phi(x0, x1, x2, x3, x4, x5, x6, x7, xd)
|
||||
//
|
||||
// The algorithm below then models the phi in the equivalence above using same intersection algorithm phi uses.
|
||||
// We also leverage known bits of the shift amount to remove "branches" in the switch that are proved to be impossible.
|
||||
func (kb *knownBitsState) computeKnownBitsForLsh(v *Value) (value, known int64) {
|
||||
xSize := v.Args[0].Type.Size() * 8
|
||||
x, xk := kb.fold(v.Args[0])
|
||||
y, yk := kb.fold(v.Args[1])
|
||||
if uint64(y) >= uint64(xSize) {
|
||||
return 0, -1
|
||||
}
|
||||
|
||||
set := false
|
||||
if v.AuxInt == 0 && uint64(^yk) >= uint64(xSize) {
|
||||
// this implement the default case of the equivalent switch above.
|
||||
// if the shift isn't bounded and there are unknown bits above the shift size we might completely stomp all bits.
|
||||
value = 0
|
||||
known = -1
|
||||
set = true
|
||||
}
|
||||
yk &= xSize - 1
|
||||
|
||||
for i := range xSize {
|
||||
if i&yk != y {
|
||||
continue
|
||||
}
|
||||
a, k := x<<i, xk<<i|(1<<i-1)
|
||||
if !set {
|
||||
value, known = a, k
|
||||
set = true
|
||||
} else {
|
||||
known &^= value ^ a
|
||||
known &= k
|
||||
}
|
||||
if known == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return value & known, known
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,3 +249,48 @@ func unknownBitsSignExt(x int16) int32 {
|
|||
x |= -0b010101010101010
|
||||
return int32(x) & -1 << 12
|
||||
}
|
||||
|
||||
func knownBitsLsh(x, y uint32) uint32 {
|
||||
x |= 0b11110
|
||||
x &^= 0b100000
|
||||
y &= 2
|
||||
|
||||
// ???01111?
|
||||
// ?01111???
|
||||
// ---------
|
||||
// ????11???
|
||||
|
||||
return (x << y) & 0b11000 // ERROR "known value of v[0-9]+ \(And32\): 24$"
|
||||
}
|
||||
|
||||
func knownBitsLshZero(x, y uint64) uint64 {
|
||||
x &^= 2
|
||||
y &^= 2
|
||||
y |= 128
|
||||
|
||||
return (x << y) & 8 // ERROR "known value of v[0-9]+ \(And64\): 0$" "known value of v[0-9]+ \(Lsh64x[0-9]+\): 0$"
|
||||
}
|
||||
|
||||
func unknownBitsLshLeftSideMsb(x uint32, y uint32) uint32 {
|
||||
x |= 0b11110
|
||||
x &^= 0b100000
|
||||
y &= 2
|
||||
|
||||
return (x << y) & 0b111000
|
||||
}
|
||||
|
||||
func unknownBitsLshLeftSideLsb(x uint32, y uint32) uint32 {
|
||||
x |= 0b11110
|
||||
x &^= 0b100000
|
||||
y &= 2
|
||||
|
||||
return (x << y) & 0b011100
|
||||
}
|
||||
|
||||
func unknownBitsLshRightSide(x uint32, y uint32) uint32 {
|
||||
x |= 0b11110
|
||||
x &^= 0b100000
|
||||
y &= 6
|
||||
|
||||
return (x << y) & 0b11000
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue