mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
cmd/compile: handle constant pointer offsets in dead store elimination
Update #63657 Update #45573 Change-Id: I163c6038c13d974dc0ca9f02144472bc05331826 Reviewed-on: https://go-review.googlesource.com/c/go/+/538595 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
parent
66b8107a26
commit
43b57b8516
@ -73,9 +73,9 @@ func dse(f *Func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Walk backwards looking for dead stores. Keep track of shadowed addresses.
|
// Walk backwards looking for dead stores. Keep track of shadowed addresses.
|
||||||
// A "shadowed address" is a pointer and a size describing a memory region that
|
// A "shadowed address" is a pointer, offset, and size describing a memory region that
|
||||||
// is known to be written. We keep track of shadowed addresses in the shadowed
|
// is known to be written. We keep track of shadowed addresses in the shadowed map,
|
||||||
// map, mapping the ID of the address to the size of the shadowed region.
|
// mapping the ID of the address to a shadowRange where future writes will happen.
|
||||||
// Since we're walking backwards, writes to a shadowed region are useless,
|
// Since we're walking backwards, writes to a shadowed region are useless,
|
||||||
// as they will be immediately overwritten.
|
// as they will be immediately overwritten.
|
||||||
shadowed.clear()
|
shadowed.clear()
|
||||||
@ -88,13 +88,20 @@ func dse(f *Func) {
|
|||||||
shadowed.clear()
|
shadowed.clear()
|
||||||
}
|
}
|
||||||
if v.Op == OpStore || v.Op == OpZero {
|
if v.Op == OpStore || v.Op == OpZero {
|
||||||
|
ptr := v.Args[0]
|
||||||
|
var off int64
|
||||||
|
for ptr.Op == OpOffPtr { // Walk to base pointer
|
||||||
|
off += ptr.AuxInt
|
||||||
|
ptr = ptr.Args[0]
|
||||||
|
}
|
||||||
var sz int64
|
var sz int64
|
||||||
if v.Op == OpStore {
|
if v.Op == OpStore {
|
||||||
sz = v.Aux.(*types.Type).Size()
|
sz = v.Aux.(*types.Type).Size()
|
||||||
} else { // OpZero
|
} else { // OpZero
|
||||||
sz = v.AuxInt
|
sz = v.AuxInt
|
||||||
}
|
}
|
||||||
if shadowedSize := int64(shadowed.get(v.Args[0].ID)); shadowedSize != -1 && shadowedSize >= sz {
|
sr := shadowRange(shadowed.get(ptr.ID))
|
||||||
|
if sr.contains(off, off+sz) {
|
||||||
// Modify the store/zero into a copy of the memory state,
|
// Modify the store/zero into a copy of the memory state,
|
||||||
// effectively eliding the store operation.
|
// effectively eliding the store operation.
|
||||||
if v.Op == OpStore {
|
if v.Op == OpStore {
|
||||||
@ -108,10 +115,8 @@ func dse(f *Func) {
|
|||||||
v.AuxInt = 0
|
v.AuxInt = 0
|
||||||
v.Op = OpCopy
|
v.Op = OpCopy
|
||||||
} else {
|
} else {
|
||||||
if sz > 0x7fffffff { // work around sparseMap's int32 value type
|
// Extend shadowed region.
|
||||||
sz = 0x7fffffff
|
shadowed.set(ptr.ID, int32(sr.merge(off, off+sz)))
|
||||||
}
|
|
||||||
shadowed.set(v.Args[0].ID, int32(sz))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// walk to previous store
|
// walk to previous store
|
||||||
@ -131,6 +136,49 @@ func dse(f *Func) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A shadowRange encodes a set of byte offsets [lo():hi()] from
|
||||||
|
// a given pointer that will be written to later in the block.
|
||||||
|
// A zero shadowRange encodes an empty shadowed range (and so
|
||||||
|
// does a -1 shadowRange, which is what sparsemap.get returns
|
||||||
|
// on a failed lookup).
|
||||||
|
type shadowRange int32
|
||||||
|
|
||||||
|
func (sr shadowRange) lo() int64 {
|
||||||
|
return int64(sr & 0xffff)
|
||||||
|
}
|
||||||
|
func (sr shadowRange) hi() int64 {
|
||||||
|
return int64((sr >> 16) & 0xffff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains reports whether [lo:hi] is completely within sr.
|
||||||
|
func (sr shadowRange) contains(lo, hi int64) bool {
|
||||||
|
return lo >= sr.lo() && hi <= sr.hi()
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge returns the union of sr and [lo:hi].
|
||||||
|
// merge is allowed to return something smaller than the union.
|
||||||
|
func (sr shadowRange) merge(lo, hi int64) shadowRange {
|
||||||
|
if lo < 0 || hi > 0xffff {
|
||||||
|
// Ignore offsets that are too large or small.
|
||||||
|
return sr
|
||||||
|
}
|
||||||
|
if sr.lo() == sr.hi() {
|
||||||
|
// Old range is empty - use new one.
|
||||||
|
return shadowRange(lo + hi<<16)
|
||||||
|
}
|
||||||
|
if hi < sr.lo() || lo > sr.hi() {
|
||||||
|
// The two regions don't overlap or abut, so we would
|
||||||
|
// have to keep track of multiple disjoint ranges.
|
||||||
|
// Because we can only keep one, keep the larger one.
|
||||||
|
if sr.hi()-sr.lo() >= hi-lo {
|
||||||
|
return sr
|
||||||
|
}
|
||||||
|
return shadowRange(lo + hi<<16)
|
||||||
|
}
|
||||||
|
// Regions overlap or abut - compute the union.
|
||||||
|
return shadowRange(min(lo, sr.lo()) + max(hi, sr.hi())<<16)
|
||||||
|
}
|
||||||
|
|
||||||
// elimDeadAutosGeneric deletes autos that are never accessed. To achieve this
|
// elimDeadAutosGeneric deletes autos that are never accessed. To achieve this
|
||||||
// we track the operations that the address of each auto reaches and if it only
|
// we track the operations that the address of each auto reaches and if it only
|
||||||
// reaches stores then we delete all the stores. The other operations will then
|
// reaches stores then we delete all the stores. The other operations will then
|
||||||
|
@ -1183,6 +1183,12 @@ func min(x, y int64) int64 {
|
|||||||
}
|
}
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
func max(x, y int64) int64 {
|
||||||
|
if x > y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
func isConstZero(v *Value) bool {
|
func isConstZero(v *Value) bool {
|
||||||
switch v.Op {
|
switch v.Op {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user