func (b *builder) stmt(fn *Function, _s ast.Stmt) {
	// The label of the current statement.  If non-nil, its _goto
	// target is always set; its _break and _continue are set only
	// within the body of switch/typeswitch/select/for/range.
	// It is effectively an additional default-nil parameter of stmt().
	var label *lblock
start:
	switch s := _s.(type) {
	case *ast.EmptyStmt:
		// ignore.  (Usually removed by gofmt.)

	case *ast.DeclStmt: // Con, Var or Typ
		d := s.Decl.(*ast.GenDecl)
		if d.Tok == token.VAR {
			for _, spec := range d.Specs {
				if vs, ok := spec.(*ast.ValueSpec); ok {
					b.localValueSpec(fn, vs)
				}
			}
		}

	case *ast.LabeledStmt:
		label = fn.labelledBlock(s.Label)
		emitJump(fn, label._goto)
		fn.currentBlock = label._goto
		_s = s.Stmt
		goto start // effectively: tailcall stmt(fn, s.Stmt, label)

	case *ast.ExprStmt:
		b.expr(fn, s.X)

	case *ast.SendStmt:
		fn.emit(&Send{
			Chan: b.expr(fn, s.Chan),
			X: emitConv(fn, b.expr(fn, s.Value),
				fn.Pkg.typeOf(s.Chan).Underlying().(*types.Chan).Elem()),
			pos: s.Arrow,
		})

	case *ast.IncDecStmt:
		op := token.ADD
		if s.Tok == token.DEC {
			op = token.SUB
		}
		loc := b.addr(fn, s.X, false)
		b.assignOp(fn, loc, NewConst(exact.MakeInt64(1), loc.typ()), op, s.Pos())

	case *ast.AssignStmt:
		switch s.Tok {
		case token.ASSIGN, token.DEFINE:
			b.assignStmt(fn, s.Lhs, s.Rhs, s.Tok == token.DEFINE)

		default: // +=, etc.
			op := s.Tok + token.ADD - token.ADD_ASSIGN
			b.assignOp(fn, b.addr(fn, s.Lhs[0], false), b.expr(fn, s.Rhs[0]), op, s.Pos())
		}

	case *ast.GoStmt:
		// The "intrinsics" new/make/len/cap are forbidden here.
		// panic is treated like an ordinary function call.
		v := Go{pos: s.Go}
		b.setCall(fn, s.Call, &v.Call)
		fn.emit(&v)

	case *ast.DeferStmt:
		// The "intrinsics" new/make/len/cap are forbidden here.
		// panic is treated like an ordinary function call.
		v := Defer{pos: s.Defer}
		b.setCall(fn, s.Call, &v.Call)
		fn.emit(&v)

		// A deferred call can cause recovery from panic,
		// and control resumes at the Recover block.
		createRecoverBlock(fn)

	case *ast.ReturnStmt:
		var results []Value
		if len(s.Results) == 1 && fn.Signature.Results().Len() > 1 {
			// Return of one expression in a multi-valued function.
			tuple := b.exprN(fn, s.Results[0])
			ttuple := tuple.Type().(*types.Tuple)
			for i, n := 0, ttuple.Len(); i < n; i++ {
				results = append(results,
					emitConv(fn, emitExtract(fn, tuple, i),
						fn.Signature.Results().At(i).Type()))
			}
		} else {
			// 1:1 return, or no-arg return in non-void function.
			for i, r := range s.Results {
				v := emitConv(fn, b.expr(fn, r), fn.Signature.Results().At(i).Type())
				results = append(results, v)
			}
		}
		if fn.namedResults != nil {
			// Function has named result parameters (NRPs).
			// Perform parallel assignment of return operands to NRPs.
			for i, r := range results {
				emitStore(fn, fn.namedResults[i], r, s.Return)
			}
		}
		// Run function calls deferred in this
		// function when explicitly returning from it.
		fn.emit(new(RunDefers))
		if fn.namedResults != nil {
			// Reload NRPs to form the result tuple.
			results = results[:0]
			for _, r := range fn.namedResults {
				results = append(results, emitLoad(fn, r))
			}
		}
		fn.emit(&Return{Results: results, pos: s.Return})
		fn.currentBlock = fn.newBasicBlock("unreachable")

	case *ast.BranchStmt:
		var block *BasicBlock
		switch s.Tok {
		case token.BREAK:
			if s.Label != nil {
				block = fn.labelledBlock(s.Label)._break
			} else {
				for t := fn.targets; t != nil && block == nil; t = t.tail {
					block = t._break
				}
			}

		case token.CONTINUE:
			if s.Label != nil {
				block = fn.labelledBlock(s.Label)._continue
			} else {
				for t := fn.targets; t != nil && block == nil; t = t.tail {
					block = t._continue
				}
			}

		case token.FALLTHROUGH:
			for t := fn.targets; t != nil && block == nil; t = t.tail {
				block = t._fallthrough
			}

		case token.GOTO:
			block = fn.labelledBlock(s.Label)._goto
		}
		emitJump(fn, block)
		fn.currentBlock = fn.newBasicBlock("unreachable")

	case *ast.BlockStmt:
		b.stmtList(fn, s.List)

	case *ast.IfStmt:
		if s.Init != nil {
			b.stmt(fn, s.Init)
		}
		then := fn.newBasicBlock("if.then")
		done := fn.newBasicBlock("if.done")
		els := done
		if s.Else != nil {
			els = fn.newBasicBlock("if.else")
		}
		b.cond(fn, s.Cond, then, els)
		fn.currentBlock = then
		b.stmt(fn, s.Body)
		emitJump(fn, done)

		if s.Else != nil {
			fn.currentBlock = els
			b.stmt(fn, s.Else)
			emitJump(fn, done)
		}

		fn.currentBlock = done

	case *ast.SwitchStmt:
		b.switchStmt(fn, s, label)

	case *ast.TypeSwitchStmt:
		b.typeSwitchStmt(fn, s, label)

	case *ast.SelectStmt:
		b.selectStmt(fn, s, label)

	case *ast.ForStmt:
		b.forStmt(fn, s, label)

	case *ast.RangeStmt:
		b.rangeStmt(fn, s, label)

	default:
		panic(fmt.Sprintf("unexpected statement kind: %T", s))
	}
}
func (b *builder) stmt(fn *Function, _s ast.Stmt) {
	
	
	
	
	var label *lblock
start:
	switch s := _s.(type) {
	case *ast.EmptyStmt:
		

	case *ast.DeclStmt: 
		d := s.Decl.(*ast.GenDecl)
		if d.Tok == token.VAR {
			for _, spec := range d.Specs {
				if vs, ok := spec.(*ast.ValueSpec); ok {
					b.localValueSpec(fn, vs)
				}
			}
		}

	case *ast.LabeledStmt:
		label = fn.labelledBlock(s.Label)
		emitJump(fn, label._goto)
		fn.currentBlock = label._goto
		_s = s.Stmt
		goto start 

	case *ast.ExprStmt:
		b.expr(fn, s.X)

	case *ast.SendStmt:
		fn.emit(&Send{
			Chan: b.expr(fn, s.Chan),
			X: emitConv(fn, b.expr(fn, s.Value),
				fn.Pkg.typeOf(s.Chan).Underlying().(*types.Chan).Elem()),
			pos: s.Arrow,
		})

	case *ast.IncDecStmt:
		op := token.ADD
		if s.Tok == token.DEC {
			op = token.SUB
		}
		loc := b.addr(fn, s.X, false)
		b.assignOp(fn, loc, NewConst(exact.MakeInt64(1), loc.typ()), op, s.Pos())

	case *ast.AssignStmt:
		switch s.Tok {
		case token.ASSIGN, token.DEFINE:
			b.assignStmt(fn, s.Lhs, s.Rhs, s.Tok == token.DEFINE)

		default: 
			op := s.Tok + token.ADD - token.ADD_ASSIGN
			b.assignOp(fn, b.addr(fn, s.Lhs[0], false), b.expr(fn, s.Rhs[0]), op, s.Pos())
		}

	case *ast.GoStmt:
		
		
		v := Go{pos: s.Go}
		b.setCall(fn, s.Call, &v.Call)
		fn.emit(&v)

	case *ast.DeferStmt:
		
		
		v := Defer{pos: s.Defer}
		b.setCall(fn, s.Call, &v.Call)
		fn.emit(&v)

		
		
		createRecoverBlock(fn)

	case *ast.ReturnStmt:
		var results []Value
		if len(s.Results) == 1 && fn.Signature.Results().Len() > 1 {
			
			tuple := b.exprN(fn, s.Results[0])
			ttuple := tuple.Type().(*types.Tuple)
			for i, n := 0, ttuple.Len(); i < n; i++ {
				results = append(results,
					emitConv(fn, emitExtract(fn, tuple, i),
						fn.Signature.Results().At(i).Type()))
			}
		} else {
			
			for i, r := range s.Results {
				v := emitConv(fn, b.expr(fn, r), fn.Signature.Results().At(i).Type())
				results = append(results, v)
			}
		}
		if fn.namedResults != nil {
			
			
			for i, r := range results {
				emitStore(fn, fn.namedResults[i], r, s.Return)
			}
		}
		
		
		fn.emit(new(RunDefers))
		if fn.namedResults != nil {
			
			results = results[:0]
			for _, r := range fn.namedResults {
				results = append(results, emitLoad(fn, r))
			}
		}
		fn.emit(&Return{Results: results, pos: s.Return})
		fn.currentBlock = fn.newBasicBlock("unreachable")

	case *ast.BranchStmt:
		var block *BasicBlock
		switch s.Tok {
		case token.BREAK:
			if s.Label != nil {
				block = fn.labelledBlock(s.Label)._break
			} else {
				for t := fn.targets; t != nil && block == nil; t = t.tail {
					block = t._break
				}
			}

		case token.CONTINUE:
			if s.Label != nil {
				block = fn.labelledBlock(s.Label)._continue
			} else {
				for t := fn.targets; t != nil && block == nil; t = t.tail {
					block = t._continue
				}
			}

		case token.FALLTHROUGH:
			for t := fn.targets; t != nil && block == nil; t = t.tail {
				block = t._fallthrough
			}

		case token.GOTO:
			block = fn.labelledBlock(s.Label)._goto
		}
		emitJump(fn, block)
		fn.currentBlock = fn.newBasicBlock("unreachable")

	case *ast.BlockStmt:
		b.stmtList(fn, s.List)

	case *ast.IfStmt:
		if s.Init != nil {
			b.stmt(fn, s.Init)
		}
		then := fn.newBasicBlock("if.then")
		done := fn.newBasicBlock("if.done")
		els := done
		if s.Else != nil {
			els = fn.newBasicBlock("if.else")
		}
		b.cond(fn, s.Cond, then, els)
		fn.currentBlock = then
		b.stmt(fn, s.Body)
		emitJump(fn, done)

		if s.Else != nil {
			fn.currentBlock = els
			b.stmt(fn, s.Else)
			emitJump(fn, done)
		}

		fn.currentBlock = done

	case *ast.SwitchStmt:
		b.switchStmt(fn, s, label)

	case *ast.TypeSwitchStmt:
		b.typeSwitchStmt(fn, s, label)

	case *ast.SelectStmt:
		b.selectStmt(fn, s, label)

	case *ast.ForStmt:
		b.forStmt(fn, s, label)

	case *ast.RangeStmt:
		b.rangeStmt(fn, s, label)

	default:
		panic(fmt.Sprintf("unexpected statement kind: %T", s))
	}
}
func ( b * builder ) stmt ( fn * Function , _s ast . Stmt ) { // The label of the current statement.  If non-nil, its _goto // target is always set; its _break and _continue are set only // within the body of switch/typeswitch/select/for/range. // It is effectively an additional default-nil parameter of stmt(). var label * lblock start : switch s := _s . ( type ) { case * ast . EmptyStmt : // ignore.  (Usually removed by gofmt.) case * ast . DeclStmt : // Con, Var or Typ d := s . Decl . ( * ast . GenDecl ) if d . Tok == token . VAR { for _ , spec := range d . Specs { if vs , ok := spec . ( * ast . ValueSpec ) ; ok { b . localValueSpec ( fn , vs ) } } } case * ast . LabeledStmt : label = fn . labelledBlock ( s . Label ) emitJump ( fn , label . _goto ) fn . currentBlock = label . _goto _s = s . Stmt goto start // effectively: tailcall stmt(fn, s.Stmt, label) case * ast . ExprStmt : b . expr ( fn , s . X ) case * ast . SendStmt : fn . emit ( & Send { Chan : b . expr ( fn , s . Chan ) , X : emitConv ( fn , b . expr ( fn , s . Value ) , fn . Pkg . typeOf ( s . Chan ) . Underlying ( ) . ( * types . Chan ) . Elem ( ) ) , pos : s . Arrow , } ) case * ast . IncDecStmt : op := token . ADD if s . Tok == token . DEC { op = token . SUB } loc := b . addr ( fn , s . X , false ) b . assignOp ( fn , loc , NewConst ( exact . MakeInt64 ( 1 ) , loc . typ ( ) ) , op , s . Pos ( ) ) case * ast . AssignStmt : switch s . Tok { case token . ASSIGN , token . DEFINE : b . assignStmt ( fn , s . Lhs , s . Rhs , s . Tok == token . DEFINE ) default : // +=, etc. op := s . Tok + token . ADD - token . ADD_ASSIGN b . assignOp ( fn , b . addr ( fn , s . Lhs [ 0 ] , false ) , b . expr ( fn , s . Rhs [ 0 ] ) , op , s . Pos ( ) ) } case * ast . GoStmt : // The "intrinsics" new/make/len/cap are forbidden here. // panic is treated like an ordinary function call. v := Go { pos : s . Go } b . setCall ( fn , s . Call , & v . Call ) fn . emit ( & v ) case * ast . DeferStmt : // The "intrinsics" new/make/len/cap are forbidden here. // panic is treated like an ordinary function call. v := Defer { pos : s . Defer } b . setCall ( fn , s . Call , & v . Call ) fn . emit ( & v ) // A deferred call can cause recovery from panic, // and control resumes at the Recover block. createRecoverBlock ( fn ) case * ast . ReturnStmt : var results [ ] Value if len ( s . Results ) == 1 && fn . Signature . Results ( ) . Len ( ) > 1 { // Return of one expression in a multi-valued function. tuple := b . exprN ( fn , s . Results [ 0 ] ) ttuple := tuple . Type ( ) . ( * types . Tuple ) for i , n := 0 , ttuple . Len ( ) ; i < n ; i ++ { results = append ( results , emitConv ( fn , emitExtract ( fn , tuple , i ) , fn . Signature . Results ( ) . At ( i ) . Type ( ) ) ) } } else { // 1:1 return, or no-arg return in non-void function. for i , r := range s . Results { v := emitConv ( fn , b . expr ( fn , r ) , fn . Signature . Results ( ) . At ( i ) . Type ( ) ) results = append ( results , v ) } } if fn . namedResults != nil { // Function has named result parameters (NRPs). // Perform parallel assignment of return operands to NRPs. for i , r := range results { emitStore ( fn , fn . namedResults [ i ] , r , s . Return ) } } // Run function calls deferred in this // function when explicitly returning from it. fn . emit ( new ( RunDefers ) ) if fn . namedResults != nil { // Reload NRPs to form the result tuple. results = results [ : 0 ] for _ , r := range fn . namedResults { results = append ( results , emitLoad ( fn , r ) ) } } fn . emit ( & Return { Results : results , pos : s . Return } ) fn . currentBlock = fn . newBasicBlock ( " " ) case * ast . BranchStmt : var block * BasicBlock switch s . Tok { case token . BREAK : if s . Label != nil { block = fn . labelledBlock ( s . Label ) . _break } else { for t := fn . targets ; t != nil && block == nil ; t = t . tail { block = t . _break } } case token . CONTINUE : if s . Label != nil { block = fn . labelledBlock ( s . Label ) . _continue } else { for t := fn . targets ; t != nil && block == nil ; t = t . tail { block = t . _continue } } case token . FALLTHROUGH : for t := fn . targets ; t != nil && block == nil ; t = t . tail { block = t . _fallthrough } case token . GOTO : block = fn . labelledBlock ( s . Label ) . _goto } emitJump ( fn , block ) fn . currentBlock = fn . newBasicBlock ( " " ) case * ast . BlockStmt : b . stmtList ( fn , s . List ) case * ast . IfStmt : if s . Init != nil { b . stmt ( fn , s . Init ) } then := fn . newBasicBlock ( " " ) done := fn . newBasicBlock ( " " ) els := done if s . Else != nil { els = fn . newBasicBlock ( " " ) } b . cond ( fn , s . Cond , then , els ) fn . currentBlock = then b . stmt ( fn , s . Body ) emitJump ( fn , done ) if s . Else != nil { fn . currentBlock = els b . stmt ( fn , s . Else ) emitJump ( fn , done ) } fn . currentBlock = done case * ast . SwitchStmt : b . switchStmt ( fn , s , label ) case * ast . TypeSwitchStmt : b . typeSwitchStmt ( fn , s , label ) case * ast . SelectStmt : b . selectStmt ( fn , s , label ) case * ast . ForStmt : b . forStmt ( fn , s , label ) case * ast . RangeStmt : b . rangeStmt ( fn , s , label ) default : panic ( fmt . Sprintf ( " " , s ) ) } }
// stmt lowers statement s to SSA form, emitting code to fn.
stmt lowers statement s to SSA form emitting code to fn .