mirror of
https://github.com/golang/go
synced 2024-11-18 16:54:43 -07:00
go.tools/ssa: extend debug information to arbitrary ast.Exprs.
CanonicalPos was inadequate since many pairs of instruction share the same pos (e.g. Allocs and Phis). Instead, we generalize the DebugRef instruction to associate not just Idents but Exprs with ssa.Values. We no longer store any DebugRefs for constant expressions, to save space. (The type and value of such expressions can be obtained by other means, at a cost in complexity.) Function.ValueForExpr queries the DebugRef info to return the ssa.Value of a given Expr. Added tests. Also: - the DebugInfo flag is now per package, not global. It must be set between Create and Build phases if desired. - {Value,Instruction}.Pos() documentation updated: we still maintain this information in the instruction stream even in non-debug mode, but we make fewer claims about its invariants. - Go and Defer instructions can now use their respective go/defer token positions (not the call's lparen), so they do. - SelectState: Posn token.Pos indicates the <- position DebugNode ast.Expr is the send stmt or receive expr. - In building SelectStmt, we introduce extra temporaries in debug mode to hold the result of the receive in 'case <-ch' even though this value isn't ordinarily needed. - Use *SelectState (indirectly) since the struct is getting bigger. - Document some missing instructions in doc.go. R=gri CC=golang-dev https://golang.org/cl/12147043
This commit is contained in:
parent
e5cfd92deb
commit
c28bf6e069
@ -38,7 +38,7 @@ func main() {
|
||||
|
||||
f, err := parser.ParseFile(imp.Fset, "<input>", test, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ const (
|
||||
SanityCheckFunctions // Perform sanity checking of function bodies
|
||||
NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers
|
||||
BuildSerially // Build packages serially, not in parallel.
|
||||
DebugInfo // Include DebugRef instructions [TODO(adonovan): finer grain?]
|
||||
)
|
||||
|
||||
// NewProgram returns a new SSA Program initially containing no
|
||||
|
17
ssa/doc.go
17
ssa/doc.go
@ -59,6 +59,7 @@
|
||||
// *ChangeType ✔ ✔
|
||||
// *Const ✔
|
||||
// *Convert ✔ ✔
|
||||
// *DebugRef ✔
|
||||
// *Defer ✔
|
||||
// *Extract ✔ ✔
|
||||
// *Field ✔ ✔
|
||||
@ -86,7 +87,9 @@
|
||||
// *Ret ✔
|
||||
// *RunDefers ✔
|
||||
// *Select ✔ ✔
|
||||
// *Send ✔
|
||||
// *Slice ✔ ✔
|
||||
// *Store ✔
|
||||
// *Type ✔ (type)
|
||||
// *TypeAssert ✔ ✔
|
||||
// *UnOp ✔ ✔
|
||||
@ -110,16 +113,14 @@
|
||||
// TODO(adonovan): Consider the exceptional control-flow implications
|
||||
// of defer and recover().
|
||||
//
|
||||
// TODO(adonovan): Consider how token.Pos source location information
|
||||
// should be made available generally. Currently it is only present
|
||||
// in package Members and selected Instructions for which there is a
|
||||
// direct source correspondence. We'll need to work harder to tie all
|
||||
// defs/uses of named variables together, esp. because SSA splits them
|
||||
// into separate webs.
|
||||
//
|
||||
// TODO(adonovan): write an example showing how to visit all functions
|
||||
// in a Program, including package init functions, methods of named
|
||||
// and anon types, and functions used as values but never called
|
||||
// directly.
|
||||
// directly. See AllFunctions().
|
||||
//
|
||||
// TODO(adonovan): write a how-to document for all the various cases
|
||||
// of trying to determine corresponding elements across the four
|
||||
// domains of source locations, ast.Nodes, types.Objects,
|
||||
// ssa.Values/Instructions.
|
||||
//
|
||||
package ssa
|
||||
|
22
ssa/emit.go
22
ssa/emit.go
@ -31,22 +31,28 @@ func emitLoad(f *Function, addr Value) *UnOp {
|
||||
}
|
||||
|
||||
// emitDebugRef emits to f a DebugRef pseudo-instruction associating
|
||||
// reference id with local var/const value v.
|
||||
// expression e with value v.
|
||||
//
|
||||
func emitDebugRef(f *Function, id *ast.Ident, v Value) {
|
||||
func emitDebugRef(f *Function, e ast.Expr, v Value) {
|
||||
if !f.debugInfo() {
|
||||
return // debugging not enabled
|
||||
}
|
||||
if isBlankIdent(id) {
|
||||
return
|
||||
if v == nil || e == nil {
|
||||
panic("nil")
|
||||
}
|
||||
obj := f.Pkg.objectOf(id)
|
||||
if obj.Parent() == types.Universe {
|
||||
return // skip nil/true/false
|
||||
var obj types.Object
|
||||
if id, ok := e.(*ast.Ident); ok {
|
||||
if isBlankIdent(id) {
|
||||
return
|
||||
}
|
||||
obj = f.Pkg.objectOf(id)
|
||||
if _, ok := obj.(*types.Const); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
f.emit(&DebugRef{
|
||||
X: v,
|
||||
pos: id.Pos(),
|
||||
Expr: unparen(e),
|
||||
object: obj,
|
||||
})
|
||||
}
|
||||
|
@ -42,14 +42,14 @@ func main() {
|
||||
// Parse the input file.
|
||||
file, err := parser.ParseFile(imp.Fset, "hello.go", hello, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
fmt.Printf(err.Error()) // parse error
|
||||
fmt.Print(err.Error()) // parse error
|
||||
return
|
||||
}
|
||||
|
||||
// Create a "main" package containing one file.
|
||||
info := imp.CreateSourcePackage("main", []*ast.File{file})
|
||||
if info.Err != nil {
|
||||
fmt.Printf(info.Err.Error()) // type error
|
||||
fmt.Print(info.Err.Error()) // type error
|
||||
return
|
||||
}
|
||||
|
||||
|
12
ssa/func.go
12
ssa/func.go
@ -374,10 +374,18 @@ func (f *Function) removeNilBlocks() {
|
||||
f.Blocks = f.Blocks[:j]
|
||||
}
|
||||
|
||||
// SetDebugMode sets the debug mode for package pkg. If true, all its
|
||||
// functions will include full debug info. This greatly increases
|
||||
// the size of the instruction stream.
|
||||
//
|
||||
func (pkg *Package) SetDebugMode(debug bool) {
|
||||
// TODO(adonovan): do we want ast.File granularity?
|
||||
pkg.debug = debug
|
||||
}
|
||||
|
||||
// debugInfo reports whether debug info is wanted for this function.
|
||||
func (f *Function) debugInfo() bool {
|
||||
// TODO(adonovan): make the policy finer grained.
|
||||
return f.Prog.mode&DebugInfo != 0
|
||||
return f.Pkg.debug
|
||||
}
|
||||
|
||||
// addNamedLocal creates a local variable, adds it to function f and
|
||||
|
@ -25,7 +25,7 @@ type lvalue interface {
|
||||
type address struct {
|
||||
addr Value
|
||||
starPos token.Pos // source position, if from explicit *addr
|
||||
id *ast.Ident // source syntax, if from *ast.Ident
|
||||
expr ast.Expr // source syntax [debug mode]
|
||||
object types.Object // source var, if from *ast.Ident
|
||||
}
|
||||
|
||||
@ -38,16 +38,16 @@ func (a *address) load(fn *Function) Value {
|
||||
func (a *address) store(fn *Function, v Value) {
|
||||
store := emitStore(fn, a.addr, v)
|
||||
store.pos = a.starPos
|
||||
if a.id != nil {
|
||||
if a.expr != nil {
|
||||
// store.Val is v converted for assignability.
|
||||
emitDebugRef(fn, a.id, store.Val)
|
||||
emitDebugRef(fn, a.expr, store.Val)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *address) address(fn *Function) Value {
|
||||
if a.id != nil {
|
||||
if a.expr != nil {
|
||||
// NB: this kind of DebugRef yields the object's address.
|
||||
emitDebugRef(fn, a.id, a.addr)
|
||||
emitDebugRef(fn, a.expr, a.addr)
|
||||
}
|
||||
return a.addr
|
||||
}
|
||||
|
11
ssa/print.go
11
ssa/print.go
@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
@ -348,8 +349,14 @@ func (s *MapUpdate) String() string {
|
||||
}
|
||||
|
||||
func (s *DebugRef) String() string {
|
||||
p := s.Parent().Prog.Fset.Position(s.pos)
|
||||
return fmt.Sprintf("; %s is %s @ %d:%d", s.X.Name(), s.object, p.Line, p.Column)
|
||||
p := s.Parent().Prog.Fset.Position(s.Pos())
|
||||
var descr interface{}
|
||||
if s.object != nil {
|
||||
descr = s.object // e.g. "var x int"
|
||||
} else {
|
||||
descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr"
|
||||
}
|
||||
return fmt.Sprintf("; %s is %s @ %d:%d", s.X.Name(), descr, p.Line, p.Column)
|
||||
}
|
||||
|
||||
func (p *Package) String() string {
|
||||
|
110
ssa/source.go
110
ssa/source.go
@ -3,6 +3,9 @@ package ssa
|
||||
// This file defines utilities for working with source positions
|
||||
// or source-level named entities ("objects").
|
||||
|
||||
// TODO(adonovan): test that {Value,Instruction}.Pos() positions match
|
||||
// the originating syntax, as specified.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
@ -114,78 +117,39 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanonicalPos returns the canonical position of the AST node n,
|
||||
// ValueForExpr returns the SSA Value that corresponds to non-constant
|
||||
// expression e.
|
||||
//
|
||||
// For each Node kind that may generate an SSA Value or Instruction,
|
||||
// exactly one token within it is designated as "canonical". The
|
||||
// position of that token is returned by {Value,Instruction}.Pos().
|
||||
// The specifications of those methods determine the implementation of
|
||||
// this function.
|
||||
// It returns nil if no value was found, e.g.
|
||||
// - the expression is not lexically contained within f;
|
||||
// - f was not built with debug information; or
|
||||
// - e is a constant expression. (For efficiency, no debug
|
||||
// information is stored for constants. Use
|
||||
// importer.PackageInfo.ValueOf(e) instead.)
|
||||
// - the value was optimised away.
|
||||
//
|
||||
// TODO(adonovan): test coverage.
|
||||
// The types of e and the result are equal (modulo "untyped" bools
|
||||
// resulting from comparisons) and they have equal "pointerness".
|
||||
//
|
||||
func CanonicalPos(n ast.Node) token.Pos {
|
||||
// Comments show the Value/Instruction kinds v that may be
|
||||
// created by n such that CanonicalPos(n) == v.Pos().
|
||||
switch n := n.(type) {
|
||||
case *ast.ParenExpr:
|
||||
return CanonicalPos(n.X)
|
||||
|
||||
case *ast.CallExpr:
|
||||
// f(x): *Call, *Go, *Defer.
|
||||
// T(x): *ChangeType, *Convert, *MakeInterface, *ChangeInterface.
|
||||
// make(): *MakeMap, *MakeChan, *MakeSlice.
|
||||
// new(): *Alloc.
|
||||
// panic(): *Panic.
|
||||
return n.Lparen
|
||||
|
||||
case *ast.Ident:
|
||||
return n.NamePos // *Parameter, *Alloc, *Capture
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
return n.Lparen // *ChangeInterface or *TypeAssertExpr
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
return n.Sel.NamePos // *MakeClosure, *Field, *FieldAddr
|
||||
|
||||
case *ast.FuncLit:
|
||||
return n.Type.Func // *Function or *MakeClosure
|
||||
|
||||
case *ast.CompositeLit:
|
||||
return n.Lbrace // *Alloc or *Slice
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
return n.OpPos // *Phi or *BinOp
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
return n.OpPos // *Phi or *UnOp
|
||||
|
||||
case *ast.IndexExpr:
|
||||
return n.Lbrack // *Index or *IndexAddr
|
||||
|
||||
case *ast.SliceExpr:
|
||||
return n.Lbrack // *Slice
|
||||
|
||||
case *ast.SelectStmt:
|
||||
return n.Select // *Select
|
||||
|
||||
case *ast.RangeStmt:
|
||||
return n.For // *Range
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
return n.Return // *Ret
|
||||
|
||||
case *ast.SendStmt:
|
||||
return n.Arrow // *Send
|
||||
|
||||
case *ast.StarExpr:
|
||||
return n.Star // *Store
|
||||
|
||||
case *ast.KeyValueExpr:
|
||||
return n.Colon // *MapUpdate
|
||||
// (Tip: to find the ssa.Value given a source position, use
|
||||
// importer.PathEnclosingInterval to locate the ast.Node, then
|
||||
// EnclosingFunction to locate the Function, then ValueForExpr to find
|
||||
// the ssa.Value.)
|
||||
//
|
||||
func (f *Function) ValueForExpr(e ast.Expr) Value {
|
||||
if f.debugInfo() { // (opt)
|
||||
e = unparen(e)
|
||||
for _, b := range f.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
if ref, ok := instr.(*DebugRef); ok {
|
||||
if ref.Expr == e {
|
||||
return ref.X
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return token.NoPos
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Lookup functions for source-level named entities (types.Objects) ---
|
||||
@ -233,7 +197,7 @@ func (prog *Program) FuncValue(obj *types.Func) Value {
|
||||
//
|
||||
func (prog *Program) ConstValue(obj *types.Const) *Const {
|
||||
// TODO(adonovan): opt: share (don't reallocate)
|
||||
// Consts for const objects.
|
||||
// Consts for const objects and constant ast.Exprs.
|
||||
|
||||
// Universal constant? {true,false,nil}
|
||||
if obj.Parent() == types.Universe {
|
||||
@ -250,10 +214,8 @@ func (prog *Program) ConstValue(obj *types.Const) *Const {
|
||||
// identifier denoting the source-level named variable obj.
|
||||
//
|
||||
// VarValue returns nil if a local variable was not found, perhaps
|
||||
// because its package was not built, the DebugInfo flag was not set
|
||||
// during SSA construction, or the value was optimized away.
|
||||
//
|
||||
// TODO(adonovan): test on x.f where x is a field.
|
||||
// because its package was not built, the debug information was not
|
||||
// requested during SSA construction, or the value was optimized away.
|
||||
//
|
||||
// ref must be the path to an ast.Ident (e.g. from
|
||||
// PathEnclosingInterval), and that ident must resolve to obj.
|
||||
@ -315,5 +277,5 @@ func (prog *Program) VarValue(obj *types.Var, ref []ast.Node) Value {
|
||||
}
|
||||
}
|
||||
|
||||
return nil // e.g. DebugInfo unset, or var optimized away
|
||||
return nil // e.g. debug info not requested, or var optimized away
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
@ -20,7 +22,7 @@ func TestObjValueLookup(t *testing.T) {
|
||||
imp := importer.New(new(importer.Config)) // (uses GCImporter)
|
||||
f, err := parser.ParseFile(imp.Fset, "testdata/objlookup.go", nil, parser.DeclarationErrors|parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -46,12 +48,13 @@ func TestObjValueLookup(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
prog := ssa.NewProgram(imp.Fset, ssa.DebugInfo /*|ssa.LogFunctions*/)
|
||||
prog := ssa.NewProgram(imp.Fset, 0 /*|ssa.LogFunctions*/)
|
||||
for _, info := range imp.Packages {
|
||||
prog.CreatePackage(info)
|
||||
}
|
||||
pkg := prog.Package(info.Pkg)
|
||||
pkg.Build()
|
||||
mainPkg := prog.Package(info.Pkg)
|
||||
mainPkg.SetDebugMode(true)
|
||||
mainPkg.Build()
|
||||
|
||||
// Gather all idents and objects in file.
|
||||
objs := make(map[types.Object]bool)
|
||||
@ -178,3 +181,75 @@ func checkVarValue(t *testing.T, prog *ssa.Program, ref []ast.Node, obj *types.V
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that, in debug mode, we can determine the ssa.Value
|
||||
// corresponding to every ast.Expr.
|
||||
func TestValueForExpr(t *testing.T) {
|
||||
imp := importer.New(new(importer.Config)) // (uses GCImporter)
|
||||
f, err := parser.ParseFile(imp.Fset, "testdata/valueforexpr.go", nil,
|
||||
parser.DeclarationErrors|parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
info := imp.CreateSourcePackage("main", []*ast.File{f})
|
||||
if info.Err != nil {
|
||||
t.Error(info.Err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
prog := ssa.NewProgram(imp.Fset, 0)
|
||||
for _, info := range imp.Packages {
|
||||
prog.CreatePackage(info)
|
||||
}
|
||||
mainPkg := prog.Package(info.Pkg)
|
||||
mainPkg.SetDebugMode(true)
|
||||
mainPkg.Build()
|
||||
|
||||
fn := mainPkg.Func("f")
|
||||
|
||||
if false {
|
||||
fn.DumpTo(os.Stderr) // debugging
|
||||
}
|
||||
|
||||
// Find the actual AST node for each canonical position.
|
||||
parenExprByPos := make(map[token.Pos]*ast.ParenExpr)
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
if n != nil {
|
||||
if e, ok := n.(*ast.ParenExpr); ok {
|
||||
parenExprByPos[e.Pos()] = e
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// Find all annotations of form /*@kind*/.
|
||||
for _, c := range f.Comments {
|
||||
text := strings.TrimSpace(c.Text())
|
||||
if text == "" || text[0] != '@' {
|
||||
continue
|
||||
}
|
||||
text = text[1:]
|
||||
pos := c.End() + 1
|
||||
position := imp.Fset.Position(pos)
|
||||
var e ast.Expr
|
||||
if target := parenExprByPos[pos]; target == nil {
|
||||
t.Errorf("%s: annotation doesn't precede ParenExpr: %q", position, text)
|
||||
continue
|
||||
} else {
|
||||
e = target.X
|
||||
}
|
||||
|
||||
v := fn.ValueForExpr(e) // (may be nil)
|
||||
got := strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")
|
||||
if want := text; got != want {
|
||||
t.Errorf("%s: got value %q, want %q", position, got, want)
|
||||
}
|
||||
if v != nil {
|
||||
if !types.IsIdentical(v.Type(), info.TypeOf(e)) {
|
||||
t.Errorf("%s: got type %s, want %s", position, info.TypeOf(e), v.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
86
ssa/ssa.go
86
ssa/ssa.go
@ -41,6 +41,7 @@ type Package struct {
|
||||
Members map[string]Member // all package members keyed by name
|
||||
values map[types.Object]Value // package-level vars & funcs (incl. methods), keyed by object
|
||||
init *Function // Func("init"); the package's (concatenated) init function
|
||||
debug bool // include full debug info in this package.
|
||||
|
||||
// The following fields are set transiently, then cleared
|
||||
// after building.
|
||||
@ -125,14 +126,20 @@ type Value interface {
|
||||
// Instruction.Operands contains the inverse of this relation.
|
||||
Referrers() *[]Instruction
|
||||
|
||||
// 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.
|
||||
// Pos returns the location of the AST token most closely
|
||||
// associated with the operation 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.
|
||||
// For each ast.Node type, a particular token is designated as
|
||||
// the closest location for the expression, e.g. the Lparen
|
||||
// for an *ast.CallExpr. This permits a compact but
|
||||
// approximate mapping from Values to source positions for use
|
||||
// in diagnostic messages, for example.
|
||||
//
|
||||
// (Do not use this position to determine which Value
|
||||
// corresponds to an ast.Expr; use Function.ValueForExpr
|
||||
// instead. NB: it requires that the function was built with
|
||||
// debug information.)
|
||||
//
|
||||
Pos() token.Pos
|
||||
}
|
||||
@ -191,15 +198,22 @@ type Instruction interface {
|
||||
// Values.)
|
||||
Operands(rands []*Value) []*Value
|
||||
|
||||
// Pos returns the location of the source construct that
|
||||
// gave rise to this instruction, or token.NoPos if it was not
|
||||
// explicit in the source.
|
||||
// Pos returns the location of the AST token most closely
|
||||
// associated with the operation that gave rise to this
|
||||
// instruction, 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
|
||||
// instruction corresponding to a given piece of source
|
||||
// syntax.
|
||||
// For each ast.Node type, a particular token is designated as
|
||||
// the closest location for the expression, e.g. the Go token
|
||||
// for an *ast.GoStmt. This permits a compact but approximate
|
||||
// mapping from Instructions to source positions for use in
|
||||
// diagnostic messages, for example.
|
||||
//
|
||||
// (Do not use this position to determine which Instruction
|
||||
// corresponds to an ast.Expr; see the notes for Value.Pos.
|
||||
// This position may be used to determine which non-Value
|
||||
// Instruction corresponds to some ast.Stmts, but not all: If
|
||||
// and Jump instructions have no Pos(), for example.)
|
||||
//
|
||||
Pos() token.Pos
|
||||
}
|
||||
@ -791,10 +805,11 @@ type Lookup struct {
|
||||
// It represents one goal state and its corresponding communication.
|
||||
//
|
||||
type SelectState struct {
|
||||
Dir ast.ChanDir // direction of case
|
||||
Chan Value // channel to use (for send or receive)
|
||||
Send Value // value to send (for send)
|
||||
Pos token.Pos // position of token.ARROW
|
||||
Dir ast.ChanDir // direction of case
|
||||
Chan Value // channel to use (for send or receive)
|
||||
Send Value // value to send (for send)
|
||||
Pos token.Pos // position of token.ARROW
|
||||
DebugNode ast.Node // ast.SendStmt or ast.UnaryExpr(<-) [debug mode]
|
||||
}
|
||||
|
||||
// The Select instruction tests whether (or blocks until) one or more
|
||||
@ -834,7 +849,7 @@ type SelectState struct {
|
||||
//
|
||||
type Select struct {
|
||||
Register
|
||||
States []SelectState
|
||||
States []*SelectState
|
||||
Blocking bool
|
||||
}
|
||||
|
||||
@ -906,8 +921,9 @@ type Next struct {
|
||||
//
|
||||
// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
|
||||
// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
|
||||
// instruction arose from an explicit e.(T) operation; or token.NoPos
|
||||
// otherwise.
|
||||
// instruction arose from an explicit e.(T) operation; or the
|
||||
// ast.CaseClause.Case if the instruction arose from a case of a
|
||||
// type-switch statement.
|
||||
//
|
||||
// Example printed form:
|
||||
// t1 = typeassert t0.(int)
|
||||
@ -1037,6 +1053,8 @@ type Panic struct {
|
||||
//
|
||||
// See CallCommon for generic function call documentation.
|
||||
//
|
||||
// Pos() returns the ast.GoStmt.Go.
|
||||
//
|
||||
// Example printed form:
|
||||
// go println(t0, t1)
|
||||
// go t3()
|
||||
@ -1045,6 +1063,7 @@ type Panic struct {
|
||||
type Go struct {
|
||||
anInstruction
|
||||
Call CallCommon
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
// The Defer instruction pushes the specified call onto a stack of
|
||||
@ -1052,6 +1071,8 @@ type Go struct {
|
||||
//
|
||||
// See CallCommon for generic function call documentation.
|
||||
//
|
||||
// Pos() returns the ast.DeferStmt.Defer.
|
||||
//
|
||||
// Example printed form:
|
||||
// defer println(t0, t1)
|
||||
// defer t3()
|
||||
@ -1060,6 +1081,7 @@ type Go struct {
|
||||
type Defer struct {
|
||||
anInstruction
|
||||
Call CallCommon
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
// The Send instruction sends X on channel Chan.
|
||||
@ -1107,15 +1129,15 @@ type MapUpdate struct {
|
||||
}
|
||||
|
||||
// A DebugRef instruction provides the position information for a
|
||||
// specific source-level reference that denotes the SSA value X.
|
||||
// specific source-level expression that compiles to the SSA value X.
|
||||
//
|
||||
// DebugRef is a pseudo-instruction: it has no dynamic effect.
|
||||
//
|
||||
// Pos() returns the ast.Ident or ast.Selector.Sel of the source-level
|
||||
// reference.
|
||||
// Pos() returns Expr.Pos(), the position of the source-level
|
||||
// expression.
|
||||
//
|
||||
// Object() returns the source-level (var/const) object denoted by
|
||||
// that reference.
|
||||
// Object() returns the source-level (var/const/func) object denoted
|
||||
// by Expr if it is an *ast.Ident; otherwise it is nil.
|
||||
//
|
||||
// (By representing these as instructions, rather than out-of-band,
|
||||
// consistency is maintained during transformation passes by the
|
||||
@ -1124,8 +1146,8 @@ type MapUpdate struct {
|
||||
type DebugRef struct {
|
||||
anInstruction
|
||||
X Value // the value whose position we're declaring
|
||||
pos token.Pos // location of the reference
|
||||
object types.Object // the identity of the source var/const
|
||||
Expr ast.Expr // the referring expression
|
||||
object types.Object // the identity of the source var/const/func
|
||||
}
|
||||
|
||||
// Embeddable mix-ins and helpers for common parts of other structs. -----------
|
||||
@ -1385,8 +1407,8 @@ func (p *Package) Type(name string) (t *Type) {
|
||||
}
|
||||
|
||||
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 }
|
||||
func (s *Defer) Pos() token.Pos { return s.pos }
|
||||
func (s *Go) Pos() token.Pos { return s.pos }
|
||||
func (s *MapUpdate) Pos() token.Pos { return s.pos }
|
||||
func (s *Panic) Pos() token.Pos { return s.pos }
|
||||
func (s *Ret) Pos() token.Pos { return s.pos }
|
||||
@ -1395,7 +1417,7 @@ func (s *Store) Pos() token.Pos { return s.pos }
|
||||
func (s *If) Pos() token.Pos { return token.NoPos }
|
||||
func (s *Jump) Pos() token.Pos { return token.NoPos }
|
||||
func (s *RunDefers) Pos() token.Pos { return token.NoPos }
|
||||
func (s *DebugRef) Pos() token.Pos { return s.pos }
|
||||
func (s *DebugRef) Pos() token.Pos { return s.Expr.Pos() }
|
||||
|
||||
// Operands.
|
||||
|
||||
|
@ -54,11 +54,12 @@ func main() {
|
||||
|
||||
impctx := importer.Config{Loader: importer.MakeGoBuildLoader(nil)}
|
||||
|
||||
var debugMode bool
|
||||
var mode ssa.BuilderMode
|
||||
for _, c := range *buildFlag {
|
||||
switch c {
|
||||
case 'D':
|
||||
mode |= ssa.DebugInfo
|
||||
debugMode = true
|
||||
case 'P':
|
||||
mode |= ssa.LogPackages | ssa.BuildSerially
|
||||
case 'F':
|
||||
@ -115,7 +116,7 @@ func main() {
|
||||
// Create and build SSA-form program representation.
|
||||
prog := ssa.NewProgram(imp.Fset, mode)
|
||||
for _, info := range imp.Packages {
|
||||
prog.CreatePackage(info)
|
||||
prog.CreatePackage(info).SetDebugMode(debugMode)
|
||||
}
|
||||
prog.BuildAll()
|
||||
|
||||
|
@ -20,6 +20,8 @@ import (
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
||||
const debugMode = false
|
||||
|
||||
func allPackages() []string {
|
||||
var pkgs []string
|
||||
root := filepath.Join(runtime.GOROOT(), "src/pkg") + "/"
|
||||
@ -70,10 +72,10 @@ func TestStdlib(t *testing.T) {
|
||||
alloc := memstats.Alloc
|
||||
|
||||
// Create SSA packages.
|
||||
prog := ssa.NewProgram(imp.Fset, ssa.DebugInfo|ssa.SanityCheckFunctions)
|
||||
prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
|
||||
for _, info := range imp.Packages {
|
||||
if info.Err == nil {
|
||||
prog.CreatePackage(info)
|
||||
prog.CreatePackage(info).SetDebugMode(debugMode)
|
||||
}
|
||||
}
|
||||
|
||||
|
45
ssa/testdata/valueforexpr.go
vendored
Normal file
45
ssa/testdata/valueforexpr.go
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
// This file is the input to TestCanonicalPos in source_test.go, which
|
||||
// ensures that each expression e immediately following a /*@kind*/(x)
|
||||
// annotation, when passed to Function.ValueForExpr(e), returns a
|
||||
// non-nil Value of the same type as e and of kind 'kind'.
|
||||
|
||||
func f(param int) {
|
||||
_ = /*@<nil>*/ (1 + 2) // (constant)
|
||||
i := 0
|
||||
/*@Call*/ (print( /*@BinOp*/ (i + 1)))
|
||||
ch := /*@MakeChan*/ (make(chan int))
|
||||
/*@UnOp*/ (<-ch)
|
||||
x := /*@UnOp*/ (<-ch)
|
||||
select {
|
||||
case /*@Extract*/ (<-ch):
|
||||
case x := /*@Extract*/ (<-ch):
|
||||
}
|
||||
defer /*@Function*/ (func() {
|
||||
})()
|
||||
go /*@Function*/ (func() {
|
||||
})()
|
||||
y := 0
|
||||
if true && /*@BinOp*/ (bool(y > 0)) {
|
||||
y = 1
|
||||
}
|
||||
_ = /*@Phi*/ (y)
|
||||
map1 := /*@MakeMap*/ (make(map[string]string))
|
||||
_ = /*@MakeMap*/ (map[string]string{"": ""})
|
||||
_ = /*@MakeSlice*/ (make([]int, 0))
|
||||
_ = /*@MakeClosure*/ (func() { print(param) })
|
||||
sl := /*@Slice*/ ([]int{})
|
||||
_ = /*@Alloc*/ (&struct{}{})
|
||||
_ = /*@Slice*/ (sl[:0])
|
||||
_ = /*@Alloc*/ (new(int))
|
||||
var iface interface{}
|
||||
_ = /*@TypeAssert*/ (iface.(int))
|
||||
_ = /*@UnOp*/ (sl[0])
|
||||
_ = /*@IndexAddr*/ (&sl[0])
|
||||
_ = /*@Index*/ ([2]int{}[0])
|
||||
var p *int
|
||||
_ = /*@UnOp*/ (*p)
|
||||
}
|
Loading…
Reference in New Issue
Block a user