1
0
mirror of https://github.com/golang/go synced 2024-09-30 10:18:32 -06:00

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
This commit is contained in:
Alan Donovan 2013-05-30 09:59:17 -04:00
parent d6c1c75eab
commit 6c7ce1c2d3
15 changed files with 242 additions and 251 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}
}

View File

@ -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{

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

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

View File

@ -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

View File

@ -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 {

View File

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

View File

@ -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)

View File

@ -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:

View File

@ -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 }