open-vault/vendor/honnef.co/go/tools/ir/builder.go

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())))
}