
Before this commit, goroutine support was spread through the compiler. This commit changes this support, so that the compiler itself only generates simple intrinsics and leaves the real support to a compiler pass that runs as one of the TinyGo-specific optimization passes. The biggest change, that was done together with the rewrite, was support for goroutines in WebAssembly for JavaScript. The challenge in JavaScript is that in general no blocking operations are allowed, which means that programs that call time.Sleep() but do not start goroutines also have to be scheduled by the scheduler.
95 строки
2,5 КиБ
Go
95 строки
2,5 КиБ
Go
package compiler
|
|
|
|
import (
|
|
"github.com/aykevl/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
|
|
}
|
|
|
|
// 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
|
|
}
|