cmd/compile: manage Slot array better

steals idea from CL 312093

further investigation revealed additional duplicate
slots (equivalent, but not equal), so delete those too.

Rearranged Func.Names to be addresses of slots,
create canonical addresses so that split slots
(which use those addresses to refer to their parent,
and split slots can be further split)
will preserve "equivalent slots are equal".

Removes duplicates, improves metrics for "args at entry".

Change-Id: I5bbdcb50bd33655abcab3d27ad8cdce25499faaf
Reviewed-on: https://go-review.googlesource.com/c/go/+/312292
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
David Chase 2021-04-21 10:55:42 -04:00
parent 68327e1aa1
commit b38b1b2f9a
12 changed files with 238 additions and 220 deletions

View file

@ -147,13 +147,6 @@ type Frontend interface {
// Given the name for a compound type, returns the name we should use // Given the name for a compound type, returns the name we should use
// for the parts of that compound type. // for the parts of that compound type.
SplitString(LocalSlot) (LocalSlot, LocalSlot)
SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
SplitStruct(LocalSlot, int) LocalSlot
SplitArray(LocalSlot) LocalSlot // array must be length 1
SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo)
SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot
// DerefItab dereferences an itab function // DerefItab dereferences an itab function

View file

@ -26,7 +26,7 @@ func copyelim(f *Func) {
// Update named values. // Update named values.
for _, name := range f.Names { for _, name := range f.Names {
values := f.NamedValues[name] values := f.NamedValues[*name]
for i, v := range values { for i, v := range values {
if v.Op == OpCopy { if v.Op == OpCopy {
values[i] = v.Args[0] values[i] = v.Args[0]

View file

@ -223,7 +223,7 @@ func deadcode(f *Func) {
for _, name := range f.Names { for _, name := range f.Names {
j := 0 j := 0
s.clear() s.clear()
values := f.NamedValues[name] values := f.NamedValues[*name]
for _, v := range values { for _, v := range values {
if live[v.ID] && !s.contains(v.ID) { if live[v.ID] && !s.contains(v.ID) {
values[j] = v values[j] = v
@ -232,19 +232,19 @@ func deadcode(f *Func) {
} }
} }
if j == 0 { if j == 0 {
delete(f.NamedValues, name) delete(f.NamedValues, *name)
} else { } else {
f.Names[i] = name f.Names[i] = name
i++ i++
for k := len(values) - 1; k >= j; k-- { for k := len(values) - 1; k >= j; k-- {
values[k] = nil values[k] = nil
} }
f.NamedValues[name] = values[:j] f.NamedValues[*name] = values[:j]
} }
} }
clearNames := f.Names[i:] clearNames := f.Names[i:]
for j := range clearNames { for j := range clearNames {
clearNames[j] = LocalSlot{} clearNames[j] = nil
} }
f.Names = f.Names[:i] f.Names = f.Names[:i]

View file

@ -367,12 +367,12 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
state.slots = state.slots[:0] state.slots = state.slots[:0]
state.vars = state.vars[:0] state.vars = state.vars[:0]
for i, slot := range f.Names { for i, slot := range f.Names {
state.slots = append(state.slots, slot) state.slots = append(state.slots, *slot)
if ir.IsSynthetic(slot.N) { if ir.IsSynthetic(slot.N) {
continue continue
} }
topSlot := &slot topSlot := slot
for topSlot.SplitOf != nil { for topSlot.SplitOf != nil {
topSlot = topSlot.SplitOf topSlot = topSlot.SplitOf
} }
@ -436,7 +436,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
if ir.IsSynthetic(slot.N) { if ir.IsSynthetic(slot.N) {
continue continue
} }
for _, value := range f.NamedValues[slot] { for _, value := range f.NamedValues[*slot] {
state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i)) state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i))
} }
} }

View file

@ -36,64 +36,65 @@ func decomposeBuiltIn(f *Func) {
// accumulate new LocalSlots in newNames for addition after the iteration. This decomposition is for // accumulate new LocalSlots in newNames for addition after the iteration. This decomposition is for
// builtin types with leaf components, and thus there is no need to reprocess the newly create LocalSlots. // builtin types with leaf components, and thus there is no need to reprocess the newly create LocalSlots.
var toDelete []namedVal var toDelete []namedVal
var newNames []LocalSlot var newNames []*LocalSlot
for i, name := range f.Names { for i, name := range f.Names {
t := name.Type t := name.Type
switch { switch {
case t.IsInteger() && t.Size() > f.Config.RegSize: case t.IsInteger() && t.Size() > f.Config.RegSize:
hiName, loName := f.fe.SplitInt64(name) hiName, loName := f.SplitInt64(name)
newNames = append(newNames, hiName, loName) newNames = maybeAppend2(f, newNames, hiName, loName)
for j, v := range f.NamedValues[name] { for j, v := range f.NamedValues[*name] {
if v.Op != OpInt64Make { if v.Op != OpInt64Make {
continue continue
} }
f.NamedValues[hiName] = append(f.NamedValues[hiName], v.Args[0]) f.NamedValues[*hiName] = append(f.NamedValues[*hiName], v.Args[0])
f.NamedValues[loName] = append(f.NamedValues[loName], v.Args[1]) f.NamedValues[*loName] = append(f.NamedValues[*loName], v.Args[1])
toDelete = append(toDelete, namedVal{i, j}) toDelete = append(toDelete, namedVal{i, j})
} }
case t.IsComplex(): case t.IsComplex():
rName, iName := f.fe.SplitComplex(name) rName, iName := f.SplitComplex(name)
newNames = append(newNames, rName, iName) newNames = maybeAppend2(f, newNames, rName, iName)
for j, v := range f.NamedValues[name] { for j, v := range f.NamedValues[*name] {
if v.Op != OpComplexMake { if v.Op != OpComplexMake {
continue continue
} }
f.NamedValues[rName] = append(f.NamedValues[rName], v.Args[0]) f.NamedValues[*rName] = append(f.NamedValues[*rName], v.Args[0])
f.NamedValues[iName] = append(f.NamedValues[iName], v.Args[1]) f.NamedValues[*iName] = append(f.NamedValues[*iName], v.Args[1])
toDelete = append(toDelete, namedVal{i, j}) toDelete = append(toDelete, namedVal{i, j})
} }
case t.IsString(): case t.IsString():
ptrName, lenName := f.fe.SplitString(name) ptrName, lenName := f.SplitString(name)
newNames = append(newNames, ptrName, lenName) newNames = maybeAppend2(f, newNames, ptrName, lenName)
for j, v := range f.NamedValues[name] { for j, v := range f.NamedValues[*name] {
if v.Op != OpStringMake { if v.Op != OpStringMake {
continue continue
} }
f.NamedValues[ptrName] = append(f.NamedValues[ptrName], v.Args[0]) f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0])
f.NamedValues[lenName] = append(f.NamedValues[lenName], v.Args[1]) f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1])
toDelete = append(toDelete, namedVal{i, j}) toDelete = append(toDelete, namedVal{i, j})
} }
case t.IsSlice(): case t.IsSlice():
ptrName, lenName, capName := f.fe.SplitSlice(name) ptrName, lenName, capName := f.SplitSlice(name)
newNames = append(newNames, ptrName, lenName, capName) newNames = maybeAppend2(f, newNames, ptrName, lenName)
for j, v := range f.NamedValues[name] { newNames = maybeAppend(f, newNames, capName)
for j, v := range f.NamedValues[*name] {
if v.Op != OpSliceMake { if v.Op != OpSliceMake {
continue continue
} }
f.NamedValues[ptrName] = append(f.NamedValues[ptrName], v.Args[0]) f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0])
f.NamedValues[lenName] = append(f.NamedValues[lenName], v.Args[1]) f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1])
f.NamedValues[capName] = append(f.NamedValues[capName], v.Args[2]) f.NamedValues[*capName] = append(f.NamedValues[*capName], v.Args[2])
toDelete = append(toDelete, namedVal{i, j}) toDelete = append(toDelete, namedVal{i, j})
} }
case t.IsInterface(): case t.IsInterface():
typeName, dataName := f.fe.SplitInterface(name) typeName, dataName := f.SplitInterface(name)
newNames = append(newNames, typeName, dataName) newNames = maybeAppend2(f, newNames, typeName, dataName)
for j, v := range f.NamedValues[name] { for j, v := range f.NamedValues[*name] {
if v.Op != OpIMake { if v.Op != OpIMake {
continue continue
} }
f.NamedValues[typeName] = append(f.NamedValues[typeName], v.Args[0]) f.NamedValues[*typeName] = append(f.NamedValues[*typeName], v.Args[0])
f.NamedValues[dataName] = append(f.NamedValues[dataName], v.Args[1]) f.NamedValues[*dataName] = append(f.NamedValues[*dataName], v.Args[1])
toDelete = append(toDelete, namedVal{i, j}) toDelete = append(toDelete, namedVal{i, j})
} }
case t.IsFloat(): case t.IsFloat():
@ -107,6 +108,18 @@ func decomposeBuiltIn(f *Func) {
f.Names = append(f.Names, newNames...) f.Names = append(f.Names, newNames...)
} }
func maybeAppend(f *Func, ss []*LocalSlot, s *LocalSlot) []*LocalSlot {
if _, ok := f.NamedValues[*s]; !ok {
f.NamedValues[*s] = nil
return append(ss, s)
}
return ss
}
func maybeAppend2(f *Func, ss []*LocalSlot, s1, s2 *LocalSlot) []*LocalSlot {
return maybeAppend(f, maybeAppend(f, ss, s1), s2)
}
func decomposeBuiltInPhi(v *Value) { func decomposeBuiltInPhi(v *Value) {
switch { switch {
case v.Type.IsInteger() && v.Type.Size() > v.Block.Func.Config.RegSize: case v.Type.IsInteger() && v.Type.Size() > v.Block.Func.Config.RegSize:
@ -230,7 +243,7 @@ func decomposeUser(f *Func) {
} }
// Split up named values into their components. // Split up named values into their components.
i := 0 i := 0
var newNames []LocalSlot var newNames []*LocalSlot
for _, name := range f.Names { for _, name := range f.Names {
t := name.Type t := name.Type
switch { switch {
@ -250,7 +263,7 @@ func decomposeUser(f *Func) {
// decomposeUserArrayInto creates names for the element(s) of arrays referenced // decomposeUserArrayInto creates names for the element(s) of arrays referenced
// by name where possible, and appends those new names to slots, which is then // by name where possible, and appends those new names to slots, which is then
// returned. // returned.
func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalSlot { func decomposeUserArrayInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot {
t := name.Type t := name.Type
if t.NumElem() == 0 { if t.NumElem() == 0 {
// TODO(khr): Not sure what to do here. Probably nothing. // TODO(khr): Not sure what to do here. Probably nothing.
@ -261,20 +274,20 @@ func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalS
// shouldn't get here due to CanSSA // shouldn't get here due to CanSSA
f.Fatalf("array not of size 1") f.Fatalf("array not of size 1")
} }
elemName := f.fe.SplitArray(name) elemName := f.SplitArray(name)
var keep []*Value var keep []*Value
for _, v := range f.NamedValues[name] { for _, v := range f.NamedValues[*name] {
if v.Op != OpArrayMake1 { if v.Op != OpArrayMake1 {
keep = append(keep, v) keep = append(keep, v)
continue continue
} }
f.NamedValues[elemName] = append(f.NamedValues[elemName], v.Args[0]) f.NamedValues[*elemName] = append(f.NamedValues[*elemName], v.Args[0])
} }
if len(keep) == 0 { if len(keep) == 0 {
// delete the name for the array as a whole // delete the name for the array as a whole
delete(f.NamedValues, name) delete(f.NamedValues, *name)
} else { } else {
f.NamedValues[name] = keep f.NamedValues[*name] = keep
} }
if t.Elem().IsArray() { if t.Elem().IsArray() {
@ -289,38 +302,38 @@ func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalS
// decomposeUserStructInto creates names for the fields(s) of structs referenced // decomposeUserStructInto creates names for the fields(s) of structs referenced
// by name where possible, and appends those new names to slots, which is then // by name where possible, and appends those new names to slots, which is then
// returned. // returned.
func decomposeUserStructInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalSlot { func decomposeUserStructInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot {
fnames := []LocalSlot{} // slots for struct in name fnames := []*LocalSlot{} // slots for struct in name
t := name.Type t := name.Type
n := t.NumFields() n := t.NumFields()
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
fs := f.fe.SplitStruct(name, i) fs := f.SplitStruct(name, i)
fnames = append(fnames, fs) fnames = append(fnames, fs)
// arrays and structs will be decomposed further, so // arrays and structs will be decomposed further, so
// there's no need to record a name // there's no need to record a name
if !fs.Type.IsArray() && !fs.Type.IsStruct() { if !fs.Type.IsArray() && !fs.Type.IsStruct() {
slots = append(slots, fs) slots = maybeAppend(f, slots, fs)
} }
} }
makeOp := StructMakeOp(n) makeOp := StructMakeOp(n)
var keep []*Value var keep []*Value
// create named values for each struct field // create named values for each struct field
for _, v := range f.NamedValues[name] { for _, v := range f.NamedValues[*name] {
if v.Op != makeOp { if v.Op != makeOp {
keep = append(keep, v) keep = append(keep, v)
continue continue
} }
for i := 0; i < len(fnames); i++ { for i := 0; i < len(fnames); i++ {
f.NamedValues[fnames[i]] = append(f.NamedValues[fnames[i]], v.Args[i]) f.NamedValues[*fnames[i]] = append(f.NamedValues[*fnames[i]], v.Args[i])
} }
} }
if len(keep) == 0 { if len(keep) == 0 {
// delete the name for the struct as a whole // delete the name for the struct as a whole
delete(f.NamedValues, name) delete(f.NamedValues, *name)
} else { } else {
f.NamedValues[name] = keep f.NamedValues[*name] = keep
} }
// now that this f.NamedValues contains values for the struct // now that this f.NamedValues contains values for the struct
@ -328,10 +341,10 @@ func decomposeUserStructInto(f *Func, name LocalSlot, slots []LocalSlot) []Local
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
if name.Type.FieldType(i).IsStruct() { if name.Type.FieldType(i).IsStruct() {
slots = decomposeUserStructInto(f, fnames[i], slots) slots = decomposeUserStructInto(f, fnames[i], slots)
delete(f.NamedValues, fnames[i]) delete(f.NamedValues, *fnames[i])
} else if name.Type.FieldType(i).IsArray() { } else if name.Type.FieldType(i).IsArray() {
slots = decomposeUserArrayInto(f, fnames[i], slots) slots = decomposeUserArrayInto(f, fnames[i], slots)
delete(f.NamedValues, fnames[i]) delete(f.NamedValues, *fnames[i])
} }
} }
return slots return slots
@ -416,9 +429,10 @@ type namedVal struct {
locIndex, valIndex int // f.NamedValues[f.Names[locIndex]][valIndex] = key locIndex, valIndex int // f.NamedValues[f.Names[locIndex]][valIndex] = key
} }
// deleteNamedVals removes particular values with debugger names from f's naming data structures // deleteNamedVals removes particular values with debugger names from f's naming data structures,
// removes all values with OpInvalid, and re-sorts the list of Names.
func deleteNamedVals(f *Func, toDelete []namedVal) { func deleteNamedVals(f *Func, toDelete []namedVal) {
// Arrange to delete from larger indices to smaller, to ensure swap-with-end deletion does not invalid pending indices. // Arrange to delete from larger indices to smaller, to ensure swap-with-end deletion does not invalidate pending indices.
sort.Slice(toDelete, func(i, j int) bool { sort.Slice(toDelete, func(i, j int) bool {
if toDelete[i].locIndex != toDelete[j].locIndex { if toDelete[i].locIndex != toDelete[j].locIndex {
return toDelete[i].locIndex > toDelete[j].locIndex return toDelete[i].locIndex > toDelete[j].locIndex
@ -430,16 +444,36 @@ func deleteNamedVals(f *Func, toDelete []namedVal) {
// Get rid of obsolete names // Get rid of obsolete names
for _, d := range toDelete { for _, d := range toDelete {
loc := f.Names[d.locIndex] loc := f.Names[d.locIndex]
vals := f.NamedValues[loc] vals := f.NamedValues[*loc]
l := len(vals) - 1 l := len(vals) - 1
if l > 0 { if l > 0 {
vals[d.valIndex] = vals[l] vals[d.valIndex] = vals[l]
f.NamedValues[loc] = vals[:l] }
} else { vals[l] = nil
delete(f.NamedValues, loc) f.NamedValues[*loc] = vals[:l]
l = len(f.Names) - 1 }
f.Names[d.locIndex] = f.Names[l] // Delete locations with no values attached.
f.Names = f.Names[:l] end := len(f.Names)
for i := len(f.Names) - 1; i >= 0; i-- {
loc := f.Names[i]
vals := f.NamedValues[*loc]
last := len(vals)
for j := len(vals) - 1; j >= 0; j-- {
if vals[j].Op == OpInvalid {
last--
vals[j] = vals[last]
vals[last] = nil
} }
} }
if last < len(vals) {
f.NamedValues[*loc] = vals[:last]
}
if len(vals) == 0 {
delete(f.NamedValues, *loc)
end--
f.Names[i] = f.Names[end]
f.Names[end] = nil
}
}
f.Names = f.Names[:end]
} }

View file

@ -243,10 +243,10 @@ func (x *expandState) offsetFrom(b *Block, from *Value, offset int64, pt *types.
} }
// splitSlots splits one "field" (specified by sfx, offset, and ty) out of the LocalSlots in ls and returns the new LocalSlots this generates. // splitSlots splits one "field" (specified by sfx, offset, and ty) out of the LocalSlots in ls and returns the new LocalSlots this generates.
func (x *expandState) splitSlots(ls []LocalSlot, sfx string, offset int64, ty *types.Type) []LocalSlot { func (x *expandState) splitSlots(ls []*LocalSlot, sfx string, offset int64, ty *types.Type) []*LocalSlot {
var locs []LocalSlot var locs []*LocalSlot
for i := range ls { for i := range ls {
locs = append(locs, x.f.fe.SplitSlot(&ls[i], sfx, offset, ty)) locs = append(locs, x.f.SplitSlot(ls[i], sfx, offset, ty))
} }
return locs return locs
} }
@ -301,13 +301,13 @@ func (x *expandState) Printf(format string, a ...interface{}) (n int, err error)
// It emits the code necessary to implement the leaf select operation that leads to the root. // It emits the code necessary to implement the leaf select operation that leads to the root.
// //
// TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []LocalSlot { func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []*LocalSlot {
if x.debug { if x.debug {
x.indent(3) x.indent(3)
defer x.indent(-3) defer x.indent(-3)
x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset) x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset)
} }
var locs []LocalSlot var locs []*LocalSlot
leafType := leaf.Type leafType := leaf.Type
if len(selector.Args) > 0 { if len(selector.Args) > 0 {
w := selector.Args[0] w := selector.Args[0]
@ -477,7 +477,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
case OpStructSelect: case OpStructSelect:
w := selector.Args[0] w := selector.Args[0]
var ls []LocalSlot var ls []*LocalSlot
if w.Type.Kind() != types.TSTRUCT { // IData artifact if w.Type.Kind() != types.TSTRUCT { // IData artifact
ls = x.rewriteSelect(leaf, w, offset, regOffset) ls = x.rewriteSelect(leaf, w, offset, regOffset)
} else { } else {
@ -485,7 +485,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
ls = x.rewriteSelect(leaf, w, offset+w.Type.FieldOff(fldi), regOffset+x.regOffset(w.Type, fldi)) ls = x.rewriteSelect(leaf, w, offset+w.Type.FieldOff(fldi), regOffset+x.regOffset(w.Type, fldi))
if w.Op != OpIData { if w.Op != OpIData {
for _, l := range ls { for _, l := range ls {
locs = append(locs, x.f.fe.SplitStruct(l, int(selector.AuxInt))) locs = append(locs, x.f.SplitStruct(l, int(selector.AuxInt)))
} }
} }
} }
@ -662,7 +662,7 @@ outer:
func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
pa := x.prAssignForArg(source) pa := x.prAssignForArg(source)
var locs []LocalSlot var locs []*LocalSlot
for _, s := range x.namedSelects[source] { for _, s := range x.namedSelects[source] {
locs = append(locs, x.f.Names[s.locIndex]) locs = append(locs, x.f.Names[s.locIndex])
} }
@ -756,12 +756,15 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t
return nil return nil
} }
func (x *expandState) splitSlotsIntoNames(locs []LocalSlot, suffix string, off int64, rt *types.Type, w *Value) { func (x *expandState) splitSlotsIntoNames(locs []*LocalSlot, suffix string, off int64, rt *types.Type, w *Value) {
wlocs := x.splitSlots(locs, suffix, off, rt) wlocs := x.splitSlots(locs, suffix, off, rt)
for _, l := range wlocs { for _, l := range wlocs {
x.f.NamedValues[l] = append(x.f.NamedValues[l], w) old, ok := x.f.NamedValues[*l]
x.f.NamedValues[*l] = append(old, w)
if !ok {
x.f.Names = append(x.f.Names, l)
}
} }
x.f.Names = append(x.f.Names, wlocs...)
} }
// decomposeLoad is a helper for storeArgOrLoad. // decomposeLoad is a helper for storeArgOrLoad.
@ -826,7 +829,7 @@ func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value,
// storeOneArg creates a decomposed (one step) arg that is then stored. // storeOneArg creates a decomposed (one step) arg that is then stored.
// pos and b locate the store instruction, source is the "base" of the value input, // pos and b locate the store instruction, source is the "base" of the value input,
// mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases. // mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases.
func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
if x.debug { if x.debug {
x.indent(3) x.indent(3)
defer x.indent(-3) defer x.indent(-3)
@ -848,7 +851,7 @@ func storeOneLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t
return x.storeArgOrLoad(pos, b, w, mem, t, offStore, loadRegOffset, storeRc) return x.storeArgOrLoad(pos, b, w, mem, t, offStore, loadRegOffset, storeRc)
} }
func storeTwoArg(x *expandState, pos src.XPos, b *Block, locs []LocalSlot, suffix1 string, suffix2 string, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { func storeTwoArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix1 string, suffix2 string, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
mem = storeOneArg(x, pos, b, locs, suffix1, source, mem, t1, offArg, offStore, loadRegOffset, storeRc.next(t1)) mem = storeOneArg(x, pos, b, locs, suffix1, source, mem, t1, offArg, offStore, loadRegOffset, storeRc.next(t1))
pos = pos.WithNotStmt() pos = pos.WithNotStmt()
t1Size := t1.Size() t1Size := t1.Size()
@ -1168,7 +1171,7 @@ func expandCalls(f *Func) {
for i, name := range f.Names { for i, name := range f.Names {
t := name.Type t := name.Type
if x.isAlreadyExpandedAggregateType(t) { if x.isAlreadyExpandedAggregateType(t) {
for j, v := range f.NamedValues[name] { for j, v := range f.NamedValues[*name] {
if v.Op == OpSelectN || v.Op == OpArg && x.isAlreadyExpandedAggregateType(v.Type) { if v.Op == OpSelectN || v.Op == OpArg && x.isAlreadyExpandedAggregateType(v.Type) {
ns := x.namedSelects[v] ns := x.namedSelects[v]
x.namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j}) x.namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j})
@ -1477,10 +1480,10 @@ func expandCalls(f *Func) {
// Leaf types may have debug locations // Leaf types may have debug locations
if !x.isAlreadyExpandedAggregateType(v.Type) { if !x.isAlreadyExpandedAggregateType(v.Type) {
for _, l := range locs { for _, l := range locs {
if _, ok := f.NamedValues[l]; !ok { if _, ok := f.NamedValues[*l]; !ok {
f.Names = append(f.Names, l) f.Names = append(f.Names, l)
} }
f.NamedValues[l] = append(f.NamedValues[l], v) f.NamedValues[*l] = append(f.NamedValues[*l], v)
} }
continue continue
} }
@ -1553,7 +1556,7 @@ func expandCalls(f *Func) {
// Step 6: elide any copies introduced. // Step 6: elide any copies introduced.
// Update named values. // Update named values.
for _, name := range f.Names { for _, name := range f.Names {
values := f.NamedValues[name] values := f.NamedValues[*name]
for i, v := range values { for i, v := range values {
if v.Op == OpCopy { if v.Op == OpCopy {
a := v.Args[0] a := v.Args[0]
@ -1725,7 +1728,8 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64,
loc := LocalSlot{N: aux.Name, Type: t, Off: 0} loc := LocalSlot{N: aux.Name, Type: t, Off: 0}
values, ok := x.f.NamedValues[loc] values, ok := x.f.NamedValues[loc]
if !ok { if !ok {
x.f.Names = append(x.f.Names, loc) ploc := x.f.localSlotAddr(loc)
x.f.Names = append(x.f.Names, ploc)
} }
x.f.NamedValues[loc] = append(values, w) x.f.NamedValues[loc] = append(values, w)
} }

View file

@ -73,36 +73,6 @@ func (TestFrontend) Auto(pos src.XPos, t *types.Type) *ir.Name {
n.Class = ir.PAUTO n.Class = ir.PAUTO
return n return n
} }
func (d TestFrontend) SplitString(s LocalSlot) (LocalSlot, LocalSlot) {
return LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 8}
}
func (d TestFrontend) SplitInterface(s LocalSlot) (LocalSlot, LocalSlot) {
return LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off + 8}
}
func (d TestFrontend) SplitSlice(s LocalSlot) (LocalSlot, LocalSlot, LocalSlot) {
return LocalSlot{N: s.N, Type: s.Type.Elem().PtrTo(), Off: s.Off},
LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 8},
LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 16}
}
func (d TestFrontend) SplitComplex(s LocalSlot) (LocalSlot, LocalSlot) {
if s.Type.Size() == 16 {
return LocalSlot{N: s.N, Type: testTypes.Float64, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Float64, Off: s.Off + 8}
}
return LocalSlot{N: s.N, Type: testTypes.Float32, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Float32, Off: s.Off + 4}
}
func (d TestFrontend) SplitInt64(s LocalSlot) (LocalSlot, LocalSlot) {
if s.Type.IsSigned() {
return LocalSlot{N: s.N, Type: testTypes.Int32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off}
}
return LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off}
}
func (d TestFrontend) SplitStruct(s LocalSlot, i int) LocalSlot {
return LocalSlot{N: s.N, Type: s.Type.FieldType(i), Off: s.Off + s.Type.FieldOff(i)}
}
func (d TestFrontend) SplitArray(s LocalSlot) LocalSlot {
return LocalSlot{N: s.N, Type: s.Type.Elem(), Off: s.Off}
}
func (d TestFrontend) SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot { func (d TestFrontend) SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot {
return LocalSlot{N: parent.N, Type: t, Off: offset} return LocalSlot{N: parent.N, Type: t, Off: offset}
} }

View file

@ -6,6 +6,7 @@ package ssa
import ( import (
"cmd/compile/internal/abi" "cmd/compile/internal/abi"
"cmd/compile/internal/base"
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/src" "cmd/internal/src"
"crypto/sha1" "crypto/sha1"
@ -61,7 +62,11 @@ type Func struct {
NamedValues map[LocalSlot][]*Value NamedValues map[LocalSlot][]*Value
// Names is a copy of NamedValues.Keys. We keep a separate list // Names is a copy of NamedValues.Keys. We keep a separate list
// of keys to make iteration order deterministic. // of keys to make iteration order deterministic.
Names []LocalSlot Names []*LocalSlot
// Canonicalize root/top-level local slots, and canonicalize their pieces.
// Because LocalSlot pieces refer to their parents with a pointer, this ensures that equivalent slots really are equal.
CanonicalLocalSlots map[LocalSlot]*LocalSlot
CanonicalLocalSplits map[LocalSlotSplitKey]*LocalSlot
// RegArgs is a slice of register-memory pairs that must be spilled and unspilled in the uncommon path of function entry. // RegArgs is a slice of register-memory pairs that must be spilled and unspilled in the uncommon path of function entry.
RegArgs []Spill RegArgs []Spill
@ -87,10 +92,16 @@ type Func struct {
constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type
} }
type LocalSlotSplitKey struct {
parent *LocalSlot
Off int64 // offset of slot in N
Type *types.Type // type of slot
}
// NewFunc returns a new, empty function object. // NewFunc returns a new, empty function object.
// Caller must set f.Config and f.Cache before using f. // Caller must set f.Config and f.Cache before using f.
func NewFunc(fe Frontend) *Func { func NewFunc(fe Frontend) *Func {
return &Func{fe: fe, NamedValues: make(map[LocalSlot][]*Value)} return &Func{fe: fe, NamedValues: make(map[LocalSlot][]*Value), CanonicalLocalSlots: make(map[LocalSlot]*LocalSlot), CanonicalLocalSplits: make(map[LocalSlotSplitKey]*LocalSlot)}
} }
// NumBlocks returns an integer larger than the id of any Block in the Func. // NumBlocks returns an integer larger than the id of any Block in the Func.
@ -193,6 +204,101 @@ func (f *Func) retDeadcodeLiveOrderStmts(liveOrderStmts []*Value) {
f.Cache.deadcode.liveOrderStmts = liveOrderStmts f.Cache.deadcode.liveOrderStmts = liveOrderStmts
} }
func (f *Func) localSlotAddr(slot LocalSlot) *LocalSlot {
a, ok := f.CanonicalLocalSlots[slot]
if !ok {
a = new(LocalSlot)
*a = slot // don't escape slot
f.CanonicalLocalSlots[slot] = a
}
return a
}
func (f *Func) SplitString(name *LocalSlot) (*LocalSlot, *LocalSlot) {
ptrType := types.NewPtr(types.Types[types.TUINT8])
lenType := types.Types[types.TINT]
// Split this string up into two separate variables.
p := f.SplitSlot(name, ".ptr", 0, ptrType)
l := f.SplitSlot(name, ".len", ptrType.Size(), lenType)
return p, l
}
func (f *Func) SplitInterface(name *LocalSlot) (*LocalSlot, *LocalSlot) {
n := name.N
u := types.Types[types.TUINTPTR]
t := types.NewPtr(types.Types[types.TUINT8])
// Split this interface up into two separate variables.
sfx := ".itab"
if n.Type().IsEmptyInterface() {
sfx = ".type"
}
c := f.SplitSlot(name, sfx, 0, u) // see comment in typebits.Set
d := f.SplitSlot(name, ".data", u.Size(), t)
return c, d
}
func (f *Func) SplitSlice(name *LocalSlot) (*LocalSlot, *LocalSlot, *LocalSlot) {
ptrType := types.NewPtr(name.Type.Elem())
lenType := types.Types[types.TINT]
p := f.SplitSlot(name, ".ptr", 0, ptrType)
l := f.SplitSlot(name, ".len", ptrType.Size(), lenType)
c := f.SplitSlot(name, ".cap", ptrType.Size()+lenType.Size(), lenType)
return p, l, c
}
func (f *Func) SplitComplex(name *LocalSlot) (*LocalSlot, *LocalSlot) {
s := name.Type.Size() / 2
var t *types.Type
if s == 8 {
t = types.Types[types.TFLOAT64]
} else {
t = types.Types[types.TFLOAT32]
}
r := f.SplitSlot(name, ".real", 0, t)
i := f.SplitSlot(name, ".imag", t.Size(), t)
return r, i
}
func (f *Func) SplitInt64(name *LocalSlot) (*LocalSlot, *LocalSlot) {
var t *types.Type
if name.Type.IsSigned() {
t = types.Types[types.TINT32]
} else {
t = types.Types[types.TUINT32]
}
if f.Config.BigEndian {
return f.SplitSlot(name, ".hi", 0, t), f.SplitSlot(name, ".lo", t.Size(), types.Types[types.TUINT32])
}
return f.SplitSlot(name, ".hi", t.Size(), t), f.SplitSlot(name, ".lo", 0, types.Types[types.TUINT32])
}
func (f *Func) SplitStruct(name *LocalSlot, i int) *LocalSlot {
st := name.Type
return f.SplitSlot(name, st.FieldName(i), st.FieldOff(i), st.FieldType(i))
}
func (f *Func) SplitArray(name *LocalSlot) *LocalSlot {
n := name.N
at := name.Type
if at.NumElem() != 1 {
base.FatalfAt(n.Pos(), "bad array size")
}
et := at.Elem()
return f.SplitSlot(name, "[0]", 0, et)
}
func (f *Func) SplitSlot(name *LocalSlot, sfx string, offset int64, t *types.Type) *LocalSlot {
lssk := LocalSlotSplitKey{name, offset, t}
if als, ok := f.CanonicalLocalSplits[lssk]; ok {
return als
}
// Note: the _ field may appear several times. But
// have no fear, identically-named but distinct Autos are
// ok, albeit maybe confusing for a debugger.
ls := f.fe.SplitSlot(name, sfx, offset, t)
f.CanonicalLocalSplits[lssk] = &ls
return &ls
}
// newValue allocates a new Value with the given fields and places it at the end of b.Values. // newValue allocates a new Value with the given fields and places it at the end of b.Values.
func (f *Func) newValue(op Op, t *types.Type, b *Block, pos src.XPos) *Value { func (f *Func) newValue(op Op, t *types.Type, b *Block, pos src.XPos) *Value {
var v *Value var v *Value

View file

@ -12,26 +12,10 @@ func layout(f *Func) {
} }
// Register allocation may use a different order which has constraints // Register allocation may use a different order which has constraints
// imposed by the linear-scan algorithm. Note that f.pass here is // imposed by the linear-scan algorithm.
// regalloc, so the switch is conditional on -d=ssa/regalloc/test=N
func layoutRegallocOrder(f *Func) []*Block { func layoutRegallocOrder(f *Func) []*Block {
// remnant of an experiment; perhaps there will be another.
switch f.pass.test {
case 0: // layout order
return layoutOrder(f) return layoutOrder(f)
case 1: // existing block order
return f.Blocks
case 2: // reverse of postorder; legal, but usually not good.
po := f.postorder()
visitOrder := make([]*Block, len(po))
for i, b := range po {
j := len(po) - i - 1
visitOrder[j] = b
}
return visitOrder
}
return nil
} }
func layoutOrder(f *Func) []*Block { func layoutOrder(f *Func) []*Block {

View file

@ -154,6 +154,6 @@ func fprintFunc(p funcPrinter, f *Func) {
p.endBlock(b) p.endBlock(b)
} }
for _, name := range f.Names { for _, name := range f.Names {
p.named(name, f.NamedValues[name]) p.named(*name, f.NamedValues[*name])
} }
} }

View file

@ -141,10 +141,11 @@ func (s *stackAllocState) stackalloc() {
s.names = make([]LocalSlot, n) s.names = make([]LocalSlot, n)
} }
names := s.names names := s.names
empty := LocalSlot{}
for _, name := range f.Names { for _, name := range f.Names {
// Note: not "range f.NamedValues" above, because // Note: not "range f.NamedValues" above, because
// that would be nondeterministic. // that would be nondeterministic.
for _, v := range f.NamedValues[name] { for _, v := range f.NamedValues[*name] {
if v.Op == OpArgIntReg || v.Op == OpArgFloatReg { if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
aux := v.Aux.(*AuxNameOffset) aux := v.Aux.(*AuxNameOffset)
// Never let an arg be bound to a differently named thing. // Never let an arg be bound to a differently named thing.
@ -162,10 +163,12 @@ func (s *stackAllocState) stackalloc() {
continue continue
} }
if names[v.ID] == empty {
if f.pass.debug > stackDebug { if f.pass.debug > stackDebug {
fmt.Printf("stackalloc value %s to name %s\n", v, name) fmt.Printf("stackalloc value %s to name %s\n", v, *name)
}
names[v.ID] = *name
} }
names[v.ID] = name
} }
} }

View file

@ -8,7 +8,6 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"cmd/compile/internal/abi" "cmd/compile/internal/abi"
"encoding/binary"
"fmt" "fmt"
"go/constant" "go/constant"
"html" "html"
@ -6463,7 +6462,8 @@ func (s *state) addNamedValue(n *ir.Name, v *ssa.Value) {
loc := ssa.LocalSlot{N: n, Type: n.Type(), Off: 0} loc := ssa.LocalSlot{N: n, Type: n.Type(), Off: 0}
values, ok := s.f.NamedValues[loc] values, ok := s.f.NamedValues[loc]
if !ok { if !ok {
s.f.Names = append(s.f.Names, loc) s.f.Names = append(s.f.Names, &loc)
s.f.CanonicalLocalSlots[loc] = &loc
} }
s.f.NamedValues[loc] = append(values, v) s.f.NamedValues[loc] = append(values, v)
} }
@ -7552,82 +7552,6 @@ func (e *ssafn) Auto(pos src.XPos, t *types.Type) *ir.Name {
return typecheck.TempAt(pos, e.curfn, t) // Note: adds new auto to e.curfn.Func.Dcl list return typecheck.TempAt(pos, e.curfn, t) // Note: adds new auto to e.curfn.Func.Dcl list
} }
func (e *ssafn) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
ptrType := types.NewPtr(types.Types[types.TUINT8])
lenType := types.Types[types.TINT]
// Split this string up into two separate variables.
p := e.SplitSlot(&name, ".ptr", 0, ptrType)
l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
return p, l
}
func (e *ssafn) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
n := name.N
u := types.Types[types.TUINTPTR]
t := types.NewPtr(types.Types[types.TUINT8])
// Split this interface up into two separate variables.
f := ".itab"
if n.Type().IsEmptyInterface() {
f = ".type"
}
c := e.SplitSlot(&name, f, 0, u) // see comment in typebits.Set
d := e.SplitSlot(&name, ".data", u.Size(), t)
return c, d
}
func (e *ssafn) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) {
ptrType := types.NewPtr(name.Type.Elem())
lenType := types.Types[types.TINT]
p := e.SplitSlot(&name, ".ptr", 0, ptrType)
l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
c := e.SplitSlot(&name, ".cap", ptrType.Size()+lenType.Size(), lenType)
return p, l, c
}
func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
s := name.Type.Size() / 2
var t *types.Type
if s == 8 {
t = types.Types[types.TFLOAT64]
} else {
t = types.Types[types.TFLOAT32]
}
r := e.SplitSlot(&name, ".real", 0, t)
i := e.SplitSlot(&name, ".imag", t.Size(), t)
return r, i
}
func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
var t *types.Type
if name.Type.IsSigned() {
t = types.Types[types.TINT32]
} else {
t = types.Types[types.TUINT32]
}
if Arch.LinkArch.ByteOrder == binary.BigEndian {
return e.SplitSlot(&name, ".hi", 0, t), e.SplitSlot(&name, ".lo", t.Size(), types.Types[types.TUINT32])
}
return e.SplitSlot(&name, ".hi", t.Size(), t), e.SplitSlot(&name, ".lo", 0, types.Types[types.TUINT32])
}
func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
st := name.Type
// Note: the _ field may appear several times. But
// have no fear, identically-named but distinct Autos are
// ok, albeit maybe confusing for a debugger.
return e.SplitSlot(&name, "."+st.FieldName(i), st.FieldOff(i), st.FieldType(i))
}
func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
n := name.N
at := name.Type
if at.NumElem() != 1 {
e.Fatalf(n.Pos(), "bad array size")
}
et := at.Elem()
return e.SplitSlot(&name, "[0]", 0, et)
}
func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym { func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym {
return reflectdata.ITabSym(it, offset) return reflectdata.ITabSym(it, offset)
} }