// TODO(adonovan): instead of creating several "unreachable" blocks
// per function in the Builder, reuse a single one (e.g. at Blocks[1])
// to reduce garbage.
-//
-// TODO(adonovan): in the absence of multiway branch instructions,
-// each BasicBlock has 0, 1, or 2 successors. We should preallocate
-// the backing array for the Succs slice inline in BasicBlock.
import (
"fmt"
}
// A inherits B's successors
- a.Succs = b.Succs
+ a.Succs = append(a.succs2[:0], b.Succs...)
// Fix up Preds links of all successors of B.
for _, c := range b.Succs {
func (f *Function) addParam(name string, typ types.Type) *Parameter {
v := &Parameter{
Name_: name,
- Type_: pointer(typ), // address of param
+ Type_: typ,
}
f.Params = append(f.Params, v)
return v
}
-func (f *Function) addObjParam(obj types.Object) *Parameter {
- p := f.addParam(obj.GetName(), obj.GetType())
- f.objects[obj] = p
- return p
+// addSpilledParam declares a parameter that is pre-spilled to the
+// stack; the function body will load/store the spilled location.
+// Subsequent registerization will eliminate spills where possible.
+//
+func (f *Function) addSpilledParam(obj types.Object) {
+ name := obj.GetName()
+ param := f.addParam(name, obj.GetType())
+ spill := &Alloc{
+ Name_: name + "~", // "~" means "spilled"
+ Type_: pointer(obj.GetType()),
+ }
+ f.objects[obj] = spill
+ f.Locals = append(f.Locals, spill)
+ f.emit(spill)
+ f.emit(&Store{Addr: spill, Val: param})
}
// start initializes the function prior to generating SSA code for its body.
if f.syntax.recvField != nil {
for _, field := range f.syntax.recvField.List {
for _, n := range field.Names {
- f.addObjParam(idents[n])
+ f.addSpilledParam(idents[n])
}
if field.Names == nil {
f.addParam(f.Signature.Recv.Name, f.Signature.Recv.Type)
if f.syntax.paramFields != nil {
for _, field := range f.syntax.paramFields.List {
for _, n := range field.Names {
- f.addObjParam(idents[n])
+ f.addSpilledParam(idents[n])
}
}
}
func (f *Function) lookup(obj types.Object, escaping bool) Value {
if v, ok := f.objects[obj]; ok {
if escaping {
- switch v := v.(type) {
- case *Capture:
- // TODO(adonovan): fix: we must support this case.
- // Requires copying to a 'new' Alloc.
- fmt.Fprintln(os.Stderr, "Error: escaping reference to Capture")
- case *Parameter:
- v.Heap = true
- case *Alloc:
- v.Heap = true
- default:
- panic(fmt.Sprintf("Unexpected Function.objects kind: %T", v))
+ // Walk up the chain of Captures.
+ x := v
+ for {
+ if c, ok := x.(*Capture); ok {
+ x = c.Outer
+ } else {
+ break
+ }
}
+ // By construction, all captures are ultimately Allocs in the
+ // naive SSA form. Parameters are pre-spilled to the stack.
+ x.(*Alloc).Heap = true
}
return v // function-local var (address)
}
func (f *Function) DumpTo(w io.Writer) {
fmt.Fprintf(w, "# Name: %s\n", f.FullName())
fmt.Fprintf(w, "# Declared at %s\n", f.Prog.Files.Position(f.Pos))
- fmt.Fprintf(w, "# Type: %s\n", f.Type())
+ fmt.Fprintf(w, "# Type: %s\n", f.Signature)
if f.Enclosing != nil {
fmt.Fprintf(w, "# Parent: %s\n", f.Enclosing.Name())
Name: fmt.Sprintf("%d.%s", len(f.Blocks), name),
Func: f,
}
+ b.Succs = b.succs2[:0]
f.Blocks = append(f.Blocks, b)
return b
}
// instructions, respectively).
//
type BasicBlock struct {
- Name string // label; no semantic significance
- Func *Function // containing function
- Instrs []Instruction // instructions in order
- Preds, Succs []*BasicBlock // predecessors and successors
+ Name string // label; no semantic significance
+ Func *Function // containing function
+ Instrs []Instruction // instructions in order
+ Preds, Succs []*BasicBlock // predecessors and successors
+ succs2 [2]*BasicBlock // initial space for Succs.
}
// Pure values ----------------------------------------
// A Capture is a pointer to a lexically enclosing local variable.
//
-// The referent of a capture is a Parameter, Alloc or another Capture
-// and is always considered potentially escaping, so Captures are
-// always addresses in the heap, and have pointer types.
+// The referent of a capture is an Alloc or another Capture and is
+// always considered potentially escaping, so Captures are always
+// addresses in the heap, and have pointer types.
//
type Capture struct {
Outer Value // the Value captured from the enclosing context.
// A Parameter represents an input parameter of a function.
//
-// Parameters are addresses and thus have pointer types.
-// TODO(adonovan): this will change. We should just spill parameters
-// to ordinary Alloc-style locals if they are ever used in an
-// addressable context. Then we can lose the Heap flag.
-//
-// In the common case where Heap=false, Parameters are pointers into
-// the function's stack frame. If the case where Heap=true because a
-// parameter's address may escape from its function, Parameters are
-// pointers into a space in the heap implicitly allocated during the
-// function call. (See also Alloc, which uses the Heap flag in a
-// similar manner.)
-//
type Parameter struct {
Name_ string
- Type_ *types.Pointer
- Heap bool
+ Type_ types.Type
}
// A Literal represents a literal nil, boolean, string or numeric