github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/wazevo/backend/compiler_lower.go (about) 1 package backend 2 3 import ( 4 "github.com/wasilibs/wazerox/internal/engine/wazevo/backend/regalloc" 5 "github.com/wasilibs/wazerox/internal/engine/wazevo/ssa" 6 ) 7 8 // Lower implements Compiler.Lower. 9 func (c *compiler) Lower() { 10 c.assignVirtualRegisters() 11 c.mach.InitializeABI(c.ssaBuilder.Signature()) 12 c.mach.StartLoweringFunction(c.ssaBuilder.BlockIDMax()) 13 c.lowerBlocks() 14 c.mach.EndLoweringFunction() 15 } 16 17 // lowerBlocks lowers each block in the ssa.Builder. 18 func (c *compiler) lowerBlocks() { 19 builder := c.ssaBuilder 20 for blk := builder.BlockIteratorReversePostOrderBegin(); blk != nil; blk = builder.BlockIteratorReversePostOrderNext() { 21 c.lowerBlock(blk) 22 } 23 // After lowering all blocks, we need to link adjacent blocks to layout one single instruction list. 24 var prev ssa.BasicBlock 25 for next := builder.BlockIteratorReversePostOrderBegin(); next != nil; next = builder.BlockIteratorReversePostOrderNext() { 26 if prev != nil { 27 c.mach.LinkAdjacentBlocks(prev, next) 28 } 29 prev = next 30 } 31 } 32 33 func (c *compiler) lowerBlock(blk ssa.BasicBlock) { 34 mach := c.mach 35 mach.StartBlock(blk) 36 37 // We traverse the instructions in reverse order because we might want to lower multiple 38 // instructions together. 39 cur := blk.Tail() 40 41 // First gather the branching instructions at the end of the blocks. 42 var br0, br1 *ssa.Instruction 43 if cur.IsBranching() { 44 br0 = cur 45 cur = cur.Prev() 46 if cur != nil && cur.IsBranching() { 47 br1 = cur 48 cur = cur.Prev() 49 } 50 } 51 52 if br0 != nil { 53 c.lowerBranches(br0, br1) 54 } 55 56 if br1 != nil && br0 == nil { 57 panic("BUG? when a block has conditional branch but doesn't end with an unconditional branch?") 58 } 59 60 // Now start lowering the non-branching instructions. 61 for ; cur != nil; cur = cur.Prev() { 62 c.setCurrentGroupID(cur.GroupID()) 63 if cur.Lowered() { 64 continue 65 } 66 67 switch cur.Opcode() { 68 case ssa.OpcodeReturn: 69 c.lowerFunctionReturns(cur.ReturnVals()) 70 c.mach.InsertReturn() 71 default: 72 mach.LowerInstr(cur) 73 } 74 mach.FlushPendingInstructions() 75 } 76 77 // Finally, if this is the entry block, we have to insert copies of arguments from the real location to the VReg. 78 if blk.EntryBlock() { 79 c.lowerFunctionArguments(blk) 80 } 81 82 mach.EndBlock() 83 } 84 85 // lowerBranches is called right after StartBlock and before any LowerInstr call if 86 // there are branches to the given block. br0 is the very end of the block and b1 is the before the br0 if it exists. 87 // At least br0 is not nil, but br1 can be nil if there's no branching before br0. 88 // 89 // See ssa.Instruction IsBranching, and the comment on ssa.BasicBlock. 90 func (c *compiler) lowerBranches(br0, br1 *ssa.Instruction) { 91 c.setCurrentGroupID(br0.GroupID()) 92 c.mach.LowerSingleBranch(br0) 93 c.mach.FlushPendingInstructions() 94 if br1 != nil { 95 c.setCurrentGroupID(br1.GroupID()) 96 c.mach.LowerConditionalBranch(br1) 97 c.mach.FlushPendingInstructions() 98 } 99 100 if br0.Opcode() == ssa.OpcodeJump { 101 _, args, target := br0.BranchData() 102 argExists := len(args) != 0 103 if argExists && br1 != nil { 104 panic("BUG: critical edge split failed") 105 } 106 if argExists && target.ReturnBlock() { 107 c.lowerFunctionReturns(args) 108 } else if argExists { 109 c.lowerBlockArguments(args, target) 110 } 111 } 112 c.mach.FlushPendingInstructions() 113 } 114 115 func (c *compiler) lowerFunctionArguments(entry ssa.BasicBlock) { 116 c.tmpVals = c.tmpVals[:0] 117 for i := 0; i < entry.Params(); i++ { 118 p := entry.Param(i) 119 if c.ssaValueRefCounts[p.ID()] > 0 { 120 c.tmpVals = append(c.tmpVals, p) 121 } else { 122 // If the argument is not used, we can just pass an invalid value. 123 c.tmpVals = append(c.tmpVals, ssa.ValueInvalid) 124 } 125 } 126 c.mach.ABI().CalleeGenFunctionArgsToVRegs(c.tmpVals) 127 c.mach.FlushPendingInstructions() 128 } 129 130 func (c *compiler) lowerFunctionReturns(returns []ssa.Value) { 131 c.mach.ABI().CalleeGenVRegsToFunctionReturns(returns) 132 } 133 134 // lowerBlockArguments lowers how to pass arguments to the given successor block. 135 func (c *compiler) lowerBlockArguments(args []ssa.Value, succ ssa.BasicBlock) { 136 if len(args) != succ.Params() { 137 panic("BUG: mismatched number of arguments") 138 } 139 140 c.varEdges = c.varEdges[:0] 141 c.varEdgeTypes = c.varEdgeTypes[:0] 142 c.constEdges = c.constEdges[:0] 143 for i := 0; i < len(args); i++ { 144 dst := succ.Param(i) 145 src := args[i] 146 147 dstReg := c.VRegOf(dst) 148 srcDef := c.ssaValueDefinitions[src.ID()] 149 if srcDef.IsFromInstr() && srcDef.Instr.Constant() { 150 c.constEdges = append(c.constEdges, struct { 151 cInst *ssa.Instruction 152 dst regalloc.VReg 153 }{cInst: srcDef.Instr, dst: dstReg}) 154 } else { 155 srcReg := c.VRegOf(src) 156 // Even when the src=dst, insert the move so that we can keep such registers keep-alive. 157 c.varEdges = append(c.varEdges, [2]regalloc.VReg{srcReg, dstReg}) 158 c.varEdgeTypes = append(c.varEdgeTypes, src.Type()) 159 } 160 } 161 162 // Check if there's an overlap among the dsts and srcs in varEdges. 163 c.vRegIDs = c.vRegIDs[:0] 164 for _, edge := range c.varEdges { 165 src := edge[0].ID() 166 if int(src) >= len(c.vRegSet) { 167 c.vRegSet = append(c.vRegSet, make([]bool, src+1)...) 168 } 169 c.vRegSet[src] = true 170 c.vRegIDs = append(c.vRegIDs, src) 171 } 172 separated := true 173 for _, edge := range c.varEdges { 174 dst := edge[1].ID() 175 if int(dst) >= len(c.vRegSet) { 176 c.vRegSet = append(c.vRegSet, make([]bool, dst+1)...) 177 } else { 178 if c.vRegSet[dst] { 179 separated = false 180 break 181 } 182 } 183 } 184 for _, id := range c.vRegIDs { 185 c.vRegSet[id] = false // reset for the next use. 186 } 187 188 if separated { 189 // If there's no overlap, we can simply move the source to destination. 190 for i, edge := range c.varEdges { 191 src, dst := edge[0], edge[1] 192 c.mach.InsertMove(dst, src, c.varEdgeTypes[i]) 193 } 194 } else { 195 // Otherwise, we allocate a temporary registers and move the source to the temporary register, 196 // 197 // First move all of them to temporary registers. 198 c.tempRegs = c.tempRegs[:0] 199 for i, edge := range c.varEdges { 200 src := edge[0] 201 typ := c.varEdgeTypes[i] 202 temp := c.AllocateVReg(typ) 203 c.tempRegs = append(c.tempRegs, temp) 204 c.mach.InsertMove(temp, src, typ) 205 } 206 // Then move the temporary registers to the destination. 207 for i, edge := range c.varEdges { 208 temp := c.tempRegs[i] 209 dst := edge[1] 210 c.mach.InsertMove(dst, temp, c.varEdgeTypes[i]) 211 } 212 } 213 214 // Finally, move the constants. 215 for _, edge := range c.constEdges { 216 cInst, dst := edge.cInst, edge.dst 217 c.mach.InsertLoadConstant(cInst, dst) 218 } 219 }