github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/wazevo/ssa/basic_block.go (about) 1 package ssa 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 ) 8 9 // BasicBlock represents the Basic Block of an SSA function. 10 // Each BasicBlock always ends with branching instructions (e.g. Branch, Return, etc.), 11 // and at most two branches are allowed. If there's two branches, these two are placed together at the end of the block. 12 // In other words, there's no branching instruction in the middle of the block. 13 // 14 // Note: we use the "block argument" variant of SSA, instead of PHI functions. See the package level doc comments. 15 // 16 // Note: we use "parameter/param" as a placeholder which represents a variant of PHI, and "argument/arg" as an actual 17 // Value passed to that "parameter/param". 18 type BasicBlock interface { 19 // ID returns the unique ID of this block. 20 ID() BasicBlockID 21 22 // Name returns the unique string ID of this block. e.g. blk0, blk1, ... 23 Name() string 24 25 // AddParam adds the parameter to the block whose type specified by `t`. 26 AddParam(b Builder, t Type) Value 27 28 // Params returns the number of parameters to this block. 29 Params() int 30 31 // Param returns (Variable, Value) which corresponds to the i-th parameter of this block. 32 // The returned Value is the definition of the param in this block. 33 Param(i int) Value 34 35 // InsertInstruction inserts an instruction that implements Value into the tail of this block. 36 InsertInstruction(raw *Instruction) 37 38 // Root returns the root instruction of this block. 39 Root() *Instruction 40 41 // Tail returns the tail instruction of this block. 42 Tail() *Instruction 43 44 // EntryBlock returns true if this block represents the function entry. 45 EntryBlock() bool 46 47 // ReturnBlock returns ture if this block represents the function return. 48 ReturnBlock() bool 49 50 // FormatHeader returns the debug string of this block, not including instruction. 51 FormatHeader(b Builder) string 52 53 // Valid is true if this block is still valid even after optimizations. 54 Valid() bool 55 56 // BeginPredIterator returns the first predecessor of this block. 57 BeginPredIterator() BasicBlock 58 59 // NextPredIterator returns the next predecessor of this block. 60 NextPredIterator() BasicBlock 61 62 // Preds returns the number of predecessors of this block. 63 Preds() int 64 65 // Pred returns the i-th predecessor of this block. 66 Pred(i int) BasicBlock 67 68 // Succs returns the number of successors of this block. 69 Succs() int 70 71 // Succ returns the i-th successor of this block. 72 Succ(i int) BasicBlock 73 74 // LoopHeader returns true if this block is a loop header. 75 LoopHeader() bool 76 77 // LoopNestingForestChildren returns the children of this block in the loop nesting forest. 78 LoopNestingForestChildren() []BasicBlock 79 } 80 81 type ( 82 // basicBlock is a basic block in a SSA-transformed function. 83 basicBlock struct { 84 id BasicBlockID 85 rootInstr, currentInstr *Instruction 86 params []blockParam 87 predIter int 88 preds []basicBlockPredecessorInfo 89 success []*basicBlock 90 // singlePred is the alias to preds[0] for fast lookup, and only set after Seal is called. 91 singlePred *basicBlock 92 // lastDefinitions maps Variable to its last definition in this block. 93 lastDefinitions map[Variable]Value 94 // unknownsValues are used in builder.findValue. The usage is well-described in the paper. 95 unknownValues map[Variable]Value 96 // invalid is true if this block is made invalid during optimizations. 97 invalid bool 98 // sealed is true if this is sealed (all the predecessors are known). 99 sealed bool 100 // loopHeader is true if this block is a loop header: 101 // 102 // > A loop header (sometimes called the entry point of the loop) is a dominator that is the target 103 // > of a loop-forming back edge. The loop header dominates all blocks in the loop body. 104 // > A block may be a loop header for more than one loop. A loop may have multiple entry points, 105 // > in which case it has no "loop header". 106 // 107 // See https://en.wikipedia.org/wiki/Control-flow_graph for more details. 108 // 109 // This is modified during the subPassLoopDetection pass. 110 loopHeader bool 111 112 // loopNestingForestChildren holds the children of this block in the loop nesting forest. 113 // Non-empty if and only if this block is a loop header (i.e. loopHeader=true) 114 loopNestingForestChildren []BasicBlock 115 116 // reversePostOrder is used to sort all the blocks in the function in reverse post order. 117 // This is used in builder.LayoutBlocks. 118 reversePostOrder int 119 120 // child and sibling are the ones in the dominator tree. 121 child, sibling *basicBlock 122 } 123 // BasicBlockID is the unique ID of a basicBlock. 124 BasicBlockID uint32 125 126 // blockParam implements Value and represents a parameter to a basicBlock. 127 blockParam struct { 128 // value is the Value that corresponds to the parameter in this block, 129 // and can be considered as an output of PHI instruction in traditional SSA. 130 value Value 131 // typ is the type of the parameter. 132 typ Type 133 } 134 ) 135 136 const basicBlockIDReturnBlock = 0xffffffff 137 138 // Name implements BasicBlock.Name. 139 func (bb *basicBlock) Name() string { 140 if bb.id == basicBlockIDReturnBlock { 141 return "blk_ret" 142 } else { 143 return fmt.Sprintf("blk%d", bb.id) 144 } 145 } 146 147 // String implements fmt.Stringer for debugging. 148 func (bid BasicBlockID) String() string { 149 if bid == basicBlockIDReturnBlock { 150 return "blk_ret" 151 } else { 152 return fmt.Sprintf("blk%d", bid) 153 } 154 } 155 156 // ID implements BasicBlock.ID. 157 func (bb *basicBlock) ID() BasicBlockID { 158 return bb.id 159 } 160 161 // basicBlockPredecessorInfo is the information of a predecessor of a basicBlock. 162 // predecessor is determined by a pair of block and the branch instruction used to jump to the successor. 163 type basicBlockPredecessorInfo struct { 164 blk *basicBlock 165 branch *Instruction 166 } 167 168 // EntryBlock implements BasicBlock.EntryBlock. 169 func (bb *basicBlock) EntryBlock() bool { 170 return bb.id == 0 171 } 172 173 // ReturnBlock implements BasicBlock.ReturnBlock. 174 func (bb *basicBlock) ReturnBlock() bool { 175 return bb.id == basicBlockIDReturnBlock 176 } 177 178 // AddParam implements BasicBlock.AddParam. 179 func (bb *basicBlock) AddParam(b Builder, typ Type) Value { 180 paramValue := b.allocateValue(typ) 181 bb.params = append(bb.params, blockParam{typ: typ, value: paramValue}) 182 return paramValue 183 } 184 185 // addParamOn adds a parameter to this block whose value is already allocated. 186 func (bb *basicBlock) addParamOn(typ Type, value Value) { 187 bb.params = append(bb.params, blockParam{typ: typ, value: value}) 188 } 189 190 // Params implements BasicBlock.Params. 191 func (bb *basicBlock) Params() int { 192 return len(bb.params) 193 } 194 195 // Param implements BasicBlock.Param. 196 func (bb *basicBlock) Param(i int) Value { 197 p := &bb.params[i] 198 return p.value 199 } 200 201 // Valid implements BasicBlock.Valid. 202 func (bb *basicBlock) Valid() bool { 203 return !bb.invalid 204 } 205 206 // InsertInstruction implements BasicBlock.InsertInstruction. 207 func (bb *basicBlock) InsertInstruction(next *Instruction) { 208 current := bb.currentInstr 209 if current != nil { 210 current.next = next 211 next.prev = current 212 } else { 213 bb.rootInstr = next 214 } 215 bb.currentInstr = next 216 217 switch next.opcode { 218 case OpcodeJump, OpcodeBrz, OpcodeBrnz: 219 target := next.blk.(*basicBlock) 220 target.addPred(bb, next) 221 case OpcodeBrTable: 222 for _, _target := range next.targets { 223 target := _target.(*basicBlock) 224 target.addPred(bb, next) 225 } 226 } 227 } 228 229 // NumPreds implements BasicBlock.NumPreds. 230 func (bb *basicBlock) NumPreds() int { 231 return len(bb.preds) 232 } 233 234 // BeginPredIterator implements BasicBlock.BeginPredIterator. 235 func (bb *basicBlock) BeginPredIterator() BasicBlock { 236 bb.predIter = 0 237 return bb.NextPredIterator() 238 } 239 240 // NextPredIterator implements BasicBlock.NextPredIterator. 241 func (bb *basicBlock) NextPredIterator() BasicBlock { 242 if bb.predIter >= len(bb.preds) { 243 return nil 244 } 245 pred := bb.preds[bb.predIter].blk 246 bb.predIter++ 247 return pred 248 } 249 250 // Preds implements BasicBlock.Preds. 251 func (bb *basicBlock) Preds() int { 252 return len(bb.preds) 253 } 254 255 // Pred implements BasicBlock.Pred. 256 func (bb *basicBlock) Pred(i int) BasicBlock { 257 return bb.preds[i].blk 258 } 259 260 // Succs implements BasicBlock.Succs. 261 func (bb *basicBlock) Succs() int { 262 return len(bb.success) 263 } 264 265 // Succ implements BasicBlock.Succ. 266 func (bb *basicBlock) Succ(i int) BasicBlock { 267 return bb.success[i] 268 } 269 270 // Root implements BasicBlock.Root. 271 func (bb *basicBlock) Root() *Instruction { 272 return bb.rootInstr 273 } 274 275 // Tail implements BasicBlock.Tail. 276 func (bb *basicBlock) Tail() *Instruction { 277 return bb.currentInstr 278 } 279 280 // reset resets the basicBlock to its initial state so that it can be reused for another function. 281 func resetBasicBlock(bb *basicBlock) { 282 bb.params = bb.params[:0] 283 bb.rootInstr, bb.currentInstr = nil, nil 284 bb.preds = bb.preds[:0] 285 bb.success = bb.success[:0] 286 bb.invalid, bb.sealed = false, false 287 bb.singlePred = nil 288 bb.unknownValues = make(map[Variable]Value) 289 bb.lastDefinitions = make(map[Variable]Value) 290 bb.reversePostOrder = -1 291 bb.loopNestingForestChildren = bb.loopNestingForestChildren[:0] 292 bb.loopHeader = false 293 bb.sibling = nil 294 bb.child = nil 295 } 296 297 // addPred adds a predecessor to this block specified by the branch instruction. 298 func (bb *basicBlock) addPred(blk BasicBlock, branch *Instruction) { 299 if bb.sealed { 300 panic("BUG: trying to add predecessor to a sealed block: " + bb.Name()) 301 } 302 303 pred := blk.(*basicBlock) 304 for i := range bb.preds { 305 existingPred := &bb.preds[i] 306 if existingPred.blk == pred && existingPred.branch != branch { 307 // If the target is already added, then this must come from the same BrTable, 308 // otherwise such redundant branch should be eliminated by the frontend. (which should be simpler). 309 panic(fmt.Sprintf("BUG: redundant non BrTable jumps in %s whose targes are the same", bb.Name())) 310 } 311 } 312 313 bb.preds = append(bb.preds, basicBlockPredecessorInfo{ 314 blk: pred, 315 branch: branch, 316 }) 317 318 pred.success = append(pred.success, bb) 319 } 320 321 // FormatHeader implements BasicBlock.FormatHeader. 322 func (bb *basicBlock) FormatHeader(b Builder) string { 323 ps := make([]string, len(bb.params)) 324 for i, p := range bb.params { 325 ps[i] = p.value.formatWithType(b) 326 } 327 328 if len(bb.preds) > 0 { 329 preds := make([]string, 0, len(bb.preds)) 330 for _, pred := range bb.preds { 331 if pred.blk.invalid { 332 continue 333 } 334 preds = append(preds, fmt.Sprintf("blk%d", pred.blk.id)) 335 336 } 337 return fmt.Sprintf("blk%d: (%s) <-- (%s)", 338 bb.id, strings.Join(ps, ","), strings.Join(preds, ",")) 339 } else { 340 return fmt.Sprintf("blk%d: (%s)", bb.id, strings.Join(ps, ", ")) 341 } 342 } 343 344 // validates validates the basicBlock for debugging purpose. 345 func (bb *basicBlock) validate(b *builder) { 346 if bb.invalid { 347 panic("BUG: trying to validate an invalid block: " + bb.Name()) 348 } 349 if len(bb.preds) > 0 { 350 for _, pred := range bb.preds { 351 if pred.branch.opcode != OpcodeBrTable { 352 if target := pred.branch.blk; target != bb { 353 panic(fmt.Sprintf("BUG: '%s' is not branch to %s, but to %s", 354 pred.branch.Format(b), bb.Name(), target.Name())) 355 } 356 } 357 358 var exp int 359 if bb.ReturnBlock() { 360 exp = len(b.currentSignature.Results) 361 } else { 362 exp = len(bb.params) 363 } 364 365 if len(pred.branch.vs) != exp { 366 panic(fmt.Sprintf( 367 "BUG: len(argument at %s) != len(params at %s): %d != %d: %s", 368 pred.blk.Name(), bb.Name(), 369 len(pred.branch.vs), len(bb.params), pred.branch.Format(b), 370 )) 371 } 372 373 } 374 } 375 } 376 377 // String implements fmt.Stringer for debugging purpose only. 378 func (bb *basicBlock) String() string { 379 return strconv.Itoa(int(bb.id)) 380 } 381 382 // LoopNestingForestChildren implements BasicBlock.LoopNestingForestChildren. 383 func (bb *basicBlock) LoopNestingForestChildren() []BasicBlock { 384 return bb.loopNestingForestChildren 385 } 386 387 // LoopHeader implements BasicBlock.LoopHeader. 388 func (bb *basicBlock) LoopHeader() bool { 389 return bb.loopHeader 390 }