// Simple block optimizations to simplify the control flow graph.
-// TODO(adonovan): instead of creating several "unreachable" blocks
+// TODO(adonovan): opt: instead of creating several "unreachable" blocks
// per function in the Builder, reuse a single one (e.g. at Blocks[1])
// to reduce garbage.
func (b *Builder) lookup(obj types.Object) (v Value, ok bool) {
v, ok = b.globals[obj]
if ok {
- // TODO(adonovan): the build phase should only
+ // TODO(adonovan): opt: the build phase should only
// propagate to v if it's in the same package as the
// caller of lookup if we want to make this
// concurrent.
// expr() for rvalues.
// It does require mutation of Builder.types though; if we want to
// make the Builder concurrent we'll have to avoid that.
-// TODO(adonovan): emit code directly rather than desugaring the AST.
+// TODO(adonovan): opt: emit code directly rather than desugaring the AST.
//
func (b *Builder) demoteSelector(sel *ast.SelectorExpr, pkg *Package) (sel2 *ast.SelectorExpr, index int) {
id := makeId(sel.Sel.Name, pkg.Types)
X: x,
Sel: &ast.Ident{Name: path.field.Name},
}
- b.types[sel] = path.field.Type // TODO(adonovan): fix: not thread-safe
+ b.types[sel] = path.field.Type // TODO(adonovan): opt: not thread-safe
return sel
}
X: makeSelector(b, sel.X, path),
Sel: sel.Sel,
}
- b.types[sel2] = b.exprType(sel) // TODO(adonovan): fix: not thread-safe
+ b.types[sel2] = b.exprType(sel) // TODO(adonovan): opt: not thread-safe
return
}
// Case 2a: X.f() or (*X).f(): a statically dipatched call to
// the method f in the method-set of X or *X. X may be
// an interface. Treat like case 0.
- // TODO(adonovan): inline expr() here, to make the call static
+ // TODO(adonovan): opt: inline expr() here, to make the call static
// and to avoid generation of a stub for an interface method.
if b.isType(sel.X) {
c.Func = b.expr(fn, e.Fun)
// 3. Ellipsis call f(a, b, rest...) to variadic function.
// 'rest' is already a slice; all args treated in the usual manner.
// 4. f(g()) where g has >1 return parameters. f may also be variadic.
- // TODO(adonovan): implement.
+ // TODO(adonovan): fix: implement.
var args, varargs []ast.Expr = e.Args, nil
c.HasEllipsis = e.Ellipsis != 0
// isDef is true if this is a short variable declaration (:=).
//
// Note the similarity with localValueSpec.
-// TODO(adonovan): explain differences.
//
func (b *Builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) {
// Side effects of all LHSs and RHSs must occur in left-to-right order.
func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
// A blocking select of a single case degenerates to a
// simple send or receive.
- // TODO(adonovan): is this optimization worth its weight?
+ // TODO(adonovan): opt: is this optimization worth its weight?
if len(s.Body.List) == 1 {
clause := s.Body.List[0].(*ast.CommClause)
if clause.Comm != nil {
// } else {
// ...default...
// }
- //
- // TODO(adonovan): opt: define and use a multiway dispatch instr.
pair := &Select{
States: states,
Blocking: blocking,
// The typechecker sets types.Package.Path only for GcImported
// packages, since it doesn't know import path until after typechecking is done.
// Here we ensure it is always set, since we know the correct path.
- // TODO(adonovan): eliminate redundant ssa.Package.ImportPath field.
if typkg.Path == "" {
typkg.Path = importPath
+ } else if typkg.Path != importPath {
+ panic(fmt.Sprintf("%s != %s", typkg.Path, importPath))
}
p := &Package{
- Prog: b.Prog,
- Types: typkg,
- ImportPath: importPath,
- Members: make(map[string]Member),
- files: files,
+ Prog: b.Prog,
+ Types: typkg,
+ Members: make(map[string]Member),
+ files: files,
}
b.packages[typkg] = p
return // already done (or nothing to do)
}
if b.mode&LogSource != 0 {
- fmt.Fprintln(os.Stderr, "build package", p.ImportPath)
+ fmt.Fprintln(os.Stderr, "build package", p.Types.Path)
}
init := p.Init
init.start(b.idents)
// order. This causes init() code to be generated in
// topological order. We visit them transitively through
// functions of the same package, but we don't treat functions
- // as roots. TODO(adonovan): fix: don't visit through other
+ // as roots. TODO(adonovan): opt: don't visit through other
// packages.
//
// We also ensure all functions and methods are built, even if
// t4 = fmt.Println(t3) (n int, err error)
// ret
//
+//
+// The ssadump utility is an example of an application that loads and
+// dumps the SSA form of a Go program, whether a single package or a
+// whole program.
+//
// TODO(adonovan): demonstrate more features in the example:
// parameters and control flow at the least.
//
// should be made available generally. Currently it is only present in
// Package, Function and CallCommon.
//
-// TODO(adonovan): Provide an example skeleton application that loads
-// and dumps the SSA form of a program. Accommodate package-at-a-time
-// vs. whole-program operation.
-//
// TODO(adonovan): Consider the exceptional control-flow implications
// of defer and recover().
//
// switch true { case e: ... }
// if e==true { ... }
// even in the case when e's type is an interface.
- // TODO(adonovan): generalise to x==true, false!=y, etc.
+ // TODO(adonovan): opt: generalise to x==true, false!=y, etc.
if x == vTrue && op == token.EQL {
if yt, ok := yt.(*types.Basic); ok && yt.Info&types.IsBoolean != 0 {
return y
// Package-level function.
// Prefix with package name for cross-package references only.
if from != f.Pkg {
- return fmt.Sprintf("%s.%s", f.Pkg.ImportPath, f.Name_)
+ return fmt.Sprintf("%s.%s", f.Pkg.Types.Path, f.Name_)
}
return f.Name_
}
io.WriteString(w, " ")
if sig.IsVariadic && i == len(params)-1 {
io.WriteString(w, "...")
+ io.WriteString(w, underlyingType(v.Type()).(*types.Slice).Elt.String())
+ } else {
+ io.WriteString(w, v.Type().String())
}
- io.WriteString(w, v.Type().String())
}
io.WriteString(w, ")")
if res := sig.Results; res != nil {
func initReflect(i *interpreter) {
i.reflectPackage = &ssa.Package{
- Prog: i.prog,
- Types: reflectTypesPackage,
- ImportPath: "reflect",
- Members: make(map[string]ssa.Member),
+ Prog: i.prog,
+ Types: reflectTypesPackage,
+ Members: make(map[string]ssa.Member),
}
i.rtypeMethods = ssa.MethodSet{
// TODO(adonovan): more.
//
// Validate this file with 'go run' after editing.
+// TODO(adonovan): break this into small files organized by theme.
package main
panic(m)
}
}
+
+//////////////////////////////////////////////////////////////////////
+// Variadic bridge methods and interface thunks.
+
+type VT int
+
+var vcount = 0
+
+func (VT) f(x int, y ...string) {
+ vcount++
+ if x != 1 {
+ panic(x)
+ }
+ if len(y) != 2 || y[0] != "foo" || y[1] != "bar" {
+ panic(y)
+ }
+}
+
+type VS struct {
+ VT
+}
+
+type VI interface {
+ f(x int, y ...string)
+}
+
+func init() {
+ foobar := []string{"foo", "bar"}
+ var s VS
+ s.f(1, "foo", "bar")
+ s.f(1, foobar...)
+ if vcount != 2 {
+ panic("s.f not called twice")
+ }
+
+ fn := VI.f
+ fn(s, 1, "foo", "bar")
+ fn(s, 1, foobar...)
+ if vcount != 4 {
+ panic("I.f not called twice")
+ }
+}
// http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html
// (Be sure to expand the whole thread.)
-// TODO(adonovan): there are many optimizations worth evaluating, and
+// TODO(adonovan): opt: there are many optimizations worth evaluating, and
// the conventional wisdom for SSA construction is that a simple
// algorithm well engineered often beats those of better asymptotic
// complexity on all but the most egregious inputs.
// take removes an arbitrary element from a set s and
// returns its index, or returns -1 if empty.
-//
-// TODO(adonovan): add this method (optimized) to big.Int.
func (s *blockSet) take() int {
l := s.BitLen()
for i := 0; i < l; i++ {
case *big.Int:
return x.Int64()
case *big.Rat:
- // TODO(adonovan): fix: is this the right rounding mode?
var q big.Int
- return q.Quo(x.Num(), x.Denom()).Int64()
+ return q.Quo(x.Num(), x.Denom()).Int64() // truncate
}
panic(fmt.Sprintf("unexpected literal value: %T", l.Value))
}
case *big.Int:
return x.Uint64()
case *big.Rat:
- // TODO(adonovan): fix: is this right?
var q big.Int
- return q.Quo(x.Num(), x.Denom()).Uint64()
+ return q.Quo(x.Num(), x.Denom()).Uint64() // truncate
}
panic(fmt.Sprintf("unexpected literal value: %T", l.Value))
}
}
func (bl blank) typ() types.Type {
- // TODO(adonovan): this should be the type of the blank Ident;
- // the typechecker doesn't provide this yet, but fortunately,
- // we don't need it yet either.
+ // This should be the type of the blank Ident; the typechecker
+ // doesn't provide this yet, but fortunately, we don't need it
+ // yet either.
panic("blank.typ is unimplemented")
}
// FullName returns g's package-qualified name.
func (g *Global) FullName() string {
- return fmt.Sprintf("%s.%s", g.Pkg.ImportPath, g.Name_)
+ return fmt.Sprintf("%s.%s", g.Pkg.Types.Path, g.Name_)
}
// Instruction.String()
}
func (p *Package) String() string {
- return "Package " + p.ImportPath
+ return "Package " + p.Types.Path
}
func (p *Package) DumpTo(w io.Writer) {
- fmt.Fprintf(w, "Package %s at %s:\n", p.ImportPath, p.Prog.Files.File(p.Pos).Name())
+ fmt.Fprintf(w, "Package %s at %s:\n", p.Types.Path, p.Prog.Files.File(p.Pos).Name())
var names []string
maxname := 0
// building bridge methods as needed for promoted methods.
// A nil result indicates an empty set.
//
-// Thread-safe. TODO(adonovan): explain concurrency invariants in detail.
+// Thread-safe.
func (p *Program) MethodSet(typ types.Type) MethodSet {
if !canHaveConcreteMethods(typ, true) {
return nil
//
func buildMethodSet(prog *Program, typ types.Type) MethodSet {
if prog.mode&LogSource != 0 {
- // TODO(adonovan): this isn't quite appropriate for LogSource
fmt.Fprintf(os.Stderr, "buildMethodSet %s %T\n", typ, typ)
}
}
fn.start(nil)
fn.addSpilledParam(sig.Recv)
- // TODO(adonovan): fix: test variadic case---careful with types.
+ var last *Parameter
for _, p := range fn.Signature.Params {
- fn.addParam(p.Name, p.Type)
+ last = fn.addParam(p.Name, p.Type)
+ }
+ if fn.Signature.IsVariadic {
+ last.Type_ = &types.Slice{Elt: last.Type_}
}
// Each bridge method performs a sequence of selections,
// TODO(adonovan): set fn.Pos to location of interface method ast.Field.
fn.start(nil)
fn.addParam("recv", typ)
- // TODO(adonovan): fix: test variadic case---careful with types.
+ var last *Parameter
for _, p := range fn.Signature.Params {
- fn.addParam(p.Name, p.Type)
+ last = fn.addParam(p.Name, p.Type)
+ }
+ if fn.Signature.IsVariadic {
+ last.Type_ = &types.Slice{Elt: last.Type_}
}
+
var c Call
c.Method = index
c.Recv = fn.Params[0]
// type-specific accessor methods Func, Type, Var and Const.
//
type Package struct {
- Prog *Program // the owning program
- Types *types.Package // the type checker's package object for this package.
- ImportPath string // e.g. "sync/atomic"
- Pos token.Pos // position of an arbitrary file in the package
- Members map[string]Member // all exported and unexported members of the package
- AnonFuncs []*Function // all anonymous functions in this package
- Init *Function // the package's (concatenated) init function
+ Prog *Program // the owning program
+ Types *types.Package // the type checker's package object for this package.
+ Pos token.Pos // position of an arbitrary file in the package
+ Members map[string]Member // all exported and unexported members of the package
+ AnonFuncs []*Function // all anonymous functions in this package
+ Init *Function // the package's (concatenated) init function
// The following fields are set transiently during building,
// then cleared.
Method int // index of interface method within Recv.Type().(*types.Interface).Methods
Func Value // target of call, iff function call
Args []Value // actual parameters, including receiver in invoke mode
- HasEllipsis bool // true iff last Args is a slice (needed?)
+ HasEllipsis bool // true iff last Args is a slice of '...' args (needed?)
Pos token.Pos // position of call expression
}