open-nomad/vendor/honnef.co/go/tools/ir/builder.go
Seth Hoenig 435c0d9fc8 deps: Switch to Go modules for dependency management
This PR switches the Nomad repository from using govendor to Go modules
for managing dependencies. Aspects of the Nomad workflow remain pretty
much the same. The usual Makefile targets should continue to work as
they always did. The API submodule simply defers to the parent Nomad
version on the repository, keeping the semantics of API versioning that
currently exists.
2020-06-02 14:30:36 -05:00

2475 lines
66 KiB
Go

// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ir
// This file implements the BUILD phase of IR construction.
//
// IR construction has two phases, CREATE and BUILD. In the CREATE phase
// (create.go), all packages are constructed and type-checked and
// definitions of all package members are created, method-sets are
// computed, and wrapper methods are synthesized.
// ir.Packages are created in arbitrary order.
//
// In the BUILD phase (builder.go), the builder traverses the AST of
// each Go source function and generates IR instructions for the
// function body. Initializer expressions for package-level variables
// are emitted to the package's init() function in the order specified
// by go/types.Info.InitOrder, then code for each function in the
// package is generated in lexical order.
//
// The builder's and Program's indices (maps) are populated and
// mutated during the CREATE phase, but during the BUILD phase they
// remain constant. The sole exception is Prog.methodSets and its
// related maps, which are protected by a dedicated mutex.
import (
"fmt"
"go/ast"
"go/constant"
"go/token"
"go/types"
"os"
)
type opaqueType struct {
types.Type
name string
}
func (t *opaqueType) String() string { return t.name }
var (
varOk = newVar("ok", tBool)
varIndex = newVar("index", tInt)
// Type constants.
tBool = types.Typ[types.Bool]
tByte = types.Typ[types.Byte]
tInt = types.Typ[types.Int]
tInvalid = types.Typ[types.Invalid]
tString = types.Typ[types.String]
tUntypedNil = types.Typ[types.UntypedNil]
tRangeIter = &opaqueType{nil, "iter"} // the type of all "range" iterators
tEface = types.NewInterfaceType(nil, nil).Complete()
)
// builder holds state associated with the package currently being built.
// Its methods contain all the logic for AST-to-IR conversion.
type builder struct {
printFunc string
blocksets [5]BlockSet
}
// cond emits to fn code to evaluate boolean condition e and jump
// to t or f depending on its value, performing various simplifications.
//
// Postcondition: fn.currentBlock is nil.
//
func (b *builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) *If {
switch e := e.(type) {
case *ast.ParenExpr:
return b.cond(fn, e.X, t, f)
case *ast.BinaryExpr:
switch e.Op {
case token.LAND:
ltrue := fn.newBasicBlock("cond.true")
b.cond(fn, e.X, ltrue, f)
fn.currentBlock = ltrue
return b.cond(fn, e.Y, t, f)
case token.LOR:
lfalse := fn.newBasicBlock("cond.false")
b.cond(fn, e.X, t, lfalse)
fn.currentBlock = lfalse
return b.cond(fn, e.Y, t, f)
}
case *ast.UnaryExpr:
if e.Op == token.NOT {
return b.cond(fn, e.X, f, t)
}
}
// A traditional compiler would simplify "if false" (etc) here
// but we do not, for better fidelity to the source code.
//
// The value of a constant condition may be platform-specific,
// and may cause blocks that are reachable in some configuration
// to be hidden from subsequent analyses such as bug-finding tools.
return emitIf(fn, b.expr(fn, e), t, f, e)
}
// logicalBinop emits code to fn to evaluate e, a &&- or
// ||-expression whose reified boolean value is wanted.
// The value is returned.
//
func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value {
rhs := fn.newBasicBlock("binop.rhs")
done := fn.newBasicBlock("binop.done")
// T(e) = T(e.X) = T(e.Y) after untyped constants have been
// eliminated.
// TODO(adonovan): not true; MyBool==MyBool yields UntypedBool.
t := fn.Pkg.typeOf(e)
var short Value // value of the short-circuit path
switch e.Op {
case token.LAND:
b.cond(fn, e.X, rhs, done)
short = emitConst(fn, NewConst(constant.MakeBool(false), t))
case token.LOR:
b.cond(fn, e.X, done, rhs)
short = emitConst(fn, NewConst(constant.MakeBool(true), t))
}
// Is rhs unreachable?
if rhs.Preds == nil {
// Simplify false&&y to false, true||y to true.
fn.currentBlock = done
return short
}
// Is done unreachable?
if done.Preds == nil {
// Simplify true&&y (or false||y) to y.
fn.currentBlock = rhs
return b.expr(fn, e.Y)
}
// All edges from e.X to done carry the short-circuit value.
var edges []Value
for range done.Preds {
edges = append(edges, short)
}
// The edge from e.Y to done carries the value of e.Y.
fn.currentBlock = rhs
edges = append(edges, b.expr(fn, e.Y))
emitJump(fn, done, e)
fn.currentBlock = done
phi := &Phi{Edges: edges}
phi.typ = t
return done.emit(phi, e)
}
// exprN lowers a multi-result expression e to IR form, emitting code
// to fn and returning a single Value whose type is a *types.Tuple.
// The caller must access the components via Extract.
//
// Multi-result expressions include CallExprs in a multi-value
// assignment or return statement, and "value,ok" uses of
// TypeAssertExpr, IndexExpr (when X is a map), and Recv.
//
func (b *builder) exprN(fn *Function, e ast.Expr) Value {
typ := fn.Pkg.typeOf(e).(*types.Tuple)
switch e := e.(type) {
case *ast.ParenExpr:
return b.exprN(fn, e.X)
case *ast.CallExpr:
// Currently, no built-in function nor type conversion
// has multiple results, so we can avoid some of the
// cases for single-valued CallExpr.
var c Call
b.setCall(fn, e, &c.Call)
c.typ = typ
return fn.emit(&c, e)
case *ast.IndexExpr:
mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
lookup := &MapLookup{
X: b.expr(fn, e.X),
Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key(), e),
CommaOk: true,
}
lookup.setType(typ)
return fn.emit(lookup, e)
case *ast.TypeAssertExpr:
return emitTypeTest(fn, b.expr(fn, e.X), typ.At(0).Type(), e)
case *ast.UnaryExpr: // must be receive <-
return emitRecv(fn, b.expr(fn, e.X), true, typ, e)
}
panic(fmt.Sprintf("exprN(%T) in %s", e, fn))
}
// builtin emits to fn IR instructions to implement a call to the
// built-in function obj with the specified arguments
// and return type. It returns the value defined by the result.
//
// The result is nil if no special handling was required; in this case
// the caller should treat this like an ordinary library function
// call.
//
func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ types.Type, source ast.Node) Value {
switch obj.Name() {
case "make":
switch typ.Underlying().(type) {
case *types.Slice:
n := b.expr(fn, args[1])
m := n
if len(args) == 3 {
m = b.expr(fn, args[2])
}
if m, ok := m.(*Const); ok {
// treat make([]T, n, m) as new([m]T)[:n]
cap := m.Int64()
at := types.NewArray(typ.Underlying().(*types.Slice).Elem(), cap)
alloc := emitNew(fn, at, source)
v := &Slice{
X: alloc,
High: n,
}
v.setType(typ)
return fn.emit(v, source)
}
v := &MakeSlice{
Len: n,
Cap: m,
}
v.setType(typ)
return fn.emit(v, source)
case *types.Map:
var res Value
if len(args) == 2 {
res = b.expr(fn, args[1])
}
v := &MakeMap{Reserve: res}
v.setType(typ)
return fn.emit(v, source)
case *types.Chan:
var sz Value = emitConst(fn, intConst(0))
if len(args) == 2 {
sz = b.expr(fn, args[1])
}
v := &MakeChan{Size: sz}
v.setType(typ)
return fn.emit(v, source)
}
case "new":
alloc := emitNew(fn, deref(typ), source)
return alloc
case "len", "cap":
// Special case: len or cap of an array or *array is
// based on the type, not the value which may be nil.
// We must still evaluate the value, though. (If it
// was side-effect free, the whole call would have
// been constant-folded.)
t := deref(fn.Pkg.typeOf(args[0])).Underlying()
if at, ok := t.(*types.Array); ok {
b.expr(fn, args[0]) // for effects only
return emitConst(fn, intConst(at.Len()))
}
// Otherwise treat as normal.
case "panic":
fn.emit(&Panic{
X: emitConv(fn, b.expr(fn, args[0]), tEface, source),
}, source)
addEdge(fn.currentBlock, fn.Exit)
fn.currentBlock = fn.newBasicBlock("unreachable")
return emitConst(fn, NewConst(constant.MakeBool(true), tBool)) // any non-nil Value will do
}
return nil // treat all others as a regular function call
}
// addr lowers a single-result addressable expression e to IR form,
// emitting code to fn and returning the location (an lvalue) defined
// by the expression.
//
// If escaping is true, addr marks the base variable of the
// addressable expression e as being a potentially escaping pointer
// value. For example, in this code:
//
// a := A{
// b: [1]B{B{c: 1}}
// }
// return &a.b[0].c
//
// the application of & causes a.b[0].c to have its address taken,
// which means that ultimately the local variable a must be
// heap-allocated. This is a simple but very conservative escape
// analysis.
//
// Operations forming potentially escaping pointers include:
// - &x, including when implicit in method call or composite literals.
// - a[:] iff a is an array (not *array)
// - references to variables in lexically enclosing functions.
//
func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
switch e := e.(type) {
case *ast.Ident:
if isBlankIdent(e) {
return blank{}
}
obj := fn.Pkg.objectOf(e)
v := fn.Prog.packageLevelValue(obj) // var (address)
if v == nil {
v = fn.lookup(obj, escaping)
}
return &address{addr: v, expr: e}
case *ast.CompositeLit:
t := deref(fn.Pkg.typeOf(e))
var v *Alloc
if escaping {
v = emitNew(fn, t, e)
} else {
v = fn.addLocal(t, e)
}
var sb storebuf
b.compLit(fn, v, e, true, &sb)
sb.emit(fn)
return &address{addr: v, expr: e}
case *ast.ParenExpr:
return b.addr(fn, e.X, escaping)
case *ast.SelectorExpr:
sel, ok := fn.Pkg.info.Selections[e]
if !ok {
// qualified identifier
return b.addr(fn, e.Sel, escaping)
}
if sel.Kind() != types.FieldVal {
panic(sel)
}
wantAddr := true
v := b.receiver(fn, e.X, wantAddr, escaping, sel, e)
last := len(sel.Index()) - 1
return &address{
addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel),
expr: e.Sel,
}
case *ast.IndexExpr:
var x Value
var et types.Type
switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
case *types.Array:
x = b.addr(fn, e.X, escaping).address(fn)
et = types.NewPointer(t.Elem())
case *types.Pointer: // *array
x = b.expr(fn, e.X)
et = types.NewPointer(t.Elem().Underlying().(*types.Array).Elem())
case *types.Slice:
x = b.expr(fn, e.X)
et = types.NewPointer(t.Elem())
case *types.Map:
return &element{
m: b.expr(fn, e.X),
k: emitConv(fn, b.expr(fn, e.Index), t.Key(), e.Index),
t: t.Elem(),
}
default:
panic("unexpected container type in IndexExpr: " + t.String())
}
v := &IndexAddr{
X: x,
Index: emitConv(fn, b.expr(fn, e.Index), tInt, e.Index),
}
v.setType(et)
return &address{addr: fn.emit(v, e), expr: e}
case *ast.StarExpr:
return &address{addr: b.expr(fn, e.X), expr: e}
}
panic(fmt.Sprintf("unexpected address expression: %T", e))
}
type store struct {
lhs lvalue
rhs Value
source ast.Node
}
type storebuf struct{ stores []store }
func (sb *storebuf) store(lhs lvalue, rhs Value, source ast.Node) {
sb.stores = append(sb.stores, store{lhs, rhs, source})
}
func (sb *storebuf) emit(fn *Function) {
for _, s := range sb.stores {
s.lhs.store(fn, s.rhs, s.source)
}
}
// assign emits to fn code to initialize the lvalue loc with the value
// of expression e. If isZero is true, assign assumes that loc holds
// the zero value for its type.
//
// This is equivalent to loc.store(fn, b.expr(fn, e)), but may generate
// better code in some cases, e.g., for composite literals in an
// addressable location.
//
// If sb is not nil, assign generates code to evaluate expression e, but
// not to update loc. Instead, the necessary stores are appended to the
// storebuf sb so that they can be executed later. This allows correct
// in-place update of existing variables when the RHS is a composite
// literal that may reference parts of the LHS.
//
func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *storebuf, source ast.Node) {
// Can we initialize it in place?
if e, ok := unparen(e).(*ast.CompositeLit); ok {
// A CompositeLit never evaluates to a pointer,
// so if the type of the location is a pointer,
// an &-operation is implied.
if _, ok := loc.(blank); !ok { // avoid calling blank.typ()
if isPointer(loc.typ()) {
ptr := b.addr(fn, e, true).address(fn)
// copy address
if sb != nil {
sb.store(loc, ptr, source)
} else {
loc.store(fn, ptr, source)
}
return
}
}
if _, ok := loc.(*address); ok {
if isInterface(loc.typ()) {
// e.g. var x interface{} = T{...}
// Can't in-place initialize an interface value.
// Fall back to copying.
} else {
// x = T{...} or x := T{...}
addr := loc.address(fn)
if sb != nil {
b.compLit(fn, addr, e, isZero, sb)
} else {
var sb storebuf
b.compLit(fn, addr, e, isZero, &sb)
sb.emit(fn)
}
// Subtle: emit debug ref for aggregate types only;
// slice and map are handled by store ops in compLit.
switch loc.typ().Underlying().(type) {
case *types.Struct, *types.Array:
emitDebugRef(fn, e, addr, true)
}
return
}
}
}
// simple case: just copy
rhs := b.expr(fn, e)
if sb != nil {
sb.store(loc, rhs, source)
} else {
loc.store(fn, rhs, source)
}
}
// expr lowers a single-result expression e to IR form, emitting code
// to fn and returning the Value defined by the expression.
//
func (b *builder) expr(fn *Function, e ast.Expr) Value {
e = unparen(e)
tv := fn.Pkg.info.Types[e]
// Is expression a constant?
if tv.Value != nil {
return emitConst(fn, NewConst(tv.Value, tv.Type))
}
var v Value
if tv.Addressable() {
// Prefer pointer arithmetic ({Index,Field}Addr) followed
// by Load over subelement extraction (e.g. Index, Field),
// to avoid large copies.
v = b.addr(fn, e, false).load(fn, e)
} else {
v = b.expr0(fn, e, tv)
}
if fn.debugInfo() {
emitDebugRef(fn, e, v, false)
}
return v
}
func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
switch e := e.(type) {
case *ast.BasicLit:
panic("non-constant BasicLit") // unreachable
case *ast.FuncLit:
fn2 := &Function{
name: fmt.Sprintf("%s$%d", fn.Name(), 1+len(fn.AnonFuncs)),
Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature),
parent: fn,
Pkg: fn.Pkg,
Prog: fn.Prog,
functionBody: new(functionBody),
}
fn2.source = e
fn.AnonFuncs = append(fn.AnonFuncs, fn2)
fn2.initHTML(b.printFunc)
b.buildFunction(fn2)
if fn2.FreeVars == nil {
return fn2
}
v := &MakeClosure{Fn: fn2}
v.setType(tv.Type)
for _, fv := range fn2.FreeVars {
v.Bindings = append(v.Bindings, fv.outer)
fv.outer = nil
}
return fn.emit(v, e)
case *ast.TypeAssertExpr: // single-result form only
return emitTypeAssert(fn, b.expr(fn, e.X), tv.Type, e)
case *ast.CallExpr:
if fn.Pkg.info.Types[e.Fun].IsType() {
// Explicit type conversion, e.g. string(x) or big.Int(x)
x := b.expr(fn, e.Args[0])
y := emitConv(fn, x, tv.Type, e)
return y
}
// Call to "intrinsic" built-ins, e.g. new, make, panic.
if id, ok := unparen(e.Fun).(*ast.Ident); ok {
if obj, ok := fn.Pkg.info.Uses[id].(*types.Builtin); ok {
if v := b.builtin(fn, obj, e.Args, tv.Type, e); v != nil {
return v
}
}
}
// Regular function call.
var v Call
b.setCall(fn, e, &v.Call)
v.setType(tv.Type)
return fn.emit(&v, e)
case *ast.UnaryExpr:
switch e.Op {
case token.AND: // &X --- potentially escaping.
addr := b.addr(fn, e.X, true)
if _, ok := unparen(e.X).(*ast.StarExpr); ok {
// &*p must panic if p is nil (http://golang.org/s/go12nil).
// For simplicity, we'll just (suboptimally) rely
// on the side effects of a load.
// TODO(adonovan): emit dedicated nilcheck.
addr.load(fn, e)
}
return addr.address(fn)
case token.ADD:
return b.expr(fn, e.X)
case token.NOT, token.SUB, token.XOR: // ! <- - ^
v := &UnOp{
Op: e.Op,
X: b.expr(fn, e.X),
}
v.setType(tv.Type)
return fn.emit(v, e)
case token.ARROW:
return emitRecv(fn, b.expr(fn, e.X), false, tv.Type, e)
default:
panic(e.Op)
}
case *ast.BinaryExpr:
switch e.Op {
case token.LAND, token.LOR:
return b.logicalBinop(fn, e)
case token.SHL, token.SHR:
fallthrough
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), tv.Type, e)
case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ:
cmp := emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e)
// The type of x==y may be UntypedBool.
return emitConv(fn, cmp, types.Default(tv.Type), e)
default:
panic("illegal op in BinaryExpr: " + e.Op.String())
}
case *ast.SliceExpr:
var low, high, max Value
var x Value
switch fn.Pkg.typeOf(e.X).Underlying().(type) {
case *types.Array:
// Potentially escaping.
x = b.addr(fn, e.X, true).address(fn)
case *types.Basic, *types.Slice, *types.Pointer: // *array
x = b.expr(fn, e.X)
default:
panic("unreachable")
}
if e.High != nil {
high = b.expr(fn, e.High)
}
if e.Low != nil {
low = b.expr(fn, e.Low)
}
if e.Slice3 {
max = b.expr(fn, e.Max)
}
v := &Slice{
X: x,
Low: low,
High: high,
Max: max,
}
v.setType(tv.Type)
return fn.emit(v, e)
case *ast.Ident:
obj := fn.Pkg.info.Uses[e]
// Universal built-in or nil?
switch obj := obj.(type) {
case *types.Builtin:
return &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)}
case *types.Nil:
return emitConst(fn, nilConst(tv.Type))
}
// Package-level func or var?
if v := fn.Prog.packageLevelValue(obj); v != nil {
if _, ok := obj.(*types.Var); ok {
return emitLoad(fn, v, e) // var (address)
}
return v // (func)
}
// Local var.
return emitLoad(fn, fn.lookup(obj, false), e) // var (address)
case *ast.SelectorExpr:
sel, ok := fn.Pkg.info.Selections[e]
if !ok {
// qualified identifier
return b.expr(fn, e.Sel)
}
switch sel.Kind() {
case types.MethodExpr:
// (*T).f or T.f, the method f from the method-set of type T.
// The result is a "thunk".
return emitConv(fn, makeThunk(fn.Prog, sel), tv.Type, e)
case types.MethodVal:
// e.f where e is an expression and f is a method.
// The result is a "bound".
obj := sel.Obj().(*types.Func)
rt := recvType(obj)
wantAddr := isPointer(rt)
escaping := true
v := b.receiver(fn, e.X, wantAddr, escaping, sel, e)
if isInterface(rt) {
// If v has interface type I,
// we must emit a check that v is non-nil.
// We use: typeassert v.(I).
emitTypeAssert(fn, v, rt, e)
}
c := &MakeClosure{
Fn: makeBound(fn.Prog, obj),
Bindings: []Value{v},
}
c.source = e.Sel
c.setType(tv.Type)
return fn.emit(c, e)
case types.FieldVal:
indices := sel.Index()
last := len(indices) - 1
v := b.expr(fn, e.X)
v = emitImplicitSelections(fn, v, indices[:last], e)
v = emitFieldSelection(fn, v, indices[last], false, e.Sel)
return v
}
panic("unexpected expression-relative selector")
case *ast.IndexExpr:
switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
case *types.Array:
// Non-addressable array (in a register).
v := &Index{
X: b.expr(fn, e.X),
Index: emitConv(fn, b.expr(fn, e.Index), tInt, e.Index),
}
v.setType(t.Elem())
return fn.emit(v, e)
case *types.Map:
// Maps are not addressable.
mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
v := &MapLookup{
X: b.expr(fn, e.X),
Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key(), e.Index),
}
v.setType(mapt.Elem())
return fn.emit(v, e)
case *types.Basic: // => string
// Strings are not addressable.
v := &StringLookup{
X: b.expr(fn, e.X),
Index: b.expr(fn, e.Index),
}
v.setType(tByte)
return fn.emit(v, e)
case *types.Slice, *types.Pointer: // *array
// Addressable slice/array; use IndexAddr and Load.
return b.addr(fn, e, false).load(fn, e)
default:
panic("unexpected container type in IndexExpr: " + t.String())
}
case *ast.CompositeLit, *ast.StarExpr:
// Addressable types (lvalues)
return b.addr(fn, e, false).load(fn, e)
}
panic(fmt.Sprintf("unexpected expr: %T", e))
}
// stmtList emits to fn code for all statements in list.
func (b *builder) stmtList(fn *Function, list []ast.Stmt) {
for _, s := range list {
b.stmt(fn, s)
}
}
// receiver emits to fn code for expression e in the "receiver"
// position of selection e.f (where f may be a field or a method) and
// returns the effective receiver after applying the implicit field
// selections of sel.
//
// wantAddr requests that the result is an an address. If
// !sel.Indirect(), this may require that e be built in addr() mode; it
// must thus be addressable.
//
// escaping is defined as per builder.addr().
//
func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *types.Selection, source ast.Node) Value {
var v Value
if wantAddr && !sel.Indirect() && !isPointer(fn.Pkg.typeOf(e)) {
v = b.addr(fn, e, escaping).address(fn)
} else {
v = b.expr(fn, e)
}
last := len(sel.Index()) - 1
v = emitImplicitSelections(fn, v, sel.Index()[:last], source)
if !wantAddr && isPointer(v.Type()) {
v = emitLoad(fn, v, e)
}
return v
}
// setCallFunc populates the function parts of a CallCommon structure
// (Func, Method, Recv, Args[0]) based on the kind of invocation
// occurring in e.
//
func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
// Is this a method call?
if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {
sel, ok := fn.Pkg.info.Selections[selector]
if ok && sel.Kind() == types.MethodVal {
obj := sel.Obj().(*types.Func)
recv := recvType(obj)
wantAddr := isPointer(recv)
escaping := true
v := b.receiver(fn, selector.X, wantAddr, escaping, sel, selector)
if isInterface(recv) {
// Invoke-mode call.
c.Value = v
c.Method = obj
} else {
// "Call"-mode call.
c.Value = fn.Prog.declaredFunc(obj)
c.Args = append(c.Args, v)
}
return
}
// sel.Kind()==MethodExpr indicates T.f() or (*T).f():
// a statically dispatched call to the method f in the
// method-set of T or *T. T may be an interface.
//
// e.Fun would evaluate to a concrete method, interface
// wrapper function, or promotion wrapper.
//
// For now, we evaluate it in the usual way.
//
// TODO(adonovan): opt: inline expr() here, to make the
// call static and to avoid generation of wrappers.
// It's somewhat tricky as it may consume the first
// actual parameter if the call is "invoke" mode.
//
// Examples:
// type T struct{}; func (T) f() {} // "call" mode
// type T interface { f() } // "invoke" mode
//
// type S struct{ T }
//
// var s S
// S.f(s)
// (*S).f(&s)
//
// Suggested approach:
// - consume the first actual parameter expression
// and build it with b.expr().
// - apply implicit field selections.
// - use MethodVal logic to populate fields of c.
}
// Evaluate the function operand in the usual way.
c.Value = b.expr(fn, e.Fun)
}
// emitCallArgs emits to f code for the actual parameters of call e to
// a (possibly built-in) function of effective type sig.
// The argument values are appended to args, which is then returned.
//
func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallExpr, args []Value) []Value {
// f(x, y, z...): pass slice z straight through.
if e.Ellipsis != 0 {
for i, arg := range e.Args {
v := emitConv(fn, b.expr(fn, arg), sig.Params().At(i).Type(), arg)
args = append(args, v)
}
return args
}
offset := len(args) // 1 if call has receiver, 0 otherwise
// Evaluate actual parameter expressions.
//
// If this is a chained call of the form f(g()) where g has
// multiple return values (MRV), they are flattened out into
// args; a suffix of them may end up in a varargs slice.
for _, arg := range e.Args {
v := b.expr(fn, arg)
if ttuple, ok := v.Type().(*types.Tuple); ok { // MRV chain
for i, n := 0, ttuple.Len(); i < n; i++ {
args = append(args, emitExtract(fn, v, i, arg))
}
} else {
args = append(args, v)
}
}
// Actual->formal assignability conversions for normal parameters.
np := sig.Params().Len() // number of normal parameters
if sig.Variadic() {
np--
}
for i := 0; i < np; i++ {
args[offset+i] = emitConv(fn, args[offset+i], sig.Params().At(i).Type(), args[offset+i].Source())
}
// Actual->formal assignability conversions for variadic parameter,
// and construction of slice.
if sig.Variadic() {
varargs := args[offset+np:]
st := sig.Params().At(np).Type().(*types.Slice)
vt := st.Elem()
if len(varargs) == 0 {
args = append(args, emitConst(fn, nilConst(st)))
} else {
// Replace a suffix of args with a slice containing it.
at := types.NewArray(vt, int64(len(varargs)))
a := emitNew(fn, at, e)
a.source = e
for i, arg := range varargs {
iaddr := &IndexAddr{
X: a,
Index: emitConst(fn, intConst(int64(i))),
}
iaddr.setType(types.NewPointer(vt))
fn.emit(iaddr, e)
emitStore(fn, iaddr, arg, arg.Source())
}
s := &Slice{X: a}
s.setType(st)
args[offset+np] = fn.emit(s, args[offset+np].Source())
args = args[:offset+np+1]
}
}
return args
}
// setCall emits to fn code to evaluate all the parameters of a function
// call e, and populates *c with those values.
//
func (b *builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {
// First deal with the f(...) part and optional receiver.
b.setCallFunc(fn, e, c)
// Then append the other actual parameters.
sig, _ := fn.Pkg.typeOf(e.Fun).Underlying().(*types.Signature)
if sig == nil {
panic(fmt.Sprintf("no signature for call of %s", e.Fun))
}
c.Args = b.emitCallArgs(fn, sig, e, c.Args)
}
// assignOp emits to fn code to perform loc <op>= val.
func (b *builder) assignOp(fn *Function, loc lvalue, val Value, op token.Token, source ast.Node) {
oldv := loc.load(fn, source)
loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, val, oldv.Type(), source), loc.typ(), source), source)
}
// localValueSpec emits to fn code to define all of the vars in the
// function-local ValueSpec, spec.
//
func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
switch {
case len(spec.Values) == len(spec.Names):
// e.g. var x, y = 0, 1
// 1:1 assignment
for i, id := range spec.Names {
if !isBlankIdent(id) {
fn.addLocalForIdent(id)
}
lval := b.addr(fn, id, false) // non-escaping
b.assign(fn, lval, spec.Values[i], true, nil, spec)
}
case len(spec.Values) == 0:
// e.g. var x, y int
// Locals are implicitly zero-initialized.
for _, id := range spec.Names {
if !isBlankIdent(id) {
lhs := fn.addLocalForIdent(id)
if fn.debugInfo() {
emitDebugRef(fn, id, lhs, true)
}
}
}
default:
// e.g. var x, y = pos()
tuple := b.exprN(fn, spec.Values[0])
for i, id := range spec.Names {
if !isBlankIdent(id) {
fn.addLocalForIdent(id)
lhs := b.addr(fn, id, false) // non-escaping
lhs.store(fn, emitExtract(fn, tuple, i, id), id)
}
}
}
}
// assignStmt emits code to fn for a parallel assignment of rhss to lhss.
// isDef is true if this is a short variable declaration (:=).
//
// Note the similarity with localValueSpec.
//
func (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool, source ast.Node) {
// Side effects of all LHSs and RHSs must occur in left-to-right order.
lvals := make([]lvalue, len(lhss))
isZero := make([]bool, len(lhss))
for i, lhs := range lhss {
var lval lvalue = blank{}
if !isBlankIdent(lhs) {
if isDef {
if obj := fn.Pkg.info.Defs[lhs.(*ast.Ident)]; obj != nil {
fn.addNamedLocal(obj, lhs)
isZero[i] = true
}
}
lval = b.addr(fn, lhs, false) // non-escaping
}
lvals[i] = lval
}
if len(lhss) == len(rhss) {
// Simple assignment: x = f() (!isDef)
// Parallel assignment: x, y = f(), g() (!isDef)
// or short var decl: x, y := f(), g() (isDef)
//
// In all cases, the RHSs may refer to the LHSs,
// so we need a storebuf.
var sb storebuf
for i := range rhss {
b.assign(fn, lvals[i], rhss[i], isZero[i], &sb, source)
}
sb.emit(fn)
} else {
// e.g. x, y = pos()
tuple := b.exprN(fn, rhss[0])
emitDebugRef(fn, rhss[0], tuple, false)
for i, lval := range lvals {
lval.store(fn, emitExtract(fn, tuple, i, source), source)
}
}
}
// arrayLen returns the length of the array whose composite literal elements are elts.
func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
var max int64 = -1
var i int64 = -1
for _, e := range elts {
if kv, ok := e.(*ast.KeyValueExpr); ok {
i = b.expr(fn, kv.Key).(*Const).Int64()
} else {
i++
}
if i > max {
max = i
}
}
return max + 1
}
// compLit emits to fn code to initialize a composite literal e at
// address addr with type typ.
//
// Nested composite literals are recursively initialized in place
// where possible. If isZero is true, compLit assumes that addr
// holds the zero value for typ.
//
// Because the elements of a composite literal may refer to the
// variables being updated, as in the second line below,
// x := T{a: 1}
// x = T{a: x.a}
// all the reads must occur before all the writes. Thus all stores to
// loc are emitted to the storebuf sb for later execution.
//
// A CompositeLit may have pointer type only in the recursive (nested)
// case when the type name is implicit. e.g. in []*T{{}}, the inner
// literal has type *T behaves like &T{}.
// In that case, addr must hold a T, not a *T.
//
func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {
typ := deref(fn.Pkg.typeOf(e))
switch t := typ.Underlying().(type) {
case *types.Struct:
if !isZero && len(e.Elts) != t.NumFields() {
// memclear
sb.store(&address{addr, nil}, zeroValue(fn, deref(addr.Type()), e), e)
isZero = true
}
for i, e := range e.Elts {
fieldIndex := i
if kv, ok := e.(*ast.KeyValueExpr); ok {
fname := kv.Key.(*ast.Ident).Name
for i, n := 0, t.NumFields(); i < n; i++ {
sf := t.Field(i)
if sf.Name() == fname {
fieldIndex = i
e = kv.Value
break
}
}
}
sf := t.Field(fieldIndex)
faddr := &FieldAddr{
X: addr,
Field: fieldIndex,
}
faddr.setType(types.NewPointer(sf.Type()))
fn.emit(faddr, e)
b.assign(fn, &address{addr: faddr, expr: e}, e, isZero, sb, e)
}
case *types.Array, *types.Slice:
var at *types.Array
var array Value
switch t := t.(type) {
case *types.Slice:
at = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts))
alloc := emitNew(fn, at, e)
array = alloc
case *types.Array:
at = t
array = addr
if !isZero && int64(len(e.Elts)) != at.Len() {
// memclear
sb.store(&address{array, nil}, zeroValue(fn, deref(array.Type()), e), e)
}
}
var idx *Const
for _, e := range e.Elts {
if kv, ok := e.(*ast.KeyValueExpr); ok {
idx = b.expr(fn, kv.Key).(*Const)
e = kv.Value
} else {
var idxval int64
if idx != nil {
idxval = idx.Int64() + 1
}
idx = emitConst(fn, intConst(idxval))
}
iaddr := &IndexAddr{
X: array,
Index: idx,
}
iaddr.setType(types.NewPointer(at.Elem()))
fn.emit(iaddr, e)
if t != at { // slice
// backing array is unaliased => storebuf not needed.
b.assign(fn, &address{addr: iaddr, expr: e}, e, true, nil, e)
} else {
b.assign(fn, &address{addr: iaddr, expr: e}, e, true, sb, e)
}
}
if t != at { // slice
s := &Slice{X: array}
s.setType(typ)
sb.store(&address{addr: addr, expr: e}, fn.emit(s, e), e)
}
case *types.Map:
m := &MakeMap{Reserve: emitConst(fn, intConst(int64(len(e.Elts))))}
m.setType(typ)
fn.emit(m, e)
for _, e := range e.Elts {
e := e.(*ast.KeyValueExpr)
// If a key expression in a map literal is itself a
// composite literal, the type may be omitted.
// For example:
// map[*struct{}]bool{{}: true}
// An &-operation may be implied:
// map[*struct{}]bool{&struct{}{}: true}
var key Value
if _, ok := unparen(e.Key).(*ast.CompositeLit); ok && isPointer(t.Key()) {
// A CompositeLit never evaluates to a pointer,
// so if the type of the location is a pointer,
// an &-operation is implied.
key = b.addr(fn, e.Key, true).address(fn)
} else {
key = b.expr(fn, e.Key)
}
loc := element{
m: m,
k: emitConv(fn, key, t.Key(), e),
t: t.Elem(),
}
// We call assign() only because it takes care
// of any &-operation required in the recursive
// case, e.g.,
// map[int]*struct{}{0: {}} implies &struct{}{}.
// In-place update is of course impossible,
// and no storebuf is needed.
b.assign(fn, &loc, e.Value, true, nil, e)
}
sb.store(&address{addr: addr, expr: e}, m, e)
default:
panic("unexpected CompositeLit type: " + t.String())
}
}
func (b *builder) switchStmt(fn *Function, s *ast.SwitchStmt, label *lblock) {
if s.Tag == nil {
b.switchStmtDynamic(fn, s, label)
return
}
dynamic := false
for _, iclause := range s.Body.List {
clause := iclause.(*ast.CaseClause)
for _, cond := range clause.List {
if fn.Pkg.info.Types[unparen(cond)].Value == nil {
dynamic = true
break
}
}
}
if dynamic {
b.switchStmtDynamic(fn, s, label)
return
}
if s.Init != nil {
b.stmt(fn, s.Init)
}
entry := fn.currentBlock
tag := b.expr(fn, s.Tag)
heads := make([]*BasicBlock, 0, len(s.Body.List))
bodies := make([]*BasicBlock, len(s.Body.List))
conds := make([]Value, 0, len(s.Body.List))
hasDefault := false
done := fn.newBasicBlock(fmt.Sprintf("switch.done"))
if label != nil {
label._break = done
}
for i, stmt := range s.Body.List {
body := fn.newBasicBlock(fmt.Sprintf("switch.body.%d", i))
bodies[i] = body
cas := stmt.(*ast.CaseClause)
if cas.List == nil {
// default branch
hasDefault = true
head := fn.newBasicBlock(fmt.Sprintf("switch.head.%d", i))
conds = append(conds, nil)
heads = append(heads, head)
fn.currentBlock = head
emitJump(fn, body, cas)
}
for j, cond := range stmt.(*ast.CaseClause).List {
fn.currentBlock = entry
head := fn.newBasicBlock(fmt.Sprintf("switch.head.%d.%d", i, j))
conds = append(conds, b.expr(fn, cond))
heads = append(heads, head)
fn.currentBlock = head
emitJump(fn, body, cond)
}
}
for i, stmt := range s.Body.List {
clause := stmt.(*ast.CaseClause)
body := bodies[i]
fn.currentBlock = body
fallthru := done
if i+1 < len(bodies) {
fallthru = bodies[i+1]
}
fn.targets = &targets{
tail: fn.targets,
_break: done,
_fallthrough: fallthru,
}
b.stmtList(fn, clause.Body)
fn.targets = fn.targets.tail
emitJump(fn, done, stmt)
}
if !hasDefault {
head := fn.newBasicBlock(fmt.Sprintf("switch.head.implicit-default"))
body := fn.newBasicBlock("switch.body.implicit-default")
fn.currentBlock = head
emitJump(fn, body, s)
fn.currentBlock = body
emitJump(fn, done, s)
heads = append(heads, head)
conds = append(conds, nil)
}
if len(heads) != len(conds) {
panic(fmt.Sprintf("internal error: %d heads for %d conds", len(heads), len(conds)))
}
for _, head := range heads {
addEdge(entry, head)
}
fn.currentBlock = entry
entry.emit(&ConstantSwitch{
Tag: tag,
Conds: conds,
}, s)
fn.currentBlock = done
}
// switchStmt emits to fn code for the switch statement s, optionally
// labelled by label.
//
func (b *builder) switchStmtDynamic(fn *Function, s *ast.SwitchStmt, label *lblock) {
// We treat SwitchStmt like a sequential if-else chain.
// Multiway dispatch can be recovered later by irutil.Switches()
// to those cases that are free of side effects.
if s.Init != nil {
b.stmt(fn, s.Init)
}
kTrue := emitConst(fn, NewConst(constant.MakeBool(true), tBool))
var tagv Value = kTrue
var tagSource ast.Node = s
if s.Tag != nil {
tagv = b.expr(fn, s.Tag)
tagSource = s.Tag
}
// lifting only considers loads and stores, but we want different
// sigma nodes for the different comparisons. use a temporary and
// load it in every branch.
tag := fn.addLocal(tagv.Type(), tagSource)
emitStore(fn, tag, tagv, tagSource)
done := fn.newBasicBlock("switch.done")
if label != nil {
label._break = done
}
// We pull the default case (if present) down to the end.
// But each fallthrough label must point to the next
// body block in source order, so we preallocate a
// body block (fallthru) for the next case.
// Unfortunately this makes for a confusing block order.
var dfltBody *[]ast.Stmt
var dfltFallthrough *BasicBlock
var fallthru, dfltBlock *BasicBlock
ncases := len(s.Body.List)
for i, clause := range s.Body.List {
body := fallthru
if body == nil {
body = fn.newBasicBlock("switch.body") // first case only
}
// Preallocate body block for the next case.
fallthru = done
if i+1 < ncases {
fallthru = fn.newBasicBlock("switch.body")
}
cc := clause.(*ast.CaseClause)
if cc.List == nil {
// Default case.
dfltBody = &cc.Body
dfltFallthrough = fallthru
dfltBlock = body
continue
}
var nextCond *BasicBlock
for _, cond := range cc.List {
nextCond = fn.newBasicBlock("switch.next")
if tagv == kTrue {
// emit a proper if/else chain instead of a comparison
// of a value against true.
//
// NOTE(dh): adonovan had a todo saying "don't forget
// conversions though". As far as I can tell, there
// aren't any conversions that we need to take care of
// here. `case bool(a) && bool(b)` as well as `case
// bool(a && b)` are being taken care of by b.cond,
// and `case a` where a is not of type bool is
// invalid.
b.cond(fn, cond, body, nextCond)
} else {
cond := emitCompare(fn, token.EQL, emitLoad(fn, tag, cond), b.expr(fn, cond), cond)
emitIf(fn, cond, body, nextCond, cond.Source())
}
fn.currentBlock = nextCond
}
fn.currentBlock = body
fn.targets = &targets{
tail: fn.targets,
_break: done,
_fallthrough: fallthru,
}
b.stmtList(fn, cc.Body)
fn.targets = fn.targets.tail
emitJump(fn, done, s)
fn.currentBlock = nextCond
}
if dfltBlock != nil {
// The lack of a Source for the jump doesn't matter, block
// fusing will get rid of the jump later.
emitJump(fn, dfltBlock, s)
fn.currentBlock = dfltBlock
fn.targets = &targets{
tail: fn.targets,
_break: done,
_fallthrough: dfltFallthrough,
}
b.stmtList(fn, *dfltBody)
fn.targets = fn.targets.tail
}
emitJump(fn, done, s)
fn.currentBlock = done
}
func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lblock) {
if s.Init != nil {
b.stmt(fn, s.Init)
}
var tag Value
switch e := s.Assign.(type) {
case *ast.ExprStmt: // x.(type)
tag = b.expr(fn, unparen(e.X).(*ast.TypeAssertExpr).X)
case *ast.AssignStmt: // y := x.(type)
tag = b.expr(fn, unparen(e.Rhs[0]).(*ast.TypeAssertExpr).X)
default:
panic("unreachable")
}
tagPtr := fn.addLocal(tag.Type(), tag.Source())
emitStore(fn, tagPtr, tag, tag.Source())
// +1 in case there's no explicit default case
heads := make([]*BasicBlock, 0, len(s.Body.List)+1)
entry := fn.currentBlock
done := fn.newBasicBlock("done")
if label != nil {
label._break = done
}
// set up type switch and constant switch, populate their conditions
tswtch := &TypeSwitch{
Tag: emitLoad(fn, tagPtr, tag.Source()),
Conds: make([]types.Type, 0, len(s.Body.List)+1),
}
cswtch := &ConstantSwitch{
Conds: make([]Value, 0, len(s.Body.List)+1),
}
rets := make([]types.Type, 0, len(s.Body.List)+1)
index := 0
var default_ *ast.CaseClause
for _, clause := range s.Body.List {
cc := clause.(*ast.CaseClause)
if obj := fn.Pkg.info.Implicits[cc]; obj != nil {
fn.addNamedLocal(obj, cc)
}
if cc.List == nil {
// default case
default_ = cc
} else {
for _, expr := range cc.List {
tswtch.Conds = append(tswtch.Conds, fn.Pkg.typeOf(expr))
cswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(index))))
index++
}
if len(cc.List) == 1 {
rets = append(rets, fn.Pkg.typeOf(cc.List[0]))
} else {
for range cc.List {
rets = append(rets, tag.Type())
}
}
}
}
// default branch
rets = append(rets, tag.Type())
var vars []*types.Var
vars = append(vars, varIndex)
for _, typ := range rets {
vars = append(vars, anonVar(typ))
}
tswtch.setType(types.NewTuple(vars...))
// default branch
fn.currentBlock = entry
fn.emit(tswtch, s)
cswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(-1))))
// in theory we should add a local and stores/loads for tswtch, to
// generate sigma nodes in the branches. however, there isn't any
// useful information we could possibly attach to it.
cswtch.Tag = emitExtract(fn, tswtch, 0, s)
fn.emit(cswtch, s)
// build heads and bodies
index = 0
for _, clause := range s.Body.List {
cc := clause.(*ast.CaseClause)
if cc.List == nil {
continue
}
body := fn.newBasicBlock("typeswitch.body")
for _, expr := range cc.List {
head := fn.newBasicBlock("typeswitch.head")
heads = append(heads, head)
fn.currentBlock = head
if obj := fn.Pkg.info.Implicits[cc]; obj != nil {
// In a switch y := x.(type), each case clause
// implicitly declares a distinct object y.
// In a single-type case, y has that type.
// In multi-type cases, 'case nil' and default,
// y has the same type as the interface operand.
l := fn.objects[obj]
if rets[index] == tUntypedNil {
emitStore(fn, l, emitConst(fn, nilConst(tswtch.Tag.Type())), s.Assign)
} else {
x := emitExtract(fn, tswtch, index+1, s.Assign)
emitStore(fn, l, x, nil)
}
}
emitJump(fn, body, expr)
index++
}
fn.currentBlock = body
fn.targets = &targets{
tail: fn.targets,
_break: done,
}
b.stmtList(fn, cc.Body)
fn.targets = fn.targets.tail
emitJump(fn, done, clause)
}
if default_ == nil {
// implicit default
heads = append(heads, done)
} else {
body := fn.newBasicBlock("typeswitch.default")
heads = append(heads, body)
fn.currentBlock = body
fn.targets = &targets{
tail: fn.targets,
_break: done,
}
if obj := fn.Pkg.info.Implicits[default_]; obj != nil {
l := fn.objects[obj]
x := emitExtract(fn, tswtch, index+1, s.Assign)
emitStore(fn, l, x, s)
}
b.stmtList(fn, default_.Body)
fn.targets = fn.targets.tail
emitJump(fn, done, s)
}
fn.currentBlock = entry
for _, head := range heads {
addEdge(entry, head)
}
fn.currentBlock = done
}
// selectStmt emits to fn code for the select statement s, optionally
// labelled by label.
//
func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (noreturn bool) {
if len(s.Body.List) == 0 {
instr := &Select{Blocking: true}
instr.setType(types.NewTuple(varIndex, varOk))
fn.emit(instr, s)
fn.emit(new(Unreachable), s)
addEdge(fn.currentBlock, fn.Exit)
return true
}
// A blocking select of a single case degenerates to a
// simple send or receive.
// TODO(adonovan): opt: is this optimization worth its weight?
if len(s.Body.List) == 1 {
clause := s.Body.List[0].(*ast.CommClause)
if clause.Comm != nil {
b.stmt(fn, clause.Comm)
done := fn.newBasicBlock("select.done")
if label != nil {
label._break = done
}
fn.targets = &targets{
tail: fn.targets,
_break: done,
}
b.stmtList(fn, clause.Body)
fn.targets = fn.targets.tail
emitJump(fn, done, clause)
fn.currentBlock = done
return false
}
}
// First evaluate all channels in all cases, and find
// the directions of each state.
var states []*SelectState
blocking := true
debugInfo := fn.debugInfo()
for _, clause := range s.Body.List {
var st *SelectState
switch comm := clause.(*ast.CommClause).Comm.(type) {
case nil: // default case
blocking = false
continue
case *ast.SendStmt: // ch<- i
ch := b.expr(fn, comm.Chan)
st = &SelectState{
Dir: types.SendOnly,
Chan: ch,
Send: emitConv(fn, b.expr(fn, comm.Value),
ch.Type().Underlying().(*types.Chan).Elem(), comm),
Pos: comm.Arrow,
}
if debugInfo {
st.DebugNode = comm
}
case *ast.AssignStmt: // x := <-ch
recv := unparen(comm.Rhs[0]).(*ast.UnaryExpr)
st = &SelectState{
Dir: types.RecvOnly,
Chan: b.expr(fn, recv.X),
Pos: recv.OpPos,
}
if debugInfo {
st.DebugNode = recv
}
case *ast.ExprStmt: // <-ch
recv := unparen(comm.X).(*ast.UnaryExpr)
st = &SelectState{
Dir: types.RecvOnly,
Chan: b.expr(fn, recv.X),
Pos: recv.OpPos,
}
if debugInfo {
st.DebugNode = recv
}
}
states = append(states, st)
}
// We dispatch on the (fair) result of Select using a
// switch on the returned index.
sel := &Select{
States: states,
Blocking: blocking,
}
sel.source = s
var vars []*types.Var
vars = append(vars, varIndex, varOk)
for _, st := range states {
if st.Dir == types.RecvOnly {
tElem := st.Chan.Type().Underlying().(*types.Chan).Elem()
vars = append(vars, anonVar(tElem))
}
}
sel.setType(types.NewTuple(vars...))
fn.emit(sel, s)
idx := emitExtract(fn, sel, 0, s)
done := fn.newBasicBlock("select.done")
if label != nil {
label._break = done
}
entry := fn.currentBlock
swtch := &ConstantSwitch{
Tag: idx,
// one condition per case
Conds: make([]Value, 0, len(s.Body.List)+1),
}
// note that we don't need heads; a select case can only have a single condition
var bodies []*BasicBlock
state := 0
r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV
for _, cc := range s.Body.List {
clause := cc.(*ast.CommClause)
if clause.Comm == nil {
body := fn.newBasicBlock("select.default")
fn.currentBlock = body
bodies = append(bodies, body)
fn.targets = &targets{
tail: fn.targets,
_break: done,
}
b.stmtList(fn, clause.Body)
emitJump(fn, done, s)
fn.targets = fn.targets.tail
swtch.Conds = append(swtch.Conds, emitConst(fn, intConst(-1)))
continue
}
swtch.Conds = append(swtch.Conds, emitConst(fn, intConst(int64(state))))
body := fn.newBasicBlock("select.body")
fn.currentBlock = body
bodies = append(bodies, body)
fn.targets = &targets{
tail: fn.targets,
_break: done,
}
switch comm := clause.Comm.(type) {
case *ast.ExprStmt: // <-ch
if debugInfo {
v := emitExtract(fn, sel, r, comm)
emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
}
r++
case *ast.AssignStmt: // x := <-states[state].Chan
if comm.Tok == token.DEFINE {
fn.addLocalForIdent(comm.Lhs[0].(*ast.Ident))
}
x := b.addr(fn, comm.Lhs[0], false) // non-escaping
v := emitExtract(fn, sel, r, comm)
if debugInfo {
emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
}
x.store(fn, v, comm)
if len(comm.Lhs) == 2 { // x, ok := ...
if comm.Tok == token.DEFINE {
fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident))
}
ok := b.addr(fn, comm.Lhs[1], false) // non-escaping
ok.store(fn, emitExtract(fn, sel, 1, comm), comm)
}
r++
}
b.stmtList(fn, clause.Body)
fn.targets = fn.targets.tail
emitJump(fn, done, s)
state++
}
fn.currentBlock = entry
fn.emit(swtch, s)
for _, body := range bodies {
addEdge(entry, body)
}
fn.currentBlock = done
return false
}
// forStmt emits to fn code for the for statement s, optionally
// labelled by label.
//
func (b *builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) {
// ...init...
// jump loop
// loop:
// if cond goto body else done
// body:
// ...body...
// jump post
// post: (target of continue)
// ...post...
// jump loop
// done: (target of break)
if s.Init != nil {
b.stmt(fn, s.Init)
}
body := fn.newBasicBlock("for.body")
done := fn.newBasicBlock("for.done") // target of 'break'
loop := body // target of back-edge
if s.Cond != nil {
loop = fn.newBasicBlock("for.loop")
}
cont := loop // target of 'continue'
if s.Post != nil {
cont = fn.newBasicBlock("for.post")
}
if label != nil {
label._break = done
label._continue = cont
}
emitJump(fn, loop, s)
fn.currentBlock = loop
if loop != body {
b.cond(fn, s.Cond, body, done)
fn.currentBlock = body
}
fn.targets = &targets{
tail: fn.targets,
_break: done,
_continue: cont,
}
b.stmt(fn, s.Body)
fn.targets = fn.targets.tail
emitJump(fn, cont, s)
if s.Post != nil {
fn.currentBlock = cont
b.stmt(fn, s.Post)
emitJump(fn, loop, s) // back-edge
}
fn.currentBlock = done
}
// rangeIndexed emits to fn the header for an integer-indexed loop
// over array, *array or slice value x.
// The v result is defined only if tv is non-nil.
// forPos is the position of the "for" token.
//
func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, source ast.Node) (k, v Value, loop, done *BasicBlock) {
//
// length = len(x)
// index = -1
// loop: (target of continue)
// index++
// if index < length goto body else done
// body:
// k = index
// v = x[index]
// ...body...
// jump loop
// done: (target of break)
// Determine number of iterations.
var length Value
if arr, ok := deref(x.Type()).Underlying().(*types.Array); ok {
// For array or *array, the number of iterations is
// known statically thanks to the type. We avoid a
// data dependence upon x, permitting later dead-code
// elimination if x is pure, static unrolling, etc.
// Ranging over a nil *array may have >0 iterations.
// We still generate code for x, in case it has effects.
length = emitConst(fn, intConst(arr.Len()))
} else {
// length = len(x).
var c Call
c.Call.Value = makeLen(x.Type())
c.Call.Args = []Value{x}
c.setType(tInt)
length = fn.emit(&c, source)
}
index := fn.addLocal(tInt, source)
emitStore(fn, index, emitConst(fn, intConst(-1)), source)
loop = fn.newBasicBlock("rangeindex.loop")
emitJump(fn, loop, source)
fn.currentBlock = loop
incr := &BinOp{
Op: token.ADD,
X: emitLoad(fn, index, source),
Y: emitConst(fn, intConst(1)),
}
incr.setType(tInt)
emitStore(fn, index, fn.emit(incr, source), source)
body := fn.newBasicBlock("rangeindex.body")
done = fn.newBasicBlock("rangeindex.done")
emitIf(fn, emitCompare(fn, token.LSS, incr, length, source), body, done, source)
fn.currentBlock = body
k = emitLoad(fn, index, source)
if tv != nil {
switch t := x.Type().Underlying().(type) {
case *types.Array:
instr := &Index{
X: x,
Index: k,
}
instr.setType(t.Elem())
v = fn.emit(instr, source)
case *types.Pointer: // *array
instr := &IndexAddr{
X: x,
Index: k,
}
instr.setType(types.NewPointer(t.Elem().Underlying().(*types.Array).Elem()))
v = emitLoad(fn, fn.emit(instr, source), source)
case *types.Slice:
instr := &IndexAddr{
X: x,
Index: k,
}
instr.setType(types.NewPointer(t.Elem()))
v = emitLoad(fn, fn.emit(instr, source), source)
default:
panic("rangeIndexed x:" + t.String())
}
}
return
}
// rangeIter emits to fn the header for a loop using
// Range/Next/Extract to iterate over map or string value x.
// tk and tv are the types of the key/value results k and v, or nil
// if the respective component is not wanted.
//
func (b *builder) rangeIter(fn *Function, x Value, tk, tv types.Type, source ast.Node) (k, v Value, loop, done *BasicBlock) {
//
// it = range x
// loop: (target of continue)
// okv = next it (ok, key, value)
// ok = extract okv #0
// if ok goto body else done
// body:
// k = extract okv #1
// v = extract okv #2
// ...body...
// jump loop
// done: (target of break)
//
if tk == nil {
tk = tInvalid
}
if tv == nil {
tv = tInvalid
}
rng := &Range{X: x}
rng.setType(tRangeIter)
it := fn.emit(rng, source)
loop = fn.newBasicBlock("rangeiter.loop")
emitJump(fn, loop, source)
fn.currentBlock = loop
_, isString := x.Type().Underlying().(*types.Basic)
okv := &Next{
Iter: it,
IsString: isString,
}
okv.setType(types.NewTuple(
varOk,
newVar("k", tk),
newVar("v", tv),
))
fn.emit(okv, source)
body := fn.newBasicBlock("rangeiter.body")
done = fn.newBasicBlock("rangeiter.done")
emitIf(fn, emitExtract(fn, okv, 0, source), body, done, source)
fn.currentBlock = body
if tk != tInvalid {
k = emitExtract(fn, okv, 1, source)
}
if tv != tInvalid {
v = emitExtract(fn, okv, 2, source)
}
return
}
// rangeChan emits to fn the header for a loop that receives from
// channel x until it fails.
// tk is the channel's element type, or nil if the k result is
// not wanted
// pos is the position of the '=' or ':=' token.
//
func (b *builder) rangeChan(fn *Function, x Value, tk types.Type, source ast.Node) (k Value, loop, done *BasicBlock) {
//
// loop: (target of continue)
// ko = <-x (key, ok)
// ok = extract ko #1
// if ok goto body else done
// body:
// k = extract ko #0
// ...
// goto loop
// done: (target of break)
loop = fn.newBasicBlock("rangechan.loop")
emitJump(fn, loop, source)
fn.currentBlock = loop
retv := emitRecv(fn, x, true, types.NewTuple(newVar("k", x.Type().Underlying().(*types.Chan).Elem()), varOk), source)
body := fn.newBasicBlock("rangechan.body")
done = fn.newBasicBlock("rangechan.done")
emitIf(fn, emitExtract(fn, retv, 1, source), body, done, source)
fn.currentBlock = body
if tk != nil {
k = emitExtract(fn, retv, 0, source)
}
return
}
// rangeStmt emits to fn code for the range statement s, optionally
// labelled by label.
//
func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, source ast.Node) {
var tk, tv types.Type
if s.Key != nil && !isBlankIdent(s.Key) {
tk = fn.Pkg.typeOf(s.Key)
}
if s.Value != nil && !isBlankIdent(s.Value) {
tv = fn.Pkg.typeOf(s.Value)
}
// If iteration variables are defined (:=), this
// occurs once outside the loop.
//
// Unlike a short variable declaration, a RangeStmt
// using := never redeclares an existing variable; it
// always creates a new one.
if s.Tok == token.DEFINE {
if tk != nil {
fn.addLocalForIdent(s.Key.(*ast.Ident))
}
if tv != nil {
fn.addLocalForIdent(s.Value.(*ast.Ident))
}
}
x := b.expr(fn, s.X)
var k, v Value
var loop, done *BasicBlock
switch rt := x.Type().Underlying().(type) {
case *types.Slice, *types.Array, *types.Pointer: // *array
k, v, loop, done = b.rangeIndexed(fn, x, tv, source)
case *types.Chan:
k, loop, done = b.rangeChan(fn, x, tk, source)
case *types.Map, *types.Basic: // string
k, v, loop, done = b.rangeIter(fn, x, tk, tv, source)
default:
panic("Cannot range over: " + rt.String())
}
// Evaluate both LHS expressions before we update either.
var kl, vl lvalue
if tk != nil {
kl = b.addr(fn, s.Key, false) // non-escaping
}
if tv != nil {
vl = b.addr(fn, s.Value, false) // non-escaping
}
if tk != nil {
kl.store(fn, k, s)
}
if tv != nil {
vl.store(fn, v, s)
}
if label != nil {
label._break = done
label._continue = loop
}
fn.targets = &targets{
tail: fn.targets,
_break: done,
_continue: loop,
}
b.stmt(fn, s.Body)
fn.targets = fn.targets.tail
emitJump(fn, loop, source) // back-edge
fn.currentBlock = done
}
// stmt lowers statement s to IR form, emitting code to fn.
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, s)
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:
instr := &Send{
Chan: b.expr(fn, s.Chan),
X: emitConv(fn, b.expr(fn, s.Value),
fn.Pkg.typeOf(s.Chan).Underlying().(*types.Chan).Elem(), s),
}
fn.emit(instr, s)
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, emitConst(fn, NewConst(constant.MakeInt64(1), loc.typ())), op, s)
case *ast.AssignStmt:
switch s.Tok {
case token.ASSIGN, token.DEFINE:
b.assignStmt(fn, s.Lhs, s.Rhs, s.Tok == token.DEFINE, _s)
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)
}
case *ast.GoStmt:
// The "intrinsics" new/make/len/cap are forbidden here.
// panic is treated like an ordinary function call.
v := Go{}
b.setCall(fn, s.Call, &v.Call)
fn.emit(&v, s)
case *ast.DeferStmt:
// The "intrinsics" new/make/len/cap are forbidden here.
// panic is treated like an ordinary function call.
v := Defer{}
b.setCall(fn, s.Call, &v.Call)
fn.hasDefer = true
fn.emit(&v, s)
case *ast.ReturnStmt:
// TODO(dh): we could emit tigher position information by
// using the ith returned expression
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, s),
fn.Signature.Results().At(i).Type(), s))
}
} 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(), s)
results = append(results, v)
}
}
ret := fn.results()
for i, r := range results {
emitStore(fn, ret[i], r, s)
}
emitJump(fn, fn.Exit, s)
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
}
j := emitJump(fn, block, s)
j.Comment = s.Tok.String()
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")
}
instr := b.cond(fn, s.Cond, then, els)
instr.source = s
fn.currentBlock = then
b.stmt(fn, s.Body)
emitJump(fn, done, s)
if s.Else != nil {
fn.currentBlock = els
b.stmt(fn, s.Else)
emitJump(fn, done, s)
}
fn.currentBlock = done
case *ast.SwitchStmt:
b.switchStmt(fn, s, label)
case *ast.TypeSwitchStmt:
b.typeSwitchStmt(fn, s, label)
case *ast.SelectStmt:
if b.selectStmt(fn, s, label) {
// the select has no cases, it blocks forever
fn.currentBlock = fn.newBasicBlock("unreachable")
}
case *ast.ForStmt:
b.forStmt(fn, s, label)
case *ast.RangeStmt:
b.rangeStmt(fn, s, label, s)
default:
panic(fmt.Sprintf("unexpected statement kind: %T", s))
}
}
// buildFunction builds IR code for the body of function fn. Idempotent.
func (b *builder) buildFunction(fn *Function) {
if fn.Blocks != nil {
return // building already started
}
var recvField *ast.FieldList
var body *ast.BlockStmt
var functype *ast.FuncType
switch n := fn.source.(type) {
case nil:
return // not a Go source function. (Synthetic, or from object file.)
case *ast.FuncDecl:
functype = n.Type
recvField = n.Recv
body = n.Body
case *ast.FuncLit:
functype = n.Type
body = n.Body
default:
panic(n)
}
if fn.Package().Pkg.Path() == "syscall" && fn.Name() == "Exit" {
// syscall.Exit is a stub and the way os.Exit terminates the
// process. Note that there are other functions in the runtime
// that also terminate or unwind that we cannot analyze.
// However, they aren't stubs, so buildExits ends up getting
// called on them, so that's where we handle those special
// cases.
fn.WillExit = true
}
if body == nil {
// External function.
if fn.Params == nil {
// This condition ensures we add a non-empty
// params list once only, but we may attempt
// the degenerate empty case repeatedly.
// TODO(adonovan): opt: don't do that.
// We set Function.Params even though there is no body
// code to reference them. This simplifies clients.
if recv := fn.Signature.Recv(); recv != nil {
// XXX synthesize an ast.Node
fn.addParamObj(recv, nil)
}
params := fn.Signature.Params()
for i, n := 0, params.Len(); i < n; i++ {
// XXX synthesize an ast.Node
fn.addParamObj(params.At(i), nil)
}
}
return
}
if fn.Prog.mode&LogSource != 0 {
defer logStack("build function %s @ %s", fn, fn.Prog.Fset.Position(fn.Pos()))()
}
fn.blocksets = b.blocksets
fn.startBody()
fn.createSyntacticParams(recvField, functype)
fn.exitBlock()
b.stmt(fn, body)
if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) {
// Control fell off the end of the function's body block.
//
// Block optimizations eliminate the current block, if
// unreachable. It is a builder invariant that
// if this no-arg return is ill-typed for
// fn.Signature.Results, this block must be
// unreachable. The sanity checker checks this.
// fn.emit(new(RunDefers))
// fn.emit(new(Return))
emitJump(fn, fn.Exit, nil)
}
optimizeBlocks(fn)
buildFakeExits(fn)
b.buildExits(fn)
b.addUnreachables(fn)
fn.finishBody()
b.blocksets = fn.blocksets
fn.functionBody = nil
}
// buildFuncDecl builds IR code for the function or method declared
// by decl in package pkg.
//
func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
id := decl.Name
if isBlankIdent(id) {
return // discard
}
fn := pkg.values[pkg.info.Defs[id]].(*Function)
if decl.Recv == nil && id.Name == "init" {
var v Call
v.Call.Value = fn
v.setType(types.NewTuple())
pkg.init.emit(&v, decl)
}
fn.source = decl
b.buildFunction(fn)
}
// Build calls Package.Build for each package in prog.
//
// Build is intended for whole-program analysis; a typical compiler
// need only build a single package.
//
// Build is idempotent and thread-safe.
//
func (prog *Program) Build() {
for _, p := range prog.packages {
p.Build()
}
}
// Build builds IR code for all functions and vars in package p.
//
// Precondition: CreatePackage must have been called for all of p's
// direct imports (and hence its direct imports must have been
// error-free).
//
// Build is idempotent and thread-safe.
//
func (p *Package) Build() { p.buildOnce.Do(p.build) }
func (p *Package) build() {
if p.info == nil {
return // synthetic package, e.g. "testmain"
}
// Ensure we have runtime type info for all exported members.
// TODO(adonovan): ideally belongs in memberFromObject, but
// that would require package creation in topological order.
for name, mem := range p.Members {
if ast.IsExported(name) {
p.Prog.needMethodsOf(mem.Type())
}
}
if p.Prog.mode&LogSource != 0 {
defer logStack("build %s", p)()
}
init := p.init
init.startBody()
init.exitBlock()
var done *BasicBlock
// Make init() skip if package is already initialized.
initguard := p.Var("init$guard")
doinit := init.newBasicBlock("init.start")
done = init.Exit
emitIf(init, emitLoad(init, initguard, nil), done, doinit, nil)
init.currentBlock = doinit
emitStore(init, initguard, emitConst(init, NewConst(constant.MakeBool(true), tBool)), nil)
// Call the init() function of each package we import.
for _, pkg := range p.Pkg.Imports() {
prereq := p.Prog.packages[pkg]
if prereq == nil {
panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Pkg.Path(), pkg.Path()))
}
var v Call
v.Call.Value = prereq.init
v.setType(types.NewTuple())
init.emit(&v, nil)
}
b := builder{
printFunc: p.printFunc,
}
// Initialize package-level vars in correct order.
for _, varinit := range p.info.InitOrder {
if init.Prog.mode&LogSource != 0 {
fmt.Fprintf(os.Stderr, "build global initializer %v @ %s\n",
varinit.Lhs, p.Prog.Fset.Position(varinit.Rhs.Pos()))
}
if len(varinit.Lhs) == 1 {
// 1:1 initialization: var x, y = a(), b()
var lval lvalue
if v := varinit.Lhs[0]; v.Name() != "_" {
lval = &address{addr: p.values[v].(*Global)}
} else {
lval = blank{}
}
// TODO(dh): do emit position information
b.assign(init, lval, varinit.Rhs, true, nil, nil)
} else {
// n:1 initialization: var x, y := f()
tuple := b.exprN(init, varinit.Rhs)
for i, v := range varinit.Lhs {
if v.Name() == "_" {
continue
}
emitStore(init, p.values[v].(*Global), emitExtract(init, tuple, i, nil), nil)
}
}
}
// Build all package-level functions, init functions
// and methods, including unreachable/blank ones.
// We build them in source order, but it's not significant.
for _, file := range p.files {
for _, decl := range file.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok {
b.buildFuncDecl(p, decl)
}
}
}
// Finish up init().
emitJump(init, done, nil)
init.finishBody()
p.info = nil // We no longer need ASTs or go/types deductions.
if p.Prog.mode&SanityCheckFunctions != 0 {
sanityCheckPackage(p)
}
}
// Like ObjectOf, but panics instead of returning nil.
// Only valid during p's create and build phases.
func (p *Package) objectOf(id *ast.Ident) types.Object {
if o := p.info.ObjectOf(id); o != nil {
return o
}
panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s",
id.Name, p.Prog.Fset.Position(id.Pos())))
}
// Like TypeOf, but panics instead of returning nil.
// Only valid during p's create and build phases.
func (p *Package) typeOf(e ast.Expr) types.Type {
if T := p.info.TypeOf(e); T != nil {
return T
}
panic(fmt.Sprintf("no type for %T @ %s",
e, p.Prog.Fset.Position(e.Pos())))
}