]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: encapsulate OSLICE* representation
authorJosh Bleecher Snyder <josharian@gmail.com>
Thu, 21 Apr 2016 18:55:33 +0000 (11:55 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Mon, 25 Apr 2016 18:39:33 +0000 (18:39 +0000)
As a nice side-effect, this allows us to
unify several code paths.

The terminology (low, high, max, simple slice expr,
full slice expr) is taken from the spec and
the examples in the spec.

This is a trial run. The plan, probably for Go 1.8,
is to change slice expressions to use Node.List
instead of OKEY, and to do some similar
tree structure changes for other ops.

Passes toolstash -cmp. No performance change.
all.bash passes with GO_GCFLAGS=-newexport.

Updates #15350

Change-Id: Ic1efdc36e79cdb95ae1636e9817a3ac8f83ab1ac
Reviewed-on: https://go-review.googlesource.com/22425
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
13 files changed:
src/cmd/compile/internal/gc/bexport.go
src/cmd/compile/internal/gc/bimport.go
src/cmd/compile/internal/gc/cgen.go
src/cmd/compile/internal/gc/fmt.go
src/cmd/compile/internal/gc/inl.go
src/cmd/compile/internal/gc/order.go
src/cmd/compile/internal/gc/parser.go
src/cmd/compile/internal/gc/racewalk.go
src/cmd/compile/internal/gc/sinit.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/gc/subr.go
src/cmd/compile/internal/gc/typecheck.go
src/cmd/compile/internal/gc/walk.go

index f0907b45eb99206c58262035efe410faf6e2be8b..bf1354c71fd3f5e38aa77a533e8e1dae1df03099 100644 (file)
@@ -1191,12 +1191,15 @@ func (p *exporter) expr(n *Node) {
        case OSLICE, OSLICESTR, OSLICEARR:
                p.op(OSLICE)
                p.expr(n.Left)
-               p.expr(n.Right)
+               low, high, _ := n.SliceBounds()
+               p.exprsOrNil(low, high)
 
        case OSLICE3, OSLICE3ARR:
                p.op(OSLICE3)
                p.expr(n.Left)
-               p.expr(n.Right)
+               low, high, max := n.SliceBounds()
+               p.exprsOrNil(low, high)
+               p.expr(max)
 
        case OCOPY, OCOMPLEX:
                p.op(op)
index 1219d8d37081e70cdefe044188ee675bc92ea63b..3665bbdec2582248a248cbced9c20413858ef319 100644 (file)
@@ -822,9 +822,19 @@ func (p *importer) node() *Node {
        // case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
        //      unreachable - mapped to cases below by exporter
 
-       case OINDEX, OSLICE, OSLICE3:
+       case OINDEX:
                return Nod(op, p.expr(), p.expr())
 
+       case OSLICE, OSLICE3:
+               n := Nod(op, p.expr(), nil)
+               low, high := p.exprsOrNil()
+               var max *Node
+               if n.Op.IsSlice3() {
+                       max = p.expr()
+               }
+               n.SetSliceBounds(low, high, max)
+               return n
+
        case OCOPY, OCOMPLEX:
                n := builtinCall(op)
                n.List.Set([]*Node{p.expr(), p.expr()})
index a9393a6d9e1880e90514583cc397ce2ba4618ac3..3d3600a079b6ce07523f61e360a8330bebf58def 100644 (file)
@@ -3106,15 +3106,7 @@ func cgen_slice(n, res *Node, wb bool) {
                x.Xoffset -= 2 * int64(Widthptr)
        }
 
-       var x1, x2, x3 *Node // unevaluated index arguments
-       x1 = n.Right.Left
-       switch n.Op {
-       default:
-               x2 = n.Right.Right
-       case OSLICE3, OSLICE3ARR:
-               x2 = n.Right.Right.Left
-               x3 = n.Right.Right.Right
-       }
+       x1, x2, x3 := n.SliceBounds() // unevaluated index arguments
 
        // load computes src into targ, but if src refers to the len or cap of n.Left,
        // load copies those from xlen, xcap, loading xlen if needed.
index 12ae915fb2535457a43bbaadd9d9f528a5526094..27ece1d393d08cf6cdd3f059da434e2ad68ba685 100644 (file)
@@ -1312,17 +1312,29 @@ func exprfmt(n *Node, prec int) string {
                f += fmt.Sprintf(".(%v)", n.Type)
                return f
 
-       case OINDEX,
-               OINDEXMAP,
-               OSLICE,
-               OSLICESTR,
-               OSLICEARR,
-               OSLICE3,
-               OSLICE3ARR:
-               var f string
-               f += exprfmt(n.Left, nprec)
-               f += fmt.Sprintf("[%v]", n.Right)
-               return f
+       case OINDEX, OINDEXMAP:
+               return fmt.Sprintf("%s[%v]", exprfmt(n.Left, nprec), n.Right)
+
+       case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
+               var buf bytes.Buffer
+               buf.WriteString(exprfmt(n.Left, nprec))
+               buf.WriteString("[")
+               low, high, max := n.SliceBounds()
+               if low != nil {
+                       buf.WriteString(low.String())
+               }
+               buf.WriteString(":")
+               if high != nil {
+                       buf.WriteString(high.String())
+               }
+               if n.Op.IsSlice3() {
+                       buf.WriteString(":")
+                       if max != nil {
+                               buf.WriteString(max.String())
+                       }
+               }
+               buf.WriteString("]")
+               return buf.String()
 
        case OCOPY, OCOMPLEX:
                return fmt.Sprintf("%v(%v, %v)", Oconv(n.Op, FmtSharp), n.Left, n.Right)
index c863b84203b6eed821d89b008f55b61a12cf4ebc..10b61377ca4fb190a6af86c9fc8fe4dbf0809f81 100644 (file)
@@ -754,7 +754,7 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
                        vararrtype := typArray(varargtype.Elem(), int64(varargcount))
                        as.Right = Nod(OCOMPLIT, nil, typenod(vararrtype))
                        as.Right.List.Set(varargs)
-                       as.Right = Nod(OSLICE, as.Right, Nod(OKEY, nil, nil))
+                       as.Right = Nod(OSLICE, as.Right, nil)
                }
 
                as = typecheck(as, Etop)
index 00ba4308cb7465f06bc366695352157953ed5fcb..7373479ac916db7eacf1dcab5953d6f13e952fef 100644 (file)
@@ -1123,24 +1123,16 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
                        n = ordercopyexpr(n, n.Type, order, 0)
                }
 
-       case OSLICE, OSLICEARR, OSLICESTR:
+       case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
                n.Left = orderexpr(n.Left, order, nil)
-               n.Right.Left = orderexpr(n.Right.Left, order, nil)
-               n.Right.Left = ordercheapexpr(n.Right.Left, order)
-               n.Right.Right = orderexpr(n.Right.Right, order, nil)
-               n.Right.Right = ordercheapexpr(n.Right.Right, order)
-               if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
-                       n = ordercopyexpr(n, n.Type, order, 0)
-               }
-
-       case OSLICE3, OSLICE3ARR:
-               n.Left = orderexpr(n.Left, order, nil)
-               n.Right.Left = orderexpr(n.Right.Left, order, nil)
-               n.Right.Left = ordercheapexpr(n.Right.Left, order)
-               n.Right.Right.Left = orderexpr(n.Right.Right.Left, order, nil)
-               n.Right.Right.Left = ordercheapexpr(n.Right.Right.Left, order)
-               n.Right.Right.Right = orderexpr(n.Right.Right.Right, order, nil)
-               n.Right.Right.Right = ordercheapexpr(n.Right.Right.Right, order)
+               low, high, max := n.SliceBounds()
+               low = orderexpr(low, order, nil)
+               low = ordercheapexpr(low, order)
+               high = orderexpr(high, order, nil)
+               high = ordercheapexpr(high, order)
+               max = orderexpr(max, order, nil)
+               max = ordercheapexpr(max, order)
+               n.SetSliceBounds(low, high, max)
                if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
                        n = ordercopyexpr(n, n.Type, order, 0)
                }
