compiler: refactor creating of channel operations

Этот коммит содержится в:
Ayke van Laethem 2019-11-23 00:27:18 +01:00 коммит произвёл Ron Evans
родитель b8d20535ba
коммит 6dafb6c65e
3 изменённых файлов: 113 добавлений и 96 удалений

Просмотреть файл

@ -11,79 +11,79 @@ import (
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
func (c *Compiler) emitMakeChan(frame *Frame, expr *ssa.MakeChan) llvm.Value { func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
elementSize := c.targetData.TypeAllocSize(c.getLLVMType(expr.Type().(*types.Chan).Elem())) elementSize := b.targetData.TypeAllocSize(b.getLLVMType(expr.Type().(*types.Chan).Elem()))
elementSizeValue := llvm.ConstInt(c.uintptrType, elementSize, false) elementSizeValue := llvm.ConstInt(b.uintptrType, elementSize, false)
bufSize := frame.getValue(expr.Size) bufSize := b.getValue(expr.Size)
frame.createChanBoundsCheck(elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos()) b.createChanBoundsCheck(elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos())
if bufSize.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() { if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
bufSize = c.builder.CreateZExt(bufSize, c.uintptrType, "") bufSize = b.CreateZExt(bufSize, b.uintptrType, "")
} else if bufSize.Type().IntTypeWidth() > c.uintptrType.IntTypeWidth() { } else if bufSize.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
bufSize = c.builder.CreateTrunc(bufSize, c.uintptrType, "") bufSize = b.CreateTrunc(bufSize, b.uintptrType, "")
} }
return c.createRuntimeCall("chanMake", []llvm.Value{elementSizeValue, bufSize}, "") return b.createRuntimeCall("chanMake", []llvm.Value{elementSizeValue, bufSize}, "")
} }
// emitChanSend emits a pseudo chan send operation. It is lowered to the actual // createChanSend emits a pseudo chan send operation. It is lowered to the
// channel send operation during goroutine lowering. // actual channel send operation during goroutine lowering.
func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) { func (b *builder) createChanSend(instr *ssa.Send) {
ch := frame.getValue(instr.Chan) ch := b.getValue(instr.Chan)
chanValue := frame.getValue(instr.X) chanValue := b.getValue(instr.X)
// store value-to-send // store value-to-send
valueType := c.getLLVMType(instr.X.Type()) valueType := b.getLLVMType(instr.X.Type())
valueAlloca, valueAllocaCast, valueAllocaSize := c.createTemporaryAlloca(valueType, "chan.value") valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
c.builder.CreateStore(chanValue, valueAlloca) b.CreateStore(chanValue, valueAlloca)
// Do the send. // Do the send.
c.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "") b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "")
// End the lifetime of the alloca. // End the lifetime of the alloca.
// This also works around a bug in CoroSplit, at least in LLVM 8: // This also works around a bug in CoroSplit, at least in LLVM 8:
// https://bugs.llvm.org/show_bug.cgi?id=41742 // https://bugs.llvm.org/show_bug.cgi?id=41742
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
} }
// emitChanRecv emits a pseudo chan receive operation. It is lowered to the // createChanRecv emits a pseudo chan receive operation. It is lowered to the
// actual channel receive operation during goroutine lowering. // actual channel receive operation during goroutine lowering.
func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value { func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
valueType := c.getLLVMType(unop.X.Type().(*types.Chan).Elem()) valueType := b.getLLVMType(unop.X.Type().(*types.Chan).Elem())
ch := frame.getValue(unop.X) ch := b.getValue(unop.X)
// Allocate memory to receive into. // Allocate memory to receive into.
valueAlloca, valueAllocaCast, valueAllocaSize := c.createTemporaryAlloca(valueType, "chan.value") valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
// Do the receive. // Do the receive.
commaOk := c.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "") commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "")
received := c.builder.CreateLoad(valueAlloca, "chan.received") received := b.CreateLoad(valueAlloca, "chan.received")
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
if unop.CommaOk { if unop.CommaOk {
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{valueType, c.ctx.Int1Type()}, false)) tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
tuple = c.builder.CreateInsertValue(tuple, received, 0, "") tuple = b.CreateInsertValue(tuple, received, 0, "")
tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") tuple = b.CreateInsertValue(tuple, commaOk, 1, "")
return tuple return tuple
} else { } else {
return received return received
} }
} }
// emitChanClose closes the given channel. // createChanClose closes the given channel.
func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) { func (b *builder) createChanClose(param ssa.Value) {
ch := frame.getValue(param) ch := b.getValue(param)
c.createRuntimeCall("chanClose", []llvm.Value{ch}, "") b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
} }
// emitSelect emits all IR necessary for a select statements. That's a // createSelect emits all IR necessary for a select statements. That's a
// non-trivial amount of code because select is very complex to implement. // non-trivial amount of code because select is very complex to implement.
func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value { func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
if len(expr.States) == 0 { if len(expr.States) == 0 {
// Shortcuts for some simple selects. // Shortcuts for some simple selects.
llvmType := c.getLLVMType(expr.Type()) llvmType := b.getLLVMType(expr.Type())
if expr.Blocking { if expr.Blocking {
// Blocks forever: // Blocks forever:
// select {} // select {}
c.createRuntimeCall("deadlock", nil, "") b.createRuntimeCall("deadlock", nil, "")
return llvm.Undef(llvmType) return llvm.Undef(llvmType)
} else { } else {
// No-op: // No-op:
@ -91,7 +91,7 @@ func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value {
// default: // default:
// } // }
retval := llvm.Undef(llvmType) retval := llvm.Undef(llvmType)
retval = c.builder.CreateInsertValue(retval, llvm.ConstInt(c.intType, 0xffffffffffffffff, true), 0, "") retval = b.CreateInsertValue(retval, llvm.ConstInt(b.intType, 0xffffffffffffffff, true), 0, "")
return retval // {-1, false} return retval // {-1, false}
} }
} }
@ -109,30 +109,30 @@ func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value {
recvbufAlign := 0 recvbufAlign := 0
hasReceives := false hasReceives := false
var selectStates []llvm.Value var selectStates []llvm.Value
chanSelectStateType := c.getLLVMRuntimeType("chanSelectState") chanSelectStateType := b.getLLVMRuntimeType("chanSelectState")
for _, state := range expr.States { for _, state := range expr.States {
ch := frame.getValue(state.Chan) ch := b.getValue(state.Chan)
selectState := llvm.ConstNull(chanSelectStateType) selectState := llvm.ConstNull(chanSelectStateType)
selectState = c.builder.CreateInsertValue(selectState, ch, 0, "") selectState = b.CreateInsertValue(selectState, ch, 0, "")
switch state.Dir { switch state.Dir {
case types.RecvOnly: case types.RecvOnly:
// Make sure the receive buffer is big enough and has the correct alignment. // Make sure the receive buffer is big enough and has the correct alignment.
llvmType := c.getLLVMType(state.Chan.Type().(*types.Chan).Elem()) llvmType := b.getLLVMType(state.Chan.Type().(*types.Chan).Elem())
if size := c.targetData.TypeAllocSize(llvmType); size > recvbufSize { if size := b.targetData.TypeAllocSize(llvmType); size > recvbufSize {
recvbufSize = size recvbufSize = size
} }
if align := c.targetData.ABITypeAlignment(llvmType); align > recvbufAlign { if align := b.targetData.ABITypeAlignment(llvmType); align > recvbufAlign {
recvbufAlign = align recvbufAlign = align
} }
hasReceives = true hasReceives = true
case types.SendOnly: case types.SendOnly:
// Store this value in an alloca and put a pointer to this alloca // Store this value in an alloca and put a pointer to this alloca
// in the send state. // in the send state.
sendValue := frame.getValue(state.Send) sendValue := b.getValue(state.Send)
alloca := llvmutil.CreateEntryBlockAlloca(c.builder, sendValue.Type(), "select.send.value") alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
c.builder.CreateStore(sendValue, alloca) b.CreateStore(sendValue, alloca)
ptr := c.builder.CreateBitCast(alloca, c.i8ptrType, "") ptr := b.CreateBitCast(alloca, b.i8ptrType, "")
selectState = c.builder.CreateInsertValue(selectState, ptr, 1, "") selectState = b.CreateInsertValue(selectState, ptr, 1, "")
default: default:
panic("unreachable") panic("unreachable")
} }
@ -140,74 +140,74 @@ func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value {
} }
// Create a receive buffer, where the received value will be stored. // Create a receive buffer, where the received value will be stored.
recvbuf := llvm.Undef(c.i8ptrType) recvbuf := llvm.Undef(b.i8ptrType)
if hasReceives { if hasReceives {
allocaType := llvm.ArrayType(c.ctx.Int8Type(), int(recvbufSize)) allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
recvbufAlloca, _, _ := c.createTemporaryAlloca(allocaType, "select.recvbuf.alloca") recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
recvbufAlloca.SetAlignment(recvbufAlign) recvbufAlloca.SetAlignment(recvbufAlign)
recvbuf = c.builder.CreateGEP(recvbufAlloca, []llvm.Value{ recvbuf = b.CreateGEP(recvbufAlloca, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
}, "select.recvbuf") }, "select.recvbuf")
} }
// Create the states slice (allocated on the stack). // Create the states slice (allocated on the stack).
statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates)) statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates))
statesAlloca, statesI8, statesSize := c.createTemporaryAlloca(statesAllocaType, "select.states.alloca") statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
for i, state := range selectStates { for i, state := range selectStates {
// Set each slice element to the appropriate channel. // Set each slice element to the appropriate channel.
gep := c.builder.CreateGEP(statesAlloca, []llvm.Value{ gep := b.CreateGEP(statesAlloca, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false), llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}, "") }, "")
c.builder.CreateStore(state, gep) b.CreateStore(state, gep)
} }
statesPtr := c.builder.CreateGEP(statesAlloca, []llvm.Value{ statesPtr := b.CreateGEP(statesAlloca, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
}, "select.states") }, "select.states")
statesLen := llvm.ConstInt(c.uintptrType, uint64(len(selectStates)), false) statesLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
// Do the select in the runtime. // Do the select in the runtime.
var results llvm.Value var results llvm.Value
if expr.Blocking { if expr.Blocking {
// Stack-allocate operation structures. // Stack-allocate operation structures.
// If these were simply created as a slice, they would heap-allocate. // If these were simply created as a slice, they would heap-allocate.
chBlockAllocaType := llvm.ArrayType(c.getLLVMRuntimeType("channelBlockedList"), len(selectStates)) chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
chBlockAlloca, chBlockAllocaPtr, chBlockSize := c.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca") chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
chBlockLen := llvm.ConstInt(c.uintptrType, uint64(len(selectStates)), false) chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
chBlockPtr := c.builder.CreateGEP(chBlockAlloca, []llvm.Value{ chBlockPtr := b.CreateGEP(chBlockAlloca, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
}, "select.block") }, "select.block")
results = c.createRuntimeCall("chanSelect", []llvm.Value{ results = b.createRuntimeCall("chanSelect", []llvm.Value{
recvbuf, recvbuf,
statesPtr, statesLen, statesLen, // []chanSelectState statesPtr, statesLen, statesLen, // []chanSelectState
chBlockPtr, chBlockLen, chBlockLen, // []channelBlockList chBlockPtr, chBlockLen, chBlockLen, // []channelBlockList
}, "select.result") }, "select.result")
// Terminate the lifetime of the operation structures. // Terminate the lifetime of the operation structures.
c.emitLifetimeEnd(chBlockAllocaPtr, chBlockSize) b.emitLifetimeEnd(chBlockAllocaPtr, chBlockSize)
} else { } else {
results = c.createRuntimeCall("tryChanSelect", []llvm.Value{ results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
recvbuf, recvbuf,
statesPtr, statesLen, statesLen, // []chanSelectState statesPtr, statesLen, statesLen, // []chanSelectState
}, "select.result") }, "select.result")
} }
// Terminate the lifetime of the states alloca. // Terminate the lifetime of the states alloca.
c.emitLifetimeEnd(statesI8, statesSize) b.emitLifetimeEnd(statesI8, statesSize)
// The result value does not include all the possible received values, // The result value does not include all the possible received values,
// because we can't load them in advance. Instead, the *ssa.Extract // because we can't load them in advance. Instead, the *ssa.Extract
// instruction will treat a *ssa.Select specially and load it there inline. // instruction will treat a *ssa.Select specially and load it there inline.
// Store the receive alloca in a sidetable until we hit this extract // Store the receive alloca in a sidetable until we hit this extract
// instruction. // instruction.
if frame.selectRecvBuf == nil { if b.selectRecvBuf == nil {
frame.selectRecvBuf = make(map[*ssa.Select]llvm.Value) b.selectRecvBuf = make(map[*ssa.Select]llvm.Value)
} }
frame.selectRecvBuf[expr] = recvbuf b.selectRecvBuf[expr] = recvbuf
return results return results
} }
@ -216,28 +216,28 @@ func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value {
// when extracting a value from a select statement (*ssa.Select). Because // when extracting a value from a select statement (*ssa.Select). Because
// *ssa.Select cannot load all values in advance, it does this later in the // *ssa.Select cannot load all values in advance, it does this later in the
// *ssa.Extract expression. // *ssa.Extract expression.
func (c *Compiler) getChanSelectResult(frame *Frame, expr *ssa.Extract) llvm.Value { func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
if expr.Index == 0 { if expr.Index == 0 {
// index // index
value := frame.getValue(expr.Tuple) value := b.getValue(expr.Tuple)
index := c.builder.CreateExtractValue(value, expr.Index, "") index := b.CreateExtractValue(value, expr.Index, "")
if index.Type().IntTypeWidth() < c.intType.IntTypeWidth() { if index.Type().IntTypeWidth() < b.intType.IntTypeWidth() {
index = c.builder.CreateSExt(index, c.intType, "") index = b.CreateSExt(index, b.intType, "")
} }
return index return index
} else if expr.Index == 1 { } else if expr.Index == 1 {
// comma-ok // comma-ok
value := frame.getValue(expr.Tuple) value := b.getValue(expr.Tuple)
return c.builder.CreateExtractValue(value, expr.Index, "") return b.CreateExtractValue(value, expr.Index, "")
} else { } else {
// Select statements are (index, ok, ...) where ... is a number of // Select statements are (index, ok, ...) where ... is a number of
// received values, depending on how many receive statements there // received values, depending on how many receive statements there
// are. They are all combined into one alloca (because only one // are. They are all combined into one alloca (because only one
// receive can proceed at a time) so we'll get that alloca, bitcast // receive can proceed at a time) so we'll get that alloca, bitcast
// it to the correct type, and dereference it. // it to the correct type, and dereference it.
recvbuf := frame.selectRecvBuf[expr.Tuple.(*ssa.Select)] recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
typ := llvm.PointerType(c.getLLVMType(expr.Type()), 0) typ := llvm.PointerType(b.getLLVMType(expr.Type()), 0)
ptr := c.builder.CreateBitCast(recvbuf, typ, "") ptr := b.CreateBitCast(recvbuf, typ, "")
return c.builder.CreateLoad(ptr, "") return b.CreateLoad(ptr, "")
} }
} }

