go.tools/ssa: small changes accumulated during gri's vacation. :)

Method sets:
- Simplify CallCommon.
  Avoid the implicit copy when calling a T method on a *T
  receiver.  This simplifies clients.  Instead we generate
  "indirection wrapper" functions that do this (like gc does).
  New invariant:
  m's receiver type is exactly T for all m in MethodSet(T)
- MakeInterface no longer holds the concrete type's MethodSet.
  We can defer its computation this way.
- ssa.Type now just wraps a types.TypeName object.
  MethodSets are computed as needed, not eagerly.

Position info:
- new CanonicalPos utility maps ast.Expr to canonical
  token.Pos, as returned by {Instruction,Value}.Pos() methods.
- Don't set posn for implicit operations (e.g. varargs array alloc)
- Set position info for ChangeInterface and Slice instructions.

Cosmetic:
- add Member.Token() method
- simplify isPointer
- Omit words "interface", "slice" when printing MakeInterface,
  MakeSlice; the type is enough.
- Comments on PathEnclosingInterval.
- Remove Function.FullName() where implicit String() suffices.

Also:
- Exposed NewLiteral to clients.
- Added ssa.Instruction.Parent() *Function
  Added ssa.BasicBlock.Parent() *Function.
  Added Sanity checks for above.

R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/10166045
This commit is contained in:
Alan Donovan 2013-06-13 14:43:35 -04:00
parent 9ce6fcb502
commit 341a07a3aa
15 changed files with 319 additions and 162 deletions

View File

