github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go (about)

     1  package arm64
     2  
     3  // This file implements the interfaces required for register allocations. See regalloc/api.go.
     4  
     5  import (
     6  	"github.com/bananabytelabs/wazero/internal/engine/wazevo/backend/regalloc"
     7  	"github.com/bananabytelabs/wazero/internal/engine/wazevo/ssa"
     8  )
     9  
    10  type (
    11  	// regAllocFunctionImpl implements regalloc.Function.
    12  	regAllocFunctionImpl struct {
    13  		m *machine
    14  		// iter is the iterator for reversePostOrderBlocks
    15  		iter                   int
    16  		reversePostOrderBlocks []regAllocBlockImpl
    17  		// labelToRegAllocBlockIndex maps label to the index of reversePostOrderBlocks.
    18  		labelToRegAllocBlockIndex map[label]int
    19  		loopNestingForestRoots    []ssa.BasicBlock
    20  	}
    21  
    22  	// regAllocBlockImpl implements regalloc.Block.
    23  	regAllocBlockImpl struct {
    24  		// f is the function this instruction belongs to. Used to reuse the regAllocFunctionImpl.predsSlice slice for Defs() and Uses().
    25  		f                         *regAllocFunctionImpl
    26  		sb                        ssa.BasicBlock
    27  		l                         label
    28  		pos                       *labelPosition
    29  		loopNestingForestChildren []ssa.BasicBlock
    30  		cur                       *instruction
    31  		id                        int
    32  		cachedLastInstr           regalloc.Instr
    33  	}
    34  )
    35  
    36  func (f *regAllocFunctionImpl) addBlock(sb ssa.BasicBlock, l label, pos *labelPosition) {
    37  	i := len(f.reversePostOrderBlocks)
    38  	f.reversePostOrderBlocks = append(f.reversePostOrderBlocks, regAllocBlockImpl{
    39  		f:   f,
    40  		sb:  sb,
    41  		l:   l,
    42  		pos: pos,
    43  		id:  int(sb.ID()),
    44  	})
    45  	f.labelToRegAllocBlockIndex[l] = i
    46  }
    47  
    48  func (f *regAllocFunctionImpl) reset() {
    49  	f.reversePostOrderBlocks = f.reversePostOrderBlocks[:0]
    50  	f.iter = 0
    51  }
    52  
    53  var (
    54  	_ regalloc.Function = (*regAllocFunctionImpl)(nil)
    55  	_ regalloc.Block    = (*regAllocBlockImpl)(nil)
    56  )
    57  
    58  // PostOrderBlockIteratorBegin implements regalloc.Function PostOrderBlockIteratorBegin.
    59  func (f *regAllocFunctionImpl) PostOrderBlockIteratorBegin() regalloc.Block {
    60  	f.iter = len(f.reversePostOrderBlocks) - 1
    61  	return f.PostOrderBlockIteratorNext()
    62  }
    63  
    64  // PostOrderBlockIteratorNext implements regalloc.Function PostOrderBlockIteratorNext.
    65  func (f *regAllocFunctionImpl) PostOrderBlockIteratorNext() regalloc.Block {
    66  	if f.iter < 0 {
    67  		return nil
    68  	}
    69  	b := &f.reversePostOrderBlocks[f.iter]
    70  	f.iter--
    71  	return b
    72  }
    73  
    74  // ReversePostOrderBlockIteratorBegin implements regalloc.Function ReversePostOrderBlockIteratorBegin.
    75  func (f *regAllocFunctionImpl) ReversePostOrderBlockIteratorBegin() regalloc.Block {
    76  	f.iter = 0
    77  	return f.ReversePostOrderBlockIteratorNext()
    78  }
    79  
    80  // ReversePostOrderBlockIteratorNext implements regalloc.Function ReversePostOrderBlockIteratorNext.
    81  func (f *regAllocFunctionImpl) ReversePostOrderBlockIteratorNext() regalloc.Block {
    82  	if f.iter >= len(f.reversePostOrderBlocks) {
    83  		return nil
    84  	}
    85  	b := &f.reversePostOrderBlocks[f.iter]
    86  	f.iter++
    87  	return b
    88  }
    89  
    90  // ClobberedRegisters implements regalloc.Function ClobberedRegisters.
    91  func (f *regAllocFunctionImpl) ClobberedRegisters(regs []regalloc.VReg) {
    92  	m := f.m
    93  	m.clobberedRegs = append(m.clobberedRegs[:0], regs...)
    94  }
    95  
    96  // StoreRegisterBefore implements regalloc.Function StoreRegisterBefore.
    97  func (f *regAllocFunctionImpl) StoreRegisterBefore(v regalloc.VReg, instr regalloc.Instr) {
    98  	m := f.m
    99  	m.insertStoreRegisterAt(v, instr.(*instruction), false)
   100  }
   101  
   102  // SwapAtEndOfBlock implements regalloc.Function SwapAtEndOfBlock.
   103  func (f *regAllocFunctionImpl) SwapAtEndOfBlock(x1, x2, tmp regalloc.VReg, block regalloc.Block) {
   104  	blk := block.(*regAllocBlockImpl)
   105  	cur := blk.LastInstr().(*instruction)
   106  	cur = cur.prev
   107  	f.m.swap(cur, x1, x2, tmp)
   108  }
   109  
   110  func (m *machine) swap(cur *instruction, x1, x2, tmp regalloc.VReg) {
   111  	prevNext := cur.next
   112  	var mov1, mov2, mov3 *instruction
   113  	if x1.RegType() == regalloc.RegTypeInt {
   114  		if !tmp.Valid() {
   115  			tmp = tmpRegVReg
   116  		}
   117  		mov1 = m.allocateInstr().asMove64(tmp, x1)
   118  		mov2 = m.allocateInstr().asMove64(x1, x2)
   119  		mov3 = m.allocateInstr().asMove64(x2, tmp)
   120  		cur = linkInstr(cur, mov1)
   121  		cur = linkInstr(cur, mov2)
   122  		cur = linkInstr(cur, mov3)
   123  		linkInstr(cur, prevNext)
   124  	} else {
   125  		if !tmp.Valid() {
   126  			r2 := x2.RealReg()
   127  			// Temporarily spill x1 to stack.
   128  			cur = m.insertStoreRegisterAt(x1, cur, true).prev
   129  			// Then move x2 to x1.
   130  			cur = linkInstr(cur, m.allocateInstr().asFpuMov128(x1, x2))
   131  			linkInstr(cur, prevNext)
   132  			// Then reload the original value on x1 from stack to r2.
   133  			m.insertReloadRegisterAt(x1.SetRealReg(r2), cur, true)
   134  		} else {
   135  			mov1 = m.allocateInstr().asFpuMov128(tmp, x1)
   136  			mov2 = m.allocateInstr().asFpuMov128(x1, x2)
   137  			mov3 = m.allocateInstr().asFpuMov128(x2, tmp)
   138  			cur = linkInstr(cur, mov1)
   139  			cur = linkInstr(cur, mov2)
   140  			cur = linkInstr(cur, mov3)
   141  			linkInstr(cur, prevNext)
   142  		}
   143  	}
   144  }
   145  
   146  // InsertMoveBefore implements regalloc.Function InsertMoveBefore.
   147  func (f *regAllocFunctionImpl) InsertMoveBefore(dst, src regalloc.VReg, instr regalloc.Instr) {
   148  	m := f.m
   149  
   150  	typ := src.RegType()
   151  	if typ != dst.RegType() {
   152  		panic("BUG: src and dst must have the same type")
   153  	}
   154  
   155  	mov := m.allocateInstr()
   156  	if typ == regalloc.RegTypeInt {
   157  		mov.asMove64(dst, src)
   158  	} else {
   159  		mov.asFpuMov128(dst, src)
   160  	}
   161  
   162  	cur := instr.(*instruction).prev
   163  	prevNext := cur.next
   164  	cur = linkInstr(cur, mov)
   165  	linkInstr(cur, prevNext)
   166  }
   167  
   168  // StoreRegisterAfter implements regalloc.Function StoreRegisterAfter.
   169  func (f *regAllocFunctionImpl) StoreRegisterAfter(v regalloc.VReg, instr regalloc.Instr) {
   170  	m := f.m
   171  	m.insertStoreRegisterAt(v, instr.(*instruction), true)
   172  }
   173  
   174  // ReloadRegisterBefore implements regalloc.Function ReloadRegisterBefore.
   175  func (f *regAllocFunctionImpl) ReloadRegisterBefore(v regalloc.VReg, instr regalloc.Instr) {
   176  	m := f.m
   177  	m.insertReloadRegisterAt(v, instr.(*instruction), false)
   178  }
   179  
   180  // ReloadRegisterAfter implements regalloc.Function ReloadRegisterAfter.
   181  func (f *regAllocFunctionImpl) ReloadRegisterAfter(v regalloc.VReg, instr regalloc.Instr) {
   182  	m := f.m
   183  	m.insertReloadRegisterAt(v, instr.(*instruction), true)
   184  }
   185  
   186  // Done implements regalloc.Function Done.
   187  func (f *regAllocFunctionImpl) Done() {
   188  	m := f.m
   189  	// Now that we know the final spill slot size, we must align spillSlotSize to 16 bytes.
   190  	m.spillSlotSize = (m.spillSlotSize + 15) &^ 15
   191  }
   192  
   193  // ID implements regalloc.Block ID.
   194  func (r *regAllocBlockImpl) ID() int {
   195  	return r.id
   196  }
   197  
   198  // Preds implements regalloc.Block Preds.
   199  func (r *regAllocBlockImpl) Preds() int {
   200  	return r.sb.Preds()
   201  }
   202  
   203  // Pred implements regalloc.Block Pred.
   204  func (r *regAllocBlockImpl) Pred(i int) regalloc.Block {
   205  	sb := r.sb
   206  	pred := sb.Pred(i)
   207  	l := r.f.m.executableContext.SsaBlockIDToLabels[pred.ID()]
   208  	index := r.f.labelToRegAllocBlockIndex[l]
   209  	return &r.f.reversePostOrderBlocks[index]
   210  }
   211  
   212  // Succs implements regalloc.Block Succs.
   213  func (r *regAllocBlockImpl) Succs() int {
   214  	return r.sb.Succs()
   215  }
   216  
   217  // Succ implements regalloc.Block Succ.
   218  func (r *regAllocBlockImpl) Succ(i int) regalloc.Block {
   219  	sb := r.sb
   220  	succ := sb.Succ(i)
   221  	if succ.ReturnBlock() {
   222  		return nil
   223  	}
   224  	l := r.f.m.executableContext.SsaBlockIDToLabels[succ.ID()]
   225  	index := r.f.labelToRegAllocBlockIndex[l]
   226  	return &r.f.reversePostOrderBlocks[index]
   227  }
   228  
   229  // LoopHeader implements regalloc.Block LoopHeader.
   230  func (r *regAllocBlockImpl) LoopHeader() bool {
   231  	return r.sb.LoopHeader()
   232  }
   233  
   234  // LoopNestingForestRoots implements regalloc.Function LoopNestingForestRoots.
   235  func (f *regAllocFunctionImpl) LoopNestingForestRoots() int {
   236  	f.loopNestingForestRoots = f.m.compiler.SSABuilder().LoopNestingForestRoots()
   237  	return len(f.loopNestingForestRoots)
   238  }
   239  
   240  // LoopNestingForestRoot implements regalloc.Function LoopNestingForestRoot.
   241  func (f *regAllocFunctionImpl) LoopNestingForestRoot(i int) regalloc.Block {
   242  	blk := f.loopNestingForestRoots[i]
   243  	l := f.m.executableContext.SsaBlockIDToLabels[blk.ID()]
   244  	index := f.labelToRegAllocBlockIndex[l]
   245  	return &f.reversePostOrderBlocks[index]
   246  }
   247  
   248  // LoopNestingForestChildren implements regalloc.Block LoopNestingForestChildren.
   249  func (r *regAllocBlockImpl) LoopNestingForestChildren() int {
   250  	r.loopNestingForestChildren = r.sb.LoopNestingForestChildren()
   251  	return len(r.loopNestingForestChildren)
   252  }
   253  
   254  // LoopNestingForestChild implements regalloc.Block LoopNestingForestChild.
   255  func (r *regAllocBlockImpl) LoopNestingForestChild(i int) regalloc.Block {
   256  	blk := r.loopNestingForestChildren[i]
   257  	l := r.f.m.executableContext.SsaBlockIDToLabels[blk.ID()]
   258  	index := r.f.labelToRegAllocBlockIndex[l]
   259  	return &r.f.reversePostOrderBlocks[index]
   260  }
   261  
   262  // InstrIteratorBegin implements regalloc.Block InstrIteratorBegin.
   263  func (r *regAllocBlockImpl) InstrIteratorBegin() regalloc.Instr {
   264  	r.cur = r.pos.Begin
   265  	return r.cur
   266  }
   267  
   268  // InstrIteratorNext implements regalloc.Block InstrIteratorNext.
   269  func (r *regAllocBlockImpl) InstrIteratorNext() regalloc.Instr {
   270  	for {
   271  		if r.cur == r.pos.End {
   272  			return nil
   273  		}
   274  		instr := r.cur.next
   275  		r.cur = instr
   276  		if instr == nil {
   277  			return nil
   278  		} else if instr.addedBeforeRegAlloc {
   279  			// Only concerned about the instruction added before regalloc.
   280  			return instr
   281  		}
   282  	}
   283  }
   284  
   285  // InstrRevIteratorBegin implements regalloc.Block InstrRevIteratorBegin.
   286  func (r *regAllocBlockImpl) InstrRevIteratorBegin() regalloc.Instr {
   287  	r.cur = r.pos.End
   288  	return r.cur
   289  }
   290  
   291  // InstrRevIteratorNext implements regalloc.Block InstrRevIteratorNext.
   292  func (r *regAllocBlockImpl) InstrRevIteratorNext() regalloc.Instr {
   293  	for {
   294  		if r.cur == r.pos.Begin {
   295  			return nil
   296  		}
   297  		instr := r.cur.prev
   298  		r.cur = instr
   299  		if instr == nil {
   300  			return nil
   301  		} else if instr.addedBeforeRegAlloc {
   302  			// Only concerned about the instruction added before regalloc.
   303  			return instr
   304  		}
   305  	}
   306  }
   307  
   308  // BlockParams implements regalloc.Block BlockParams.
   309  func (r *regAllocBlockImpl) BlockParams(regs *[]regalloc.VReg) []regalloc.VReg {
   310  	c := r.f.m.compiler
   311  	*regs = (*regs)[:0]
   312  	for i := 0; i < r.sb.Params(); i++ {
   313  		v := c.VRegOf(r.sb.Param(i))
   314  		*regs = append(*regs, v)
   315  	}
   316  	return *regs
   317  }
   318  
   319  // Entry implements regalloc.Block Entry.
   320  func (r *regAllocBlockImpl) Entry() bool { return r.sb.EntryBlock() }
   321  
   322  // RegisterInfo implements backend.Machine.
   323  func (m *machine) RegisterInfo() *regalloc.RegisterInfo {
   324  	return regInfo
   325  }
   326  
   327  // Function implements backend.Machine Function.
   328  func (m *machine) Function() regalloc.Function {
   329  	m.regAllocStarted = true
   330  	return &m.regAllocFn
   331  }
   332  
   333  func (m *machine) insertStoreRegisterAt(v regalloc.VReg, instr *instruction, after bool) *instruction {
   334  	if !v.IsRealReg() {
   335  		panic("BUG: VReg must be backed by real reg to be stored")
   336  	}
   337  
   338  	typ := m.compiler.TypeOf(v)
   339  
   340  	var prevNext, cur *instruction
   341  	if after {
   342  		cur, prevNext = instr, instr.next
   343  	} else {
   344  		cur, prevNext = instr.prev, instr
   345  	}
   346  
   347  	offsetFromSP := m.getVRegSpillSlotOffsetFromSP(v.ID(), typ.Size())
   348  	var amode addressMode
   349  	cur, amode = m.resolveAddressModeForOffsetAndInsert(cur, offsetFromSP, typ.Bits(), spVReg, true)
   350  	store := m.allocateInstr()
   351  	store.asStore(operandNR(v), amode, typ.Bits())
   352  
   353  	cur = linkInstr(cur, store)
   354  	return linkInstr(cur, prevNext)
   355  }
   356  
   357  func (m *machine) insertReloadRegisterAt(v regalloc.VReg, instr *instruction, after bool) *instruction {
   358  	if !v.IsRealReg() {
   359  		panic("BUG: VReg must be backed by real reg to be stored")
   360  	}
   361  
   362  	typ := m.compiler.TypeOf(v)
   363  
   364  	var prevNext, cur *instruction
   365  	if after {
   366  		cur, prevNext = instr, instr.next
   367  	} else {
   368  		cur, prevNext = instr.prev, instr
   369  	}
   370  
   371  	offsetFromSP := m.getVRegSpillSlotOffsetFromSP(v.ID(), typ.Size())
   372  	var amode addressMode
   373  	cur, amode = m.resolveAddressModeForOffsetAndInsert(cur, offsetFromSP, typ.Bits(), spVReg, true)
   374  	load := m.allocateInstr()
   375  	switch typ {
   376  	case ssa.TypeI32, ssa.TypeI64:
   377  		load.asULoad(operandNR(v), amode, typ.Bits())
   378  	case ssa.TypeF32, ssa.TypeF64:
   379  		load.asFpuLoad(operandNR(v), amode, typ.Bits())
   380  	case ssa.TypeV128:
   381  		load.asFpuLoad(operandNR(v), amode, 128)
   382  	default:
   383  		panic("TODO")
   384  	}
   385  
   386  	cur = linkInstr(cur, load)
   387  	return linkInstr(cur, prevNext)
   388  }
   389  
   390  // LowestCommonAncestor implements regalloc.Function LowestCommonAncestor.
   391  func (f *regAllocFunctionImpl) LowestCommonAncestor(blk1, blk2 regalloc.Block) regalloc.Block {
   392  	ret := f.m.compiler.SSABuilder().LowestCommonAncestor(blk1.(*regAllocBlockImpl).sb, blk2.(*regAllocBlockImpl).sb)
   393  	l := f.m.executableContext.SsaBlockIDToLabels[ret.ID()]
   394  	index := f.labelToRegAllocBlockIndex[l]
   395  	return &f.reversePostOrderBlocks[index]
   396  }
   397  
   398  func (f *regAllocFunctionImpl) Idom(blk regalloc.Block) regalloc.Block {
   399  	builder := f.m.compiler.SSABuilder()
   400  	idom := builder.Idom(blk.(*regAllocBlockImpl).sb)
   401  	if idom == nil {
   402  		panic("BUG: idom must not be nil")
   403  	}
   404  	l := f.m.executableContext.SsaBlockIDToLabels[idom.ID()]
   405  	index := f.labelToRegAllocBlockIndex[l]
   406  	return &f.reversePostOrderBlocks[index]
   407  }
   408  
   409  // FirstInstr implements regalloc.Block FirstInstr.
   410  func (r *regAllocBlockImpl) FirstInstr() regalloc.Instr {
   411  	return r.pos.Begin
   412  }
   413  
   414  // LastInstr implements regalloc.Block LastInstr.
   415  func (r *regAllocBlockImpl) LastInstr() regalloc.Instr {
   416  	if r.cachedLastInstr == nil {
   417  		cur := r.pos.End
   418  		for cur.kind == nop0 {
   419  			cur = cur.prev
   420  			if cur == r.pos.Begin {
   421  				r.cachedLastInstr = r.pos.End
   422  				return r.cachedLastInstr
   423  			}
   424  		}
   425  		switch cur.kind {
   426  		case br:
   427  			r.cachedLastInstr = cur
   428  		default:
   429  			r.cachedLastInstr = r.pos.End
   430  		}
   431  	}
   432  	return r.cachedLastInstr
   433  }