2013-08-27 16:49:13 -06:00
|
|
|
// 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.
|
|
|
|
|
2013-05-17 14:25:48 -06:00
|
|
|
package ssa
|
|
|
|
|
|
|
|
// This file implements the Function and BasicBlock types.
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
|
|
|
"io"
|
|
|
|
"os"
|
2013-05-22 15:56:18 -06:00
|
|
|
"strings"
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
// addEdge adds a control-flow graph edge from from to to.
|
|
|
|
func addEdge(from, to *BasicBlock) {
|
|
|
|
from.Succs = append(from.Succs, to)
|
|
|
|
to.Preds = append(to.Preds, from)
|
|
|
|
}
|
|
|
|
|
2013-06-13 12:43:35 -06:00
|
|
|
// Parent returns the function that contains block b.
|
|
|
|
func (b *BasicBlock) Parent() *Function { return b.parent }
|
|
|
|
|
2013-05-17 14:25:48 -06:00
|
|
|
// String returns a human-readable label of this block.
|
|
|
|
// It is not guaranteed unique within the function.
|
|
|
|
//
|
|
|
|
func (b *BasicBlock) String() string {
|
|
|
|
return fmt.Sprintf("%d.%s", b.Index, b.Comment)
|
|
|
|
}
|
|
|
|
|
|
|
|
// emit appends an instruction to the current basic block.
|
|
|
|
// If the instruction defines a Value, it is returned.
|
|
|
|
//
|
|
|
|
func (b *BasicBlock) emit(i Instruction) Value {
|
|
|
|
i.SetBlock(b)
|
|
|
|
b.Instrs = append(b.Instrs, i)
|
|
|
|
v, _ := i.(Value)
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// predIndex returns the i such that b.Preds[i] == c or panics if
|
|
|
|
// there is none.
|
|
|
|
func (b *BasicBlock) predIndex(c *BasicBlock) int {
|
|
|
|
for i, pred := range b.Preds {
|
|
|
|
if pred == c {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
panic(fmt.Sprintf("no edge %s -> %s", c, b))
|
|
|
|
}
|
|
|
|
|
|
|
|
// hasPhi returns true if b.Instrs contains φ-nodes.
|
|
|
|
func (b *BasicBlock) hasPhi() bool {
|
|
|
|
_, ok := b.Instrs[0].(*Phi)
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// phis returns the prefix of b.Instrs containing all the block's φ-nodes.
|
|
|
|
func (b *BasicBlock) phis() []Instruction {
|
|
|
|
for i, instr := range b.Instrs {
|
|
|
|
if _, ok := instr.(*Phi); !ok {
|
|
|
|
return b.Instrs[:i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil // unreachable in well-formed blocks
|
|
|
|
}
|
|
|
|
|
|
|
|
// replacePred replaces all occurrences of p in b's predecessor list with q.
|
|
|
|
// Ordinarily there should be at most one.
|
|
|
|
//
|
|
|
|
func (b *BasicBlock) replacePred(p, q *BasicBlock) {
|
|
|
|
for i, pred := range b.Preds {
|
|
|
|
if pred == p {
|
|
|
|
b.Preds[i] = q
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// replaceSucc replaces all occurrences of p in b's successor list with q.
|
|
|
|
// Ordinarily there should be at most one.
|
|
|
|
//
|
|
|
|
func (b *BasicBlock) replaceSucc(p, q *BasicBlock) {
|
|
|
|
for i, succ := range b.Succs {
|
|
|
|
if succ == p {
|
|
|
|
b.Succs[i] = q
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// removePred removes all occurrences of p in b's
|
|
|
|
// predecessor list and φ-nodes.
|
|
|
|
// Ordinarily there should be at most one.
|
|
|
|
//
|
|
|
|
func (b *BasicBlock) removePred(p *BasicBlock) {
|
|
|
|
phis := b.phis()
|
|
|
|
|
|
|
|
// We must preserve edge order for φ-nodes.
|
|
|
|
j := 0
|
|
|
|
for i, pred := range b.Preds {
|
|
|
|
if pred != p {
|
|
|
|
b.Preds[j] = b.Preds[i]
|
|
|
|
// Strike out φ-edge too.
|
|
|
|
for _, instr := range phis {
|
|
|
|
phi := instr.(*Phi)
|
|
|
|
phi.Edges[j] = phi.Edges[i]
|
|
|
|
}
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Nil out b.Preds[j:] and φ-edges[j:] to aid GC.
|
|
|
|
for i := j; i < len(b.Preds); i++ {
|
|
|
|
b.Preds[i] = nil
|
|
|
|
for _, instr := range phis {
|
|
|
|
instr.(*Phi).Edges[i] = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b.Preds = b.Preds[:j]
|
|
|
|
for _, instr := range phis {
|
|
|
|
phi := instr.(*Phi)
|
|
|
|
phi.Edges = phi.Edges[:j]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destinations associated with unlabelled for/switch/select stmts.
|
|
|
|
// We push/pop one of these as we enter/leave each construct and for
|
|
|
|
// each BranchStmt we scan for the innermost target of the right type.
|
|
|
|
//
|
|
|
|
type targets struct {
|
|
|
|
tail *targets // rest of stack
|
|
|
|
_break *BasicBlock
|
|
|
|
_continue *BasicBlock
|
|
|
|
_fallthrough *BasicBlock
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destinations associated with a labelled block.
|
|
|
|
// We populate these as labels are encountered in forward gotos or
|
|
|
|
// labelled statements.
|
|
|
|
//
|
|
|
|
type lblock struct {
|
|
|
|
_goto *BasicBlock
|
|
|
|
_break *BasicBlock
|
|
|
|
_continue *BasicBlock
|
|
|
|
}
|
|
|
|
|
|
|
|
// labelledBlock returns the branch target associated with the
|
|
|
|
// specified label, creating it if needed.
|
|
|
|
//
|
|
|
|
func (f *Function) labelledBlock(label *ast.Ident) *lblock {
|
|
|
|
lb := f.lblocks[label.Obj]
|
|
|
|
if lb == nil {
|
|
|
|
lb = &lblock{_goto: f.newBasicBlock(label.Name)}
|
|
|
|
if f.lblocks == nil {
|
|
|
|
f.lblocks = make(map[*ast.Object]*lblock)
|
|
|
|
}
|
|
|
|
f.lblocks[label.Obj] = lb
|
|
|
|
}
|
|
|
|
return lb
|
|
|
|
}
|
|
|
|
|
|
|
|
// addParam adds a (non-escaping) parameter to f.Params of the
|
2013-05-30 07:59:17 -06:00
|
|
|
// specified name, type and source position.
|
2013-05-17 14:25:48 -06:00
|
|
|
//
|
2013-05-30 07:59:17 -06:00
|
|
|
func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter {
|
2013-05-17 14:25:48 -06:00
|
|
|
v := &Parameter{
|
2013-06-13 12:43:35 -06:00
|
|
|
name: name,
|
|
|
|
typ: typ,
|
|
|
|
pos: pos,
|
|
|
|
parent: f,
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
f.Params = append(f.Params, v)
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2013-05-30 07:59:17 -06:00
|
|
|
func (f *Function) addParamObj(obj types.Object) *Parameter {
|
|
|
|
name := obj.Name()
|
|
|
|
if name == "" {
|
|
|
|
name = fmt.Sprintf("arg%d", len(f.Params))
|
|
|
|
}
|
go.tools/ssa: add debug information for all ast.Idents.
This CL adds three new functions to determine the SSA Value
for a given syntactic var, func or const object:
Program.{Const,Func,Var}Value.
Since constants and functions are immutable, the first
two only need a types.Object; but each distinct
reference to a var may return a distinct Value, so the third
requires an ast.Ident parameter too.
Debug information for local vars is encoded in the
instruction stream in the form of DebugRef instructions,
which are a no-op but relate their operand to a particular
ident in the AST. The beauty of this approach is that it
naturally stays consistent during optimisation passes
(e.g. lifting) without additional bookkeeping.
DebugRef instructions are only generated if the DebugMode
builder flag is set; I plan to make the policy more fine-
grained (per function).
DebugRef instructions are inserted for:
- expr(Ident) for rvalue idents
- address.store() for idents that update an lvalue
- address.address() for idents that take address of lvalue
(this new method replaces all uses of lval.(address).addr)
- expr() for all constant expressions
- local ValueSpecs with implicit zero initialization (no RHS)
(this case doesn't call store() or address())
To ensure we don't forget to emit debug info for uses of Idents,
we must use the lvalue mechanism consistently. (Previously,
many simple cases had effectively inlined these functions.)
Similarly setCallFunc no longer inlines expr(Ident).
Also:
- Program.Value() has been inlined & specialized.
- Program.Package() has moved nearer the new lookup functions.
- refactoring: funcSyntax has lost paramFields, resultFields;
gained funcType, which provides access to both.
- add package-level constants to Package.values map.
- opt: don't call localValueSpec for constants.
(The resulting code is always optimised away.)
There are a number of comments asking whether Literals
should have positions. Will address in a follow-up.
Added tests of all interesting cases.
R=gri
CC=golang-dev
https://golang.org/cl/11259044
2013-07-15 11:56:46 -06:00
|
|
|
param := f.addParam(name, obj.Type(), obj.Pos())
|
|
|
|
param.object = obj
|
|
|
|
return param
|
2013-05-30 07:59:17 -06:00
|
|
|
}
|
|
|
|
|
2013-05-17 14:25:48 -06:00
|
|
|
// 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) {
|
2013-05-30 07:59:17 -06:00
|
|
|
param := f.addParamObj(obj)
|
2013-08-01 12:06:10 -06:00
|
|
|
spill := &Alloc{Comment: obj.Name()}
|
|
|
|
spill.setType(types.NewPointer(obj.Type()))
|
|
|
|
spill.setPos(obj.Pos())
|
2013-05-17 14:25:48 -06:00
|
|
|
f.objects[obj] = spill
|
|
|
|
f.Locals = append(f.Locals, spill)
|
|
|
|
f.emit(spill)
|
|
|
|
f.emit(&Store{Addr: spill, Val: param})
|
|
|
|
}
|
|
|
|
|
|
|
|
// startBody initializes the function prior to generating SSA code for its body.
|
|
|
|
// Precondition: f.Type() already set.
|
|
|
|
//
|
|
|
|
func (f *Function) startBody() {
|
|
|
|
f.currentBlock = f.newBasicBlock("entry")
|
|
|
|
f.objects = make(map[types.Object]Value) // needed for some synthetics, e.g. init
|
|
|
|
}
|
|
|
|
|
|
|
|
// createSyntacticParams populates f.Params and generates code (spills
|
|
|
|
// and named result locals) for all the parameters declared in the
|
|
|
|
// syntax. In addition it populates the f.objects mapping.
|
|
|
|
//
|
|
|
|
// Preconditions:
|
|
|
|
// f.startBody() was called.
|
|
|
|
// Postcondition:
|
2013-05-17 15:02:47 -06:00
|
|
|
// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
|
2013-05-17 14:25:48 -06:00
|
|
|
//
|
2013-10-27 08:55:21 -06:00
|
|
|
func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
|
2013-05-17 14:25:48 -06:00
|
|
|
// Receiver (at most one inner iteration).
|
2013-10-27 08:55:21 -06:00
|
|
|
if recv != nil {
|
|
|
|
for _, field := range recv.List {
|
2013-05-17 14:25:48 -06:00
|
|
|
for _, n := range field.Names {
|
2013-05-31 14:14:13 -06:00
|
|
|
f.addSpilledParam(f.Pkg.objectOf(n))
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
// Anonymous receiver? No need to spill.
|
|
|
|
if field.Names == nil {
|
2013-05-30 07:59:17 -06:00
|
|
|
f.addParamObj(f.Signature.Recv())
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parameters.
|
2013-10-27 08:55:21 -06:00
|
|
|
if functype.Params != nil {
|
2013-05-17 14:25:48 -06:00
|
|
|
n := len(f.Params) // 1 if has recv, 0 otherwise
|
2013-10-27 08:55:21 -06:00
|
|
|
for _, field := range functype.Params.List {
|
2013-05-17 14:25:48 -06:00
|
|
|
for _, n := range field.Names {
|
2013-05-31 14:14:13 -06:00
|
|
|
f.addSpilledParam(f.Pkg.objectOf(n))
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
// Anonymous parameter? No need to spill.
|
|
|
|
if field.Names == nil {
|
2013-05-30 07:59:17 -06:00
|
|
|
f.addParamObj(f.Signature.Params().At(len(f.Params) - n))
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Named results.
|
2013-10-27 08:55:21 -06:00
|
|
|
if functype.Results != nil {
|
|
|
|
for _, field := range functype.Results.List {
|
2013-05-17 14:25:48 -06:00
|
|
|
// Implicit "var" decl of locals for named results.
|
|
|
|
for _, n := range field.Names {
|
2013-07-03 13:10:49 -06:00
|
|
|
f.namedResults = append(f.namedResults, f.addLocalForIdent(n))
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// numberRegisters assigns numbers to all SSA registers
|
|
|
|
// (value-defining Instructions) in f, to aid debugging.
|
|
|
|
// (Non-Instruction Values are named at construction.)
|
|
|
|
//
|
|
|
|
func numberRegisters(f *Function) {
|
2013-08-01 12:06:10 -06:00
|
|
|
v := 0
|
2013-05-17 14:25:48 -06:00
|
|
|
for _, b := range f.Blocks {
|
|
|
|
for _, instr := range b.Instrs {
|
2013-08-01 12:06:10 -06:00
|
|
|
switch instr.(type) {
|
2013-05-17 14:25:48 -06:00
|
|
|
case Value:
|
|
|
|
instr.(interface {
|
|
|
|
setNum(int)
|
|
|
|
}).setNum(v)
|
|
|
|
v++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// buildReferrers populates the def/use information in all non-nil
|
|
|
|
// Value.Referrers slice.
|
|
|
|
// Precondition: all such slices are initially empty.
|
|
|
|
func buildReferrers(f *Function) {
|
|
|
|
var rands []*Value
|
|
|
|
for _, b := range f.Blocks {
|
|
|
|
for _, instr := range b.Instrs {
|
|
|
|
rands = instr.Operands(rands[:0]) // recycle storage
|
|
|
|
for _, rand := range rands {
|
|
|
|
if r := *rand; r != nil {
|
|
|
|
if ref := r.Referrers(); ref != nil {
|
|
|
|
*ref = append(*ref, instr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// finishBody() finalizes the function after SSA code generation of its body.
|
|
|
|
func (f *Function) finishBody() {
|
|
|
|
f.objects = nil
|
|
|
|
f.currentBlock = nil
|
|
|
|
f.lblocks = nil
|
2013-10-27 08:55:21 -06:00
|
|
|
|
|
|
|
// Don't pin the AST in memory (except in debug mode).
|
|
|
|
if n := f.syntax; n != nil && !f.debugInfo() {
|
|
|
|
f.syntax = extentNode{n.Pos(), n.End()}
|
|
|
|
}
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
// Remove any f.Locals that are now heap-allocated.
|
|
|
|
j := 0
|
|
|
|
for _, l := range f.Locals {
|
|
|
|
if !l.Heap {
|
|
|
|
f.Locals[j] = l
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Nil out f.Locals[j:] to aid GC.
|
|
|
|
for i := j; i < len(f.Locals); i++ {
|
|
|
|
f.Locals[i] = nil
|
|
|
|
}
|
|
|
|
f.Locals = f.Locals[:j]
|
|
|
|
|
|
|
|
optimizeBlocks(f)
|
|
|
|
|
|
|
|
buildReferrers(f)
|
|
|
|
|
|
|
|
if f.Prog.mode&NaiveForm == 0 {
|
|
|
|
// For debugging pre-state of lifting pass:
|
|
|
|
// numberRegisters(f)
|
|
|
|
// f.DumpTo(os.Stderr)
|
|
|
|
|
|
|
|
lift(f)
|
|
|
|
}
|
|
|
|
|
go.tools/ssa: implement correct control flow for recovered panic.
A function such as this:
func one() (x int) {
defer func() { recover() }()
x = 1
panic("return")
}
that combines named return parameters (NRPs) with deferred calls
that call recover, may return non-zero values despite the
fact it doesn't even contain a return statement. (!)
This requires a change to the SSA API: all functions'
control-flow graphs now have a second entry point, called
Recover, which is the block at which control flow resumes
after a recovered panic. The Recover block simply loads the
NRPs and returns them.
As an optimization, most functions don't need a Recover block,
so it is omitted. In fact it is only needed for functions that
have NRPs and defer a call to another function that _may_ call
recover.
Dataflow analysis of SSA now requires extra work, since every
may-panic instruction has an implicit control-flow edge to
the Recover block. The only dataflow analysis so far implemented
is SSA renaming, for which we make the following simplifying
assumption: the Recover block only loads the NRPs and returns.
This means we don't really need to analyze it, we can just
skip the "lifting" of such NRPs. We also special-case the Recover
block in the dominance computation.
Rejected alternative approaches:
- Specifying a Recover block for every defer instruction (like a
traditional exception handler).
This seemed like excessive generality, since Go programs
only need the same degenerate form of Recover block.
- Adding an instruction to set the Recover block immediately
after the named return values are set up, so that dominance
can be computed without special-casing.
This didn't seem worth the effort.
Interpreter:
- This CL completely reimplements the panic/recover/
defer logic in the interpreter. It's clearer and simpler
and closer to the model in the spec.
- Some runtime panic messages have been changed to be closer
to gc's, since tests depend on it.
- The interpreter now requires that the runtime.runtimeError
type be part of the SSA program. This requires that clients
import this package prior to invoking the interpreter.
This in turn requires (Importer).ImportPackage(path string),
which this CL adds.
- All $GOROOT/test/recover{,1,2,3}.go tests are now passing.
NB, the bug described in coverage.go (defer/recover in a concatenated
init function) remains. Will be fixed in a follow-up.
Fixes golang/go#6381
R=gri
CC=crawshaw, golang-dev
https://golang.org/cl/13844043
2013-10-14 13:38:56 -06:00
|
|
|
f.namedResults = nil // (used by lifting)
|
|
|
|
|
2013-05-17 14:25:48 -06:00
|
|
|
numberRegisters(f)
|
|
|
|
|
|
|
|
if f.Prog.mode&LogFunctions != 0 {
|
|
|
|
f.DumpTo(os.Stderr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.Prog.mode&SanityCheckFunctions != 0 {
|
2013-07-11 12:12:30 -06:00
|
|
|
mustSanityCheck(f, nil)
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// removeNilBlocks eliminates nils from f.Blocks and updates each
|
|
|
|
// BasicBlock.Index. Use this after any pass that may delete blocks.
|
|
|
|
//
|
|
|
|
func (f *Function) removeNilBlocks() {
|
|
|
|
j := 0
|
|
|
|
for _, b := range f.Blocks {
|
|
|
|
if b != nil {
|
|
|
|
b.Index = j
|
|
|
|
f.Blocks[j] = b
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Nil out f.Blocks[j:] to aid GC.
|
|
|
|
for i := j; i < len(f.Blocks); i++ {
|
|
|
|
f.Blocks[i] = nil
|
|
|
|
}
|
|
|
|
f.Blocks = f.Blocks[:j]
|
|
|
|
}
|
|
|
|
|
2013-07-31 11:13:05 -06:00
|
|
|
// SetDebugMode sets the debug mode for package pkg. If true, all its
|
2013-10-09 10:47:30 -06:00
|
|
|
// functions will include full debug info. This greatly increases the
|
|
|
|
// size of the instruction stream, and causes Functions to depend upon
|
|
|
|
// the ASTs, potentially keeping them live in memory for longer.
|
2013-07-31 11:13:05 -06:00
|
|
|
//
|
|
|
|
func (pkg *Package) SetDebugMode(debug bool) {
|
|
|
|
// TODO(adonovan): do we want ast.File granularity?
|
|
|
|
pkg.debug = debug
|
|
|
|
}
|
|
|
|
|
go.tools/ssa: add debug information for all ast.Idents.
This CL adds three new functions to determine the SSA Value
for a given syntactic var, func or const object:
Program.{Const,Func,Var}Value.
Since constants and functions are immutable, the first
two only need a types.Object; but each distinct
reference to a var may return a distinct Value, so the third
requires an ast.Ident parameter too.
Debug information for local vars is encoded in the
instruction stream in the form of DebugRef instructions,
which are a no-op but relate their operand to a particular
ident in the AST. The beauty of this approach is that it
naturally stays consistent during optimisation passes
(e.g. lifting) without additional bookkeeping.
DebugRef instructions are only generated if the DebugMode
builder flag is set; I plan to make the policy more fine-
grained (per function).
DebugRef instructions are inserted for:
- expr(Ident) for rvalue idents
- address.store() for idents that update an lvalue
- address.address() for idents that take address of lvalue
(this new method replaces all uses of lval.(address).addr)
- expr() for all constant expressions
- local ValueSpecs with implicit zero initialization (no RHS)
(this case doesn't call store() or address())
To ensure we don't forget to emit debug info for uses of Idents,
we must use the lvalue mechanism consistently. (Previously,
many simple cases had effectively inlined these functions.)
Similarly setCallFunc no longer inlines expr(Ident).
Also:
- Program.Value() has been inlined & specialized.
- Program.Package() has moved nearer the new lookup functions.
- refactoring: funcSyntax has lost paramFields, resultFields;
gained funcType, which provides access to both.
- add package-level constants to Package.values map.
- opt: don't call localValueSpec for constants.
(The resulting code is always optimised away.)
There are a number of comments asking whether Literals
should have positions. Will address in a follow-up.
Added tests of all interesting cases.
R=gri
CC=golang-dev
https://golang.org/cl/11259044
2013-07-15 11:56:46 -06:00
|
|
|
// debugInfo reports whether debug info is wanted for this function.
|
|
|
|
func (f *Function) debugInfo() bool {
|
2013-10-27 08:55:21 -06:00
|
|
|
return f.Pkg != nil && f.Pkg.debug
|
go.tools/ssa: add debug information for all ast.Idents.
This CL adds three new functions to determine the SSA Value
for a given syntactic var, func or const object:
Program.{Const,Func,Var}Value.
Since constants and functions are immutable, the first
two only need a types.Object; but each distinct
reference to a var may return a distinct Value, so the third
requires an ast.Ident parameter too.
Debug information for local vars is encoded in the
instruction stream in the form of DebugRef instructions,
which are a no-op but relate their operand to a particular
ident in the AST. The beauty of this approach is that it
naturally stays consistent during optimisation passes
(e.g. lifting) without additional bookkeeping.
DebugRef instructions are only generated if the DebugMode
builder flag is set; I plan to make the policy more fine-
grained (per function).
DebugRef instructions are inserted for:
- expr(Ident) for rvalue idents
- address.store() for idents that update an lvalue
- address.address() for idents that take address of lvalue
(this new method replaces all uses of lval.(address).addr)
- expr() for all constant expressions
- local ValueSpecs with implicit zero initialization (no RHS)
(this case doesn't call store() or address())
To ensure we don't forget to emit debug info for uses of Idents,
we must use the lvalue mechanism consistently. (Previously,
many simple cases had effectively inlined these functions.)
Similarly setCallFunc no longer inlines expr(Ident).
Also:
- Program.Value() has been inlined & specialized.
- Program.Package() has moved nearer the new lookup functions.
- refactoring: funcSyntax has lost paramFields, resultFields;
gained funcType, which provides access to both.
- add package-level constants to Package.values map.
- opt: don't call localValueSpec for constants.
(The resulting code is always optimised away.)
There are a number of comments asking whether Literals
should have positions. Will address in a follow-up.
Added tests of all interesting cases.
R=gri
CC=golang-dev
https://golang.org/cl/11259044
2013-07-15 11:56:46 -06:00
|
|
|
}
|
|
|
|
|
2013-05-17 14:25:48 -06:00
|
|
|
// addNamedLocal creates a local variable, adds it to function f and
|
|
|
|
// returns it. Its name and type are taken from obj. Subsequent
|
|
|
|
// calls to f.lookup(obj) will return the same local.
|
|
|
|
//
|
|
|
|
func (f *Function) addNamedLocal(obj types.Object) *Alloc {
|
2013-05-17 15:02:47 -06:00
|
|
|
l := f.addLocal(obj.Type(), obj.Pos())
|
2013-08-01 12:06:10 -06:00
|
|
|
l.Comment = obj.Name()
|
2013-05-17 14:25:48 -06:00
|
|
|
f.objects[obj] = l
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
2013-07-03 13:10:49 -06:00
|
|
|
func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc {
|
|
|
|
return f.addNamedLocal(f.Pkg.objectOf(id))
|
|
|
|
}
|
|
|
|
|
2013-05-17 14:25:48 -06:00
|
|
|
// addLocal creates an anonymous local variable of type typ, adds it
|
|
|
|
// to function f and returns it. pos is the optional source location.
|
|
|
|
//
|
|
|
|
func (f *Function) addLocal(typ types.Type, pos token.Pos) *Alloc {
|
2013-08-01 12:06:10 -06:00
|
|
|
v := &Alloc{}
|
|
|
|
v.setType(types.NewPointer(typ))
|
|
|
|
v.setPos(pos)
|
2013-05-17 14:25:48 -06:00
|
|
|
f.Locals = append(f.Locals, v)
|
|
|
|
f.emit(v)
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// lookup returns the address of the named variable identified by obj
|
|
|
|
// that is local to function f or one of its enclosing functions.
|
|
|
|
// If escaping, the reference comes from a potentially escaping pointer
|
|
|
|
// expression and the referent must be heap-allocated.
|
|
|
|
//
|
|
|
|
func (f *Function) lookup(obj types.Object, escaping bool) Value {
|
|
|
|
if v, ok := f.objects[obj]; ok {
|
2013-05-22 15:56:18 -06:00
|
|
|
if alloc, ok := v.(*Alloc); ok && escaping {
|
|
|
|
alloc.Heap = true
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
return v // function-local var (address)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Definition must be in an enclosing function;
|
|
|
|
// plumb it through intervening closures.
|
|
|
|
if f.Enclosing == nil {
|
2013-05-17 15:02:47 -06:00
|
|
|
panic("no Value for type.Object " + obj.Name())
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
2013-05-22 15:56:18 -06:00
|
|
|
outer := f.Enclosing.lookup(obj, true) // escaping
|
|
|
|
v := &Capture{
|
2013-09-06 07:19:34 -06:00
|
|
|
name: obj.Name(),
|
2013-06-13 12:43:35 -06:00
|
|
|
typ: outer.Type(),
|
|
|
|
pos: outer.Pos(),
|
|
|
|
outer: outer,
|
|
|
|
parent: f,
|
2013-05-22 15:56:18 -06:00
|
|
|
}
|
2013-05-17 14:25:48 -06:00
|
|
|
f.objects[obj] = v
|
|
|
|
f.FreeVars = append(f.FreeVars, v)
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// emit emits the specified instruction to function f, updating the
|
|
|
|
// control-flow graph if required.
|
|
|
|
//
|
|
|
|
func (f *Function) emit(instr Instruction) Value {
|
|
|
|
return f.currentBlock.emit(instr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FullName returns the full name of this function, qualified by
|
|
|
|
// package name, receiver type, etc.
|
|
|
|
//
|
|
|
|
// The specific formatting rules are not guaranteed and may change.
|
|
|
|
//
|
|
|
|
// Examples:
|
|
|
|
// "math.IsNaN" // a package-level function
|
|
|
|
// "IsNaN" // intra-package reference to same
|
|
|
|
// "(*sync.WaitGroup).Add" // a declared method
|
2013-10-08 10:31:39 -06:00
|
|
|
// "(*exp/ssa.Return).Block" // a promotion wrapper method
|
2013-06-14 13:50:37 -06:00
|
|
|
// "(ssa.Instruction).Block" // an interface method wrapper
|
2013-05-17 14:25:48 -06:00
|
|
|
// "func@5.32" // an anonymous function
|
2013-06-14 13:50:37 -06:00
|
|
|
// "bound$(*T).f" // a bound method wrapper
|
2013-05-17 14:25:48 -06:00
|
|
|
//
|
2013-06-26 10:38:08 -06:00
|
|
|
// If from==f.Pkg, suppress package qualification.
|
2013-05-17 14:25:48 -06:00
|
|
|
func (f *Function) fullName(from *Package) string {
|
2013-07-26 09:22:34 -06:00
|
|
|
// TODO(adonovan): expose less fragile case discrimination
|
|
|
|
// using f.method.
|
2013-05-22 15:56:18 -06:00
|
|
|
|
2013-05-17 14:25:48 -06:00
|
|
|
// Anonymous?
|
|
|
|
if f.Enclosing != nil {
|
2013-05-30 07:59:17 -06:00
|
|
|
return f.name
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
|
2013-08-19 13:38:30 -06:00
|
|
|
// Declared method, or promotion/indirection wrapper?
|
|
|
|
if recv := f.Signature.Recv(); recv != nil {
|
|
|
|
return fmt.Sprintf("(%s).%s", relType(recv.Type(), from), f.name)
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
|
2013-08-19 13:38:30 -06:00
|
|
|
// Other synthetic wrapper?
|
|
|
|
if f.Synthetic != "" {
|
|
|
|
// Bound method wrapper?
|
|
|
|
if strings.HasPrefix(f.name, "bound$") {
|
|
|
|
return f.name
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interface method wrapper?
|
|
|
|
if strings.HasPrefix(f.Synthetic, "interface ") {
|
|
|
|
return fmt.Sprintf("(%s).%s", relType(f.Params[0].Type(), from), f.name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// "package initializer" or "loaded from GC object file": fall through.
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Package-level function.
|
|
|
|
// Prefix with package name for cross-package references only.
|
|
|
|
if from != f.Pkg {
|
2013-07-01 13:24:50 -06:00
|
|
|
return fmt.Sprintf("%s.%s", f.Pkg.Object.Path(), f.name)
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
2013-05-30 07:59:17 -06:00
|
|
|
return f.name
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// writeSignature writes to w the signature sig in declaration syntax.
|
|
|
|
// Derived from types.Signature.String().
|
|
|
|
//
|
|
|
|
func writeSignature(w io.Writer, name string, sig *types.Signature, params []*Parameter) {
|
|
|
|
io.WriteString(w, "func ")
|
2013-05-17 15:02:47 -06:00
|
|
|
if recv := sig.Recv(); recv != nil {
|
2013-05-17 14:25:48 -06:00
|
|
|
io.WriteString(w, "(")
|
|
|
|
if n := params[0].Name(); n != "" {
|
|
|
|
io.WriteString(w, n)
|
|
|
|
io.WriteString(w, " ")
|
|
|
|
}
|
|
|
|
io.WriteString(w, params[0].Type().String())
|
|
|
|
io.WriteString(w, ") ")
|
|
|
|
params = params[1:]
|
|
|
|
}
|
|
|
|
io.WriteString(w, name)
|
|
|
|
io.WriteString(w, "(")
|
|
|
|
for i, v := range params {
|
|
|
|
if i > 0 {
|
|
|
|
io.WriteString(w, ", ")
|
|
|
|
}
|
|
|
|
io.WriteString(w, v.Name())
|
|
|
|
io.WriteString(w, " ")
|
2013-05-17 15:02:47 -06:00
|
|
|
if sig.IsVariadic() && i == len(params)-1 {
|
2013-05-17 14:25:48 -06:00
|
|
|
io.WriteString(w, "...")
|
2013-05-17 15:02:47 -06:00
|
|
|
io.WriteString(w, v.Type().Underlying().(*types.Slice).Elem().String())
|
2013-05-17 14:25:48 -06:00
|
|
|
} else {
|
|
|
|
io.WriteString(w, v.Type().String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
io.WriteString(w, ")")
|
2013-05-17 15:02:47 -06:00
|
|
|
if n := sig.Results().Len(); n > 0 {
|
2013-05-17 14:25:48 -06:00
|
|
|
io.WriteString(w, " ")
|
2013-05-17 15:02:47 -06:00
|
|
|
r := sig.Results()
|
|
|
|
if n == 1 && r.At(0).Name() == "" {
|
|
|
|
io.WriteString(w, r.At(0).Type().String())
|
2013-05-17 14:25:48 -06:00
|
|
|
} else {
|
2013-05-17 15:02:47 -06:00
|
|
|
io.WriteString(w, r.String())
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// DumpTo prints to w a human readable "disassembly" of the SSA code of
|
|
|
|
// all basic blocks of function f.
|
|
|
|
//
|
|
|
|
func (f *Function) DumpTo(w io.Writer) {
|
2013-06-26 10:38:08 -06:00
|
|
|
fmt.Fprintf(w, "# Name: %s\n", f.String())
|
2013-07-03 15:57:20 -06:00
|
|
|
if syn := f.Synthetic; syn != "" {
|
|
|
|
fmt.Fprintln(w, "# Synthetic:", syn)
|
|
|
|
}
|
2013-05-22 15:56:18 -06:00
|
|
|
if pos := f.Pos(); pos.IsValid() {
|
2013-07-03 15:57:20 -06:00
|
|
|
fmt.Fprintf(w, "# Location: %s\n", f.Prog.Fset.Position(pos))
|
2013-05-22 15:56:18 -06:00
|
|
|
}
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
if f.Enclosing != nil {
|
|
|
|
fmt.Fprintf(w, "# Parent: %s\n", f.Enclosing.Name())
|
|
|
|
}
|
|
|
|
|
go.tools/ssa: implement correct control flow for recovered panic.
A function such as this:
func one() (x int) {
defer func() { recover() }()
x = 1
panic("return")
}
that combines named return parameters (NRPs) with deferred calls
that call recover, may return non-zero values despite the
fact it doesn't even contain a return statement. (!)
This requires a change to the SSA API: all functions'
control-flow graphs now have a second entry point, called
Recover, which is the block at which control flow resumes
after a recovered panic. The Recover block simply loads the
NRPs and returns them.
As an optimization, most functions don't need a Recover block,
so it is omitted. In fact it is only needed for functions that
have NRPs and defer a call to another function that _may_ call
recover.
Dataflow analysis of SSA now requires extra work, since every
may-panic instruction has an implicit control-flow edge to
the Recover block. The only dataflow analysis so far implemented
is SSA renaming, for which we make the following simplifying
assumption: the Recover block only loads the NRPs and returns.
This means we don't really need to analyze it, we can just
skip the "lifting" of such NRPs. We also special-case the Recover
block in the dominance computation.
Rejected alternative approaches:
- Specifying a Recover block for every defer instruction (like a
traditional exception handler).
This seemed like excessive generality, since Go programs
only need the same degenerate form of Recover block.
- Adding an instruction to set the Recover block immediately
after the named return values are set up, so that dominance
can be computed without special-casing.
This didn't seem worth the effort.
Interpreter:
- This CL completely reimplements the panic/recover/
defer logic in the interpreter. It's clearer and simpler
and closer to the model in the spec.
- Some runtime panic messages have been changed to be closer
to gc's, since tests depend on it.
- The interpreter now requires that the runtime.runtimeError
type be part of the SSA program. This requires that clients
import this package prior to invoking the interpreter.
This in turn requires (Importer).ImportPackage(path string),
which this CL adds.
- All $GOROOT/test/recover{,1,2,3}.go tests are now passing.
NB, the bug described in coverage.go (defer/recover in a concatenated
init function) remains. Will be fixed in a follow-up.
Fixes golang/go#6381
R=gri
CC=crawshaw, golang-dev
https://golang.org/cl/13844043
2013-10-14 13:38:56 -06:00
|
|
|
if f.Recover != nil {
|
|
|
|
fmt.Fprintf(w, "# Recover: %s\n", f.Recover)
|
|
|
|
}
|
|
|
|
|
2013-05-17 14:25:48 -06:00
|
|
|
if f.FreeVars != nil {
|
|
|
|
io.WriteString(w, "# Free variables:\n")
|
|
|
|
for i, fv := range f.FreeVars {
|
|
|
|
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, fv.Name(), fv.Type())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(f.Locals) > 0 {
|
|
|
|
io.WriteString(w, "# Locals:\n")
|
|
|
|
for i, l := range f.Locals {
|
2013-07-12 22:09:33 -06:00
|
|
|
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), deref(l.Type()))
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
writeSignature(w, f.Name(), f.Signature, f.Params)
|
|
|
|
io.WriteString(w, ":\n")
|
|
|
|
|
|
|
|
if f.Blocks == nil {
|
|
|
|
io.WriteString(w, "\t(external)\n")
|
|
|
|
}
|
|
|
|
|
2013-05-17 15:33:09 -06:00
|
|
|
// NB. column calculations are confused by non-ASCII characters.
|
|
|
|
const punchcard = 80 // for old time's sake.
|
2013-05-17 14:25:48 -06:00
|
|
|
for _, b := range f.Blocks {
|
|
|
|
if b == nil {
|
|
|
|
// Corrupt CFG.
|
|
|
|
fmt.Fprintf(w, ".nil:\n")
|
|
|
|
continue
|
|
|
|
}
|
2013-05-17 15:33:09 -06:00
|
|
|
n, _ := fmt.Fprintf(w, ".%s:", b)
|
|
|
|
fmt.Fprintf(w, "%*sP:%d S:%d\n", punchcard-1-n-len("P:n S:n"), "", len(b.Preds), len(b.Succs))
|
|
|
|
|
2013-05-17 14:25:48 -06:00
|
|
|
if false { // CFG debugging
|
|
|
|
fmt.Fprintf(w, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
|
|
|
|
}
|
|
|
|
for _, instr := range b.Instrs {
|
|
|
|
io.WriteString(w, "\t")
|
|
|
|
switch v := instr.(type) {
|
|
|
|
case Value:
|
2013-05-17 15:33:09 -06:00
|
|
|
l := punchcard
|
2013-05-17 14:25:48 -06:00
|
|
|
// Left-align the instruction.
|
|
|
|
if name := v.Name(); name != "" {
|
|
|
|
n, _ := fmt.Fprintf(w, "%s = ", name)
|
|
|
|
l -= n
|
|
|
|
}
|
|
|
|
n, _ := io.WriteString(w, instr.String())
|
|
|
|
l -= n
|
|
|
|
// Right-align the type.
|
|
|
|
if t := v.Type(); t != nil {
|
|
|
|
fmt.Fprintf(w, " %*s", l-10, t)
|
|
|
|
}
|
|
|
|
case nil:
|
|
|
|
// Be robust against bad transforms.
|
|
|
|
io.WriteString(w, "<deleted>")
|
|
|
|
default:
|
|
|
|
io.WriteString(w, instr.String())
|
|
|
|
}
|
|
|
|
io.WriteString(w, "\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
// newBasicBlock adds to f a new basic block and returns it. It does
|
|
|
|
// not automatically become the current block for subsequent calls to emit.
|
|
|
|
// comment is an optional string for more readable debugging output.
|
|
|
|
//
|
|
|
|
func (f *Function) newBasicBlock(comment string) *BasicBlock {
|
|
|
|
b := &BasicBlock{
|
|
|
|
Index: len(f.Blocks),
|
|
|
|
Comment: comment,
|
2013-06-13 12:43:35 -06:00
|
|
|
parent: f,
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
b.Succs = b.succs2[:0]
|
|
|
|
f.Blocks = append(f.Blocks, b)
|
|
|
|
return b
|
|
|
|
}
|
2013-05-30 07:59:17 -06:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
//
|
2013-07-03 15:57:20 -06:00
|
|
|
func NewFunction(name string, sig *types.Signature, provenance string) *Function {
|
|
|
|
return &Function{name: name, Signature: sig, Synthetic: provenance}
|
2013-05-30 07:59:17 -06:00
|
|
|
}
|
2013-10-27 08:55:21 -06:00
|
|
|
|
|
|
|
type extentNode [2]token.Pos
|
|
|
|
|
|
|
|
func (n extentNode) Pos() token.Pos { return n[0] }
|
|
|
|
func (n extentNode) End() token.Pos { return n[1] }
|
|
|
|
|
|
|
|
// Syntax returns an ast.Node whose Pos/End methods provide the
|
|
|
|
// lexical extent of the function if it was defined by Go source code
|
|
|
|
// (f.Synthetic==""), or nil otherwise.
|
|
|
|
//
|
|
|
|
// If f was built with debug information (see Package.SetDebugRef),
|
|
|
|
// the result is the *ast.FuncDecl or *ast.FuncLit that declared the
|
|
|
|
// function. Otherwise, it is an opaque Node providing only position
|
|
|
|
// information; this avoids pinning the AST in memory.
|
|
|
|
//
|
|
|
|
func (f *Function) Syntax() ast.Node { return f.syntax }
|