[dev.ssa] cmd/compile: start arguments as spilled

Declare a function's arguments as having already been
spilled so their use just requires a restore.

Allow spill locations to be portions of larger objects the stack.
Required to load portions of compound input arguments.

Rename the memory input to InputMem.  Use Arg for the
pre-spilled argument values.

Change-Id: I8fe2a03ffbba1022d98bfae2052b376b96d32dda
Reviewed-on: https://go-review.googlesource.com/16536
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 5a8e43d..2c935b7 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -97,7 +97,7 @@
 	// Allocate starting values
 	s.labels = map[string]*ssaLabel{}
 	s.labeledNodes = map[*Node]*ssaLabel{}
-	s.startmem = s.entryNewValue0(ssa.OpArg, ssa.TypeMem)
+	s.startmem = s.entryNewValue0(ssa.OpInitMem, ssa.TypeMem)
 	s.sp = s.entryNewValue0(ssa.OpSP, Types[TUINTPTR]) // TODO: use generic pointer type (unsafe.Pointer?) instead
 	s.sb = s.entryNewValue0(ssa.OpSB, Types[TUINTPTR])
 
@@ -3168,6 +3168,12 @@
 		if name == &memVar {
 			return s.startmem
 		}
+		if canSSA(name) {
+			v := s.entryNewValue0A(ssa.OpArg, t, name)
+			// v starts with AuxInt == 0.
+			s.addNamedValue(name, v)
+			return v
+		}
 		// variable is live at the entry block.  Load it.
 		addr := s.decladdrs[name]
 		if addr == nil {
@@ -3239,18 +3245,21 @@
 		// Don't track autotmp_ variables.
 		return
 	}
-	if n.Class == PPARAM || n.Class == PPARAMOUT {
-		// TODO: Remove this
+	if n.Class == PAUTO && (v.Type.IsString() || v.Type.IsSlice() || v.Type.IsInterface()) {
+		// TODO: can't handle auto compound objects with pointers yet.
+		// The live variable analysis barfs because we don't put VARDEF
+		// pseudos in the right place when we spill to these nodes.
 		return
 	}
 	if n.Class == PAUTO && n.Xoffset != 0 {
 		s.Fatalf("AUTO var with offset %s %d", n, n.Xoffset)
 	}
-	values, ok := s.f.NamedValues[n]
+	loc := ssa.LocalSlot{N: n, Type: n.Type, Off: 0}
+	values, ok := s.f.NamedValues[loc]
 	if !ok {
-		s.f.Names = append(s.f.Names, n)
+		s.f.Names = append(s.f.Names, loc)
 	}
-	s.f.NamedValues[n] = append(values, v)
+	s.f.NamedValues[loc] = append(values, v)
 }
 
 // an unresolved branch
@@ -3873,11 +3882,17 @@
 			return
 		}
 		p := Prog(movSizeByType(v.Type))
-		n := autoVar(v.Args[0])
+		n, off := autoVar(v.Args[0])
 		p.From.Type = obj.TYPE_MEM
-		p.From.Name = obj.NAME_AUTO
 		p.From.Node = n
 		p.From.Sym = Linksym(n.Sym)
+		p.From.Offset = off
+		if n.Class == PPARAM {
+			p.From.Name = obj.NAME_PARAM
+			p.From.Offset += n.Xoffset
+		} else {
+			p.From.Name = obj.NAME_AUTO
+		}
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = regnum(v)
 
@@ -3889,11 +3904,17 @@
 		p := Prog(movSizeByType(v.Type))
 		p.From.Type = obj.TYPE_REG
 		p.From.Reg = regnum(v.Args[0])
-		n := autoVar(v)
+		n, off := autoVar(v)
 		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_AUTO
 		p.To.Node = n
 		p.To.Sym = Linksym(n.Sym)
+		p.To.Offset = off
+		if n.Class == PPARAM {
+			p.To.Name = obj.NAME_PARAM
+			p.To.Offset += n.Xoffset
+		} else {
+			p.To.Name = obj.NAME_AUTO
+		}
 	case ssa.OpPhi:
 		// just check to make sure regalloc and stackalloc did it right
 		if v.Type.IsMemory() {
@@ -3912,9 +3933,10 @@
 			v.Fatalf("const value %v shouldn't have a location", v)
 		}
 
-	case ssa.OpArg:
+	case ssa.OpInitMem:
 		// memory arg needs no code
-		// TODO: check that only mem arg goes here.
+	case ssa.OpArg:
+		// input args need no code
 	case ssa.OpAMD64LoweredGetClosurePtr:
 		// Output is hardwired to DX only,
 		// and DX contains the closure pointer on
@@ -4476,9 +4498,11 @@
 	return ssaRegToReg[reg.(*ssa.Register).Num]
 }
 
-// autoVar returns a *Node representing the auto variable assigned to v.
-func autoVar(v *ssa.Value) *Node {
-	return v.Block.Func.RegAlloc[v.ID].(*ssa.LocalSlot).N.(*Node)
+// autoVar returns a *Node and int64 representing the auto variable and offset within it
+// where v should be spilled.
+func autoVar(v *ssa.Value) (*Node, int64) {
+	loc := v.Block.Func.RegAlloc[v.ID].(ssa.LocalSlot)
+	return loc.N.(*Node), loc.Off
 }
 
 // ssaExport exports a bunch of compiler services for the ssa backend.