[dev.ssa] cmd/compile/internal/ssa: implement slice opcodes

Implement OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR.

reviewer: Ignore the code in OINDEX, that's from CL 14466.

Change-Id: I00cc8aecd4c6f40ea5517cd660bb0ce759d91171
Reviewed-on: https://go-review.googlesource.com/14538
Reviewed-by: Todd Neal <todd@tneal.org>
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 0551ddb..738685b 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -240,6 +240,9 @@
 // dummy node for the memory variable
 var memvar = Node{Op: ONAME, Sym: &Sym{Name: "mem"}}
 
+// dummy nodes for temporary variables
+var ptrvar = Node{Op: ONAME, Sym: &Sym{Name: "ptr"}}
+
 // startBlock sets the current block we're generating code in to b.
 func (s *state) startBlock(b *ssa.Block) {
 	if s.curBlock != nil {
@@ -1747,70 +1750,38 @@
 		data := s.expr(n.Right)
 		return s.newValue2(ssa.OpIMake, n.Type, tab, data)
 
+	case OSLICE, OSLICEARR:
+		v := s.expr(n.Left)
+		var i, j *ssa.Value
+		if n.Right.Left != nil {
+			i = s.extendIndex(s.expr(n.Right.Left))
+		}
+		if n.Right.Right != nil {
+			j = s.extendIndex(s.expr(n.Right.Right))
+		}
+		p, l, c := s.slice(n.Left.Type, v, i, j, nil)
+		return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
 	case OSLICESTR:
-		// Evaluate the string once.
-		str := s.expr(n.Left)
-		ptr := s.newValue1(ssa.OpStringPtr, Ptrto(Types[TUINT8]), str)
-		len := s.newValue1(ssa.OpStringLen, Types[TINT], str)
-		zero := s.constInt(Types[TINT], 0)
-
-		// Evaluate the slice indexes.
-		var low, high *ssa.Value
-		if n.Right.Left == nil {
-			low = zero
-		} else {
-			low = s.extendIndex(s.expr(n.Right.Left))
+		v := s.expr(n.Left)
+		var i, j *ssa.Value
+		if n.Right.Left != nil {
+			i = s.extendIndex(s.expr(n.Right.Left))
 		}
-		if n.Right.Right == nil {
-			high = len
-		} else {
-			high = s.extendIndex(s.expr(n.Right.Right))
+		if n.Right.Right != nil {
+			j = s.extendIndex(s.expr(n.Right.Right))
 		}
-
-		// Panic if slice indices are not in bounds.
-		s.sliceBoundsCheck(low, high)
-		s.sliceBoundsCheck(high, len)
-
-		// Generate the following code assuming that indexes are in bounds.
-		// The conditional is to make sure that we don't generate a string
-		// that points to the next object in memory.
-		// rlen = (SubPtr high low)
-		// p = ptr
-		// if rlen != 0 {
-		//    p = (AddPtr ptr low)
-		// }
-		// result = (StringMake p size)
-		rlen := s.newValue2(ssa.OpSubPtr, Types[TINT], high, low)
-
-		// Use n as the "variable" for p.
-		s.vars[n] = ptr
-
-		// Generate code to test the resulting slice length.
-		var cmp *ssa.Value
-		if s.config.IntSize == 8 {
-			cmp = s.newValue2(ssa.OpNeq64, Types[TBOOL], rlen, zero)
-		} else {
-			cmp = s.newValue2(ssa.OpNeq32, Types[TBOOL], rlen, zero)
+		p, l, _ := s.slice(n.Left.Type, v, i, j, nil)
+		return s.newValue2(ssa.OpStringMake, n.Type, p, l)
+	case OSLICE3, OSLICE3ARR:
+		v := s.expr(n.Left)
+		var i *ssa.Value
+		if n.Right.Left != nil {
+			i = s.extendIndex(s.expr(n.Right.Left))
 		}
-
-		b := s.endBlock()
-		b.Kind = ssa.BlockIf
-		b.Likely = ssa.BranchLikely
-		b.Control = cmp
-
-		// Generate code for non-zero length slice case.
-		nz := s.f.NewBlock(ssa.BlockPlain)
-		b.AddEdgeTo(nz)
-		s.startBlock(nz)
-		s.vars[n] = s.newValue2(ssa.OpAddPtr, Ptrto(Types[TUINT8]), ptr, low)
-		s.endBlock()
-
-		// All done.
-		merge := s.f.NewBlock(ssa.BlockPlain)
-		b.AddEdgeTo(merge)
-		nz.AddEdgeTo(merge)
-		s.startBlock(merge)
-		return s.newValue2(ssa.OpStringMake, Types[TSTRING], s.variable(n, Ptrto(Types[TUINT8])), rlen)
+		j := s.extendIndex(s.expr(n.Right.Right.Left))
+		k := s.extendIndex(s.expr(n.Right.Right.Right))
+		p, l, c := s.slice(n.Left.Type, v, i, j, k)
+		return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
 
 	case OCALLFUNC, OCALLMETH:
 		left := n.Left
@@ -2201,6 +2172,125 @@
 	s.startBlock(bNext)
 }
 
+// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
+// i,j,k may be nil, in which case they are set to their default value.
+// t is a slice, ptr to array, or string type.
+func (s *state) slice(t *Type, v, i, j, k *ssa.Value) (p, l, c *ssa.Value) {
+	var elemtype *Type
+	var ptrtype *Type
+	var ptr *ssa.Value
+	var len *ssa.Value
+	var cap *ssa.Value
+	zero := s.constInt(Types[TINT], 0)
+	switch {
+	case t.IsSlice():
+		elemtype = t.Type
+		ptrtype = Ptrto(elemtype)
+		ptr = s.newValue1(ssa.OpSlicePtr, ptrtype, v)
+		len = s.newValue1(ssa.OpSliceLen, Types[TINT], v)
+		cap = s.newValue1(ssa.OpSliceCap, Types[TINT], v)
+	case t.IsString():
+		elemtype = Types[TUINT8]
+		ptrtype = Ptrto(elemtype)
+		ptr = s.newValue1(ssa.OpStringPtr, ptrtype, v)
+		len = s.newValue1(ssa.OpStringLen, Types[TINT], v)
+		cap = len
+	case t.IsPtr():
+		if !t.Type.IsArray() {
+			s.Fatalf("bad ptr to array in slice %v\n", t)
+		}
+		elemtype = t.Type.Type
+		ptrtype = Ptrto(elemtype)
+		s.nilCheck(v)
+		ptr = v
+		len = s.constInt(Types[TINT], t.Type.Bound)
+		cap = len
+	default:
+		s.Fatalf("bad type in slice %v\n", t)
+	}
+
+	// Set default values
+	if i == nil {
+		i = zero
+	}
+	if j == nil {
+		j = len
+	}
+	if k == nil {
+		k = cap
+	}
+
+	// Panic if slice indices are not in bounds.
+	s.sliceBoundsCheck(i, j)
+	if j != k {
+		s.sliceBoundsCheck(j, k)
+	}
+	if k != cap {
+		s.sliceBoundsCheck(k, cap)
+	}
+
+	// Generate the following code assuming that indexes are in bounds.
+	// The conditional is to make sure that we don't generate a slice
+	// that points to the next object in memory.
+	// rlen = (SubPtr j i)
+	// rcap = (SubPtr k i)
+	// p = ptr
+	// if rcap != 0 {
+	//    p = (AddPtr ptr (MulPtr low (ConstPtr size)))
+	// }
+	// result = (SliceMake p size)
+	rlen := s.newValue2(ssa.OpSubPtr, Types[TINT], j, i)
+	var rcap *ssa.Value
+	switch {
+	case t.IsString():
+		// Capacity of the result is unimportant.  However, we use
+		// rcap to test if we've generated a zero-length slice.
+		// Use length of strings for that.
+		rcap = rlen
+	case j == k:
+		rcap = rlen
+	default:
+		rcap = s.newValue2(ssa.OpSubPtr, Types[TINT], k, i)
+	}
+
+	s.vars[&ptrvar] = ptr
+
+	// Generate code to test the resulting slice length.
+	var cmp *ssa.Value
+	if s.config.IntSize == 8 {
+		cmp = s.newValue2(ssa.OpNeq64, Types[TBOOL], rcap, s.constInt(Types[TINT], 0))
+	} else {
+		cmp = s.newValue2(ssa.OpNeq32, Types[TBOOL], rcap, s.constInt(Types[TINT], 0))
+	}
+
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.Likely = ssa.BranchLikely
+	b.Control = cmp
+
+	// Generate code for non-zero length slice case.
+	nz := s.f.NewBlock(ssa.BlockPlain)
+	b.AddEdgeTo(nz)
+	s.startBlock(nz)
+	var inc *ssa.Value
+	if elemtype.Width == 1 {
+		inc = i
+	} else {
+		inc = s.newValue2(ssa.OpMulPtr, Types[TUINTPTR], i, s.constInt(Types[TINT], elemtype.Width))
+	}
+	s.vars[&ptrvar] = s.newValue2(ssa.OpAddPtr, ptrtype, ptr, inc)
+	s.endBlock()
+
+	// All done.
+	merge := s.f.NewBlock(ssa.BlockPlain)
+	b.AddEdgeTo(merge)
+	nz.AddEdgeTo(merge)
+	s.startBlock(merge)
+	rptr := s.variable(&ptrvar, ptrtype)
+	delete(s.vars, &ptrvar)
+	return rptr, rlen, rcap
+}
+
 type u2fcvtTab struct {
 	geq, cvt2F, and, rsh, or, add ssa.Op
 	one                           func(*state, ssa.Type, int64) *ssa.Value