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  }