Commit f12bd8a5 authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

cmd/compile: encapsulate OSLICE* representation

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/22425Reviewed-by: default avatarMatthew Dempsky <mdempsky@google.com>
parent 2bf7034d
......@@ -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)
......
......@@ -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()})
......
......@@ -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.
......
......@@ -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)
......
......@@ -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)
......
......@@ -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)
}
......
......@@ -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")
......
......@@ -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:
......
......@@ -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
......
......@@ -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) {
......
......@@ -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]
......
......@@ -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
}
......
......@@ -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]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment