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  }