compiler: fix invalid incoming block in complex typeassert flow

A single *ssa.BasicBlock may be split in multiple LLVM basic blocks due
to typeassert instructions. This means the incoming block and outgoing
block are different. PHI nodes need to get the result from the outgoing
block, which was fixed before, but incoming branches need to branch to
the incoming block, not the outgoing block.

Branching to the outgoing block led to a LLVM verification error when
compiling the fmt package.

Originally found in (*fmt.pp).handleMethods.
Этот коммит содержится в:
Ayke van Laethem 2018-10-23 14:59:44 +02:00
родитель 96f74ec153
коммит 6c6a43310a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
3 изменённых файлов: 32 добавлений и 15 удалений

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

@ -68,8 +68,9 @@ type Compiler struct {
type Frame struct { type Frame struct {
fn *ir.Function fn *ir.Function
locals map[ssa.Value]llvm.Value // local variables locals map[ssa.Value]llvm.Value // local variables
blocks map[*ssa.BasicBlock]llvm.BasicBlock blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up
blockExits map[*ssa.BasicBlock]llvm.BasicBlock // these are the exit blocks
currentBlock *ssa.BasicBlock currentBlock *ssa.BasicBlock
phis []Phi phis []Phi
blocking bool blocking bool
@ -917,10 +918,11 @@ func (c *Compiler) wrapInterfaceInvoke(f *ir.Function) (llvm.Value, error) {
func (c *Compiler) parseFuncDecl(f *ir.Function) (*Frame, error) { func (c *Compiler) parseFuncDecl(f *ir.Function) (*Frame, error) {
frame := &Frame{ frame := &Frame{
fn: f, fn: f,
locals: make(map[ssa.Value]llvm.Value), locals: make(map[ssa.Value]llvm.Value),
blocks: make(map[*ssa.BasicBlock]llvm.BasicBlock), blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
blocking: c.ir.IsBlocking(f), blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
blocking: c.ir.IsBlocking(f),
} }
var retType llvm.Type var retType llvm.Type
@ -1346,13 +1348,14 @@ func (c *Compiler) parseFunc(frame *Frame) error {
// Pre-create all basic blocks in the function. // Pre-create all basic blocks in the function.
for _, block := range frame.fn.DomPreorder() { for _, block := range frame.fn.DomPreorder() {
llvmBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, block.Comment) llvmBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, block.Comment)
frame.blocks[block] = llvmBlock frame.blockEntries[block] = llvmBlock
frame.blockExits[block] = llvmBlock
} }
if frame.blocking { if frame.blocking {
frame.cleanupBlock = c.ctx.AddBasicBlock(frame.fn.LLVMFn, "task.cleanup") frame.cleanupBlock = c.ctx.AddBasicBlock(frame.fn.LLVMFn, "task.cleanup")
frame.suspendBlock = c.ctx.AddBasicBlock(frame.fn.LLVMFn, "task.suspend") frame.suspendBlock = c.ctx.AddBasicBlock(frame.fn.LLVMFn, "task.suspend")
} }
entryBlock := frame.blocks[frame.fn.Blocks[0]] entryBlock := frame.blockEntries[frame.fn.Blocks[0]]
c.builder.SetInsertPointAtEnd(entryBlock) c.builder.SetInsertPointAtEnd(entryBlock)
// Load function parameters // Load function parameters
@ -1479,9 +1482,9 @@ func (c *Compiler) parseFunc(frame *Frame) error {
// Fill blocks with instructions. // Fill blocks with instructions.
for _, block := range frame.fn.DomPreorder() { for _, block := range frame.fn.DomPreorder() {
if c.DumpSSA { if c.DumpSSA {
fmt.Printf("%s:\n", block.Comment) fmt.Printf("%d: %s:\n", block.Index, block.Comment)
} }
c.builder.SetInsertPointAtEnd(frame.blocks[block]) c.builder.SetInsertPointAtEnd(frame.blockEntries[block])
frame.currentBlock = block frame.currentBlock = block
for _, instr := range block.Instrs { for _, instr := range block.Instrs {
if _, ok := instr.(*ssa.DebugRef); ok { if _, ok := instr.(*ssa.DebugRef); ok {
@ -1512,7 +1515,7 @@ func (c *Compiler) parseFunc(frame *Frame) error {
if err != nil { if err != nil {
return err return err
} }
llvmBlock := frame.blocks[block.Preds[i]] llvmBlock := frame.blockExits[block.Preds[i]]
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock}) phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
} }
} }
@ -1651,12 +1654,12 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
return err return err
} }
block := instr.Block() block := instr.Block()
blockThen := frame.blocks[block.Succs[0]] blockThen := frame.blockEntries[block.Succs[0]]
blockElse := frame.blocks[block.Succs[1]] blockElse := frame.blockEntries[block.Succs[1]]
c.builder.CreateCondBr(cond, blockThen, blockElse) c.builder.CreateCondBr(cond, blockThen, blockElse)
return nil return nil
case *ssa.Jump: case *ssa.Jump:
blockJump := frame.blocks[instr.Block().Succs[0]] blockJump := frame.blockEntries[instr.Block().Succs[0]]
c.builder.CreateBr(blockJump) c.builder.CreateBr(blockJump)
return nil return nil
case *ssa.MapUpdate: case *ssa.MapUpdate:
@ -2793,7 +2796,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
prevBlock := c.builder.GetInsertBlock() prevBlock := c.builder.GetInsertBlock()
okBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.ok") okBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.ok")
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.next") nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.next")
frame.blocks[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
c.builder.CreateCondBr(commaOk, okBlock, nextBlock) c.builder.CreateCondBr(commaOk, okBlock, nextBlock)
// Retrieve the value from the interface if the type assert was // Retrieve the value from the interface if the type assert was

13
testdata/interface.go предоставленный
Просмотреть файл

@ -20,6 +20,8 @@ func main() {
println("Stringer.String():", s.String()) println("Stringer.String():", s.String())
var itf interface{} = s var itf interface{} = s
println("Stringer.(*Thing).String():", itf.(Stringer).String()) println("Stringer.(*Thing).String():", itf.(Stringer).String())
println("nested switch:", nestedSwitch('v', 3))
} }
func printItf(val interface{}) { func printItf(val interface{}) {
@ -46,6 +48,17 @@ func printItf(val interface{}) {
} }
} }
func nestedSwitch(verb rune, arg interface{}) bool {
switch verb {
case 'v', 's':
switch arg.(type) {
case int:
return true
}
}
return false
}
type Thing struct { type Thing struct {
name string name string
} }

1
testdata/interface.txt предоставленный
Просмотреть файл

@ -16,3 +16,4 @@ is Tuple: 0 8 16 24
SmallPair.Print: 3 5 SmallPair.Print: 3 5
Stringer.String(): foo Stringer.String(): foo
Stringer.(*Thing).String(): foo Stringer.(*Thing).String(): foo
nested switch: true