Skip to content

Commit

Permalink
x/tools/...: fork and tag !1.5 all files that use go/types et al
Browse files Browse the repository at this point in the history
This change will ensure that the tree continues to work with go1.4.1.

All files continue to depend on golang.org/x/tools/go/types, but in a
follow-up change, I will switch the primary files to depend on the
standard go/types package.  Another (smaller) set of files will be
forked and tagged, this time !1.6, due to API differences between the
two packages.

All tests pass using 1.4.1, 1.5, and ~1.6 (tip).

Change-Id: Ifd75a6330e120957d646be91693daaba1ce0e8c9
Reviewed-on: https://go-review.googlesource.com/18333
Reviewed-by: Robert Griesemer <gri@golang.org>
  • Loading branch information
adonovan committed Jan 6, 2016
1 parent 8f90b5e commit 2477c0d
Show file tree
Hide file tree
Showing 147 changed files with 33,615 additions and 1 deletion.
2 changes: 2 additions & 0 deletions cmd/bundle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build go1.5

// The bundle command concatenates the source files of a package,
// renaming package-level names by adding a prefix and renaming
// identifiers as needed to preserve referential integrity.
Expand Down
2 changes: 2 additions & 0 deletions cmd/ssadump/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build go1.5

// ssadump: a tool for displaying and interpreting the SSA form of Go programs.
package main // import "golang.org/x/tools/cmd/ssadump"

Expand Down
188 changes: 188 additions & 0 deletions cmd/ssadump/main14.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !go1.5

// ssadump: a tool for displaying and interpreting the SSA form of Go programs.
package main // import "golang.org/x/tools/cmd/ssadump"

import (
"flag"
"fmt"
"go/build"
"os"
"runtime"
"runtime/pprof"

"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/interp"
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
)

