Skip to content

Commit

Permalink
ssa: backport upstream changes
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikh committed May 8, 2019
1 parent 1e47452 commit 76464b7
Show file tree
Hide file tree
Showing 16 changed files with 384 additions and 212 deletions.
16 changes: 8 additions & 8 deletions ssa/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ package ssa
import (
"fmt"
"go/ast"
exact "go/constant"
"go/constant"
"go/token"
"go/types"
"os"
Expand Down Expand Up @@ -63,7 +63,7 @@ var (
// SSA Value constants.
vZero = intConst(0)
vOne = intConst(1)
vTrue = NewConst(exact.MakeBool(true), tBool)
vTrue = NewConst(constant.MakeBool(true), tBool)
)

// builder holds state associated with the package currently being built.
Expand Down Expand Up @@ -131,11 +131,11 @@ func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value {
switch e.Op {
case token.LAND:
b.cond(fn, e.X, rhs, done)
short = NewConst(exact.MakeBool(false), t)
short = NewConst(constant.MakeBool(false), t)

case token.LOR:
b.cond(fn, e.X, done, rhs)
short = NewConst(exact.MakeBool(true), t)
short = NewConst(constant.MakeBool(true), t)
}

// Is rhs unreachable?
Expand Down Expand Up @@ -969,10 +969,10 @@ func (b *builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {
c.Args = b.emitCallArgs(fn, sig, e, c.Args)
}

// 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, pos token.Pos) {
// assignOp emits to fn code to perform loc <op>= val.
func (b *builder) assignOp(fn *Function, loc lvalue, val Value, op token.Token, pos token.Pos) {
oldv := loc.load(fn)
loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, incr, oldv.Type()), loc.typ(), pos))
loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, val, oldv.Type()), loc.typ(), pos))
}

// localValueSpec emits to fn code to define all of the vars in the
Expand Down Expand Up @@ -1998,7 +1998,7 @@ start:
op = token.SUB
}
loc := b.addr(fn, s.X, false)
b.assignOp(fn, loc, NewConst(exact.MakeInt64(1), loc.typ()), op, s.Pos())
b.assignOp(fn, loc, NewConst(constant.MakeInt64(1), loc.typ()), op, s.Pos())

