mirror of
https://github.com/golang/go
synced 2024-11-15 02:00:33 -07:00
cmd/compile: teach dse about equivalent LocalAddrs
This patch teaches DSE that two LocalAddrs of the same variable
are equal, even if they are from different memory states. This avoids
dependance on a store into the same LocalAddr being added to
loadUse even though the store is unnecessary and is in fact
shadowed.
Fixes #59021
Change-Id: I0ef128b783c4ad6fd2236fa5ff20345b4d31eddb
GitHub-Last-Rev: b80a6b28fb
GitHub-Pull-Request: golang/go#66793
Reviewed-on: https://go-review.googlesource.com/c/go/+/578376
Reviewed-by: Keith Randall <khr@golang.org>
Auto-Submit: Keith Randall <khr@golang.org>
Reviewed-by: Joedian Reid <joedian@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
parent
a8ba163695
commit
57026007c8
@ -21,12 +21,18 @@ func dse(f *Func) {
|
|||||||
defer f.retSparseSet(storeUse)
|
defer f.retSparseSet(storeUse)
|
||||||
shadowed := f.newSparseMap(f.NumValues())
|
shadowed := f.newSparseMap(f.NumValues())
|
||||||
defer f.retSparseMap(shadowed)
|
defer f.retSparseMap(shadowed)
|
||||||
|
// localAddrs maps from a local variable (the Aux field of a LocalAddr value) to an instance of a LocalAddr value for that variable in the current block.
|
||||||
|
localAddrs := map[any]*Value{}
|
||||||
for _, b := range f.Blocks {
|
for _, b := range f.Blocks {
|
||||||
// Find all the stores in this block. Categorize their uses:
|
// Find all the stores in this block. Categorize their uses:
|
||||||
// loadUse contains stores which are used by a subsequent load.
|
// loadUse contains stores which are used by a subsequent load.
|
||||||
// storeUse contains stores which are used by a subsequent store.
|
// storeUse contains stores which are used by a subsequent store.
|
||||||
loadUse.clear()
|
loadUse.clear()
|
||||||
storeUse.clear()
|
storeUse.clear()
|
||||||
|
// TODO(deparker): use the 'clear' builtin once compiler bootstrap minimum version is raised to 1.21.
|
||||||
|
for k := range localAddrs {
|
||||||
|
delete(localAddrs, k)
|
||||||
|
}
|
||||||
stores = stores[:0]
|
stores = stores[:0]
|
||||||
for _, v := range b.Values {
|
for _, v := range b.Values {
|
||||||
if v.Op == OpPhi {
|
if v.Op == OpPhi {
|
||||||
@ -46,6 +52,13 @@ func dse(f *Func) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if v.Op == OpLocalAddr {
|
||||||
|
if _, ok := localAddrs[v.Aux]; !ok {
|
||||||
|
localAddrs[v.Aux] = v
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, a := range v.Args {
|
for _, a := range v.Args {
|
||||||
if a.Block == b && a.Type.IsMemory() {
|
if a.Block == b && a.Type.IsMemory() {
|
||||||
loadUse.add(a.ID)
|
loadUse.add(a.ID)
|
||||||
@ -100,6 +113,11 @@ func dse(f *Func) {
|
|||||||
} else { // OpZero
|
} else { // OpZero
|
||||||
sz = v.AuxInt
|
sz = v.AuxInt
|
||||||
}
|
}
|
||||||
|
if ptr.Op == OpLocalAddr {
|
||||||
|
if la, ok := localAddrs[ptr.Aux]; ok {
|
||||||
|
ptr = la
|
||||||
|
}
|
||||||
|
}
|
||||||
sr := shadowRange(shadowed.get(ptr.ID))
|
sr := shadowRange(shadowed.get(ptr.ID))
|
||||||
if sr.contains(off, off+sz) {
|
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,
|
||||||
@ -146,6 +164,7 @@ type shadowRange int32
|
|||||||
func (sr shadowRange) lo() int64 {
|
func (sr shadowRange) lo() int64 {
|
||||||
return int64(sr & 0xffff)
|
return int64(sr & 0xffff)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sr shadowRange) hi() int64 {
|
func (sr shadowRange) hi() int64 {
|
||||||
return int64((sr >> 16) & 0xffff)
|
return int64((sr >> 16) & 0xffff)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ package ssa
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/internal/src"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ func TestDeadStore(t *testing.T) {
|
|||||||
t.Errorf("dead store (zero) not removed")
|
t.Errorf("dead store (zero) not removed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeadStorePhi(t *testing.T) {
|
func TestDeadStorePhi(t *testing.T) {
|
||||||
// make sure we don't get into an infinite loop with phi values.
|
// make sure we don't get into an infinite loop with phi values.
|
||||||
c := testConfig(t)
|
c := testConfig(t)
|
||||||
@ -127,3 +129,46 @@ func TestDeadStoreUnsafe(t *testing.T) {
|
|||||||
t.Errorf("store %s incorrectly removed", v)
|
t.Errorf("store %s incorrectly removed", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeadStoreSmallStructInit(t *testing.T) {
|
||||||
|
c := testConfig(t)
|
||||||
|
ptrType := c.config.Types.BytePtr
|
||||||
|
typ := types.NewStruct([]*types.Field{
|
||||||
|
types.NewField(src.NoXPos, &types.Sym{Name: "A"}, c.config.Types.Int),
|
||||||
|
types.NewField(src.NoXPos, &types.Sym{Name: "B"}, c.config.Types.Int),
|
||||||
|
})
|
||||||
|
name := c.Temp(typ)
|
||||||
|
fun := c.Fun("entry",
|
||||||
|
Bloc("entry",
|
||||||
|
Valu("start", OpInitMem, types.TypeMem, 0, nil),
|
||||||
|
Valu("sp", OpSP, c.config.Types.Uintptr, 0, nil),
|
||||||
|
Valu("zero", OpConst64, c.config.Types.Int, 0, nil),
|
||||||
|
Valu("v6", OpLocalAddr, ptrType, 0, name, "sp", "start"),
|
||||||
|
Valu("v3", OpOffPtr, ptrType, 8, nil, "v6"),
|
||||||
|
Valu("v22", OpOffPtr, ptrType, 0, nil, "v6"),
|
||||||
|
Valu("zerostore1", OpStore, types.TypeMem, 0, c.config.Types.Int, "v22", "zero", "start"),
|
||||||
|
Valu("zerostore2", OpStore, types.TypeMem, 0, c.config.Types.Int, "v3", "zero", "zerostore1"),
|
||||||
|
Valu("v8", OpLocalAddr, ptrType, 0, name, "sp", "zerostore2"),
|
||||||
|
Valu("v23", OpOffPtr, ptrType, 8, nil, "v8"),
|
||||||
|
Valu("v25", OpOffPtr, ptrType, 0, nil, "v8"),
|
||||||
|
Valu("zerostore3", OpStore, types.TypeMem, 0, c.config.Types.Int, "v25", "zero", "zerostore2"),
|
||||||
|
Valu("zerostore4", OpStore, types.TypeMem, 0, c.config.Types.Int, "v23", "zero", "zerostore3"),
|
||||||
|
Goto("exit")),
|
||||||
|
Bloc("exit",
|
||||||
|
Exit("zerostore4")))
|
||||||
|
|
||||||
|
fun.f.Name = "smallstructinit"
|
||||||
|
CheckFunc(fun.f)
|
||||||
|
cse(fun.f)
|
||||||
|
dse(fun.f)
|
||||||
|
CheckFunc(fun.f)
|
||||||
|
|
||||||
|
v1 := fun.values["zerostore1"]
|
||||||
|
if v1.Op != OpCopy {
|
||||||
|
t.Errorf("dead store not removed")
|
||||||
|
}
|
||||||
|
v2 := fun.values["zerostore2"]
|
||||||
|
if v2.Op != OpCopy {
|
||||||
|
t.Errorf("dead store not removed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user