Skip to content

Commit 89f7805

Browse files
committed
cmd/compile/internal: merge stack slots for selected local auto vars
Preliminary compiler support for merging/overlapping stack slots of local variables whose access patterns are disjoint. This patch includes changes in AllocFrame to do the actual merging/overlapping based on information returned from a new liveness.MergeLocals helper. The MergeLocals helper identifies candidates by looking for sets of AUTO variables that either A) have the same size and GC shape (if types contain pointers), or B) have the same size (but potentially different types as long as those types have no pointers). Variables must be greater than (3*types.PtrSize) in size to be considered for merging. After forming candidates, MergeLocals collects variables into "can be overlapped" equivalence classes or partitions; this process is driven by an additional liveness analysis pass. Ideally it would be nice to move the existing stackmap liveness pass up before AllocFrame and "widen" it to include merge candidates so that we can do just a single liveness as opposed to two passes, however this may be difficult given that the merge-locals liveness has to take into account writes corresponding to dead stores. This patch also required a change to the way ssa.OpVarDef pseudo-ops are generated; prior to this point they would only be created for variables whose type included pointers; if stack slot merging is enabled then the ssagen code creates OpVarDef ops for all auto vars that are merge candidates. Note that some temporaries created late in the compilation process (e.g. during ssa backend) are difficult to reason about, especially in cases where we take the address of a temp and pass it to the runtime. For the time being we mark most of the vars created post-ssagen as "not a merge candidate". Stack slot merging for locals/autos is enabled by default if "-N" is not in effect, and can be disabled via "-gcflags=-d=mergelocals=0". Fixmes/todos/restrictions: - try lowering size restrictions - re-evaluate the various skips that happen in SSA-created autotmps Fixes #62737. Updates #65532. Updates #65495. Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: Ibc22e8a76c87e47bc9fafe4959804d9ea923623d Reviewed-on: https://go-review.googlesource.com/c/go/+/553055 Reviewed-by: Cherry Mui <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 754f870 commit 89f7805

File tree

14 files changed

+1512
-222
lines changed

14 files changed

+1512
-222
lines changed

src/cmd/compile/internal/base/debug.go

+4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ type DebugFlags struct {
4141
LoopVarHash string `help:"for debugging changes in loop behavior. Overrides experiment and loopvar flag."`
4242
LocationLists int `help:"print information about DWARF location list creation"`
4343
MaxShapeLen int `help:"hash shape names longer than this threshold (default 500)" concurrent:"ok"`
44+
MergeLocals int `help:"merge together non-interfering local stack slots" concurrent:"ok"`
45+
MergeLocalsDumpFunc string `help:"dump specified func in merge locals"`
46+
MergeLocalsHash string `help:"hash value for debugging stack slot merging of local variables" concurrent:"ok"`
47+
MergeLocalsTrace int `help:"trace debug output for locals merging"`
4448
Nil int `help:"print information about nil checks"`
4549
NoOpenDefer int `help:"disable open-coded defers" concurrent:"ok"`
4650
NoRefName int `help:"do not include referenced symbol names in object file" concurrent:"ok"`

src/cmd/compile/internal/base/flag.go

+4
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ func ParseFlags() {
184184
Debug.SyncFrames = -1 // disable sync markers by default
185185
Debug.ZeroCopy = 1
186186
Debug.RangeFuncCheck = 1
187+
Debug.MergeLocals = 1
187188

188189
Debug.Checkptr = -1 // so we can tell whether it is set explicitly
189190

@@ -260,6 +261,9 @@ func ParseFlags() {
260261
if Debug.PGOHash != "" {
261262
PGOHash = NewHashDebug("pgohash", Debug.PGOHash, nil)
262263
}
264+
if Debug.MergeLocalsHash != "" {
265+
MergeLocalsHash = NewHashDebug("mergelocals", Debug.MergeLocalsHash, nil)
266+
}
263267

264268
if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
265269
log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)

src/cmd/compile/internal/base/hashdebug.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ func (d *HashDebug) SetInlineSuffixOnly(b bool) *HashDebug {
5353
// The default compiler-debugging HashDebug, for "-d=gossahash=..."
5454
var hashDebug *HashDebug
5555

56-
var FmaHash *HashDebug // for debugging fused-multiply-add floating point changes
57-
var LoopVarHash *HashDebug // for debugging shared/private loop variable changes
58-
var PGOHash *HashDebug // for debugging PGO optimization decisions
56+
var FmaHash *HashDebug // for debugging fused-multiply-add floating point changes
57+
var LoopVarHash *HashDebug // for debugging shared/private loop variable changes
58+
var PGOHash *HashDebug // for debugging PGO optimization decisions
59+
var MergeLocalsHash *HashDebug // for debugging local stack slot merging changes
5960

6061
// DebugHashMatchPkgFunc reports whether debug variable Gossahash
6162
//

src/cmd/compile/internal/ir/name.go

+3
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ const (
194194
nameLibfuzzer8BitCounter // if PEXTERN should be assigned to __sancov_cntrs section
195195
nameCoverageAuxVar // instrumentation counter var or pkg ID for cmd/cover
196196
nameAlias // is type name an alias
197+
nameNonMergeable // not a candidate for stack slot merging
197198
)
198199

199200
func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 }
@@ -209,6 +210,7 @@ func (n *Name) InlLocal() bool { return n.flags&nameInlLocal !=
209210
func (n *Name) OpenDeferSlot() bool { return n.flags&nameOpenDeferSlot != 0 }
210211
func (n *Name) Libfuzzer8BitCounter() bool { return n.flags&nameLibfuzzer8BitCounter != 0 }
211212
func (n *Name) CoverageAuxVar() bool { return n.flags&nameCoverageAuxVar != 0 }
213+
func (n *Name) NonMergeable() bool { return n.flags&nameNonMergeable != 0 }
212214

213215
func (n *Name) setReadonly(b bool) { n.flags.set(nameReadonly, b) }
214216
func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) }
@@ -223,6 +225,7 @@ func (n *Name) SetInlLocal(b bool) { n.flags.set(nameInlLocal, b
223225
func (n *Name) SetOpenDeferSlot(b bool) { n.flags.set(nameOpenDeferSlot, b) }
224226
func (n *Name) SetLibfuzzer8BitCounter(b bool) { n.flags.set(nameLibfuzzer8BitCounter, b) }
225227
func (n *Name) SetCoverageAuxVar(b bool) { n.flags.set(nameCoverageAuxVar, b) }
228+
func (n *Name) SetNonMergeable(b bool) { n.flags.set(nameNonMergeable, b) }
226229

227230
// OnStack reports whether variable n may reside on the stack.
228231
func (n *Name) OnStack() bool {

0 commit comments

Comments
 (0)