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