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 }