1
0
mirror of https://github.com/golang/go synced 2024-11-19 17:44:43 -07:00

cmd/compile/internal: reuse more memory

Reuse even more memory, and keep track of it in a long-lived debugState
object rather than piecemeal in the Cache.

Change-Id: Ib6936b4e8594dc6dda1f59ece753c00fd1c136ba
Reviewed-on: https://go-review.googlesource.com/92404
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Heschi Kreinick 2018-02-05 17:04:44 -05:00
parent ac81c5c402
commit 438a757d73
4 changed files with 134 additions and 96 deletions

View File

@ -572,7 +572,6 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/ssa.Block %v": "", "*cmd/compile/internal/ssa.Block %v": "",
"*cmd/compile/internal/ssa.Func %s": "", "*cmd/compile/internal/ssa.Func %s": "",
"*cmd/compile/internal/ssa.Func %v": "", "*cmd/compile/internal/ssa.Func %v": "",
"*cmd/compile/internal/ssa.LocalSlot %v": "",
"*cmd/compile/internal/ssa.Register %s": "", "*cmd/compile/internal/ssa.Register %s": "",
"*cmd/compile/internal/ssa.Register %v": "", "*cmd/compile/internal/ssa.Register %v": "",
"*cmd/compile/internal/ssa.SparseTreeNode %v": "", "*cmd/compile/internal/ssa.SparseTreeNode %v": "",

View File

@ -650,7 +650,7 @@ func createComplexVar(fn *Func, varID ssa.VarID) *dwarf.Var {
// variables just give it the first one. It's not used otherwise. // variables just give it the first one. It's not used otherwise.
// This won't work well if the first slot hasn't been assigned a stack // This won't work well if the first slot hasn't been assigned a stack
// location, but it's not obvious how to do better. // location, but it's not obvious how to do better.
StackOffset: stackOffset(*debug.Slots[debug.VarSlots[varID][0]]), StackOffset: stackOffset(debug.Slots[debug.VarSlots[varID][0]]),
DeclFile: declpos.RelFilename(), DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(), DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(), DeclCol: declpos.Col(),

View File

@ -25,15 +25,7 @@ type Cache struct {
scrSparse []*sparseSet // scratch sparse sets to be re-used. scrSparse []*sparseSet // scratch sparse sets to be re-used.
ValueToProgAfter []*obj.Prog ValueToProgAfter []*obj.Prog
blockDebug []BlockDebug debugState debugState
valueNames [][]SlotID
slotLocs []VarLoc
regContents [][]SlotID
pendingEntries []pendingEntry
pendingSlotLocs []VarLoc
liveSlotSliceBegin int
liveSlots []liveSlot
} }
func (c *Cache) Reset() { func (c *Cache) Reset() {
@ -53,16 +45,5 @@ func (c *Cache) Reset() {
xl[i] = nil xl[i] = nil
} }
c.liveSlots = c.liveSlots[:0]
c.liveSlotSliceBegin = 0
} }
func (c *Cache) AppendLiveSlot(ls liveSlot) {
c.liveSlots = append(c.liveSlots, ls)
}
func (c *Cache) GetLiveSlotSlice() []liveSlot {
s := c.liveSlots[c.liveSlotSliceBegin:]
c.liveSlotSliceBegin = len(c.liveSlots)
return s
}

View File

