wazevo: add VRegTable data structure (#1802)
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import (
|
|||||||
const xArgRetRegMax, vArgRetRegMax = x7, v7 // x0-x7 & v0-v7.
|
const xArgRetRegMax, vArgRetRegMax = x7, v7 // x0-x7 & v0-v7.
|
||||||
|
|
||||||
var regInfo = ®alloc.RegisterInfo{
|
var regInfo = ®alloc.RegisterInfo{
|
||||||
AllocatableRegisters: [regalloc.RegTypeNum][]regalloc.RealReg{
|
AllocatableRegisters: [regalloc.NumRegType][]regalloc.RealReg{
|
||||||
// We don't allocate:
|
// We don't allocate:
|
||||||
// - x18: Reserved by the macOS: https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-purpose-of-specific-CPU-registers
|
// - x18: Reserved by the macOS: https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-purpose-of-specific-CPU-registers
|
||||||
// - x28: Reserved by Go runtime.
|
// - x28: Reserved by Go runtime.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package regalloc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
|
"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
|
||||||
)
|
)
|
||||||
@@ -13,6 +14,10 @@ type VReg uint64
|
|||||||
// VRegID is the lower 32bit of VReg, which is the pure identifier of VReg without RealReg info.
|
// VRegID is the lower 32bit of VReg, which is the pure identifier of VReg without RealReg info.
|
||||||
type VRegID uint32
|
type VRegID uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxVRegID = ^VRegID(0)
|
||||||
|
)
|
||||||
|
|
||||||
// RealReg returns the RealReg of this VReg.
|
// RealReg returns the RealReg of this VReg.
|
||||||
func (v VReg) RealReg() RealReg {
|
func (v VReg) RealReg() RealReg {
|
||||||
return RealReg(v >> 32)
|
return RealReg(v >> 32)
|
||||||
@@ -58,6 +63,130 @@ func (v VReg) Valid() bool {
|
|||||||
return v.ID() != vRegIDInvalid && v.RegType() != RegTypeInvalid
|
return v.ID() != vRegIDInvalid && v.RegType() != RegTypeInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VRegIDMinSet is used to collect the minimum ID by type in a collection of
|
||||||
|
// virtual registers.
|
||||||
|
//
|
||||||
|
// We store the min values + 1 so the zero-value of the VRegIDMinSet is valid.
|
||||||
|
type VRegIDMinSet [NumRegType]VRegID
|
||||||
|
|
||||||
|
func (mins *VRegIDMinSet) Min(t RegType) VRegID {
|
||||||
|
return mins[t] - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mins *VRegIDMinSet) Observe(v VReg) {
|
||||||
|
if rt, id := v.RegType(), v.ID(); id < (mins[rt] - 1) {
|
||||||
|
mins[rt] = id + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VRegTable is a data structure designed for fast association of program
|
||||||
|
// counters to virtual registers.
|
||||||
|
type VRegTable [NumRegType]VRegTypeTable
|
||||||
|
|
||||||
|
func (t *VRegTable) Contains(v VReg) bool {
|
||||||
|
return t[v.RegType()].Contains(v.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VRegTable) Lookup(v VReg) programCounter {
|
||||||
|
return t[v.RegType()].Lookup(v.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VRegTable) Insert(v VReg, p programCounter) {
|
||||||
|
if v.IsRealReg() {
|
||||||
|
panic("BUG: cannot insert real registers in virtual register table")
|
||||||
|
}
|
||||||
|
t[v.RegType()].Insert(v.ID(), p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VRegTable) Range(f func(VReg, programCounter)) {
|
||||||
|
for i := range t {
|
||||||
|
t[i].Range(func(id VRegID, p programCounter) {
|
||||||
|
f(VReg(id).SetRegType(RegType(i)), p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VRegTable) Reset(minVRegIDs VRegIDMinSet) {
|
||||||
|
for i := range t {
|
||||||
|
t[i].Reset(minVRegIDs.Min(RegType(i)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VRegTypeTable implements a table for virtual registers of a specific type.
|
||||||
|
//
|
||||||
|
// The parent VRegTable uses 4 instances of this type to maintain a table for
|
||||||
|
// each virtual register type.
|
||||||
|
//
|
||||||
|
// The virtual register type table uses a bitset to accelerate checking whether
|
||||||
|
// a virtual register exists in the table. The bitset also helps identify which
|
||||||
|
// slots in the program counter array are used, since the table is sparse and
|
||||||
|
// some locations may not be occupied and will have the value zero even though
|
||||||
|
// zero is a valid program counter value.
|
||||||
|
type VRegTypeTable struct {
|
||||||
|
min VRegID
|
||||||
|
set bitset
|
||||||
|
pcs []programCounter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VRegTypeTable) Contains(id VRegID) bool {
|
||||||
|
return t.set.has(uint(id - t.min))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VRegTypeTable) Lookup(id VRegID) programCounter {
|
||||||
|
if id := int(id - t.min); t.set.has(uint(id)) {
|
||||||
|
return t.pcs[id]
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VRegTypeTable) Insert(id VRegID, p programCounter) {
|
||||||
|
if p < 0 {
|
||||||
|
panic("BUG: cannot insert negative program counter in virtual register table")
|
||||||
|
}
|
||||||
|
i := int(id - t.min)
|
||||||
|
if len(t.pcs) <= i {
|
||||||
|
t.pcs = append(t.pcs, make([]programCounter, (i+1)-len(t.pcs))...)
|
||||||
|
}
|
||||||
|
t.set.set(uint(i))
|
||||||
|
t.pcs[i] = p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VRegTypeTable) Range(f func(VRegID, programCounter)) {
|
||||||
|
t.set.scan(func(i uint) { f(VRegID(i)+t.min, t.pcs[i]) })
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VRegTypeTable) Reset(minVRegID VRegID) {
|
||||||
|
t.min = minVRegID
|
||||||
|
t.set = nil
|
||||||
|
t.pcs = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type bitset []uint64
|
||||||
|
|
||||||
|
func (b bitset) scan(f func(uint)) {
|
||||||
|
for i, v := range b {
|
||||||
|
for j := uint(i * 64); v != 0; j++ {
|
||||||
|
n := uint(bits.TrailingZeros64(v))
|
||||||
|
j += n
|
||||||
|
v >>= (n + 1)
|
||||||
|
f(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b bitset) has(i uint) bool {
|
||||||
|
index, shift := i/64, i%64
|
||||||
|
return index < uint(len(b)) && ((b[index] & (1 << shift)) != 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bitset) set(i uint) {
|
||||||
|
index, shift := i/64, i%64
|
||||||
|
if index >= uint(len(*b)) {
|
||||||
|
*b = append(*b, make([]uint64, (index+1)-uint(len(*b)))...)
|
||||||
|
}
|
||||||
|
(*b)[index] |= 1 << shift
|
||||||
|
}
|
||||||
|
|
||||||
// RealReg represents a physical register.
|
// RealReg represents a physical register.
|
||||||
type RealReg byte
|
type RealReg byte
|
||||||
|
|
||||||
@@ -95,7 +224,7 @@ const (
|
|||||||
RegTypeInvalid RegType = iota
|
RegTypeInvalid RegType = iota
|
||||||
RegTypeInt
|
RegTypeInt
|
||||||
RegTypeFloat
|
RegTypeFloat
|
||||||
RegTypeNum
|
NumRegType
|
||||||
)
|
)
|
||||||
|
|
||||||
// String implements fmt.Stringer.
|
// String implements fmt.Stringer.
|
||||||
|
|||||||
@@ -25,3 +25,33 @@ func Test_FromRealReg(t *testing.T) {
|
|||||||
require.Equal(t, RealReg(5), r.RealReg())
|
require.Equal(t, RealReg(5), r.RealReg())
|
||||||
require.Equal(t, VRegID(5), r.ID())
|
require.Equal(t, VRegID(5), r.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVRegTable(t *testing.T) {
|
||||||
|
min := VRegIDMinSet{}
|
||||||
|
min.Observe(VReg(vRegIDReservedForRealNum + 2))
|
||||||
|
min.Observe(VReg(vRegIDReservedForRealNum + 1))
|
||||||
|
min.Observe(VReg(vRegIDReservedForRealNum + 0))
|
||||||
|
|
||||||
|
table := VRegTable{}
|
||||||
|
table.Reset(min)
|
||||||
|
table.Insert(VReg(vRegIDReservedForRealNum+0), 1)
|
||||||
|
table.Insert(VReg(vRegIDReservedForRealNum+1), 10)
|
||||||
|
table.Insert(VReg(vRegIDReservedForRealNum+2), 100)
|
||||||
|
|
||||||
|
vregs := map[VReg]programCounter{}
|
||||||
|
table.Range(func(v VReg, p programCounter) {
|
||||||
|
vregs[v] = p
|
||||||
|
})
|
||||||
|
require.Equal(t, 3, len(vregs))
|
||||||
|
|
||||||
|
for v, p := range vregs {
|
||||||
|
require.True(t, table.Contains(v))
|
||||||
|
require.Equal(t, p, table.Lookup(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
table.Range(func(v VReg, p programCounter) {
|
||||||
|
require.Equal(t, vregs[v], p)
|
||||||
|
delete(vregs, v)
|
||||||
|
})
|
||||||
|
require.Equal(t, 0, len(vregs))
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ type (
|
|||||||
RegisterInfo struct {
|
RegisterInfo struct {
|
||||||
// AllocatableRegisters is a 2D array of allocatable RealReg, indexed by regTypeNum and regNum.
|
// AllocatableRegisters is a 2D array of allocatable RealReg, indexed by regTypeNum and regNum.
|
||||||
// The order matters: the first element is the most preferred one when allocating.
|
// The order matters: the first element is the most preferred one when allocating.
|
||||||
AllocatableRegisters [RegTypeNum][]RealReg
|
AllocatableRegisters [NumRegType][]RealReg
|
||||||
CalleeSavedRegisters [RealRegsNumMax]bool
|
CalleeSavedRegisters [RealRegsNumMax]bool
|
||||||
CallerSavedRegisters [RealRegsNumMax]bool
|
CallerSavedRegisters [RealRegsNumMax]bool
|
||||||
RealRegToVReg []VReg
|
RealRegToVReg []VReg
|
||||||
@@ -78,7 +78,7 @@ type (
|
|||||||
liveOuts map[VReg]struct{}
|
liveOuts map[VReg]struct{}
|
||||||
liveIns map[VReg]struct{}
|
liveIns map[VReg]struct{}
|
||||||
defs map[VReg]programCounter
|
defs map[VReg]programCounter
|
||||||
lastUses map[VReg]programCounter
|
lastUses VRegTable
|
||||||
kills map[VReg]programCounter
|
kills map[VReg]programCounter
|
||||||
// Pre-colored real registers can have multiple live ranges in one block.
|
// Pre-colored real registers can have multiple live ranges in one block.
|
||||||
realRegUses [vRegIDReservedForRealNum][]programCounter
|
realRegUses [vRegIDReservedForRealNum][]programCounter
|
||||||
@@ -172,6 +172,19 @@ func (a *Allocator) livenessAnalysis(f Function) {
|
|||||||
for blk := f.PostOrderBlockIteratorBegin(); blk != nil; blk = f.PostOrderBlockIteratorNext() {
|
for blk := f.PostOrderBlockIteratorBegin(); blk != nil; blk = f.PostOrderBlockIteratorNext() {
|
||||||
info := a.blockInfoAt(blk.ID())
|
info := a.blockInfoAt(blk.ID())
|
||||||
|
|
||||||
|
// We have to do a first pass to find the lowest VRegID in the block;
|
||||||
|
// this is used to reduce memory utilization in the VRegTable, which
|
||||||
|
// can avoid allocating memory for registers zero to minVRegID-1.
|
||||||
|
minVRegID := VRegIDMinSet{}
|
||||||
|
for instr := blk.InstrIteratorBegin(); instr != nil; instr = blk.InstrIteratorNext() {
|
||||||
|
for _, use := range instr.Uses() {
|
||||||
|
if !use.IsRealReg() {
|
||||||
|
minVRegID.Observe(use)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info.lastUses.Reset(minVRegID)
|
||||||
|
|
||||||
var pc programCounter
|
var pc programCounter
|
||||||
for instr := blk.InstrIteratorBegin(); instr != nil; instr = blk.InstrIteratorNext() {
|
for instr := blk.InstrIteratorBegin(); instr != nil; instr = blk.InstrIteratorNext() {
|
||||||
var srcVR, dstVR VReg
|
var srcVR, dstVR VReg
|
||||||
@@ -181,7 +194,7 @@ func (a *Allocator) livenessAnalysis(f Function) {
|
|||||||
if use.IsRealReg() {
|
if use.IsRealReg() {
|
||||||
info.addRealRegUsage(use, pos)
|
info.addRealRegUsage(use, pos)
|
||||||
} else {
|
} else {
|
||||||
info.lastUses[use] = pos
|
info.lastUses.Insert(use, pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, def := range instr.Defs() {
|
for _, def := range instr.Defs() {
|
||||||
@@ -209,7 +222,6 @@ func (a *Allocator) livenessAnalysis(f Function) {
|
|||||||
}
|
}
|
||||||
pc += pcStride
|
pc += pcStride
|
||||||
}
|
}
|
||||||
|
|
||||||
if wazevoapi.RegAllocLoggingEnabled {
|
if wazevoapi.RegAllocLoggingEnabled {
|
||||||
fmt.Printf("prepared block info for block[%d]:\n%s\n\n", blk.ID(), info.Format(a.regInfo))
|
fmt.Printf("prepared block info for block[%d]:\n%s\n\n", blk.ID(), info.Format(a.regInfo))
|
||||||
}
|
}
|
||||||
@@ -231,13 +243,13 @@ func (a *Allocator) livenessAnalysis(f Function) {
|
|||||||
// Now that we finished gathering liveIns, liveOuts, defs, and lastUses, the only thing left is to construct kills.
|
// Now that we finished gathering liveIns, liveOuts, defs, and lastUses, the only thing left is to construct kills.
|
||||||
for blk := f.PostOrderBlockIteratorBegin(); blk != nil; blk = f.PostOrderBlockIteratorNext() { // Order doesn't matter.
|
for blk := f.PostOrderBlockIteratorBegin(); blk != nil; blk = f.PostOrderBlockIteratorNext() { // Order doesn't matter.
|
||||||
info := a.blockInfoAt(blk.ID())
|
info := a.blockInfoAt(blk.ID())
|
||||||
lastUses, outs := info.lastUses, info.liveOuts
|
outs := info.liveOuts
|
||||||
for use, pc := range lastUses {
|
info.lastUses.Range(func(use VReg, pc programCounter) {
|
||||||
// Usage without live-outs is a kill.
|
// Usage without live-outs is a kill.
|
||||||
if _, ok := outs[use]; !ok {
|
if _, ok := outs[use]; !ok {
|
||||||
info.kills[use] = pc
|
info.kills[use] = pc
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
if wazevoapi.RegAllocLoggingEnabled {
|
if wazevoapi.RegAllocLoggingEnabled {
|
||||||
fmt.Printf("\nfinalized info for block[%d]:\n%s\n", blk.ID(), info.Format(a.regInfo))
|
fmt.Printf("\nfinalized info for block[%d]:\n%s\n", blk.ID(), info.Format(a.regInfo))
|
||||||
@@ -251,7 +263,7 @@ func (a *Allocator) beginUpAndMarkStack(f Function, v VReg, isPhi bool, phiDefin
|
|||||||
panic(fmt.Sprintf("block without predecessor must be optimized out by the compiler: %d", blk.ID()))
|
panic(fmt.Sprintf("block without predecessor must be optimized out by the compiler: %d", blk.ID()))
|
||||||
}
|
}
|
||||||
info := a.blockInfoAt(blk.ID())
|
info := a.blockInfoAt(blk.ID())
|
||||||
if _, ok := info.lastUses[v]; !ok {
|
if !info.lastUses.Contains(v) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// TODO: we might want to avoid recursion here.
|
// TODO: we might want to avoid recursion here.
|
||||||
@@ -463,7 +475,7 @@ func (a *Allocator) Reset() {
|
|||||||
|
|
||||||
func (a *Allocator) allocateBlockInfo(blockID int) *blockInfo {
|
func (a *Allocator) allocateBlockInfo(blockID int) *blockInfo {
|
||||||
if blockID >= len(a.blockInfos) {
|
if blockID >= len(a.blockInfos) {
|
||||||
a.blockInfos = append(a.blockInfos, make([]blockInfo, blockID+1)...)
|
a.blockInfos = append(a.blockInfos, make([]blockInfo, (blockID+1)-len(a.blockInfos))...)
|
||||||
}
|
}
|
||||||
info := &a.blockInfos[blockID]
|
info := &a.blockInfos[blockID]
|
||||||
a.initBlockInfo(info)
|
a.initBlockInfo(info)
|
||||||
@@ -543,11 +555,6 @@ func (a *Allocator) initBlockInfo(i *blockInfo) {
|
|||||||
} else {
|
} else {
|
||||||
resetMap(a, i.defs)
|
resetMap(a, i.defs)
|
||||||
}
|
}
|
||||||
if i.lastUses == nil {
|
|
||||||
i.lastUses = make(map[VReg]programCounter)
|
|
||||||
} else {
|
|
||||||
resetMap(a, i.lastUses)
|
|
||||||
}
|
|
||||||
if i.kills == nil {
|
if i.kills == nil {
|
||||||
i.kills = make(map[VReg]programCounter)
|
i.kills = make(map[VReg]programCounter)
|
||||||
} else {
|
} else {
|
||||||
@@ -587,9 +594,9 @@ func (i *blockInfo) Format(ri *RegisterInfo) string {
|
|||||||
buf.WriteString(fmt.Sprintf("%v@%v ", v, pos))
|
buf.WriteString(fmt.Sprintf("%v@%v ", v, pos))
|
||||||
}
|
}
|
||||||
buf.WriteString("\n\tlastUses: ")
|
buf.WriteString("\n\tlastUses: ")
|
||||||
for v, pos := range i.lastUses {
|
i.lastUses.Range(func(v VReg, pos programCounter) {
|
||||||
buf.WriteString(fmt.Sprintf("%v@%v ", v, pos))
|
buf.WriteString(fmt.Sprintf("%v@%v ", v, pos))
|
||||||
}
|
})
|
||||||
buf.WriteString("\n\tkills: ")
|
buf.WriteString("\n\tkills: ")
|
||||||
for v, pos := range i.kills {
|
for v, pos := range i.kills {
|
||||||
buf.WriteString(fmt.Sprintf("%v@%v ", v, pos))
|
buf.WriteString(fmt.Sprintf("%v@%v ", v, pos))
|
||||||
|
|||||||
@@ -6,6 +6,18 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func makeVRegTable(vregs map[VReg]programCounter) (table VRegTable) {
|
||||||
|
min := VRegIDMinSet{}
|
||||||
|
for v := range vregs {
|
||||||
|
min.Observe(v)
|
||||||
|
}
|
||||||
|
table.Reset(min)
|
||||||
|
for v, p := range vregs {
|
||||||
|
table.Insert(v, p)
|
||||||
|
}
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
func TestAllocator_livenessAnalysis(t *testing.T) {
|
func TestAllocator_livenessAnalysis(t *testing.T) {
|
||||||
const realRegID, realRegID2 = 50, 100
|
const realRegID, realRegID2 = 50, 100
|
||||||
realReg, realReg2 := FromRealReg(realRegID, RegTypeInt), FromRealReg(realRegID2, RegTypeInt)
|
realReg, realReg2 := FromRealReg(realRegID, RegTypeInt), FromRealReg(realRegID2, RegTypeInt)
|
||||||
@@ -28,11 +40,12 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
exp: map[int]*blockInfo{
|
exp: map[int]*blockInfo{
|
||||||
0: {
|
0: {
|
||||||
defs: map[VReg]programCounter{2: pcDefOffset + pcStride, 1: pcDefOffset},
|
defs: map[VReg]programCounter{2: pcDefOffset + pcStride, 1: pcDefOffset},
|
||||||
lastUses: map[VReg]programCounter{1: pcStride + pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{1: pcStride + pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{1: pcStride + pcUseOffset},
|
kills: map[VReg]programCounter{1: pcStride + pcUseOffset},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "straight",
|
name: "straight",
|
||||||
// b0 -> b1 -> b2
|
// b0 -> b1 -> b2
|
||||||
@@ -62,11 +75,11 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
2: pcDefOffset,
|
2: pcDefOffset,
|
||||||
3: pcStride*2 + pcDefOffset,
|
3: pcStride*2 + pcDefOffset,
|
||||||
},
|
},
|
||||||
lastUses: map[VReg]programCounter{
|
lastUses: makeVRegTable(map[VReg]programCounter{
|
||||||
1000: pcStride + pcUseOffset,
|
1000: pcStride + pcUseOffset,
|
||||||
1: pcStride*2 + pcUseOffset,
|
1: pcStride*2 + pcUseOffset,
|
||||||
2: pcStride*2 + pcUseOffset,
|
2: pcStride*2 + pcUseOffset,
|
||||||
},
|
}),
|
||||||
liveOuts: map[VReg]struct{}{3: {}},
|
liveOuts: map[VReg]struct{}{3: {}},
|
||||||
kills: map[VReg]programCounter{
|
kills: map[VReg]programCounter{
|
||||||
1000: pcStride + pcUseOffset,
|
1000: pcStride + pcUseOffset,
|
||||||
@@ -77,9 +90,9 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
1: {
|
1: {
|
||||||
liveIns: map[VReg]struct{}{3: {}},
|
liveIns: map[VReg]struct{}{3: {}},
|
||||||
liveOuts: map[VReg]struct{}{3: {}, 4: {}, 5: {}},
|
liveOuts: map[VReg]struct{}{3: {}, 4: {}, 5: {}},
|
||||||
lastUses: map[VReg]programCounter{
|
lastUses: makeVRegTable(map[VReg]programCounter{
|
||||||
3: pcStride + pcUseOffset,
|
3: pcStride + pcUseOffset,
|
||||||
},
|
}),
|
||||||
defs: map[VReg]programCounter{
|
defs: map[VReg]programCounter{
|
||||||
4: pcStride + pcDefOffset,
|
4: pcStride + pcDefOffset,
|
||||||
5: pcStride + pcDefOffset,
|
5: pcStride + pcDefOffset,
|
||||||
@@ -93,7 +106,7 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
liveIns: map[VReg]struct{}{3: {}, 4: {}, 5: {}},
|
liveIns: map[VReg]struct{}{3: {}, 4: {}, 5: {}},
|
||||||
lastUses: map[VReg]programCounter{3: pcUseOffset, 4: pcUseOffset, 5: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{3: pcUseOffset, 4: pcUseOffset, 5: pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{3: pcUseOffset, 4: pcUseOffset, 5: pcUseOffset},
|
kills: map[VReg]programCounter{3: pcUseOffset, 4: pcUseOffset, 5: pcUseOffset},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -139,11 +152,12 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
2: pcStride*2 + pcDefOffset,
|
2: pcStride*2 + pcDefOffset,
|
||||||
},
|
},
|
||||||
liveOuts: map[VReg]struct{}{1000: {}, 1: {}, 2: {}},
|
liveOuts: map[VReg]struct{}{1000: {}, 1: {}, 2: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
1: {
|
1: {
|
||||||
liveIns: map[VReg]struct{}{1000: {}, 1: {}},
|
liveIns: map[VReg]struct{}{1000: {}, 1: {}},
|
||||||
liveOuts: map[VReg]struct{}{1000: {}},
|
liveOuts: map[VReg]struct{}{1000: {}},
|
||||||
lastUses: map[VReg]programCounter{1: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{1: pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{1: pcUseOffset},
|
kills: map[VReg]programCounter{1: pcUseOffset},
|
||||||
realRegDefs: [vRegIDReservedForRealNum][]programCounter{
|
realRegDefs: [vRegIDReservedForRealNum][]programCounter{
|
||||||
realRegID: {pcDefOffset, pcStride*4 + pcDefOffset},
|
realRegID: {pcDefOffset, pcStride*4 + pcDefOffset},
|
||||||
@@ -157,18 +171,19 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
2: {
|
2: {
|
||||||
liveIns: map[VReg]struct{}{1000: {}, 2: {}},
|
liveIns: map[VReg]struct{}{1000: {}, 2: {}},
|
||||||
liveOuts: map[VReg]struct{}{1000: {}},
|
liveOuts: map[VReg]struct{}{1000: {}},
|
||||||
lastUses: map[VReg]programCounter{2: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{2: pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{2: pcUseOffset},
|
kills: map[VReg]programCounter{2: pcUseOffset},
|
||||||
realRegUses: [vRegIDReservedForRealNum][]programCounter{realRegID2: {pcUseOffset}},
|
realRegUses: [vRegIDReservedForRealNum][]programCounter{realRegID2: {pcUseOffset}},
|
||||||
realRegDefs: [vRegIDReservedForRealNum][]programCounter{realRegID2: {0}},
|
realRegDefs: [vRegIDReservedForRealNum][]programCounter{realRegID2: {0}},
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
liveIns: map[VReg]struct{}{1000: {}},
|
liveIns: map[VReg]struct{}{1000: {}},
|
||||||
lastUses: map[VReg]programCounter{1000: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{1000: pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{1000: pcUseOffset},
|
kills: map[VReg]programCounter{1000: pcUseOffset},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "phis",
|
name: "phis",
|
||||||
// 0
|
// 0
|
||||||
@@ -203,32 +218,35 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
0: {
|
0: {
|
||||||
defs: map[VReg]programCounter{1000: pcDefOffset, 2000: pcDefOffset, 3000: pcDefOffset},
|
defs: map[VReg]programCounter{1000: pcDefOffset, 2000: pcDefOffset, 3000: pcDefOffset},
|
||||||
liveOuts: map[VReg]struct{}{1000: {}, 2000: {}, 3000: {}},
|
liveOuts: map[VReg]struct{}{1000: {}, 2000: {}, 3000: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
1: {
|
1: {
|
||||||
liveIns: map[VReg]struct{}{2000: {}, 3000: {}},
|
liveIns: map[VReg]struct{}{2000: {}, 3000: {}},
|
||||||
liveOuts: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
liveOuts: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
||||||
defs: map[VReg]programCounter{phiVReg: pcDefOffset},
|
defs: map[VReg]programCounter{phiVReg: pcDefOffset},
|
||||||
lastUses: map[VReg]programCounter{2000: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{2000: pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{2000: pcUseOffset},
|
kills: map[VReg]programCounter{2000: pcUseOffset},
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
liveIns: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
liveIns: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
||||||
liveOuts: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
liveOuts: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
liveIns: map[VReg]struct{}{1000: {}, 3000: {}},
|
liveIns: map[VReg]struct{}{1000: {}, 3000: {}},
|
||||||
liveOuts: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
liveOuts: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
||||||
defs: map[VReg]programCounter{phiVReg: pcDefOffset},
|
defs: map[VReg]programCounter{phiVReg: pcDefOffset},
|
||||||
lastUses: map[VReg]programCounter{1000: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{1000: pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{1000: pcUseOffset},
|
kills: map[VReg]programCounter{1000: pcUseOffset},
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
liveIns: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
liveIns: map[VReg]struct{}{phiVReg: {}, 3000: {}},
|
||||||
lastUses: map[VReg]programCounter{phiVReg: pcUseOffset, 3000: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{phiVReg: pcUseOffset, 3000: pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{phiVReg: pcUseOffset, 3000: pcUseOffset},
|
kills: map[VReg]programCounter{phiVReg: pcUseOffset, 3000: pcUseOffset},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "loop",
|
name: "loop",
|
||||||
// 0 -> 1 -> 2
|
// 0 -> 1 -> 2
|
||||||
@@ -274,9 +292,9 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
1: pcDefOffset,
|
1: pcDefOffset,
|
||||||
phiVReg: pcStride + pcDefOffset,
|
phiVReg: pcStride + pcDefOffset,
|
||||||
},
|
},
|
||||||
lastUses: map[VReg]programCounter{
|
lastUses: makeVRegTable(map[VReg]programCounter{
|
||||||
1: pcStride + pcUseOffset,
|
1: pcStride + pcUseOffset,
|
||||||
},
|
}),
|
||||||
kills: map[VReg]programCounter{
|
kills: map[VReg]programCounter{
|
||||||
1: pcStride + pcUseOffset,
|
1: pcStride + pcUseOffset,
|
||||||
},
|
},
|
||||||
@@ -285,32 +303,33 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
liveIns: map[VReg]struct{}{phiVReg: {}},
|
liveIns: map[VReg]struct{}{phiVReg: {}},
|
||||||
liveOuts: map[VReg]struct{}{phiVReg: {}, 9999: {}},
|
liveOuts: map[VReg]struct{}{phiVReg: {}, 9999: {}},
|
||||||
defs: map[VReg]programCounter{9999: pcDefOffset},
|
defs: map[VReg]programCounter{9999: pcDefOffset},
|
||||||
lastUses: map[VReg]programCounter{},
|
lastUses: makeVRegTable(map[VReg]programCounter{}),
|
||||||
kills: map[VReg]programCounter{},
|
kills: map[VReg]programCounter{},
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
liveIns: map[VReg]struct{}{phiVReg: {}, 9999: {}},
|
liveIns: map[VReg]struct{}{phiVReg: {}, 9999: {}},
|
||||||
liveOuts: map[VReg]struct{}{100: {}},
|
liveOuts: map[VReg]struct{}{100: {}},
|
||||||
defs: map[VReg]programCounter{100: pcDefOffset},
|
defs: map[VReg]programCounter{100: pcDefOffset},
|
||||||
lastUses: map[VReg]programCounter{phiVReg: pcUseOffset, 9999: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{phiVReg: pcUseOffset, 9999: pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{phiVReg: pcUseOffset, 9999: pcUseOffset},
|
kills: map[VReg]programCounter{phiVReg: pcUseOffset, 9999: pcUseOffset},
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
liveIns: map[VReg]struct{}{100: {}},
|
liveIns: map[VReg]struct{}{100: {}},
|
||||||
liveOuts: map[VReg]struct{}{54321: {}},
|
liveOuts: map[VReg]struct{}{54321: {}},
|
||||||
defs: map[VReg]programCounter{54321: pcDefOffset},
|
defs: map[VReg]programCounter{54321: pcDefOffset},
|
||||||
lastUses: map[VReg]programCounter{100: pcStride + pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{100: pcStride + pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{100: pcStride + pcUseOffset},
|
kills: map[VReg]programCounter{100: pcStride + pcUseOffset},
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
liveIns: map[VReg]struct{}{54321: {}},
|
liveIns: map[VReg]struct{}{54321: {}},
|
||||||
liveOuts: map[VReg]struct{}{phiVReg: {}},
|
liveOuts: map[VReg]struct{}{phiVReg: {}},
|
||||||
defs: map[VReg]programCounter{phiVReg: pcDefOffset},
|
defs: map[VReg]programCounter{phiVReg: pcDefOffset},
|
||||||
lastUses: map[VReg]programCounter{54321: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{54321: pcUseOffset}),
|
||||||
kills: map[VReg]programCounter{54321: pcUseOffset},
|
kills: map[VReg]programCounter{54321: pcUseOffset},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
// -----+
|
// -----+
|
||||||
// v |
|
// v |
|
||||||
@@ -336,23 +355,29 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
0: {
|
0: {
|
||||||
defs: map[VReg]programCounter{99999: pcDefOffset},
|
defs: map[VReg]programCounter{99999: pcDefOffset},
|
||||||
liveOuts: map[VReg]struct{}{99999: {}},
|
liveOuts: map[VReg]struct{}{99999: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
1: {
|
1: {
|
||||||
liveIns: map[VReg]struct{}{99999: {}},
|
liveIns: map[VReg]struct{}{99999: {}},
|
||||||
liveOuts: map[VReg]struct{}{99999: {}},
|
liveOuts: map[VReg]struct{}{99999: {}},
|
||||||
lastUses: map[VReg]programCounter{99999: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{99999: pcUseOffset}),
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
liveIns: map[VReg]struct{}{99999: {}},
|
liveIns: map[VReg]struct{}{99999: {}},
|
||||||
liveOuts: map[VReg]struct{}{99999: {}},
|
liveOuts: map[VReg]struct{}{99999: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
liveIns: map[VReg]struct{}{99999: {}},
|
liveIns: map[VReg]struct{}{99999: {}},
|
||||||
liveOuts: map[VReg]struct{}{99999: {}},
|
liveOuts: map[VReg]struct{}{99999: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
4: {},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// 2
|
// 2
|
||||||
// ^ +----+
|
// ^ +----+
|
||||||
// | v |
|
// | v |
|
||||||
@@ -405,35 +430,48 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
return newMockFunction(b0, b1, b2, b3, b4, b7, b8, b5, b6, b9)
|
return newMockFunction(b0, b1, b2, b3, b4, b7, b8, b5, b6, b9)
|
||||||
},
|
},
|
||||||
exp: map[int]*blockInfo{
|
exp: map[int]*blockInfo{
|
||||||
0: {},
|
0: {
|
||||||
1: {},
|
lastUses: makeVRegTable(nil),
|
||||||
2: {},
|
},
|
||||||
|
1: {
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
|
},
|
||||||
3: {
|
3: {
|
||||||
liveOuts: map[VReg]struct{}{100: {}},
|
|
||||||
defs: map[VReg]programCounter{100: pcDefOffset},
|
defs: map[VReg]programCounter{100: pcDefOffset},
|
||||||
|
liveOuts: map[VReg]struct{}{100: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
liveIns: map[VReg]struct{}{100: {}},
|
liveIns: map[VReg]struct{}{100: {}},
|
||||||
liveOuts: map[VReg]struct{}{100: {}},
|
liveOuts: map[VReg]struct{}{100: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
5: {
|
5: {
|
||||||
liveIns: map[VReg]struct{}{100: {}},
|
liveIns: map[VReg]struct{}{100: {}},
|
||||||
liveOuts: map[VReg]struct{}{100: {}},
|
liveOuts: map[VReg]struct{}{100: {}},
|
||||||
lastUses: map[VReg]programCounter{100: pcUseOffset},
|
lastUses: makeVRegTable(map[VReg]programCounter{100: pcUseOffset}),
|
||||||
},
|
},
|
||||||
6: {
|
6: {
|
||||||
liveIns: map[VReg]struct{}{100: {}},
|
liveIns: map[VReg]struct{}{100: {}},
|
||||||
liveOuts: map[VReg]struct{}{100: {}},
|
liveOuts: map[VReg]struct{}{100: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
7: {
|
7: {
|
||||||
liveIns: map[VReg]struct{}{100: {}},
|
liveIns: map[VReg]struct{}{100: {}},
|
||||||
liveOuts: map[VReg]struct{}{100: {}},
|
liveOuts: map[VReg]struct{}{100: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
8: {
|
8: {
|
||||||
liveIns: map[VReg]struct{}{100: {}},
|
liveIns: map[VReg]struct{}{100: {}},
|
||||||
liveOuts: map[VReg]struct{}{100: {}},
|
liveOuts: map[VReg]struct{}{100: {}},
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
|
},
|
||||||
|
9: {
|
||||||
|
lastUses: makeVRegTable(nil),
|
||||||
},
|
},
|
||||||
9: {},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
@@ -448,7 +486,7 @@ func TestAllocator_livenessAnalysis(t *testing.T) {
|
|||||||
initMapInInfo(exp)
|
initMapInInfo(exp)
|
||||||
saved := actual.intervalMng
|
saved := actual.intervalMng
|
||||||
actual.intervalMng = nil // Don't compare intervalManager.
|
actual.intervalMng = nil // Don't compare intervalManager.
|
||||||
require.Equal(t, exp, actual, "\n[exp for block[%d]]\n%s\n[actual for block[%d]]\n%s", blockID, exp, blockID, actual)
|
require.Equal(t, exp, actual, "\n[exp for block[%d]]\n%v\n[actual for block[%d]]\n%v", blockID, exp, blockID, actual)
|
||||||
actual.intervalMng = saved
|
actual.intervalMng = saved
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,9 +571,6 @@ func initMapInInfo(info *blockInfo) {
|
|||||||
if info.kills == nil {
|
if info.kills == nil {
|
||||||
info.kills = make(map[VReg]programCounter)
|
info.kills = make(map[VReg]programCounter)
|
||||||
}
|
}
|
||||||
if info.lastUses == nil {
|
|
||||||
info.lastUses = make(map[VReg]programCounter)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNode_assignedRealReg(t *testing.T) {
|
func TestNode_assignedRealReg(t *testing.T) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestSpillHandler_getUnusedOrEvictReg(t *testing.T) {
|
|||||||
require.Equal(t, 4, len(s.activeRegs))
|
require.Equal(t, 4, len(s.activeRegs))
|
||||||
|
|
||||||
regInfo := RegisterInfo{
|
regInfo := RegisterInfo{
|
||||||
AllocatableRegisters: [RegTypeNum][]RealReg{
|
AllocatableRegisters: [NumRegType][]RealReg{
|
||||||
RegTypeInt: {
|
RegTypeInt: {
|
||||||
RealReg(0xff), // unused.
|
RealReg(0xff), // unused.
|
||||||
RealReg(0), RealReg(1), RealReg(2),
|
RealReg(0), RealReg(1), RealReg(2),
|
||||||
|
|||||||
Reference in New Issue
Block a user