You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
5.0 KiB

package compiler
import (
"tinygo.org/x/go-llvm"
)
// This file contains helper functions for LLVM that are not exposed in the Go
// bindings.
// Return a list of values (actually, instructions) where this value is used as
// an operand.
func getUses(value llvm.Value) []llvm.Value {
if value.IsNil() {
return nil
}
var uses []llvm.Value
use := value.FirstUse()
for !use.IsNil() {
uses = append(uses, use.User())
use = use.NextUse()
}
return uses
}
// createEntryBlockAlloca creates a new alloca in the entry block, even though
// the IR builder is located elsewhere. It assumes that the insert point is
// at the end of the current block.
func (c *Compiler) createEntryBlockAlloca(t llvm.Type, name string) llvm.Value {
currentBlock := c.builder.GetInsertBlock()
entryBlock := currentBlock.Parent().EntryBasicBlock()
if entryBlock.FirstInstruction().IsNil() {
c.builder.SetInsertPointAtEnd(entryBlock)
} else {
c.builder.SetInsertPointBefore(entryBlock.FirstInstruction())
}
alloca := c.builder.CreateAlloca(t, name)
c.builder.SetInsertPointAtEnd(currentBlock)
return alloca
}
// createTemporaryAlloca creates a new alloca in the entry block and adds
// lifetime start infromation in the IR signalling that the alloca won't be used
// before this point.
//
// This is useful for creating temporary allocas for intrinsics. Don't forget to
// end the lifetime using emitLifetimeEnd after you're done with it.
func (c *Compiler) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
alloca = c.createEntryBlockAlloca(t, name)
bitcast = c.builder.CreateBitCast(alloca, c.i8ptrType, name+".bitcast")
size = llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(t), false)
c.builder.CreateCall(c.getLifetimeStartFunc(), []llvm.Value{size, bitcast}, "")
return
}
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
// llvm.lifetime.end intrinsic. It is commonly used together with
// createTemporaryAlloca.
func (c *Compiler) emitLifetimeEnd(ptr, size llvm.Value) {
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{size, ptr}, "")
}
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
// first if it doesn't exist yet.
func (c *Compiler) getLifetimeStartFunc() llvm.Value {
fn := c.mod.NamedFunction("llvm.lifetime.start.p0i8")
if fn.IsNil() {
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.ctx.Int64Type(), c.i8ptrType}, false)
fn = llvm.AddFunction(c.mod, "llvm.lifetime.start.p0i8", fnType)
}
return fn
}
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
// first if it doesn't exist yet.
func (c *Compiler) getLifetimeEndFunc() llvm.Value {
fn := c.mod.NamedFunction("llvm.lifetime.end.p0i8")
if fn.IsNil() {
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.ctx.Int64Type(), c.i8ptrType}, false)
fn = llvm.AddFunction(c.mod, "llvm.lifetime.end.p0i8", fnType)
}
return fn
}
// splitBasicBlock splits a LLVM basic block into two parts. All instructions
// after afterInst are moved into a new basic block (created right after the
// current one) with the given name.
func (c *Compiler) splitBasicBlock(afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock {
oldBlock := afterInst.InstructionParent()
newBlock := c.ctx.InsertBasicBlock(insertAfter, name)
var nextInstructions []llvm.Value // values to move
// Collect to-be-moved instructions.
inst := afterInst
for {
inst = llvm.NextInstruction(inst)
if inst.IsNil() {
break
}
nextInstructions = append(nextInstructions, inst)
}
// Move instructions.
c.builder.SetInsertPointAtEnd(newBlock)
for _, inst := range nextInstructions {
inst.RemoveFromParentAsInstruction()
c.builder.Insert(inst)
}
// Find PHI nodes to update.
var phiNodes []llvm.Value // PHI nodes to update
for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if inst.IsAPHINode().IsNil() {
continue
}
needsUpdate := false
incomingCount := inst.IncomingCount()
for i := 0; i < incomingCount; i++ {
if inst.IncomingBlock(i) == oldBlock {
needsUpdate = true
break
}
}
if !needsUpdate {
// PHI node has no incoming edge from the old block.
continue
}
phiNodes = append(phiNodes, inst)
}
}
// Update PHI nodes.
for _, phi := range phiNodes {
c.builder.SetInsertPointBefore(phi)
newPhi := c.builder.CreatePHI(phi.Type(), "")
incomingCount := phi.IncomingCount()
incomingVals := make([]llvm.Value, incomingCount)
incomingBlocks := make([]llvm.BasicBlock, incomingCount)
for i := 0; i < incomingCount; i++ {
value := phi.IncomingValue(i)
block := phi.IncomingBlock(i)
if block == oldBlock {
block = newBlock
}
incomingVals[i] = value
incomingBlocks[i] = block
}
newPhi.AddIncoming(incomingVals, incomingBlocks)
phi.ReplaceAllUsesWith(newPhi)
phi.EraseFromParentAsInstruction()
}
return newBlock
}