mirror of
https://github.com/golang/go
synced 2024-09-30 08:18:40 -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:
parent
d6c1c75eab
commit
6c7ce1c2d3
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
33
ssa/emit.go
33
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
|
||||
|
78
ssa/func.go
78
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}
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
15
ssa/lift.go
15
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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
16
ssa/print.go
16
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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
196
ssa/ssa.go
196
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 }
|
||||
|
Loading…
Reference in New Issue
Block a user