index 766f352d3394ffb720492235ac7e3ac4ed5000aa..97a18497ff826a4f0e4a6325108d0e2285f0283a 100644 (file)
@@ -1408,20 +1408,17 @@ loop:
                                }
                                x = Nod(OINDEX, x, i)
                        case 1:
-                               i := index[0]
-                               j := index[1]
-                               x = Nod(OSLICE, x, Nod(OKEY, i, j))
+                               x = Nod(OSLICE, x, nil)
+                               x.SetSliceBounds(index[0], index[1], nil)
                        case 2:
-                               i := index[0]
-                               j := index[1]
-                               k := index[2]
-                               if j == nil {
+                               if index[1] == nil {
                                        Yyerror("middle index required in 3-index slice")
                                }
-                               if k == nil {
+                               if index[2] == nil {
                                        Yyerror("final index required in 3-index slice")
                                }
-                               x = Nod(OSLICE3, x, Nod(OKEY, i, Nod(OKEY, j, k)))
+                               x = Nod(OSLICE3, x, nil)
+                               x.SetSliceBounds(index[0], index[1], index[2])
 
                        default:
                                panic("unreachable")
index a8a5e92485a8626e8114daca5cf42f487f3ecd37..620bcb34a365116f1b742a79c7ba46a4c6fc43ef 100644 (file)
@@ -307,7 +307,11 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
 
        case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR:
                instrumentnode(&n.Left, init, 0, 0)
-               instrumentnode(&n.Right, init, 0, 0)
+               low, high, max := n.SliceBounds()
+               instrumentnode(&low, init, 0, 0)
+               instrumentnode(&high, init, 0, 0)
+               instrumentnode(&max, init, 0, 0)
+               n.SetSliceBounds(low, high, max)
                goto ret
 
        case OKEY:
index cb4385551482877f1444b16b714003080f767911..c6f2acffbfcbb1035d1a058539e00f5b731ff226 100644 (file)
@@ -727,7 +727,7 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
                arraylit(ctxt, 2, n, vstat, init)
 
                // copy static to slice
-               a := Nod(OSLICE, vstat, Nod(OKEY, nil, nil))
+               a := Nod(OSLICE, vstat, nil)
 
                a = Nod(OAS, var_, a)
                a = typecheck(a, Etop)
@@ -851,7 +851,7 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
        }
 
        // make slice out of heap (6)
-       a = Nod(OAS, var_, Nod(OSLICE, vauto, Nod(OKEY, nil, nil)))
+       a = Nod(OAS, var_, Nod(OSLICE, vauto, nil))
 
        a = typecheck(a, Etop)
        a = orderstmtinplace(a)
@@ -1391,7 +1391,8 @@ func genAsInitNoCheck(n *Node, reportOnly bool) bool {
                fallthrough
 
        case OSLICEARR:
-               if nr.Right.Op != OKEY || nr.Right.Left != nil || nr.Right.Right != nil {
+               low, high, _ := nr.SliceBounds()
+               if low != nil || high != nil {
                        return false
                }
                nr = nr.Left
index e177ceda014c79d0660a77e003dd6801d3ea33cd..5c367c72681f17c3587c50d4199a8e33334a86e8 100644 (file)
@@ -736,14 +736,7 @@ func (s *state) stmt(n *Node) {
                if rhs != nil && (rhs.Op == OSLICE || rhs.Op == OSLICE3 || rhs.Op == OSLICESTR) && samesafeexpr(rhs.Left, n.Left) {
                        // We're assigning a slicing operation back to its source.
                        // Don't write back fields we aren't changing. See issue #14855.
-                       i := rhs.Right.Left
-                       var j, k *Node
-                       if rhs.Op == OSLICE3 {
-                               j = rhs.Right.Right.Left
-                               k = rhs.Right.Right.Right
-                       } else {
-                               j = rhs.Right.Right
-                       }
+                       i, j, k := rhs.SliceBounds()
                        if i != nil && (i.Op == OLITERAL && i.Val().Ctype() == CTINT && i.Int64() == 0) {
                                // [0:...] is the same as [:...]
                                i = nil
@@ -2038,38 +2031,34 @@ func (s *state) expr(n *Node) *ssa.Value {
                }
                return s.newValue2(ssa.OpIMake, n.Type, tab, data)
 
-       case OSLICE, OSLICEARR:
+       case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR:
                v := s.expr(n.Left)
-               var i, j *ssa.Value
-               if n.Right.Left != nil {
-                       i = s.extendIndex(s.expr(n.Right.Left))
+               var i, j, k *ssa.Value
+               low, high, max := n.SliceBounds()
+               if low != nil {
+                       i = s.extendIndex(s.expr(low))
                }
-               if n.Right.Right != nil {
-                       j = s.extendIndex(s.expr(n.Right.Right))
+               if high != nil {
+                       j = s.extendIndex(s.expr(high))
                }
-               p, l, c := s.slice(n.Left.Type, v, i, j, nil)
+               if max != nil {
+                       k = s.extendIndex(s.expr(max))
+               }
+               p, l, c := s.slice(n.Left.Type, v, i, j, k)
                return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
+
        case OSLICESTR:
                v := s.expr(n.Left)
                var i, j *ssa.Value
-               if n.Right.Left != nil {
-                       i = s.extendIndex(s.expr(n.Right.Left))
+               low, high, _ := n.SliceBounds()
+               if low != nil {
+                       i = s.extendIndex(s.expr(low))
                }
-               if n.Right.Right != nil {
-                       j = s.extendIndex(s.expr(n.Right.Right))
+               if high != nil {
+                       j = s.extendIndex(s.expr(high))
                }
                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))
-               }
-               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:
                if isIntrinsicCall1(n) {
index 5fc16858d98070e05fc10e63bf2b68fd341fcf82..38f21eb585318e1f9a6a256083d99748c0cee661 100644 (file)
@@ -1024,6 +1024,68 @@ func Is64(t *Type) bool {
        return false
 }
 
+// SliceBounds returns n's slice bounds: low, high, and max in expr[low:high:max].
+// n must be a slice expression. max is nil if n is a simple slice expression.
+func (n *Node) SliceBounds() (low, high, max *Node) {
+       switch n.Op {
+       case OSLICE, OSLICEARR, OSLICESTR:
+               if n.Right == nil {
+                       return nil, nil, nil
+               }
+               if n.Right.Op != OKEY {
+                       Fatalf("SliceBounds right %s", opnames[n.Right.Op])
+               }
+               return n.Right.Left, n.Right.Right, nil
+       case OSLICE3, OSLICE3ARR:
+               if n.Right.Op != OKEY || n.Right.Right.Op != OKEY {
+                       Fatalf("SliceBounds right %s %s", opnames[n.Right.Op], opnames[n.Right.Right.Op])
+               }
+               return n.Right.Left, n.Right.Right.Left, n.Right.Right.Right
+       }
+       Fatalf("SliceBounds op %s: %v", n.Op, n)
+       return nil, nil, nil
+}
+
+// SetSliceBounds sets n's slice bounds, where n is a slice expression.
+// n must be a slice expression. If max is non-nil, n must be a full slice expression.
+func (n *Node) SetSliceBounds(low, high, max *Node) {
+       switch n.Op {
+       case OSLICE, OSLICEARR, OSLICESTR:
+               if max != nil {
+                       Fatalf("SetSliceBounds %s given three bounds", n.Op)
+               }
+               if n.Right == nil {
+                       n.Right = Nod(OKEY, low, high)
+                       return
+               }
+               n.Right.Left = low
+               n.Right.Right = high
+               return
+       case OSLICE3, OSLICE3ARR:
+               if n.Right == nil {
+                       n.Right = Nod(OKEY, low, Nod(OKEY, high, max))
+               }
+               n.Right.Left = low
+               n.Right.Right.Left = high
+               n.Right.Right.Right = max
+               return
+       }
+       Fatalf("SetSliceBounds op %s: %v", n.Op, n)
+}
+
+// IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR).
+// o must be a slicing op.
+func (o Op) IsSlice3() bool {
+       switch o {
+       case OSLICE, OSLICEARR, OSLICESTR:
+               return false
+       case OSLICE3, OSLICE3ARR:
+               return true
+       }
+       Fatalf("IsSlice3 op %v", o)
+       return false
+}
+
 // Is a conversion between t1 and t2 a no-op?
 func Noconv(t1 *Type, t2 *Type) bool {
        e1 := Simtype[t1.Etype]
index 49b991c5a50d5fd3820f69e964525a5a87dacb3f..8860c5d803d2c084f0021b2f42f0fc530f15b86e 100644 (file)
@@ -1105,14 +1105,19 @@ OpSwitch:
                n.Type = nil
                break OpSwitch
 
-       case OSLICE:
+       case OSLICE, OSLICE3:
                ok |= Erv
                n.Left = typecheck(n.Left, top)
-               n.Right.Left = typecheck(n.Right.Left, Erv)
-               n.Right.Right = typecheck(n.Right.Right, Erv)
+               low, high, max := n.SliceBounds()
+               hasmax := n.Op.IsSlice3()
+               low = typecheck(low, Erv)
+               high = typecheck(high, Erv)
+               max = typecheck(max, Erv)
                n.Left = defaultlit(n.Left, nil)
-               n.Right.Left = indexlit(n.Right.Left)
-               n.Right.Right = indexlit(n.Right.Right)
+               low = indexlit(low)
+               high = indexlit(high)
+               max = indexlit(max)
+               n.SetSliceBounds(low, high, max)
                l := n.Left
                if l.Type.IsArray() {
                        if !islvalue(n.Left) {
@@ -1134,78 +1139,22 @@ OpSwitch:
                }
                var tp *Type
                if t.IsString() {
+                       if hasmax {
+                               Yyerror("invalid operation %v (3-index slice of string)", n)
+                               n.Type = nil
+                               return n
+                       }
                        n.Type = t
                        n.Op = OSLICESTR
                } else if t.IsPtr() && t.Elem().IsArray() {
                        tp = t.Elem()
                        n.Type = typSlice(tp.Elem())
                        dowidth(n.Type)
-                       n.Op = OSLICEARR
-               } else if t.IsSlice() {
-                       n.Type = t
-               } else {
-                       Yyerror("cannot slice %v (type %v)", l, t)
-                       n.Type = nil
-                       return n
-               }
-
-               lo := n.Right.Left
-               if lo != nil && !checksliceindex(l, lo, tp) {
-                       n.Type = nil
-                       return n
-               }
-               hi := n.Right.Right
-               if hi != nil && !checksliceindex(l, hi, tp) {
-                       n.Type = nil
-                       return n
-               }
-               if !checksliceconst(lo, hi) {
-                       n.Type = nil
-                       return n
-               }
-               break OpSwitch
-
-       case OSLICE3:
-               ok |= Erv
-               n.Left = typecheck(n.Left, top)
-               n.Right.Left = typecheck(n.Right.Left, Erv)
-               n.Right.Right.Left = typecheck(n.Right.Right.Left, Erv)
-               n.Right.Right.Right = typecheck(n.Right.Right.Right, Erv)
-               n.Left = defaultlit(n.Left, nil)
-               n.Right.Left = indexlit(n.Right.Left)
-               n.Right.Right.Left = indexlit(n.Right.Right.Left)
-               n.Right.Right.Right = indexlit(n.Right.Right.Right)
-               l := n.Left
-               if l.Type.IsArray() {
-                       if !islvalue(n.Left) {
-                               Yyerror("invalid operation %v (slice of unaddressable value)", n)
-                               n.Type = nil
-                               return n
+                       if hasmax {
+                               n.Op = OSLICE3ARR
+                       } else {
+                               n.Op = OSLICEARR
                        }
-
-                       n.Left = Nod(OADDR, n.Left, nil)
-                       n.Left.Implicit = true
-                       n.Left = typecheck(n.Left, Erv)
-                       l = n.Left
-               }
-
-               t := l.Type
-               if t == nil {
-                       n.Type = nil
-                       return n
-               }
-               if t.IsString() {
-                       Yyerror("invalid operation %v (3-index slice of string)", n)
-                       n.Type = nil
-                       return n
-               }
-
-               var tp *Type
-               if t.IsPtr() && t.Elem().IsArray() {
-                       tp = t.Elem()
-                       n.Type = typSlice(tp.Elem())
-                       dowidth(n.Type)
-                       n.Op = OSLICE3ARR
                } else if t.IsSlice() {
                        n.Type = t
                } else {
@@ -1214,22 +1163,19 @@ OpSwitch:
                        return n
                }
 
-               lo := n.Right.Left
-               if lo != nil && !checksliceindex(l, lo, tp) {
+               if low != nil && !checksliceindex(l, low, tp) {
                        n.Type = nil
                        return n
                }
-               mid := n.Right.Right.Left
-               if mid != nil && !checksliceindex(l, mid, tp) {
+               if high != nil && !checksliceindex(l, high, tp) {
                        n.Type = nil
                        return n
                }
-               hi := n.Right.Right.Right
-               if hi != nil && !checksliceindex(l, hi, tp) {
+               if max != nil && !checksliceindex(l, max, tp) {
                        n.Type = nil
                        return n
                }
-               if !checksliceconst(lo, hi) || !checksliceconst(lo, mid) || !checksliceconst(mid, hi) {
+               if !checksliceconst(low, high) || !checksliceconst(low, max) || !checksliceconst(high, max) {
                        n.Type = nil
                        return n
                }
index 04ccfad9710bbe5de8b736fdb21794ee9b8dc668..e8fee67d05c4da5413a6ae8dea55351dcb4cd7f8 100644 (file)
@@ -1241,35 +1241,28 @@ opswitch:
        case ORECV:
                Fatalf("walkexpr ORECV") // should see inside OAS only
 
-       case OSLICE, OSLICEARR, OSLICESTR:
+       case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
                n.Left = walkexpr(n.Left, init)
-               n.Right.Left = walkexpr(n.Right.Left, init)
-               if n.Right.Left != nil && iszero(n.Right.Left) {
-                       // Reduce x[0:j] to x[:j].
-                       n.Right.Left = nil
-               }
-               n.Right.Right = walkexpr(n.Right.Right, init)
-               n = reduceSlice(n)
-
-       case OSLICE3, OSLICE3ARR:
-               n.Left = walkexpr(n.Left, init)
-               n.Right.Left = walkexpr(n.Right.Left, init)
-               if n.Right.Left != nil && iszero(n.Right.Left) {
-                       // Reduce x[0:j:k] to x[:j:k].
-                       n.Right.Left = nil
-               }
-               n.Right.Right.Left = walkexpr(n.Right.Right.Left, init)
-               n.Right.Right.Right = walkexpr(n.Right.Right.Right, init)
-
-               r := n.Right.Right.Right
-               if r != nil && r.Op == OCAP && samesafeexpr(n.Left, r.Left) {
-                       // Reduce x[i:j:cap(x)] to x[i:j].
-                       n.Right.Right = n.Right.Right.Left
-                       if n.Op == OSLICE3 {
-                               n.Op = OSLICE
-                       } else {
-                               n.Op = OSLICEARR
+               low, high, max := n.SliceBounds()
+               low = walkexpr(low, init)
+               if low != nil && iszero(low) {
+                       // Reduce x[0:j] to x[:j] and x[0:j:k] to x[:j:k].
+                       low = nil
+               }
+               high = walkexpr(high, init)
+               max = walkexpr(max, init)
+               n.SetSliceBounds(low, high, max)
+               if n.Op.IsSlice3() {
+                       if max != nil && max.Op == OCAP && samesafeexpr(n.Left, max.Left) {
+                               // Reduce x[i:j:cap(x)] to x[i:j].
+                               if n.Op == OSLICE3 {
+                                       n.Op = OSLICE
+                               } else {
+                                       n.Op = OSLICEARR
+                               }
+                               n = reduceSlice(n)
                        }
+               } else {
                        n = reduceSlice(n)
                }
 
@@ -1425,8 +1418,9 @@ opswitch:
                        a := Nod(OAS, var_, nil) // zero temp
                        a = typecheck(a, Etop)
                        init.Append(a)
-                       r := Nod(OSLICE, var_, Nod(OKEY, nil, l)) // arr[:l]
-                       r = conv(r, n.Type)                       // in case n.Type is named.
+                       r := Nod(OSLICE, var_, nil) // arr[:l]
+                       r.SetSliceBounds(nil, l, nil)
+                       r = conv(r, n.Type) // in case n.Type is named.
                        r = typecheck(r, Erv)
                        r = walkexpr(r, init)
                        n = r
@@ -1596,13 +1590,15 @@ opswitch:
        return n
 }
 
+// TODO(josharian): combine this with its caller and simplify
 func reduceSlice(n *Node) *Node {
-       r := n.Right.Right
-       if r != nil && r.Op == OLEN && samesafeexpr(n.Left, r.Left) {
+       low, high, max := n.SliceBounds()
+       if high != nil && high.Op == OLEN && samesafeexpr(n.Left, high.Left) {
                // Reduce x[i:len(x)] to x[i:].
-               n.Right.Right = nil
+               high = nil
        }
-       if (n.Op == OSLICE || n.Op == OSLICESTR) && n.Right.Left == nil && n.Right.Right == nil {
+       n.SetSliceBounds(low, high, max)
+       if (n.Op == OSLICE || n.Op == OSLICESTR) && low == nil && high == nil {
                // Reduce x[:] to x.
                if Debug_slice > 0 {
                        Warn("slice: omit slice operation")
@@ -2816,14 +2812,15 @@ func appendslice(n *Node, init *Nodes) *Node {
        l = append(l, nif)
 
        // s = s[:n]
-       nt := Nod(OSLICE, s, Nod(OKEY, nil, nn))
+       nt := Nod(OSLICE, s, nil)
+       nt.SetSliceBounds(nil, nn, nil)
        nt.Etype = 1
        l = append(l, Nod(OAS, s, nt))
 
        if haspointers(l1.Type.Elem()) {
                // copy(s[len(l1):], l2)
-               nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), nil))
-
+               nptr1 := Nod(OSLICE, s, nil)
+               nptr1.SetSliceBounds(Nod(OLEN, l1, nil), nil, nil)
                nptr1.Etype = 1
                nptr2 := l2
                fn := syslook("typedslicecopy")
@@ -2835,8 +2832,8 @@ func appendslice(n *Node, init *Nodes) *Node {
        } else if instrumenting {
                // rely on runtime to instrument copy.
                // copy(s[len(l1):], l2)
-               nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), nil))
-
+               nptr1 := Nod(OSLICE, s, nil)
+               nptr1.SetSliceBounds(Nod(OLEN, l1, nil), nil, nil)
                nptr1.Etype = 1
                nptr2 := l2
                var fn *Node
@@ -2950,7 +2947,8 @@ func walkappend(n *Node, init *Nodes, dst *Node) *Node {
        nn := temp(Types[TINT])
        l = append(l, Nod(OAS, nn, Nod(OLEN, ns, nil))) // n = len(s)
 
-       nx = Nod(OSLICE, ns, Nod(OKEY, nil, Nod(OADD, nn, na))) // ...s[:n+argc]
+       nx = Nod(OSLICE, ns, nil) // ...s[:n+argc]
+       nx.SetSliceBounds(nil, Nod(OADD, nn, na), nil)
        nx.Etype = 1
        l = append(l, Nod(OAS, ns, nx)) // s = s[:n+argc]