@ -20,7 +20,7 @@ type VarID int32
// result of decomposing a larger variable. // result of decomposing a larger variable.
type FuncDebug struct { type FuncDebug struct {
// Slots is all the slots used in the debug info, indexed by their SlotID. // Slots is all the slots used in the debug info, indexed by their SlotID.
Slots []*LocalSlot Slots []LocalSlot
// The user variables, indexed by VarID. // The user variables, indexed by VarID.
Vars []GCNode Vars []GCNode
// The slots that make up each variable, indexed by VarID. // The slots that make up each variable, indexed by VarID.
@ -33,6 +33,8 @@ type FuncDebug struct {
} }
type BlockDebug struct { type BlockDebug struct {
// Whether the block had any changes to user variables at all.
relevant bool
// State at the end of the block if it's fully processed. Immutable once initialized. // State at the end of the block if it's fully processed. Immutable once initialized.
endState []liveSlot endState []liveSlot
} }
@ -164,7 +166,7 @@ func (s *debugState) logf(msg string, args ...interface{}) {
type debugState struct { type debugState struct {
// See FuncDebug. // See FuncDebug.
slots []*LocalSlot slots []LocalSlot
vars []GCNode vars []GCNode
varSlots [][]SlotID varSlots [][]SlotID
lists [][]byte lists [][]byte
@ -174,7 +176,6 @@ type debugState struct {
f *Func f *Func
loggingEnabled bool loggingEnabled bool
cache *Cache
registers []Register registers []Register
stackOffset func(LocalSlot) int32 stackOffset func(LocalSlot) int32
ctxt *obj.Link ctxt *obj.Link
@ -189,78 +190,112 @@ type debugState struct {
// The pending location list entry for each user variable, indexed by VarID. // The pending location list entry for each user variable, indexed by VarID.
pendingEntries []pendingEntry pendingEntries []pendingEntry
varParts map[GCNode][]SlotID
blockDebug []BlockDebug
pendingSlotLocs []VarLoc
liveSlots []liveSlot
liveSlotSliceBegin int
partsByVarOffset sort.Interface
} }
func (state *debugState) initializeCache() { func (state *debugState) initializeCache(f *Func, numVars, numSlots int) {
numBlocks := state.f.NumBlocks()
// One blockDebug per block. Initialized in allocBlock. // One blockDebug per block. Initialized in allocBlock.
if cap(state.cache.blockDebug) < numBlocks { if cap(state.blockDebug) < f.NumBlocks() {
state.cache.blockDebug = make([]BlockDebug, numBlocks) state.blockDebug = make([]BlockDebug, f.NumBlocks())
} } else {
// This local variable, and the ones like it below, enable compiler // This local variable, and the ones like it below, enable compiler
// optimizations. Don't inline them. // optimizations. Don't inline them.
b := state.cache.blockDebug[:numBlocks] b := state.blockDebug[:f.NumBlocks()]
for i := range b { for i := range b {
b[i] = BlockDebug{} b[i] = BlockDebug{}
}
} }
// A list of slots per Value. Reuse the previous child slices. // A list of slots per Value. Reuse the previous child slices.
if cap(state.cache.valueNames) < state.f.NumValues() { if cap(state.valueNames) < f.NumValues() {
old := state.cache.valueNames old := state.valueNames
state.cache.valueNames = make([][]SlotID, state.f.NumValues()) state.valueNames = make([][]SlotID, f.NumValues())
copy(state.cache.valueNames, old) copy(state.valueNames, old)
} }
state.valueNames = state.cache.valueNames vn := state.valueNames[:f.NumValues()]
vn := state.valueNames[:state.f.NumValues()]
for i := range vn { for i := range vn {
vn[i] = vn[i][:0] vn[i] = vn[i][:0]
} }
// Slot and register contents for currentState. Cleared by reset(). // Slot and register contents for currentState. Cleared by reset().
if cap(state.cache.slotLocs) < len(state.slots) { if cap(state.currentState.slots) < numSlots {
state.cache.slotLocs = make([]VarLoc, len(state.slots)) state.currentState.slots = make([]VarLoc, numSlots)
} else {
state.currentState.slots = state.currentState.slots[:numSlots]
} }
state.currentState.slots = state.cache.slotLocs[:len(state.slots)] if cap(state.currentState.registers) < len(state.registers) {
if cap(state.cache.regContents) < len(state.registers) { state.currentState.registers = make([][]SlotID, len(state.registers))
state.cache.regContents = make([][]SlotID, len(state.registers)) } else {
state.currentState.registers = state.currentState.registers[:len(state.registers)]
} }
state.currentState.registers = state.cache.regContents[:len(state.registers)]
// Used many times by mergePredecessors. // Used many times by mergePredecessors.
state.liveCount = make([]int, len(state.slots)) if cap(state.liveCount) < numSlots {
state.liveCount = make([]int, numSlots)
} else {
state.liveCount = state.liveCount[:numSlots]
}
// A relatively small slice, but used many times as the return from processValue. // A relatively small slice, but used many times as the return from processValue.
state.changedVars = newSparseSet(len(state.vars)) state.changedVars = newSparseSet(numVars)
// A pending entry per user variable, with space to track each of its pieces. // A pending entry per user variable, with space to track each of its pieces.
nPieces := 0 numPieces := 0
for i := range state.varSlots { for i := range state.varSlots {
nPieces += len(state.varSlots[i]) numPieces += len(state.varSlots[i])
} }
if cap(state.cache.pendingSlotLocs) < nPieces { if cap(state.pendingSlotLocs) < numPieces {
state.cache.pendingSlotLocs = make([]VarLoc, nPieces) state.pendingSlotLocs = make([]VarLoc, numPieces)
} else {
psl := state.pendingSlotLocs[:numPieces]
for i := range psl {
psl[i] = VarLoc{}
}
} }
psl := state.cache.pendingSlotLocs[:nPieces] if cap(state.pendingEntries) < numVars {
for i := range psl { state.pendingEntries = make([]pendingEntry, numVars)
psl[i] = VarLoc{}
} }
if cap(state.cache.pendingEntries) < len(state.vars) { pe := state.pendingEntries[:numVars]
state.cache.pendingEntries = make([]pendingEntry, len(state.vars))
}
pe := state.cache.pendingEntries[:len(state.vars)]
freePieceIdx := 0 freePieceIdx := 0
for varID, slots := range state.varSlots { for varID, slots := range state.varSlots {
pe[varID] = pendingEntry{ pe[varID] = pendingEntry{
pieces: state.cache.pendingSlotLocs[freePieceIdx : freePieceIdx+len(slots)], pieces: state.pendingSlotLocs[freePieceIdx : freePieceIdx+len(slots)],
} }
freePieceIdx += len(slots) freePieceIdx += len(slots)
} }
state.pendingEntries = pe state.pendingEntries = pe
if cap(state.lists) < numVars {
state.lists = make([][]byte, numVars)
} else {
state.lists = state.lists[:numVars]
for i := range state.lists {
state.lists[i] = nil
}
}
state.liveSlots = state.liveSlots[:0]
state.liveSlotSliceBegin = 0
} }
func (state *debugState) allocBlock(b *Block) *BlockDebug { func (state *debugState) allocBlock(b *Block) *BlockDebug {
return &state.cache.blockDebug[b.ID] return &state.blockDebug[b.ID]
}
func (state *debugState) appendLiveSlot(ls liveSlot) {
state.liveSlots = append(state.liveSlots, ls)
}
func (state *debugState) getLiveSlotSlice() []liveSlot {
s := state.liveSlots[state.liveSlotSliceBegin:]
state.liveSlotSliceBegin = len(state.liveSlots)
return s
} }
func (s *debugState) blockEndStateString(b *BlockDebug) string { func (s *debugState) blockEndStateString(b *BlockDebug) string {
@ -301,22 +336,28 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
if f.RegAlloc == nil { if f.RegAlloc == nil {
f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f) f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
} }
state := &debugState{ state := &f.Cache.debugState
loggingEnabled: loggingEnabled, state.loggingEnabled = loggingEnabled
slots: make([]*LocalSlot, len(f.Names)), state.f = f
state.registers = f.Config.registers
state.stackOffset = stackOffset
state.ctxt = ctxt
f: f, if state.varParts == nil {
cache: f.Cache, state.varParts = make(map[GCNode][]SlotID)
registers: f.Config.registers, } else {
stackOffset: stackOffset, for n := range state.varParts {
ctxt: ctxt, delete(state.varParts, n)
}
} }
// Recompose any decomposed variables, and record the names associated with each value. // Recompose any decomposed variables, and establish the canonical
varParts := map[GCNode][]SlotID{} // IDs for each var and slot by filling out state.vars and state.slots.
state.slots = state.slots[:0]
state.vars = state.vars[:0]
for i, slot := range f.Names { for i, slot := range f.Names {
slot := slot state.slots = append(state.slots, slot)
state.slots[i] = &slot
if slot.N.IsSynthetic() { if slot.N.IsSynthetic() {
continue continue
} }
@ -325,27 +366,42 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
for topSlot.SplitOf != nil { for topSlot.SplitOf != nil {
topSlot = topSlot.SplitOf topSlot = topSlot.SplitOf
} }
if _, ok := varParts[topSlot.N]; !ok { if _, ok := state.varParts[topSlot.N]; !ok {
state.vars = append(state.vars, topSlot.N) state.vars = append(state.vars, topSlot.N)
} }
varParts[topSlot.N] = append(varParts[topSlot.N], SlotID(i)) state.varParts[topSlot.N] = append(state.varParts[topSlot.N], SlotID(i))
} }
// Fill in the var<->slot mappings. // Fill in the var<->slot mappings.
state.varSlots = make([][]SlotID, len(state.vars)) if cap(state.varSlots) < len(state.vars) {
state.slotVars = make([]VarID, len(state.slots)) state.varSlots = make([][]SlotID, len(state.vars))
state.lists = make([][]byte, len(state.vars)) } else {
state.varSlots = state.varSlots[:len(state.vars)]
for i := range state.varSlots {
state.varSlots[i] = state.varSlots[i][:0]
}
}
if cap(state.slotVars) < len(state.slots) {
state.slotVars = make([]VarID, len(state.slots))
} else {
state.slotVars = state.slotVars[:len(state.slots)]
}
if state.partsByVarOffset == nil {
state.partsByVarOffset = &partsByVarOffset{}
}
for varID, n := range state.vars { for varID, n := range state.vars {
parts := varParts[n] parts := state.varParts[n]
state.varSlots[varID] = parts state.varSlots[varID] = parts
for _, slotID := range parts { for _, slotID := range parts {
state.slotVars[slotID] = VarID(varID) state.slotVars[slotID] = VarID(varID)
} }
sort.Sort(partsByVarOffset{parts, state.slots}) *state.partsByVarOffset.(*partsByVarOffset) = partsByVarOffset{parts, state.slots}
sort.Sort(state.partsByVarOffset)
} }
state.initializeCache() state.initializeCache(f, len(state.varParts), len(state.slots))
for i, slot := range f.Names { for i, slot := range f.Names {
if slot.N.IsSynthetic() { if slot.N.IsSynthetic() {
continue continue
@ -421,6 +477,7 @@ func (state *debugState) liveness() []*BlockDebug {
} }
locs := state.allocBlock(b) locs := state.allocBlock(b)
locs.relevant = changed
if !changed && startValid { if !changed && startValid {
locs.endState = startState locs.endState = startState
} else { } else {
@ -428,9 +485,9 @@ func (state *debugState) liveness() []*BlockDebug {
if slotLoc.absent() { if slotLoc.absent() {
continue continue
} }
state.cache.AppendLiveSlot(liveSlot{slot: SlotID(slotID), Registers: slotLoc.Registers, StackOffset: slotLoc.StackOffset}) state.appendLiveSlot(liveSlot{slot: SlotID(slotID), Registers: slotLoc.Registers, StackOffset: slotLoc.StackOffset})
} }
locs.endState = state.cache.GetLiveSlotSlice() locs.endState = state.getLiveSlotSlice()
} }
blockLocs[b.ID] = locs blockLocs[b.ID] = locs
} }
@ -641,13 +698,9 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register)
if state.loggingEnabled { if state.loggingEnabled {
state.logf("at %v: %v now in %s\n", v.ID, state.slots[slot], vReg) state.logf("at %v: %v now in %s\n", v.ID, state.slots[slot], vReg)
} }
var loc VarLoc
loc.Registers |= 1 << uint8(vReg.num) last := locs.slots[slot]
if last := locs.slots[slot]; !last.absent() { setSlot(slot, VarLoc{1<<uint8(vReg.num) | last.Registers, last.StackOffset})
loc.StackOffset = last.StackOffset
loc.Registers |= last.Registers
}
setSlot(slot, loc)
} }
} }
return changed return changed
@ -655,17 +708,18 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register)
// varOffset returns the offset of slot within the user variable it was // varOffset returns the offset of slot within the user variable it was
// decomposed from. This has nothing to do with its stack offset. // decomposed from. This has nothing to do with its stack offset.
func varOffset(slot *LocalSlot) int64 { func varOffset(slot LocalSlot) int64 {
offset := slot.Off offset := slot.Off
for ; slot.SplitOf != nil; slot = slot.SplitOf { s := &slot
offset += slot.SplitOffset for ; s.SplitOf != nil; s = s.SplitOf {
offset += s.SplitOffset
} }
return offset return offset
} }
type partsByVarOffset struct { type partsByVarOffset struct {
slotIDs []SlotID slotIDs []SlotID
slots []*LocalSlot slots []LocalSlot
} }
func (a partsByVarOffset) Len() int { return len(a.slotIDs) } func (a partsByVarOffset) Len() int { return len(a.slotIDs) }
@ -730,6 +784,10 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
// Run through the function in program text order, building up location // Run through the function in program text order, building up location
// lists as we go. The heavy lifting has mostly already been done. // lists as we go. The heavy lifting has mostly already been done.
for _, b := range state.f.Blocks { for _, b := range state.f.Blocks {
if !blockLocs[b.ID].relevant {
continue
}
state.mergePredecessors(b, blockLocs) state.mergePredecessors(b, blockLocs)
phisPending := false phisPending := false