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