github.com/aykevl/tinygo@v0.5.0/compiler/llvm.go (about)

     1  package compiler
     2  
     3  import (
     4  	"tinygo.org/x/go-llvm"
     5  )
     6  
     7  // This file contains helper functions for LLVM that are not exposed in the Go
     8  // bindings.
     9  
    10  // Return a list of values (actually, instructions) where this value is used as
    11  // an operand.
    12  func getUses(value llvm.Value) []llvm.Value {
    13  	if value.IsNil() {
    14  		return nil
    15  	}
    16  	var uses []llvm.Value
    17  	use := value.FirstUse()
    18  	for !use.IsNil() {
    19  		uses = append(uses, use.User())
    20  		use = use.NextUse()
    21  	}
    22  	return uses
    23  }
    24  
    25  // splitBasicBlock splits a LLVM basic block into two parts. All instructions
    26  // after afterInst are moved into a new basic block (created right after the
    27  // current one) with the given name.
    28  func (c *Compiler) splitBasicBlock(afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock {
    29  	oldBlock := afterInst.InstructionParent()
    30  	newBlock := c.ctx.InsertBasicBlock(insertAfter, name)
    31  	var nextInstructions []llvm.Value // values to move
    32  
    33  	// Collect to-be-moved instructions.
    34  	inst := afterInst
    35  	for {
    36  		inst = llvm.NextInstruction(inst)
    37  		if inst.IsNil() {
    38  			break
    39  		}
    40  		nextInstructions = append(nextInstructions, inst)
    41  	}
    42  
    43  	// Move instructions.
    44  	c.builder.SetInsertPointAtEnd(newBlock)
    45  	for _, inst := range nextInstructions {
    46  		inst.RemoveFromParentAsInstruction()
    47  		c.builder.Insert(inst)
    48  	}
    49  
    50  	// Find PHI nodes to update.
    51  	var phiNodes []llvm.Value // PHI nodes to update
    52  	for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
    53  		for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
    54  			if inst.IsAPHINode().IsNil() {
    55  				continue
    56  			}
    57  			needsUpdate := false
    58  			incomingCount := inst.IncomingCount()
    59  			for i := 0; i < incomingCount; i++ {
    60  				if inst.IncomingBlock(i) == oldBlock {
    61  					needsUpdate = true
    62  					break
    63  				}
    64  			}
    65  			if !needsUpdate {
    66  				// PHI node has no incoming edge from the old block.
    67  				continue
    68  			}
    69  			phiNodes = append(phiNodes, inst)
    70  		}
    71  	}
    72  
    73  	// Update PHI nodes.
    74  	for _, phi := range phiNodes {
    75  		c.builder.SetInsertPointBefore(phi)
    76  		newPhi := c.builder.CreatePHI(phi.Type(), "")
    77  		incomingCount := phi.IncomingCount()
    78  		incomingVals := make([]llvm.Value, incomingCount)
    79  		incomingBlocks := make([]llvm.BasicBlock, incomingCount)
    80  		for i := 0; i < incomingCount; i++ {
    81  			value := phi.IncomingValue(i)
    82  			block := phi.IncomingBlock(i)
    83  			if block == oldBlock {
    84  				block = newBlock
    85  			}
    86  			incomingVals[i] = value
    87  			incomingBlocks[i] = block
    88  		}
    89  		newPhi.AddIncoming(incomingVals, incomingBlocks)
    90  		phi.ReplaceAllUsesWith(newPhi)
    91  		phi.EraseFromParentAsInstruction()
    92  	}
    93  
    94  	return newBlock
    95  }