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

     1  package regalloc
     2  
     3  import "fmt"
     4  
     5  // These interfaces are implemented by ISA-specific backends to abstract away the details, and allow the register
     6  // allocators to work on any ISA.
     7  //
     8  // TODO: the interfaces are not stabilized yet, especially x64 will need some changes. E.g. x64 has an addressing mode
     9  // 	where index can be in memory. That kind of info will be useful to reduce the register pressure, and should be leveraged
    10  // 	by the register allocators, like https://docs.rs/regalloc2/latest/regalloc2/enum.OperandConstraint.html
    11  
    12  type (
    13  	// Function is the top-level interface to do register allocation, which corresponds to a CFG containing
    14  	// Blocks(s).
    15  	Function interface {
    16  		// PostOrderBlockIteratorBegin returns the first block in the post-order traversal of the CFG.
    17  		// In other words, the last blocks in the CFG will be returned first.
    18  		PostOrderBlockIteratorBegin() Block
    19  		// PostOrderBlockIteratorNext returns the next block in the post-order traversal of the CFG.
    20  		PostOrderBlockIteratorNext() Block
    21  		// ReversePostOrderBlockIteratorBegin returns the first block in the reverse post-order traversal of the CFG.
    22  		// In other words, the first blocks in the CFG will be returned first.
    23  		ReversePostOrderBlockIteratorBegin() Block
    24  		// ReversePostOrderBlockIteratorNext returns the next block in the reverse post-order traversal of the CFG.
    25  		ReversePostOrderBlockIteratorNext() Block
    26  		// ClobberedRegisters tell the clobbered registers by this function.
    27  		ClobberedRegisters([]VReg)
    28  		// LoopNestingForestRoots returns the number of roots of the loop nesting forest in a function.
    29  		LoopNestingForestRoots() int
    30  		// LoopNestingForestRoot returns the i-th root of the loop nesting forest in a function.
    31  		LoopNestingForestRoot(i int) Block
    32  		// LowestCommonAncestor returns the lowest common ancestor of two blocks in the dominator tree.
    33  		LowestCommonAncestor(blk1, blk2 Block) Block
    34  		// Idom returns the immediate dominator of the given block.
    35  		Idom(blk Block) Block
    36  
    37  		// Followings are for rewriting the function.
    38  
    39  		// SwapAtEndOfBlock swaps the two virtual registers at the end of the given block.
    40  		SwapBefore(x1, x2, tmp VReg, instr Instr)
    41  		// StoreRegisterBefore inserts store instruction(s) before the given instruction for the given virtual register.
    42  		StoreRegisterBefore(v VReg, instr Instr)
    43  		// StoreRegisterAfter inserts store instruction(s) after the given instruction for the given virtual register.
    44  		StoreRegisterAfter(v VReg, instr Instr)
    45  		// ReloadRegisterBefore inserts reload instruction(s) before the given instruction for the given virtual register.
    46  		ReloadRegisterBefore(v VReg, instr Instr)
    47  		// ReloadRegisterAfter inserts reload instruction(s) after the given instruction for the given virtual register.
    48  		ReloadRegisterAfter(v VReg, instr Instr)
    49  		// InsertMoveBefore inserts move instruction(s) before the given instruction for the given virtual registers.
    50  		InsertMoveBefore(dst, src VReg, instr Instr)
    51  	}
    52  
    53  	// Block is a basic block in the CFG of a function, and it consists of multiple instructions, and predecessor Block(s).
    54  	Block interface {
    55  		// ID returns the unique identifier of this block which is ordered in the reverse post-order traversal of the CFG.
    56  		ID() int32
    57  		// BlockParams returns the virtual registers used as the parameters of this block.
    58  		BlockParams(*[]VReg) []VReg
    59  		// InstrIteratorBegin returns the first instruction in this block. Instructions added after lowering must be skipped.
    60  		// Note: multiple Instr(s) will not be held at the same time, so it's safe to use the same impl for the return Instr.
    61  		InstrIteratorBegin() Instr
    62  		// InstrIteratorNext returns the next instruction in this block. Instructions added after lowering must be skipped.
    63  		// Note: multiple Instr(s) will not be held at the same time, so it's safe to use the same impl for the return Instr.
    64  		InstrIteratorNext() Instr
    65  		// InstrRevIteratorBegin is the same as InstrIteratorBegin, but in the reverse order.
    66  		InstrRevIteratorBegin() Instr
    67  		// InstrRevIteratorNext is the same as InstrIteratorNext, but in the reverse order.
    68  		InstrRevIteratorNext() Instr
    69  		// FirstInstr returns the fist instruction in this block where instructions will be inserted after it.
    70  		FirstInstr() Instr
    71  		// EndInstr returns the end instruction in this block.
    72  		EndInstr() Instr
    73  		// LastInstrForInsertion returns the last instruction in this block where instructions will be inserted before it.
    74  		// Such insertions only happen when we need to insert spill/reload instructions to adjust the merge edges.
    75  		// At the time of register allocation, all the critical edges are already split, so there is no need
    76  		// to worry about the case where branching instruction has multiple successors.
    77  		// Therefore, usually, it is the nop instruction, but if the block ends with an unconditional branching, then it returns
    78  		// the unconditional branch, not the nop. In other words it is either nop or unconditional branch.
    79  		LastInstrForInsertion() Instr
    80  		// Preds returns the number of predecessors of this block in the CFG.
    81  		Preds() int
    82  		// Pred returns the i-th predecessor of this block in the CFG.
    83  		Pred(i int) Block
    84  		// Entry returns true if the block is for the entry block.
    85  		Entry() bool
    86  		// Succs returns the number of successors of this block in the CFG.
    87  		Succs() int
    88  		// Succ returns the i-th successor of this block in the CFG.
    89  		Succ(i int) Block
    90  		// LoopHeader returns true if this block is a loop header.
    91  		LoopHeader() bool
    92  		// LoopNestingForestChildren returns the number of children of this block in the loop nesting forest.
    93  		LoopNestingForestChildren() int
    94  		// LoopNestingForestChild returns the i-th child of this block in the loop nesting forest.
    95  		LoopNestingForestChild(i int) Block
    96  	}
    97  
    98  	// Instr is an instruction in a block, abstracting away the underlying ISA.
    99  	Instr interface {
   100  		fmt.Stringer
   101  		// Next returns the next instruction in the same block.
   102  		Next() Instr
   103  		// Prev returns the previous instruction in the same block.
   104  		Prev() Instr
   105  		// Defs returns the virtual registers defined by this instruction.
   106  		Defs(*[]VReg) []VReg
   107  		// Uses returns the virtual registers used by this instruction.
   108  		// Note: multiple returned []VReg will not be held at the same time, so it's safe to use the same slice for this.
   109  		Uses(*[]VReg) []VReg
   110  		// AssignUse assigns the RealReg-allocated virtual register used by this instruction at the given index.
   111  		AssignUse(index int, v VReg)
   112  		// AssignDef assigns a RealReg-allocated virtual register defined by this instruction.
   113  		// This only accepts one register because we don't allocate registers for multi-def instructions (i.e. call instruction)
   114  		AssignDef(VReg)
   115  		// IsCopy returns true if this instruction is a move instruction between two registers.
   116  		// If true, the instruction is of the form of dst = src, and if the src and dst do not interfere with each other,
   117  		// we could coalesce them, and hence the copy can be eliminated from the final code.
   118  		IsCopy() bool
   119  		// IsCall returns true if this instruction is a call instruction. The result is used to insert
   120  		// caller saved register spills and restores.
   121  		IsCall() bool
   122  		// IsIndirectCall returns true if this instruction is an indirect call instruction which calls a function pointer.
   123  		//  The result is used to insert caller saved register spills and restores.
   124  		IsIndirectCall() bool
   125  		// IsReturn returns true if this instruction is a return instruction.
   126  		IsReturn() bool
   127  		// AddedBeforeRegAlloc returns true if this instruction is added before register allocation.
   128  		AddedBeforeRegAlloc() bool
   129  	}
   130  
   131  	// InstrConstraint is an interface for arch-specific instruction constraints.
   132  	InstrConstraint interface {
   133  		comparable
   134  		Instr
   135  	}
   136  )