
This code is required by transformation passes which are being moved into a separate package, but is too complicated to simply copy. Therefore, I decided to move them into a new package.
172 строки
5,8 КиБ
Go
172 строки
5,8 КиБ
Go
package compiler
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
|
"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
|
|
}
|
|
|
|
// 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) {
|
|
return llvmutil.CreateTemporaryAlloca(c.builder, c.mod, t, name)
|
|
}
|
|
|
|
// 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) {
|
|
llvmutil.EmitLifetimeEnd(c.builder, c.mod, ptr, size)
|
|
}
|
|
|
|
// emitPointerPack packs the list of values into a single pointer value using
|
|
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
|
// pointer value directly. It returns the pointer with the packed data.
|
|
func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
|
return llvmutil.EmitPointerPack(c.builder, c.mod, c.Config, values)
|
|
}
|
|
|
|
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
|
func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
|
return llvmutil.EmitPointerUnpack(c.builder, c.mod, ptr, valueTypes)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
|
// contents, and returns the global.
|
|
// Note that it is left with the default linkage etc., you should set
|
|
// linkage/constant/etc properties yourself.
|
|
func (c *Compiler) makeGlobalArray(bufItf interface{}, name string, elementType llvm.Type) llvm.Value {
|
|
buf := reflect.ValueOf(bufItf)
|
|
globalType := llvm.ArrayType(elementType, buf.Len())
|
|
global := llvm.AddGlobal(c.mod, globalType, name)
|
|
value := llvm.Undef(globalType)
|
|
for i := 0; i < buf.Len(); i++ {
|
|
ch := buf.Index(i).Uint()
|
|
value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
|
|
}
|
|
global.SetInitializer(value)
|
|
return global
|
|
}
|
|
|
|
// getGlobalBytes returns the slice contained in the array of the provided
|
|
// global. It can recover the bytes originally created using makeGlobalArray, if
|
|
// makeGlobalArray was given a byte slice.
|
|
func getGlobalBytes(global llvm.Value) []byte {
|
|
value := global.Initializer()
|
|
buf := make([]byte, value.Type().ArrayLength())
|
|
for i := range buf {
|
|
buf[i] = byte(llvm.ConstExtractValue(value, []uint32{uint32(i)}).ZExtValue())
|
|
}
|
|
return buf
|
|
}
|
|
|
|
// replaceGlobalByteWithArray replaces a global integer type in the module with
|
|
// an integer array, using a GEP to make the types match. It is a convenience
|
|
// function used for creating reflection sidetables, for example.
|
|
func (c *Compiler) replaceGlobalIntWithArray(name string, buf interface{}) llvm.Value {
|
|
oldGlobal := c.mod.NamedGlobal(name)
|
|
global := c.makeGlobalArray(buf, name+".tmp", oldGlobal.Type().ElementType())
|
|
gep := llvm.ConstGEP(global, []llvm.Value{
|
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
})
|
|
oldGlobal.ReplaceAllUsesWith(gep)
|
|
oldGlobal.EraseFromParentAsGlobal()
|
|
global.SetName(name)
|
|
return global
|
|
}
|