mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
cmd/compile: move limit fact table in prove pass to dense encoding
Here begins a pretty major rewrite of the prove pass. The fundamental observation is that although keeping facts about relations between two SSA values could use O(n^2) space, keeping facts about relations between an SSA value and constants needs only O(n) space. We can just keep track of min/max for every SSA value at little cost. Redo the limit table to just keep track of limits for all SSA values. Use just a slice instead of a map. It may use more space (but still just O(n) space), but accesses are a lot faster. And with the cache in the compiler, that space will be reused quickly. This is part of my planning to add lots more constant limits in the prove pass. Change-Id: Ie36819fad5631a8b79c3630fe0e819521796551a Reviewed-on: https://go-review.googlesource.com/c/go/+/599255 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
parent
e705a2d16e
commit
553443d41f
@ -46,14 +46,14 @@ func genAllocators() {
|
|||||||
maxLog: 32,
|
maxLog: 32,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Int64Slice",
|
name: "LimitSlice",
|
||||||
typ: "[]int64",
|
typ: "[]limit", // the limit type is basically [4]uint64.
|
||||||
capacity: "cap(%s)",
|
capacity: "cap(%s)",
|
||||||
mak: "make([]int64, %s)",
|
mak: "make([]limit, %s)",
|
||||||
resize: "%s[:%s]",
|
resize: "%s[:%s]",
|
||||||
clear: "for i := range %[1]s {\n%[1]s[i] = 0\n}",
|
clear: "for i := range %[1]s {\n%[1]s[i] = limit{}\n}",
|
||||||
minLog: 5,
|
minLog: 3,
|
||||||
maxLog: 32,
|
maxLog: 30,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SparseSet",
|
name: "SparseSet",
|
||||||
@ -92,30 +92,35 @@ func genAllocators() {
|
|||||||
typ: "[]*Block",
|
typ: "[]*Block",
|
||||||
base: "ValueSlice",
|
base: "ValueSlice",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Int64",
|
||||||
|
typ: "[]int64",
|
||||||
|
base: "LimitSlice",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "IntSlice",
|
name: "IntSlice",
|
||||||
typ: "[]int",
|
typ: "[]int",
|
||||||
base: "Int64Slice",
|
base: "LimitSlice",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Int32Slice",
|
name: "Int32Slice",
|
||||||
typ: "[]int32",
|
typ: "[]int32",
|
||||||
base: "Int64Slice",
|
base: "LimitSlice",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Int8Slice",
|
name: "Int8Slice",
|
||||||
typ: "[]int8",
|
typ: "[]int8",
|
||||||
base: "Int64Slice",
|
base: "LimitSlice",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "BoolSlice",
|
name: "BoolSlice",
|
||||||
typ: "[]bool",
|
typ: "[]bool",
|
||||||
base: "Int64Slice",
|
base: "LimitSlice",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "IDSlice",
|
name: "IDSlice",
|
||||||
typ: "[]ID",
|
typ: "[]ID",
|
||||||
base: "Int64Slice",
|
base: "LimitSlice",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,42 +47,42 @@ func (c *Cache) freeValueSlice(s []*Value) {
|
|||||||
poolFreeValueSlice[b-5].Put(sp)
|
poolFreeValueSlice[b-5].Put(sp)
|
||||||
}
|
}
|
||||||
|
|
||||||
var poolFreeInt64Slice [27]sync.Pool
|
var poolFreeLimitSlice [27]sync.Pool
|
||||||
|
|
||||||
func (c *Cache) allocInt64Slice(n int) []int64 {
|
func (c *Cache) allocLimitSlice(n int) []limit {
|
||||||
var s []int64
|
var s []limit
|
||||||
n2 := n
|
n2 := n
|
||||||
if n2 < 32 {
|
if n2 < 8 {
|
||||||
n2 = 32
|
n2 = 8
|
||||||
}
|
}
|
||||||
b := bits.Len(uint(n2 - 1))
|
b := bits.Len(uint(n2 - 1))
|
||||||
v := poolFreeInt64Slice[b-5].Get()
|
v := poolFreeLimitSlice[b-3].Get()
|
||||||
if v == nil {
|
if v == nil {
|
||||||
s = make([]int64, 1<<b)
|
s = make([]limit, 1<<b)
|
||||||
} else {
|
} else {
|
||||||
sp := v.(*[]int64)
|
sp := v.(*[]limit)
|
||||||
s = *sp
|
s = *sp
|
||||||
*sp = nil
|
*sp = nil
|
||||||
c.hdrInt64Slice = append(c.hdrInt64Slice, sp)
|
c.hdrLimitSlice = append(c.hdrLimitSlice, sp)
|
||||||
}
|
}
|
||||||
s = s[:n]
|
s = s[:n]
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (c *Cache) freeInt64Slice(s []int64) {
|
func (c *Cache) freeLimitSlice(s []limit) {
|
||||||
for i := range s {
|
for i := range s {
|
||||||
s[i] = 0
|
s[i] = limit{}
|
||||||
}
|
}
|
||||||
b := bits.Len(uint(cap(s)) - 1)
|
b := bits.Len(uint(cap(s)) - 1)
|
||||||
var sp *[]int64
|
var sp *[]limit
|
||||||
if len(c.hdrInt64Slice) == 0 {
|
if len(c.hdrLimitSlice) == 0 {
|
||||||
sp = new([]int64)
|
sp = new([]limit)
|
||||||
} else {
|
} else {
|
||||||
sp = c.hdrInt64Slice[len(c.hdrInt64Slice)-1]
|
sp = c.hdrLimitSlice[len(c.hdrLimitSlice)-1]
|
||||||
c.hdrInt64Slice[len(c.hdrInt64Slice)-1] = nil
|
c.hdrLimitSlice[len(c.hdrLimitSlice)-1] = nil
|
||||||
c.hdrInt64Slice = c.hdrInt64Slice[:len(c.hdrInt64Slice)-1]
|
c.hdrLimitSlice = c.hdrLimitSlice[:len(c.hdrLimitSlice)-1]
|
||||||
}
|
}
|
||||||
*sp = s
|
*sp = s
|
||||||
poolFreeInt64Slice[b-5].Put(sp)
|
poolFreeLimitSlice[b-3].Put(sp)
|
||||||
}
|
}
|
||||||
|
|
||||||
var poolFreeSparseSet [27]sync.Pool
|
var poolFreeSparseSet [27]sync.Pool
|
||||||
@ -179,14 +179,40 @@ func (c *Cache) freeBlockSlice(s []*Block) {
|
|||||||
}
|
}
|
||||||
c.freeValueSlice(*(*[]*Value)(unsafe.Pointer(&b)))
|
c.freeValueSlice(*(*[]*Value)(unsafe.Pointer(&b)))
|
||||||
}
|
}
|
||||||
|
func (c *Cache) allocInt64(n int) []int64 {
|
||||||
|
var base limit
|
||||||
|
var derived int64
|
||||||
|
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
||||||
|
panic("bad")
|
||||||
|
}
|
||||||
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
|
b := c.allocLimitSlice(int((uintptr(n) + scale - 1) / scale))
|
||||||
|
s := unsafeheader.Slice{
|
||||||
|
Data: unsafe.Pointer(&b[0]),
|
||||||
|
Len: n,
|
||||||
|
Cap: cap(b) * int(scale),
|
||||||
|
}
|
||||||
|
return *(*[]int64)(unsafe.Pointer(&s))
|
||||||
|
}
|
||||||
|
func (c *Cache) freeInt64(s []int64) {
|
||||||
|
var base limit
|
||||||
|
var derived int64
|
||||||
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
|
b := unsafeheader.Slice{
|
||||||
|
Data: unsafe.Pointer(&s[0]),
|
||||||
|
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
||||||
|
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
||||||
|
}
|
||||||
|
c.freeLimitSlice(*(*[]limit)(unsafe.Pointer(&b)))
|
||||||
|
}
|
||||||
func (c *Cache) allocIntSlice(n int) []int {
|
func (c *Cache) allocIntSlice(n int) []int {
|
||||||
var base int64
|
var base limit
|
||||||
var derived int
|
var derived int
|
||||||
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
||||||
panic("bad")
|
panic("bad")
|
||||||
}
|
}
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := c.allocInt64Slice(int((uintptr(n) + scale - 1) / scale))
|
b := c.allocLimitSlice(int((uintptr(n) + scale - 1) / scale))
|
||||||
s := unsafeheader.Slice{
|
s := unsafeheader.Slice{
|
||||||
Data: unsafe.Pointer(&b[0]),
|
Data: unsafe.Pointer(&b[0]),
|
||||||
Len: n,
|
Len: n,
|
||||||
@ -195,7 +221,7 @@ func (c *Cache) allocIntSlice(n int) []int {
|
|||||||
return *(*[]int)(unsafe.Pointer(&s))
|
return *(*[]int)(unsafe.Pointer(&s))
|
||||||
}
|
}
|
||||||
func (c *Cache) freeIntSlice(s []int) {
|
func (c *Cache) freeIntSlice(s []int) {
|
||||||
var base int64
|
var base limit
|
||||||
var derived int
|
var derived int
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := unsafeheader.Slice{
|
b := unsafeheader.Slice{
|
||||||
@ -203,16 +229,16 @@ func (c *Cache) freeIntSlice(s []int) {
|
|||||||
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
||||||
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
||||||
}
|
}
|
||||||
c.freeInt64Slice(*(*[]int64)(unsafe.Pointer(&b)))
|
c.freeLimitSlice(*(*[]limit)(unsafe.Pointer(&b)))
|
||||||
}
|
}
|
||||||
func (c *Cache) allocInt32Slice(n int) []int32 {
|
func (c *Cache) allocInt32Slice(n int) []int32 {
|
||||||
var base int64
|
var base limit
|
||||||
var derived int32
|
var derived int32
|
||||||
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
||||||
panic("bad")
|
panic("bad")
|
||||||
}
|
}
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := c.allocInt64Slice(int((uintptr(n) + scale - 1) / scale))
|
b := c.allocLimitSlice(int((uintptr(n) + scale - 1) / scale))
|
||||||
s := unsafeheader.Slice{
|
s := unsafeheader.Slice{
|
||||||
Data: unsafe.Pointer(&b[0]),
|
Data: unsafe.Pointer(&b[0]),
|
||||||
Len: n,
|
Len: n,
|
||||||
@ -221,7 +247,7 @@ func (c *Cache) allocInt32Slice(n int) []int32 {
|
|||||||
return *(*[]int32)(unsafe.Pointer(&s))
|
return *(*[]int32)(unsafe.Pointer(&s))
|
||||||
}
|
}
|
||||||
func (c *Cache) freeInt32Slice(s []int32) {
|
func (c *Cache) freeInt32Slice(s []int32) {
|
||||||
var base int64
|
var base limit
|
||||||
var derived int32
|
var derived int32
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := unsafeheader.Slice{
|
b := unsafeheader.Slice{
|
||||||
@ -229,16 +255,16 @@ func (c *Cache) freeInt32Slice(s []int32) {
|
|||||||
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
||||||
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
||||||
}
|
}
|
||||||
c.freeInt64Slice(*(*[]int64)(unsafe.Pointer(&b)))
|
c.freeLimitSlice(*(*[]limit)(unsafe.Pointer(&b)))
|
||||||
}
|
}
|
||||||
func (c *Cache) allocInt8Slice(n int) []int8 {
|
func (c *Cache) allocInt8Slice(n int) []int8 {
|
||||||
var base int64
|
var base limit
|
||||||
var derived int8
|
var derived int8
|
||||||
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
||||||
panic("bad")
|
panic("bad")
|
||||||
}
|
}
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := c.allocInt64Slice(int((uintptr(n) + scale - 1) / scale))
|
b := c.allocLimitSlice(int((uintptr(n) + scale - 1) / scale))
|
||||||
s := unsafeheader.Slice{
|
s := unsafeheader.Slice{
|
||||||
Data: unsafe.Pointer(&b[0]),
|
Data: unsafe.Pointer(&b[0]),
|
||||||
Len: n,
|
Len: n,
|
||||||
@ -247,7 +273,7 @@ func (c *Cache) allocInt8Slice(n int) []int8 {
|
|||||||
return *(*[]int8)(unsafe.Pointer(&s))
|
return *(*[]int8)(unsafe.Pointer(&s))
|
||||||
}
|
}
|
||||||
func (c *Cache) freeInt8Slice(s []int8) {
|
func (c *Cache) freeInt8Slice(s []int8) {
|
||||||
var base int64
|
var base limit
|
||||||
var derived int8
|
var derived int8
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := unsafeheader.Slice{
|
b := unsafeheader.Slice{
|
||||||
@ -255,16 +281,16 @@ func (c *Cache) freeInt8Slice(s []int8) {
|
|||||||
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
||||||
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
||||||
}
|
}
|
||||||
c.freeInt64Slice(*(*[]int64)(unsafe.Pointer(&b)))
|
c.freeLimitSlice(*(*[]limit)(unsafe.Pointer(&b)))
|
||||||
}
|
}
|
||||||
func (c *Cache) allocBoolSlice(n int) []bool {
|
func (c *Cache) allocBoolSlice(n int) []bool {
|
||||||
var base int64
|
var base limit
|
||||||
var derived bool
|
var derived bool
|
||||||
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
||||||
panic("bad")
|
panic("bad")
|
||||||
}
|
}
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := c.allocInt64Slice(int((uintptr(n) + scale - 1) / scale))
|
b := c.allocLimitSlice(int((uintptr(n) + scale - 1) / scale))
|
||||||
s := unsafeheader.Slice{
|
s := unsafeheader.Slice{
|
||||||
Data: unsafe.Pointer(&b[0]),
|
Data: unsafe.Pointer(&b[0]),
|
||||||
Len: n,
|
Len: n,
|
||||||
@ -273,7 +299,7 @@ func (c *Cache) allocBoolSlice(n int) []bool {
|
|||||||
return *(*[]bool)(unsafe.Pointer(&s))
|
return *(*[]bool)(unsafe.Pointer(&s))
|
||||||
}
|
}
|
||||||
func (c *Cache) freeBoolSlice(s []bool) {
|
func (c *Cache) freeBoolSlice(s []bool) {
|
||||||
var base int64
|
var base limit
|
||||||
var derived bool
|
var derived bool
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := unsafeheader.Slice{
|
b := unsafeheader.Slice{
|
||||||
@ -281,16 +307,16 @@ func (c *Cache) freeBoolSlice(s []bool) {
|
|||||||
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
||||||
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
||||||
}
|
}
|
||||||
c.freeInt64Slice(*(*[]int64)(unsafe.Pointer(&b)))
|
c.freeLimitSlice(*(*[]limit)(unsafe.Pointer(&b)))
|
||||||
}
|
}
|
||||||
func (c *Cache) allocIDSlice(n int) []ID {
|
func (c *Cache) allocIDSlice(n int) []ID {
|
||||||
var base int64
|
var base limit
|
||||||
var derived ID
|
var derived ID
|
||||||
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
if unsafe.Sizeof(base)%unsafe.Sizeof(derived) != 0 {
|
||||||
panic("bad")
|
panic("bad")
|
||||||
}
|
}
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := c.allocInt64Slice(int((uintptr(n) + scale - 1) / scale))
|
b := c.allocLimitSlice(int((uintptr(n) + scale - 1) / scale))
|
||||||
s := unsafeheader.Slice{
|
s := unsafeheader.Slice{
|
||||||
Data: unsafe.Pointer(&b[0]),
|
Data: unsafe.Pointer(&b[0]),
|
||||||
Len: n,
|
Len: n,
|
||||||
@ -299,7 +325,7 @@ func (c *Cache) allocIDSlice(n int) []ID {
|
|||||||
return *(*[]ID)(unsafe.Pointer(&s))
|
return *(*[]ID)(unsafe.Pointer(&s))
|
||||||
}
|
}
|
||||||
func (c *Cache) freeIDSlice(s []ID) {
|
func (c *Cache) freeIDSlice(s []ID) {
|
||||||
var base int64
|
var base limit
|
||||||
var derived ID
|
var derived ID
|
||||||
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
scale := unsafe.Sizeof(base) / unsafe.Sizeof(derived)
|
||||||
b := unsafeheader.Slice{
|
b := unsafeheader.Slice{
|
||||||
@ -307,5 +333,5 @@ func (c *Cache) freeIDSlice(s []ID) {
|
|||||||
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
Len: int((uintptr(len(s)) + scale - 1) / scale),
|
||||||
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
Cap: int((uintptr(cap(s)) + scale - 1) / scale),
|
||||||
}
|
}
|
||||||
c.freeInt64Slice(*(*[]int64)(unsafe.Pointer(&b)))
|
c.freeLimitSlice(*(*[]limit)(unsafe.Pointer(&b)))
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ type Cache struct {
|
|||||||
// Free "headers" for use by the allocators in allocators.go.
|
// Free "headers" for use by the allocators in allocators.go.
|
||||||
// Used to put slices in sync.Pools without allocation.
|
// Used to put slices in sync.Pools without allocation.
|
||||||
hdrValueSlice []*[]*Value
|
hdrValueSlice []*[]*Value
|
||||||
hdrInt64Slice []*[]int64
|
hdrLimitSlice []*[]limit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Reset() {
|
func (c *Cache) Reset() {
|
||||||
|
@ -173,7 +173,7 @@ type factsTable struct {
|
|||||||
orderU *poset
|
orderU *poset
|
||||||
|
|
||||||
// known lower and upper bounds on individual values.
|
// known lower and upper bounds on individual values.
|
||||||
limits map[ID]limit
|
limits []limit // indexed by value ID
|
||||||
limitStack []limitFact // previous entries
|
limitStack []limitFact // previous entries
|
||||||
|
|
||||||
// For each slice s, a map from s to a len(s)/cap(s) value (if any)
|
// For each slice s, a map from s to a len(s)/cap(s) value (if any)
|
||||||
@ -199,9 +199,12 @@ func newFactsTable(f *Func) *factsTable {
|
|||||||
ft.orderU.SetUnsigned(true)
|
ft.orderU.SetUnsigned(true)
|
||||||
ft.facts = make(map[pair]relation)
|
ft.facts = make(map[pair]relation)
|
||||||
ft.stack = make([]fact, 4)
|
ft.stack = make([]fact, 4)
|
||||||
ft.limits = make(map[ID]limit)
|
ft.limits = f.Cache.allocLimitSlice(f.NumValues())
|
||||||
ft.limitStack = make([]limitFact, 4)
|
ft.limitStack = make([]limitFact, 4)
|
||||||
ft.zero = f.ConstInt64(f.Config.Types.Int64, 0)
|
ft.zero = f.ConstInt64(f.Config.Types.Int64, 0)
|
||||||
|
for i := range ft.limits {
|
||||||
|
ft.limits[i] = noLimit
|
||||||
|
}
|
||||||
return ft
|
return ft
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,9 +299,8 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
|
|||||||
// In fact if there is overflow/underflow, the corresponding
|
// In fact if there is overflow/underflow, the corresponding
|
||||||
// code is unreachable because the known range is outside the range
|
// code is unreachable because the known range is outside the range
|
||||||
// of the value's type.
|
// of the value's type.
|
||||||
old, ok := ft.limits[v.ID]
|
old := ft.limits[v.ID]
|
||||||
if !ok {
|
if old == noLimit {
|
||||||
old = noLimit
|
|
||||||
if v.isGenericIntConst() {
|
if v.isGenericIntConst() {
|
||||||
switch d {
|
switch d {
|
||||||
case signed:
|
case signed:
|
||||||
@ -444,14 +446,14 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
|
|||||||
// x-1 >= w && x > min ⇒ x > w
|
// x-1 >= w && x > min ⇒ x > w
|
||||||
//
|
//
|
||||||
// Useful for i > 0; s[i-1].
|
// Useful for i > 0; s[i-1].
|
||||||
lim, ok := ft.limits[x.ID]
|
lim := ft.limits[x.ID]
|
||||||
if ok && ((d == signed && lim.min > opMin[v.Op]) || (d == unsigned && lim.umin > 0)) {
|
if lim != noLimit && ((d == signed && lim.min > opMin[v.Op]) || (d == unsigned && lim.umin > 0)) {
|
||||||
ft.update(parent, x, w, d, gt)
|
ft.update(parent, x, w, d, gt)
|
||||||
}
|
}
|
||||||
} else if x, delta := isConstDelta(w); x != nil && delta == 1 {
|
} else if x, delta := isConstDelta(w); x != nil && delta == 1 {
|
||||||
// v >= x+1 && x < max ⇒ v > x
|
// v >= x+1 && x < max ⇒ v > x
|
||||||
lim, ok := ft.limits[x.ID]
|
lim := ft.limits[x.ID]
|
||||||
if ok && ((d == signed && lim.max < opMax[w.Op]) || (d == unsigned && lim.umax < opUMax[w.Op])) {
|
if lim != noLimit && ((d == signed && lim.max < opMax[w.Op]) || (d == unsigned && lim.umax < opUMax[w.Op])) {
|
||||||
ft.update(parent, v, x, d, gt)
|
ft.update(parent, v, x, d, gt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,7 +467,7 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
|
|||||||
parent.Func.Warnl(parent.Pos, "x+d %s w; x:%v %v delta:%v w:%v d:%v", r, x, parent.String(), delta, w.AuxInt, d)
|
parent.Func.Warnl(parent.Pos, "x+d %s w; x:%v %v delta:%v w:%v d:%v", r, x, parent.String(), delta, w.AuxInt, d)
|
||||||
}
|
}
|
||||||
underflow := true
|
underflow := true
|
||||||
if l, has := ft.limits[x.ID]; has && delta < 0 {
|
if l := ft.limits[x.ID]; l != noLimit && delta < 0 {
|
||||||
if (x.Type.Size() == 8 && l.min >= math.MinInt64-delta) ||
|
if (x.Type.Size() == 8 && l.min >= math.MinInt64-delta) ||
|
||||||
(x.Type.Size() == 4 && l.min >= math.MinInt32-delta) {
|
(x.Type.Size() == 4 && l.min >= math.MinInt32-delta) {
|
||||||
underflow = false
|
underflow = false
|
||||||
@ -543,7 +545,7 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
|
|||||||
// We know that either x>min OR x<=max. factsTable cannot record OR conditions,
|
// We know that either x>min OR x<=max. factsTable cannot record OR conditions,
|
||||||
// so let's see if we can already prove that one of them is false, in which case
|
// so let's see if we can already prove that one of them is false, in which case
|
||||||
// the other must be true
|
// the other must be true
|
||||||
if l, has := ft.limits[x.ID]; has {
|
if l := ft.limits[x.ID]; l != noLimit {
|
||||||
if l.max <= min {
|
if l.max <= min {
|
||||||
if r&eq == 0 || l.max < min {
|
if r&eq == 0 || l.max < min {
|
||||||
// x>min (x>=min) is impossible, so it must be x<=max
|
// x>min (x>=min) is impossible, so it must be x<=max
|
||||||
@ -617,13 +619,13 @@ func (ft *factsTable) isNonNegative(v *Value) bool {
|
|||||||
|
|
||||||
// Check if the recorded limits can prove that the value is positive
|
// Check if the recorded limits can prove that the value is positive
|
||||||
|
|
||||||
if l, has := ft.limits[v.ID]; has && (l.min >= 0 || l.umax <= uint64(max)) {
|
if l := ft.limits[v.ID]; l != noLimit && (l.min >= 0 || l.umax <= uint64(max)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if v = x+delta, and we can use x's limits to prove that it's positive
|
// Check if v = x+delta, and we can use x's limits to prove that it's positive
|
||||||
if x, delta := isConstDelta(v); x != nil {
|
if x, delta := isConstDelta(v); x != nil {
|
||||||
if l, has := ft.limits[x.ID]; has {
|
if l := ft.limits[x.ID]; l != noLimit {
|
||||||
if delta > 0 && l.min >= -delta && l.max <= max-delta {
|
if delta > 0 && l.min >= -delta && l.max <= max-delta {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -681,11 +683,7 @@ func (ft *factsTable) restore() {
|
|||||||
if old.vid == 0 { // checkpointBound
|
if old.vid == 0 { // checkpointBound
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if old.limit == noLimit {
|
ft.limits[old.vid] = old.limit
|
||||||
delete(ft.limits, old.vid)
|
|
||||||
} else {
|
|
||||||
ft.limits[old.vid] = old.limit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ft.orderS.Undo()
|
ft.orderS.Undo()
|
||||||
ft.orderU.Undo()
|
ft.orderU.Undo()
|
||||||
@ -765,6 +763,7 @@ func (ft *factsTable) cleanup(f *Func) {
|
|||||||
}
|
}
|
||||||
f.retPoset(po)
|
f.retPoset(po)
|
||||||
}
|
}
|
||||||
|
f.Cache.freeLimitSlice(ft.limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
// prove removes redundant BlockIf branches that can be inferred
|
// prove removes redundant BlockIf branches that can be inferred
|
||||||
@ -1261,10 +1260,7 @@ func addBranchRestrictions(ft *factsTable, b *Block, br branch) {
|
|||||||
c = v
|
c = v
|
||||||
val -= off
|
val -= off
|
||||||
}
|
}
|
||||||
old, ok := ft.limits[c.ID]
|
old := ft.limits[c.ID]
|
||||||
if !ok {
|
|
||||||
old = noLimit
|
|
||||||
}
|
|
||||||
ft.limitStack = append(ft.limitStack, limitFact{c.ID, old})
|
ft.limitStack = append(ft.limitStack, limitFact{c.ID, old})
|
||||||
if val < old.min || val > old.max || uint64(val) < old.umin || uint64(val) > old.umax {
|
if val < old.min || val > old.max || uint64(val) < old.umin || uint64(val) > old.umax {
|
||||||
ft.unsat = true
|
ft.unsat = true
|
||||||
@ -1473,8 +1469,8 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
|
|||||||
}
|
}
|
||||||
// slicemask(x + y)
|
// slicemask(x + y)
|
||||||
// if x is larger than -y (y is negative), then slicemask is -1.
|
// if x is larger than -y (y is negative), then slicemask is -1.
|
||||||
lim, ok := ft.limits[x.ID]
|
lim := ft.limits[x.ID]
|
||||||
if !ok {
|
if lim == noLimit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if lim.umin > uint64(-delta) {
|
if lim.umin > uint64(-delta) {
|
||||||
@ -1493,8 +1489,8 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
|
|||||||
// code for CtzNN if we know that the argument is non-zero.
|
// code for CtzNN if we know that the argument is non-zero.
|
||||||
// Capture that information here for use in arch-specific optimizations.
|
// Capture that information here for use in arch-specific optimizations.
|
||||||
x := v.Args[0]
|
x := v.Args[0]
|
||||||
lim, ok := ft.limits[x.ID]
|
lim := ft.limits[x.ID]
|
||||||
if !ok {
|
if lim == noLimit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if lim.umin > 0 || lim.min > 0 || lim.max < 0 {
|
if lim.umin > 0 || lim.min > 0 || lim.max < 0 {
|
||||||
@ -1542,8 +1538,8 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
|
|||||||
// Check whether, for a << b, we know that b
|
// Check whether, for a << b, we know that b
|
||||||
// is strictly less than the number of bits in a.
|
// is strictly less than the number of bits in a.
|
||||||
by := v.Args[1]
|
by := v.Args[1]
|
||||||
lim, ok := ft.limits[by.ID]
|
lim := ft.limits[by.ID]
|
||||||
if !ok {
|
if lim == noLimit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
bits := 8 * v.Args[0].Type.Size()
|
bits := 8 * v.Args[0].Type.Size()
|
||||||
@ -1563,11 +1559,11 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
divr := v.Args[1]
|
divr := v.Args[1]
|
||||||
divrLim, divrLimok := ft.limits[divr.ID]
|
divrLim := ft.limits[divr.ID]
|
||||||
divd := v.Args[0]
|
divd := v.Args[0]
|
||||||
divdLim, divdLimok := ft.limits[divd.ID]
|
divdLim := ft.limits[divd.ID]
|
||||||
if (divrLimok && (divrLim.max < -1 || divrLim.min > -1)) ||
|
if (divrLim != noLimit && (divrLim.max < -1 || divrLim.min > -1)) ||
|
||||||
(divdLimok && divdLim.min > mostNegativeDividend[v.Op]) {
|
(divdLim != noLimit && divdLim.min > mostNegativeDividend[v.Op]) {
|
||||||
// See DivisionNeedsFixUp in rewrite.go.
|
// See DivisionNeedsFixUp in rewrite.go.
|
||||||
// v.AuxInt = 1 means we have proved both that the divisor is not -1
|
// v.AuxInt = 1 means we have proved both that the divisor is not -1
|
||||||
// and that the dividend is not the most negative integer,
|
// and that the dividend is not the most negative integer,
|
||||||
@ -1585,8 +1581,8 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
|
|||||||
case OpConst64, OpConst32, OpConst16, OpConst8:
|
case OpConst64, OpConst32, OpConst16, OpConst8:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
lim, ok := ft.limits[arg.ID]
|
lim := ft.limits[arg.ID]
|
||||||
if !ok {
|
if lim == noLimit {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user