case *ast.AssignStmt:
switch s.Tok {
Expand Down
44 changes: 22 additions & 22 deletions ssa/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package ssa

import (
"fmt"
exact "go/constant"
"go/constant"
"go/token"
"go/types"
"strconv"
Expand All @@ -17,14 +17,14 @@ import (
// NewConst returns a new constant of the specified value and type.
// val must be valid according to the specification of Const.Value.
//
func NewConst(val exact.Value, typ types.Type) *Const {
func NewConst(val constant.Value, typ types.Type) *Const {
return &Const{typ, val}
}

// intConst returns an 'int' constant that evaluates to i.
// (i is an int64 in case the host is narrower than the target.)
func intConst(i int64) *Const {
return NewConst(exact.MakeInt64(i), tInt)
return NewConst(constant.MakeInt64(i), tInt)
}

// nilConst returns a nil constant of the specified type, which may
Expand All @@ -36,7 +36,7 @@ func nilConst(typ types.Type) *Const {

// stringConst returns a 'string' constant that evaluates to s.
func stringConst(s string) *Const {
return NewConst(exact.MakeString(s), tString)
return NewConst(constant.MakeString(s), tString)
}

// zeroConst returns a new "zero" constant of the specified type,
Expand All @@ -48,11 +48,11 @@ func zeroConst(t types.Type) *Const {
case *types.Basic:
switch {
case t.Info()&types.IsBoolean != 0:
return NewConst(exact.MakeBool(false), t)
return NewConst(constant.MakeBool(false), t)
case t.Info()&types.IsNumeric != 0:
return NewConst(exact.MakeInt64(0), t)
return NewConst(constant.MakeInt64(0), t)
case t.Info()&types.IsString != 0:
return NewConst(exact.MakeString(""), t)
return NewConst(constant.MakeString(""), t)
case t.Kind() == types.UnsafePointer:
fallthrough
case t.Kind() == types.UntypedNil:
Expand All @@ -74,8 +74,8 @@ func (c *Const) RelString(from *types.Package) string {
var s string
if c.Value == nil {
s = "nil"
} else if c.Value.Kind() == exact.String {
s = exact.StringVal(c.Value)
} else if c.Value.Kind() == constant.String {
s = constant.StringVal(c.Value)
const max = 20
// TODO(adonovan): don't cut a rune in half.
if len(s) > max {
Expand Down Expand Up @@ -121,14 +121,14 @@ func (c *Const) IsNil() bool {
// a signed 64-bit integer.
//
func (c *Const) Int64() int64 {
switch x := exact.ToInt(c.Value); x.Kind() {
case exact.Int:
if i, ok := exact.Int64Val(x); ok {
switch x := constant.ToInt(c.Value); x.Kind() {
case constant.Int:
if i, ok := constant.Int64Val(x); ok {
return i
}
return 0
case exact.Float:
f, _ := exact.Float64Val(x)
case constant.Float:
f, _ := constant.Float64Val(x)
return int64(f)
}
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
Expand All @@ -138,14 +138,14 @@ func (c *Const) Int64() int64 {
// an unsigned 64-bit integer.
//
func (c *Const) Uint64() uint64 {
switch x := exact.ToInt(c.Value); x.Kind() {
case exact.Int:
if u, ok := exact.Uint64Val(x); ok {
switch x := constant.ToInt(c.Value); x.Kind() {
case constant.Int:
if u, ok := constant.Uint64Val(x); ok {
return u
}
return 0
case exact.Float:
f, _ := exact.Float64Val(x)
case constant.Float:
f, _ := constant.Float64Val(x)
return uint64(f)
}
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
Expand All @@ -155,15 +155,15 @@ func (c *Const) Uint64() uint64 {
// a float64.
//
func (c *Const) Float64() float64 {
f, _ := exact.Float64Val(c.Value)
f, _ := constant.Float64Val(c.Value)
return f
}

// Complex128 returns the complex value of this constant truncated to
// fit a complex128.
//
func (c *Const) Complex128() complex128 {
re, _ := exact.Float64Val(exact.Real(c.Value))
im, _ := exact.Float64Val(exact.Imag(c.Value))
re, _ := constant.Float64Val(constant.Real(c.Value))
im, _ := constant.Float64Val(constant.Imag(c.Value))
return complex(re, im)
}
17 changes: 12 additions & 5 deletions ssa/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,19 @@ func (prog *Program) AllPackages() []*Package {
return pkgs
}

// ImportedPackage returns the importable SSA Package whose import
// path is path, or nil if no such SSA package has been created.
// ImportedPackage returns the importable Package whose PkgPath
// is path, or nil if no such Package has been created.
//
// Not all packages are importable. For example, no import
// declaration can resolve to the x_test package created by 'go test'
// or the ad-hoc main package created 'go build foo.go'.
// A parameter to CreatePackage determines whether a package should be
// considered importable. For example, no import declaration can resolve
// to the ad-hoc main package created by 'go build foo.go'.
//
// TODO(adonovan): rethink this function and the "importable" concept;
// most packages are importable. This function assumes that all
// types.Package.Path values are unique within the ssa.Program, which is
// false---yet this function remains very convenient.
// Clients should use (*Program).Package instead where possible.
// SSA doesn't really need a string-keyed map of packages.
//
func (prog *Program) ImportedPackage(path string) *Package {
return prog.imported[path]
Expand Down
12 changes: 7 additions & 5 deletions ssa/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
// such as multi-way branch can be reconstructed as needed; see
// ssautil.Switches() for an example.
//
// To construct an SSA-form program, call ssautil.CreateProgram on a
// loader.Program, a set of type-checked packages created from
// parsed Go source files. The resulting ssa.Program contains all the
// packages and their members, but SSA code is not created for
// function bodies until a subsequent call to (*Package).Build.
// The simplest way to create the SSA representation of a package is
// to load typed syntax trees using golang.org/x/tools/go/packages, then
// invoke the ssautil.Packages helper function. See ExampleLoadPackages
// and ExampleWholeProgram for examples.
// The resulting ssa.Program contains all the packages and their
// members, but SSA code is not created for function bodies until a
// subsequent call to (*Package).Build or (*Program).Build.
//
// The builder initially builds a naive SSA form in which all local
// variables are addresses of stack locations with explicit loads and
Expand Down
62 changes: 44 additions & 18 deletions ssa/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import (
"go/parser"
"go/token"
"go/types"
"log"
"os"

"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/packages"
"honnef.co/go/tools/ssa"
"honnef.co/go/tools/ssa/ssautil"
)
Expand Down Expand Up @@ -113,26 +114,51 @@ func Example_buildPackage() {
// return
}

// This program shows how to load a main package (cmd/cover) and all its
// dependencies from source, using the loader, and then build SSA code
// for the entire program. This is what you'd typically use for a
// whole-program analysis.
//
func Example_loadProgram() {
// Load cmd/cover and its dependencies.
var conf loader.Config
conf.Import("cmd/cover")
lprog, err := conf.Load()
// This example builds SSA code for a set of packages using the
// x/tools/go/packages API. This is what you would typically use for a
// analysis capable of operating on a single package.
func Example_loadPackages() {
// Load, parse, and type-check the initial packages.
cfg := &packages.Config{Mode: packages.LoadSyntax}
initial, err := packages.Load(cfg, "fmt", "net/http")
if err != nil {
fmt.Print(err) // type error in some package
return
log.Fatal(err)
}

// Create SSA-form program representation.
prog := ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions)
// Stop if any package had errors.
// This step is optional; without it, the next step
// will create SSA for only a subset of packages.
if packages.PrintErrors(initial) > 0 {
log.Fatalf("packages contain errors")
}

// Build SSA code for the entire cmd/cover program.
prog.Build()
// Create SSA packages for all well-typed packages.
prog, pkgs := ssautil.Packages(initial, ssa.PrintPackages)
_ = prog

// Output:
// Build SSA code for the well-typed initial packages.
for _, p := range pkgs {
if p != nil {
p.Build()
}
}
}

// This example builds SSA code for a set of packages plus all their dependencies,
// using the x/tools/go/packages API.
// This is what you'd typically use for a whole-program analysis.
func Example_loadWholeProgram() {
// Load, parse, and type-check the whole program.
cfg := packages.Config{Mode: packages.LoadAllSyntax}
initial, err := packages.Load(&cfg, "fmt", "net/http")
if err != nil {
log.Fatal(err)
}

// Create SSA packages for well-typed packages and their dependencies.
prog, pkgs := ssautil.AllPackages(initial, ssa.PrintPackages)
_ = pkgs

// Build SSA code for the whole program.
prog.Build()
}
4 changes: 2 additions & 2 deletions ssa/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import (
//
func (prog *Program) MethodValue(sel *types.Selection) *Function {
if sel.Kind() != types.MethodVal {
panic(fmt.Sprintf("Method(%s) kind != MethodVal", sel))
panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
}
T := sel.Recv()
if isInterface(T) {
return nil // abstract method
}
if prog.mode&LogSource != 0 {
defer logStack("Method %s %v", T, sel)()
defer logStack("MethodValue %s %v", T, sel)()
}

prog.methodsMu.Lock()
Expand Down
12 changes: 12 additions & 0 deletions ssa/sanity.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,18 @@ func (s *sanity) checkFunction(fn *Function) bool {
if p.Parent() != fn {
s.errorf("Param %s at index %d has wrong parent", p.Name(), i)
}
// Check common suffix of Signature and Params match type.
if sig := fn.Signature; sig != nil {
j := i - len(fn.Params) + sig.Params().Len() // index within sig.Params
if j < 0 {
continue
}
if !types.Identical(p.Type(), sig.Params().At(j).Type()) {
s.errorf("Param %s at index %d has wrong type (%s, versus %s in Signature)", p.Name(), i, p.Type(), sig.Params().At(j).Type())

}
}

s.checkReferrerList(p)
}
for i, fv := range fn.FreeVars {
Expand Down
2 changes: 1 addition & 1 deletion ssa/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
// (modulo "untyped" bools resulting from comparisons).
//
// (Tip: to find the ssa.Value given a source position, use
// importer.PathEnclosingInterval to locate the ast.Node, then
// astutil.PathEnclosingInterval to locate the ast.Node, then
// EnclosingFunction to locate the Function, then ValueForExpr to find
// the ssa.Value.)
//
Expand Down
Loading

0 comments on commit 76464b7

Please sign in to comment.