[dev.ssa] cmd/internal/ssa: SSA backend compiler skeleton

First pass adding code for SSA backend.  It is standalone for now.
I've included just a few passes to make the review size manageable -
I have more passes coming.

cmd/internal/ssa is the library containing the ssa compiler proper.

cmd/internal/ssa/ssac is a driver that loads an sexpr-based IR,
converts it to SSA form, and calls the above library.  It is essentially
throwaway code - it will disappear once the Go compiler calls
cmd/internal/ssa itself.  The .goir files in ssac/ are dumps of fibonacci
programs I made from a hacked-up compiler.  They are just for testing.

Change-Id: I5ee89356ec12c87cd916681097cd3c2cd591040c
Reviewed-on: https://go-review.googlesource.com/6681
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/cmd/internal/ssa/block.go b/src/cmd/internal/ssa/block.go
new file mode 100644
index 0000000..ff1cb1b
--- /dev/null
+++ b/src/cmd/internal/ssa/block.go
@@ -0,0 +1,92 @@
+// Copyright 2015 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.
+
+package ssa
+
+import (
+	"fmt"
+	"strings"
+)
+
+// Block represents a basic block in the control flow graph of a function.
+type Block struct {
+	// A unique identifier for the block.  The system will attempt to allocate
+	// these IDs densely, but no guarantees.
+	ID ID
+
+	// The kind of block this is.
+	Kind BlockKind
+
+	// Subsequent blocks, if any.  The number and order depend on the block kind.
+	// All blocks must be distinct (to make phi values in successors unambiguous).
+	Succs []*Block
+
+	// Inverse of successors.
+	// The order is significant to Phi nodes in the block.
+	Preds []*Block
+	// TODO: predecessors is a pain to maintain.  Can we somehow order phi
+	// arguments by block id and have this field computed explicitly when needed?
+
+	// A value that determines how the block is exited.  Its value depends on the kind
+	// of the block.  For instance, a BlockIf has a boolean control value and BlockExit
+	// has a memory control value.
+	Control *Value
+
+	// The unordered set of Values contained in this block.
+	// The list must include the control value, if any. (TODO: need this last condition?)
+	Values []*Value
+
+	// The containing function
+	Func *Func
+}
+
+//     kind           control    successors
+//   ------------------------------------------
+//     Exit        return mem                []
+//    Plain               nil            [next]
+//       If   a boolean Value      [then, else]
+//     Call               mem  [nopanic, panic]  (control opcode should be OpCall or OpStaticCall)
+type BlockKind int8
+
+const (
+	BlockExit  BlockKind = iota // no successors.  There should only be 1 of these.
+	BlockPlain                  // a single successor
+	BlockIf                     // 2 successors, if control goto Succs[0] else goto Succs[1]
+	BlockCall                   // 2 successors, normal return and panic
+	BlockUnknown
+
+	// 386/amd64 variants of BlockIf that take the flags register as an arg
+	BlockEQ
+	BlockNE
+	BlockLT
+	BlockLE
+	BlockGT
+	BlockGE
+	BlockULT
+	BlockULE
+	BlockUGT
+	BlockUGE
+)
+
+//go:generate stringer -type=BlockKind
+
+// short form print
+func (b *Block) String() string {
+	return fmt.Sprintf("b%d", b.ID)
+}
+
+// long form print
+func (b *Block) LongString() string {
+	s := strings.TrimPrefix(b.Kind.String(), "Block")
+	if b.Control != nil {
+		s += fmt.Sprintf(" %s", b.Control)
+	}
+	if len(b.Succs) > 0 {
+		s += " ->"
+		for _, c := range b.Succs {
+			s += " " + c.String()
+		}
+	}
+	return s
+}
diff --git a/src/cmd/internal/ssa/blockkind_string.go b/src/cmd/internal/ssa/blockkind_string.go
new file mode 100644
index 0000000..6204f19
--- /dev/null
+++ b/src/cmd/internal/ssa/blockkind_string.go
@@ -0,0 +1,16 @@
+// generated by stringer -type=BlockKind; DO NOT EDIT
+
+package ssa
+
+import "fmt"
+
+const _BlockKind_name = "BlockExitBlockPlainBlockIfBlockCallBlockUnknownBlockEQBlockNEBlockLTBlockLEBlockGTBlockGEBlockULTBlockULEBlockUGTBlockUGE"
+
+var _BlockKind_index = [...]uint8{0, 9, 19, 26, 35, 47, 54, 61, 68, 75, 82, 89, 97, 105, 113, 121}
+
+func (i BlockKind) String() string {
+	if i < 0 || i+1 >= BlockKind(len(_BlockKind_index)) {
+		return fmt.Sprintf("BlockKind(%d)", i)
+	}
+	return _BlockKind_name[_BlockKind_index[i]:_BlockKind_index[i+1]]
+}
diff --git a/src/cmd/internal/ssa/check.go b/src/cmd/internal/ssa/check.go
new file mode 100644
index 0000000..b501cdb
--- /dev/null
+++ b/src/cmd/internal/ssa/check.go
@@ -0,0 +1,125 @@
+// Copyright 2015 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.
+
+package ssa
+
+import "log"
+
+// checkFunc checks invariants of f.
+func checkFunc(f *Func) {
+	blockMark := make([]bool, f.NumBlocks())
+	valueMark := make([]bool, f.NumValues())
+
+	for _, b := range f.Blocks {
+		if blockMark[b.ID] {
+			log.Panicf("block %s appears twice in %s!", b, f.Name)
+		}
+		blockMark[b.ID] = true
+		if b.Func != f {
+			log.Panicf("%s.Func=%s, want %s", b, b.Func.Name, f.Name)
+		}
+
+		for i, c := range b.Succs {
+			for j, d := range b.Succs {
+				if i != j && c == d {
+					log.Panicf("%s.Succs has duplicate block %s", b, c)
+				}
+			}
+		}
+		// Note: duplicate successors are hard in the following case:
+		//      if(...) goto x else goto x
+		//   x: v = phi(a, b)
+		// If the conditional is true, does v get the value of a or b?
+		// We could solve this other ways, but the easiest is just to
+		// require (by possibly adding empty control-flow blocks) that
+		// all successors are distinct.  They will need to be distinct
+		// anyway for register allocation (duplicate successors implies
+		// the existence of critical edges).
+
+		for _, p := range b.Preds {
+			var found bool
+			for _, c := range p.Succs {
+				if c == b {
+					found = true
+					break
+				}
+			}
+			if !found {
+				log.Panicf("block %s is not a succ of its pred block %s", b, p)
+			}
+		}
+
+		switch b.Kind {
+		case BlockExit:
+			if len(b.Succs) != 0 {
+				log.Panicf("exit block %s has successors", b)
+			}
+			if b.Control == nil {
+				log.Panicf("exit block %s has no control value", b)
+			}
+			if b.Control.Type != TypeMem {
+				log.Panicf("exit block %s has non-memory control value %s", b, b.Control.LongString())
+			}
+		case BlockPlain:
+			if len(b.Succs) != 1 {
+				log.Panicf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs))
+			}
+			if b.Control != nil {
+				log.Panicf("plain block %s has non-nil control %s", b, b.Control.LongString())
+			}
+		case BlockIf:
+			if len(b.Succs) != 2 {
+				log.Panicf("if block %s len(Succs)==%d, want 2", b, len(b.Succs))
+			}
+			if b.Control == nil {
+				log.Panicf("if block %s has no control value", b)
+			}
+			if b.Control.Type != TypeBool {
+				log.Panicf("if block %s has non-bool control value %s", b, b.Control.LongString())
+			}
+		case BlockCall:
+			if len(b.Succs) != 2 {
+				log.Panicf("call block %s len(Succs)==%d, want 2", b, len(b.Succs))
+			}
+			if b.Control == nil {
+				log.Panicf("call block %s has no control value", b)
+			}
+			if b.Control.Type != TypeMem {
+				log.Panicf("call block %s has non-memory control value %s", b, b.Control.LongString())
+			}
+			if b.Succs[1].Kind != BlockExit {
+				log.Panicf("exception edge from call block %s does not go to exit but %s", b, b.Succs[1])
+			}
+		}
+
+		for _, v := range b.Values {
+			if valueMark[v.ID] {
+				log.Panicf("value %s appears twice!", v.LongString())
+			}
+			valueMark[v.ID] = true
+
+			if v.Block != b {
+				log.Panicf("%s.block != %s", v, b)
+			}
+			if v.Op == OpPhi && len(v.Args) != len(b.Preds) {
+				log.Panicf("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b)
+			}
+
+			// TODO: check idom
+			// TODO: check for cycles in values
+			// TODO: check type
+		}
+	}
+
+	for _, id := range f.bid.free {
+		if blockMark[id] {
+			log.Panicf("used block b%d in free list", id)
+		}
+	}
+	for _, id := range f.vid.free {
+		if valueMark[id] {
+			log.Panicf("used value v%d in free list", id)
+		}
+	}
+}
diff --git a/src/cmd/internal/ssa/compile.go b/src/cmd/internal/ssa/compile.go
new file mode 100644
index 0000000..5e21bdf6
--- /dev/null
+++ b/src/cmd/internal/ssa/compile.go
@@ -0,0 +1,65 @@
+// Copyright 2015 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.
+
+package ssa
+
+import "fmt"
+
+// Compile is the main entry point for this package.
+// Compile modifies f so that on return:
+//   · all Values in f map to 0 or 1 assembly instructions of the target architecture
+//   · the order of f.Blocks is the order to emit the Blocks
+//   · the order of b.Values is the order to emit the Values in each Block
+//   · f has a non-nil regAlloc field
+func Compile(f *Func) {
+	// TODO: debugging - set flags to control verbosity of compiler,
+	// which phases to dump IR before/after, etc.
+	fmt.Printf("compiling %s\n", f.Name)
+
+	// hook to print function & phase if panic happens
+	phaseName := "init"
+	defer func() {
+		if phaseName != "" {
+			fmt.Printf("panic during %s while compiling %s\n", phaseName, f.Name)
+		}
+	}()
+
+	// Run all the passes
+	printFunc(f)
+	checkFunc(f)
+	for _, p := range passes {
+		phaseName = p.name
+		fmt.Printf("  pass %s begin\n", p.name)
+		p.fn(f)
+		fmt.Printf("  pass %s end\n", p.name)
+		printFunc(f)
+		checkFunc(f)
+	}
+
+	// Squash error printing defer
+	phaseName = ""
+}
+
+type pass struct {
+	name string
+	fn   func(*Func)
+}
+
+// list of passes for the compiler
+var passes = [...]pass{
+	{"phielim", phielim},
+	{"copyelim", copyelim},
+	//{"opt", opt},
+	// cse
+	{"deadcode", deadcode},
+	//{"fuse", fuse},
+	//{"lower", lower},
+	// cse
+	//{"critical", critical}, // remove critical edges
+	//{"layout", layout},     // schedule blocks
+	//{"schedule", schedule}, // schedule values
+	// regalloc
+	// stack slot alloc (+size stack frame)
+	//{"cgen", cgen},
+}
diff --git a/src/cmd/internal/ssa/copyelim.go b/src/cmd/internal/ssa/copyelim.go
new file mode 100644
index 0000000..10c2dcc
--- /dev/null
+++ b/src/cmd/internal/ssa/copyelim.go
@@ -0,0 +1,29 @@
+// Copyright 2015 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.
+
+package ssa
+
+// copyelim removes all copies from f.
+func copyelim(f *Func) {
+	for _, b := range f.Blocks {
+		for _, v := range b.Values {
+			for i, w := range v.Args {
+				x := w
+				for x.Op == OpCopy {
+					x = x.Args[0]
+				}
+				if x != w {
+					v.Args[i] = x
+				}
+			}
+		}
+		v := b.Control
+		if v != nil {
+			for v.Op == OpCopy {
+				v = v.Args[0]
+			}
+			b.Control = v
+		}
+	}
+}
diff --git a/src/cmd/internal/ssa/deadcode.go b/src/cmd/internal/ssa/deadcode.go
new file mode 100644
index 0000000..1647ea9
--- /dev/null
+++ b/src/cmd/internal/ssa/deadcode.go
@@ -0,0 +1,153 @@
+// Copyright 2015 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.
+
+package ssa
+
+import "log"
+
+// deadcode removes dead code from f.
+func deadcode(f *Func) {
+
+	// Find all reachable basic blocks.
+	reachable := make([]bool, f.NumBlocks())
+	reachable[f.Entry.ID] = true
+	p := []*Block{f.Entry} // stack-like worklist
+	for len(p) > 0 {
+		// pop a reachable block
+		b := p[len(p)-1]
+		p = p[:len(p)-1]
+
+		// constant-fold conditionals
+		// TODO: rewrite rules instead?
+		if b.Kind == BlockIf && b.Control.Op == OpConstBool {
+			cond := b.Control.Aux.(bool)
+			var c *Block
+			if cond {
+				// then branch is always taken
+				c = b.Succs[1]
+			} else {
+				// else branch is always taken
+				c = b.Succs[0]
+				b.Succs[0] = b.Succs[1]
+			}
+			b.Succs[1] = nil // aid GC
+			b.Succs = b.Succs[:1]
+			removePredecessor(b, c)
+			b.Kind = BlockPlain
+			b.Control = nil
+		}
+
+		for _, c := range b.Succs {
+			if !reachable[c.ID] {
+				reachable[c.ID] = true
+				p = append(p, c) // push
+			}
+		}
+	}
+
+	// Find all live values
+	live := make([]bool, f.NumValues()) // flag to set for each live value
+	var q []*Value                      // stack-like worklist of unscanned values
+
+	// Starting set: all control values of reachable blocks are live.
+	for _, b := range f.Blocks {
+		if !reachable[b.ID] {
+			continue
+		}
+		if v := b.Control; v != nil && !live[v.ID] {
+			live[v.ID] = true
+			q = append(q, v)
+		}
+	}
+
+	// Compute transitive closure of live values.
+	for len(q) > 0 {
+		// pop a reachable value
+		v := q[len(q)-1]
+		q = q[:len(q)-1]
+		for _, x := range v.Args {
+			if !live[x.ID] {
+				live[x.ID] = true
+				q = append(q, x) // push
+			}
+		}
+	}
+
+	// Remove dead values from blocks' value list.  Return dead
+	// value ids to the allocator.
+	for _, b := range f.Blocks {
+		i := 0
+		for _, v := range b.Values {
+			if live[v.ID] {
+				b.Values[i] = v
+				i++
+			} else {
+				f.vid.put(v.ID)
+			}
+		}
+		for j := i; j < len(b.Values); j++ {
+			b.Values[j] = nil // aid GC
+		}
+		b.Values = b.Values[:i]
+	}
+
+	// Remove unreachable blocks.  Return dead block ids to allocator.
+	i := 0
+	for _, b := range f.Blocks {
+		if reachable[b.ID] {
+			f.Blocks[i] = b
+			i++
+		} else {
+			if len(b.Values) > 0 {
+				panic("live value in unreachable block")
+			}
+			f.bid.put(b.ID)
+		}
+	}
+	// zero remainder to help gc
+	for j := i; j < len(f.Blocks); j++ {
+		f.Blocks[j] = nil
+	}
+	f.Blocks = f.Blocks[:i]
+
+	// TODO: renumber Blocks and Values densely?
+}
+
+// There was an edge b->c.  It has been removed from b's successors.
+// Fix up c to handle that fact.
+func removePredecessor(b, c *Block) {
+	n := len(c.Preds) - 1
+	if n == 0 {
+		// c is now dead - don't bother working on it
+		if c.Preds[0] != b {
+			log.Panicf("%s.Preds[0]==%s, want %s", c, c.Preds[0], b)
+		}
+		return
+	}
+
+	// find index of b in c's predecessor list
+	var i int
+	for j, p := range c.Preds {
+		if p == b {
+			i = j
+			break
+		}
+	}
+
+	c.Preds[i] = c.Preds[n]
+	c.Preds[n] = nil // aid GC
+	c.Preds = c.Preds[:n]
+	// rewrite phi ops to match the new predecessor list
+	for _, v := range c.Values {
+		if v.Op != OpPhi {
+			continue
+		}
+		v.Args[i] = v.Args[n]
+		v.Args[n] = nil // aid GC
+		v.Args = v.Args[:n]
+		if n == 1 {
+			v.Op = OpCopy
+		}
+	}
+}
diff --git a/src/cmd/internal/ssa/deadcode_test.go b/src/cmd/internal/ssa/deadcode_test.go
new file mode 100644
index 0000000..94fc359
--- /dev/null
+++ b/src/cmd/internal/ssa/deadcode_test.go
@@ -0,0 +1,112 @@
+// Copyright 2015 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.
+
+// TODO: these tests are pretty verbose.  Is there a way to simplify
+// building a small Func for testing?
+
+package ssa_test
+
+import (
+	. "cmd/internal/ssa"
+	"testing"
+)
+
+func TestDeadLoop(t *testing.T) {
+	f := new(Func)
+	entry := f.NewBlock(BlockPlain)
+	exit := f.NewBlock(BlockExit)
+	f.Entry = entry
+	addEdge(entry, exit)
+	mem := entry.NewValue(OpArg, TypeMem, ".mem")
+	exit.Control = mem
+
+	// dead loop
+	deadblock := f.NewBlock(BlockIf)
+	addEdge(deadblock, deadblock)
+	addEdge(deadblock, exit)
+
+	// dead value in dead block
+	deadval := deadblock.NewValue(OpConstBool, TypeBool, true)
+	deadblock.Control = deadval
+
+	CheckFunc(f)
+	Deadcode(f)
+	CheckFunc(f)
+
+	for _, b := range f.Blocks {
+		if b == deadblock {
+			t.Errorf("dead block not removed")
+		}
+		for _, v := range b.Values {
+			if v == deadval {
+				t.Errorf("control value of dead block not removed")
+			}
+		}
+	}
+}
+
+func TestDeadValue(t *testing.T) {
+	f := new(Func)
+	entry := f.NewBlock(BlockPlain)
+	exit := f.NewBlock(BlockExit)
+	f.Entry = entry
+	addEdge(entry, exit)
+	mem := entry.NewValue(OpArg, TypeMem, ".mem")
+	exit.Control = mem
+
+	deadval := entry.NewValue(OpConstInt, TypeInt, 37)
+
+	CheckFunc(f)
+	Deadcode(f)
+	CheckFunc(f)
+
+	for _, b := range f.Blocks {
+		for _, v := range b.Values {
+			if v == deadval {
+				t.Errorf("dead value not removed")
+			}
+		}
+	}
+}
+
+func TestNeverTaken(t *testing.T) {
+	f := new(Func)
+	entry := f.NewBlock(BlockIf)
+	exit := f.NewBlock(BlockExit)
+	then := f.NewBlock(BlockPlain)
+	else_ := f.NewBlock(BlockPlain)
+	f.Entry = entry
+	addEdge(entry, then)
+	addEdge(entry, else_)
+	addEdge(then, exit)
+	addEdge(else_, exit)
+	mem := entry.NewValue(OpArg, TypeMem, ".mem")
+	exit.Control = mem
+
+	cond := entry.NewValue(OpConstBool, TypeBool, false)
+	entry.Control = cond
+
+	CheckFunc(f)
+	Deadcode(f)
+	CheckFunc(f)
+
+	if entry.Kind != BlockPlain {
+		t.Errorf("if(false) not simplified")
+	}
+	for _, b := range f.Blocks {
+		if b == then {
+			t.Errorf("then block still present")
+		}
+		for _, v := range b.Values {
+			if v == cond {
+				t.Errorf("constant condition still present")
+			}
+		}
+	}
+}
+
+func addEdge(b, c *Block) {
+	b.Succs = append(b.Succs, c)
+	c.Preds = append(c.Preds, b)
+}
diff --git a/src/cmd/internal/ssa/export_test.go b/src/cmd/internal/ssa/export_test.go
new file mode 100644
index 0000000..ab4ab82
--- /dev/null
+++ b/src/cmd/internal/ssa/export_test.go
@@ -0,0 +1,9 @@
+// Copyright 2015 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.
+
+package ssa
+
+var CheckFunc = checkFunc
+var PrintFunc = printFunc
+var Deadcode = deadcode
diff --git a/src/cmd/internal/ssa/func.go b/src/cmd/internal/ssa/func.go
new file mode 100644
index 0000000..6868e3d
--- /dev/null
+++ b/src/cmd/internal/ssa/func.go
@@ -0,0 +1,61 @@
+// Copyright 2015 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.
+
+package ssa
+
+// A Func represents a Go func declaration (or function literal) and
+// its body.  This package compiles each Func independently.
+type Func struct {
+	Name   string   // e.g. bytes·Compare
+	Type   Type     // type signature of the function.
+	Blocks []*Block // unordered set of all basic blocks (note: not indexable by ID)
+	Entry  *Block   // the entry basic block
+	bid    idAlloc  // block ID allocator
+	vid    idAlloc  // value ID allocator
+
+	// when register allocation is done, maps value ids to locations
+	RegAlloc []Location
+}
+
+// NumBlocks returns an integer larger than the id of any Block in the Func.
+func (f *Func) NumBlocks() int {
+	return f.bid.num()
+}
+
+// NumValues returns an integer larger than the id of any Value in the Func.
+func (f *Func) NumValues() int {
+	return f.vid.num()
+}
+
+// NewBlock returns a new block of the given kind and appends it to f.Blocks.
+func (f *Func) NewBlock(kind BlockKind) *Block {
+	b := &Block{
+		ID:   f.bid.get(),
+		Kind: kind,
+		Func: f,
+	}
+	f.Blocks = append(f.Blocks, b)
+	return b
+}
+
+// NewValue returns a new value in the block with no arguments.
+func (b *Block) NewValue(op Op, t Type, aux interface{}) *Value {
+	v := &Value{
+		ID:    b.Func.vid.get(),
+		Op:    op,
+		Type:  t,
+		Aux:   aux,
+		Block: b,
+	}
+	v.Args = v.argstorage[:0]
+	b.Values = append(b.Values, v)
+	return v
+}
+
+// ConstInt returns an int constant representing its argument.
+func (f *Func) ConstInt(c int64) *Value {
+	// TODO: cache?
+	// TODO: different types?
+	return f.Entry.NewValue(OpConstInt, TypeInt, c)
+}
diff --git a/src/cmd/internal/ssa/id.go b/src/cmd/internal/ssa/id.go
new file mode 100644
index 0000000..43f23c8
--- /dev/null
+++ b/src/cmd/internal/ssa/id.go
@@ -0,0 +1,41 @@
+// Copyright 2015 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.
+
+package ssa
+
+type ID int32
+
+// idAlloc provides an allocator for unique integers.
+type idAlloc struct {
+	last ID
+	free []ID
+}
+
+// get allocates an ID and returns it.
+func (a *idAlloc) get() ID {
+	if n := len(a.free); n > 0 {
+		x := a.free[n-1]
+		a.free = a.free[:n-1]
+		return x
+	}
+	x := a.last
+	x++
+	if x == 1<<31-1 {
+		panic("too many ids for this function")
+	}
+	a.last = x
+	return x
+}
+
+// put deallocates an ID.
+func (a *idAlloc) put(x ID) {
+	a.free = append(a.free, x)
+	// TODO: IR check should make sure that the IR contains
+	// no IDs that are in the free list.
+}
+
+// num returns the maximum ID ever returned + 1.
+func (a *idAlloc) num() int {
+	return int(a.last + 1)
+}
diff --git a/src/cmd/internal/ssa/location.go b/src/cmd/internal/ssa/location.go
new file mode 100644
index 0000000..94c1b42
--- /dev/null
+++ b/src/cmd/internal/ssa/location.go
@@ -0,0 +1,42 @@
+// Copyright 2015 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.
+
+package ssa
+
+import (
+	"fmt"
+)
+
+// A place that an ssa variable can reside.
+type Location interface {
+	Name() string // name to use in assembly templates: %rax, 16(%rsp), ...
+}
+
+// A Register is a machine register, like %rax.
+type Register struct {
+	name string
+}
+
+func (r *Register) Name() string {
+	return r.name
+}
+
+// A LocalSlot is a location in the stack frame.
+type LocalSlot struct {
+	idx int64 // offset in locals area (distance down from FP == caller's SP)
+}
+
+func (s *LocalSlot) Name() string {
+	return fmt.Sprintf("loc%d", s.idx)
+}
+
+// An ArgSlot is a location in the parents' stack frame where it passed us an argument.
+type ArgSlot struct {
+	idx int64 // offset in argument area
+}
+
+// A CalleeSlot is a location in the stack frame where we pass an argument to a callee.
+type CalleeSlot struct {
+	idx int64 // offset in callee area
+}
diff --git a/src/cmd/internal/ssa/op.go b/src/cmd/internal/ssa/op.go
new file mode 100644
index 0000000..a4364b1
--- /dev/null
+++ b/src/cmd/internal/ssa/op.go
@@ -0,0 +1,345 @@
+// Copyright 2015 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.
+
+package ssa
+
+// An Op encodes the specific operation that a Value performs.
+// Opcodes' semantics can be modified by the type and aux fields of the Value.
+// For instance, OpAdd can be 32 or 64 bit, signed or unsigned, float or complex, depending on Value.Type.
+// Semantics of each op are described below.
+// Ops come in two flavors, architecture-independent and architecture-dependent.
+type Op int32
+
+// All the opcodes
+const (
+	OpUnknown Op = iota
+
+	// machine-independent opcodes
+
+	OpNop   // should never be used, appears only briefly during construction,  Has type Void.
+	OpThunk // used during ssa construction.  Like OpCopy, but the arg has not been specified yet.
+
+	// 2-input arithmetic
+	OpAdd
+	OpSub
+	OpMul
+
+	// 2-input comparisons
+	OpLess
+
+	// constants
+	OpConstNil
+	OpConstBool    // aux is type bool
+	OpConstString  // aux is type string
+	OpConstInt     // aux is type int64
+	OpConstFloat   // aux is type float64
+	OpConstComplex // aux is type complex128
+
+	OpArg    // address of a function parameter/result
+	OpGlobal // address of a global variable
+	OpFunc   // entry address of a function
+	OpCopy   // output = input
+	OpPhi    // select an input based on which predecessor we came from
+
+	OpSliceMake // args are ptr/len/cap
+	OpSlicePtr
+	OpSliceLen
+	OpSliceCap
+
+	OpStringMake // args are ptr/len
+	OpStringPtr
+	OpStringLen
+
+	OpSlice
+	OpIndex
+	OpIndexAddr
+
+	OpLoad  // args are ptr, memory
+	OpStore // args are ptr, memory, returns memory
+
+	OpCheckNil   // arg[0] != nil
+	OpCheckBound // 0 <= arg[0] < arg[1]
+
+	// function calls.  Arguments to the call have already been written to the stack.
+	// Return values appear on the stack.
+	OpCall       // args are function ptr, memory
+	OpStaticCall // aux is function, arg is memory
+
+	OpConvert
+	OpConvNop
+
+	// These ops return a pointer to a location on the stack.  Aux contains an int64
+	// indicating an offset from the base pointer.
+	OpFPAddr // offset from FP (+ == args from caller, - == locals)
+	OpSPAddr // offset from SP
+
+	// load/store from constant offsets from SP/FP
+	// The distinction between FP/SP needs to be maintained until after
+	// register allocation because we don't know the size of the frame yet.
+	OpLoadFP
+	OpLoadSP
+	OpStoreFP
+	OpStoreSP
+
+	// spill&restore ops for the register allocator.  These are
+	// semantically identical to OpCopy - they do not take/return
+	// stores like regular memory ops do.  We can get away with that because
+	// we know there is no aliasing to spill slots on the stack.
+	OpStoreReg8
+	OpLoadReg8
+
+	// machine-dependent opcodes go here
+
+	// x86
+	OpADDQ
+	OpSUBQ
+	OpADDCQ // 1 input arg, add aux which is an int64 constant
+	OpSUBCQ // 1 input arg.  output = input - aux.(int64)
+	OpNEGQ
+	OpCMPQ
+	OpCMPCQ // 1 input arg.  Compares input with aux.(int64)
+	OpADDL
+	OpInvertFlags // inverts interpretation of the flags register (< to >=, etc.)
+	OpSETL        // generate bool = "flags encode less than"
+	OpSETGE
+
+	OpLEAQ  // x+y
+	OpLEAQ2 // x+2*y
+	OpLEAQ4 // x+4*y
+	OpLEAQ8 // x+8*y
+
+	OpLoadFP8
+	OpLoadSP8
+	OpStoreFP8
+	OpStoreSP8
+
+	OpMax // sentinel
+)
+
+//go:generate stringer -type=Op
+
+type OpInfo struct {
+	flags int32
+
+	// assembly template
+	// %In: location of input n
+	// %On: location of output n
+	// %A: print aux with fmt.Print
+	asm string
+
+	// computes type for values with this opcode
+	typer func(v *Value)
+
+	// returns a reg constraint for the instruction. [0] gives a reg constraint
+	// for each input, [1] gives a reg constraint for each output. (Values have
+	// exactly one output for now)
+	reg [2][]regMask
+}
+
+type regMask uint64
+
+var regs386 = [...]string{
+	"AX",
+	"BX",
+	"CX",
+	"DX",
+	"SI",
+	"DI",
+	"SP",
+	"BP",
+	"X0",
+
+	// pseudo registers
+	"FLAGS",
+	"OVERWRITE0", // the same register as the first input
+}
+
+// TODO: match up these with regs386 above
+var gp regMask = 0xff
+var cx regMask = 0x4
+var flags regMask = 1 << 9
+var overwrite0 regMask = 1 << 10
+
+const (
+	// possible properties of opcodes
+	OpFlagCommutative int32 = 1 << iota
+
+	// architecture constants
+	Arch386
+	ArchAmd64
+	ArchArm
+)
+
+func firstArgTyper(v *Value) {
+	v.Type = v.Args[0].Type
+}
+func boolTyper(v *Value) {
+	v.Type = TypeBool
+}
+func stringTyper(v *Value) {
+	v.Type = TypeString
+}
+func flagsTyper(v *Value) {
+	v.Type = TypeFlags
+}
+func uint8Typer(v *Value) {
+	v.Type = TypeUint8
+}
+func uint64Typer(v *Value) {
+	v.Type = TypeUint64
+}
+func auxTyper(v *Value) {
+	v.Type = v.Aux.(Type)
+}
+
+// general purpose registers, 2 input, 1 output
+var gp21 = [2][]regMask{{gp, gp}, {gp}}
+var gp21_overwrite = [2][]regMask{{gp, gp}, {overwrite0}}
+
+// general purpose registers, 1 input, 1 output
+var gp11 = [2][]regMask{{gp}, {gp}}
+var gp11_overwrite = [2][]regMask{{gp}, {overwrite0}}
+
+// shift operations
+var shift = [2][]regMask{{gp, cx}, {overwrite0}}
+
+var gp2_flags = [2][]regMask{{gp, gp}, {flags}}
+var gp1_flags = [2][]regMask{{gp}, {flags}}
+var gpload = [2][]regMask{{gp, 0}, {gp}}
+var gpstore = [2][]regMask{{gp, gp, 0}, {0}}
+
+// Opcodes that represent the input Go program
+var genericTable = [...]OpInfo{
+	// the unknown op is used only during building and should not appear in a
+	// fully formed ssa representation.
+
+	OpAdd:  {flags: OpFlagCommutative, typer: firstArgTyper},
+	OpSub:  {typer: firstArgTyper},
+	OpMul:  {flags: OpFlagCommutative, typer: firstArgTyper},
+	OpLess: {typer: boolTyper},
+
+	OpConstBool:    {typer: boolTyper},   // aux is a bool
+	OpConstString:  {typer: stringTyper}, // aux is a string
+	OpConstInt:     {},                   // aux is an int64
+	OpConstFloat:   {},                   // aux is a float64
+	OpConstComplex: {},
+	OpArg:          {}, // aux is the name of the input variable  TODO:?
+	OpGlobal:       {}, // address of a global variable
+	OpFunc:         {},
+	OpCopy:         {},
+	OpPhi:          {},
+
+	OpConvNop: {}, // aux is the type to convert to
+
+	/*
+		// build and take apart slices
+		{name: "slicemake"}, // (ptr,len,cap) -> slice
+		{name: "sliceptr"},  // pointer part of slice
+		{name: "slicelen"},  // length part of slice
+		{name: "slicecap"},  // capacity part of slice
+
+		// build and take apart strings
+		{name: "stringmake"}, // (ptr,len) -> string
+		{name: "stringptr"},  // pointer part of string
+		{name: "stringlen"},  // length part of string
+
+		// operations on arrays/slices/strings
+		{name: "slice"},     // (s, i, j) -> s[i:j]
+		{name: "index"},     // (mem, ptr, idx) -> val
+		{name: "indexaddr"}, // (ptr, idx) -> ptr
+
+		// loads & stores
+		{name: "load"},  // (mem, check, ptr) -> val
+		{name: "store"}, // (mem, check, ptr, val) -> mem
+
+		// checks
+		{name: "checknil"},   // (mem, ptr) -> check
+		{name: "checkbound"}, // (mem, idx, len) -> check
+
+		// functions
+		{name: "call"},
+
+		// builtins
+		{name: "len"},
+		{name: "convert"},
+
+		// tuples
+		{name: "tuple"},         // build a tuple out of its arguments
+		{name: "extract"},       // aux is an int64.  Extract that index out of a tuple
+		{name: "extractsuffix"}, // aux is an int64.  Slice a tuple with [aux:]
+
+	*/
+}
+
+// Opcodes that appear in an output amd64 program
+var amd64Table = [...]OpInfo{
+	OpADDQ:  {flags: OpFlagCommutative, asm: "ADDQ\t%I0,%I1,%O0", reg: gp21, typer: firstArgTyper}, // TODO: overwrite
+	OpADDCQ: {asm: "ADDQ\t$%A,%I0,%O0", reg: gp11_overwrite, typer: firstArgTyper},                 // aux = int64 constant to add
+	OpSUBQ:  {asm: "SUBQ\t%I0,%I1,%O0", reg: gp21, typer: firstArgTyper},
+	OpSUBCQ: {asm: "SUBQ\t$%A,%I0,%O0", reg: gp11_overwrite, typer: firstArgTyper},
+
+	OpCMPQ:  {asm: "CMPQ\t%I0,%I1", reg: gp2_flags, typer: flagsTyper}, // compute arg[0]-arg[1] and produce flags
+	OpCMPCQ: {asm: "CMPQ\t$%A,%I0", reg: gp1_flags},
+
+	OpLEAQ:  {flags: OpFlagCommutative, asm: "LEAQ\t%A(%I0)(%I1*1),%O0", reg: gp21}, // aux = int64 constant to add
+	OpLEAQ2: {asm: "LEAQ\t%A(%I0)(%I1*2),%O0"},
+	OpLEAQ4: {asm: "LEAQ\t%A(%I0)(%I1*4),%O0"},
+	OpLEAQ8: {asm: "LEAQ\t%A(%I0)(%I1*8),%O0"},
+
+	//OpLoad8:  {asm: "MOVQ\t%A(%I0),%O0", reg: gpload},
+	//OpStore8: {asm: "MOVQ\t%I1,%A(%I0)", reg: gpstore},
+
+	OpStaticCall: {asm: "CALL\t%A(SB)"},
+
+	OpCopy: {asm: "MOVQ\t%I0,%O0", reg: gp11},
+
+	// convert from flags back to boolean
+	OpSETL: {typer: boolTyper},
+
+	// ops for load/store to stack
+	OpLoadFP8:  {asm: "MOVQ\t%A(FP),%O0"},
+	OpLoadSP8:  {asm: "MOVQ\t%A(SP),%O0"},
+	OpStoreFP8: {asm: "MOVQ\t%I0,%A(FP)"},
+	OpStoreSP8: {asm: "MOVQ\t%I0,%A(SP)"},
+
+	// ops for spilling of registers
+	// unlike regular loads & stores, these take no memory argument.
+	// They are just like OpCopy but we use them during register allocation.
+	// TODO: different widths, float
+	OpLoadReg8:  {asm: "MOVQ\t%I0,%O0", reg: gp11},
+	OpStoreReg8: {asm: "MOVQ\t%I0,%O0", reg: gp11},
+}
+
+// A Table is a list of opcodes with a common set of flags.
+type Table struct {
+	t     []OpInfo
+	flags int32
+}
+
+var tables = []Table{
+	{genericTable[:], 0},
+	{amd64Table[:], ArchAmd64}, // TODO: pick this dynamically
+}
+
+// table of opcodes, indexed by opcode ID
+var opcodeTable [OpMax]OpInfo
+
+// map from opcode names to opcode IDs
+var nameToOp map[string]Op
+
+func init() {
+	// build full opcode table
+	// Note that the arch-specific table overwrites the generic table
+	for _, t := range tables {
+		for op, entry := range t.t {
+			entry.flags |= t.flags
+			opcodeTable[op] = entry
+		}
+	}
+	// build name to opcode mapping
+	nameToOp = make(map[string]Op)
+	for op := range opcodeTable {
+		nameToOp[Op(op).String()] = Op(op)
+	}
+}
diff --git a/src/cmd/internal/ssa/op_string.go b/src/cmd/internal/ssa/op_string.go
new file mode 100644
index 0000000..40051eb
--- /dev/null
+++ b/src/cmd/internal/ssa/op_string.go
@@ -0,0 +1,16 @@
+// generated by stringer -type=Op; DO NOT EDIT
+
+package ssa
+
+import "fmt"
+
+const _Op_name = "OpUnknownOpNopOpThunkOpAddOpSubOpMulOpLessOpConstNilOpConstBoolOpConstStringOpConstIntOpConstFloatOpConstComplexOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpSliceOpIndexOpIndexAddrOpLoadOpStoreOpCheckNilOpCheckBoundOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpLoadFPOpLoadSPOpStoreFPOpStoreSPOpStoreReg8OpLoadReg8OpADDQOpSUBQOpADDCQOpSUBCQOpNEGQOpCMPQOpCMPCQOpADDLOpInvertFlagsOpSETLOpSETGEOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpLoadFP8OpLoadSP8OpStoreFP8OpStoreSP8OpMax"
+
+var _Op_index = [...]uint16{0, 9, 14, 21, 26, 31, 36, 42, 52, 63, 76, 86, 98, 112, 117, 125, 131, 137, 142, 153, 163, 173, 183, 195, 206, 217, 224, 231, 242, 248, 255, 265, 277, 283, 295, 304, 313, 321, 329, 337, 345, 354, 363, 374, 384, 390, 396, 403, 410, 416, 422, 429, 435, 448, 454, 461, 467, 474, 481, 488, 497, 506, 516, 526, 531}
+
+func (i Op) String() string {
+	if i < 0 || i+1 >= Op(len(_Op_index)) {
+		return fmt.Sprintf("Op(%d)", i)
+	}
+	return _Op_name[_Op_index[i]:_Op_index[i+1]]
+}
diff --git a/src/cmd/internal/ssa/phielim.go b/src/cmd/internal/ssa/phielim.go
new file mode 100644
index 0000000..19c0d07
--- /dev/null
+++ b/src/cmd/internal/ssa/phielim.go
@@ -0,0 +1,44 @@
+// Copyright 2015 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.
+
+package ssa
+
+// phielim eliminates redundant phi values from f.
+// A phi is redundant if its arguments are all equal.  For
+// purposes of counting, ignore the phi itself.  Both of
+// these phis are redundant:
+//   v = phi(x,x,x)
+//   v = phi(x,v,x,v)
+func phielim(f *Func) {
+	args := newSparseSet(f.NumValues())
+	for _, b := range f.Blocks {
+		for _, v := range b.Values {
+			if v.Op != OpPhi {
+				continue
+			}
+			args.clear()
+			for _, x := range v.Args {
+				for x.Op == OpCopy {
+					x = x.Args[0]
+				}
+				args.add(x.ID)
+			}
+			switch {
+			case args.size() == 1:
+				v.Op = OpCopy
+				v.SetArgs1(v.Args[0])
+			case args.size() == 2 && args.contains(v.ID):
+				var w *Value
+				for _, x := range v.Args {
+					if x.ID != v.ID {
+						w = x
+						break
+					}
+				}
+				v.Op = OpCopy
+				v.SetArgs1(w)
+			}
+		}
+	}
+}
diff --git a/src/cmd/internal/ssa/print.go b/src/cmd/internal/ssa/print.go
new file mode 100644
index 0000000..eeea30d
--- /dev/null
+++ b/src/cmd/internal/ssa/print.go
@@ -0,0 +1,63 @@
+// Copyright 2015 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.
+
+package ssa
+
+import "fmt"
+
+func printFunc(f *Func) {
+	fmt.Print(f.Name)
+	fmt.Print(" ")
+	fmt.Println(f.Type)
+	printed := make([]bool, f.NumValues())
+	for _, b := range f.Blocks {
+		fmt.Printf("  b%d:\n", b.ID)
+		n := 0
+
+		// print phis first since all value cycles contain a phi
+		for _, v := range b.Values {
+			if v.Op != OpPhi {
+				continue
+			}
+			fmt.Print("    ")
+			fmt.Println(v.LongString())
+			printed[v.ID] = true
+			n++
+		}
+
+		// print rest of values in dependency order
+		for n < len(b.Values) {
+			m := n
+		outer:
+			for _, v := range b.Values {
+				if printed[v.ID] {
+					continue
+				}
+				for _, w := range v.Args {
+					if w.Block == b && !printed[w.ID] {
+						continue outer
+					}
+				}
+				fmt.Print("    ")
+				fmt.Println(v.LongString())
+				printed[v.ID] = true
+				n++
+			}
+			if m == n {
+				fmt.Println("dependency cycle!")
+				for _, v := range b.Values {
+					if printed[v.ID] {
+						continue
+					}
+					fmt.Print("    ")
+					fmt.Println(v.LongString())
+					printed[v.ID] = true
+					n++
+				}
+			}
+		}
+
+		fmt.Println("    " + b.LongString())
+	}
+}
diff --git a/src/cmd/internal/ssa/sparseset.go b/src/cmd/internal/ssa/sparseset.go
new file mode 100644
index 0000000..e1f9a9a
--- /dev/null
+++ b/src/cmd/internal/ssa/sparseset.go
@@ -0,0 +1,60 @@
+// Copyright 2015 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.
+
+package ssa
+
+// from http://research.swtch.com/sparse
+// in turn, from Briggs and Torczon
+
+type sparseSet struct {
+	dense  []ID
+	sparse []int
+}
+
+// newSparseSet returns a sparseSet that can represent
+// integers between 0 and n-1
+func newSparseSet(n int) *sparseSet {
+	return &sparseSet{nil, make([]int, n)}
+}
+
+func (s *sparseSet) size() int {
+	return len(s.dense)
+}
+
+func (s *sparseSet) contains(x ID) bool {
+	i := s.sparse[x]
+	return i < len(s.dense) && s.dense[i] == x
+}
+
+func (s *sparseSet) add(x ID) {
+	i := len(s.dense)
+	s.dense = append(s.dense, x)
+	s.sparse[x] = i
+}
+
+func (s *sparseSet) remove(x ID) {
+	i := s.sparse[x]
+	if i < len(s.dense) && s.dense[i] == x {
+		y := s.dense[len(s.dense)-1]
+		s.dense[i] = y
+		s.sparse[y] = i
+		s.dense = s.dense[:len(s.dense)-1]
+	}
+}
+
+// pop removes an arbitrary element from the set.
+// The set must be nonempty.
+func (s *sparseSet) pop() ID {
+	x := s.dense[len(s.dense)-1]
+	s.dense = s.dense[:len(s.dense)-1]
+	return x
+}
+
+func (s *sparseSet) clear() {
+	s.dense = s.dense[:0]
+}
+
+func (s *sparseSet) contents() []ID {
+	return s.dense
+}
diff --git a/src/cmd/internal/ssa/ssac/.gitignore b/src/cmd/internal/ssa/ssac/.gitignore
new file mode 100644
index 0000000..ab17b9d
--- /dev/null
+++ b/src/cmd/internal/ssa/ssac/.gitignore
@@ -0,0 +1 @@
+ssac
diff --git a/src/cmd/internal/ssa/ssac/fib.goir b/src/cmd/internal/ssa/ssac/fib.goir
new file mode 100644
index 0000000..b572cda
--- /dev/null
+++ b/src/cmd/internal/ssa/ssac/fib.goir
@@ -0,0 +1,46 @@
+    (TYPE T127bd68 int)
+    (TYPE T127bd68 int)
+    (TYPE T127bd68 int)
+    (TYPE T127bd68 int)
+    (TYPE T7faedc523360 (FUNC (int) (int)))
+    (TYPE T127bd68 int)
+    (TYPE T127bd68 int)
+    (TYPE T7faedc523360 (FUNC (int) (int)))
+    (TYPE T127bd68 int)
+    (TYPE T127bd68 int)
+    (TYPE T127bd68 int)
+    (TYPE T127bd68 int)
+    (TYPE T127bd68 int)
+    (TYPE T127bd68 int)
+    (DCL n T127bd68)
+    (DCL ~r1 T127bd68)
+    (DCL n T127bd68)
+    (DCL autotmp_0000 T127bd68)
+    (DCL fib T7faedc523360)
+    (DCL n T127bd68)
+    (DCL autotmp_0001 T127bd68)
+    (DCL fib T7faedc523360)
+    (DCL n T127bd68)
+    (DCL ~r1 T127bd68)
+    (DCL autotmp_0000 T127bd68)
+    (DCL autotmp_0001 T127bd68)
+    (DCL autotmp_0001 T127bd68)
+    (DCL autotmp_0000 T127bd68)
+    (IF (LT n (CINT 2)) .then0 .else0)
+    (LABEL .then0)
+    (AS ~r1 n)
+    (AS (SP T127bd68 8) ~r1)
+    (RETURN)
+    (GOTO .end0)
+    (LABEL .else0)
+    (GOTO .end0)
+    (LABEL .end0)
+    (AS (SP T127bd68 0) (SUB n (CINT 1)))
+    (CALL fib)
+    (AS autotmp_0000 (LOAD (SP T127bd68 8)))
+    (AS (SP T127bd68 0) (SUB n (CINT 2)))
+    (CALL fib)
+    (AS autotmp_0001 (LOAD (SP T127bd68 8)))
+    (AS ~r1 (ADD autotmp_0000 autotmp_0001))
+    (AS (SP T127bd68 8) ~r1)
+    (RETURN)
diff --git a/src/cmd/internal/ssa/ssac/fibiter.goir b/src/cmd/internal/ssa/ssac/fibiter.goir
new file mode 100644
index 0000000..43c7a3d
--- /dev/null
+++ b/src/cmd/internal/ssa/ssac/fibiter.goir
@@ -0,0 +1,62 @@
+    (NAME runtime·fibiter)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (TYPE Tf5dd68 int)
+    (DCL a Tf5dd68)
+    (DCL a Tf5dd68)
+    (DCL b Tf5dd68)
+    (DCL b Tf5dd68)
+    (DCL i Tf5dd68)
+    (DCL i Tf5dd68)
+    (DCL i Tf5dd68)
+    (DCL n Tf5dd68)
+    (DCL autotmp_0002 Tf5dd68)
+    (DCL i Tf5dd68)
+    (DCL i Tf5dd68)
+    (DCL autotmp_0002 Tf5dd68)
+    (DCL autotmp_0002 Tf5dd68)
+    (DCL autotmp_0003 Tf5dd68)
+    (DCL a Tf5dd68)
+    (DCL b Tf5dd68)
+    (DCL a Tf5dd68)
+    (DCL b Tf5dd68)
+    (DCL b Tf5dd68)
+    (DCL autotmp_0003 Tf5dd68)
+    (DCL ~r1 Tf5dd68)
+    (DCL a Tf5dd68)
+    (AS n (LOAD (SP Tf5dd68 0)))
+    (AS a (CINT 0))
+    (AS b (CINT 1))
+    (AS i (CINT 0))
+    (GOTO .top0)
+    (LABEL .top0)
+    (IF (LT i n) .body0 .end0)
+    (LABEL .body0)
+    (AS autotmp_0003 (ADD a b))
+    (AS a b)
+    (AS b autotmp_0003)
+    (AS autotmp_0002 i)
+    (AS i (ADD autotmp_0002 (CINT 1)))
+    (GOTO .top0)
+    (LABEL .end0)
+    (AS (SP Tf5dd68 8) a)
+    (RETURN)
diff --git a/src/cmd/internal/ssa/ssac/main.go b/src/cmd/internal/ssa/ssac/main.go
new file mode 100644
index 0000000..4975b50
--- /dev/null
+++ b/src/cmd/internal/ssa/ssac/main.go
@@ -0,0 +1,436 @@
+// Copyright 2015 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.
+
+package main
+
+// Stub package for testing ssa compiler backend.  Will eventually
+// be deleted when ssa is called directly from the main compiler.
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"io"
+	"os"
+	"strconv"
+	"strings"
+
+	"cmd/internal/ssa/types"
+
+	"cmd/internal/ssa"
+)
+
+// testing harness which runs the compiler using an IR read from a file
+func main() {
+	flag.Parse()
+	file := flag.Arg(0)
+	r, err := os.Open(file)
+	if err != nil {
+		panic(err)
+	}
+	f := buildFunc(readFunc(r))
+	ssa.Compile(f)
+	// TODO: output f
+}
+
+// readFunc reads the intermediate representation generated by the
+// compiler frontend and returns it as a list of sexpressions.
+func readFunc(r io.Reader) []sexpr {
+	var lines []sexpr
+	s := bufio.NewScanner(r)
+	for s.Scan() {
+		line := s.Text()
+		e := parseSexpr(strings.Trim(line, " "))
+
+		if !e.compound {
+			panic("bad stmt: " + line)
+		}
+		if e.parts[0].compound {
+			panic("bad op: " + line)
+		}
+		lines = append(lines, e)
+	}
+	return lines
+}
+
+// buildFunc converts from the 6g IR dump format to the internal
+// form.  Builds SSA and all that.
+func buildFunc(lines []sexpr) *ssa.Func {
+	f := new(ssa.Func)
+
+	// We construct SSA using an algorithm similar to
+	// Brau, Buchwald, Hack, Leißa, Mallon, and Zwinkau
+	// http://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
+
+	// allocate starting block
+	f.Entry = f.NewBlock(ssa.BlockPlain)
+	// TODO: all args.  Make a struct containing args/returnvals, declare
+	// an FP which contains a pointer to that struct.
+
+	var exit *ssa.Block // all returns (if any) branch to here  TODO: defers & panics?
+
+	// add a block for each label
+	// Also a few other preprocessing steps, all in one pass.
+	labels := map[string]*ssa.Block{}
+	types := map[string]ssa.Type{}
+	callFallthrough := map[int]*ssa.Block{}
+	for i, e := range lines {
+		switch e.parts[0].name {
+		case "LABEL":
+			labels[e.parts[1].name] = f.NewBlock(ssa.BlockPlain)
+		case "NAME":
+			f.Name = e.parts[1].name
+		case "RETURN":
+			if exit == nil {
+				exit = f.NewBlock(ssa.BlockExit)
+			}
+		case "TYPE":
+			types[e.parts[1].name] = parseSexprType(e.parts[2])
+		case "CALL":
+			// allocate a new block for fallthrough
+			callFallthrough[i] = f.NewBlock(ssa.BlockPlain)
+			if exit == nil {
+				exit = f.NewBlock(ssa.BlockExit)
+			}
+		}
+	}
+
+	// map from block id to sexprs in that block
+	blocklines := make([][]sexpr, f.NumBlocks())
+
+	// Add sexprs to the correct block.  Add edges between blocks.
+	b := f.Entry
+	var i int
+	for j, e := range lines {
+		if b == nil && e.parts[0].name != "LABEL" {
+			// dead code (e.g. return in "if" branch makes the "goto end" statement dead)
+			continue
+		}
+		switch e.parts[0].name {
+		case "IF":
+			if b.Kind != ssa.BlockPlain {
+				panic("bad b state")
+			}
+			b.Kind = ssa.BlockIf
+			edge(b, labels[e.parts[2].name])
+			edge(b, labels[e.parts[3].name])
+			blocklines[b.ID] = lines[i : j+1]
+			b = nil
+		case "GOTO":
+			edge(b, labels[e.parts[1].name])
+			blocklines[b.ID] = lines[i:j]
+			b = nil
+		case "LABEL":
+			b = labels[e.parts[1].name]
+			i = j + 1
+		case "RETURN":
+			if b.Kind != ssa.BlockPlain {
+				panic("bad b state")
+			}
+			edge(b, exit)
+			blocklines[b.ID] = lines[i:j]
+			b = nil
+		case "CALL":
+			if b.Kind != ssa.BlockPlain {
+				panic("bad b state")
+			}
+			b.Kind = ssa.BlockCall
+			c := callFallthrough[j]
+			edge(b, c)
+			edge(b, exit)
+			blocklines[b.ID] = lines[i : j+1]
+			b = c
+			i = j + 1
+		}
+		// note that we don't keep goto/label/return sexprs
+	}
+	if b != nil {
+		panic("control flow falls off end of function")
+	}
+
+	// Read types for each variable
+	// Number the variables densely
+	varids := map[string]int{} // map from variable name to id
+	var varnames []string      // map from id to variable name
+	var vartypes []ssa.Type    // map from variable id to type
+	for _, e := range lines {
+		if e.parts[0].name != "DCL" {
+			continue
+		}
+		name := e.parts[1].name
+		if _, ok := varids[name]; ok {
+			continue
+		}
+		id := len(varids)
+		if id == 1<<31-1 {
+			panic("too many variables")
+		}
+		fmt.Printf("var %d = %s\n", id, name)
+		varids[name] = id
+		varnames = append(varnames, name)
+		vartypes = append(vartypes, types[e.parts[2].name])
+	}
+	memID := len(varids)
+	fmt.Printf("var %d = .mem\n", memID)
+	varids[".mem"] = memID // TODO: need .mem here?
+	varnames = append(varnames, ".mem")
+	vartypes = append(vartypes, ssa.TypeMem)
+
+	// map from variable ID to current Value of that variable
+	curBlock := NewSparseMap(len(varids))
+
+	var state ssaFuncState
+	state.types = types
+	state.varids = varids
+	state.varnames = varnames
+	state.vartypes = vartypes
+	state.curBlock = curBlock
+	state.done = make([]bool, f.NumBlocks())
+	state.defs = map[blockvar]*ssa.Value{}
+	state.memID = memID
+
+	// Convert each block to ssa
+	// TODO: order blocks for maximum happiness - we want to process
+	// all the predecessors of a block before processing the block itself,
+	// if at all possible.
+	for _, b := range f.Blocks {
+		fmt.Printf("processing block %d\n", b.ID)
+		curBlock.Clear()
+		for _, e := range blocklines[b.ID] {
+			switch e.parts[0].name {
+			case "AS":
+				if e.parts[1].compound {
+					// store expression
+					lhs := genExpr(&state, b, e.parts[1])
+					rhs := genExpr(&state, b, e.parts[2])
+					mem := genVar(&state, b, memID)
+					v := b.NewValue(ssa.OpStore, ssa.TypeMem, nil)
+					v.AddArg(lhs)
+					v.AddArg(rhs)
+					v.AddArg(mem)
+					curBlock.Put(memID, v)
+				} else {
+					// variable assignment
+					v := genExpr(&state, b, e.parts[2])
+					curBlock.Put(varids[e.parts[1].name], v)
+				}
+			case "DCL":
+				// nothing to do
+			case "IF":
+				b.Control = genExpr(&state, b, e.parts[1])
+			case "CALL":
+				// only direct call for now - indirect call takes addr value as well
+				v := b.NewValue(ssa.OpStaticCall, ssa.TypeMem, e.parts[1].name)
+				v.AddArg(genVar(&state, b, memID))
+				curBlock.Put(memID, v)
+				b.Control = v
+			}
+		}
+		// link up thunks to their actual values
+		for _, v := range b.Values {
+			if v.Op != ssa.OpThunk {
+				continue
+			}
+			varid := v.Aux.(int)
+			w := genVar(&state, b, varid)
+			v.Op = ssa.OpCopy
+			v.Aux = nil
+			v.AddArg(w)
+		}
+
+		// record final values at the end of the block
+		for _, e := range curBlock.Contents() {
+			state.defs[blockvar{b.ID, e.Key}] = e.Val
+			// TODO: somehow avoid storing dead values to this map.
+		}
+		curBlock.Clear()
+		state.done[b.ID] = true
+	}
+
+	// the final store value is returned
+	if exit != nil {
+		exit.Control = genVar(&state, exit, memID)
+	}
+
+	return f
+}
+
+func edge(a, b *ssa.Block) {
+	a.Succs = append(a.Succs, b)
+	b.Preds = append(b.Preds, a)
+}
+
+func genVar(state *ssaFuncState, b *ssa.Block, id int) *ssa.Value {
+	// look up variable
+	v := state.curBlock.Get(id)
+	if v != nil {
+		// variable was defined previously in this block
+		// (or we memoized the result)
+		return v
+	}
+
+	// Variable comes in from outside of basic block.
+	v = lookupVarIncoming(state, b, id)
+
+	// memoize result so future callers will not look it up again
+	state.curBlock.Put(id, v)
+	return v
+}
+
+func genExpr(state *ssaFuncState, b *ssa.Block, e sexpr) *ssa.Value {
+	if !e.compound {
+		return genVar(state, b, state.varids[e.name])
+	}
+	switch e.parts[0].name {
+	case "ADD":
+		x := genExpr(state, b, e.parts[1])
+		y := genExpr(state, b, e.parts[2])
+		v := b.NewValue(ssa.OpAdd, x.Type, nil)
+		v.AddArg(x)
+		v.AddArg(y)
+		return v
+	case "SUB":
+		x := genExpr(state, b, e.parts[1])
+		y := genExpr(state, b, e.parts[2])
+		v := b.NewValue(ssa.OpSub, x.Type, nil)
+		v.AddArg(x)
+		v.AddArg(y)
+		return v
+	case "CINT":
+		c, err := strconv.ParseInt(e.parts[1].name, 10, 64)
+		if err != nil {
+			panic("bad cint value")
+		}
+		return b.Func.ConstInt(c)
+	case "LT":
+		x := genExpr(state, b, e.parts[1])
+		y := genExpr(state, b, e.parts[2])
+		v := b.NewValue(ssa.OpLess, ssa.TypeBool, nil)
+		v.AddArg(x)
+		v.AddArg(y)
+		return v
+	case "FP":
+		typ := state.types[e.parts[1].name]
+		offset, err := strconv.ParseInt(e.parts[2].name, 10, 64)
+		if err != nil {
+			panic(err)
+		}
+		v := b.NewValue(ssa.OpFPAddr, types.NewPointer(typ), offset)
+		return v
+	case "SP":
+		typ := state.types[e.parts[1].name]
+		offset, err := strconv.ParseInt(e.parts[2].name, 10, 64)
+		if err != nil {
+			panic(err)
+		}
+		v := b.NewValue(ssa.OpSPAddr, types.NewPointer(typ), offset)
+		return v
+	case "LOAD":
+		p := genExpr(state, b, e.parts[1])
+		v := b.NewValue(ssa.OpLoad, p.Type.(*types.Pointer).Elem(), nil)
+		v.AddArg(p)
+		v.AddArg(genVar(state, b, state.memID))
+		return v
+	default:
+		fmt.Println(e.parts[0].name)
+		panic("unknown op")
+	}
+}
+
+// map key combining block id and variable id
+type blockvar struct {
+	bid   ssa.ID
+	varid int
+}
+
+type ssaFuncState struct {
+	types    map[string]ssa.Type
+	varnames []string
+	varids   map[string]int
+	vartypes []ssa.Type
+	curBlock *SparseMap              // value of each variable in block we're working on
+	defs     map[blockvar]*ssa.Value // values for variables at the end of blocks
+	done     []bool
+	memID    int
+}
+
+// Find the value of the variable with the given id leaving block b.
+func lookupVarOutgoing(state *ssaFuncState, b *ssa.Block, id int) *ssa.Value {
+	fmt.Printf("lookupOutgoing var=%d block=%d\n", id, b.ID)
+	v := state.defs[blockvar{b.ID, id}]
+	if v != nil {
+		return v
+	}
+	if state.done[b.ID] {
+		// The variable was not defined in this block, and we haven't
+		// memoized the answer yet.  Look it up recursively.  This might
+		// cause infinite recursion, so add a copy first.
+		v = b.NewValue(ssa.OpCopy, state.vartypes[id], nil)
+		state.defs[blockvar{b.ID, id}] = v
+		v.AddArg(lookupVarIncoming(state, b, id))
+		return v
+	}
+	// We don't know about defined variables in this block (yet).
+	// Make a thunk for this variable.
+	fmt.Printf("making thunk for var=%d in block=%d\n", id, b.ID)
+	v = b.NewValue(ssa.OpThunk, state.vartypes[id], id)
+
+	// memoize result
+	state.defs[blockvar{b.ID, id}] = v
+	return v
+}
+
+// Find the Value of the variable coming into block b.
+func lookupVarIncoming(state *ssaFuncState, b *ssa.Block, id int) *ssa.Value {
+	fmt.Printf("lookupIncoming var=%d block=%d\n", id, b.ID)
+	var v *ssa.Value
+	switch len(b.Preds) {
+	case 0:
+		// TODO: handle function args some other way (assignments in starting block?)
+		// TODO: error if variable isn't a function arg (including mem input)
+		v = b.NewValue(ssa.OpArg, state.vartypes[id], state.varnames[id])
+	case 1:
+		v = lookupVarOutgoing(state, b.Preds[0], id)
+	default:
+		v = b.NewValue(ssa.OpCopy, state.vartypes[id], nil)
+
+		args := make([]*ssa.Value, len(b.Preds))
+		for i, p := range b.Preds {
+			args[i] = lookupVarOutgoing(state, p, id)
+		}
+
+		// if <=1 value that isn't this variable's thunk, don't make phi
+		v.Op = ssa.OpPhi
+		v.AddArgs(args...) // note: order corresponding to b.Pred
+	}
+	return v
+}
+
+func parseSexprType(e sexpr) ssa.Type {
+	if !e.compound {
+		switch e.name {
+		case "int":
+			return ssa.TypeInt
+		default:
+			fmt.Println(e.name)
+			panic("unknown type")
+		}
+	}
+	if e.parts[0].name == "FUNC" {
+		// TODO: receiver?  Already folded into args?  Variadic?
+		var args, rets []*types.Var
+		for _, s := range e.parts[1].parts {
+			t := parseSexprType(s)
+			args = append(args, types.NewParam(0, nil, "noname", t))
+		}
+		for _, s := range e.parts[2].parts {
+			t := parseSexprType(s)
+			rets = append(rets, types.NewParam(0, nil, "noname", t))
+		}
+		sig := types.NewSignature(nil, nil, types.NewTuple(args...), types.NewTuple(rets...), false)
+		return ssa.Type(sig)
+	}
+	// TODO: array/struct/...
+	panic("compound type")
+}
diff --git a/src/cmd/internal/ssa/ssac/sexpr.go b/src/cmd/internal/ssa/ssac/sexpr.go
new file mode 100644
index 0000000..77e8923
--- /dev/null
+++ b/src/cmd/internal/ssa/ssac/sexpr.go
@@ -0,0 +1,82 @@
+// Copyright 2015 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.
+
+package main
+
+import "strings"
+
+// an sexpr is an s-expression.  It is either a token or a
+// parenthesized list of s-expressions.
+//
+// Used just for initial development.  Should we keep it for testing, or
+// ditch it once we've plugged into the main compiler output?
+
+type sexpr struct {
+	compound bool
+	name     string  // !compound
+	parts    []sexpr // compound
+}
+
+func (s *sexpr) String() string {
+	if !s.compound {
+		return s.name
+	}
+	x := "("
+	for i, p := range s.parts {
+		if i != 0 {
+			x += " "
+		}
+		x += p.String()
+	}
+	return x + ")"
+}
+
+func parseSexpr(s string) sexpr {
+	var e string
+	e, s = grabOne(s)
+	if len(e) > 0 && e[0] == '(' {
+		e = e[1 : len(e)-1]
+		var parts []sexpr
+		for e != "" {
+			var p string
+			p, e = grabOne(e)
+			parts = append(parts, parseSexpr(p))
+		}
+		return sexpr{true, "", parts}
+	}
+	return sexpr{false, e, nil}
+}
+
+// grabOne peels off first token or parenthesized string from s.
+// returns first thing and the remainder of s.
+func grabOne(s string) (string, string) {
+	for len(s) > 0 && s[0] == ' ' {
+		s = s[1:]
+	}
+	if len(s) == 0 || s[0] != '(' {
+		i := strings.Index(s, " ")
+		if i < 0 {
+			return s, ""
+		}
+		return s[:i], s[i:]
+	}
+	d := 0
+	i := 0
+	for {
+		if len(s) == i {
+			panic("unterminated s-expression: " + s)
+		}
+		if s[i] == '(' {
+			d++
+		}
+		if s[i] == ')' {
+			d--
+			if d == 0 {
+				i++
+				return s[:i], s[i:]
+			}
+		}
+		i++
+	}
+}
diff --git a/src/cmd/internal/ssa/ssac/sparsemap.go b/src/cmd/internal/ssa/ssac/sparsemap.go
new file mode 100644
index 0000000..b7a0fb0
--- /dev/null
+++ b/src/cmd/internal/ssa/ssac/sparsemap.go
@@ -0,0 +1,69 @@
+// Copyright 2015 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.
+
+package main
+
+// Maintains a map[int]*ssa.Value, but cheaper.
+
+// from http://research.swtch.com/sparse
+// in turn, from Briggs and Torczon
+
+import (
+	"cmd/internal/ssa"
+)
+
+type SparseMap struct {
+	dense  []SparseMapEntry
+	sparse []int
+}
+type SparseMapEntry struct {
+	Key int
+	Val *ssa.Value
+}
+
+// NewSparseMap returns a SparseMap that can have
+// integers between 0 and n-1 as keys.
+func NewSparseMap(n int) *SparseMap {
+	return &SparseMap{nil, make([]int, n)}
+}
+
+func (s *SparseMap) Get(x int) *ssa.Value {
+	i := s.sparse[x]
+	if i < len(s.dense) && s.dense[i].Key == x {
+		return s.dense[i].Val
+	}
+	return nil
+}
+
+func (s *SparseMap) Put(x int, v *ssa.Value) {
+	i := s.sparse[x]
+	if i < len(s.dense) && s.dense[i].Key == x {
+		s.dense[i].Val = v
+		return
+	}
+	i = len(s.dense)
+	s.dense = append(s.dense, SparseMapEntry{x, v})
+	s.sparse[x] = i
+}
+
+func (s *SparseMap) Remove(x int) {
+	i := s.sparse[x]
+	if i < len(s.dense) && s.dense[i].Key == x {
+		y := s.dense[len(s.dense)-1]
+		s.dense[i] = y
+		s.sparse[y.Key] = i
+		s.dense = s.dense[:len(s.dense)-1]
+	}
+}
+
+func (s *SparseMap) Clear() {
+	s.dense = s.dense[:0]
+}
+
+// Contents returns a slice of key/value pairs.
+// Caller must not modify any returned entries.
+// The return value is invalid after the SparseMap is modified in any way.
+func (s *SparseMap) Contents() []SparseMapEntry {
+	return s.dense
+}
diff --git a/src/cmd/internal/ssa/type.go b/src/cmd/internal/ssa/type.go
new file mode 100644
index 0000000..3389622
--- /dev/null
+++ b/src/cmd/internal/ssa/type.go
@@ -0,0 +1,84 @@
+// Copyright 2015 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.
+
+package ssa
+
+import (
+	"cmd/internal/ssa/types" // TODO: use golang.org/x/tools/go/types instead
+)
+
+// We just inherit types from go/types
+type Type types.Type
+
+var (
+	// shortcuts for commonly used basic types
+	TypeInt     = types.Typ[types.Int]
+	TypeUint    = types.Typ[types.Uint]
+	TypeInt8    = types.Typ[types.Int8]
+	TypeInt16   = types.Typ[types.Int16]
+	TypeInt32   = types.Typ[types.Int32]
+	TypeInt64   = types.Typ[types.Int64]
+	TypeUint8   = types.Typ[types.Uint8]
+	TypeUint16  = types.Typ[types.Uint16]
+	TypeUint32  = types.Typ[types.Uint32]
+	TypeUint64  = types.Typ[types.Uint64]
+	TypeUintptr = types.Typ[types.Uintptr]
+	TypeBool    = types.Typ[types.Bool]
+	TypeString  = types.Typ[types.String]
+
+	TypeInvalid = types.Typ[types.Invalid]
+
+	// Additional compiler-only types go here.
+	TypeMem   = &Memory{}
+	TypeFlags = &Flags{}
+)
+
+// typeIdentical returns whether it two arguments are the same type.
+func typeIdentical(t, u Type) bool {
+	if t == TypeMem {
+		return u == TypeMem
+	}
+	if t == TypeFlags {
+		return u == TypeFlags
+	}
+	return types.Identical(t, u)
+}
+
+// A type representing all of memory
+type Memory struct {
+}
+
+func (t *Memory) Underlying() types.Type { panic("Underlying of Memory") }
+func (t *Memory) String() string         { return "mem" }
+
+// A type representing the unknown type
+type Unknown struct {
+}
+
+func (t *Unknown) Underlying() types.Type { panic("Underlying of Unknown") }
+func (t *Unknown) String() string         { return "unk" }
+
+// A type representing the void type.  Used during building, should always
+// be eliminated by the first deadcode pass.
+type Void struct {
+}
+
+func (t *Void) Underlying() types.Type { panic("Underlying of Void") }
+func (t *Void) String() string         { return "void" }
+
+// A type representing the results of a nil check or bounds check.
+// TODO: or type check?
+// TODO: just use bool?
+type Check struct {
+}
+
+func (t *Check) Underlying() types.Type { panic("Underlying of Check") }
+func (t *Check) String() string         { return "check" }
+
+// x86 flags type
+type Flags struct {
+}
+
+func (t *Flags) Underlying() types.Type { panic("Underlying of Flags") }
+func (t *Flags) String() string         { return "flags" }
diff --git a/src/cmd/internal/ssa/types/object.go b/src/cmd/internal/ssa/types/object.go
new file mode 100644
index 0000000..cd0be16
--- /dev/null
+++ b/src/cmd/internal/ssa/types/object.go
@@ -0,0 +1,39 @@
+// Copyright 2015 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.
+
+// This package is a drop-in replacement for go/types
+// for use until go/types is included in the main repo.
+
+package types
+
+// An Object describes a named language entity such as a package,
+// constant, type, variable, function (incl. methods), or label.
+// All objects implement the Object interface.
+//
+type Object interface {
+	Name() string // package local object name
+	Type() Type   // object type
+}
+
+// An object implements the common parts of an Object.
+type object struct {
+	name string
+	typ  Type
+}
+
+func (obj *object) Name() string { return obj.name }
+func (obj *object) Type() Type   { return obj.typ }
+
+// A Variable represents a declared variable (including function parameters and results, and struct fields).
+type Var struct {
+	object
+	anonymous bool // if set, the variable is an anonymous struct field, and name is the type name
+	visited   bool // for initialization cycle detection
+	isField   bool // var is struct field
+	used      bool // set if the variable was used
+}
+
+func NewParam(pos int, pkg *int, name string, typ Type) *Var {
+	return &Var{object: object{name, typ}, used: true} // parameters are always 'used'
+}
diff --git a/src/cmd/internal/ssa/types/type.go b/src/cmd/internal/ssa/types/type.go
new file mode 100644
index 0000000..e01de5c
--- /dev/null
+++ b/src/cmd/internal/ssa/types/type.go
@@ -0,0 +1,229 @@
+// Copyright 2015 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.
+
+// This package is a drop-in replacement for go/types
+// for use until go/types is included in the main repo.
+
+package types
+
+// A Type represents a type of Go.
+// All types implement the Type interface.
+type Type interface {
+	// Underlying returns the underlying type of a type.
+	Underlying() Type
+
+	// String returns a string representation of a type.
+	String() string
+}
+
+// BasicKind describes the kind of basic type.
+type BasicKind int
+
+const (
+	Invalid BasicKind = iota // type is invalid
+
+	// predeclared types
+	Bool
+	Int
+	Int8
+	Int16
+	Int32
+	Int64
+	Uint
+	Uint8
+	Uint16
+	Uint32
+	Uint64
+	Uintptr
+	Float32
+	Float64
+	Complex64
+	Complex128
+	String
+	UnsafePointer
+
+	// types for untyped values
+	UntypedBool
+	UntypedInt
+	UntypedRune
+	UntypedFloat
+	UntypedComplex
+	UntypedString
+	UntypedNil
+
+	// aliases
+	Byte = Uint8
+	Rune = Int32
+)
+
+// BasicInfo is a set of flags describing properties of a basic type.
+type BasicInfo int
+
+// Properties of basic types.
+const (
+	IsBoolean BasicInfo = 1 << iota
+	IsInteger
+	IsUnsigned
+	IsFloat
+	IsComplex
+	IsString
+	IsUntyped
+
+	IsOrdered   = IsInteger | IsFloat | IsString
+	IsNumeric   = IsInteger | IsFloat | IsComplex
+	IsConstType = IsBoolean | IsNumeric | IsString
+)
+
+// A Basic represents a basic type.
+type Basic struct {
+	kind BasicKind
+	info BasicInfo
+	name string
+}
+
+// Kind returns the kind of basic type b.
+func (b *Basic) Kind() BasicKind { return b.kind }
+
+// Info returns information about properties of basic type b.
+func (b *Basic) Info() BasicInfo { return b.info }
+
+// Name returns the name of basic type b.
+func (b *Basic) Name() string { return b.name }
+
+// A Pointer represents a pointer type.
+type Pointer struct {
+	base Type // element type
+}
+
+// NewPointer returns a new pointer type for the given element (base) type.
+func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
+
+// Elem returns the element type for the given pointer p.
+func (p *Pointer) Elem() Type { return p.base }
+
+// A Slice represents a slice type.
+type Slice struct {
+	elem Type
+}
+
+// NewSlice returns a new slice type for the given element type.
+func NewSlice(elem Type) *Slice { return &Slice{elem} }
+
+// Elem returns the element type of slice s.
+func (s *Slice) Elem() Type { return s.elem }
+
+// Implementations for Type methods.
+func (t *Basic) Underlying() Type     { return t }
+func (t *Slice) Underlying() Type     { return t }
+func (t *Pointer) Underlying() Type   { return t }
+func (t *Signature) Underlying() Type { return t }
+
+func (b *Basic) String() string     { return b.name }
+func (t *Slice) String() string     { return "[]" + t.elem.String() }
+func (t *Pointer) String() string   { return "*" + t.base.String() }
+func (t *Signature) String() string { return "sig" /* TODO */ }
+
+var Typ = [...]*Basic{
+	Invalid: {Invalid, 0, "invalid type"},
+
+	Bool:          {Bool, IsBoolean, "bool"},
+	Int:           {Int, IsInteger, "int"},
+	Int8:          {Int8, IsInteger, "int8"},
+	Int16:         {Int16, IsInteger, "int16"},
+	Int32:         {Int32, IsInteger, "int32"},
+	Int64:         {Int64, IsInteger, "int64"},
+	Uint:          {Uint, IsInteger | IsUnsigned, "uint"},
+	Uint8:         {Uint8, IsInteger | IsUnsigned, "uint8"},
+	Uint16:        {Uint16, IsInteger | IsUnsigned, "uint16"},
+	Uint32:        {Uint32, IsInteger | IsUnsigned, "uint32"},
+	Uint64:        {Uint64, IsInteger | IsUnsigned, "uint64"},
+	Uintptr:       {Uintptr, IsInteger | IsUnsigned, "uintptr"},
+	Float32:       {Float32, IsFloat, "float32"},
+	Float64:       {Float64, IsFloat, "float64"},
+	Complex64:     {Complex64, IsComplex, "complex64"},
+	Complex128:    {Complex128, IsComplex, "complex128"},
+	String:        {String, IsString, "string"},
+	UnsafePointer: {UnsafePointer, 0, "Pointer"},
+
+	UntypedBool:    {UntypedBool, IsBoolean | IsUntyped, "untyped bool"},
+	UntypedInt:     {UntypedInt, IsInteger | IsUntyped, "untyped int"},
+	UntypedRune:    {UntypedRune, IsInteger | IsUntyped, "untyped rune"},
+	UntypedFloat:   {UntypedFloat, IsFloat | IsUntyped, "untyped float"},
+	UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex"},
+	UntypedString:  {UntypedString, IsString | IsUntyped, "untyped string"},
+	UntypedNil:     {UntypedNil, IsUntyped, "untyped nil"},
+}
+
+// Identical reports whether x and y are identical.
+func Identical(x, y Type) bool {
+	if x == y {
+		return true
+	}
+
+	switch x := x.(type) {
+	case *Basic:
+		// Basic types are singletons except for the rune and byte
+		// aliases, thus we cannot solely rely on the x == y check
+		// above.
+		if y, ok := y.(*Basic); ok {
+			return x.kind == y.kind
+		}
+	default:
+		panic("can't handle yet")
+	}
+	return false
+}
+
+// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
+// Tuples are used as components of signatures and to represent the type of multiple
+// assignments; they are not first class types of Go.
+type Tuple struct {
+	vars []*Var
+}
+
+// NewTuple returns a new tuple for the given variables.
+func NewTuple(x ...*Var) *Tuple {
+	if len(x) > 0 {
+		return &Tuple{x}
+	}
+	return nil
+}
+
+// Len returns the number variables of tuple t.
+func (t *Tuple) Len() int {
+	if t != nil {
+		return len(t.vars)
+	}
+	return 0
+}
+
+// At returns the i'th variable of tuple t.
+func (t *Tuple) At(i int) *Var { return t.vars[i] }
+
+// A Signature represents a (non-builtin) function or method type.
+type Signature struct {
+	recv     *Var   // nil if not a method
+	params   *Tuple // (incoming) parameters from left to right; or nil
+	results  *Tuple // (outgoing) results from left to right; or nil
+	variadic bool   // true if the last parameter's type is of the form ...T (or string, for append built-in only)
+}
+
+// NewSignature returns a new function type for the given receiver, parameters,
+// and results, either of which may be nil. If variadic is set, the function
+// is variadic, it must have at least one parameter, and the last parameter
+// must be of unnamed slice type.
+func NewSignature(scope *int, recv *Var, params, results *Tuple, variadic bool) *Signature {
+	// TODO(gri) Should we rely on the correct (non-nil) incoming scope
+	//           or should this function allocate and populate a scope?
+	if variadic {
+		n := params.Len()
+		if n == 0 {
+			panic("types.NewSignature: variadic function must have at least one parameter")
+		}
+		if _, ok := params.At(n - 1).typ.(*Slice); !ok {
+			panic("types.NewSignature: variadic parameter must be of unnamed slice type")
+		}
+	}
+	return &Signature{recv, params, results, variadic}
+}
diff --git a/src/cmd/internal/ssa/value.go b/src/cmd/internal/ssa/value.go
new file mode 100644
index 0000000..740525a
--- /dev/null
+++ b/src/cmd/internal/ssa/value.go
@@ -0,0 +1,117 @@
+// Copyright 2015 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.
+
+package ssa
+
+import (
+	"fmt"
+	"strings"
+)
+
+// A Value represents a value in the SSA representation of the program.
+// The ID and Type fields must not be modified.  The remainder may be modified
+// if they preserve the value of the Value (e.g. changing a (mul 2 x) to an (add x x)).
+type Value struct {
+	// A unique identifier for the value.  For performance we allocate these IDs
+	// densely starting at 0.  There is no guarantee that there won't be occasional holes, though.
+	ID ID
+
+	// The operation that computes this value.  See op.go.
+	Op Op
+
+	// The type of this value.  Normally this will be a Go type, but there
+	// are a few other pseudo-types, see type.go.
+	Type Type
+
+	// Auxiliary info for this value.  The type of this information depends on the opcode (& type).
+	Aux interface{}
+
+	// Arguments of this value
+	Args []*Value
+
+	// Containing basic block
+	Block *Block
+
+	// Storage for the first two args
+	argstorage [2]*Value
+}
+
+// Examples:
+// Opcode          aux   args
+//  OpAdd          nil      2
+//  OpConstStr  string      0
+//  OpConstInt   int64      0
+//  OpAddcq      int64      1    amd64 op: v = arg[0] + constant
+
+// short form print.  Just v#.
+func (v *Value) String() string {
+	return fmt.Sprintf("v%d", v.ID)
+}
+
+// long form print.  v# = opcode <type> [aux] args [: reg]
+func (v *Value) LongString() string {
+	s := fmt.Sprintf("v%d = %s", v.ID, strings.TrimPrefix(v.Op.String(), "Op"))
+	s += " <" + v.Type.String() + ">"
+	if v.Aux != nil {
+		s += fmt.Sprintf(" [%v]", v.Aux)
+	}
+	for _, a := range v.Args {
+		s += fmt.Sprintf(" %v", a)
+	}
+	r := v.Block.Func.RegAlloc
+	if r != nil && r[v.ID] != nil {
+		s += " : " + r[v.ID].Name()
+	}
+	return s
+}
+
+func (v *Value) AddArg(w *Value) {
+	v.Args = append(v.Args, w)
+}
+func (v *Value) AddArgs(a ...*Value) {
+	v.Args = append(v.Args, a...)
+}
+func (v *Value) SetArg(i int, w *Value) {
+	v.Args[i] = w
+}
+func (v *Value) RemoveArg(i int) {
+	copy(v.Args[i:], v.Args[i+1:])
+	v.Args = v.Args[:len(v.Args)-1]
+}
+func (v *Value) SetArgs1(a *Value) {
+	v.resetArgs()
+	v.AddArg(a)
+}
+func (v *Value) SetArgs2(a *Value, b *Value) {
+	v.resetArgs()
+	v.AddArg(a)
+	v.AddArg(b)
+}
+
+func (v *Value) resetArgs() {
+	v.argstorage[0] = nil
+	v.argstorage[1] = nil
+	v.Args = v.argstorage[:0]
+}
+
+// CopyFrom converts v to be the same value as w.  v and w must
+// have the same type.
+func (v *Value) CopyFrom(w *Value) {
+	if !typeIdentical(v.Type, w.Type) {
+		panic("copyFrom with unequal types")
+	}
+	v.Op = w.Op
+	v.Aux = w.Aux
+	v.resetArgs()
+	v.AddArgs(w.Args...)
+}
+
+// SetType sets the type of v.  v must not have had its type
+// set yet (it must be TypeInvalid).
+func (v *Value) SetType() {
+	if v.Type != TypeInvalid {
+		panic("setting type when it is already set")
+	}
+	opcodeTable[v.Op].typer(v)
+}