package ssa import ( "git.smesh.lol/moxie/pkg/token" "git.smesh.lol/moxie/pkg/syntax" "git.smesh.lol/moxie/pkg/types" ) func (fb *ssaFuncBuilder) buildIf(s *syntax.IfStmt) { var savedVars map[types.Object]*SSAAlloc if s.Init != nil { savedVars = fb.saveVars() fb.buildStmt(s.Init) } if fb.currentBlock == nil { if savedVars != nil { fb.vars = savedVars } return } cond := fb.buildExpr(s.Cond) if cond == nil { if savedVars != nil { fb.vars = savedVars } return } thenBlock := fb.newBlock("if.then") var elseBlock *SSABasicBlock doneBlock := fb.newBlock("if.done") if s.Else != nil { elseBlock = fb.newBlock("if.else") } else { elseBlock = doneBlock } fb.emit(&SSAIf{Cond: cond}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, thenBlock, elseBlock) thenBlock.Preds = append(thenBlock.Preds, fb.currentBlock) elseBlock.Preds = append(elseBlock.Preds, fb.currentBlock) oldThen := fb.enterScope() thenBlock.ScopeID = fb.scopeID fb.currentBlock = thenBlock fb.buildBlock(s.Then) if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) { fb.emit(&SSAJump{Comment: "if.done"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock) doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock) } fb.exitScope(oldThen) if s.Else != nil { oldElse := fb.enterScope() elseBlock.ScopeID = fb.scopeID fb.currentBlock = elseBlock fb.buildStmt(s.Else) if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) { fb.emit(&SSAJump{Comment: "if.done"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock) doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock) } fb.exitScope(oldElse) } fb.currentBlock = doneBlock if savedVars != nil { fb.vars = savedVars } } func (fb *ssaFuncBuilder) buildFor(s *syntax.ForStmt) { var savedVars map[types.Object]*SSAAlloc if s.Init != nil { if rc, ok := s.Init.(*syntax.RangeClause); ok { fb.buildRangeLoop(s, rc) return } savedVars = fb.saveVars() fb.buildStmt(s.Init) } condBlock := fb.newBlock("for.cond") bodyBlock := fb.newBlock("for.body") postBlock := fb.newBlock("for.post") doneBlock := fb.newBlock("for.done") fb.emit(&SSAJump{Comment: "for.cond"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock) condBlock.Preds = append(condBlock.Preds, fb.currentBlock) fb.loops = append(fb.loops, ssaLoopState{label: fb.pendingLabel, body: bodyBlock, post: postBlock, done: doneBlock}) fb.pendingLabel = "" fb.currentBlock = condBlock if s.Cond != nil { cond := fb.buildExpr(s.Cond) if cond != nil { fb.emit(&SSAIf{Cond: cond}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, bodyBlock, doneBlock) bodyBlock.Preds = append(bodyBlock.Preds, condBlock) doneBlock.Preds = append(doneBlock.Preds, condBlock) } } else { fb.emit(&SSAJump{Comment: "for.body"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, bodyBlock) bodyBlock.Preds = append(bodyBlock.Preds, condBlock) } forScope := fb.enterScope() bodyBlock.ScopeID = fb.scopeID fb.currentBlock = bodyBlock fb.buildBlock(s.Body) if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) { fb.emit(&SSAJump{Comment: "for.post"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, postBlock) postBlock.Preds = append(postBlock.Preds, fb.currentBlock) } postBlock.ScopeID = fb.scopeID fb.currentBlock = postBlock if s.Post != nil { fb.buildStmt(s.Post) } if fb.currentBlock != nil { fb.emit(&SSAJump{Comment: "for.cond"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock) condBlock.Preds = append(condBlock.Preds, fb.currentBlock) } fb.exitScope(forScope) fb.loops = fb.loops[:len(fb.loops)-1] fb.currentBlock = doneBlock if savedVars != nil { fb.vars = savedVars } } func (fb *ssaFuncBuilder) buildRangeLoop(s *syntax.ForStmt, rc *syntax.RangeClause) { savedVars := fb.saveVars() iterExpr := fb.buildExpr(rc.X) if iterExpr == nil { return } r := &SSARange{X: iterExpr} r.typ = types.Typ[types.Invalid] r.name = fb.nextName() fb.emit(r) bodyBlock := fb.newBlock("range.body") doneBlock := fb.newBlock("range.done") condBlock := fb.newBlock("range.cond") fb.loops = append(fb.loops, ssaLoopState{label: fb.pendingLabel, body: bodyBlock, post: condBlock, done: doneBlock}) fb.pendingLabel = "" fb.emit(&SSAJump{Comment: "range.cond"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock) condBlock.Preds = append(condBlock.Preds, fb.currentBlock) fb.currentBlock = condBlock iterSSAType := iterExpr.SSAType() if iterSSAType == nil { switch x := rc.X.(type) { case *syntax.SelectorExpr: if x != nil { tv := x.GetTypeInfo() if tv.Type != nil { iterSSAType = tv.Type } } case *syntax.Name: if x != nil { tv := x.GetTypeInfo() if tv.Type != nil { iterSSAType = tv.Type } } } if iterSSAType == nil { writeStr(2, "WARN: unresolved range type in " | fb.fn.Pkg.Pkg.Path() | "." | fb.fn.name | "\n") return } } nxt := &SSANext{Iter: r, IsString: ssaIsStringType(iterSSAType)} keyTyp := asType(types.Typ[types.Int32]) valTyp := asType(types.Typ[types.Invalid]) iterT := iterSSAType iterU := types.SafeUnderlying(iterT) if sl, ok := iterU.(*types.Slice); ok { valTyp = sl.Elem() } else if sl, ok := iterT.(*types.Slice); ok { valTyp = sl.Elem() } else if mt, ok := iterU.(*types.TCMap); ok { keyTyp = mt.Key() valTyp = mt.Elem() } else if mt, ok := iterT.(*types.TCMap); ok { keyTyp = mt.Key() valTyp = mt.Elem() } else if ar, ok := iterU.(*types.Array); ok { valTyp = ar.Elem() } else if ar, ok := iterT.(*types.Array); ok { valTyp = ar.Elem() } else if p, ok := iterU.(*types.Pointer); ok && p.Elem() != nil { if ar, ok2 := types.SafeUnderlying(p.Elem()).(*types.Array); ok2 { valTyp = ar.Elem() } } if valTyp == asType(types.Typ[types.Invalid]) && ssaIsStringType(iterSSAType) { valTyp = asType(types.Typ[types.Uint8]) } nxt.typ = types.NewTuple( types.NewTCVar(nil, "ok", types.Typ[types.Bool]), types.NewTCVar(nil, "k", keyTyp), types.NewTCVar(nil, "v", valTyp), ) nxt.name = fb.nextName() fb.emit(nxt) okExt := &SSAExtract{Tuple: nxt, Index: 0} okExt.typ = types.Typ[types.Bool] okExt.name = fb.nextName() fb.emit(okExt) fb.emit(&SSAIf{Cond: okExt}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, bodyBlock, doneBlock) bodyBlock.Preds = append(bodyBlock.Preds, condBlock) doneBlock.Preds = append(doneBlock.Preds, condBlock) rangeScope := fb.enterScope() bodyBlock.ScopeID = fb.scopeID fb.currentBlock = bodyBlock if rc.Lhs != nil && rc.Def { names := ssaExprNames(rc.Lhs) for i, name := range names { if name.Value == "_" { continue } ext := &SSAExtract{Tuple: nxt, Index: i + 1} ext.typ = ssaTupleElemType(nxt.typ, i+1) ext.name = fb.nextName() fb.emit(ext) var obj types.Object if fb.info != nil { obj = fb.info.Defs[name] } if obj == nil { obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, ext.typ) } fb.removeVar(name.Value) alloc := fb.emitAlloc(ext.typ, 0) fb.vars[obj] = alloc fb.emitStore(alloc, ext) } } else if rc.Lhs != nil && !rc.Def { names := ssaExprNames(rc.Lhs) for i, name := range names { if name.Value == "_" { continue } ext := &SSAExtract{Tuple: nxt, Index: i + 1} ext.typ = ssaTupleElemType(nxt.typ, i+1) ext.name = fb.nextName() fb.emit(ext) fb.buildStore(name, ext) } } fb.buildBlock(s.Body) if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) { fb.emit(&SSAJump{Comment: "range.cond"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock) condBlock.Preds = append(condBlock.Preds, fb.currentBlock) } fb.exitScope(rangeScope) fb.loops = fb.loops[:len(fb.loops)-1] fb.currentBlock = doneBlock fb.vars = savedVars } func (fb *ssaFuncBuilder) buildSwitch(s *syntax.SwitchStmt) { var savedVars map[types.Object]*SSAAlloc if s.Init != nil { savedVars = fb.saveVars() fb.buildStmt(s.Init) } if fb.currentBlock == nil { if savedVars != nil { fb.vars = savedVars } return } doneBlock := fb.newBlock("switch.done") savedLoops := fb.loops fb.loops = append(fb.loops, ssaLoopState{label: fb.pendingLabel, done: doneBlock}) fb.pendingLabel = "" if s.Tag != nil { if tsg, ok := s.Tag.(*types.TypeSwitchGuard); ok { fb.buildTypeSwitch(s, tsg, doneBlock) fb.loops = savedLoops fb.currentBlock = doneBlock if savedVars != nil { fb.vars = savedVars } return } } var tag SSAValue if s.Tag != nil { tag = fb.buildExpr(s.Tag) } for _, clause := range s.Body { caseBlock := fb.newBlock("switch.case") nextBlock := fb.newBlock("switch.next") if clause.Cases != nil && tag != nil { var cond SSAValue caseExprs := []syntax.Expr{clause.Cases} if list, ok := clause.Cases.(*syntax.ListExpr); ok { caseExprs = list.ElemList } for _, ce := range caseExprs { caseVal := fb.buildExpr(ce) cmp := &SSABinOp{Op: OpEql, X: tag, Y: caseVal} cmp.typ = types.Typ[types.Bool] cmp.name = fb.nextName() fb.emit(cmp) if cond == nil { cond = cmp } else { orOp := &SSABinOp{Op: OpLor, X: cond, Y: cmp} orOp.typ = types.Typ[types.Bool] orOp.name = fb.nextName() fb.emit(orOp) cond = orOp } } fb.emit(&SSAIf{Cond: cond}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock, nextBlock) caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock) nextBlock.Preds = append(nextBlock.Preds, fb.currentBlock) } else if clause.Cases != nil && tag == nil { cond := fb.buildExpr(clause.Cases) fb.emit(&SSAIf{Cond: cond}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock, nextBlock) caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock) nextBlock.Preds = append(nextBlock.Preds, fb.currentBlock) } else { fb.emit(&SSAJump{Comment: "switch.case"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock) caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock) } caseScope := fb.enterScope() caseBlock.ScopeID = fb.scopeID fb.currentBlock = caseBlock caseSaved := fb.saveVars() for _, stmt := range clause.Body { fb.buildStmt(stmt) if fb.currentBlock == nil { break } } if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) { fb.emit(&SSAJump{Comment: "switch.done"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock) doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock) } fb.exitScope(caseScope) fb.vars = caseSaved fb.currentBlock = nextBlock } if fb.currentBlock != nil { fb.emit(&SSAJump{Comment: "switch.done"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock) doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock) } fb.loops = savedLoops fb.currentBlock = doneBlock if savedVars != nil { fb.vars = savedVars } } func (fb *ssaFuncBuilder) buildTypeSwitch(s *syntax.SwitchStmt, tsg *types.TypeSwitchGuard, doneBlock *SSABasicBlock) { x := fb.buildExpr(tsg.X) var savedObj types.Object var savedAlloc *SSAAlloc if tsg.Lhs != nil { for obj, alloc := range fb.vars { if obj.Name() == tsg.Lhs.Value { savedObj = obj savedAlloc = alloc break } } } for _, clause := range s.Body { caseBlock := fb.newBlock("typeswitch.case") nextBlock := fb.newBlock("typeswitch.next") if clause.Cases != nil { caseExprs := []syntax.Expr{clause.Cases} if list, ok := clause.Cases.(*syntax.ListExpr); ok { caseExprs = list.ElemList } var cond SSAValue var firstTA *SSATypeAssert var firstType types.Type for _, ce := range caseExprs { if n, isName := ce.(*syntax.Name); isName && n.Value == "nil" { nilType := x.SSAType() if nilType == nil { nilType = types.NewTCInterface(nil, nil) } nilCheck := &SSABinOp{Op: OpEql, X: x, Y: &SSAConst{typ: nilType, val: nil}} nilCheck.typ = types.Typ[types.Bool] nilCheck.name = fb.nextName() fb.emit(nilCheck) if cond == nil { cond = nilCheck } else { orOp := &SSABinOp{Op: OpLor, X: cond, Y: nilCheck} orOp.typ = types.Typ[types.Bool] orOp.name = fb.nextName() fb.emit(orOp) cond = orOp } continue } var assertedType types.Type if fb.info != nil { tv := fb.info.Types[ce] assertedType = tv.Type } if assertedType == nil { assertedType = fb.resolveType(ce) } if assertedType == nil { assertedType = types.Typ[types.Invalid] } ta := &SSATypeAssert{X: x, AssertedType: assertedType, CommaOk: true} ta.typ = types.NewTuple( types.NewTCVar(nil, "val", assertedType), types.NewTCVar(nil, "ok", types.Typ[types.Bool]), ) ta.name = fb.nextName() fb.emit(ta) okExt := &SSAExtract{Tuple: ta, Index: 1} okExt.typ = types.Typ[types.Bool] okExt.name = fb.nextName() fb.emit(okExt) if firstTA == nil { firstTA = ta firstType = assertedType } if cond == nil { cond = okExt } else { orOp := &SSABinOp{Op: OpLor, X: cond, Y: okExt} orOp.typ = types.Typ[types.Bool] orOp.name = fb.nextName() fb.emit(orOp) cond = orOp } } fb.emit(&SSAIf{Cond: cond}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock, nextBlock) caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock) nextBlock.Preds = append(nextBlock.Preds, fb.currentBlock) fb.currentBlock = caseBlock if tsg.Lhs != nil { var guardVal SSAValue var guardType types.Type if firstTA != nil { ext := &SSAExtract{Tuple: firstTA, Index: 0} ext.typ = firstType ext.name = fb.nextName() fb.emit(ext) guardVal = ext guardType = firstType } else { guardType = x.SSAType() if guardType == nil { guardType = types.NewTCInterface(nil, nil) } guardVal = &SSAConst{typ: guardType, val: nil} } for old := range fb.vars { if old.Name() == tsg.Lhs.Value { delete(fb.vars, old) break } } obj := types.NewTCVar(fb.fn.Pkg.Pkg, tsg.Lhs.Value, guardType) alloc := fb.emitAlloc(guardType, 0) fb.vars[obj] = alloc fb.emitStore(alloc, guardVal) } } else { fb.emit(&SSAJump{Comment: "typeswitch.case"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock) caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock) fb.currentBlock = caseBlock if tsg.Lhs != nil { guardType := x.SSAType() if guardType == nil { guardType = types.NewTCInterface(nil, nil) } for old := range fb.vars { if old.Name() == tsg.Lhs.Value { delete(fb.vars, old) break } } obj := types.NewTCVar(fb.fn.Pkg.Pkg, tsg.Lhs.Value, guardType) alloc := fb.emitAlloc(guardType, 0) fb.vars[obj] = alloc fb.emitStore(alloc, x) } } tsScope := fb.enterScope() caseBlock.ScopeID = fb.scopeID caseSaved := fb.saveVars() for _, stmt := range clause.Body { fb.buildStmt(stmt) if fb.currentBlock == nil { break } } if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) { fb.emit(&SSAJump{Comment: "switch.done"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock) doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock) } fb.exitScope(tsScope) fb.vars = caseSaved fb.currentBlock = nextBlock } if savedObj != nil && savedAlloc != nil { for old := range fb.vars { if old.Name() == tsg.Lhs.Value { delete(fb.vars, old) break } } fb.vars[savedObj] = savedAlloc } if fb.currentBlock != nil { fb.emit(&SSAJump{Comment: "switch.done"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock) doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock) } } func (fb *ssaFuncBuilder) buildSelect(s *syntax.SelectStmt) { doneBlock := fb.newBlock("select.done") var states []*SSASelectState caseBlocks := [](*SSABasicBlock){:0:len(s.Body)} for i, clause := range s.Body { cb := fb.newBlock("select.case") caseBlocks = append(caseBlocks, cb) state := &SSASelectState{} if clause.Comm != nil { switch comm := clause.Comm.(type) { case *syntax.SendStmt: state.Dir = SelectDirSend state.Chan = fb.buildExpr(comm.Chan) state.Send = fb.buildExpr(comm.Value) case *syntax.AssignStmt: var chanExpr syntax.Expr if op, ok2 := comm.Rhs.(*syntax.Operation); ok2 && op.Y == nil && op.Op == token.Recv { chanExpr = op.X } if chanExpr != nil { state.Dir = SelectDirRecv state.Chan = fb.buildExpr(chanExpr) } case *syntax.ExprStmt: if op, ok2 := comm.X.(*syntax.Operation); ok2 && op.Y == nil && op.Op == token.Recv { state.Dir = SelectDirRecv state.Chan = fb.buildExpr(op.X) } } } states = append(states, state) _ = i } sel := &SSASelect{States: states, Blocking: true} sel.typ = types.NewTuple( types.NewTCVar(nil, "index", types.Typ[types.Int32]), types.NewTCVar(nil, "recvOk", types.Typ[types.Bool]), ) sel.name = fb.nextName() fb.emit(sel) for i, clause := range s.Body { selScope := fb.enterScope() caseBlocks[i].ScopeID = fb.scopeID fb.currentBlock = caseBlocks[i] if clause.Comm != nil { fb.buildStmt(clause.Comm) } for _, stmt := range clause.Body { fb.buildStmt(stmt) if fb.currentBlock == nil { break } } if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) { fb.emit(&SSAJump{Comment: "select.done"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock) doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock) } fb.exitScope(selScope) } fb.currentBlock = doneBlock } func (fb *ssaFuncBuilder) isSelfTailCall(e syntax.Expr) (*syntax.CallExpr, bool) { call, ok := e.(*syntax.CallExpr) if !ok || fb.tailBlock == nil || fb.deferred > 0 { return nil, false } name, ok := call.Fun.(*syntax.Name) if !ok { return nil, false } obj := fb.lookupObject(name.Value) if obj == nil { return nil, false } tc, ok := obj.(*types.TCFunc) if !ok || tc == nil { return nil, false } fn, _ := fb.fn.Pkg.Members[name.Value].(*SSAFunction) if fn != fb.fn { return nil, false } return call, true } func (fb *ssaFuncBuilder) buildReturn(s *syntax.ReturnStmt) { if s.Results != nil { if call, ok := fb.isSelfTailCall(s.Results); ok { args := fb.buildArgs(call.ArgList) if len(args) == len(fb.paramAllocas) { for i, arg := range args { fb.emitStore(fb.paramAllocas[i], arg) } fb.emit(&SSAJump{Comment: "tailcall"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, fb.tailBlock) fb.tailBlock.Preds = append(fb.tailBlock.Preds, fb.currentBlock) fb.currentBlock = nil return } } } var vals []SSAValue if s.Results != nil { if list, ok := s.Results.(*syntax.ListExpr); ok { for i, el := range list.ElemList { v := fb.buildExpr(el) if v != nil { vals = append(vals, v) } else { var zeroType types.Type if fb.fn.Signature != nil && fb.fn.Signature.Results() != nil && int32(i) < int32(fb.fn.Signature.Results().Len()) { zeroType = fb.fn.Signature.Results().At(int32(i)).Type() } vals = append(vals, &SSAConst{typ: zeroType, val: nil}) } } } else { v := fb.buildExpr(s.Results) if v != nil { if v.SSAType() == nil { vals = append(vals, v) } else if tup, ok := v.SSAType().(*types.Tuple); ok && tup.Len() > 1 { for i := 0; i < tup.Len(); i++ { ext := &SSAExtract{Tuple: v, Index: i} ext.typ = tup.At(i).Type() ext.name = fb.nextName() fb.emit(ext) vals = append(vals, ext) } } else { vals = append(vals, v) } } } } else if len(fb.namedResults) > 0 { for _, nr := range fb.namedResults { elemType := nr.SSAType() if p, ok := elemType.(*types.Pointer); ok { elemType = p.Elem() } vals = append(vals, fb.emitLoad(nr, elemType)) } } if fb.fn.Signature != nil && fb.fn.Signature.Results() != nil { for i, v := range vals { if i < fb.fn.Signature.Results().Len() { retType := fb.fn.Signature.Results().At(i).Type() vals[i] = fb.coerceToInterface(v, retType) } } } fb.emitReturn(vals, 0) } func (fb *ssaFuncBuilder) buildBranch(s *syntax.BranchStmt) { if fb.currentBlock == nil { return } switch s.Tok { case token.Break: if len(fb.loops) > 0 { var doneBlock *SSABasicBlock if s.Label != nil { for i := len(fb.loops) - 1; i >= 0; i-- { if fb.loops[i].label == s.Label.Value { doneBlock = fb.loops[i].done break } } } if doneBlock == nil { doneBlock = fb.loops[len(fb.loops)-1].done } fb.emit(&SSAJump{Comment: "break"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock) doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock) fb.currentBlock = nil } case token.Continue: for i := len(fb.loops) - 1; i >= 0; i-- { postBlock := fb.loops[i].post if postBlock == nil { postBlock = fb.loops[i].body } if postBlock == nil { continue } fb.emit(&SSAJump{Comment: "continue"}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, postBlock) postBlock.Preds = append(postBlock.Preds, fb.currentBlock) fb.currentBlock = nil break } case token.Goto: if s.Label != nil { target := fb.labelBlock(s.Label.Value) fb.emit(&SSAJump{Comment: "goto." | s.Label.Value}) fb.currentBlock.Succs = append(fb.currentBlock.Succs, target) target.Preds = append(target.Preds, fb.currentBlock) fb.currentBlock = nil } case token.Return: fb.emitReturn(nil, 0) } } func (fb *ssaFuncBuilder) buildGoStmt(call *syntax.CallExpr) { fn := fb.buildExpr(call.Fun) if fn == nil { return } args := fb.buildArgs(call.ArgList) g := &SSAGo{Call: SSACallCommon{Value: fn, Args: args}} fb.emit(g) } func (fb *ssaFuncBuilder) buildDeferStmt(call *syntax.CallExpr) { fn := fb.buildExpr(call.Fun) if fn == nil { return } args := fb.buildArgs(call.ArgList) d := &SSADefer{Call: SSACallCommon{Value: fn, Args: args}} fb.emit(d) fb.deferred++ }