From 9aaa9a204300381128740295af236d50d7ec2a9c Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Thu, 2 Jan 2020 14:25:53 +0100 Subject: [PATCH 1/2] Track register allocations To help debugging keep track of where registers are allocated. After a bit back and forth I ended up with this, which shows sorted physical id and where the virtual register is allocated in the calling code: ``` failed to allocate registers. 38 virtual: 00: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:81 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 00: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:81 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 01: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:83 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 02: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:102 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 02: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:102 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 03: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:102 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 03: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:102 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 05: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:102 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 05: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:102 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 06: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:115 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 06: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:115 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 06: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:115 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr8) 07: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:134 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 07: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:134 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 08: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:160 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 08: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:160 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 09: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:176 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 09: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:176 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 10: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:176 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 10: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:176 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 10: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:176 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr64) 11: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:186 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 11: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:186 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr16) 11: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:186 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 11: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:186 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr64) 11: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:186 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr8) 12: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:191 main.genEncodeBlockAsm() (type:reg.gpv, kind:gpr64) 12: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:191 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr64) 13: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:381 main.emitLiteral() (type:reg.gpv, kind:gpr64) 13: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:381 main.emitLiteral() (type:reg.virtual, kind:gpr16) 13: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:381 main.emitLiteral() (type:reg.virtual, kind:gpr32) 13: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:381 main.emitLiteral() (type:reg.virtual, kind:gpr8) 14: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:382 main.emitLiteral() (type:reg.gpv, kind:gpr64) 14: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:382 main.emitLiteral() (type:reg.virtual, kind:gpr32) 14: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:382 main.emitLiteral() (type:reg.virtual, kind:gpr8) 15: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:247 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr16) 15: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:247 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr32) 15: e:/gopath/src/github.com/klauspost/compress/s2/gen.go:247 main.genEncodeBlockAsm() (type:reg.virtual, kind:gpr8) ``` When #100 is fixed this will allow easier tracking of what avo considers allocated. If nothing else, it could also help debugging #100. Using the package path is the most reasonable approach I could find to determine what to store. --- pass/alloc.go | 15 ++++++++++++++- reg/func.go | 28 ++++++++++++++++++++++++++++ reg/types.go | 29 +++++++++++++++++++++-------- reg/x86.go | 36 +++++++++++++++++++++++++++++++++++- 4 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 reg/func.go diff --git a/pass/alloc.go b/pass/alloc.go index c67a211c..9f48cf73 100644 --- a/pass/alloc.go +++ b/pass/alloc.go @@ -2,7 +2,10 @@ package pass import ( "errors" + "fmt" "math" + "sort" + "strings" "github.com/mmcloughlin/avo/reg" ) @@ -156,7 +159,17 @@ func (a *Allocator) discardconflicting(v reg.Virtual, p reg.Physical) { func (a *Allocator) alloc(v reg.Virtual) error { ps := a.possible[v] if len(ps) == 0 { - return errors.New("failed to allocate registers") + var allocs []string + for i, v := range a.allocation { + if ai, ok := i.(reg.AllocInfoer); ok { + allocs = append(allocs, fmt.Sprintf("%02d: %s (type:%T, kind:%v%d)", v.PhysicalID(), ai.AllocInfo(), i, i.Kind(), i.Size()*8)) + } else { + allocs = append(allocs, fmt.Sprintf("%02d: No alloc info. (type:%T, kind:%v%d)", v.PhysicalID(), i, i.Kind(), i.Size()*8)) + } + } + sort.Strings(allocs) + err := fmt.Sprintf("failed to allocate registers. %d virtual:\n%s", len(a.allocation), strings.Join(allocs, "\n")) + return errors.New(err) } p := ps[0] a.allocation[v] = p diff --git a/reg/func.go b/reg/func.go new file mode 100644 index 00000000..3161838b --- /dev/null +++ b/reg/func.go @@ -0,0 +1,28 @@ +package reg + +import ( + "fmt" + "runtime" + "strings" +) + +// getFnNameFile returns the first file+function in the call stack that is not avo. +func getFnNameFile(depth int) string { + caller := [100]uintptr{0} + runtime.Callers(1+depth, caller[:]) + frames := runtime.CallersFrames(caller[:]) + for { + f, more := frames.Next() + file := f.File + line := f.Line + fnName := f.Func.Name() + + if !strings.Contains(file, "github.com/mmcloughlin/avo") { + return fmt.Sprintf("%s:%d %s()", file, line, fnName) + } + if !more { + break + } + } + return "" +} diff --git a/reg/types.go b/reg/types.go index cd9f32ac..53531451 100644 --- a/reg/types.go +++ b/reg/types.go @@ -106,18 +106,30 @@ type virtual struct { id VID kind Kind Width - mask uint16 + mask uint16 + allocAt string } // NewVirtual builds a Virtual register. func NewVirtual(id VID, k Kind, w Width) Virtual { return virtual{ - id: id, - kind: k, - Width: w, + allocAt: getFnNameFile(1), + id: id, + kind: k, + Width: w, } } +// AllocInfoer allows to track where resources were allocated. +type AllocInfoer interface { + AllocInfo() string +} + +// AllocInfo returns allocation info. +func (v virtual) AllocInfo() string { + return v.allocAt +} + func (v virtual) VirtualID() VID { return v.id } func (v virtual) Kind() Kind { return v.kind } @@ -132,10 +144,11 @@ func (v virtual) SatisfiedBy(p Physical) bool { func (v virtual) as(s Spec) Register { return virtual{ - id: v.id, - kind: v.kind, - Width: Width(s.Size()), - mask: s.Mask(), + id: v.id, + kind: v.kind, + Width: Width(s.Size()), + mask: s.Mask(), + allocAt: v.allocAt, } } diff --git a/reg/x86.go b/reg/x86.go index 96316bd5..fa37bc6f 100644 --- a/reg/x86.go +++ b/reg/x86.go @@ -20,6 +20,20 @@ var ( } ) +// String returns a human readable representation of the Kind. +func (k Kind) String() string { + switch k { + case KindPseudo: + return "pseudo" + case KindGP: + return "gpr" + case KindVector: + return "vector" + default: + return "invalid" + } +} + var familiesByKind = map[Kind]*Family{} func init() { @@ -84,9 +98,29 @@ type GPVirtual interface { type gpv struct { Virtual GP + allocAt string +} + +func newgpv(v Virtual) GPVirtual { + g := gpv{ + Virtual: v, + GP: gpcasts{v}, + allocAt: getFnNameFile(1), + } + if ai, ok := v.(AllocInfoer); ok { + parent := ai.AllocInfo() + if g.allocAt != parent { + // Only add ourself if different from parent. + g.allocAt = parent + " -> " + g.allocAt + } + } + return g } -func newgpv(v Virtual) GPVirtual { return gpv{Virtual: v, GP: gpcasts{v}} } +// AllocInfo returns allocation info. +func (g gpv) AllocInfo() string { + return g.allocAt +} func gp(s Spec, id PID, name string, flags ...Info) GPPhysical { r := newgpp(newregister(GeneralPurpose, s, id, name, flags...)) From fe08d149a281936bd231d2450fb1d1457ad400c0 Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Thu, 2 Jan 2020 14:57:53 +0100 Subject: [PATCH 2/2] Separate AsX() in printing. --- reg/types.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/reg/types.go b/reg/types.go index 53531451..2d382010 100644 --- a/reg/types.go +++ b/reg/types.go @@ -144,11 +144,12 @@ func (v virtual) SatisfiedBy(p Physical) bool { func (v virtual) as(s Spec) Register { return virtual{ - id: v.id, - kind: v.kind, - Width: Width(s.Size()), - mask: s.Mask(), - allocAt: v.allocAt, + id: v.id, + kind: v.kind, + Width: Width(s.Size()), + mask: s.Mask(), + // Non-breaking space for sorting. + allocAt: fmt.Sprintf("\u00A0- As%v() %s", s.String(), getFnNameFile(2)), } } @@ -248,6 +249,16 @@ func (s Spec) Size() uint { return (x >> 1) + (x & 1) } +// Size returns the register width in bytes. +func (s Spec) String() string { + switch s { + case S8H: + return "8H" + default: + return fmt.Sprint(s.Size() * 8) + } +} + // AreConflicting returns whether registers conflict with each other. func AreConflicting(x, y Physical) bool { return x.Kind() == y.Kind() && x.PhysicalID() == y.PhysicalID() && (x.Mask()&y.Mask()) != 0