github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/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 // Done tells the implementation that register allocation is done, and it can finalize the stack 29 Done() 30 // LoopNestingForestRoots returns the number of roots of the loop nesting forest in a function. 31 LoopNestingForestRoots() int 32 // LoopNestingForestRoot returns the i-th root of the loop nesting forest in a function. 33 LoopNestingForestRoot(i int) Block 34 // LowestCommonAncestor returns the lowest common ancestor of two blocks in the dominator tree. 35 LowestCommonAncestor(blk1, blk2 Block) Block 36 // Idom returns the immediate dominator of the given block. 37 Idom(blk Block) Block 38 39 // Followings are for rewriting the function. 40 41 // SwapAtEndOfBlock swaps the two virtual registers at the end of the given block. 42 SwapAtEndOfBlock(x1, x2, tmp VReg, block Block) 43 // StoreRegisterBefore inserts store instruction(s) before the given instruction for the given virtual register. 44 StoreRegisterBefore(v VReg, instr Instr) 45 // StoreRegisterAfter inserts store instruction(s) after the given instruction for the given virtual register. 46 StoreRegisterAfter(v VReg, instr Instr) 47 // ReloadRegisterBefore inserts reload instruction(s) before the given instruction for the given virtual register. 48 ReloadRegisterBefore(v VReg, instr Instr) 49 // ReloadRegisterAfter inserts reload instruction(s) after the given instruction for the given virtual register. 50 ReloadRegisterAfter(v VReg, instr Instr) 51 // InsertMoveBefore inserts move instruction(s) before the given instruction for the given virtual registers. 52 InsertMoveBefore(dst, src VReg, instr Instr) 53 } 54 55 // Block is a basic block in the CFG of a function, and it consists of multiple instructions, and predecessor Block(s). 56 Block interface { 57 // ID returns the unique identifier of this block which is ordered in the reverse post-order traversal of the CFG. 58 ID() int 59 // BlockParams returns the virtual registers used as the parameters of this block. 60 BlockParams(*[]VReg) []VReg 61 // InstrIteratorBegin returns the first instruction in this block. Instructions added after lowering must be skipped. 62 // 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. 63 InstrIteratorBegin() Instr 64 // InstrIteratorNext returns the next instruction in this block. Instructions added after lowering must be skipped. 65 // 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. 66 InstrIteratorNext() Instr 67 // InstrRevIteratorBegin is the same as InstrIteratorBegin, but in the reverse order. 68 InstrRevIteratorBegin() Instr 69 // InstrRevIteratorNext is the same as InstrIteratorNext, but in the reverse order. 70 InstrRevIteratorNext() Instr 71 // FirstInstr returns the fist instruction in this block where instructions will be inserted after it. 72 FirstInstr() Instr 73 // LastInstr 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 // If the very last instruction is the unconditional branching, then the returned instruction is the one before it. 76 // Note that at the time of register allocation, all the critical edges are already split, so there is no need 77 // to worry about the case where branching instruction has multiple successors. 78 LastInstr() Instr 79 // Preds returns the number of predecessors of this block in the CFG. 80 Preds() int 81 // Pred returns the i-th predecessor of this block in the CFG. 82 Pred(i int) Block 83 // Entry returns true if the block is for the entry block. 84 Entry() bool 85 // Succs returns the number of successors of this block in the CFG. 86 Succs() int 87 // Succ returns the i-th successor of this block in the CFG. 88 Succ(i int) Block 89 // LoopHeader returns true if this block is a loop header. 90 LoopHeader() bool 91 // LoopNestingForestChildren returns the number of children of this block in the loop nesting forest. 92 LoopNestingForestChildren() int 93 // LoopNestingForestChild returns the i-th child of this block in the loop nesting forest. 94 LoopNestingForestChild(i int) Block 95 } 96 97 // Instr is an instruction in a block, abstracting away the underlying ISA. 98 Instr interface { 99 fmt.Stringer 100 101 // Defs returns the virtual registers defined by this instruction. 102 Defs(*[]VReg) []VReg 103 // Uses returns the virtual registers used by this instruction. 104 // Note: multiple returned []VReg will not be held at the same time, so it's safe to use the same slice for this. 105 Uses(*[]VReg) []VReg 106 // AssignUse assigns the RealReg-allocated virtual register used by this instruction at the given index. 107 AssignUse(index int, v VReg) 108 // AssignDef assigns a RealReg-allocated virtual register defined by this instruction. 109 // This only accepts one register because we don't allocate registers for multi-def instructions (i.e. call instruction) 110 AssignDef(VReg) 111 // IsCopy returns true if this instruction is a move instruction between two registers. 112 // If true, the instruction is of the form of dst = src, and if the src and dst do not interfere with each other, 113 // we could coalesce them, and hence the copy can be eliminated from the final code. 114 IsCopy() bool 115 // IsCall returns true if this instruction is a call instruction. The result is used to insert 116 // caller saved register spills and restores. 117 IsCall() bool 118 // IsIndirectCall returns true if this instruction is an indirect call instruction which calls a function pointer. 119 // The result is used to insert caller saved register spills and restores. 120 IsIndirectCall() bool 121 // IsReturn returns true if this instruction is a return instruction. 122 IsReturn() bool 123 } 124 )