github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/backend/regalloc.go (about)

     1  package backend
     2  
     3  import (
     4  	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
     5  	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
     6  )
     7  
     8  // RegAllocFunctionMachine is the interface for the machine specific logic that will be used in RegAllocFunction.
     9  type RegAllocFunctionMachine[I regalloc.InstrConstraint] interface {
    10  	// InsertMoveBefore inserts the move instruction from src to dst before the given instruction.
    11  	InsertMoveBefore(dst, src regalloc.VReg, instr I)
    12  	// InsertStoreRegisterAt inserts the instruction(s) to store the given virtual register at the given instruction.
    13  	// If after is true, the instruction(s) will be inserted after the given instruction, otherwise before.
    14  	InsertStoreRegisterAt(v regalloc.VReg, instr I, after bool) I
    15  	// InsertReloadRegisterAt inserts the instruction(s) to reload the given virtual register at the given instruction.
    16  	// If after is true, the instruction(s) will be inserted after the given instruction, otherwise before.
    17  	InsertReloadRegisterAt(v regalloc.VReg, instr I, after bool) I
    18  	// ClobberedRegisters is called when the register allocation is done and the clobbered registers are known.
    19  	ClobberedRegisters(regs []regalloc.VReg)
    20  	// Swap swaps the two virtual registers after the given instruction.
    21  	Swap(cur I, x1, x2, tmp regalloc.VReg)
    22  	// LastInstrForInsertion implements LastInstrForInsertion of regalloc.Function. See its comment for details.
    23  	LastInstrForInsertion(begin, end I) I
    24  	// SSABlockLabel returns the label of the given ssa.BasicBlockID.
    25  	SSABlockLabel(id ssa.BasicBlockID) Label
    26  }
    27  
    28  type (
    29  	// RegAllocFunction implements regalloc.Function.
    30  	RegAllocFunction[I regalloc.InstrConstraint, m RegAllocFunctionMachine[I]] struct {
    31  		m   m
    32  		ssb ssa.Builder
    33  		c   Compiler
    34  		// iter is the iterator for reversePostOrderBlocks
    35  		iter                   int
    36  		reversePostOrderBlocks []RegAllocBlock[I, m]
    37  		// labelToRegAllocBlockIndex maps label to the index of reversePostOrderBlocks.
    38  		labelToRegAllocBlockIndex map[Label]int
    39  		loopNestingForestRoots    []ssa.BasicBlock
    40  	}
    41  
    42  	// RegAllocBlock implements regalloc.Block.
    43  	RegAllocBlock[I regalloc.InstrConstraint, m RegAllocFunctionMachine[I]] struct {
    44  		// f is the function this instruction belongs to. Used to reuse the regAllocFunctionImpl.predsSlice slice for Defs() and Uses().
    45  		f                           *RegAllocFunction[I, m]
    46  		sb                          ssa.BasicBlock
    47  		l                           Label
    48  		begin, end                  I
    49  		loopNestingForestChildren   []ssa.BasicBlock
    50  		cur                         I
    51  		id                          int
    52  		cachedLastInstrForInsertion I
    53  	}
    54  )
    55  
    56  // NewRegAllocFunction returns a new RegAllocFunction.
    57  func NewRegAllocFunction[I regalloc.InstrConstraint, M RegAllocFunctionMachine[I]](m M, ssb ssa.Builder, c Compiler) *RegAllocFunction[I, M] {
    58  	return &RegAllocFunction[I, M]{
    59  		m:                         m,
    60  		ssb:                       ssb,
    61  		c:                         c,
    62  		labelToRegAllocBlockIndex: make(map[Label]int),
    63  	}
    64  }
    65  
    66  // AddBlock adds a new block to the function.
    67  func (f *RegAllocFunction[I, M]) AddBlock(sb ssa.BasicBlock, l Label, begin, end I) {
    68  	i := len(f.reversePostOrderBlocks)
    69  	f.reversePostOrderBlocks = append(f.reversePostOrderBlocks, RegAllocBlock[I, M]{
    70  		f:     f,
    71  		sb:    sb,
    72  		l:     l,
    73  		begin: begin,
    74  		end:   end,
    75  		id:    int(sb.ID()),
    76  	})
    77  	f.labelToRegAllocBlockIndex[l] = i
    78  }
    79  
    80  // Reset resets the function for the next compilation.
    81  func (f *RegAllocFunction[I, M]) Reset() {
    82  	f.reversePostOrderBlocks = f.reversePostOrderBlocks[:0]
    83  	f.iter = 0
    84  }
    85  
    86  // StoreRegisterAfter implements regalloc.Function StoreRegisterAfter.
    87  func (f *RegAllocFunction[I, M]) StoreRegisterAfter(v regalloc.VReg, instr regalloc.Instr) {
    88  	m := f.m
    89  	m.InsertStoreRegisterAt(v, instr.(I), true)
    90  }
    91  
    92  // ReloadRegisterBefore implements regalloc.Function ReloadRegisterBefore.
    93  func (f *RegAllocFunction[I, M]) ReloadRegisterBefore(v regalloc.VReg, instr regalloc.Instr) {
    94  	m := f.m
    95  	m.InsertReloadRegisterAt(v, instr.(I), false)
    96  }
    97  
    98  // ReloadRegisterAfter implements regalloc.Function ReloadRegisterAfter.
    99  func (f *RegAllocFunction[I, M]) ReloadRegisterAfter(v regalloc.VReg, instr regalloc.Instr) {
   100  	m := f.m
   101  	m.InsertReloadRegisterAt(v, instr.(I), true)
   102  }
   103  
   104  // StoreRegisterBefore implements regalloc.Function StoreRegisterBefore.
   105  func (f *RegAllocFunction[I, M]) StoreRegisterBefore(v regalloc.VReg, instr regalloc.Instr) {
   106  	m := f.m
   107  	m.InsertStoreRegisterAt(v, instr.(I), false)
   108  }
   109  
   110  // ClobberedRegisters implements regalloc.Function ClobberedRegisters.
   111  func (f *RegAllocFunction[I, M]) ClobberedRegisters(regs []regalloc.VReg) {
   112  	f.m.ClobberedRegisters(regs)
   113  }
   114  
   115  // SwapBefore implements regalloc.Function SwapBefore.
   116  func (f *RegAllocFunction[I, M]) SwapBefore(x1, x2, tmp regalloc.VReg, instr regalloc.Instr) {
   117  	f.m.Swap(instr.Prev().(I), x1, x2, tmp)
   118  }
   119  
   120  // PostOrderBlockIteratorBegin implements regalloc.Function PostOrderBlockIteratorBegin.
   121  func (f *RegAllocFunction[I, M]) PostOrderBlockIteratorBegin() regalloc.Block {
   122  	f.iter = len(f.reversePostOrderBlocks) - 1
   123  	return f.PostOrderBlockIteratorNext()
   124  }
   125  
   126  // PostOrderBlockIteratorNext implements regalloc.Function PostOrderBlockIteratorNext.
   127  func (f *RegAllocFunction[I, M]) PostOrderBlockIteratorNext() regalloc.Block {
   128  	if f.iter < 0 {
   129  		return nil
   130  	}
   131  	b := &f.reversePostOrderBlocks[f.iter]
   132  	f.iter--
   133  	return b
   134  }
   135  
   136  // ReversePostOrderBlockIteratorBegin implements regalloc.Function ReversePostOrderBlockIteratorBegin.
   137  func (f *RegAllocFunction[I, M]) ReversePostOrderBlockIteratorBegin() regalloc.Block {
   138  	f.iter = 0
   139  	return f.ReversePostOrderBlockIteratorNext()
   140  }
   141  
   142  // ReversePostOrderBlockIteratorNext implements regalloc.Function ReversePostOrderBlockIteratorNext.
   143  func (f *RegAllocFunction[I, M]) ReversePostOrderBlockIteratorNext() regalloc.Block {
   144  	if f.iter >= len(f.reversePostOrderBlocks) {
   145  		return nil
   146  	}
   147  	b := &f.reversePostOrderBlocks[f.iter]
   148  	f.iter++
   149  	return b
   150  }
   151  
   152  // LoopNestingForestRoots implements regalloc.Function LoopNestingForestRoots.
   153  func (f *RegAllocFunction[I, M]) LoopNestingForestRoots() int {
   154  	f.loopNestingForestRoots = f.ssb.LoopNestingForestRoots()
   155  	return len(f.loopNestingForestRoots)
   156  }
   157  
   158  // LoopNestingForestRoot implements regalloc.Function LoopNestingForestRoot.
   159  func (f *RegAllocFunction[I, M]) LoopNestingForestRoot(i int) regalloc.Block {
   160  	blk := f.loopNestingForestRoots[i]
   161  	l := f.m.SSABlockLabel(blk.ID())
   162  	index := f.labelToRegAllocBlockIndex[l]
   163  	return &f.reversePostOrderBlocks[index]
   164  }
   165  
   166  // InsertMoveBefore implements regalloc.Function InsertMoveBefore.
   167  func (f *RegAllocFunction[I, M]) InsertMoveBefore(dst, src regalloc.VReg, instr regalloc.Instr) {
   168  	f.m.InsertMoveBefore(dst, src, instr.(I))
   169  }
   170  
   171  // LowestCommonAncestor implements regalloc.Function LowestCommonAncestor.
   172  func (f *RegAllocFunction[I, M]) LowestCommonAncestor(blk1, blk2 regalloc.Block) regalloc.Block {
   173  	ret := f.ssb.LowestCommonAncestor(blk1.(*RegAllocBlock[I, M]).sb, blk2.(*RegAllocBlock[I, M]).sb)
   174  	l := f.m.SSABlockLabel(ret.ID())
   175  	index := f.labelToRegAllocBlockIndex[l]
   176  	return &f.reversePostOrderBlocks[index]
   177  }
   178  
   179  // Idom implements regalloc.Function Idom.
   180  func (f *RegAllocFunction[I, M]) Idom(blk regalloc.Block) regalloc.Block {
   181  	builder := f.ssb
   182  	idom := builder.Idom(blk.(*RegAllocBlock[I, M]).sb)
   183  	if idom == nil {
   184  		panic("BUG: idom must not be nil")
   185  	}
   186  	l := f.m.SSABlockLabel(idom.ID())
   187  	index := f.labelToRegAllocBlockIndex[l]
   188  	return &f.reversePostOrderBlocks[index]
   189  }
   190  
   191  // ID implements regalloc.Block.
   192  func (r *RegAllocBlock[I, m]) ID() int32 { return int32(r.id) }
   193  
   194  // BlockParams implements regalloc.Block.
   195  func (r *RegAllocBlock[I, m]) BlockParams(regs *[]regalloc.VReg) []regalloc.VReg {
   196  	c := r.f.c
   197  	*regs = (*regs)[:0]
   198  	for i := 0; i < r.sb.Params(); i++ {
   199  		v := c.VRegOf(r.sb.Param(i))
   200  		*regs = append(*regs, v)
   201  	}
   202  	return *regs
   203  }
   204  
   205  // InstrIteratorBegin implements regalloc.Block.
   206  func (r *RegAllocBlock[I, m]) InstrIteratorBegin() regalloc.Instr {
   207  	r.cur = r.begin
   208  	return r.cur
   209  }
   210  
   211  // InstrIteratorNext implements regalloc.Block.
   212  func (r *RegAllocBlock[I, m]) InstrIteratorNext() regalloc.Instr {
   213  	for {
   214  		if r.cur == r.end {
   215  			return nil
   216  		}
   217  		instr := r.cur.Next()
   218  		r.cur = instr.(I)
   219  		if instr == nil {
   220  			return nil
   221  		} else if instr.AddedBeforeRegAlloc() {
   222  			// Only concerned about the instruction added before regalloc.
   223  			return instr
   224  		}
   225  	}
   226  }
   227  
   228  // InstrRevIteratorBegin implements regalloc.Block.
   229  func (r *RegAllocBlock[I, m]) InstrRevIteratorBegin() regalloc.Instr {
   230  	r.cur = r.end
   231  	return r.cur
   232  }
   233  
   234  // InstrRevIteratorNext implements regalloc.Block.
   235  func (r *RegAllocBlock[I, m]) InstrRevIteratorNext() regalloc.Instr {
   236  	for {
   237  		if r.cur == r.begin {
   238  			return nil
   239  		}
   240  		instr := r.cur.Prev()
   241  		r.cur = instr.(I)
   242  		if instr == nil {
   243  			return nil
   244  		} else if instr.AddedBeforeRegAlloc() {
   245  			// Only concerned about the instruction added before regalloc.
   246  			return instr
   247  		}
   248  	}
   249  }
   250  
   251  // FirstInstr implements regalloc.Block.
   252  func (r *RegAllocBlock[I, m]) FirstInstr() regalloc.Instr {
   253  	return r.begin
   254  }
   255  
   256  // EndInstr implements regalloc.Block.
   257  func (r *RegAllocBlock[I, m]) EndInstr() regalloc.Instr {
   258  	return r.end
   259  }
   260  
   261  // LastInstrForInsertion implements regalloc.Block.
   262  func (r *RegAllocBlock[I, m]) LastInstrForInsertion() regalloc.Instr {
   263  	var nil I
   264  	if r.cachedLastInstrForInsertion == nil {
   265  		r.cachedLastInstrForInsertion = r.f.m.LastInstrForInsertion(r.begin, r.end)
   266  	}
   267  	return r.cachedLastInstrForInsertion
   268  }
   269  
   270  // Preds implements regalloc.Block.
   271  func (r *RegAllocBlock[I, m]) Preds() int { return r.sb.Preds() }
   272  
   273  // Pred implements regalloc.Block.
   274  func (r *RegAllocBlock[I, m]) Pred(i int) regalloc.Block {
   275  	sb := r.sb
   276  	pred := sb.Pred(i)
   277  	l := r.f.m.SSABlockLabel(pred.ID())
   278  	index := r.f.labelToRegAllocBlockIndex[l]
   279  	return &r.f.reversePostOrderBlocks[index]
   280  }
   281  
   282  // Entry implements regalloc.Block.
   283  func (r *RegAllocBlock[I, m]) Entry() bool { return r.sb.EntryBlock() }
   284  
   285  // Succs implements regalloc.Block.
   286  func (r *RegAllocBlock[I, m]) Succs() int {
   287  	return r.sb.Succs()
   288  }
   289  
   290  // Succ implements regalloc.Block.
   291  func (r *RegAllocBlock[I, m]) Succ(i int) regalloc.Block {
   292  	sb := r.sb
   293  	succ := sb.Succ(i)
   294  	if succ.ReturnBlock() {
   295  		return nil
   296  	}
   297  	l := r.f.m.SSABlockLabel(succ.ID())
   298  	index := r.f.labelToRegAllocBlockIndex[l]
   299  	return &r.f.reversePostOrderBlocks[index]
   300  }
   301  
   302  // LoopHeader implements regalloc.Block.
   303  func (r *RegAllocBlock[I, m]) LoopHeader() bool {
   304  	return r.sb.LoopHeader()
   305  }
   306  
   307  // LoopNestingForestChildren implements regalloc.Block.
   308  func (r *RegAllocBlock[I, m]) LoopNestingForestChildren() int {
   309  	r.loopNestingForestChildren = r.sb.LoopNestingForestChildren()
   310  	return len(r.loopNestingForestChildren)
   311  }
   312  
   313  // LoopNestingForestChild implements regalloc.Block.
   314  func (r *RegAllocBlock[I, m]) LoopNestingForestChild(i int) regalloc.Block {
   315  	blk := r.loopNestingForestChildren[i]
   316  	l := r.f.m.SSABlockLabel(blk.ID())
   317  	index := r.f.labelToRegAllocBlockIndex[l]
   318  	return &r.f.reversePostOrderBlocks[index]
   319  }