var (
modeFlag = ssa.BuilderModeFlag(flag.CommandLine, "build", 0)

testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.")

runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.")

interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter.
The value is a sequence of zero or more more of these letters:
R disable [R]ecover() from panic; show interpreter crash instead.
T [T]race execution of the program. Best for single-threaded programs!
`)
)

const usage = `SSA builder and interpreter.
Usage: ssadump [<flag> ...] <args> ...
Use -help flag to display options.
Examples:
% ssadump -build=F hello.go # dump SSA form of a single package
% ssadump -run -interp=T hello.go # interpret a program, with tracing
% ssadump -run -test unicode -- -test.v # interpret the unicode package's tests, verbosely
` + loader.FromArgsUsage +
`
When -run is specified, ssadump will run the program.
The entry point depends on the -test flag:
if clear, it runs the first package named main.
if set, it runs the tests of each package.
`

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func init() {
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)

// If $GOMAXPROCS isn't set, use the full capacity of the machine.
// For small machines, use at least 4 threads.
if os.Getenv("GOMAXPROCS") == "" {
n := runtime.NumCPU()
if n < 4 {
n = 4
}
runtime.GOMAXPROCS(n)
}
}

func main() {
if err := doMain(); err != nil {
fmt.Fprintf(os.Stderr, "ssadump: %s\n", err)
os.Exit(1)
}
}

func doMain() error {
flag.Parse()
args := flag.Args()

conf := loader.Config{Build: &build.Default}

// Choose types.Sizes from conf.Build.
var wordSize int64 = 8
switch conf.Build.GOARCH {
case "386", "arm":
wordSize = 4
}
conf.TypeChecker.Sizes = &types.StdSizes{
MaxAlign: 8,
WordSize: wordSize,
}

var interpMode interp.Mode
for _, c := range *interpFlag {
switch c {
case 'T':
interpMode |= interp.EnableTracing
case 'R':
interpMode |= interp.DisableRecover
default:
return fmt.Errorf("unknown -interp option: '%c'", c)
}
}

if len(args) == 0 {
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}

// Profiling support.
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}

// Use the initial packages from the command line.
args, err := conf.FromArgs(args, *testFlag)
if err != nil {
return err
}

// The interpreter needs the runtime package.
if *runFlag {
conf.Import("runtime")
}

// Load, parse and type-check the whole program.
iprog, err := conf.Load()
if err != nil {
return err
}

// Create and build SSA-form program representation.
prog := ssautil.CreateProgram(iprog, *modeFlag)

// Build and display only the initial packages
// (and synthetic wrappers), unless -run is specified.
for _, info := range iprog.InitialPackages() {
prog.Package(info.Pkg).Build()
}

// Run the interpreter.
if *runFlag {
prog.Build()

var main *ssa.Package
pkgs := prog.AllPackages()
if *testFlag {
// If -test, run all packages' tests.
if len(pkgs) > 0 {
main = prog.CreateTestMainPackage(pkgs...)
}
if main == nil {
return fmt.Errorf("no tests")
}
} else {
// Otherwise, run main.main.
for _, pkg := range pkgs {
if pkg.Pkg.Name() == "main" {
main = pkg
if main.Func("main") == nil {
return fmt.Errorf("no func main() in main package")
}
break
}
}
if main == nil {
return fmt.Errorf("no main package")
}
}

if runtime.GOARCH != build.Default.GOARCH {
return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)",
build.Default.GOARCH, runtime.GOARCH)
}

interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args)
}
return nil
}
6 changes: 6 additions & 0 deletions go/callgraph/cha/cha.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build go1.5

// Package cha computes the call graph of a Go program using the Class
// Hierarchy Analysis (CHA) algorithm.
//
Expand Down
126 changes: 126 additions & 0 deletions go/callgraph/cha/cha14.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !go1.5

// Package cha computes the call graph of a Go program using the Class
// Hierarchy Analysis (CHA) algorithm.
//
// CHA was first described in "Optimization of Object-Oriented Programs
// Using Static Class Hierarchy Analysis", Jeffrey Dean, David Grove,
// and Craig Chambers, ECOOP'95.
//
// CHA is related to RTA (see go/callgraph/rta); the difference is that
// CHA conservatively computes the entire "implements" relation between
// interfaces and concrete types ahead of time, whereas RTA uses dynamic
// programming to construct it on the fly as it encounters new functions
// reachable from main. CHA may thus include spurious call edges for
// types that haven't been instantiated yet, or types that are never
// instantiated.
//
// Since CHA conservatively assumes that all functions are address-taken
// and all concrete types are put into interfaces, it is sound to run on
// partial programs, such as libraries without a main or test function.
//
package cha // import "golang.org/x/tools/go/callgraph/cha"

import (
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)

// CallGraph computes the call graph of the specified program using the
// Class Hierarchy Analysis algorithm.
//
func CallGraph(prog *ssa.Program) *callgraph.Graph {
cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph

allFuncs := ssautil.AllFunctions(prog)

// funcsBySig contains all functions, keyed by signature. It is
// the effective set of address-taken functions used to resolve
// a dynamic call of a particular signature.
var funcsBySig typeutil.Map // value is []*ssa.Function

// methodsByName contains all methods,
// grouped by name for efficient lookup.
methodsByName := make(map[string][]*ssa.Function)

// methodsMemo records, for every abstract method call call I.f on
// interface type I, the set of concrete methods C.f of all
// types C that satisfy interface I.
methodsMemo := make(map[*types.Func][]*ssa.Function)
lookupMethods := func(m *types.Func) []*ssa.Function {
methods, ok := methodsMemo[m]
if !ok {
I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface)
for _, f := range methodsByName[m.Name()] {
C := f.Signature.Recv().Type() // named or *named
if types.Implements(C, I) {
methods = append(methods, f)
}
}
methodsMemo[m] = methods
}
return methods
}

for f := range allFuncs {
if f.Signature.Recv() == nil {
// Package initializers can never be address-taken.
if f.Name() == "init" && f.Synthetic == "package initializer" {
continue
}
funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function)
funcs = append(funcs, f)
funcsBySig.Set(f.Signature, funcs)
} else {
methodsByName[f.Name()] = append(methodsByName[f.Name()], f)
}
}

addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) {
gnode := cg.CreateNode(g)
callgraph.AddEdge(fnode, site, gnode)
}

addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) {
// Because every call to a highly polymorphic and
// frequently used abstract method such as
// (io.Writer).Write is assumed to call every concrete
// Write method in the program, the call graph can
// contain a lot of duplication.
//
// TODO(adonovan): opt: consider factoring the callgraph
// API so that the Callers component of each edge is a
// slice of nodes, not a singleton.
for _, g := range callees {
addEdge(fnode, site, g)
}
}

for f := range allFuncs {
fnode := cg.CreateNode(f)
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
if site, ok := instr.(ssa.CallInstruction); ok {
call := site.Common()
if call.IsInvoke() {
addEdges(fnode, site, lookupMethods(call.Method))
} else if g := call.StaticCallee(); g != nil {
addEdge(fnode, site, g)
} else if _, ok := call.Value.(*ssa.Builtin); !ok {
callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function)
addEdges(fnode, site, callees)
}
}
}
}
}

return cg
}
Loading

0 comments on commit 2477c0d

Please sign in to comment.