[dev.ssa] cmd/internal/gc: convert standard IR into SSA.

Hook into the current compiler to convert the existing
IR (after walk) into SSA.  Any function ending in "_ssa"
will take this path.  The resulting assembly is printed
and then discarded.

Use gc.Type directly in ssa instead of a wrapper for go types.
It makes the IR->SSA rewrite a lot simpler.

Only a few opcodes are implemented in this change.  It is
enough to compile simple examples like
    func f(p *int) int { return *p }
    func g(a []int, i int) int { return a[i] }

Change-Id: I5e18841b752a83ca0519aa1b2d36ef02ce1de6f9
Reviewed-on: https://go-review.googlesource.com/8971
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/cmd/internal/gc/ssa.go b/src/cmd/internal/gc/ssa.go
new file mode 100644
index 0000000..415e9dc
--- /dev/null
+++ b/src/cmd/internal/gc/ssa.go
@@ -0,0 +1,450 @@
+// 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 gc
+
+import (
+	"log"
+
+	"cmd/internal/ssa"
+)
+
+func buildssa(fn *Node) {
+	dumplist("buildssa", Curfn.Nbody)
+
+	var s ssaState
+
+	// TODO(khr): build config just once at the start of the compiler binary
+	s.config = ssa.NewConfig(Thearch.Thestring)
+	s.f = s.config.NewFunc()
+	s.f.Name = fn.Nname.Sym.Name
+
+	// 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
+	// TODO: check this comment
+
+	// Allocate starting block
+	s.f.Entry = s.f.NewBlock(ssa.BlockPlain)
+
+	// Allocate exit block
+	s.exit = s.f.NewBlock(ssa.BlockExit)
+
+	// TODO(khr): all args.  Make a struct containing args/returnvals, declare
+	// an FP which contains a pointer to that struct.
+
+	s.vars = map[string]*ssa.Value{}
+	s.labels = map[string]*ssa.Block{}
+	s.argOffsets = map[string]int64{}
+
+	// Convert the AST-based IR to the SSA-based IR
+	s.startBlock(s.f.Entry)
+	s.stmtList(fn.Nbody)
+
+	// Finish up exit block
+	s.startBlock(s.exit)
+	s.exit.Control = s.mem()
+	s.endBlock()
+
+	// Link up variable uses to variable definitions
+	s.linkForwardReferences()
+
+	ssa.Compile(s.f)
+
+	// TODO(khr): Use the resulting s.f to generate code
+}
+
+type ssaState struct {
+	// configuration (arch) information
+	config *ssa.Config
+
+	// function we're building
+	f *ssa.Func
+
+	// exit block that "return" jumps to (and panics jump to)
+	exit *ssa.Block
+
+	// the target block for each label in f
+	labels map[string]*ssa.Block
+
+	// current location where we're interpreting the AST
+	curBlock *ssa.Block
+
+	// variable assignments in the current block (map from variable name to ssa value)
+	vars map[string]*ssa.Value
+
+	// all defined variables at the end of each block.  Indexed by block ID.
+	defvars []map[string]*ssa.Value
+
+	// offsets of argument slots
+	// unnamed and unused args are not listed.
+	argOffsets map[string]int64
+}
+
+// startBlock sets the current block we're generating code in to b.
+func (s *ssaState) startBlock(b *ssa.Block) {
+	s.curBlock = b
+	s.vars = map[string]*ssa.Value{}
+}
+
+// endBlock marks the end of generating code for the current block.
+// Returns the (former) current block.  Returns nil if there is no current
+// block, i.e. if no code flows to the current execution point.
+func (s *ssaState) endBlock() *ssa.Block {
+	b := s.curBlock
+	if b == nil {
+		return nil
+	}
+	for len(s.defvars) <= int(b.ID) {
+		s.defvars = append(s.defvars, nil)
+	}
+	s.defvars[b.ID] = s.vars
+	s.curBlock = nil
+	s.vars = nil
+	return b
+}
+
+// ssaStmtList converts the statement n to SSA and adds it to s.
+func (s *ssaState) stmtList(l *NodeList) {
+	for ; l != nil; l = l.Next {
+		s.stmt(l.N)
+	}
+}
+
+// ssaStmt converts the statement n to SSA and adds it to s.
+func (s *ssaState) stmt(n *Node) {
+	s.stmtList(n.Ninit)
+	switch n.Op {
+
+	case OBLOCK:
+		s.stmtList(n.List)
+
+	case ODCL:
+		// TODO: ???  Assign 0?
+
+	case OLABEL, OGOTO:
+		// get block at label, or make one
+		t := s.labels[n.Left.Sym.Name]
+		if t == nil {
+			t = s.f.NewBlock(ssa.BlockPlain)
+			s.labels[n.Left.Sym.Name] = t
+		}
+		// go to that label (we pretend "label:" is preceded by "goto label")
+		b := s.endBlock()
+		addEdge(b, t)
+
+		if n.Op == OLABEL {
+			// next we work on the label's target block
+			s.startBlock(t)
+		}
+
+	case OAS:
+		// TODO(khr): colas?
+		val := s.expr(n.Right)
+		if n.Left.Op == OINDREG {
+			// indirect off a register (TODO: always SP?)
+			// used for storing arguments to callees
+			addr := s.f.Entry.NewValue(ssa.OpSPAddr, Ptrto(n.Right.Type), n.Left.Xoffset)
+			s.vars[".mem"] = s.curBlock.NewValue3(ssa.OpStore, ssa.TypeMem, nil, addr, val, s.mem())
+		} else if n.Left.Op != ONAME {
+			// some more complicated expression.  Rewrite to a store.  TODO
+			addr := s.expr(n.Left) // TODO: wrap in &
+
+			// TODO(khr): nil check
+			s.vars[".mem"] = s.curBlock.NewValue3(ssa.OpStore, n.Right.Type, nil, addr, val, s.mem())
+		} else if n.Left.Addable == 0 {
+			// TODO
+			log.Fatalf("assignment to non-addable value")
+		} else if n.Left.Class&PHEAP != 0 {
+			// TODO
+			log.Fatalf("assignment to heap value")
+		} else if n.Left.Class == PPARAMOUT {
+			// store to parameter slot
+			addr := s.f.Entry.NewValue(ssa.OpFPAddr, Ptrto(n.Right.Type), n.Left.Xoffset)
+			s.vars[".mem"] = s.curBlock.NewValue3(ssa.OpStore, ssa.TypeMem, nil, addr, val, s.mem())
+		} else {
+			// normal variable
+			s.vars[n.Left.Sym.Name] = val
+		}
+	case OIF:
+		cond := s.expr(n.Ntest)
+		b := s.endBlock()
+		b.Kind = ssa.BlockIf
+		b.Control = cond
+		// TODO(khr): likely direction
+
+		bThen := s.f.NewBlock(ssa.BlockPlain)
+		bEnd := s.f.NewBlock(ssa.BlockPlain)
+		var bElse *ssa.Block
+
+		if n.Nelse == nil {
+			addEdge(b, bThen)
+			addEdge(b, bEnd)
+		} else {
+			bElse = s.f.NewBlock(ssa.BlockPlain)
+			addEdge(b, bThen)
+			addEdge(b, bElse)
+		}
+
+		s.startBlock(bThen)
+		s.stmtList(n.Nbody)
+		b = s.endBlock()
+		if b != nil {
+			addEdge(b, bEnd)
+		}
+
+		if n.Nelse != nil {
+			s.startBlock(bElse)
+			s.stmtList(n.Nelse)
+			b = s.endBlock()
+			if b != nil {
+				addEdge(b, bEnd)
+			}
+		}
+		s.startBlock(bEnd)
+
+	case ORETURN:
+		s.stmtList(n.List)
+		b := s.endBlock()
+		addEdge(b, s.exit)
+
+	case OFOR:
+		bCond := s.f.NewBlock(ssa.BlockPlain)
+		bBody := s.f.NewBlock(ssa.BlockPlain)
+		bEnd := s.f.NewBlock(ssa.BlockPlain)
+
+		// first, jump to condition test
+		b := s.endBlock()
+		addEdge(b, bCond)
+
+		// generate code to test condition
+		// TODO(khr): Ntest == nil exception
+		s.startBlock(bCond)
+		cond := s.expr(n.Ntest)
+		b = s.endBlock()
+		b.Kind = ssa.BlockIf
+		b.Control = cond
+		// TODO(khr): likely direction
+		addEdge(b, bBody)
+		addEdge(b, bEnd)
+
+		// generate body
+		s.startBlock(bBody)
+		s.stmtList(n.Nbody)
+		s.stmt(n.Nincr)
+		b = s.endBlock()
+		addEdge(b, bCond)
+
+		s.startBlock(bEnd)
+
+	case OVARKILL:
+		// TODO(khr): ??? anything to do here?  Only for addrtaken variables?
+		// Maybe just link it in the store chain?
+	default:
+		log.Fatalf("unhandled stmt %s", opnames[n.Op])
+	}
+}
+
+// expr converts the expression n to ssa, adds it to s and returns the ssa result.
+func (s *ssaState) expr(n *Node) *ssa.Value {
+	if n == nil {
+		// TODO(khr): is this nil???
+		return s.f.Entry.NewValue(ssa.OpConst, n.Type, nil)
+	}
+	switch n.Op {
+	case ONAME:
+		// remember offsets for PPARAM names
+		s.argOffsets[n.Sym.Name] = n.Xoffset
+		return s.variable(n.Sym.Name, n.Type)
+		// binary ops
+	case OLITERAL:
+		switch n.Val.Ctype {
+		case CTINT:
+			return s.f.ConstInt(n.Type, Mpgetfix(n.Val.U.Xval))
+		default:
+			log.Fatalf("unhandled OLITERAL %v", n.Val.Ctype)
+			return nil
+		}
+	case OLT:
+		a := s.expr(n.Left)
+		b := s.expr(n.Right)
+		return s.curBlock.NewValue2(ssa.OpLess, ssa.TypeBool, nil, a, b)
+	case OADD:
+		a := s.expr(n.Left)
+		b := s.expr(n.Right)
+		return s.curBlock.NewValue2(ssa.OpAdd, a.Type, nil, a, b)
+
+	case OSUB:
+		// TODO:(khr) fold code for all binary ops together somehow
+		a := s.expr(n.Left)
+		b := s.expr(n.Right)
+		return s.curBlock.NewValue2(ssa.OpSub, a.Type, nil, a, b)
+
+	case OIND:
+		p := s.expr(n.Left)
+		c := s.curBlock.NewValue1(ssa.OpCheckNil, ssa.TypeBool, nil, p)
+		b := s.endBlock()
+		b.Kind = ssa.BlockIf
+		b.Control = c
+		bNext := s.f.NewBlock(ssa.BlockPlain)
+		addEdge(b, bNext)
+		addEdge(b, s.exit)
+		s.startBlock(bNext)
+		// TODO(khr): if ptr check fails, don't go directly to exit.
+		// Instead, go to a call to panicnil or something.
+		// TODO: implicit nil checks somehow?
+
+		return s.curBlock.NewValue2(ssa.OpLoad, n.Type, nil, p, s.mem())
+	case ODOTPTR:
+		p := s.expr(n.Left)
+		// TODO: nilcheck
+		p = s.curBlock.NewValue2(ssa.OpAdd, p.Type, nil, p, s.f.ConstInt(s.config.UIntPtr, n.Xoffset))
+		return s.curBlock.NewValue2(ssa.OpLoad, n.Type, nil, p, s.mem())
+
+	case OINDEX:
+		// TODO: slice vs array?  Map index is already reduced to a function call
+		a := s.expr(n.Left)
+		i := s.expr(n.Right)
+		// convert index to full width
+		// TODO: if index is 64-bit and we're compiling to 32-bit, check that high
+		// 32 bits are zero (and use a low32 op instead of convnop here).
+		i = s.curBlock.NewValue1(ssa.OpConvNop, s.config.UIntPtr, nil, i)
+
+		// bounds check
+		len := s.curBlock.NewValue1(ssa.OpSliceLen, s.config.UIntPtr, nil, a)
+		cmp := s.curBlock.NewValue2(ssa.OpCheckBound, ssa.TypeBool, nil, i, len)
+		b := s.endBlock()
+		b.Kind = ssa.BlockIf
+		b.Control = cmp
+		bNext := s.f.NewBlock(ssa.BlockPlain)
+		addEdge(b, bNext)
+		addEdge(b, s.exit)
+		s.startBlock(bNext)
+		// TODO: don't go directly to s.exit.  Go to a stub that calls panicindex first.
+
+		return s.curBlock.NewValue3(ssa.OpSliceIndex, n.Left.Type.Type, nil, a, i, s.mem())
+
+	case OCALLFUNC:
+		// run all argument assignments
+		// TODO(khr): do we need to evaluate function first?
+		// Or is it already side-effect-free and does not require a call?
+		s.stmtList(n.List)
+
+		if n.Left.Op != ONAME {
+			// TODO(khr): closure calls?
+			log.Fatalf("can't handle CALLFUNC with non-ONAME fn %s", opnames[n.Left.Op])
+		}
+		bNext := s.f.NewBlock(ssa.BlockPlain)
+		call := s.curBlock.NewValue1(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym.Name, s.mem())
+		b := s.endBlock()
+		b.Kind = ssa.BlockCall
+		b.Control = call
+		addEdge(b, bNext)
+		addEdge(b, s.exit)
+
+		// read result from stack at the start of the fallthrough block
+		s.startBlock(bNext)
+		var titer Iter
+		fp := Structfirst(&titer, Getoutarg(n.Left.Type))
+		a := s.f.Entry.NewValue(ssa.OpSPAddr, Ptrto(fp.Type), fp.Width)
+		return s.curBlock.NewValue2(ssa.OpLoad, fp.Type, nil, a, call)
+	default:
+		log.Fatalf("unhandled expr %s", opnames[n.Op])
+		return nil
+	}
+}
+
+// variable returns the value of a variable at the current location.
+func (s *ssaState) variable(name string, t ssa.Type) *ssa.Value {
+	if s.curBlock == nil {
+		log.Fatalf("nil curblock!")
+	}
+	v := s.vars[name]
+	if v == nil {
+		// TODO: get type?  Take Sym as arg?
+		v = s.curBlock.NewValue(ssa.OpFwdRef, t, name)
+		s.vars[name] = v
+	}
+	return v
+}
+
+func (s *ssaState) mem() *ssa.Value {
+	return s.variable(".mem", ssa.TypeMem)
+}
+
+func (s *ssaState) linkForwardReferences() {
+	// Build ssa graph.  Each variable on its first use in a basic block
+	// leaves a FwdRef in that block representing the incoming value
+	// of that variable.  This function links that ref up with possible definitions,
+	// inserting Phi values as needed.  This is essentially the algorithm
+	// described by Brau, Buchwald, Hack, Leißa, Mallon, and Zwinkau:
+	// http://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
+	for _, b := range s.f.Blocks {
+		for _, v := range b.Values {
+			if v.Op != ssa.OpFwdRef {
+				continue
+			}
+			name := v.Aux.(string)
+			v.Op = ssa.OpCopy
+			v.Aux = nil
+			v.SetArgs1(s.lookupVarIncoming(b, v.Type, name))
+		}
+	}
+}
+
+// lookupVarIncoming finds the variable's value at the start of block b.
+func (s *ssaState) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Value {
+	// TODO(khr): have lookupVarIncoming overwrite the fwdRef or copy it
+	// will be used in, instead of having the result used in a copy value.
+	if b == s.f.Entry {
+		if name == ".mem" {
+			return b.NewValue(ssa.OpArg, t, name)
+		}
+		// variable is live at the entry block.  Load it.
+		a := s.f.Entry.NewValue(ssa.OpFPAddr, Ptrto(t.(*Type)), s.argOffsets[name])
+		m := b.NewValue(ssa.OpArg, ssa.TypeMem, ".mem") // TODO: reuse mem starting value
+		return b.NewValue2(ssa.OpLoad, t, nil, a, m)
+	}
+	var vals []*ssa.Value
+	for _, p := range b.Preds {
+		vals = append(vals, s.lookupVarOutgoing(p, t, name))
+	}
+	v0 := vals[0]
+	for i := 1; i < len(vals); i++ {
+		if vals[i] != v0 {
+			// need a phi value
+			v := b.NewValue(ssa.OpPhi, t, nil)
+			v.AddArgs(vals...)
+			return v
+		}
+	}
+	return v0
+}
+
+// lookupVarOutgoing finds the variable's value at the end of block b.
+func (s *ssaState) lookupVarOutgoing(b *ssa.Block, t ssa.Type, name string) *ssa.Value {
+	m := s.defvars[b.ID]
+	if v, ok := m[name]; ok {
+		return v
+	}
+	// The variable is not defined by b and we haven't
+	// looked it up yet.  Generate v, a copy value which
+	// will be the outgoing value of the variable.  Then
+	// look up w, the incoming value of the variable.
+	// Make v = copy(w).  We need the extra copy to
+	// prevent infinite recursion when looking up the
+	// incoming value of the variable.
+	v := b.NewValue(ssa.OpCopy, t, nil)
+	m[name] = v
+	v.AddArg(s.lookupVarIncoming(b, t, name))
+	return v
+}
+
+// TODO: the above mutually recursive functions can lead to very deep stacks.  Fix that.
+
+// addEdge adds an edge from b to c.
+func addEdge(b, c *ssa.Block) {
+	b.Succs = append(b.Succs, c)
+	c.Preds = append(c.Preds, b)
+}