@ -62,8 +62,8 @@ var (
// SSA Value constants.
vZero = intLiteral(0)
vOne = intLiteral(1)
vTrue = newLiteral(exact.MakeBool(true), tBool)
vFalse = newLiteral(exact.MakeBool(false), tBool)
vTrue = NewLiteral(exact.MakeBool(true), tBool)
vFalse = NewLiteral(exact.MakeBool(false), tBool)
)
// builder holds state associated with the package currently being built.
@ -230,7 +230,7 @@ func (b *builder) exprN(fn *Function, e ast.Expr) Value {
tuple = fn.emit(lookup)
case *ast.TypeAssertExpr:
return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e))
return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e), e.Lparen)
case *ast.UnaryExpr: // must be receive <-
typ = fn.Pkg.typeOf(e.X).Underlying().(*types.Chan).Elem()
@ -345,7 +345,7 @@ func (b *builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping
if !wantAddr {
if m, recv := b.findMethod(fn, e.X, id); m != nil {
c := &MakeClosure{
Fn: makeBoundMethodThunk(fn.Prog, m, recv),
Fn: makeBoundMethodThunk(fn.Prog, m, recv.Type()),
Bindings: []Value{recv},
}
c.setPos(e.Sel.Pos())
@ -579,7 +579,7 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) {
//
func (b *builder) expr(fn *Function, e ast.Expr) Value {
if v := fn.Pkg.info.ValueOf(e); v != nil {
return newLiteral(v, fn.Pkg.typeOf(e))
return NewLiteral(v, fn.Pkg.typeOf(e))
}
switch e := e.(type) {
@ -618,7 +618,7 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
return b.expr(fn, e.X)
case *ast.TypeAssertExpr: // single-result form only
return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e))
return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e), e.Lparen)
case *ast.CallExpr:
typ := fn.Pkg.typeOf(e)
@ -709,6 +709,7 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
Low: low,
High: high,
}
v.setPos(e.Lbrack)
v.setType(fn.Pkg.typeOf(e))
return fn.emit(v)
@ -969,7 +970,8 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx
} else {
// Replace a suffix of args with a slice containing it.
at := types.NewArray(vt, int64(len(varargs)))
a := emitNew(fn, at, e.Lparen)
// Don't set pos (e.g. to e.Lparen) for implicit Allocs.
a := emitNew(fn, at, token.NoPos)
for i, arg := range varargs {
iaddr := &IndexAddr{
X: a,
@ -1501,7 +1503,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
if casetype == tUntypedNil {
condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()), token.NoPos)
} else {
yok := emitTypeTest(fn, x, casetype)
yok := emitTypeTest(fn, x, casetype, token.NoPos)
ti = emitExtract(fn, yok, 0, casetype)
condv = emitExtract(fn, yok, 1, tBool)
}
@ -1643,7 +1645,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
switch comm := clause.Comm.(type) {
case *ast.AssignStmt: // x := <-states[state].Chan
xdecl := fn.addNamedLocal(fn.Pkg.objectOf(comm.Lhs[0].(*ast.Ident)))
recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref())
recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref(), token.NoPos)
emitStore(fn, xdecl, recv)
if len(comm.Lhs) == 2 { // x, ok := ...

View File

@ -35,13 +35,14 @@ const (
//
func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
prog := &Program{
Files: fset,
Packages: make(map[string]*Package),
packages: make(map[*types.Package]*Package),
Builtins: make(map[types.Object]*Builtin),
methodSets: make(map[types.Type]MethodSet),
concreteMethods: make(map[*types.Func]*Function),
mode: mode,
Files: fset,
Packages: make(map[string]*Package),
packages: make(map[*types.Package]*Package),
Builtins: make(map[types.Object]*Builtin),
methodSets: make(map[types.Type]MethodSet),
concreteMethods: make(map[*types.Func]*Function),
indirectionWrappers: make(map[*Function]*Function),
mode: mode,
}
// Create Values for built-in functions.
@ -67,25 +68,9 @@ func (prog *Program) CreatePackages(imp *importer.Importer) {
// TODO(adonovan): make this idempotent, so that a second call
// to CreatePackages creates only the packages that appeared
// in imp since the first.
//
// TODO(adonovan): make it create packages in topological
// order (using info.Imports()) so we can compute method sets
// in postorder (or within createPackage) rather than as a
// second pass.
for path, info := range imp.Packages {
createPackage(prog, path, info)
}
// Compute the method sets, now that we have all packages' methods.
for _, pkg := range prog.Packages {
for _, mem := range pkg.Members {
if t, ok := mem.(*Type); ok {
t.Methods = prog.MethodSet(t.NamedType)
t.PtrMethods = prog.MethodSet(pointer(t.NamedType))
}
}
}
}
// memberFromObject populates package pkg with a member for the
@ -99,12 +84,12 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
name := obj.Name()
switch obj := obj.(type) {
case *types.TypeName:
pkg.Members[name] = &Type{NamedType: obj.Type().(*types.Named)}
pkg.Members[name] = &Type{Object: obj}
case *types.Const:
pkg.Members[name] = &Constant{
name: name,
Value: newLiteral(obj.Val(), obj.Type()),
Value: NewLiteral(obj.Val(), obj.Type()),
pos: obj.Pos(),
}

View File

@ -160,7 +160,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
// Assignment from one interface type to another?
if _, ok := ut_src.(*types.Interface); ok {
return emitTypeAssert(f, val, typ)
return emitTypeAssert(f, val, typ, token.NoPos)
}
// Untyped nil literal? Return interface-typed nil literal.
@ -174,10 +174,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
val = emitConv(f, val, DefaultType(ut_src))
}
mi := &MakeInterface{
X: val,
Methods: f.Prog.MethodSet(t_src),
}
mi := &MakeInterface{X: val}
mi.setType(typ)
return f.emit(mi)
}
@ -188,7 +185,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
// change yet; this defers the point at which the number of
// possible representations explodes.
if l, ok := val.(*Literal); ok {
return newLiteral(l.Value, typ)
return NewLiteral(l.Value, typ)
}
// A representation-changing conversion.
@ -246,7 +243,7 @@ func emitExtract(f *Function, tuple Value, index int, typ types.Type) Value {
// emitTypeAssert emits to f a type assertion value := x.(t) and
// returns the value. x.Type() must be an interface.
//
func emitTypeAssert(f *Function, x Value, t types.Type) Value {
func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value {
// Simplify infallible assertions.
txi := x.Type().Underlying().(*types.Interface)
if ti, ok := t.Underlying().(*types.Interface); ok {
@ -255,12 +252,14 @@ func emitTypeAssert(f *Function, x Value, t types.Type) Value {
}
if isSuperinterface(ti, txi) {
c := &ChangeInterface{X: x}
c.setPos(pos)
c.setType(t)
return f.emit(c)
}
}
a := &TypeAssert{X: x, AssertedType: t}
a.setPos(pos)
a.setType(t)
return f.emit(a)
}
@ -268,7 +267,7 @@ func emitTypeAssert(f *Function, x Value, t types.Type) Value {
// emitTypeTest emits to f a type test value,ok := x.(t) and returns
// a (value, ok) tuple. x.Type() must be an interface.
//
func emitTypeTest(f *Function, x Value, t types.Type) Value {
func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
// TODO(adonovan): opt: simplify infallible tests as per
// emitTypeAssert, and return (x, vTrue).
// (Requires that exprN returns a slice of extracted values,
@ -278,6 +277,7 @@ func emitTypeTest(f *Function, x Value, t types.Type) Value {
AssertedType: t,
CommaOk: true,
}
a.setPos(pos)
a.setType(types.NewTuple(
types.NewVar(token.NoPos, nil, "value", t),
varOk,

View File

@ -100,7 +100,7 @@ func main() {
// .0.entry: P:0 S:0
// a0 = new [1]interface{} *[1]interface{}
// t0 = &a0[0:untyped integer] *interface{}
// t1 = make interface interface{} <- string ("Hello, World!":string) interface{}
// t1 = make interface{} <- string ("Hello, World!":string) interface{}
// *t0 = t1
// t2 = slice a0[:] []interface{}
// t3 = fmt.Println(t2) (n int, err error)

View File

@ -19,6 +19,9 @@ func addEdge(from, to *BasicBlock) {
to.Preds = append(to.Preds, from)
}
// Parent returns the function that contains block b.
func (b *BasicBlock) Parent() *Function { return b.parent }
// String returns a human-readable label of this block.
// It is not guaranteed unique within the function.
//
@ -168,9 +171,10 @@ func (f *Function) labelledBlock(label *ast.Ident) *lblock {
//
func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter {
v := &Parameter{
name: name,
typ: typ,
pos: pos,
name: name,
typ: typ,
pos: pos,
parent: f,
}
f.Params = append(f.Params, v)
return v
@ -412,10 +416,11 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value {
}
outer := f.Enclosing.lookup(obj, true) // escaping
v := &Capture{
name: outer.Name(),
typ: outer.Type(),
pos: outer.Pos(),
outer: outer,
name: outer.Name(),
typ: outer.Type(),
pos: outer.Pos(),
outer: outer,
parent: f,
}
f.objects[obj] = v
f.FreeVars = append(f.FreeVars, v)
@ -612,7 +617,7 @@ func (f *Function) newBasicBlock(comment string) *BasicBlock {
b := &BasicBlock{
Index: len(f.Blocks),
Comment: comment,
Func: f,
parent: f,
}
b.Succs = b.succs2[:0]
f.Blocks = append(f.Blocks, b)

View File

@ -369,7 +369,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
}
// prepareCall determines the function value and argument values for a
// function call in a Call, Go or Defer instruction, peforming
// function call in a Call, Go or Defer instruction, performing
// interface method lookup if needed.
//
func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) {
@ -383,23 +383,12 @@ func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) {
panic("method invoked on nil interface")
}
id := call.MethodId()
m := findMethodSet(fr.i, recv.t)[id]
if m == nil {
fn = findMethodSet(fr.i, recv.t)[id]
if fn == nil {
// Unreachable in well-typed programs.
panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, id))
}
_, aptr := recv.v.(*value) // actual pointerness
_, fptr := m.Signature.Recv().Type().(*types.Pointer) // formal pointerness
switch {
case aptr == fptr:
args = append(args, copyVal(recv.v))
case aptr:
// Calling func(T) with a *T receiver: make a copy.
args = append(args, copyVal(*recv.v.(*value)))
case fptr:
panic("illegal call of *T method with T receiver")
}
fn = m
args = append(args, copyVal(recv.v))
}
for _, arg := range call.Args {
args = append(args, fr.get(arg))

View File

@ -349,7 +349,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
fmt.Fprintln(os.Stderr, "liftAlloc: lifting ", alloc, alloc.Name())
}
fn := alloc.Block().Func
fn := alloc.Parent()
// Φ-insertion.
//

View File

@ -11,25 +11,23 @@ import (
"code.google.com/p/go.tools/go/types"
)
// newLiteral returns a new literal of the specified value and type.
// NewLiteral returns a new literal of the specified value and type.
// val must be valid according to the specification of Literal.Value.
//
func newLiteral(val exact.Value, typ types.Type) *Literal {
// This constructor exists to provide a single place to
// insert logging/assertions during debugging.
func NewLiteral(val exact.Value, typ types.Type) *Literal {
return &Literal{typ, val}
}
// intLiteral returns an untyped integer literal that evaluates to i.
func intLiteral(i int64) *Literal {
return newLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt])
return NewLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt])
}
// nilLiteral returns a nil literal of the specified type, which may
// be any reference type, including interfaces.
//
func nilLiteral(typ types.Type) *Literal {
return newLiteral(exact.MakeNil(), typ)
return NewLiteral(exact.MakeNil(), typ)
}
// zeroLiteral returns a new "zero" literal of the specified type,
@ -41,11 +39,11 @@ func zeroLiteral(t types.Type) *Literal {
case *types.Basic:
switch {
case t.Info()&types.IsBoolean != 0:
return newLiteral(exact.MakeBool(false), t)
return NewLiteral(exact.MakeBool(false), t)
case t.Info()&types.IsNumeric != 0:
return newLiteral(exact.MakeInt64(0), t)
return NewLiteral(exact.MakeInt64(0), t)
case t.Info()&types.IsString != 0:
return newLiteral(exact.MakeString(""), t)
return NewLiteral(exact.MakeString(""), t)
case t.Kind() == types.UnsafePointer:
fallthrough
case t.Kind() == types.UntypedNil:
@ -56,7 +54,7 @@ func zeroLiteral(t types.Type) *Literal {
case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
return nilLiteral(t)
case *types.Named:
return newLiteral(zeroLiteral(t.Underlying()).Value, t)
return NewLiteral(zeroLiteral(t.Underlying()).Value, t)
case *types.Array, *types.Struct:
panic(fmt.Sprint("zeroLiteral applied to aggregate:", t))
}

View File

@ -28,14 +28,14 @@ func (id Id) String() string {
func relName(v Value, i Instruction) string {
switch v := v.(type) {
case *Global:
if i != nil && v.Pkg == i.Block().Func.Pkg {
if i != nil && v.Pkg == i.Parent().Pkg {
return v.Name()
}
return v.FullName()
case *Function:
var pkg *Package
if i != nil {
pkg = i.Block().Func.Pkg
pkg = i.Parent().Pkg
}
return v.fullName(pkg)
}
@ -166,7 +166,7 @@ func (v *ChangeInterface) String() string {
}
func (v *MakeInterface) String() string {
return fmt.Sprintf("make interface %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v))
return fmt.Sprintf("make %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v))
}
func (v *MakeClosure) String() string {
@ -187,7 +187,7 @@ func (v *MakeClosure) String() string {
func (v *MakeSlice) String() string {
var b bytes.Buffer
b.WriteString("make slice ")
b.WriteString("make ")
b.WriteString(v.Type().String())
b.WriteString(" ")
b.WriteString(relName(v.Len, v))
@ -381,19 +381,22 @@ func (p *Package) DumpTo(w io.Writer) {
fmt.Fprintf(w, " func %-*s %s\n", maxname, name, mem.Type())
case *Type:
fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.NamedType.Underlying())
// We display only PtrMethods since its keys
// are a superset of Methods' keys, though the
fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.Type().Underlying())
// We display only mset(*T) since its keys
// are a superset of mset(T)'s keys, though the
// methods themselves may differ,
// e.g. different bridge methods.
// TODO(adonovan): show pointerness of receivers.
// NB: if mem.Type() is a pointer, mset is empty.
mset := p.Prog.MethodSet(pointer(mem.Type()))
var keys ids
for id := range mem.PtrMethods {
for id := range mset {
keys = append(keys, id)
}
sort.Sort(keys)
for _, id := range keys {
method := mem.PtrMethods[id]
method := mset[id]
// TODO(adonovan): show pointerness of receiver of declared method, not the index
fmt.Fprintf(w, " method %s %s\n", id, method.Signature)
}

View File

@ -91,7 +91,8 @@ func (c candidate) ptrRecv() bool {
}
// MethodSet returns the method set for type typ,
// building bridge methods as needed for promoted methods.
// building bridge methods as needed for promoted methods
// and indirection wrappers for *T receiver types.
// A nil result indicates an empty set.
//
// Thread-safe.
@ -104,7 +105,7 @@ func (p *Program) MethodSet(typ types.Type) MethodSet {
defer p.methodSetsMu.Unlock()
// TODO(adonovan): Using Types as map keys doesn't properly
// de-dup. e.g. *NamedType are canonical but *Struct and
// de-dup. e.g. *Named are canonical but *Struct and
// others are not. Need to de-dup using typemap.T.
mset := p.methodSets[typ]
if mset == nil {
@ -151,7 +152,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
m := nt.Method(i)
concrete := prog.concreteMethods[m]
if concrete == nil {
panic(fmt.Sprintf("no ssa.Function for mset(%s)[%s]", t, m.Name()))
panic(fmt.Sprintf("no ssa.Function for methodset(%s)[%s]", t, m.Name()))
}
addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, concrete, node)
}
@ -205,7 +206,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
if cand == nil {
continue // blocked; ignore
}
if cand.ptrRecv() && !(isPointer(typ) || cand.path.isIndirect()) {
if cand.ptrRecv() && !isPointer(typ) && !cand.path.isIndirect() {
// A candidate concrete method f with receiver
// *C is promoted into the method set of
// (non-pointer) E iff the implicit path selection
@ -214,8 +215,13 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
}
var method *Function
if cand.path == nil {
// Trivial member of method-set; no bridge needed.
// Trivial member of method-set; no promotion needed.
method = cand.concrete
if !cand.ptrRecv() && isPointer(typ) {
// Call to method on T from receiver of type *T.
method = indirectionWrapper(method)
}
} else {
method = makeBridgeMethod(prog, typ, cand)
}
@ -332,7 +338,9 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
return fn
}
// createParams creates parameters for bridge method fn based on its Signature.
// createParams creates parameters for bridge method fn based on its
// Signature.Params, which do not include the receiver.
//
func createParams(fn *Function) {
var last *Parameter
tparams := fn.Signature.Params()
@ -412,7 +420,7 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
//
// TODO(adonovan): memoize creation of these functions in the Program.
//
func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function {
func makeBoundMethodThunk(prog *Program, meth *Function, recvType types.Type) *Function {
if prog.mode&LogSource != 0 {
defer logStack("makeBoundMethodThunk %s", meth)()
}
@ -423,7 +431,7 @@ func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function {
Prog: prog,
}
cap := &Capture{name: "recv", typ: recv.Type()}
cap := &Capture{name: "recv", typ: recvType, parent: fn}
fn.FreeVars = []*Capture{cap}
fn.startBody()
createParams(fn)
@ -438,6 +446,53 @@ func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function {
return fn
}
// Receiver indirection wrapper ------------------------------------
// indirectionWrapper returns a synthetic method with *T receiver
// that delegates to meth, which has a T receiver.
//
// func (recv *T) f(...) ... {
// return (*recv).f(...)
// }
//
// EXCLUSIVE_LOCKS_REQUIRED(meth.Prog.methodSetsMu)
//
func indirectionWrapper(meth *Function) *Function {
prog := meth.Prog
fn, ok := prog.indirectionWrappers[meth]
if !ok {
if prog.mode&LogSource != 0 {
defer logStack("makeIndirectionWrapper %s", meth)()
}
s := meth.Signature
recv := types.NewVar(token.NoPos, meth.Pkg.Types, "recv",
types.NewPointer(s.Recv().Type()))
fn = &Function{
name: meth.Name(),
Signature: types.NewSignature(recv, s.Params(), s.Results(), s.IsVariadic()),
Prog: prog,
}
fn.startBody()
fn.addParamObj(recv)
createParams(fn)
// TODO(adonovan): consider emitting a nil-pointer check here
// with a nice error message, like gc does.
var c Call
c.Call.Func = meth
c.Call.Args = append(c.Call.Args, emitLoad(fn, fn.Params[0]))
for _, arg := range fn.Params[1:] {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
prog.indirectionWrappers[meth] = fn
}
return fn
}
// Implicit field promotion ----------------------------------------
// For a given struct type and (promoted) field Id, findEmbeddedField

View File

@ -41,7 +41,7 @@ func MustSanityCheck(fn *Function, reporter io.Writer) {
}
func (s *sanity) diagnostic(prefix, format string, args ...interface{}) {
fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn.FullName())
fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn)
if s.block != nil {
fmt.Fprintf(s.reporter, ", block %s", s.block)
}
@ -197,8 +197,8 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
if b.Index != index {
s.errorf("block has incorrect Index %d", b.Index)
}
if b.Func != s.fn {
s.errorf("block has incorrect Func %s", b.Func.FullName())
if b.parent != s.fn {
s.errorf("block has incorrect parent %s", b.parent)
}
// Check all blocks are reachable.
@ -226,8 +226,8 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
if !found {
s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs)
}
if a.Func != s.fn {
s.errorf("predecessor %s belongs to different function %s", a, a.Func.FullName())
if a.parent != s.fn {
s.errorf("predecessor %s belongs to different function %s", a, a.parent)
}
}
for _, c := range b.Succs {
@ -241,8 +241,8 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
if !found {
s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds)
}
if c.Func != s.fn {
s.errorf("successor %s belongs to different function %s", c, c.Func.FullName())
if c.parent != s.fn {
s.errorf("successor %s belongs to different function %s", c, c.parent)
}
}
@ -286,10 +286,24 @@ func (s *sanity) checkFunction(fn *Function) bool {
s.errorf("nil Prog")
}
for i, l := range fn.Locals {
if l.Parent() != fn {
s.errorf("Local %s at index %d has wrong parent", l.Name(), i)
}
if l.Heap {
s.errorf("Local %s at index %d has Heap flag set", l.Name(), i)
}
}
for i, p := range fn.Params {
if p.Parent() != fn {
s.errorf("Param %s at index %d has wrong parent", p.Name(), i)
}
}
for i, fv := range fn.FreeVars {
if fv.Parent() != fn {
s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i)
}
}
if fn.Blocks != nil && len(fn.Blocks) == 0 {
// Function _had_ blocks (so it's not external) but
// they were "optimized" away, even the entry block.

View File

@ -26,17 +26,19 @@ func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
//
// imp provides ASTs for the program's packages.
//
// pkg may be nil if no SSA package has yet been created for the found
// package. Call prog.CreatePackages(imp) to avoid this.
//
// The result is (nil, nil, false) if not found.
//
func (prog *Program) PathEnclosingInterval(imp *importer.Importer, start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) {
for path, info := range imp.Packages {
pkg := prog.Packages[path]
for importPath, info := range imp.Packages {
for _, f := range info.Files {
if !tokenFileContainsPos(prog.Files.File(f.Package), start) {
continue
}
if path, exact := PathEnclosingInterval(f, start, end); path != nil {
return pkg, path, exact
return prog.Packages[importPath], path, exact
}
}
}
@ -134,7 +136,12 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
return mem
}
case *Type:
for _, meth := range mem.PtrMethods {
for _, meth := range pkg.Prog.MethodSet(mem.Type()) {
if meth.Pos() == pos {
return meth
}
}
for _, meth := range pkg.Prog.MethodSet(pointer(mem.Type())) {
if meth.Pos() == pos {
return meth
}
@ -143,3 +150,77 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
}
return nil
}
// CanonicalPos returns the canonical position of the AST node n,
//
// For each Node kind that may generate an SSA Value or Instruction,
// exactly one token within it is designated as "canonical". The
// position of that token is returned by {Value,Instruction}.Pos().
// The specifications of those methods determine the implementation of
// this function.
//
// TODO(adonovan): test coverage.
//
func CanonicalPos(n ast.Node) token.Pos {
// Comments show the Value/Instruction kinds v that may be
// created by n such that CanonicalPos(n) == v.Pos().
switch n := n.(type) {
case *ast.ParenExpr:
return CanonicalPos(n.X)
case *ast.CallExpr:
// f(x): *Call, *Go, *Defer.
// T(x): *ChangeType, *Convert, *MakeInterface, *ChangeInterface.
// make(): *MakeMap, *MakeChan, *MakeSlice.
// new(): *Alloc.
// panic(): *Panic.
return n.Lparen
case *ast.Ident:
return n.NamePos // *Parameter, *Alloc, *Capture
case *ast.TypeAssertExpr:
return n.Lparen // *ChangeInterface or *TypeAssertExpr
case *ast.SelectorExpr:
return n.Sel.NamePos // *MakeClosure, *Field or *FieldAddr
case *ast.FuncLit:
return n.Type.Func // *Function or *MakeClosure
case *ast.CompositeLit:
return n.Lbrace // *Alloc or *Slice
case *ast.BinaryExpr:
return n.OpPos // *Phi or *BinOp
case *ast.UnaryExpr:
return n.OpPos // *Phi or *UnOp
case *ast.IndexExpr:
return n.Lbrack // *Index or *IndexAddr
case *ast.SliceExpr:
return n.Lbrack // *Slice
case *ast.SelectStmt:
return n.Select // *Select
case *ast.RangeStmt:
return n.For // *Range
case *ast.ReturnStmt:
return n.Return // *Ret
case *ast.SendStmt:
return n.Arrow // *Send
case *ast.StarExpr:
return n.Star // *Store
case *ast.KeyValueExpr:
return n.Colon // *MapUpdate
}
return token.NoPos
}

View File

@ -20,7 +20,7 @@ import (
// additional whitespace abutting a node to be enclosed by it.
// In this example:
//
// z := x + y // add them
// z := x + y // add them
// <-A->
// <----B----->
//
@ -41,7 +41,7 @@ import (
// interior whitespace of path[0].
// In this example:
//
// z := x + y // add them
// z := x + y // add them
// <--C--> <---E-->
// ^
// D
@ -430,7 +430,10 @@ func childrenOf(n ast.Node) []ast.Node {
children = append(children, tok(n.Switch, len("switch")))
case *ast.TypeAssertExpr:
// nop
children = append(children,
tok(n.Lparen-1, len(".")),
tok(n.Lparen, len("(")),
tok(n.Rparen, len(")")))
case *ast.TypeSpec:
// TODO(adonovan): TypeSpec.{Doc,Comment}?

View File

@ -17,15 +17,16 @@ import (
// A Program is a partial or complete Go program converted to SSA form.
//
type Program struct {
Files *token.FileSet // position information for the files of this Program [TODO: rename Fset]
Packages map[string]*Package // all loaded Packages, keyed by import path [TODO rename packagesByPath]
packages map[*types.Package]*Package // all loaded Packages, keyed by object [TODO rename Packages]
Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects.
Files *token.FileSet // position information for the files of this Program [TODO: rename Fset]
Packages map[string]*Package // all loaded Packages, keyed by import path [TODO rename packagesByPath]
packages map[*types.Package]*Package // all loaded Packages, keyed by object [TODO rename Packages]
Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects.
concreteMethods map[*types.Func]*Function // maps named concrete methods to their code
mode BuilderMode // set of mode bits for SSA construction
methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup]
methodSetsMu sync.Mutex // serializes all accesses to methodSets
concreteMethods map[*types.Func]*Function // maps named concrete methods to their code
mode BuilderMode // set of mode bits for SSA construction
methodSetsMu sync.Mutex // guards methodSets, indirectionWrappers
methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup]
indirectionWrappers map[*Function]*Function // func(*T) wrappers for T-methods
}
// A Package is a single analyzed Go package containing Members for
@ -51,10 +52,11 @@ type Package struct {
// const, var, func and type declarations respectively.
//
type Member interface {
Name() string // the declared name of the package member
String() string // human-readable information about the value
Pos() token.Pos // position of member's declaration, if known
Type() types.Type // the type of the package member
Name() string // the declared name of the package member
String() string // human-readable information about the value
Pos() token.Pos // position of member's declaration, if known
Type() types.Type // the type of the package member
Token() token.Token // token.{VAR,FUNC,CONST,TYPE}
}
// An Id identifies the name of a field of a struct type, or the name
@ -77,20 +79,21 @@ type Id struct {
Name string
}
// A MethodSet contains all the methods for a particular type.
// A MethodSet contains all the methods for a particular type T.
// The method sets for T and *T are distinct entities.
// The methods for a non-pointer type T all have receiver type T, but
// the methods for pointer type *T may have receiver type *T or T.
//
// All methods in the method set for T have a receiver type of exactly
// T. The method set of *T may contain synthetic indirection methods
// that wrap methods whose receiver type is T.
//
type MethodSet map[Id]*Function
// A Type is a Member of a Package representing the name, underlying
// type and method set of a named type declared at package scope.
// A Type is a Member of a Package representing a package-level named type.
//
// Type() returns a *types.Named.
//
type Type struct {
NamedType *types.Named
Methods MethodSet // concrete method set of N
PtrMethods MethodSet // concrete method set of (*N)
Object *types.TypeName
}
// A Constant is a Member of Package representing a package-level
@ -190,6 +193,10 @@ type Instruction interface {
// memory 'local int' and a definition of a pointer y.)
String() string
// Parent returns the function to which this instruction
// belongs.
Parent() *Function
// Block returns the basic block to which this instruction
// belongs.
Block() *BasicBlock
@ -299,7 +306,7 @@ type Function struct {
type BasicBlock struct {
Index int // index of this block within Func.Blocks
Comment string // optional label; no semantic significance
Func *Function // containing function
parent *Function // parent function
Instrs []Instruction // instructions in order
Preds, Succs []*BasicBlock // predecessors and successors
succs2 [2]*BasicBlock // initial space for Succs.
@ -327,10 +334,10 @@ type BasicBlock struct {
// belongs to an enclosing function.
//
type Capture struct {
name string
typ types.Type
pos token.Pos
name string
typ types.Type
pos token.Pos
parent *Function
referrers []Instruction
// Transiently needed during building.
@ -340,10 +347,10 @@ type Capture struct {
// A Parameter represents an input parameter of a function.
//
type Parameter struct {
name string
typ types.Type
pos token.Pos
name string
typ types.Type
pos token.Pos
parent *Function
referrers []Instruction
}
@ -584,8 +591,12 @@ type Convert struct {
// This operation cannot fail. Use TypeAssert for interface
// conversions that may fail dynamically.
//
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
// from an explicit conversion in the source.
// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
// instruction arose from an explicit e.(T) operation; or token.NoPos
// otherwise.
//
// TODO(adonovan) what about the nil case of e = e.(E)? Test.
//
// Example printed form:
// t1 = change interface interface{} <- I (t0)
@ -596,7 +607,9 @@ type ChangeInterface struct {
}
// MakeInterface constructs an instance of an interface type from a
// value and its method-set.
// value of a concrete type.
//
// Use Program.MethodSet(X.Type()) to find the method-set of X.
//
// To construct the zero value of an interface type T, use:
// &Literal{exact.MakeNil(), T}
@ -605,12 +618,12 @@ type ChangeInterface struct {
// from an explicit conversion in the source.
//
// Example printed form:
// t1 = make interface interface{} <- int (42:int)
// t1 = make interface{} <- int (42:int)
// t2 = make Stringer <- t0
//
type MakeInterface struct {
Register
X Value
Methods MethodSet // method set of (non-interface) X
X Value
}
// The MakeClosure instruction yields a closure value whose code is
@ -641,6 +654,7 @@ type MakeClosure struct {
//
// Example printed form:
// t1 = make map[string]int t0
// t1 = make StringIntMap t0
//
type MakeMap struct {
Register
@ -657,6 +671,7 @@ type MakeMap struct {
//
// Example printed form:
// t0 = make chan int 0
// t0 = make IntChan 0
//
type MakeChan struct {
Register
@ -677,7 +692,8 @@ type MakeChan struct {
// created it.
//
// Example printed form:
// t1 = make slice []string 1:int t0
// t1 = make []string 1:int t0
// t1 = make StringSlice 1:int t0
//
type MakeSlice struct {
Register
@ -905,6 +921,11 @@ type Next struct {
// Type() reflects the actual type of the result, possibly a
// 2-types.Tuple; AssertedType is the asserted type.
//
// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
// instruction arose from an explicit e.(T) operation; or token.NoPos
// otherwise.
//
// Example printed form:
// t1 = typeassert t0.(int)
// t3 = typeassert,ok t2.(T)
@ -1168,10 +1189,6 @@ type anInstruction struct {
// receiver but the first true argument. Func is not used in this
// mode.
//
// If the called method's receiver has non-pointer type T, but the
// receiver supplied by the interface value has type *T, an implicit
// load (copy) operation is performed.
//
// Example printed form:
// t1 = invoke t0.String()
// go invoke t3.Run(t2)
@ -1244,21 +1261,25 @@ func (v *Capture) Type() types.Type { return v.typ }
func (v *Capture) Name() string { return v.name }
func (v *Capture) Referrers() *[]Instruction { return &v.referrers }
func (v *Capture) Pos() token.Pos { return v.pos }
func (v *Capture) Parent() *Function { return v.parent }
func (v *Global) Type() types.Type { return v.typ }
func (v *Global) Name() string { return v.name }
func (v *Global) Pos() token.Pos { return v.pos }
func (*Global) Referrers() *[]Instruction { return nil }
func (v *Global) Token() token.Token { return token.VAR }
func (v *Function) Name() string { return v.name }
func (v *Function) Type() types.Type { return v.Signature }
func (v *Function) Pos() token.Pos { return v.pos }
func (*Function) Referrers() *[]Instruction { return nil }
func (v *Function) Token() token.Token { return token.FUNC }
func (v *Parameter) Type() types.Type { return v.typ }
func (v *Parameter) Name() string { return v.name }
func (v *Parameter) Referrers() *[]Instruction { return &v.referrers }
func (v *Parameter) Pos() token.Pos { return v.pos }
func (v *Parameter) Parent() *Function { return v.parent }
func (v *Alloc) Type() types.Type { return v.typ }
func (v *Alloc) Name() string { return v.name }
@ -1274,18 +1295,21 @@ func (v *Register) asRegister() *Register { return v }
func (v *Register) Pos() token.Pos { return v.pos }
func (v *Register) setPos(pos token.Pos) { v.pos = pos }
func (v *anInstruction) Parent() *Function { return v.block.parent }
func (v *anInstruction) Block() *BasicBlock { return v.block }
func (v *anInstruction) SetBlock(block *BasicBlock) { v.block = block }
func (t *Type) Name() string { return t.NamedType.Obj().Name() }
func (t *Type) Pos() token.Pos { return t.NamedType.Obj().Pos() }
func (t *Type) String() string { return t.Name() }
func (t *Type) Type() types.Type { return t.NamedType }
func (t *Type) Name() string { return t.Object.Name() }
func (t *Type) Pos() token.Pos { return t.Object.Pos() }
func (t *Type) String() string { return t.Name() }
func (t *Type) Type() types.Type { return t.Object.Type() }
func (t *Type) Token() token.Token { return token.TYPE }
func (c *Constant) Name() string { return c.name }
func (c *Constant) Pos() token.Pos { return c.pos }
func (c *Constant) String() string { return c.Name() }
func (c *Constant) Type() types.Type { return c.Value.Type() }
func (c *Constant) Name() string { return c.name }
func (c *Constant) Pos() token.Pos { return c.pos }
func (c *Constant) String() string { return c.Name() }
func (c *Constant) Type() types.Type { return c.Value.Type() }
func (c *Constant) Token() token.Token { return token.CONST }
// Func returns the package-level function of the specified name,
// or nil if not found.
@ -1335,7 +1359,8 @@ func (prog *Program) Value(obj types.Object) Value {
}
// Package returns the SSA package corresponding to the specified
// type-checker package object. It returns nil if not found.
// type-checker package object.
// It returns nil if no such SSA package has been created.
//
func (prog *Program) Package(pkg *types.Package) *Package {
return prog.packages[pkg]

View File

@ -45,10 +45,7 @@ func isBlankIdent(e ast.Expr) bool {
// isPointer returns true for types whose underlying type is a pointer.
func isPointer(typ types.Type) bool {
if nt, ok := typ.(*types.Named); ok {
typ = nt.Underlying()
}
_, ok := typ.(*types.Pointer)
_, ok := typ.Underlying().(*types.Pointer)
return ok
}