github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/wazevo/ssa/builder.go (about)

     1  package ssa
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/wasilibs/wazerox/internal/engine/wazevo/wazevoapi"
     9  )
    10  
    11  // Builder is used to builds SSA consisting of Basic Blocks per function.
    12  type Builder interface {
    13  	// Init must be called to reuse this builder for the next function.
    14  	Init(typ *Signature)
    15  
    16  	// Signature returns the Signature of the currently-compiled function.
    17  	Signature() *Signature
    18  
    19  	// BlockIDMax returns the maximum value of BasicBlocksID existing in the currently-compiled function.
    20  	BlockIDMax() BasicBlockID
    21  
    22  	// AllocateBasicBlock creates a basic block in SSA function.
    23  	AllocateBasicBlock() BasicBlock
    24  
    25  	// CurrentBlock returns the currently handled BasicBlock which is set by the latest call to SetCurrentBlock.
    26  	CurrentBlock() BasicBlock
    27  
    28  	// EntryBlock returns the entry BasicBlock of the currently-compiled function.
    29  	EntryBlock() BasicBlock
    30  
    31  	// SetCurrentBlock sets the instruction insertion target to the BasicBlock `b`.
    32  	SetCurrentBlock(b BasicBlock)
    33  
    34  	// DeclareVariable declares a Variable of the given Type.
    35  	DeclareVariable(Type) Variable
    36  
    37  	// DefineVariable defines a variable in the `block` with value.
    38  	// The defining instruction will be inserted into the `block`.
    39  	DefineVariable(variable Variable, value Value, block BasicBlock)
    40  
    41  	// DefineVariableInCurrentBB is the same as DefineVariable except the definition is
    42  	// inserted into the current BasicBlock. Alias to DefineVariable(x, y, CurrentBlock()).
    43  	DefineVariableInCurrentBB(variable Variable, value Value)
    44  
    45  	// AllocateInstruction returns a new Instruction.
    46  	AllocateInstruction() *Instruction
    47  
    48  	// InsertInstruction executes BasicBlock.InsertInstruction for the currently handled basic block.
    49  	InsertInstruction(raw *Instruction)
    50  
    51  	// allocateValue allocates an unused Value.
    52  	allocateValue(typ Type) Value
    53  
    54  	// MustFindValue searches the latest definition of the given Variable and returns the result.
    55  	MustFindValue(variable Variable) Value
    56  
    57  	// FindValueInLinearPath tries to find the latest definition of the given Variable in the linear path to the current BasicBlock.
    58  	// If it cannot find the definition, or it's not sealed yet, it returns ValueInvalid.
    59  	FindValueInLinearPath(variable Variable) Value
    60  
    61  	// Seal declares that we've known all the predecessors to this block and were added via AddPred.
    62  	// After calling this, AddPred will be forbidden.
    63  	Seal(blk BasicBlock)
    64  
    65  	// AnnotateValue is for debugging purpose.
    66  	AnnotateValue(value Value, annotation string)
    67  
    68  	// DeclareSignature appends the *Signature to be referenced by various instructions (e.g. OpcodeCall).
    69  	DeclareSignature(signature *Signature)
    70  
    71  	// Signatures returns the slice of declared Signatures.
    72  	Signatures() []*Signature
    73  
    74  	// ResolveSignature returns the Signature which corresponds to SignatureID.
    75  	ResolveSignature(id SignatureID) *Signature
    76  
    77  	// RunPasses runs various passes on the constructed SSA function.
    78  	RunPasses()
    79  
    80  	// Format returns the debugging string of the SSA function.
    81  	Format() string
    82  
    83  	// BlockIteratorBegin initializes the state to iterate over all the valid BasicBlock(s) compiled.
    84  	// Combined with BlockIteratorNext, we can use this like:
    85  	//
    86  	// 	for blk := builder.BlockIteratorBegin(); blk != nil; blk = builder.BlockIteratorNext() {
    87  	// 		// ...
    88  	//	}
    89  	//
    90  	// The returned blocks are ordered in the order of AllocateBasicBlock being called.
    91  	BlockIteratorBegin() BasicBlock
    92  
    93  	// BlockIteratorNext advances the state for iteration initialized by BlockIteratorBegin.
    94  	// Returns nil if there's no unseen BasicBlock.
    95  	BlockIteratorNext() BasicBlock
    96  
    97  	// ValueRefCounts returns the map of ValueID to its reference count.
    98  	// The returned slice must not be modified.
    99  	ValueRefCounts() []int
   100  
   101  	// LayoutBlocks layouts the BasicBlock(s) so that backend can easily generate the code.
   102  	// During its process, it splits the critical edges in the function.
   103  	// This must be called after RunPasses. Otherwise, it panics.
   104  	//
   105  	// The resulting order is available via BlockIteratorReversePostOrderBegin and BlockIteratorReversePostOrderNext.
   106  	LayoutBlocks()
   107  
   108  	// BlockIteratorReversePostOrderBegin is almost the same as BlockIteratorBegin except it returns the BasicBlock in the reverse post-order.
   109  	// This is available after RunPasses is run.
   110  	BlockIteratorReversePostOrderBegin() BasicBlock
   111  
   112  	// BlockIteratorReversePostOrderNext is almost the same as BlockIteratorPostOrderNext except it returns the BasicBlock in the reverse post-order.
   113  	// This is available after RunPasses is run.
   114  	BlockIteratorReversePostOrderNext() BasicBlock
   115  
   116  	// ReturnBlock returns the BasicBlock which is used to return from the function.
   117  	ReturnBlock() BasicBlock
   118  
   119  	// InsertUndefined inserts an undefined instruction at the current position.
   120  	InsertUndefined()
   121  
   122  	// SetCurrentSourceOffset sets the current source offset. The incoming instruction will be annotated with this offset.
   123  	SetCurrentSourceOffset(line SourceOffset)
   124  
   125  	// LoopNestingForestRoots returns the roots of the loop nesting forest.
   126  	LoopNestingForestRoots() []BasicBlock
   127  
   128  	// LowestCommonAncestor returns the lowest common ancestor in the dominator tree of the given BasicBlock(s).
   129  	LowestCommonAncestor(blk1, blk2 BasicBlock) BasicBlock
   130  
   131  	// Idom returns the immediate dominator of the given BasicBlock.
   132  	Idom(blk BasicBlock) BasicBlock
   133  }
   134  
   135  // NewBuilder returns a new Builder implementation.
   136  func NewBuilder() Builder {
   137  	return &builder{
   138  		instructionsPool:               wazevoapi.NewPool[Instruction](resetInstruction),
   139  		basicBlocksPool:                wazevoapi.NewPool[basicBlock](resetBasicBlock),
   140  		valueAnnotations:               make(map[ValueID]string),
   141  		signatures:                     make(map[SignatureID]*Signature),
   142  		blkVisited:                     make(map[*basicBlock]int),
   143  		valueIDAliases:                 make(map[ValueID]Value),
   144  		redundantParameterIndexToValue: make(map[int]Value),
   145  		returnBlk:                      &basicBlock{id: basicBlockIDReturnBlock},
   146  	}
   147  }
   148  
   149  // builder implements Builder interface.
   150  type builder struct {
   151  	basicBlocksPool  wazevoapi.Pool[basicBlock]
   152  	instructionsPool wazevoapi.Pool[Instruction]
   153  	signatures       map[SignatureID]*Signature
   154  	currentSignature *Signature
   155  
   156  	// reversePostOrderedBasicBlocks are the BasicBlock(s) ordered in the reverse post-order after passCalculateImmediateDominators.
   157  	reversePostOrderedBasicBlocks []*basicBlock
   158  	currentBB                     *basicBlock
   159  	returnBlk                     *basicBlock
   160  
   161  	// variables track the types for Variable with the index regarded Variable.
   162  	variables []Type
   163  	// nextValueID is used by builder.AllocateValue.
   164  	nextValueID ValueID
   165  	// nextVariable is used by builder.AllocateVariable.
   166  	nextVariable Variable
   167  
   168  	valueIDAliases   map[ValueID]Value
   169  	valueAnnotations map[ValueID]string
   170  
   171  	// valueRefCounts is used to lower the SSA in backend, and will be calculated
   172  	// by the last SSA-level optimization pass.
   173  	valueRefCounts []int
   174  
   175  	// dominators stores the immediate dominator of each BasicBlock.
   176  	// The index is blockID of the BasicBlock.
   177  	dominators []*basicBlock
   178  	sparseTree dominatorSparseTree
   179  
   180  	// loopNestingForestRoots are the roots of the loop nesting forest.
   181  	loopNestingForestRoots []BasicBlock
   182  
   183  	// The followings are used for optimization passes/deterministic compilation.
   184  	instStack                      []*Instruction
   185  	blkVisited                     map[*basicBlock]int
   186  	valueIDToInstruction           []*Instruction
   187  	blkStack                       []*basicBlock
   188  	blkStack2                      []*basicBlock
   189  	ints                           []int
   190  	redundantParameterIndexToValue map[int]Value
   191  	vars                           []Variable
   192  
   193  	// blockIterCur is used to implement blockIteratorBegin and blockIteratorNext.
   194  	blockIterCur int
   195  
   196  	// donePasses is true if RunPasses is called.
   197  	donePasses bool
   198  	// doneBlockLayout is true if LayoutBlocks is called.
   199  	doneBlockLayout bool
   200  
   201  	currentSourceOffset SourceOffset
   202  }
   203  
   204  // ReturnBlock implements Builder.ReturnBlock.
   205  func (b *builder) ReturnBlock() BasicBlock {
   206  	return b.returnBlk
   207  }
   208  
   209  // Init implements Builder.Reset.
   210  func (b *builder) Init(s *Signature) {
   211  	b.nextVariable = 0
   212  	b.currentSignature = s
   213  	resetBasicBlock(b.returnBlk)
   214  	b.instructionsPool.Reset()
   215  	b.basicBlocksPool.Reset()
   216  	b.donePasses = false
   217  	for _, sig := range b.signatures {
   218  		sig.used = false
   219  	}
   220  
   221  	b.ints = b.ints[:0]
   222  	b.blkStack = b.blkStack[:0]
   223  	b.blkStack2 = b.blkStack2[:0]
   224  	b.dominators = b.dominators[:0]
   225  	b.loopNestingForestRoots = b.loopNestingForestRoots[:0]
   226  
   227  	for i := 0; i < b.basicBlocksPool.Allocated(); i++ {
   228  		blk := b.basicBlocksPool.View(i)
   229  		delete(b.blkVisited, blk)
   230  	}
   231  	b.basicBlocksPool.Reset()
   232  
   233  	for v := ValueID(0); v < b.nextValueID; v++ {
   234  		delete(b.valueAnnotations, v)
   235  		delete(b.valueIDAliases, v)
   236  		b.valueRefCounts[v] = 0
   237  		b.valueIDToInstruction[v] = nil
   238  	}
   239  	b.nextValueID = 0
   240  	b.reversePostOrderedBasicBlocks = b.reversePostOrderedBasicBlocks[:0]
   241  	b.donePasses = false
   242  	b.doneBlockLayout = false
   243  	for i := range b.valueRefCounts {
   244  		b.valueRefCounts[i] = 0
   245  	}
   246  
   247  	b.currentSourceOffset = sourceOffsetUnknown
   248  }
   249  
   250  // Signature implements Builder.Signature.
   251  func (b *builder) Signature() *Signature {
   252  	return b.currentSignature
   253  }
   254  
   255  // AnnotateValue implements Builder.AnnotateValue.
   256  func (b *builder) AnnotateValue(value Value, a string) {
   257  	b.valueAnnotations[value.ID()] = a
   258  }
   259  
   260  // AllocateInstruction implements Builder.AllocateInstruction.
   261  func (b *builder) AllocateInstruction() *Instruction {
   262  	instr := b.instructionsPool.Allocate()
   263  	instr.id = b.instructionsPool.Allocated()
   264  	return instr
   265  }
   266  
   267  // DeclareSignature implements Builder.AnnotateValue.
   268  func (b *builder) DeclareSignature(s *Signature) {
   269  	b.signatures[s.ID] = s
   270  	s.used = false
   271  }
   272  
   273  // Signatures implements Builder.Signatures.
   274  func (b *builder) Signatures() (ret []*Signature) {
   275  	for _, sig := range b.signatures {
   276  		ret = append(ret, sig)
   277  	}
   278  	sort.Slice(ret, func(i, j int) bool {
   279  		return ret[i].ID < ret[j].ID
   280  	})
   281  	return
   282  }
   283  
   284  // SetCurrentSourceOffset implements Builder.SetCurrentSourceOffset.
   285  func (b *builder) SetCurrentSourceOffset(l SourceOffset) {
   286  	b.currentSourceOffset = l
   287  }
   288  
   289  func (b *builder) usedSignatures() (ret []*Signature) {
   290  	for _, sig := range b.signatures {
   291  		if sig.used {
   292  			ret = append(ret, sig)
   293  		}
   294  	}
   295  	sort.Slice(ret, func(i, j int) bool {
   296  		return ret[i].ID < ret[j].ID
   297  	})
   298  	return
   299  }
   300  
   301  // ResolveSignature implements Builder.ResolveSignature.
   302  func (b *builder) ResolveSignature(id SignatureID) *Signature {
   303  	return b.signatures[id]
   304  }
   305  
   306  // AllocateBasicBlock implements Builder.AllocateBasicBlock.
   307  func (b *builder) AllocateBasicBlock() BasicBlock {
   308  	return b.allocateBasicBlock()
   309  }
   310  
   311  // allocateBasicBlock allocates a new basicBlock.
   312  func (b *builder) allocateBasicBlock() *basicBlock {
   313  	id := BasicBlockID(b.basicBlocksPool.Allocated())
   314  	blk := b.basicBlocksPool.Allocate()
   315  	blk.id = id
   316  	blk.lastDefinitions = make(map[Variable]Value)
   317  	blk.unknownValues = make(map[Variable]Value)
   318  	return blk
   319  }
   320  
   321  // Idom implements Builder.Idom.
   322  func (b *builder) Idom(blk BasicBlock) BasicBlock {
   323  	return b.dominators[blk.ID()]
   324  }
   325  
   326  // InsertInstruction implements Builder.InsertInstruction.
   327  func (b *builder) InsertInstruction(instr *Instruction) {
   328  	b.currentBB.InsertInstruction(instr)
   329  
   330  	if l := b.currentSourceOffset; l.Valid() {
   331  		// Emit the source offset info only when the instruction has side effect because
   332  		// these are the only instructions that are accessed by stack unwinding.
   333  		// This reduces the significant amount of the offset info in the binary.
   334  		if instr.sideEffect() != sideEffectNone {
   335  			instr.annotateSourceOffset(l)
   336  		}
   337  	}
   338  
   339  	resultTypesFn := instructionReturnTypes[instr.opcode]
   340  	if resultTypesFn == nil {
   341  		panic("TODO: " + instr.Format(b))
   342  	}
   343  
   344  	t1, ts := resultTypesFn(b, instr)
   345  	if t1.invalid() {
   346  		return
   347  	}
   348  
   349  	r1 := b.allocateValue(t1)
   350  	instr.rValue = r1
   351  
   352  	tsl := len(ts)
   353  	if tsl == 0 {
   354  		return
   355  	}
   356  
   357  	// TODO: reuse slices, though this seems not to be common.
   358  	instr.rValues = make([]Value, tsl)
   359  	for i := 0; i < tsl; i++ {
   360  		instr.rValues[i] = b.allocateValue(ts[i])
   361  	}
   362  }
   363  
   364  // DefineVariable implements Builder.DefineVariable.
   365  func (b *builder) DefineVariable(variable Variable, value Value, block BasicBlock) {
   366  	if b.variables[variable].invalid() {
   367  		panic("BUG: trying to define variable " + variable.String() + " but is not declared yet")
   368  	}
   369  
   370  	if b.variables[variable] != value.Type() {
   371  		panic(fmt.Sprintf("BUG: inconsistent type for variable %d: expected %s but got %s", variable, b.variables[variable], value.Type()))
   372  	}
   373  	bb := block.(*basicBlock)
   374  	bb.lastDefinitions[variable] = value
   375  }
   376  
   377  // DefineVariableInCurrentBB implements Builder.DefineVariableInCurrentBB.
   378  func (b *builder) DefineVariableInCurrentBB(variable Variable, value Value) {
   379  	b.DefineVariable(variable, value, b.currentBB)
   380  }
   381  
   382  // SetCurrentBlock implements Builder.SetCurrentBlock.
   383  func (b *builder) SetCurrentBlock(bb BasicBlock) {
   384  	b.currentBB = bb.(*basicBlock)
   385  }
   386  
   387  // CurrentBlock implements Builder.CurrentBlock.
   388  func (b *builder) CurrentBlock() BasicBlock {
   389  	return b.currentBB
   390  }
   391  
   392  // EntryBlock implements Builder.EntryBlock.
   393  func (b *builder) EntryBlock() BasicBlock {
   394  	return b.entryBlk()
   395  }
   396  
   397  // DeclareVariable implements Builder.DeclareVariable.
   398  func (b *builder) DeclareVariable(typ Type) Variable {
   399  	v := b.allocateVariable()
   400  	iv := int(v)
   401  	if l := len(b.variables); l <= iv {
   402  		b.variables = append(b.variables, make([]Type, 2*(l+1))...)
   403  	}
   404  	b.variables[v] = typ
   405  	return v
   406  }
   407  
   408  // allocateVariable allocates a new variable.
   409  func (b *builder) allocateVariable() (ret Variable) {
   410  	ret = b.nextVariable
   411  	b.nextVariable++
   412  	return
   413  }
   414  
   415  // allocateValue implements Builder.AllocateValue.
   416  func (b *builder) allocateValue(typ Type) (v Value) {
   417  	v = Value(b.nextValueID)
   418  	v = v.setType(typ)
   419  	b.nextValueID++
   420  	return
   421  }
   422  
   423  // FindValueInLinearPath implements Builder.FindValueInLinearPath.
   424  func (b *builder) FindValueInLinearPath(variable Variable) Value {
   425  	return b.findValueInLinearPath(variable, b.currentBB)
   426  }
   427  
   428  func (b *builder) findValueInLinearPath(variable Variable, blk *basicBlock) Value {
   429  	if val, ok := blk.lastDefinitions[variable]; ok {
   430  		return val
   431  	} else if !blk.sealed {
   432  		return ValueInvalid
   433  	}
   434  
   435  	if pred := blk.singlePred; pred != nil {
   436  		// If this block is sealed and have only one predecessor,
   437  		// we can use the value in that block without ambiguity on definition.
   438  		return b.findValueInLinearPath(variable, pred)
   439  	}
   440  	if len(blk.preds) == 1 {
   441  		panic("BUG")
   442  	}
   443  	return ValueInvalid
   444  }
   445  
   446  // MustFindValue implements Builder.MustFindValue.
   447  func (b *builder) MustFindValue(variable Variable) Value {
   448  	typ := b.definedVariableType(variable)
   449  	return b.findValue(typ, variable, b.currentBB)
   450  }
   451  
   452  // findValue recursively tries to find the latest definition of a `variable`. The algorithm is described in
   453  // the section 2 of the paper https://link.springer.com/content/pdf/10.1007/978-3-642-37051-9_6.pdf.
   454  //
   455  // TODO: reimplement this in iterative, not recursive, to avoid stack overflow.
   456  func (b *builder) findValue(typ Type, variable Variable, blk *basicBlock) Value {
   457  	if val, ok := blk.lastDefinitions[variable]; ok {
   458  		// The value is already defined in this block!
   459  		return val
   460  	} else if !blk.sealed { // Incomplete CFG as in the paper.
   461  		// If this is not sealed, that means it might have additional unknown predecessor later on.
   462  		// So we temporarily define the placeholder value here (not add as a parameter yet!),
   463  		// and record it as unknown.
   464  		// The unknown values are resolved when we call seal this block via BasicBlock.Seal().
   465  		value := b.allocateValue(typ)
   466  		if wazevoapi.SSALoggingEnabled {
   467  			fmt.Printf("adding unknown value placeholder for %s at %d\n", variable, blk.id)
   468  		}
   469  		blk.lastDefinitions[variable] = value
   470  		blk.unknownValues[variable] = value
   471  		return value
   472  	}
   473  
   474  	if pred := blk.singlePred; pred != nil {
   475  		// If this block is sealed and have only one predecessor,
   476  		// we can use the value in that block without ambiguity on definition.
   477  		return b.findValue(typ, variable, pred)
   478  	} else if len(blk.preds) == 0 {
   479  		panic("BUG: value is not defined for " + variable.String())
   480  	}
   481  
   482  	// If this block has multiple predecessors, we have to gather the definitions,
   483  	// and treat them as an argument to this block.
   484  	//
   485  	// The first thing is to define a new parameter to this block which may or may not be redundant, but
   486  	// later we eliminate trivial params in an optimization pass. This must be done before finding the
   487  	// definitions in the predecessors so that we can break the cycle.
   488  	paramValue := blk.AddParam(b, typ)
   489  	b.DefineVariable(variable, paramValue, blk)
   490  
   491  	// After the new param is added, we have to manipulate the original branching instructions
   492  	// in predecessors so that they would pass the definition of `variable` as the argument to
   493  	// the newly added PHI.
   494  	for i := range blk.preds {
   495  		pred := &blk.preds[i]
   496  		value := b.findValue(typ, variable, pred.blk)
   497  		pred.branch.addArgumentBranchInst(value)
   498  	}
   499  	return paramValue
   500  }
   501  
   502  // Seal implements Builder.Seal.
   503  func (b *builder) Seal(raw BasicBlock) {
   504  	blk := raw.(*basicBlock)
   505  	if len(blk.preds) == 1 {
   506  		blk.singlePred = blk.preds[0].blk
   507  	}
   508  	blk.sealed = true
   509  
   510  	// To get the deterministic compilation,
   511  	// we need to sort the parameters in the order of the variable index.
   512  	b.vars = b.vars[:0]
   513  	for v := range blk.unknownValues {
   514  		b.vars = append(b.vars, v)
   515  	}
   516  	sort.Slice(b.vars, func(i, j int) bool {
   517  		return b.vars[i] < b.vars[j]
   518  	})
   519  
   520  	for _, variable := range b.vars {
   521  		phiValue := blk.unknownValues[variable]
   522  		typ := b.definedVariableType(variable)
   523  		blk.addParamOn(typ, phiValue)
   524  		for i := range blk.preds {
   525  			pred := &blk.preds[i]
   526  			predValue := b.findValue(typ, variable, pred.blk)
   527  			if !predValue.Valid() {
   528  				panic("BUG: value is not defined anywhere in the predecessors in the CFG")
   529  			}
   530  			pred.branch.addArgumentBranchInst(predValue)
   531  		}
   532  	}
   533  }
   534  
   535  // definedVariableType returns the type of the given variable. If the variable is not defined yet, it panics.
   536  func (b *builder) definedVariableType(variable Variable) Type {
   537  	typ := b.variables[variable]
   538  	if typ.invalid() {
   539  		panic(fmt.Sprintf("%s is not defined yet", variable))
   540  	}
   541  	return typ
   542  }
   543  
   544  // Format implements Builder.Format.
   545  func (b *builder) Format() string {
   546  	str := strings.Builder{}
   547  	usedSigs := b.usedSignatures()
   548  	if len(usedSigs) > 0 {
   549  		str.WriteByte('\n')
   550  		str.WriteString("signatures:\n")
   551  		for _, sig := range usedSigs {
   552  			str.WriteByte('\t')
   553  			str.WriteString(sig.String())
   554  			str.WriteByte('\n')
   555  		}
   556  	}
   557  
   558  	var iterBegin, iterNext func() *basicBlock
   559  	if b.doneBlockLayout {
   560  		iterBegin, iterNext = b.blockIteratorReversePostOrderBegin, b.blockIteratorReversePostOrderNext
   561  	} else {
   562  		iterBegin, iterNext = b.blockIteratorBegin, b.blockIteratorNext
   563  	}
   564  	for bb := iterBegin(); bb != nil; bb = iterNext() {
   565  		str.WriteByte('\n')
   566  		str.WriteString(bb.FormatHeader(b))
   567  		str.WriteByte('\n')
   568  
   569  		for cur := bb.Root(); cur != nil; cur = cur.Next() {
   570  			str.WriteByte('\t')
   571  			str.WriteString(cur.Format(b))
   572  			str.WriteByte('\n')
   573  		}
   574  	}
   575  	return str.String()
   576  }
   577  
   578  // BlockIteratorNext implements Builder.BlockIteratorNext.
   579  func (b *builder) BlockIteratorNext() BasicBlock {
   580  	if blk := b.blockIteratorNext(); blk == nil {
   581  		return nil // BasicBlock((*basicBlock)(nil)) != BasicBlock(nil)
   582  	} else {
   583  		return blk
   584  	}
   585  }
   586  
   587  // BlockIteratorNext implements Builder.BlockIteratorNext.
   588  func (b *builder) blockIteratorNext() *basicBlock {
   589  	index := b.blockIterCur
   590  	for {
   591  		if index == b.basicBlocksPool.Allocated() {
   592  			return nil
   593  		}
   594  		ret := b.basicBlocksPool.View(index)
   595  		index++
   596  		if !ret.invalid {
   597  			b.blockIterCur = index
   598  			return ret
   599  		}
   600  	}
   601  }
   602  
   603  // BlockIteratorBegin implements Builder.BlockIteratorBegin.
   604  func (b *builder) BlockIteratorBegin() BasicBlock {
   605  	return b.blockIteratorBegin()
   606  }
   607  
   608  // BlockIteratorBegin implements Builder.BlockIteratorBegin.
   609  func (b *builder) blockIteratorBegin() *basicBlock {
   610  	b.blockIterCur = 0
   611  	return b.blockIteratorNext()
   612  }
   613  
   614  // BlockIteratorReversePostOrderBegin implements Builder.BlockIteratorReversePostOrderBegin.
   615  func (b *builder) BlockIteratorReversePostOrderBegin() BasicBlock {
   616  	return b.blockIteratorReversePostOrderBegin()
   617  }
   618  
   619  // BlockIteratorBegin implements Builder.BlockIteratorBegin.
   620  func (b *builder) blockIteratorReversePostOrderBegin() *basicBlock {
   621  	b.blockIterCur = 0
   622  	return b.blockIteratorReversePostOrderNext()
   623  }
   624  
   625  // BlockIteratorReversePostOrderNext implements Builder.BlockIteratorReversePostOrderNext.
   626  func (b *builder) BlockIteratorReversePostOrderNext() BasicBlock {
   627  	if blk := b.blockIteratorReversePostOrderNext(); blk == nil {
   628  		return nil // BasicBlock((*basicBlock)(nil)) != BasicBlock(nil)
   629  	} else {
   630  		return blk
   631  	}
   632  }
   633  
   634  // BlockIteratorNext implements Builder.BlockIteratorNext.
   635  func (b *builder) blockIteratorReversePostOrderNext() *basicBlock {
   636  	if b.blockIterCur >= len(b.reversePostOrderedBasicBlocks) {
   637  		return nil
   638  	} else {
   639  		ret := b.reversePostOrderedBasicBlocks[b.blockIterCur]
   640  		b.blockIterCur++
   641  		return ret
   642  	}
   643  }
   644  
   645  // ValueRefCounts implements Builder.ValueRefCounts.
   646  func (b *builder) ValueRefCounts() []int {
   647  	return b.valueRefCounts
   648  }
   649  
   650  // alias records the alias of the given values. The alias(es) will be
   651  // eliminated in the optimization pass via resolveArgumentAlias.
   652  func (b *builder) alias(dst, src Value) {
   653  	b.valueIDAliases[dst.ID()] = src
   654  }
   655  
   656  // resolveArgumentAlias resolves the alias of the arguments of the given instruction.
   657  func (b *builder) resolveArgumentAlias(instr *Instruction) {
   658  	if instr.v.Valid() {
   659  		instr.v = b.resolveAlias(instr.v)
   660  	}
   661  
   662  	if instr.v2.Valid() {
   663  		instr.v2 = b.resolveAlias(instr.v2)
   664  	}
   665  
   666  	if instr.v3.Valid() {
   667  		instr.v3 = b.resolveAlias(instr.v3)
   668  	}
   669  
   670  	for i, v := range instr.vs {
   671  		instr.vs[i] = b.resolveAlias(v)
   672  	}
   673  }
   674  
   675  // resolveAlias resolves the alias of the given value.
   676  func (b *builder) resolveAlias(v Value) Value {
   677  	// Some aliases are chained, so we need to resolve them recursively.
   678  	for {
   679  		if src, ok := b.valueIDAliases[v.ID()]; ok {
   680  			v = src
   681  		} else {
   682  			break
   683  		}
   684  	}
   685  	return v
   686  }
   687  
   688  // entryBlk returns the entry block of the function.
   689  func (b *builder) entryBlk() *basicBlock {
   690  	return b.basicBlocksPool.View(0)
   691  }
   692  
   693  // isDominatedBy returns true if the given block `n` is dominated by the given block `d`.
   694  // Before calling this, the builder must pass by passCalculateImmediateDominators.
   695  func (b *builder) isDominatedBy(n *basicBlock, d *basicBlock) bool {
   696  	if len(b.dominators) == 0 {
   697  		panic("BUG: passCalculateImmediateDominators must be called before calling isDominatedBy")
   698  	}
   699  	ent := b.entryBlk()
   700  	doms := b.dominators
   701  	for n != d && n != ent {
   702  		n = doms[n.id]
   703  	}
   704  	return n == d
   705  }
   706  
   707  // BlockIDMax implements Builder.BlockIDMax.
   708  func (b *builder) BlockIDMax() BasicBlockID {
   709  	return BasicBlockID(b.basicBlocksPool.Allocated())
   710  }
   711  
   712  // LayoutBlocks implements Builder.LayoutBlocks. This re-organizes builder.reversePostOrderedBasicBlocks.
   713  //
   714  // TODO: there are tons of room for improvement here. e.g. LLVM has BlockPlacementPass using BlockFrequencyInfo,
   715  // BranchProbabilityInfo, and LoopInfo to do a much better job. Also, if we have the profiling instrumentation
   716  // like ball-larus algorithm, then we could do profile-guided optimization. Basically all of them are trying
   717  // to maximize the fall-through opportunities which is most efficient.
   718  //
   719  // Here, fallthrough happens when a block ends with jump instruction whose target is the right next block in the
   720  // builder.reversePostOrderedBasicBlocks.
   721  //
   722  // Currently, we just place blocks using the DFS reverse post-order of the dominator tree with the heuristics:
   723  //  1. a split edge trampoline towards a loop header will be placed as a fallthrough.
   724  //  2. we invert the brz and brnz if it makes the fallthrough more likely.
   725  //
   726  // This heuristic is done in maybeInvertBranches function.
   727  func (b *builder) LayoutBlocks() {
   728  	if !b.donePasses {
   729  		panic("LayoutBlocks must be called after all passes are done")
   730  	}
   731  
   732  	b.clearBlkVisited()
   733  
   734  	// We might end up splitting critical edges which adds more basic blocks,
   735  	// so we store the currently existing basic blocks in nonSplitBlocks temporarily.
   736  	// That way we can iterate over the original basic blocks while appending new ones into reversePostOrderedBasicBlocks.
   737  	nonSplitBlocks := b.blkStack[:0]
   738  	for i, blk := range b.reversePostOrderedBasicBlocks {
   739  		if !blk.Valid() {
   740  			continue
   741  		}
   742  		nonSplitBlocks = append(nonSplitBlocks, blk)
   743  		if i != len(b.reversePostOrderedBasicBlocks)-1 {
   744  			_ = maybeInvertBranches(blk, b.reversePostOrderedBasicBlocks[i+1])
   745  		}
   746  	}
   747  
   748  	var trampolines []*basicBlock
   749  
   750  	// Reset the order slice since we update on the fly by splitting critical edges.
   751  	b.reversePostOrderedBasicBlocks = b.reversePostOrderedBasicBlocks[:0]
   752  	uninsertedTrampolines := b.blkStack2[:0]
   753  	for _, blk := range nonSplitBlocks {
   754  		for i := range blk.preds {
   755  			pred := blk.preds[i].blk
   756  			if _, ok := b.blkVisited[pred]; ok || !pred.Valid() {
   757  				continue
   758  			} else if pred.reversePostOrder < blk.reversePostOrder {
   759  				// This means the edge is critical, and this pred is the trampoline and yet to be inserted.
   760  				// Split edge trampolines must come before the destination in reverse post-order.
   761  				b.reversePostOrderedBasicBlocks = append(b.reversePostOrderedBasicBlocks, pred)
   762  				b.blkVisited[pred] = 0 // mark as inserted, the value is not used.
   763  			}
   764  		}
   765  
   766  		// Now that we've already added all the potential trampoline blocks incoming to this block,
   767  		// we can add this block itself.
   768  		b.reversePostOrderedBasicBlocks = append(b.reversePostOrderedBasicBlocks, blk)
   769  		b.blkVisited[blk] = 0 // mark as inserted, the value is not used.
   770  
   771  		if len(blk.success) < 2 {
   772  			// There won't be critical edge originating from this block.
   773  			continue
   774  		} else if blk.currentInstr.opcode == OpcodeBrTable {
   775  			// We don't split critical edges here, because at the construction site of BrTable, we already split the edges.
   776  			continue
   777  		}
   778  
   779  		for sidx, succ := range blk.success {
   780  			if !succ.ReturnBlock() && // If the successor is a return block, we need to split the edge any way because we need "epilogue" to be inserted.
   781  				// Plus if there's no multiple incoming edges to this successor, (pred, succ) is not critical.
   782  				len(succ.preds) < 2 {
   783  				continue
   784  			}
   785  
   786  			// Otherwise, we are sure this is a critical edge. To modify the CFG, we need to find the predecessor info
   787  			// from the successor.
   788  			var predInfo *basicBlockPredecessorInfo
   789  			for i := range succ.preds { // This linear search should not be a problem since the number of predecessors should almost always small.
   790  				pred := &succ.preds[i]
   791  				if pred.blk == blk {
   792  					predInfo = pred
   793  					break
   794  				}
   795  			}
   796  
   797  			if predInfo == nil {
   798  				// This must be a bug in somewhere around branch manipulation.
   799  				panic("BUG: predecessor info not found while the successor exists in successors list")
   800  			}
   801  
   802  			if wazevoapi.SSALoggingEnabled {
   803  				fmt.Printf("trying to split edge from %d->%d at %s\n",
   804  					blk.ID(), succ.ID(), predInfo.branch.Format(b))
   805  			}
   806  
   807  			trampoline := b.splitCriticalEdge(blk, succ, predInfo)
   808  			// Update the successors slice because the target is no longer the original `succ`.
   809  			blk.success[sidx] = trampoline
   810  
   811  			if wazevoapi.SSAValidationEnabled {
   812  				trampolines = append(trampolines, trampoline)
   813  			}
   814  
   815  			if wazevoapi.SSALoggingEnabled {
   816  				fmt.Printf("edge split from %d->%d at %s as %d->%d->%d \n",
   817  					blk.ID(), succ.ID(), predInfo.branch.Format(b),
   818  					blk.ID(), trampoline.ID(), succ.ID())
   819  			}
   820  
   821  			fallthroughBranch := blk.currentInstr
   822  			if fallthroughBranch.opcode == OpcodeJump && fallthroughBranch.blk == trampoline {
   823  				// This can be lowered as fallthrough at the end of the block.
   824  				b.reversePostOrderedBasicBlocks = append(b.reversePostOrderedBasicBlocks, trampoline)
   825  				b.blkVisited[trampoline] = 0 // mark as inserted, the value is not used.
   826  			} else {
   827  				uninsertedTrampolines = append(uninsertedTrampolines, trampoline)
   828  			}
   829  		}
   830  
   831  		for _, trampoline := range uninsertedTrampolines {
   832  			if trampoline.success[0].reversePostOrder <= trampoline.reversePostOrder { // "<=", not "<" because the target might be itself.
   833  				// This means the critical edge was backward, so we insert after the current block immediately.
   834  				b.reversePostOrderedBasicBlocks = append(b.reversePostOrderedBasicBlocks, trampoline)
   835  				b.blkVisited[trampoline] = 0 // mark as inserted, the value is not used.
   836  			} // If the target is forward, we can wait to insert until the target is inserted.
   837  		}
   838  		uninsertedTrampolines = uninsertedTrampolines[:0] // Reuse the stack for the next block.
   839  	}
   840  
   841  	if wazevoapi.SSALoggingEnabled {
   842  		var bs []string
   843  		for _, blk := range b.reversePostOrderedBasicBlocks {
   844  			bs = append(bs, blk.Name())
   845  		}
   846  		fmt.Println("ordered blocks: ", strings.Join(bs, ", "))
   847  	}
   848  
   849  	if wazevoapi.SSAValidationEnabled {
   850  		for _, trampoline := range trampolines {
   851  			if _, ok := b.blkVisited[trampoline]; !ok {
   852  				panic("BUG: trampoline block not inserted: " + trampoline.FormatHeader(b))
   853  			}
   854  			trampoline.validate(b)
   855  		}
   856  	}
   857  
   858  	// Critical edges are split, so we fix the loop nesting forest.
   859  	buildLoopNestingForest(b)
   860  	buildDominatorTree(b)
   861  
   862  	// Reuse the stack for the next iteration.
   863  	b.blkStack2 = uninsertedTrampolines[:0]
   864  
   865  	// Now that we know the final placement of the blocks, we can explicitly mark the fallthrough jumps.
   866  	b.markFallthroughJumps()
   867  	b.doneBlockLayout = true
   868  }
   869  
   870  // markFallthroughJumps finds the fallthrough jumps and marks them as such.
   871  func (b *builder) markFallthroughJumps() {
   872  	l := len(b.reversePostOrderedBasicBlocks) - 1
   873  	for i, blk := range b.reversePostOrderedBasicBlocks {
   874  		if i < l {
   875  			cur := blk.currentInstr
   876  			if cur.opcode == OpcodeJump && cur.blk == b.reversePostOrderedBasicBlocks[i+1] {
   877  				cur.AsFallthroughJump()
   878  			}
   879  		}
   880  	}
   881  }
   882  
   883  // maybeInvertBranches inverts the branch instructions if it is likely possible to the fallthrough more likely with simple heuristics.
   884  // nextInRPO is the next block in the reverse post-order.
   885  //
   886  // Returns true if the branch is inverted for testing purpose.
   887  func maybeInvertBranches(now *basicBlock, nextInRPO *basicBlock) bool {
   888  	fallthroughBranch := now.currentInstr
   889  	if fallthroughBranch.opcode == OpcodeBrTable {
   890  		return false
   891  	}
   892  
   893  	condBranch := fallthroughBranch.prev
   894  	if condBranch == nil || (condBranch.opcode != OpcodeBrnz && condBranch.opcode != OpcodeBrz) {
   895  		return false
   896  	}
   897  
   898  	if len(fallthroughBranch.vs) != 0 || len(condBranch.vs) != 0 {
   899  		// If either one of them has arguments, we don't invert the branches.
   900  		return false
   901  	}
   902  
   903  	// So this block has two branches (a conditional branch followed by an unconditional branch) at the end.
   904  	// We can invert the condition of the branch if it makes the fallthrough more likely.
   905  
   906  	fallthroughTarget, condTarget := fallthroughBranch.blk.(*basicBlock), condBranch.blk.(*basicBlock)
   907  
   908  	if fallthroughTarget.loopHeader {
   909  		// First, if the tail's target is loopHeader, we don't need to do anything here,
   910  		// because the edge is likely to be critical edge for complex loops (e.g. loop with branches inside it).
   911  		// That means, we will split the edge in the end of LayoutBlocks function, and insert the trampoline block
   912  		// right after this block, which will be fallthrough in any way.
   913  		return false
   914  	} else if condTarget.loopHeader {
   915  		// On the other hand, if the condBranch's target is loopHeader, we invert the condition of the branch
   916  		// so that we could get the fallthrough to the trampoline block.
   917  		goto invert
   918  	}
   919  
   920  	if fallthroughTarget == nextInRPO {
   921  		// Also, if the tail's target is the next block in the reverse post-order, we don't need to do anything here,
   922  		// because if this is not critical edge, we would end up placing these two blocks adjacent to each other.
   923  		// Even if it is the critical edge, we place the trampoline block right after this block, which will be fallthrough in any way.
   924  		return false
   925  	} else if condTarget == nextInRPO {
   926  		// If the condBranch's target is the next block in the reverse post-order, we invert the condition of the branch
   927  		// so that we could get the fallthrough to the block.
   928  		goto invert
   929  	} else {
   930  		return false
   931  	}
   932  
   933  invert:
   934  	for i := range fallthroughTarget.preds {
   935  		pred := &fallthroughTarget.preds[i]
   936  		if pred.branch == fallthroughBranch {
   937  			pred.branch = condBranch
   938  			break
   939  		}
   940  	}
   941  	for i := range condTarget.preds {
   942  		pred := &condTarget.preds[i]
   943  		if pred.branch == condBranch {
   944  			pred.branch = fallthroughBranch
   945  			break
   946  		}
   947  	}
   948  
   949  	condBranch.InvertBrx()
   950  	condBranch.blk = fallthroughTarget
   951  	fallthroughBranch.blk = condTarget
   952  	if wazevoapi.SSALoggingEnabled {
   953  		fmt.Printf("inverting branches at %d->%d and %d->%d\n",
   954  			now.ID(), fallthroughTarget.ID(), now.ID(), condTarget.ID())
   955  	}
   956  
   957  	return true
   958  }
   959  
   960  // splitCriticalEdge splits the critical edge between the given predecessor (`pred`) and successor (owning `predInfo`).
   961  //
   962  // - `pred` is the source of the critical edge,
   963  // - `succ` is the destination of the critical edge,
   964  // - `predInfo` is the predecessor info in the succ.preds slice which represents the critical edge.
   965  //
   966  // Why splitting critical edges is important? See following links:
   967  //
   968  //   - https://en.wikipedia.org/wiki/Control-flow_graph
   969  //   - https://nickdesaulniers.github.io/blog/2023/01/27/critical-edge-splitting/
   970  //
   971  // The returned basic block is the trampoline block which is inserted to split the critical edge.
   972  func (b *builder) splitCriticalEdge(pred, succ *basicBlock, predInfo *basicBlockPredecessorInfo) *basicBlock {
   973  	// In the following, we convert the following CFG:
   974  	//
   975  	//     pred --(originalBranch)--> succ
   976  	//
   977  	// to the following CFG:
   978  	//
   979  	//     pred --(newBranch)--> trampoline --(originalBranch)-> succ
   980  	//
   981  	// where trampoline is a new basic block which is created to split the critical edge.
   982  
   983  	trampoline := b.allocateBasicBlock()
   984  	if int(trampoline.id) >= len(b.dominators) {
   985  		b.dominators = append(b.dominators, make([]*basicBlock, trampoline.id+1)...)
   986  	}
   987  	b.dominators[trampoline.id] = pred
   988  
   989  	originalBranch := predInfo.branch
   990  
   991  	// Replace originalBranch with the newBranch.
   992  	newBranch := b.AllocateInstruction()
   993  	newBranch.opcode = originalBranch.opcode
   994  	newBranch.blk = trampoline
   995  	switch originalBranch.opcode {
   996  	case OpcodeJump:
   997  	case OpcodeBrz, OpcodeBrnz:
   998  		originalBranch.opcode = OpcodeJump // Trampoline consists of one unconditional branch.
   999  		newBranch.v = originalBranch.v
  1000  		originalBranch.v = ValueInvalid
  1001  	default:
  1002  		panic("BUG: critical edge shouldn't be originated from br_table")
  1003  	}
  1004  	swapInstruction(pred, originalBranch, newBranch)
  1005  
  1006  	// Replace the original branch with the new branch.
  1007  	trampoline.rootInstr = originalBranch
  1008  	trampoline.currentInstr = originalBranch
  1009  	trampoline.success = append(trampoline.success, succ) // Do not use []*basicBlock{pred} because we might have already allocated the slice.
  1010  	trampoline.preds = append(trampoline.preds,           // same as ^.
  1011  		basicBlockPredecessorInfo{blk: pred, branch: newBranch})
  1012  	b.Seal(trampoline)
  1013  
  1014  	// Update the original branch to point to the trampoline.
  1015  	predInfo.blk = trampoline
  1016  	predInfo.branch = originalBranch
  1017  
  1018  	if wazevoapi.SSAValidationEnabled {
  1019  		trampoline.validate(b)
  1020  	}
  1021  
  1022  	if len(trampoline.params) > 0 {
  1023  		panic("trampoline should not have params")
  1024  	}
  1025  
  1026  	// Assign the same order as the original block so that this will be placed before the actual destination.
  1027  	trampoline.reversePostOrder = pred.reversePostOrder
  1028  	return trampoline
  1029  }
  1030  
  1031  // swapInstruction replaces `old` in the block `blk` with `New`.
  1032  func swapInstruction(blk *basicBlock, old, New *Instruction) {
  1033  	if blk.rootInstr == old {
  1034  		blk.rootInstr = New
  1035  		next := old.next
  1036  		New.next = next
  1037  		next.prev = New
  1038  	} else {
  1039  		if blk.currentInstr == old {
  1040  			blk.currentInstr = New
  1041  		}
  1042  		prev := old.prev
  1043  		prev.next, New.prev = New, prev
  1044  		if next := old.next; next != nil {
  1045  			New.next, next.prev = next, New
  1046  		}
  1047  	}
  1048  	old.prev, old.next = nil, nil
  1049  }
  1050  
  1051  // InsertUndefined implements Builder.InsertUndefined.
  1052  func (b *builder) InsertUndefined() {
  1053  	instr := b.AllocateInstruction()
  1054  	instr.opcode = OpcodeUndefined
  1055  	b.InsertInstruction(instr)
  1056  }
  1057  
  1058  // LoopNestingForestRoots implements Builder.LoopNestingForestRoots.
  1059  func (b *builder) LoopNestingForestRoots() []BasicBlock {
  1060  	return b.loopNestingForestRoots
  1061  }
  1062  
  1063  // LowestCommonAncestor implements Builder.LowestCommonAncestor.
  1064  func (b *builder) LowestCommonAncestor(blk1, blk2 BasicBlock) BasicBlock {
  1065  	return b.sparseTree.findLCA(blk1.ID(), blk2.ID())
  1066  }