From 6c7ce1c2d30f2477bee802ee38fd6814806a6b9f Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Thu, 30 May 2013 09:59:17 -0400 Subject: [PATCH] go.tools/ssa: Value.Pos() method + remaining source position plumbing. Implement Pos() method for Values: Parameter, Capture, Phi. (Not Literal, Builtin.) Instructions: UnOp, BinOp, Store. 'address' (an lvalue) now needs position of '*' in "*addr". Also: - Un-export fields Pos_ Type_ Name_ Block_ from various values/instructions. Define NewFunction() as a temporary measure. Will try to eliminate calls from clients... - Remove Implements{Value,Member,Interface} marker methods. I've decided I don't like them. - Func.addParamObj helper. - Various comment fixes. R=gri CC=golang-dev https://golang.org/cl/9740046 --- ssa/builder.go | 79 +++++++++-------- ssa/doc.go | 8 -- ssa/emit.go | 33 ++++--- ssa/func.go | 78 ++++++++++------ ssa/interp/external.go | 4 + ssa/interp/interp.go | 2 +- ssa/interp/reflect.go | 10 +-- ssa/interp/value.go | 3 + ssa/lift.go | 15 ++-- ssa/literal.go | 10 ++- ssa/lvalue.go | 8 +- ssa/print.go | 16 ++-- ssa/promote.go | 23 ++--- ssa/source_ast.go | 8 +- ssa/ssa.go | 196 +++++++++++++++-------------------------- 15 files changed, 242 insertions(+), 251 deletions(-) diff --git a/ssa/builder.go b/ssa/builder.go index ae5e2bbd9d..ddda130b93 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -298,13 +298,14 @@ func (b *Builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value { fn.currentBlock = done phi := &Phi{Edges: edges, Comment: e.Op.String()} - phi.Type_ = phi.Edges[0].Type() + phi.pos = e.OpPos + phi.typ = phi.Edges[0].Type() return done.emit(phi) } // exprN lowers a multi-result expression e to SSA form, emitting code -// to fn and returning a single Value whose type is a *types.Results -// (tuple). The caller must access the components via Extract. +// 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 @@ -324,7 +325,7 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value { // cases for single-valued CallExpr. var c Call b.setCall(fn, e, &c.Call) - c.Type_ = fn.Pkg.TypeOf(e) + c.typ = fn.Pkg.TypeOf(e) return fn.emit(&c) case *ast.IndexExpr: @@ -356,7 +357,7 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value { } // The typechecker sets the type of the expression to just the - // asserted type in the "value, ok" form, not to *types.Result + // asserted type in the "value, ok" form, not to *types.Tuple // (though it includes the valueOk operand in its error messages). tuple.(interface { @@ -588,7 +589,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { if !ok { v = fn.lookup(obj, escaping) } - return address{v} + return address{addr: v} case *ast.CompositeLit: t := fn.Pkg.TypeOf(e).Deref() @@ -599,7 +600,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { v = fn.addLocal(t, e.Lbrace) } b.compLit(fn, v, e, t) // initialize in place - return address{v} + return address{addr: v} case *ast.ParenExpr: return b.addr(fn, e.X, escaping) @@ -608,13 +609,13 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { // p.M where p is a package. if obj := fn.Pkg.isPackageRef(e); obj != nil { if v, ok := b.lookup(fn.Pkg, obj); ok { - return address{v} + return address{addr: v} } panic("undefined package-qualified name: " + obj.Name()) } // e.f where e is an expression. - return address{b.selector(fn, e, true, escaping)} + return address{addr: b.selector(fn, e, true, escaping)} case *ast.IndexExpr: var x Value @@ -643,10 +644,10 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { Index: emitConv(fn, b.expr(fn, e.Index), tInt), } v.setType(et) - return address{fn.emit(v)} + return address{addr: fn.emit(v)} case *ast.StarExpr: - return address{b.expr(fn, e.X)} + return address{addr: b.expr(fn, e.X), star: e.Star} } panic(fmt.Sprintf("unexpected address expression: %T", e)) @@ -698,7 +699,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { case *ast.FuncLit: posn := b.Prog.Files.Position(e.Type.Func) fn2 := &Function{ - Name_: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column), + name: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column), Signature: fn.Pkg.TypeOf(e.Type).Underlying().(*types.Signature), pos: e.Type.Func, Enclosing: fn, @@ -787,10 +788,10 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { 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), fn.Pkg.TypeOf(e)) + return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), fn.Pkg.TypeOf(e), e.OpPos) case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ: - return emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y)) + return emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e.OpPos) default: panic("illegal op in BinaryExpr: " + e.Op.String()) } @@ -1111,7 +1112,7 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { // assignOp emits to fn code to perform loc += incr or loc -= incr. func (b *Builder) assignOp(fn *Function, loc lvalue, incr Value, op token.Token) { oldv := loc.load(fn) - loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, incr, oldv.Type()), loc.typ())) + loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, incr, oldv.Type()), loc.typ(), token.NoPos)) } // buildGlobal emits code to the g.Pkg.Init function for the variable @@ -1183,7 +1184,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global continue } g.spec = nil - lval = address{g} + lval = address{addr: g} } else { // Mode B: initialize all globals. if !isBlankIdent(id) { @@ -1192,7 +1193,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global continue // already done } g2.spec = nil - lval = address{g2} + lval = address{addr: g2} } } if b.Context.Mode&LogSource != 0 { @@ -1245,7 +1246,7 @@ func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { for i, id := range spec.Names { var lval lvalue = blank{} if !isBlankIdent(id) { - lval = address{fn.addNamedLocal(fn.Pkg.ObjectOf(id))} + lval = address{addr: fn.addNamedLocal(fn.Pkg.ObjectOf(id))} } b.exprInPlace(fn, lval, spec.Values[i]) } @@ -1373,7 +1374,7 @@ func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ } faddr.setType(pointer(sf.Type)) fn.emit(faddr) - b.exprInPlace(fn, address{faddr}, e) + b.exprInPlace(fn, address{addr: faddr}, e) } case *types.Array, *types.Slice: @@ -1406,7 +1407,7 @@ func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ } iaddr.setType(pointer(at.Elem())) fn.emit(iaddr) - b.exprInPlace(fn, address{iaddr}, e) + b.exprInPlace(fn, address{addr: iaddr}, e) } if t != at { // slice s := &Slice{X: array} @@ -1498,7 +1499,7 @@ func (b *Builder) switchStmt(fn *Function, s *ast.SwitchStmt, label *lblock) { // instead of BinOp(EQL, tag, b.expr(cond)) // followed by If. Don't forget conversions // though. - cond := emitCompare(fn, token.EQL, tag, b.expr(fn, cond)) + cond := emitCompare(fn, token.EQL, tag, b.expr(fn, cond), token.NoPos) emitIf(fn, cond, body, nextCond) fn.currentBlock = nextCond } @@ -1604,7 +1605,7 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl casetype = fn.Pkg.TypeOf(cond) var condv Value if casetype == tUntypedNil { - condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type())) + condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()), token.NoPos) } else { yok := emitTypeTest(fn, x, casetype) ti = emitExtract(fn, yok, 0, casetype) @@ -1619,8 +1620,8 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl // same name but a more specific type. // Side effect: reassociates binding for y's object. y2 := fn.addNamedLocal(fn.Pkg.ObjectOf(id)) - y2.Name_ += "'" // debugging aid - y2.Type_ = pointer(casetype) + y2.name += "'" // debugging aid + y2.typ = pointer(casetype) emitStore(fn, y2, ti) } fn.targets = &targets{ @@ -1740,7 +1741,7 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { } body := fn.newBasicBlock("select.body") next := fn.newBasicBlock("select.next") - emitIf(fn, emitCompare(fn, token.EQL, idx, intLiteral(int64(state))), body, next) + emitIf(fn, emitCompare(fn, token.EQL, idx, intLiteral(int64(state)), token.NoPos), body, next) fn.currentBlock = body fn.targets = &targets{ tail: fn.targets, @@ -1883,7 +1884,7 @@ func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value body := fn.newBasicBlock("rangeindex.body") done = fn.newBasicBlock("rangeindex.done") - emitIf(fn, emitCompare(fn, token.LSS, incr, length), body, done) + emitIf(fn, emitCompare(fn, token.LSS, incr, length, token.NoPos), body, done) fn.currentBlock = body k = emitLoad(fn, index) @@ -2326,10 +2327,10 @@ func (b *Builder) buildFunction(fn *Function) { // We set Function.Params even though there is no body // code to reference them. This simplifies clients. if recv := fn.Signature.Recv(); recv != nil { - fn.addParam(recv.Name(), recv.Type()) + fn.addParamObj(recv) } fn.Signature.Params().ForEach(func(p *types.Var) { - fn.addParam(p.Name(), p.Type()) + fn.addParamObj(p) }) } return @@ -2365,7 +2366,7 @@ func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.No case *types.Const: pkg.Members[name] = &Constant{ - Name_: name, + name: name, Value: newLiteral(obj.Val(), obj.Type()), pos: obj.Pos(), } @@ -2373,11 +2374,11 @@ func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.No case *types.Var: spec, _ := syntax.(*ast.ValueSpec) g := &Global{ - Pkg: pkg, - Name_: name, - Type_: pointer(obj.Type()), // address - pos: obj.Pos(), - spec: spec, + Pkg: pkg, + name: name, + typ: pointer(obj.Type()), // address + pos: obj.Pos(), + spec: spec, } b.globals[obj] = g pkg.Members[name] = g @@ -2394,7 +2395,7 @@ func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.No } sig := obj.Type().(*types.Signature) fn := &Function{ - Name_: name, + name: name, Signature: sig, pos: obj.Pos(), // (iff syntax) Pkg: pkg, @@ -2548,7 +2549,7 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil // Add init() function (but not to Members since it can't be referenced). p.Init = &Function{ - Name_: "init", + name: "init", Signature: new(types.Signature), Pkg: p, Prog: b.Prog, @@ -2587,9 +2588,9 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil // Add initializer guard variable. initguard := &Global{ - Pkg: p, - Name_: "init$guard", - Type_: pointer(tBool), + Pkg: p, + name: "init$guard", + typ: pointer(tBool), } p.Members[initguard.Name()] = initguard diff --git a/ssa/doc.go b/ssa/doc.go index 71c04dc422..8b635dff4b 100644 --- a/ssa/doc.go +++ b/ssa/doc.go @@ -117,12 +117,4 @@ // defs/uses of named variables together, esp. because SSA splits them // into separate webs. // -// TODO(adonovan): it is practically impossible for clients to -// construct well-formed SSA functions/packages/programs directly; we -// assume this is the job of the ssa.Builder alone. -// Nonetheless it may be wise to give clients a little more -// flexibility. For example, analysis tools may wish to construct a -// fake ssa.Function for the root of the callgraph, a fake "reflect" -// package, etc. -// package ssa diff --git a/ssa/emit.go b/ssa/emit.go index dc639fc490..e5e3834b1c 100644 --- a/ssa/emit.go +++ b/ssa/emit.go @@ -13,19 +13,20 @@ import ( // func emitNew(f *Function, typ types.Type, pos token.Pos) Value { return f.emit(&Alloc{ - Type_: pointer(typ), - Heap: true, - pos: pos, + typ: pointer(typ), + Heap: true, + pos: pos, }) } // emitLoad emits to f an instruction to load the address addr into a // new temporary, and returns the value so defined. // -func emitLoad(f *Function, addr Value) Value { +func emitLoad(f *Function, addr Value) *UnOp { v := &UnOp{Op: token.MUL, X: addr} v.setType(addr.Type().Deref()) - return f.emit(v) + f.emit(v) + return v } // emitArith emits to f code to compute the binary operation op(x, y) @@ -33,7 +34,7 @@ func emitLoad(f *Function, addr Value) Value { // (Use emitCompare() for comparisons and Builder.logicalBinop() for // non-eager operations.) // -func emitArith(f *Function, op token.Token, x, y Value, t types.Type) Value { +func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.Pos) Value { switch op { case token.SHL, token.SHR: x = emitConv(f, x, t) @@ -52,6 +53,7 @@ func emitArith(f *Function, op token.Token, x, y Value, t types.Type) Value { X: x, Y: y, } + v.setPos(pos) v.setType(t) return f.emit(v) } @@ -59,7 +61,7 @@ func emitArith(f *Function, op token.Token, x, y Value, t types.Type) Value { // emitCompare emits to f code compute the boolean result of // comparison comparison 'x op y'. // -func emitCompare(f *Function, op token.Token, x, y Value) Value { +func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value { xt := x.Type().Underlying() yt := y.Type().Underlying() @@ -95,6 +97,7 @@ func emitCompare(f *Function, op token.Token, x, y Value) Value { X: x, Y: y, } + v.setPos(pos) v.setType(tBool) return f.emit(v) } @@ -197,11 +200,13 @@ func emitConv(f *Function, val Value, typ types.Type) Value { // emitStore emits to f an instruction to store value val at location // addr, applying implicit conversions as required by assignabilty rules. // -func emitStore(f *Function, addr, val Value) { - f.emit(&Store{ +func emitStore(f *Function, addr, val Value) *Store { + s := &Store{ Addr: addr, Val: emitConv(f, val, addr.Type().Deref()), - }) + } + f.emit(s) + return s } // emitJump emits to f a jump to target, and updates the control-flow graph. @@ -233,7 +238,7 @@ func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) { func emitExtract(f *Function, tuple Value, index int, typ types.Type) Value { e := &Extract{Tuple: tuple, Index: index} // In all cases but one (tSelect's recv), typ is redundant w.r.t. - // tuple.Type().(*types.Result).Values[index].Type. + // tuple.Type().(*types.Tuple).Values[index].Type. e.setType(typ) return f.emit(e) } @@ -267,7 +272,7 @@ func emitTypeTest(f *Function, x Value, t types.Type) Value { // TODO(adonovan): opt: simplify infallible tests as per // emitTypeAssert, and return (x, vTrue). // (Requires that exprN returns a slice of extracted values, - // not a single Value of type *types.Results.) + // not a single Value of type *types.Tuple.) a := &TypeAssert{ X: x, AssertedType: t, @@ -290,9 +295,9 @@ func emitTailCall(f *Function, call *Call) { tresults := f.Signature.Results() nr := tresults.Len() if nr == 1 { - call.Type_ = tresults.At(0).Type() + call.typ = tresults.At(0).Type() } else { - call.Type_ = tresults + call.typ = tresults } tuple := f.emit(call) var ret Ret diff --git a/ssa/func.go b/ssa/func.go index 34340ca939..688b8ee676 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -164,28 +164,36 @@ func (f *Function) labelledBlock(label *ast.Ident) *lblock { } // addParam adds a (non-escaping) parameter to f.Params of the -// specified name and type. +// specified name, type and source position. // -func (f *Function) addParam(name string, typ types.Type) *Parameter { +func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter { v := &Parameter{ - Name_: name, - Type_: typ, + name: name, + typ: typ, + pos: pos, } f.Params = append(f.Params, v) return v } +func (f *Function) addParamObj(obj types.Object) *Parameter { + name := obj.Name() + if name == "" { + name = fmt.Sprintf("arg%d", len(f.Params)) + } + return f.addParam(name, obj.Type(), obj.Pos()) +} + // addSpilledParam declares a parameter that is pre-spilled to the // stack; the function body will load/store the spilled location. // Subsequent lifting will eliminate spills where possible. // func (f *Function) addSpilledParam(obj types.Object) { - name := obj.Name() - param := f.addParam(name, obj.Type()) + param := f.addParamObj(obj) spill := &Alloc{ - Name_: name + "~", // "~" means "spilled" - Type_: pointer(obj.Type()), - pos: obj.Pos(), + name: obj.Name() + "~", // "~" means "spilled" + typ: pointer(obj.Type()), + pos: obj.Pos(), } f.objects[obj] = spill f.Locals = append(f.Locals, spill) @@ -223,8 +231,7 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) { } // Anonymous receiver? No need to spill. if field.Names == nil { - recvVar := f.Signature.Recv() - f.addParam(recvVar.Name(), recvVar.Type()) + f.addParamObj(f.Signature.Recv()) } } } @@ -238,8 +245,7 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) { } // Anonymous parameter? No need to spill. if field.Names == nil { - paramVar := f.Signature.Params().At(len(f.Params) - n) - f.addParam(paramVar.Name(), paramVar.Type()) + f.addParamObj(f.Signature.Params().At(len(f.Params) - n)) } } } @@ -269,8 +275,8 @@ func numberRegisters(f *Function) { switch instr := instr.(type) { case *Alloc: // Allocs may be named at birth. - if instr.Name_ == "" { - instr.Name_ = fmt.Sprintf("a%d", a) + if instr.name == "" { + instr.name = fmt.Sprintf("a%d", a) a++ } case Value: @@ -374,7 +380,7 @@ func (f *Function) removeNilBlocks() { // func (f *Function) addNamedLocal(obj types.Object) *Alloc { l := f.addLocal(obj.Type(), obj.Pos()) - l.Name_ = obj.Name() + l.name = obj.Name() f.objects[obj] = l return l } @@ -383,7 +389,7 @@ func (f *Function) addNamedLocal(obj types.Object) *Alloc { // to function f and returns it. pos is the optional source location. // func (f *Function) addLocal(typ types.Type, pos token.Pos) *Alloc { - v := &Alloc{Type_: pointer(typ), pos: pos} + v := &Alloc{typ: pointer(typ), pos: pos} f.Locals = append(f.Locals, v) f.emit(v) return v @@ -409,8 +415,9 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value { } outer := f.Enclosing.lookup(obj, true) // escaping v := &Capture{ - Name_: outer.Name(), - Type_: outer.Type(), + name: outer.Name(), + typ: outer.Type(), + pos: outer.Pos(), outer: outer, } f.objects[obj] = v @@ -449,7 +456,7 @@ func (f *Function) fullName(from *Package) string { // Anonymous? if f.Enclosing != nil { - return f.Name_ + return f.name } recv := f.Signature.Recv() @@ -459,25 +466,25 @@ func (f *Function) fullName(from *Package) string { var recvType types.Type if recv != nil { recvType = recv.Type() // bridge method - } else if strings.HasPrefix(f.Name_, "bound$") { - return f.Name_ // bound method thunk + } else if strings.HasPrefix(f.name, "bound$") { + return f.name // bound method thunk } else { recvType = f.Params[0].Type() // interface method thunk } - return fmt.Sprintf("(%s).%s", recvType, f.Name_) + return fmt.Sprintf("(%s).%s", recvType, f.name) } // Declared method? if recv != nil { - return fmt.Sprintf("(%s).%s", recv.Type(), f.Name_) + return fmt.Sprintf("(%s).%s", recv.Type(), f.name) } // Package-level function. // Prefix with package name for cross-package references only. if from != f.Pkg { - return fmt.Sprintf("%s.%s", f.Pkg.Types.Path(), f.Name_) + return fmt.Sprintf("%s.%s", f.Pkg.Types.Path(), f.name) } - return f.Name_ + return f.name } // writeSignature writes to w the signature sig in declaration syntax. @@ -614,3 +621,22 @@ func (f *Function) newBasicBlock(comment string) *BasicBlock { f.Blocks = append(f.Blocks, b) return b } + +// NewFunction returns a new synthetic Function instance with its name +// and signature fields set as specified. +// +// The caller is responsible for initializing the remaining fields of +// the function object, e.g. Pkg, Prog, Params, Blocks. +// +// It is practically impossible for clients to construct well-formed +// SSA functions/packages/programs directly, so we assume this is the +// job of the Builder alone. NewFunction exists to provide clients a +// little flexibility. For example, analysis tools may wish to +// construct fake Functions for the root of the callgraph, a fake +// "reflect" package, etc. +// +// TODO(adonovan): think harder about the API here. +// +func NewFunction(name string, sig *types.Signature) *Function { + return &Function{name: name, Signature: sig} +} diff --git a/ssa/interp/external.go b/ssa/interp/external.go index f2533076b7..78d637ee92 100644 --- a/ssa/interp/external.go +++ b/ssa/interp/external.go @@ -19,6 +19,10 @@ import ( type externalFn func(fn *ssa.Function, args []value) value +// TODO(adonovan): fix: reflect.Value abstracts an lvalue or an +// rvalue; Set() causes mutations that can be observed via aliases. +// We have not captured that correctly here. + // Key strings are from Function.FullName(). // That little dot ۰ is an Arabic zero numeral (U+06F0), categories [Nd]. var externals = map[string]externalFn{ diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go index c428237438..35bc06945a 100644 --- a/ssa/interp/interp.go +++ b/ssa/interp/interp.go @@ -310,7 +310,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings} case *ssa.Phi: - for i, pred := range instr.Block_.Preds { + for i, pred := range instr.Block().Preds { if fr.prevBlock == pred { fr.env[instr] = fr.get(instr.Edges[i]) break diff --git a/ssa/interp/reflect.go b/ssa/interp/reflect.go index 4c3008614e..19a4500a14 100644 --- a/ssa/interp/reflect.go +++ b/ssa/interp/reflect.go @@ -389,16 +389,14 @@ func ext۰reflect۰error۰Error(fn *ssa.Function, args []value) value { // newMethod creates a new method of the specified name, package and receiver type. func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function { - fn := &ssa.Function{ - Name_: name, - Pkg: pkg, - Prog: pkg.Prog, - } // TODO(adonovan): fix: hack: currently the only part of Signature // that is needed is the "pointerness" of Recv.Type, and for // now, we'll set it to always be false since we're only // concerned with rtype. Encapsulate this better. - fn.Signature = types.NewSignature(types.NewVar(nil, "recv", recvType), nil, nil, false) + sig := types.NewSignature(types.NewVar(nil, "recv", recvType), nil, nil, false) + fn := ssa.NewFunction(name, sig) + fn.Pkg = pkg + fn.Prog = pkg.Prog return fn } diff --git a/ssa/interp/value.go b/ssa/interp/value.go index 3c2542ab88..ad7ca984a6 100644 --- a/ssa/interp/value.go +++ b/ssa/interp/value.go @@ -87,6 +87,9 @@ func hashString(s string) int { // hashType returns a hash for t such that // types.IsIdentical(x, y) => hashType(x) == hashType(y). func hashType(t types.Type) int { + // TODO(adonovan): fix: not sound! "IsIdentical" Signatures + // may have different parameter names; use typemap.Hasher when + // available. return hashString(t.String()) // TODO(gri): provide a better hash } diff --git a/ssa/lift.go b/ssa/lift.go index bc836a4a76..c92c02c156 100644 --- a/ssa/lift.go +++ b/ssa/lift.go @@ -127,8 +127,7 @@ func lift(fn *Function) { // i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)). // Unclear. // - // But we will start with the simplest correct code to make - // life easier for reviewers. + // But we will start with the simplest correct code. buildDomTree(fn) @@ -304,8 +303,13 @@ type newPhiMap map[*BasicBlock][]newPhi // and returns true. // func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { - // Don't lift aggregates into registers. - // We'll need a separate SRA pass for that. + // Don't lift aggregates into registers, because we don't have + // a way to express their zero-literals. + // TODO(adonovan): define zero-literals for aggregates, or + // add a separate SRA pass. Lifting aggregates is an + // important optimisation for pointer analysis because the + // extra indirection really hurts precision under Das's + // algorithm. switch alloc.Type().Deref().Underlying().(type) { case *types.Array, *types.Struct: return false @@ -374,8 +378,9 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { Edges: make([]Value, len(v.Preds)), Comment: alloc.Name(), } + phi.pos = alloc.Pos() phi.setType(alloc.Type().Deref()) - phi.Block_ = v + phi.block = v if debugLifting { fmt.Fprintf(os.Stderr, "place %s = %s at block %s\n", phi.Name(), phi, v) } diff --git a/ssa/literal.go b/ssa/literal.go index ad07ad4bb5..24ea0ffb73 100644 --- a/ssa/literal.go +++ b/ssa/literal.go @@ -4,6 +4,7 @@ package ssa import ( "fmt" + "go/token" "strconv" "code.google.com/p/go.tools/go/exact" @@ -74,17 +75,22 @@ func (l *Literal) Name() string { } else { s = l.Value.String() } - return s + ":" + l.Type_.String() + return s + ":" + l.typ.String() } func (l *Literal) Type() types.Type { - return l.Type_ + return l.typ } func (l *Literal) Referrers() *[]Instruction { return nil } +func (l *Literal) Pos() token.Pos { + // TODO(adonovan): Literals don't have positions. Should they? + return token.NoPos +} + // IsNil returns true if this literal represents a typed or untyped nil value. func (l *Literal) IsNil() bool { return l.Value.Kind() == exact.Nil diff --git a/ssa/lvalue.go b/ssa/lvalue.go index 1f5b4cfb6c..e1e18690d5 100644 --- a/ssa/lvalue.go +++ b/ssa/lvalue.go @@ -5,6 +5,7 @@ package ssa import ( "code.google.com/p/go.tools/go/types" + "go/token" ) // An lvalue represents an assignable location that may appear on the @@ -20,14 +21,17 @@ type lvalue interface { // An address is an lvalue represented by a true pointer. type address struct { addr Value + star token.Pos // source position, if explicit *addr } func (a address) load(fn *Function) Value { - return emitLoad(fn, a.addr) + load := emitLoad(fn, a.addr) + load.pos = a.star + return load } func (a address) store(fn *Function, v Value) { - emitStore(fn, a.addr, v) + emitStore(fn, a.addr, v).pos = a.star } func (a address) typ() types.Type { diff --git a/ssa/print.go b/ssa/print.go index 92d5f86027..a4067f9b3e 100644 --- a/ssa/print.go +++ b/ssa/print.go @@ -73,7 +73,7 @@ func (v *Function) String() string { // FullName returns g's package-qualified name. func (g *Global) FullName() string { - return fmt.Sprintf("%s.%s", g.Pkg.Types.Path(), g.Name_) + return fmt.Sprintf("%s.%s", g.Pkg.Types.Path(), g.name) } // Instruction.String() @@ -95,8 +95,8 @@ func (v *Phi) String() string { } // Be robust against malformed CFG. blockname := "?" - if v.Block_ != nil && i < len(v.Block_.Preds) { - blockname = v.Block_.Preds[i].String() + if v.block != nil && i < len(v.block.Preds) { + blockname = v.block.Preds[i].String() } b.WriteString(blockname) b.WriteString(": ") @@ -275,8 +275,8 @@ func (v *Extract) String() string { func (s *Jump) String() string { // Be robust against malformed CFG. blockname := "?" - if s.Block_ != nil && len(s.Block_.Succs) == 1 { - blockname = s.Block_.Succs[0].String() + if s.block != nil && len(s.block.Succs) == 1 { + blockname = s.block.Succs[0].String() } return fmt.Sprintf("jump %s", blockname) } @@ -284,9 +284,9 @@ func (s *Jump) String() string { func (s *If) String() string { // Be robust against malformed CFG. tblockname, fblockname := "?", "?" - if s.Block_ != nil && len(s.Block_.Succs) == 2 { - tblockname = s.Block_.Succs[0].String() - fblockname = s.Block_.Succs[1].String() + if s.block != nil && len(s.block.Succs) == 2 { + tblockname = s.block.Succs[0].String() + fblockname = s.block.Succs[1].String() } return fmt.Sprintf("if %s goto %s else %s", relName(s.Cond, s), tblockname, fblockname) } diff --git a/ssa/promote.go b/ssa/promote.go index 965b748c8d..71fc48ecbc 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -6,9 +6,9 @@ package ssa // synthetic "bridge" methods. import ( - "fmt" - "code.google.com/p/go.tools/go/types" + "fmt" + "go/token" ) // anonFieldPath is a linked list of anonymous fields entered by @@ -266,7 +266,7 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function } fn := &Function{ - Name_: cand.method.Name(), + name: cand.method.Name(), Signature: sig, Prog: prog, } @@ -324,15 +324,10 @@ func createParams(fn *Function) { var last *Parameter tparams := fn.Signature.Params() for i, n := 0, tparams.Len(); i < n; i++ { - p := tparams.At(i) - name := p.Name() - if name == "" { - name = fmt.Sprintf("arg%d", i) - } - last = fn.addParam(name, p.Type()) + last = fn.addParamObj(tparams.At(i)) } if fn.Signature.IsVariadic() { - last.Type_ = types.NewSlice(last.Type_) + last.typ = types.NewSlice(last.typ) } } @@ -366,12 +361,12 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function { index, meth := methodIndex(itf, id) sig := *meth.Type().(*types.Signature) // copy; shared Values fn := &Function{ - Name_: meth.Name(), + name: meth.Name(), Signature: &sig, Prog: prog, } fn.startBody() - fn.addParam("recv", typ) + fn.addParam("recv", typ, token.NoPos) createParams(fn) var c Call c.Call.Method = index @@ -410,12 +405,12 @@ func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function { } s := meth.Signature fn := &Function{ - Name_: "bound$" + meth.FullName(), + name: "bound$" + meth.FullName(), Signature: types.NewSignature(nil, s.Params(), s.Results(), s.IsVariadic()), // drop recv Prog: prog, } - cap := &Capture{Name_: "recv", Type_: recv.Type()} + cap := &Capture{name: "recv", typ: recv.Type()} fn.FreeVars = []*Capture{cap} fn.startBody() createParams(fn) diff --git a/ssa/source_ast.go b/ssa/source_ast.go index 6882e7dbc9..af3b896d10 100644 --- a/ssa/source_ast.go +++ b/ssa/source_ast.go @@ -531,7 +531,13 @@ func NodeDescription(n ast.Node) string { case *ast.ExprStmt: return "expression statement" case *ast.Field: - return "name/type pair" + // Can be any of these: + // struct {x, y int} -- struct field(s) + // struct {T} -- anon struct field + // interface {I} -- interface embedding + // interface {f()} -- interface method + // func (A) func(B) C -- receiver, param(s), result(s) + return "field/method/parameter" case *ast.FieldList: return "field/method/parameter list" case *ast.File: diff --git a/ssa/ssa.go b/ssa/ssa.go index c46606bcb7..a15337e0cf 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -55,11 +55,10 @@ 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 - ImplementsMember() // dummy method to indicate the "implements" relation. + 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 } // An Id identifies the name of a field of a struct type, or the name @@ -107,8 +106,12 @@ type Type struct { // NB: a Constant is not a Value; it contains a literal Value, which // it augments with the name and position of its 'const' declaration. // +// TODO(adonovan): if we decide to add a token.Pos to literal, we +// should then add a name too, and merge Constant and Literal. +// Experiment. +// type Constant struct { - Name_ string + name string Value *Literal pos token.Pos } @@ -154,8 +157,16 @@ type Value interface { // Instruction.Operands contains the inverse of this relation. Referrers() *[]Instruction - // Dummy method to indicate the "implements" relation. - ImplementsValue() + // Pos returns the location of the source construct that + // gave rise to this value, or token.NoPos if it was not + // explicit in the source. + // + // For each ast.Expr type, a particular field is designated as + // the canonical location for the expression, e.g. the Lparen + // for an *ast.CallExpr. This enables us to find the value + // corresponding to a given piece of source syntax. + // + Pos() token.Pos } // An Instruction is an SSA instruction that computes a new Value or @@ -219,9 +230,6 @@ type Instruction interface { // syntax. // Pos() token.Pos - - // Dummy method to indicate the "implements" relation. - ImplementsInstruction() } // Function represents the parameters, results and code of a function @@ -254,10 +262,10 @@ type Instruction interface { // Type() returns the function's Signature. // type Function struct { - Name_ string + name string Signature *types.Signature - pos token.Pos + Enclosing *Function // enclosing function if anon; nil if global Pkg *Package // enclosing package for Go source functions; otherwise nil Prog *Program // enclosing program @@ -319,9 +327,14 @@ type BasicBlock struct { // capture represents the receiver value and may be of any type that // has concrete methods. // +// Pos() returns the position of the value that was captured, which +// belongs to an enclosing function. +// type Capture struct { - Name_ string - Type_ types.Type + name string + typ types.Type + pos token.Pos + referrers []Instruction // Transiently needed during building. @@ -331,8 +344,10 @@ type Capture struct { // A Parameter represents an input parameter of a function. // type Parameter struct { - Name_ string - Type_ types.Type + name string + typ types.Type + pos token.Pos + referrers []Instruction } @@ -355,13 +370,15 @@ type Parameter struct { // Type(), using the same representation as package go/exact uses for // constants. // +// Pos() returns token.NoPos. +// // Example printed form: // 42:int // "hello":untyped string // 3+4i:MyComplex // type Literal struct { - Type_ types.Type + typ types.Type Value exact.Value } @@ -372,10 +389,11 @@ type Literal struct { // identifier. // type Global struct { - Name_ string - Type_ types.Type - Pkg *Package - pos token.Pos + name string + typ types.Type + pos token.Pos + + Pkg *Package // The following fields are set transiently during building, // then cleared. @@ -427,8 +445,8 @@ type Builtin struct { // type Alloc struct { anInstruction - Name_ string - Type_ types.Type + name string + typ types.Type Heap bool pos token.Pos referrers []Instruction @@ -440,7 +458,9 @@ type Alloc struct { // value. Within a block, all φ-nodes must appear before all non-φ // nodes. // -// Pos() returns NoPos. +// Pos() returns the position of the && or || for short-circuit +// control-flow joins, or that of the *Alloc for φ-nodes inserted +// during SSA renaming. // // Example printed form: // t2 = phi [0.start: t0, 1.if.then: t1, ...] @@ -474,7 +494,6 @@ type Call struct { // The BinOp instruction yields the result of binary operation X Op Y. // // Pos() returns the ast.BinaryExpr.OpPos, if explicit in the source. -// TODO(adonovan): implement. // // Example printed form: // t1 = t0 + 1:int @@ -499,7 +518,7 @@ type BinOp struct { // and a boolean indicating the success of the receive. The // components of the tuple are accessed using Extract. // -// Pos() returns the ast.UnaryExpr.OpPos, if explicit in the source. +// Pos() returns the ast.UnaryExpr.OpPos, if explicit in the source, // // Example printed form: // t0 = *x @@ -830,7 +849,7 @@ type Select struct { // // Elements are accessed via Next. // -// Type() returns a (possibly named) *types.Result (tuple type). +// Type() returns a (possibly named) *types.Tuple. // // Pos() returns the ast.RangeStmt.For. // @@ -854,8 +873,8 @@ type Range struct { // over maps, as the Type() alone is insufficient: consider // map[int]rune. // -// Type() returns a *types.Result (tuple type) for the triple -// (ok, k, v). The types of k and/or v may be types.Invalid. +// Type() returns a *types.Tuple for the triple (ok, k, v). +// The types of k and/or v may be types.Invalid. // // Example printed form: // t1 = next t0 @@ -887,8 +906,8 @@ type Next struct { // If AssertedType is a superinterface of X.Type(), the operation // cannot fail; ChangeInterface is preferred in this case. // -// Type() reflects the actual type of the result, possibly a pair -// (types.Result); AssertedType is the asserted type. +// Type() reflects the actual type of the result, possibly a +// 2-types.Tuple; AssertedType is the asserted type. // // Example printed form: // t1 = typeassert t0.(int) @@ -1060,7 +1079,6 @@ type Send struct { // Stores can be of arbitrary types. // // Pos() returns the ast.StarExpr.Star, if explicit in the source. -// TODO(addr): implement. // // Example printed form: // *x = y @@ -1107,15 +1125,15 @@ type MapUpdate struct { type Register struct { anInstruction num int // "name" of virtual register, e.g. "t0". Not guaranteed unique. + typ types.Type // type of virtual register pos token.Pos // position of source expression, or NoPos - Type_ types.Type // type of virtual register referrers []Instruction } // anInstruction is a mix-in embedded by all Instructions. // It provides the implementations of the Block and SetBlock methods. type anInstruction struct { - Block_ *BasicBlock // the basic block of this instruction + block *BasicBlock // the basic block of this instruction } // CallCommon is contained by Go, Defer and Call to hold the @@ -1224,31 +1242,35 @@ func (c *CallCommon) Description() string { func (v *Builtin) Type() types.Type { return v.Object.Type() } func (v *Builtin) Name() string { return v.Object.Name() } func (*Builtin) Referrers() *[]Instruction { return nil } +func (v *Builtin) Pos() token.Pos { return token.NoPos } -func (v *Capture) Type() types.Type { return v.Type_ } -func (v *Capture) Name() string { return v.Name_ } +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 *Global) Type() types.Type { return v.Type_ } -func (v *Global) Name() string { return v.Name_ } +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 *Function) Name() string { return v.Name_ } +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 *Parameter) Type() types.Type { return v.Type_ } -func (v *Parameter) Name() string { return v.Name_ } +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 *Alloc) Type() types.Type { return v.Type_ } -func (v *Alloc) Name() string { return v.Name_ } +func (v *Alloc) Type() types.Type { return v.typ } +func (v *Alloc) Name() string { return v.name } func (v *Alloc) Referrers() *[]Instruction { return &v.referrers } +func (v *Alloc) Pos() token.Pos { return v.pos } -func (v *Register) Type() types.Type { return v.Type_ } -func (v *Register) setType(typ types.Type) { v.Type_ = typ } +func (v *Register) Type() types.Type { return v.typ } +func (v *Register) setType(typ types.Type) { v.typ = typ } func (v *Register) Name() string { return fmt.Sprintf("t%d", v.num) } func (v *Register) setNum(num int) { v.num = num } func (v *Register) Referrers() *[]Instruction { return &v.referrers } @@ -1256,8 +1278,8 @@ 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) Block() *BasicBlock { return v.Block_ } -func (v *anInstruction) SetBlock(block *BasicBlock) { v.Block_ = block } +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() } @@ -1266,7 +1288,7 @@ func (t *Type) Type() types.Type { return t.NamedType } func (p *Package) Name() string { return p.Types.Name() } -func (c *Constant) Name() string { return c.Name_ } +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() } @@ -1303,82 +1325,6 @@ func (p *Package) Type(name string) (t *Type) { return } -// "Implements" relation boilerplate. -// Don't try to factor this using promotion and mix-ins: the long-hand -// form serves as better documentation, including in godoc. - -func (*Alloc) ImplementsValue() {} -func (*BinOp) ImplementsValue() {} -func (*Builtin) ImplementsValue() {} -func (*Call) ImplementsValue() {} -func (*Capture) ImplementsValue() {} -func (*ChangeInterface) ImplementsValue() {} -func (*ChangeType) ImplementsValue() {} -func (*Convert) ImplementsValue() {} -func (*Extract) ImplementsValue() {} -func (*Field) ImplementsValue() {} -func (*FieldAddr) ImplementsValue() {} -func (*Function) ImplementsValue() {} -func (*Global) ImplementsValue() {} -func (*Index) ImplementsValue() {} -func (*IndexAddr) ImplementsValue() {} -func (*Literal) ImplementsValue() {} -func (*Lookup) ImplementsValue() {} -func (*MakeChan) ImplementsValue() {} -func (*MakeClosure) ImplementsValue() {} -func (*MakeInterface) ImplementsValue() {} -func (*MakeMap) ImplementsValue() {} -func (*MakeSlice) ImplementsValue() {} -func (*Next) ImplementsValue() {} -func (*Parameter) ImplementsValue() {} -func (*Phi) ImplementsValue() {} -func (*Range) ImplementsValue() {} -func (*Select) ImplementsValue() {} -func (*Slice) ImplementsValue() {} -func (*TypeAssert) ImplementsValue() {} -func (*UnOp) ImplementsValue() {} - -func (*Constant) ImplementsMember() {} -func (*Function) ImplementsMember() {} -func (*Global) ImplementsMember() {} -func (*Type) ImplementsMember() {} - -func (*Alloc) ImplementsInstruction() {} -func (*BinOp) ImplementsInstruction() {} -func (*Call) ImplementsInstruction() {} -func (*ChangeInterface) ImplementsInstruction() {} -func (*ChangeType) ImplementsInstruction() {} -func (*Convert) ImplementsInstruction() {} -func (*Defer) ImplementsInstruction() {} -func (*Extract) ImplementsInstruction() {} -func (*Field) ImplementsInstruction() {} -func (*FieldAddr) ImplementsInstruction() {} -func (*Go) ImplementsInstruction() {} -func (*If) ImplementsInstruction() {} -func (*Index) ImplementsInstruction() {} -func (*IndexAddr) ImplementsInstruction() {} -func (*Jump) ImplementsInstruction() {} -func (*Lookup) ImplementsInstruction() {} -func (*MakeChan) ImplementsInstruction() {} -func (*MakeClosure) ImplementsInstruction() {} -func (*MakeInterface) ImplementsInstruction() {} -func (*MakeMap) ImplementsInstruction() {} -func (*MakeSlice) ImplementsInstruction() {} -func (*MapUpdate) ImplementsInstruction() {} -func (*Next) ImplementsInstruction() {} -func (*Panic) ImplementsInstruction() {} -func (*Phi) ImplementsInstruction() {} -func (*Range) ImplementsInstruction() {} -func (*Ret) ImplementsInstruction() {} -func (*RunDefers) ImplementsInstruction() {} -func (*Select) ImplementsInstruction() {} -func (*Send) ImplementsInstruction() {} -func (*Slice) ImplementsInstruction() {} -func (*Store) ImplementsInstruction() {} -func (*TypeAssert) ImplementsInstruction() {} -func (*UnOp) ImplementsInstruction() {} - -func (v *Alloc) Pos() token.Pos { return v.pos } func (v *Call) Pos() token.Pos { return v.Call.pos } func (s *Defer) Pos() token.Pos { return s.Call.pos } func (s *Go) Pos() token.Pos { return s.Call.pos }