Просмотреть файл

@ -1131,7 +1131,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
case *ssa.RunDefers: case *ssa.RunDefers:
c.emitRunDefers(frame) c.emitRunDefers(frame)
case *ssa.Send: case *ssa.Send:
c.emitChanSend(frame, instr) frame.createChanSend(instr)
case *ssa.Store: case *ssa.Store:
llvmAddr := frame.getValue(instr.Addr) llvmAddr := frame.getValue(instr.Addr)
llvmVal := frame.getValue(instr.Val) llvmVal := frame.getValue(instr.Val)
@ -1188,7 +1188,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string,
} }
return llvmCap, nil return llvmCap, nil
case "close": case "close":
c.emitChanClose(frame, args[0]) frame.createChanClose(args[0])
return llvm.Value{}, nil return llvm.Value{}, nil
case "complex": case "complex":
r := frame.getValue(args[0]) r := frame.getValue(args[0])
@ -1510,7 +1510,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
return c.parseConvert(expr.X.Type(), expr.Type(), x, expr.Pos()) return c.parseConvert(expr.X.Type(), expr.Type(), x, expr.Pos())
case *ssa.Extract: case *ssa.Extract:
if _, ok := expr.Tuple.(*ssa.Select); ok { if _, ok := expr.Tuple.(*ssa.Select); ok {
return c.getChanSelectResult(frame, expr), nil return frame.getChanSelectResult(expr), nil
} }
value := frame.getValue(expr.Tuple) value := frame.getValue(expr.Tuple)
return c.builder.CreateExtractValue(value, expr.Index, ""), nil return c.builder.CreateExtractValue(value, expr.Index, ""), nil
@ -1626,7 +1626,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
panic("unknown lookup type: " + expr.String()) panic("unknown lookup type: " + expr.String())
} }
case *ssa.MakeChan: case *ssa.MakeChan:
return c.emitMakeChan(frame, expr), nil return frame.createMakeChan(expr), nil
case *ssa.MakeClosure: case *ssa.MakeClosure:
return c.parseMakeClosure(frame, expr) return c.parseMakeClosure(frame, expr)
case *ssa.MakeInterface: case *ssa.MakeInterface:
@ -1726,7 +1726,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
c.builder.CreateStore(llvm.ConstNull(iteratorType), it) c.builder.CreateStore(llvm.ConstNull(iteratorType), it)
return it, nil return it, nil
case *ssa.Select: case *ssa.Select:
return c.emitSelect(frame, expr), nil return frame.createSelect(expr), nil
case *ssa.Slice: case *ssa.Slice:
value := frame.getValue(expr.X) value := frame.getValue(expr.X)
@ -2566,7 +2566,7 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
case token.XOR: // ^x, toggle all bits in integer case token.XOR: // ^x, toggle all bits in integer
return c.builder.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil return c.builder.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil
case token.ARROW: // <-x, receive from channel case token.ARROW: // <-x, receive from channel
return c.emitChanRecv(frame, unop), nil return frame.createChanRecv(unop), nil
default: default:
return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown unop") return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown unop")
} }

Просмотреть файл

@ -33,6 +33,16 @@ func (c *Compiler) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitc
return llvmutil.CreateTemporaryAlloca(c.builder, c.mod, t, name) return llvmutil.CreateTemporaryAlloca(c.builder, c.mod, t, name)
} }
// 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 (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
}
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the // emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
// llvm.lifetime.end intrinsic. It is commonly used together with // llvm.lifetime.end intrinsic. It is commonly used together with
// createTemporaryAlloca. // createTemporaryAlloca.
@ -40,6 +50,13 @@ func (c *Compiler) emitLifetimeEnd(ptr, size llvm.Value) {
llvmutil.EmitLifetimeEnd(c.builder, c.mod, ptr, size) llvmutil.EmitLifetimeEnd(c.builder, c.mod, ptr, size)
} }
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
// llvm.lifetime.end intrinsic. It is commonly used together with
// createTemporaryAlloca.
func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
llvmutil.EmitLifetimeEnd(b.Builder, b.mod, ptr, size)
}
// emitPointerPack packs the list of values into a single pointer value using // 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 // 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. // pointer value directly. It returns the pointer with the packed data.