]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/gc: remove OCMPIFACE and OCMPSTR
authorMatthew Dempsky <mdempsky@google.com>
Wed, 10 Oct 2018 23:47:47 +0000 (16:47 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Thu, 11 Oct 2018 21:18:33 +0000 (21:18 +0000)
Interface and string comparisons don't need separate Ops any more than
struct or array comparisons do.

Removing them requires shuffling some code around in walk (and a
little in order), but overall allows simplifying things a bit.

Passes toolstash-check.

Change-Id: I084b8a6c089b768dc76d220379f4daed8a35db15
Reviewed-on: https://go-review.googlesource.com/c/141637
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/compile/internal/gc/const.go
src/cmd/compile/internal/gc/fmt.go
src/cmd/compile/internal/gc/iexport.go
src/cmd/compile/internal/gc/iimport.go
src/cmd/compile/internal/gc/order.go
src/cmd/compile/internal/gc/syntax.go
src/cmd/compile/internal/gc/typecheck.go
src/cmd/compile/internal/gc/walk.go

index e60e05df04530d5d3604d4e43adc9b6d8b3370f8..3c542aafae80aaafb2e0b2b08b8569e090375978 100644 (file)
@@ -1058,9 +1058,7 @@ func idealkind(n *Node) Ctype {
                OLT,
                ONE,
                ONOT,
-               OOROR,
-               OCMPSTR,
-               OCMPIFACE:
+               OOROR:
                return CTBOOL
 
                // shifts (beware!).
index 28e9b9b6dcc95166c662dbad6434217d6041a25e..23ed3f7844cfa33720d2d2a970dcb58111241383 100644 (file)
@@ -1146,8 +1146,6 @@ var opprec = []int{
        OGE:           4,
        OGT:           4,
        ONE:           4,
-       OCMPSTR:       4,
-       OCMPIFACE:     4,
        OSEND:         3,
        OANDAND:       2,
        OOROR:         1,
@@ -1507,11 +1505,6 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
                        n1.exprfmt(s, nprec, mode)
                }
 
-       case OCMPSTR, OCMPIFACE:
-               n.Left.exprfmt(s, nprec, mode)
-               mode.Fprintf(s, " %#v ", n.SubOp())
-               n.Right.exprfmt(s, nprec+1, mode)
-
        default:
                mode.Fprintf(s, "<node %v>", n.Op)
        }
index d90c97ad922d370bb453378d4b120e33a9547e31..b141e5fc09a91bb9808faf44c12dc1c98933e6c1 100644 (file)
@@ -1319,12 +1319,6 @@ func (w *exportWriter) expr(n *Node) {
                w.pos(n.Pos)
                w.exprList(n.List)
 
-       case OCMPSTR, OCMPIFACE:
-               w.op(n.SubOp())
-               w.pos(n.Pos)
-               w.expr(n.Left)
-               w.expr(n.Right)
-
        case ODCLCONST:
                // if exporting, DCLCONST should just be removed as its usage
                // has already been replaced with literals
index 6f0fd6b6d27694593704ac86b94bbdf689030ec5..4fea314263e7186b0a45250ee609d60f09ed6192 100644 (file)
@@ -935,9 +935,6 @@ func (r *importReader) node() *Node {
                }
                return x
 
-       // case OCMPSTR, OCMPIFACE:
-       //      unreachable - mapped to std comparison operators by exporter
-
        // --------------------------------------------------------------------
        // statements
        case ODCL:
index 1e22ecfcdf8d870a75eed9eca466c4481ad3eec1..8afb136515b87fca7862223bfc0068e739083a72 100644 (file)
@@ -1010,20 +1010,6 @@ func (o *Order) expr(n, lhs *Node) *Node {
                        }
                }
 
-       case OCMPSTR:
-               n.Left = o.expr(n.Left, nil)
-               n.Right = o.expr(n.Right, nil)
-
-               // Mark string(byteSlice) arguments to reuse byteSlice backing
-               // buffer during conversion. String comparison does not
-               // memorize the strings for later use, so it is safe.
-               if n.Left.Op == OARRAYBYTESTR {
-                       n.Left.Op = OARRAYBYTESTRTMP
-               }
-               if n.Right.Op == OARRAYBYTESTR {
-                       n.Right.Op = OARRAYBYTESTRTMP
-               }
-
                // key must be addressable
        case OINDEXMAP:
                n.Left = o.expr(n.Left, nil)
@@ -1181,11 +1167,24 @@ func (o *Order) expr(n, lhs *Node) *Node {
                n.Left = o.expr(n.Left, nil)
                n = o.copyExpr(n, n.Type, true)
 
-       case OEQ, ONE:
+       case OEQ, ONE, OLT, OLE, OGT, OGE:
                n.Left = o.expr(n.Left, nil)
                n.Right = o.expr(n.Right, nil)
+
                t := n.Left.Type
-               if t.IsStruct() || t.IsArray() {
+               switch {
+               case t.IsString():
+                       // Mark string(byteSlice) arguments to reuse byteSlice backing
+                       // buffer during conversion. String comparison does not
+                       // memorize the strings for later use, so it is safe.
+                       if n.Left.Op == OARRAYBYTESTR {
+                               n.Left.Op = OARRAYBYTESTRTMP
+                       }
+                       if n.Right.Op == OARRAYBYTESTR {
+                               n.Right.Op = OARRAYBYTESTRTMP
+                       }
+
+               case t.IsStruct() || t.IsArray():
                        // for complex comparisons, we need both args to be
                        // addressable so we can pass them to the runtime.
                        n.Left = o.addrTemp(n.Left)
index ab65ddebb4cb2190348f60b6c8c1776168b65ba1..1368d5edb874029c47c772437758b45c564074f6 100644 (file)
@@ -65,7 +65,7 @@ func (n *Node) ResetAux() {
 
 func (n *Node) SubOp() Op {
        switch n.Op {
-       case OASOP, OCMPIFACE, OCMPSTR, ONAME:
+       case OASOP, ONAME:
        default:
                Fatalf("unexpected op: %v", n.Op)
        }
@@ -74,7 +74,7 @@ func (n *Node) SubOp() Op {
 
 func (n *Node) SetSubOp(op Op) {
        switch n.Op {
-       case OASOP, OCMPIFACE, OCMPSTR, ONAME:
+       case OASOP, ONAME:
        default:
                Fatalf("unexpected op: %v", n.Op)
        }
@@ -610,8 +610,8 @@ const (
        OCAP             // cap(Left)
        OCLOSE           // close(Left)
        OCLOSURE         // func Type { Body } (func literal)
-       OCMPIFACE        // Left Etype Right (interface comparison, x == y or x != y)
-       OCMPSTR          // Left Etype Right (string comparison, x == y, x < y, etc)
+       _                // toolstash kludge; was OCMPIFACE
+       _                // toolstash kludge; was OCMPSTR
        OCOMPLIT         // Right{List} (composite literal, not yet lowered to specific form)
        OMAPLIT          // Type{List} (composite literal, Type is map)
        OSTRUCTLIT       // Type{List} (composite literal, Type is struct)
index 897dd710b9e69804d3180d62aee3e13000da869f..cfdd88d45ebbef0c5599cf851d335fee10148fb9 100644 (file)
@@ -747,43 +747,22 @@ func typecheck1(n *Node, top int) *Node {
                        }
                }
 
-               if et == TSTRING {
-                       if iscmp[n.Op] {
-                               ot := n.Op
-                               n.Op = OCMPSTR
-                               n.SetSubOp(ot)
-                       } else if n.Op == OADD {
-                               // create OADDSTR node with list of strings in x + y + z + (w + v) + ...
-                               n.Op = OADDSTR
-
-                               if l.Op == OADDSTR {
-                                       n.List.Set(l.List.Slice())
-                               } else {
-                                       n.List.Set1(l)
-                               }
-                               if r.Op == OADDSTR {
-                                       n.List.AppendNodes(&r.List)
-                               } else {
-                                       n.List.Append(r)
-                               }
-                               n.Left = nil
-                               n.Right = nil
-                       }
-               }
+               if et == TSTRING && n.Op == OADD {
+                       // create OADDSTR node with list of strings in x + y + z + (w + v) + ...
+                       n.Op = OADDSTR
 
-               if et == TINTER {
-                       if l.Op == OLITERAL && l.Val().Ctype() == CTNIL {
-                               // swap for back end
-                               n.Left = r
-
-                               n.Right = l
-                       } else if r.Op == OLITERAL && r.Val().Ctype() == CTNIL {
-                       } else // leave alone for back end
-                       if r.Type.IsInterface() == l.Type.IsInterface() {
-                               ot := n.Op
-                               n.Op = OCMPIFACE
-                               n.SetSubOp(ot)
+                       if l.Op == OADDSTR {
+                               n.List.Set(l.List.Slice())
+                       } else {
+                               n.List.Set1(l)
+                       }
+                       if r.Op == OADDSTR {
+                               n.List.AppendNodes(&r.List)
+                       } else {
+                               n.List.Append(r)
                        }
+                       n.Left = nil
+                       n.Right = nil
                }
 
                if (op == ODIV || op == OMOD) && Isconst(r, CTINT) {
index c3201c140494be54ccadd57f0b868b0df047378c..6b9ec51203b742fe5e171bd156376145decf6aa6 100644 (file)
@@ -514,7 +514,7 @@ opswitch:
                OIND, OSPTR, OITAB, OIDATA, OADDR:
                n.Left = walkexpr(n.Left, init)
 
-       case OEFACE, OAND, OSUB, OMUL, OLT, OLE, OGE, OGT, OADD, OOR, OXOR:
+       case OEFACE, OAND, OSUB, OMUL, OADD, OOR, OXOR:
                n.Left = walkexpr(n.Left, init)
                n.Right = walkexpr(n.Right, init)
 
@@ -584,19 +584,8 @@ opswitch:
                n.Left = walkexpr(n.Left, init)
                n.Right = walkexpr(n.Right, init)
 
-       case OEQ, ONE:
-               n.Left = walkexpr(n.Left, init)
-               n.Right = walkexpr(n.Right, init)
-
-               // Disable safemode while compiling this code: the code we
-               // generate internally can refer to unsafe.Pointer.
-               // In this case it can happen if we need to generate an ==
-               // for a struct containing a reflect.Value, which itself has
-               // an unexported field of type unsafe.Pointer.
-               old_safemode := safemode
-               safemode = false
+       case OEQ, ONE, OLT, OLE, OGT, OGE:
                n = walkcompare(n, init)
-               safemode = old_safemode
 
        case OANDAND, OOROR:
                n.Left = walkexpr(n.Left, init)
@@ -1218,149 +1207,6 @@ opswitch:
                        n = callnew(n.Type.Elem())
                }
 
-       case OCMPSTR:
-               // s + "badgerbadgerbadger" == "badgerbadgerbadger"
-               if (n.SubOp() == OEQ || n.SubOp() == ONE) && Isconst(n.Right, CTSTR) && n.Left.Op == OADDSTR && n.Left.List.Len() == 2 && Isconst(n.Left.List.Second(), CTSTR) && strlit(n.Right) == strlit(n.Left.List.Second()) {
-                       r := nod(n.SubOp(), nod(OLEN, n.Left.List.First(), nil), nodintconst(0))
-                       n = finishcompare(n, r, init)
-                       break
-               }
-
-               // Rewrite comparisons to short constant strings as length+byte-wise comparisons.
-               var cs, ncs *Node // const string, non-const string
-               switch {
-               case Isconst(n.Left, CTSTR) && Isconst(n.Right, CTSTR):
-                       // ignore; will be constant evaluated
-               case Isconst(n.Left, CTSTR):
-                       cs = n.Left
-                       ncs = n.Right
-               case Isconst(n.Right, CTSTR):
-                       cs = n.Right
-                       ncs = n.Left
-               }
-               if cs != nil {
-                       cmp := n.SubOp()
-                       // Our comparison below assumes that the non-constant string
-                       // is on the left hand side, so rewrite "" cmp x to x cmp "".
-                       // See issue 24817.
-                       if Isconst(n.Left, CTSTR) {
-                               cmp = brrev(cmp)
-                       }
-
-                       // maxRewriteLen was chosen empirically.
-                       // It is the value that minimizes cmd/go file size
-                       // across most architectures.
-                       // See the commit description for CL 26758 for details.
-                       maxRewriteLen := 6
-                       // Some architectures can load unaligned byte sequence as 1 word.
-                       // So we can cover longer strings with the same amount of code.
-                       canCombineLoads := canMergeLoads()
-                       combine64bit := false
-                       if canCombineLoads {
-                               // Keep this low enough to generate less code than a function call.
-                               maxRewriteLen = 2 * thearch.LinkArch.RegSize
-                               combine64bit = thearch.LinkArch.RegSize >= 8
-                       }
-
-                       var and Op
-                       switch cmp {
-                       case OEQ:
-                               and = OANDAND
-                       case ONE:
-                               and = OOROR
-                       default:
-                               // Don't do byte-wise comparisons for <, <=, etc.
-                               // They're fairly complicated.
-                               // Length-only checks are ok, though.
-                               maxRewriteLen = 0
-                       }
-                       if s := cs.Val().U.(string); len(s) <= maxRewriteLen {
-                               if len(s) > 0 {
-                                       ncs = safeexpr(ncs, init)
-                               }
-                               r := nod(cmp, nod(OLEN, ncs, nil), nodintconst(int64(len(s))))
-                               remains := len(s)
-                               for i := 0; remains > 0; {
-                                       if remains == 1 || !canCombineLoads {
-                                               cb := nodintconst(int64(s[i]))
-                                               ncb := nod(OINDEX, ncs, nodintconst(int64(i)))
-                                               r = nod(and, r, nod(cmp, ncb, cb))
-                                               remains--
-                                               i++
-                                               continue
-                                       }
-                                       var step int
-                                       var convType *types.Type
-                                       switch {
-                                       case remains >= 8 && combine64bit:
-                                               convType = types.Types[TINT64]
-                                               step = 8
-                                       case remains >= 4:
-                                               convType = types.Types[TUINT32]
-                                               step = 4
-                                       case remains >= 2:
-                                               convType = types.Types[TUINT16]
-                                               step = 2
-                                       }
-                                       ncsubstr := nod(OINDEX, ncs, nodintconst(int64(i)))
-                                       ncsubstr = conv(ncsubstr, convType)
-                                       csubstr := int64(s[i])
-                                       // Calculate large constant from bytes as sequence of shifts and ors.
-                                       // Like this:  uint32(s[0]) | uint32(s[1])<<8 | uint32(s[2])<<16 ...
-                                       // ssa will combine this into a single large load.
-                                       for offset := 1; offset < step; offset++ {
-                                               b := nod(OINDEX, ncs, nodintconst(int64(i+offset)))
-                                               b = conv(b, convType)
-                                               b = nod(OLSH, b, nodintconst(int64(8*offset)))
-                                               ncsubstr = nod(OOR, ncsubstr, b)
-                                               csubstr |= int64(s[i+offset]) << uint8(8*offset)
-                                       }
-                                       csubstrPart := nodintconst(csubstr)
-                                       // Compare "step" bytes as once
-                                       r = nod(and, r, nod(cmp, csubstrPart, ncsubstr))
-                                       remains -= step
-                                       i += step
-                               }
-                               n = finishcompare(n, r, init)
-                               break
-                       }
-               }
-
-               var r *Node
-               if n.SubOp() == OEQ || n.SubOp() == ONE {
-                       // prepare for rewrite below
-                       n.Left = cheapexpr(n.Left, init)
-                       n.Right = cheapexpr(n.Right, init)
-
-                       lstr := conv(n.Left, types.Types[TSTRING])
-                       rstr := conv(n.Right, types.Types[TSTRING])
-                       lptr := nod(OSPTR, lstr, nil)
-                       rptr := nod(OSPTR, rstr, nil)
-                       llen := conv(nod(OLEN, lstr, nil), types.Types[TUINTPTR])
-                       rlen := conv(nod(OLEN, rstr, nil), types.Types[TUINTPTR])
-
-                       fn := syslook("memequal")
-                       fn = substArgTypes(fn, types.Types[TUINT8], types.Types[TUINT8])
-                       r = mkcall1(fn, types.Types[TBOOL], init, lptr, rptr, llen)
-
-                       // quick check of len before full compare for == or !=.
-                       // memequal then tests equality up to length len.
-                       if n.SubOp() == OEQ {
-                               // len(left) == len(right) && memequal(left, right, len)
-                               r = nod(OANDAND, nod(OEQ, llen, rlen), r)
-                       } else {
-                               // len(left) != len(right) || !memequal(left, right, len)
-                               r = nod(ONOT, r, nil)
-                               r = nod(OOROR, nod(ONE, llen, rlen), r)
-                       }
-               } else {
-                       // sys_cmpstring(s1, s2) :: 0
-                       r = mkcall("cmpstring", types.Types[TINT], init, conv(n.Left, types.Types[TSTRING]), conv(n.Right, types.Types[TSTRING]))
-                       r = nod(n.SubOp(), r, nodintconst(0))
-               }
-
-               n = finishcompare(n, r, init)
-
        case OADDSTR:
                n = addstr(n, init)
 
@@ -1658,40 +1504,6 @@ opswitch:
 
                n = mkcall("stringtoslicerune", n.Type, init, a, conv(n.Left, types.Types[TSTRING]))
 
-               // ifaceeq(i1 any-1, i2 any-2) (ret bool);
-       case OCMPIFACE:
-               if !eqtype(n.Left.Type, n.Right.Type) {
-                       Fatalf("ifaceeq %v %v %v", n.Op, n.Left.Type, n.Right.Type)
-               }
-               var fn *Node
-               if n.Left.Type.IsEmptyInterface() {
-                       fn = syslook("efaceeq")
-               } else {
-                       fn = syslook("ifaceeq")
-               }
-
-               n.Right = cheapexpr(n.Right, init)
-               n.Left = cheapexpr(n.Left, init)
-               lt := nod(OITAB, n.Left, nil)
-               rt := nod(OITAB, n.Right, nil)
-               ld := nod(OIDATA, n.Left, nil)
-               rd := nod(OIDATA, n.Right, nil)
-               ld.Type = types.Types[TUNSAFEPTR]
-               rd.Type = types.Types[TUNSAFEPTR]
-               ld.SetTypecheck(1)
-               rd.SetTypecheck(1)
-               call := mkcall1(fn, n.Type, init, lt, ld, rd)
-
-               // Check itable/type before full compare.
-               // Note: short-circuited because order matters.
-               var cmp *Node
-               if n.SubOp() == OEQ {
-                       cmp = nod(OANDAND, nod(OEQ, lt, rt), call)
-               } else {
-                       cmp = nod(OOROR, nod(ONE, lt, rt), nod(ONOT, call, nil))
-               }
-               n = finishcompare(n, cmp, init)
-
        case OARRAYLIT, OSLICELIT, OMAPLIT, OSTRUCTLIT, OPTRLIT:
                if isStaticCompositeLiteral(n) && !canSSAType(n.Type) {
                        // n can be directly represented in the read-only data section.
@@ -3390,7 +3202,7 @@ func eqfor(t *types.Type) (n *Node, needsize bool) {
        // Should only arrive here with large memory or
        // a struct/array containing a non-memory field/element.
        // Small memory is handled inline, and single non-memory
-       // is handled during type check (OCMPSTR etc).
+       // is handled by walkcompare.
        switch a, _ := algtype1(t); a {
        case AMEM:
                n := syslook("memequal")
@@ -3415,6 +3227,28 @@ func eqfor(t *types.Type) (n *Node, needsize bool) {
 // The result of walkcompare MUST be assigned back to n, e.g.
 //     n.Left = walkcompare(n.Left, init)
 func walkcompare(n *Node, init *Nodes) *Node {
+       if n.Left.Type.IsInterface() && n.Right.Type.IsInterface() && n.Left.Op != OLITERAL && n.Right.Op != OLITERAL {
+               return walkcompareInterface(n, init)
+       }
+
+       if n.Left.Type.IsString() && n.Right.Type.IsString() {
+               return walkcompareString(n, init)
+       }
+
+       n.Left = walkexpr(n.Left, init)
+       n.Right = walkexpr(n.Right, init)
+
+       // Disable safemode while compiling this code: the code we
+       // generate internally can refer to unsafe.Pointer.
+       // In this case it can happen if we need to generate an ==
+       // for a struct containing a reflect.Value, which itself has
+       // an unexported field of type unsafe.Pointer.
+       old_safemode := safemode
+       safemode = false
+       defer func() {
+               safemode = old_safemode
+       }()
+
        // Given interface value l and concrete value r, rewrite
        //   l == r
        // into types-equal && data-equal.
@@ -3627,6 +3461,183 @@ func walkcompare(n *Node, init *Nodes) *Node {
        return n
 }
 
+func walkcompareInterface(n *Node, init *Nodes) *Node {
+       // ifaceeq(i1 any-1, i2 any-2) (ret bool);
+       if !eqtype(n.Left.Type, n.Right.Type) {
+               Fatalf("ifaceeq %v %v %v", n.Op, n.Left.Type, n.Right.Type)
+       }
+       var fn *Node
+       if n.Left.Type.IsEmptyInterface() {
+               fn = syslook("efaceeq")
+       } else {
+               fn = syslook("ifaceeq")
+       }
+
+       n.Right = cheapexpr(n.Right, init)
+       n.Left = cheapexpr(n.Left, init)
+       lt := nod(OITAB, n.Left, nil)
+       rt := nod(OITAB, n.Right, nil)
+       ld := nod(OIDATA, n.Left, nil)
+       rd := nod(OIDATA, n.Right, nil)
+       ld.Type = types.Types[TUNSAFEPTR]
+       rd.Type = types.Types[TUNSAFEPTR]
+       ld.SetTypecheck(1)
+       rd.SetTypecheck(1)
+       call := mkcall1(fn, n.Type, init, lt, ld, rd)
+
+       // Check itable/type before full compare.
+       // Note: short-circuited because order matters.
+       var cmp *Node
+       if n.Op == OEQ {
+               cmp = nod(OANDAND, nod(OEQ, lt, rt), call)
+       } else {
+               cmp = nod(OOROR, nod(ONE, lt, rt), nod(ONOT, call, nil))
+       }
+       return finishcompare(n, cmp, init)
+}
+
+func walkcompareString(n *Node, init *Nodes) *Node {
+       // s + "badgerbadgerbadger" == "badgerbadgerbadger"
+       if (n.Op == OEQ || n.Op == ONE) && Isconst(n.Right, CTSTR) && n.Left.Op == OADDSTR && n.Left.List.Len() == 2 && Isconst(n.Left.List.Second(), CTSTR) && strlit(n.Right) == strlit(n.Left.List.Second()) {
+               r := nod(n.Op, nod(OLEN, n.Left.List.First(), nil), nodintconst(0))
+               return finishcompare(n, r, init)
+       }
+
+       // Rewrite comparisons to short constant strings as length+byte-wise comparisons.
+       var cs, ncs *Node // const string, non-const string
+       switch {
+       case Isconst(n.Left, CTSTR) && Isconst(n.Right, CTSTR):
+               // ignore; will be constant evaluated
+       case Isconst(n.Left, CTSTR):
+               cs = n.Left
+               ncs = n.Right
+       case Isconst(n.Right, CTSTR):
+               cs = n.Right
+               ncs = n.Left
+       }
+       if cs != nil {
+               cmp := n.Op
+               // Our comparison below assumes that the non-constant string
+               // is on the left hand side, so rewrite "" cmp x to x cmp "".
+               // See issue 24817.
+               if Isconst(n.Left, CTSTR) {
+                       cmp = brrev(cmp)
+               }
+
+               // maxRewriteLen was chosen empirically.
+               // It is the value that minimizes cmd/go file size
+               // across most architectures.
+               // See the commit description for CL 26758 for details.
+               maxRewriteLen := 6
+               // Some architectures can load unaligned byte sequence as 1 word.
+               // So we can cover longer strings with the same amount of code.
+               canCombineLoads := canMergeLoads()
+               combine64bit := false
+               if canCombineLoads {
+                       // Keep this low enough to generate less code than a function call.
+                       maxRewriteLen = 2 * thearch.LinkArch.RegSize
+                       combine64bit = thearch.LinkArch.RegSize >= 8
+               }
+
+               var and Op
+               switch cmp {
+               case OEQ:
+                       and = OANDAND
+               case ONE:
+                       and = OOROR
+               default:
+                       // Don't do byte-wise comparisons for <, <=, etc.
+                       // They're fairly complicated.
+                       // Length-only checks are ok, though.
+                       maxRewriteLen = 0
+               }
+               if s := cs.Val().U.(string); len(s) <= maxRewriteLen {
+                       if len(s) > 0 {
+                               ncs = safeexpr(ncs, init)
+                       }
+                       r := nod(cmp, nod(OLEN, ncs, nil), nodintconst(int64(len(s))))
+                       remains := len(s)
+                       for i := 0; remains > 0; {
+                               if remains == 1 || !canCombineLoads {
+                                       cb := nodintconst(int64(s[i]))
+                                       ncb := nod(OINDEX, ncs, nodintconst(int64(i)))
+                                       r = nod(and, r, nod(cmp, ncb, cb))
+                                       remains--
+                                       i++
+                                       continue
+                               }
+                               var step int
+                               var convType *types.Type
+                               switch {
+                               case remains >= 8 && combine64bit:
+                                       convType = types.Types[TINT64]
+                                       step = 8
+                               case remains >= 4:
+                                       convType = types.Types[TUINT32]
+                                       step = 4
+                               case remains >= 2:
+                                       convType = types.Types[TUINT16]
+                                       step = 2
+                               }
+                               ncsubstr := nod(OINDEX, ncs, nodintconst(int64(i)))
+                               ncsubstr = conv(ncsubstr, convType)
+                               csubstr := int64(s[i])
+                               // Calculate large constant from bytes as sequence of shifts and ors.
+                               // Like this:  uint32(s[0]) | uint32(s[1])<<8 | uint32(s[2])<<16 ...
+                               // ssa will combine this into a single large load.
+                               for offset := 1; offset < step; offset++ {
+                                       b := nod(OINDEX, ncs, nodintconst(int64(i+offset)))
+                                       b = conv(b, convType)
+                                       b = nod(OLSH, b, nodintconst(int64(8*offset)))
+                                       ncsubstr = nod(OOR, ncsubstr, b)
+                                       csubstr |= int64(s[i+offset]) << uint8(8*offset)
+                               }
+                               csubstrPart := nodintconst(csubstr)
+                               // Compare "step" bytes as once
+                               r = nod(and, r, nod(cmp, csubstrPart, ncsubstr))
+                               remains -= step
+                               i += step
+                       }
+                       return finishcompare(n, r, init)
+               }
+       }
+
+       var r *Node
+       if n.Op == OEQ || n.Op == ONE {
+               // prepare for rewrite below
+               n.Left = cheapexpr(n.Left, init)
+               n.Right = cheapexpr(n.Right, init)
+
+               lstr := conv(n.Left, types.Types[TSTRING])
+               rstr := conv(n.Right, types.Types[TSTRING])
+               lptr := nod(OSPTR, lstr, nil)
+               rptr := nod(OSPTR, rstr, nil)
+               llen := conv(nod(OLEN, lstr, nil), types.Types[TUINTPTR])
+               rlen := conv(nod(OLEN, rstr, nil), types.Types[TUINTPTR])
+
+               fn := syslook("memequal")
+               fn = substArgTypes(fn, types.Types[TUINT8], types.Types[TUINT8])
+               r = mkcall1(fn, types.Types[TBOOL], init, lptr, rptr, llen)
+
+               // quick check of len before full compare for == or !=.
+               // memequal then tests equality up to length len.
+               if n.Op == OEQ {
+                       // len(left) == len(right) && memequal(left, right, len)
+                       r = nod(OANDAND, nod(OEQ, llen, rlen), r)
+               } else {
+                       // len(left) != len(right) || !memequal(left, right, len)
+                       r = nod(ONOT, r, nil)
+                       r = nod(OOROR, nod(ONE, llen, rlen), r)
+               }
+       } else {
+               // sys_cmpstring(s1, s2) :: 0
+               r = mkcall("cmpstring", types.Types[TINT], init, conv(n.Left, types.Types[TSTRING]), conv(n.Right, types.Types[TSTRING]))
+               r = nod(n.Op, r, nodintconst(0))
+       }
+
+       return finishcompare(n, r, init)
+}
+
 // The result of finishcompare MUST be assigned back to n, e.g.
 //     n.Left = finishcompare(n.Left, x, r, init)
 func finishcompare(n, r *Node, init *Nodes) *Node {
@@ -3961,8 +3972,6 @@ func candiscard(n *Node) bool {
                OSTRARRAYBYTE,
                OSTRARRAYRUNE,
                OCAP,
-               OCMPIFACE,
-               OCMPSTR,
                OCOMPLIT,
                OMAPLIT,
                OSTRUCTLIT,