revise defer to use heap allocations when running a variable number of times
Этот коммит содержится в:
родитель
a4fa41b49d
коммит
eee1b995f6
3 изменённых файлов: 61 добавлений и 3 удалений
|
@ -14,6 +14,7 @@ package compiler
|
|||
// frames.
|
||||
|
||||
import (
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"github.com/tinygo-org/tinygo/ir"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
|
@ -34,6 +35,40 @@ func (c *Compiler) deferInitFunc(frame *Frame) {
|
|||
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
||||
}
|
||||
|
||||
// isInLoop checks if there is a path from a basic block to itself.
|
||||
func isInLoop(start *ssa.BasicBlock) bool {
|
||||
// Use a breadth-first search to scan backwards through the block graph.
|
||||
queue := []*ssa.BasicBlock{start}
|
||||
checked := map[*ssa.BasicBlock]struct{}{}
|
||||
|
||||
for len(queue) > 0 {
|
||||
// pop a block off of the queue
|
||||
block := queue[len(queue)-1]
|
||||
queue = queue[:len(queue)-1]
|
||||
|
||||
// Search through predecessors.
|
||||
// Searching backwards means that this is pretty fast when the block is close to the start of the function.
|
||||
// Defers are often placed near the start of the function.
|
||||
for _, pred := range block.Preds {
|
||||
if pred == start {
|
||||
// cycle found
|
||||
return true
|
||||
}
|
||||
|
||||
if _, ok := checked[pred]; ok {
|
||||
// block already checked
|
||||
continue
|
||||
}
|
||||
|
||||
// add to queue and checked map
|
||||
queue = append(queue, pred)
|
||||
checked[pred] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// emitDefer emits a single defer instruction, to be run when this function
|
||||
// returns.
|
||||
func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
||||
|
@ -127,12 +162,22 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
|||
deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "")
|
||||
}
|
||||
|
||||
// Put this struct in an alloca.
|
||||
alloca := c.builder.CreateAlloca(deferFrameType, "defer.alloca")
|
||||
c.builder.CreateStore(deferFrame, alloca)
|
||||
// Put this struct in an allocation.
|
||||
var alloca llvm.Value
|
||||
if !isInLoop(instr.Block()) {
|
||||
// This can safely use a stack allocation.
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(c.builder, deferFrameType, "defer.alloca")
|
||||
} else {
|
||||
// This may be hit a variable number of times, so use a heap allocation.
|
||||
size := c.targetData.TypeAllocSize(deferFrameType)
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
allocCall := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
|
||||
alloca = c.builder.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||
}
|
||||
if c.NeedsStackObjects() {
|
||||
c.trackPointer(alloca)
|
||||
}
|
||||
c.builder.CreateStore(deferFrame, alloca)
|
||||
|
||||
// Push it on top of the linked list by replacing deferPtr.
|
||||
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||
|
|
9
testdata/calls.go
предоставленный
9
testdata/calls.go
предоставленный
|
@ -41,6 +41,9 @@ func main() {
|
|||
// deferred functions
|
||||
testDefer()
|
||||
|
||||
// defers in loop
|
||||
testDeferLoop()
|
||||
|
||||
// Take a bound method and use it as a function pointer.
|
||||
// This function pointer needs a context pointer.
|
||||
testBound(thing.String)
|
||||
|
@ -85,6 +88,12 @@ func testDefer() {
|
|||
println("deferring...")
|
||||
}
|
||||
|
||||
func testDeferLoop() {
|
||||
for j := 0; j < 4; j++ {
|
||||
defer deferred("loop", j)
|
||||
}
|
||||
}
|
||||
|
||||
func deferred(msg string, i int) {
|
||||
println(msg, i)
|
||||
}
|
||||
|
|
4
testdata/calls.txt
предоставленный
4
testdata/calls.txt
предоставленный
|
@ -4,6 +4,10 @@ Thing.Print: foo arg: bar
|
|||
...run as defer 3
|
||||
...run closure deferred: 4
|
||||
...run as defer 1
|
||||
loop 3
|
||||
loop 2
|
||||
loop 1
|
||||
loop 0
|
||||
bound method: foo
|
||||
thing inside closure: foo
|
||||
inside fp closure: foo 3
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче