
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.
96 строки
4,1 КиБ
Go
96 строки
4,1 КиБ
Go
// Package llvmutil contains utility functions used across multiple compiler
|
|
// packages. For example, they may be used by both the compiler pacakge and
|
|
// transformation packages.
|
|
//
|
|
// Normally, utility packages are avoided. However, in this case, the utility
|
|
// functions are non-trivial and hard to get right. Copying them to multiple
|
|
// places would be a big risk if only one of them is updated.
|
|
package llvmutil
|
|
|
|
import "tinygo.org/x/go-llvm"
|
|
|
|
// 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 CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm.Value {
|
|
currentBlock := builder.GetInsertBlock()
|
|
entryBlock := currentBlock.Parent().EntryBasicBlock()
|
|
if entryBlock.FirstInstruction().IsNil() {
|
|
builder.SetInsertPointAtEnd(entryBlock)
|
|
} else {
|
|
builder.SetInsertPointBefore(entryBlock.FirstInstruction())
|
|
}
|
|
alloca := builder.CreateAlloca(t, name)
|
|
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 CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
|
ctx := t.Context()
|
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
|
alloca = CreateEntryBlockAlloca(builder, t, name)
|
|
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
|
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
|
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
|
|
return
|
|
}
|
|
|
|
// CreateInstructionAlloca creates an alloca in the entry block, and places lifetime control intrinsics around the instruction
|
|
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
|
ctx := mod.Context()
|
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
|
|
|
alloca := CreateEntryBlockAlloca(builder, t, name)
|
|
builder.SetInsertPointBefore(inst)
|
|
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
|
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
|
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
|
|
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
|
builder.SetInsertPointBefore(next)
|
|
} else {
|
|
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
|
}
|
|
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, bitcast}, "")
|
|
return alloca
|
|
}
|
|
|
|
// EmitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
|
// llvm.lifetime.end intrinsic. It is commonly used together with
|
|
// createTemporaryAlloca.
|
|
func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) {
|
|
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, ptr}, "")
|
|
}
|
|
|
|
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
|
// first if it doesn't exist yet.
|
|
func getLifetimeStartFunc(mod llvm.Module) llvm.Value {
|
|
fn := mod.NamedFunction("llvm.lifetime.start.p0i8")
|
|
ctx := mod.Context()
|
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
|
if fn.IsNil() {
|
|
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
|
fn = llvm.AddFunction(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 getLifetimeEndFunc(mod llvm.Module) llvm.Value {
|
|
fn := mod.NamedFunction("llvm.lifetime.end.p0i8")
|
|
ctx := mod.Context()
|
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
|
if fn.IsNil() {
|
|
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
|
fn = llvm.AddFunction(mod, "llvm.lifetime.end.p0i8", fnType)
|
|
}
|
|
return fn
|
|
}
|