From 65e694750a8c15de4a0214f0709ce89b106906b8 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Thu, 19 Oct 2023 14:14:20 +0900 Subject: [PATCH] wazevo(regalloc): removes map usage in real reg tracking Signed-off-by: Takeshi Yoneda --- .../engine/wazevo/backend/isa/arm64/abi.go | 6 ++ .../backend/isa/arm64/machine_regalloc.go | 1 + .../wazevo/backend/regalloc/regalloc.go | 61 +++++++++---------- .../wazevo/backend/regalloc/regalloc_test.go | 33 +++++----- 4 files changed, 49 insertions(+), 52 deletions(-) diff --git a/internal/engine/wazevo/backend/isa/arm64/abi.go b/internal/engine/wazevo/backend/isa/arm64/abi.go index 21707b7880..550b8dcb87 100644 --- a/internal/engine/wazevo/backend/isa/arm64/abi.go +++ b/internal/engine/wazevo/backend/isa/arm64/abi.go @@ -48,6 +48,12 @@ var regInfo = ®alloc.RegisterInfo{ v0: v0VReg, v1: v1VReg, v2: v2VReg, v3: v3VReg, v4: v4VReg, v5: v5VReg, v6: v6VReg, v7: v7VReg, v8: v8VReg, v9: v9VReg, v10: v10VReg, v11: v11VReg, v12: v12VReg, v13: v13VReg, v14: v14VReg, v15: v15VReg, v16: v16VReg, v17: v17VReg, v18: v18VReg, v19: v19VReg, v20: v20VReg, v21: v21VReg, v22: v22VReg, v23: v23VReg, v24: v24VReg, v25: v25VReg, v26: v26VReg, v27: v27VReg, v28: v28VReg, v29: v29VReg, v30: v30VReg, v31: v31VReg, }, RealRegName: func(r regalloc.RealReg) string { return regNames[r] }, + RealRegType: func(r regalloc.RealReg) regalloc.RegType { + if r < v0 { + return regalloc.RegTypeInt + } + return regalloc.RegTypeFloat + }, } // abiImpl implements backend.FunctionABI. diff --git a/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go b/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go index 1ea3cd3aac..a5b313eb24 100644 --- a/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go +++ b/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go @@ -227,6 +227,7 @@ func (m *machine) RegisterInfo(debug bool) *regalloc.RegisterInfo { regInfoDebug.CallerSavedRegisters = regInfo.CallerSavedRegisters regInfoDebug.RealRegToVReg = regInfo.RealRegToVReg regInfoDebug.RealRegName = regInfo.RealRegName + regInfoDebug.RealRegType = regInfo.RealRegType regInfoDebug.AllocatableRegisters[regalloc.RegTypeFloat] = []regalloc.RealReg{ v18, // One callee saved. v7, v6, v5, v4, v3, v2, v1, v0, // Allocatable sets == Argument registers. diff --git a/internal/engine/wazevo/backend/regalloc/regalloc.go b/internal/engine/wazevo/backend/regalloc/regalloc.go index 9936e472ec..249161e98d 100644 --- a/internal/engine/wazevo/backend/regalloc/regalloc.go +++ b/internal/engine/wazevo/backend/regalloc/regalloc.go @@ -42,6 +42,7 @@ type ( RealRegToVReg []VReg // RealRegName returns the name of the given RealReg for debugging. RealRegName func(r RealReg) string + RealRegType func(r RealReg) RegType } // Allocator is a register allocator. @@ -80,8 +81,8 @@ type ( lastUses map[VReg]programCounter kills map[VReg]programCounter // Pre-colored real registers can have multiple live ranges in one block. - realRegUses map[VReg][]programCounter - realRegDefs map[VReg][]programCounter + realRegUses [vRegIDReservedForRealNum][]programCounter + realRegDefs [vRegIDReservedForRealNum][]programCounter intervalMng *intervalManager } @@ -185,9 +186,10 @@ func (a *Allocator) livenessAnalysis(f Function) { } for _, def := range instr.Defs() { dstVR = def + defID := def.ID() pos := pc + pcDefOffset if def.IsRealReg() { - info.realRegDefs[def] = append(info.realRegDefs[def], pos) + info.realRegDefs[defID] = append(info.realRegDefs[defID], pos) } else { if _, ok := info.defs[def]; !ok { // This means that this VReg is defined multiple times in a series of instructions @@ -407,37 +409,30 @@ func (a *Allocator) buildLiveRangesForNonReals(info *blockInfo) { func (a *Allocator) buildLiveRangesForReals(info *blockInfo) { ds, us := info.realRegDefs, info.realRegUses - // In order to do the deterministic compilation, we need to sort the registers. - a.vs = a.vs[:0] - for v := range us { + for i := 0; i < RealRegsNumMax; i++ { + r := RealReg(i) // Non allocation target registers are not needed here. - if !a.allocatableSet[v.RealReg()] { + if !a.allocatableSet[r] { continue } - a.vs = append(a.vs, v) - } - sort.SliceStable(a.vs, func(i, j int) bool { - return a.vs[i].RealReg() < a.vs[j].RealReg() - }) - for _, v := range a.vs { - uses := us[v] - defs, ok := ds[v] - if !ok || len(defs) != len(uses) { + uses := us[r] + defs := ds[r] + if len(defs) != len(uses) { // This is likely a bug of the Instr interface implementation and/or ABI around call instructions. // E.g. call or ret instructions should specify that they use all the real registers (calling convention). panic( fmt.Sprintf( "BUG: real register (%s) is defined and used, but the number of defs and uses are different: %d (defs) != %d (uses)", - a.regInfo.RealRegName(v.RealReg()), len(defs), len(uses), + a.regInfo.RealRegName(r), len(defs), len(uses), ), ) } for i := range uses { n := a.allocateNode() - n.r = v.RealReg() - n.v = v + n.r = r + n.v = FromRealReg(r, a.regInfo.RealRegType(r)) defined, used := defs[i], uses[i] intervalNode := info.intervalMng.insert(n, defined, used) n.ranges = append(n.ranges, intervalNode) @@ -558,26 +553,22 @@ func (a *Allocator) initBlockInfo(i *blockInfo) { } else { resetMap(a, i.kills) } - if i.realRegUses == nil { - i.realRegUses = make(map[VReg][]programCounter) - } else { - resetMap(a, i.realRegUses) - } - if i.realRegDefs == nil { - i.realRegDefs = make(map[VReg][]programCounter) - } else { - resetMap(a, i.realRegDefs) + + for index := range i.realRegUses { + i.realRegUses[index] = i.realRegUses[index][:0] + i.realRegDefs[index] = i.realRegDefs[index][:0] } } func (i *blockInfo) addRealRegUsage(v VReg, pc programCounter) { - defs := i.realRegDefs[v] + id := v.ID() + defs := i.realRegDefs[id] if len(defs) == 0 { // If the definition not found yet but used, this must be a function preamble, // so we let's assume it is defined at the beginning. - i.realRegDefs[v] = append(i.realRegDefs[v], 0) + i.realRegDefs[id] = append(i.realRegDefs[id], 0) } - i.realRegUses[v] = append(i.realRegUses[v], pc) + i.realRegUses[id] = append(i.realRegUses[id], pc) } // Format is for debugging. @@ -605,11 +596,15 @@ func (i *blockInfo) Format(ri *RegisterInfo) string { } buf.WriteString("\n\trealRegUses: ") for v, pos := range i.realRegUses { - buf.WriteString(fmt.Sprintf("%s@%v ", ri.RealRegName(v.RealReg()), pos)) + if len(pos) > 0 { + buf.WriteString(fmt.Sprintf("%s@%v ", ri.RealRegName(RealReg(v)), pos)) + } } buf.WriteString("\n\trealRegDefs: ") for v, pos := range i.realRegDefs { - buf.WriteString(fmt.Sprintf("%s@%v ", ri.RealRegName(v.RealReg()), pos)) + if len(pos) > 0 { + buf.WriteString(fmt.Sprintf("%s@%v ", ri.RealRegName(RealReg(v)), pos)) + } } return buf.String() } diff --git a/internal/engine/wazevo/backend/regalloc/regalloc_test.go b/internal/engine/wazevo/backend/regalloc/regalloc_test.go index 9a6d13cb78..0048c525f3 100644 --- a/internal/engine/wazevo/backend/regalloc/regalloc_test.go +++ b/internal/engine/wazevo/backend/regalloc/regalloc_test.go @@ -7,7 +7,8 @@ import ( ) func TestAllocator_livenessAnalysis(t *testing.T) { - realReg, realReg2 := FromRealReg(50, RegTypeInt), FromRealReg(100, RegTypeInt) + const realRegID, realRegID2 = 50, 100 + realReg, realReg2 := FromRealReg(realRegID, RegTypeInt), FromRealReg(realRegID2, RegTypeInt) const phiVReg = 12345 for _, tc := range []struct { name string @@ -83,11 +84,11 @@ func TestAllocator_livenessAnalysis(t *testing.T) { 4: pcStride + pcDefOffset, 5: pcStride + pcDefOffset, }, - realRegUses: map[VReg][]programCounter{ - realReg: {pcStride*2 + pcUseOffset}, + realRegUses: [vRegIDReservedForRealNum][]programCounter{ + realRegID: {pcStride*2 + pcUseOffset}, }, - realRegDefs: map[VReg][]programCounter{ - realReg: {pcDefOffset}, + realRegDefs: [vRegIDReservedForRealNum][]programCounter{ + realRegID: {pcDefOffset}, }, }, 2: { @@ -144,13 +145,13 @@ func TestAllocator_livenessAnalysis(t *testing.T) { liveOuts: map[VReg]struct{}{1000: {}}, lastUses: map[VReg]programCounter{1: pcUseOffset}, kills: map[VReg]programCounter{1: pcUseOffset}, - realRegDefs: map[VReg][]programCounter{ - realReg: {pcDefOffset, pcStride*4 + pcDefOffset}, - realReg2: {pcStride*2 + pcDefOffset}, + realRegDefs: [vRegIDReservedForRealNum][]programCounter{ + realRegID: {pcDefOffset, pcStride*4 + pcDefOffset}, + realRegID2: {pcStride*2 + pcDefOffset}, }, - realRegUses: map[VReg][]programCounter{ - realReg: {pcStride + pcUseOffset, pcStride*5 + pcUseOffset}, - realReg2: {pcStride*3 + pcUseOffset}, + realRegUses: [vRegIDReservedForRealNum][]programCounter{ + realRegID: {pcStride + pcUseOffset, pcStride*5 + pcUseOffset}, + realRegID2: {pcStride*3 + pcUseOffset}, }, }, 2: { @@ -158,8 +159,8 @@ func TestAllocator_livenessAnalysis(t *testing.T) { liveOuts: map[VReg]struct{}{1000: {}}, lastUses: map[VReg]programCounter{2: pcUseOffset}, kills: map[VReg]programCounter{2: pcUseOffset}, - realRegUses: map[VReg][]programCounter{realReg2: {pcUseOffset}}, - realRegDefs: map[VReg][]programCounter{realReg2: {0}}, + realRegUses: [vRegIDReservedForRealNum][]programCounter{realRegID2: {pcUseOffset}}, + realRegDefs: [vRegIDReservedForRealNum][]programCounter{realRegID2: {0}}, }, 3: { liveIns: map[VReg]struct{}{1000: {}}, @@ -535,12 +536,6 @@ func initMapInInfo(info *blockInfo) { if info.lastUses == nil { info.lastUses = make(map[VReg]programCounter) } - if info.realRegUses == nil { - info.realRegUses = make(map[VReg][]programCounter) - } - if info.realRegDefs == nil { - info.realRegDefs = make(map[VReg][]programCounter) - } } func TestNode_assignedRealReg(t *testing.T) {