From: Russ Cox Date: Thu, 10 Dec 2020 23:45:35 +0000 (-0500) Subject: [dev.regabi] cmd/compile: cleanup for concrete types - order X-Git-Tag: go1.17beta1~1539^2~310 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=bf9bbbd6ed;p=gostls13.git [dev.regabi] cmd/compile: cleanup for concrete types - order An automated rewrite will add concrete type assertions after a test of n.Op(), when n can be safely type-asserted (meaning, n is not reassigned a different type, n is not reassigned and then used outside the scope of the type assertion, and so on). This sequence of CLs handles the code that the automated rewrite does not: adding specific types to function arguments, adjusting code not to call n.Left() etc when n may have multiple representations, and so on. This CL focuses on order.go. Passes buildall w/ toolstash -cmp. Change-Id: Ib5731905a620175a6fe978f512da593e0dae9d87 Reviewed-on: https://go-review.googlesource.com/c/go/+/277922 Trust: Russ Cox Reviewed-by: Matthew Dempsky --- diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index e0c0cabcde..b0a9c9be3e 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -139,7 +139,7 @@ func (o *Order) cheapExpr(n ir.Node) ir.Node { if l == n.Left() { return n } - a := ir.SepCopy(n) + a := ir.SepCopy(n).(*ir.UnaryExpr) a.SetLeft(l) return typecheck(a, ctxExpr) } @@ -159,21 +159,39 @@ func (o *Order) safeExpr(n ir.Node) ir.Node { case ir.ONAME, ir.OLITERAL, ir.ONIL: return n - case ir.ODOT, ir.OLEN, ir.OCAP: + case ir.OLEN, ir.OCAP: + l := o.safeExpr(n.Left()) + if l == n.Left() { + return n + } + a := ir.SepCopy(n).(*ir.UnaryExpr) + a.SetLeft(l) + return typecheck(a, ctxExpr) + + case ir.ODOT: l := o.safeExpr(n.Left()) if l == n.Left() { return n } - a := ir.SepCopy(n) + a := ir.SepCopy(n).(*ir.SelectorExpr) + a.SetLeft(l) + return typecheck(a, ctxExpr) + + case ir.ODOTPTR: + l := o.cheapExpr(n.Left()) + if l == n.Left() { + return n + } + a := ir.SepCopy(n).(*ir.SelectorExpr) a.SetLeft(l) return typecheck(a, ctxExpr) - case ir.ODOTPTR, ir.ODEREF: + case ir.ODEREF: l := o.cheapExpr(n.Left()) if l == n.Left() { return n } - a := ir.SepCopy(n) + a := ir.SepCopy(n).(*ir.StarExpr) a.SetLeft(l) return typecheck(a, ctxExpr) @@ -188,7 +206,7 @@ func (o *Order) safeExpr(n ir.Node) ir.Node { if l == n.Left() && r == n.Right() { return n } - a := ir.SepCopy(n) + a := ir.SepCopy(n).(*ir.IndexExpr) a.SetLeft(l) a.SetRight(r) return typecheck(a, ctxExpr) @@ -206,7 +224,7 @@ func (o *Order) safeExpr(n ir.Node) ir.Node { // because we emit explicit VARKILL instructions marking the end of those // temporaries' lifetimes. func isaddrokay(n ir.Node) bool { - return islvalue(n) && (n.Op() != ir.ONAME || n.Class() == ir.PEXTERN || ir.IsAutoTmp(n)) + return islvalue(n) && (n.Op() != ir.ONAME || n.(*ir.Name).Class() == ir.PEXTERN || ir.IsAutoTmp(n)) } // addrTemp ensures that n is okay to pass by address to runtime routines. @@ -225,7 +243,7 @@ func (o *Order) addrTemp(n ir.Node) ir.Node { if s.out != nil { base.Fatalf("staticassign of const generated code: %+v", n) } - vstat = typecheck(vstat, ctxExpr) + vstat = typecheck(vstat, ctxExpr).(*ir.Name) return vstat } if isaddrokay(n) { @@ -267,6 +285,7 @@ func mapKeyReplaceStrConv(n ir.Node) bool { replaced = true case ir.OSTRUCTLIT: for _, elem := range n.List().Slice() { + elem := elem.(*ir.StructKeyExpr) if mapKeyReplaceStrConv(elem.Left()) { replaced = true } @@ -274,7 +293,7 @@ func mapKeyReplaceStrConv(n ir.Node) bool { case ir.OARRAYLIT: for _, elem := range n.List().Slice() { if elem.Op() == ir.OKEY { - elem = elem.Right() + elem = elem.(*ir.KeyExpr).Right() } if mapKeyReplaceStrConv(elem) { replaced = true @@ -337,60 +356,31 @@ func orderMakeSliceCopy(s []ir.Node) { if base.Flag.N != 0 || instrumenting { return } - - if len(s) < 2 { + if len(s) < 2 || s[0] == nil || s[0].Op() != ir.OAS || s[1] == nil || s[1].Op() != ir.OCOPY { return } - asn := s[0] - copyn := s[1] - - if asn == nil || asn.Op() != ir.OAS { - return - } - if asn.Left().Op() != ir.ONAME { - return - } - if ir.IsBlank(asn.Left()) { - return - } - maken := asn.Right() - if maken == nil || maken.Op() != ir.OMAKESLICE { - return - } - if maken.Esc() == EscNone { - return - } - if maken.Left() == nil || maken.Right() != nil { - return - } - if copyn.Op() != ir.OCOPY { - return - } - if copyn.Left().Op() != ir.ONAME { - return - } - if asn.Left().Sym() != copyn.Left().Sym() { - return - } - if copyn.Right().Op() != ir.ONAME { + as := s[0].(*ir.AssignStmt) + cp := s[1].(*ir.BinaryExpr) + if as.Right() == nil || as.Right().Op() != ir.OMAKESLICE || ir.IsBlank(as.Left()) || + as.Left().Op() != ir.ONAME || cp.Left().Op() != ir.ONAME || cp.Right().Op() != ir.ONAME || + as.Left().Name() != cp.Left().Name() || cp.Left().Name() == cp.Right().Name() { + // The line above this one is correct with the differing equality operators: + // we want as.X and cp.X to be the same name, + // but we want the initial data to be coming from a different name. return } - if copyn.Left().Sym() == copyn.Right().Sym() { + mk := as.Right().(*ir.MakeExpr) + if mk.Esc() == EscNone || mk.Left() == nil || mk.Right() != nil { return } - - maken.SetOp(ir.OMAKESLICECOPY) - maken.SetRight(copyn.Right()) + mk.SetOp(ir.OMAKESLICECOPY) + mk.SetRight(cp.Right()) // Set bounded when m = OMAKESLICE([]T, len(s)); OCOPY(m, s) - maken.SetBounded(maken.Left().Op() == ir.OLEN && samesafeexpr(maken.Left().Left(), copyn.Right())) - - maken = typecheck(maken, ctxExpr) - + mk.SetBounded(mk.Left().Op() == ir.OLEN && samesafeexpr(mk.Left().(*ir.UnaryExpr).Left(), cp.Right())) + as.SetRight(typecheck(mk, ctxExpr)) s[1] = nil // remove separate copy call - - return } // edge inserts coverage instrumentation for libfuzzer. @@ -405,8 +395,7 @@ func (o *Order) edge() { counter.Name().SetLibfuzzerExtraCounter(true) // counter += 1 - incr := ir.Nod(ir.OASOP, counter, nodintconst(1)) - incr.SetSubOp(ir.OADD) + incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, nodintconst(1)) o.append(incr) } @@ -469,20 +458,34 @@ func (o *Order) init(n ir.Node) { // call orders the call expression n. // n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY. -func (o *Order) call(n ir.Node) { - if n.Init().Len() > 0 { - // Caller should have already called o.init(n). - base.Fatalf("%v with unexpected ninit", n.Op()) +func (o *Order) call(nn ir.Node) { + if nn.Init().Len() > 0 { + // Caller should have already called o.init(nn). + base.Fatalf("%v with unexpected ninit", nn.Op()) } // Builtin functions. - if n.Op() != ir.OCALLFUNC && n.Op() != ir.OCALLMETH && n.Op() != ir.OCALLINTER { - n.SetLeft(o.expr(n.Left(), nil)) - n.SetRight(o.expr(n.Right(), nil)) - o.exprList(n.List()) + if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLMETH && nn.Op() != ir.OCALLINTER { + switch n := nn.(type) { + default: + base.Fatalf("unexpected call: %+v", n) + case *ir.UnaryExpr: + n.SetLeft(o.expr(n.Left(), nil)) + case *ir.ConvExpr: + n.SetLeft(o.expr(n.Left(), nil)) + case *ir.BinaryExpr: + n.SetLeft(o.expr(n.Left(), nil)) + n.SetRight(o.expr(n.Right(), nil)) + case *ir.MakeExpr: + n.SetLeft(o.expr(n.Left(), nil)) + n.SetRight(o.expr(n.Right(), nil)) + case *ir.CallExpr: + o.exprList(n.List()) + } return } + n := nn.(*ir.CallExpr) fixVariadicCall(n) n.SetLeft(o.expr(n.Left(), nil)) o.exprList(n.List()) @@ -495,11 +498,13 @@ func (o *Order) call(n ir.Node) { // arrange for the pointer to be kept alive until the call returns, // by copying it into a temp and marking that temp // still alive when we pop the temp stack. - if arg.Op() == ir.OCONVNOP && arg.Left().Type().IsUnsafePtr() { - x := o.copyExpr(arg.Left()) - arg.SetLeft(x) - x.Name().SetAddrtaken(true) // ensure SSA keeps the x variable - n.PtrBody().Append(typecheck(ir.Nod(ir.OVARLIVE, x, nil), ctxStmt)) + if arg.Op() == ir.OCONVNOP { + if arg.Left().Type().IsUnsafePtr() { + x := o.copyExpr(arg.Left()) + arg.SetLeft(x) + x.Name().SetAddrtaken(true) // ensure SSA keeps the x variable + n.PtrBody().Append(typecheck(ir.Nod(ir.OVARLIVE, x, nil), ctxStmt)) + } } } @@ -537,18 +542,14 @@ func (o *Order) mapAssign(n ir.Node) { default: base.Fatalf("order.mapAssign %v", n.Op()) - case ir.OAS, ir.OASOP: + case ir.OAS: if n.Left().Op() == ir.OINDEXMAP { - // Make sure we evaluate the RHS before starting the map insert. - // We need to make sure the RHS won't panic. See issue 22881. - if n.Right().Op() == ir.OAPPEND { - s := n.Right().List().Slice()[1:] - for i, n := range s { - s[i] = o.cheapExpr(n) - } - } else { - n.SetRight(o.cheapExpr(n.Right())) - } + n.SetRight(o.safeMapRHS(n.Right())) + } + o.out = append(o.out, n) + case ir.OASOP: + if n.Left().Op() == ir.OINDEXMAP { + n.SetRight(o.safeMapRHS(n.Right())) } o.out = append(o.out, n) @@ -557,6 +558,7 @@ func (o *Order) mapAssign(n ir.Node) { for i, m := range n.List().Slice() { switch { case m.Op() == ir.OINDEXMAP: + m := m.(*ir.IndexExpr) if !ir.IsAutoTmp(m.Left()) { m.SetLeft(o.copyExpr(m.Left())) } @@ -577,6 +579,19 @@ func (o *Order) mapAssign(n ir.Node) { } } +func (o *Order) safeMapRHS(r ir.Node) ir.Node { + // Make sure we evaluate the RHS before starting the map insert. + // We need to make sure the RHS won't panic. See issue 22881. + if r.Op() == ir.OAPPEND { + s := r.List().Slice()[1:] + for i, n := range s { + s[i] = o.cheapExpr(n) + } + return r + } + return o.cheapExpr(r) +} + // stmt orders the statement n, appending to o.out. // Temporaries created during the statement are cleaned // up using VARKILL instructions as possible. @@ -616,12 +631,15 @@ func (o *Order) stmt(n ir.Node) { // makes sure there is nothing too deep being copied. l1 := o.safeExpr(n.Left()) l2 := ir.DeepCopy(src.NoXPos, l1) - if l1.Op() == ir.OINDEXMAP { + if l2.Op() == ir.OINDEXMAP { l2.SetIndexMapLValue(false) } l2 = o.copyExpr(l2) r := o.expr(typecheck(ir.NewBinaryExpr(n.Pos(), n.SubOp(), l2, n.Right()), ctxExpr), nil) - n = typecheck(ir.NodAt(n.Pos(), ir.OAS, l1, r), ctxStmt) + as := typecheck(ir.NodAt(n.Pos(), ir.OAS, l1, r), ctxStmt) + o.mapAssign(as) + o.cleanTemp(t) + return } o.mapAssign(n) @@ -636,6 +654,7 @@ func (o *Order) stmt(n ir.Node) { // Special: avoid copy of func call n.Right case ir.OAS2FUNC: + n := n.(*ir.AssignListStmt) t := o.markTemp() o.exprList(n.List()) o.init(n.Rlist().First()) @@ -650,11 +669,14 @@ func (o *Order) stmt(n ir.Node) { // OAS2MAPR: make sure key is addressable if needed, // and make sure OINDEXMAP is not copied out. case ir.OAS2DOTTYPE, ir.OAS2RECV, ir.OAS2MAPR: + n := n.(*ir.AssignListStmt) t := o.markTemp() o.exprList(n.List()) switch r := n.Rlist().First(); r.Op() { - case ir.ODOTTYPE2, ir.ORECV: + case ir.ODOTTYPE2: + r.SetLeft(o.expr(r.Left(), nil)) + case ir.ORECV: r.SetLeft(o.expr(r.Left(), nil)) case ir.OINDEXMAP: r.SetLeft(o.expr(r.Left(), nil)) @@ -692,17 +714,22 @@ func (o *Order) stmt(n ir.Node) { o.out = append(o.out, n) o.cleanTemp(t) - case ir.OCLOSE, - ir.OCOPY, - ir.OPRINT, - ir.OPRINTN, - ir.ORECOVER, - ir.ORECV: + case ir.OCLOSE, ir.ORECV: + t := o.markTemp() + n.SetLeft(o.expr(n.Left(), nil)) + o.out = append(o.out, n) + o.cleanTemp(t) + + case ir.OCOPY: t := o.markTemp() n.SetLeft(o.expr(n.Left(), nil)) n.SetRight(o.expr(n.Right(), nil)) + o.out = append(o.out, n) + o.cleanTemp(t) + + case ir.OPRINT, ir.OPRINTN, ir.ORECOVER: + t := o.markTemp() o.exprList(n.List()) - o.exprList(n.Rlist()) o.out = append(o.out, n) o.cleanTemp(t) @@ -770,8 +797,9 @@ func (o *Order) stmt(n ir.Node) { // Mark []byte(str) range expression to reuse string backing storage. // It is safe because the storage cannot be mutated. + n := n.(*ir.RangeStmt) if n.Right().Op() == ir.OSTR2BYTES { - n.Right().SetOp(ir.OSTR2BYTESTMP) + n.Right().(*ir.ConvExpr).SetOp(ir.OSTR2BYTESTMP) } t := o.markTemp() @@ -845,16 +873,14 @@ func (o *Order) stmt(n ir.Node) { case ir.OSELECT: t := o.markTemp() - for _, n2 := range n.List().Slice() { - if n2.Op() != ir.OCASE { - base.Fatalf("order select case %v", n2.Op()) - } - r := n2.Left() - setlineno(n2) + for _, cas := range n.List().Slice() { + cas := cas.(*ir.CaseStmt) + r := cas.Left() + setlineno(cas) // Append any new body prologue to ninit. // The next loop will insert ninit into nbody. - if n2.Init().Len() != 0 { + if cas.Init().Len() != 0 { base.Fatalf("order select ninit") } if r == nil { @@ -866,26 +892,29 @@ func (o *Order) stmt(n ir.Node) { base.Fatalf("unknown op in select %v", r.Op()) case ir.OSELRECV, ir.OSELRECV2: - var dst, ok, recv ir.Node + var dst, ok ir.Node + var recv *ir.UnaryExpr + var def bool if r.Op() == ir.OSELRECV { // case x = <-c // case <-c (dst is ir.BlankNode) - dst, ok, recv = r.Left(), ir.BlankNode, r.Right() + def, dst, ok, recv = r.Colas(), r.Left(), ir.BlankNode, r.Right().(*ir.UnaryExpr) } else { + r := r.(*ir.AssignListStmt) // case x, ok = <-c - dst, ok, recv = r.List().First(), r.List().Second(), r.Rlist().First() + def, dst, ok, recv = r.Colas(), r.List().First(), r.List().Second(), r.Rlist().First().(*ir.UnaryExpr) } // If this is case x := <-ch or case x, y := <-ch, the case has // the ODCL nodes to declare x and y. We want to delay that // declaration (and possible allocation) until inside the case body. // Delete the ODCL nodes here and recreate them inside the body below. - if r.Colas() { + if def { init := r.Init().Slice() - if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].Left() == dst { + if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).Left() == dst { init = init[1:] } - if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].Left() == ok { + if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).Left() == ok { init = init[1:] } r.PtrInit().Set(init) @@ -910,35 +939,36 @@ func (o *Order) stmt(n ir.Node) { // use channel element type for temporary to avoid conversions, // such as in case interfacevalue = <-intchan. // the conversion happens in the OAS instead. - if r.Colas() { + if def { dcl := ir.Nod(ir.ODCL, dst, nil) - n2.PtrInit().Append(typecheck(dcl, ctxStmt)) + cas.PtrInit().Append(typecheck(dcl, ctxStmt)) } tmp := o.newTemp(recv.Left().Type().Elem(), recv.Left().Type().Elem().HasPointers()) as := ir.Nod(ir.OAS, dst, tmp) - n2.PtrInit().Append(typecheck(as, ctxStmt)) + cas.PtrInit().Append(typecheck(as, ctxStmt)) dst = tmp } if !ir.IsBlank(ok) { - if r.Colas() { + if def { dcl := ir.Nod(ir.ODCL, ok, nil) - n2.PtrInit().Append(typecheck(dcl, ctxStmt)) + cas.PtrInit().Append(typecheck(dcl, ctxStmt)) } tmp := o.newTemp(types.Types[types.TBOOL], false) as := ir.Nod(ir.OAS, ok, conv(tmp, ok.Type())) - n2.PtrInit().Append(typecheck(as, ctxStmt)) + cas.PtrInit().Append(typecheck(as, ctxStmt)) ok = tmp } if r.Op() == ir.OSELRECV { r.SetLeft(dst) } else { + r := r.(*ir.AssignListStmt) r.List().SetIndex(0, dst) r.List().SetIndex(1, ok) } - orderBlock(n2.PtrInit(), o.free) + orderBlock(cas.PtrInit(), o.free) case ir.OSEND: if r.Init().Len() != 0 { @@ -962,14 +992,15 @@ func (o *Order) stmt(n ir.Node) { // Now that we have accumulated all the temporaries, clean them. // Also insert any ninit queued during the previous loop. // (The temporary cleaning must follow that ninit work.) - for _, n3 := range n.List().Slice() { - orderBlock(n3.PtrBody(), o.free) - n3.PtrBody().Prepend(o.cleanTempNoPop(t)...) + for _, cas := range n.List().Slice() { + cas := cas.(*ir.CaseStmt) + orderBlock(cas.PtrBody(), o.free) + cas.PtrBody().Prepend(o.cleanTempNoPop(t)...) // TODO(mdempsky): Is this actually necessary? // walkselect appears to walk Ninit. - n3.PtrBody().Prepend(n3.Init().Slice()...) - n3.PtrInit().Set(nil) + cas.PtrBody().Prepend(cas.Init().Slice()...) + cas.PtrInit().Set(nil) } o.out = append(o.out, n) @@ -998,6 +1029,7 @@ func (o *Order) stmt(n ir.Node) { // For now just clean all the temporaries at the end. // In practice that's fine. case ir.OSWITCH: + n := n.(*ir.SwitchStmt) if base.Debug.Libfuzzer != 0 && !hasDefaultCase(n) { // Add empty "default:" case for instrumentation. n.PtrList().Append(ir.Nod(ir.OCASE, nil, nil)) @@ -1006,9 +1038,7 @@ func (o *Order) stmt(n ir.Node) { t := o.markTemp() n.SetLeft(o.expr(n.Left(), nil)) for _, ncas := range n.List().Slice() { - if ncas.Op() != ir.OCASE { - base.Fatalf("order switch case %v", ncas.Op()) - } + ncas := ncas.(*ir.CaseStmt) o.exprListInPlace(ncas.List()) orderBlock(ncas.PtrBody(), o.free) } @@ -1020,11 +1050,9 @@ func (o *Order) stmt(n ir.Node) { base.Pos = lno } -func hasDefaultCase(n ir.Node) bool { +func hasDefaultCase(n *ir.SwitchStmt) bool { for _, ncas := range n.List().Slice() { - if ncas.Op() != ir.OCASE { - base.Fatalf("expected case, found %v", ncas.Op()) - } + ncas := ncas.(*ir.CaseStmt) if ncas.List().Len() == 0 { return true } @@ -1067,8 +1095,13 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { if n == nil { return n } - lno := setlineno(n) + n = o.expr1(n, lhs) + base.Pos = lno + return n +} + +func (o *Order) expr1(n, lhs ir.Node) ir.Node { o.init(n) switch n.Op() { @@ -1077,6 +1110,7 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { o.edit = o.exprNoLHS // create closure once } ir.EditChildren(n, o.edit) + return n // Addition of strings turns into a function call. // Allocate a temporary to hold the strings. @@ -1111,6 +1145,7 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { } } } + return n case ir.OINDEXMAP: n.SetLeft(o.expr(n.Left(), nil)) @@ -1133,15 +1168,16 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { // key must be addressable n.SetRight(o.mapKeyTemp(n.Left().Type(), n.Right())) if needCopy { - n = o.copyExpr(n) + return o.copyExpr(n) } + return n // concrete type (not interface) argument might need an addressable // temporary to pass to the runtime conversion routine. case ir.OCONVIFACE: n.SetLeft(o.expr(n.Left(), nil)) if n.Left().Type().IsInterface() { - break + return n } if _, needsaddr := convFuncName(n.Left().Type(), n.Type()); needsaddr || isStaticCompositeLiteral(n.Left()) { // Need a temp if we need to pass the address to the conversion function. @@ -1149,20 +1185,23 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { // whose address we can put directly in an interface (see OCONVIFACE case in walk). n.SetLeft(o.addrTemp(n.Left())) } + return n case ir.OCONVNOP: if n.Type().IsKind(types.TUNSAFEPTR) && n.Left().Type().IsKind(types.TUINTPTR) && (n.Left().Op() == ir.OCALLFUNC || n.Left().Op() == ir.OCALLINTER || n.Left().Op() == ir.OCALLMETH) { + call := n.Left().(*ir.CallExpr) // When reordering unsafe.Pointer(f()) into a separate // statement, the conversion and function call must stay // together. See golang.org/issue/15329. - o.init(n.Left()) - o.call(n.Left()) + o.init(call) + o.call(call) if lhs == nil || lhs.Op() != ir.ONAME || instrumenting { - n = o.copyExpr(n) + return o.copyExpr(n) } } else { n.SetLeft(o.expr(n.Left(), nil)) } + return n case ir.OANDAND, ir.OOROR: // ... = LHS && RHS @@ -1199,7 +1238,7 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { nif.PtrRlist().Set(gen) } o.out = append(o.out, nif) - n = r + return r case ir.OCALLFUNC, ir.OCALLINTER, @@ -1222,27 +1261,31 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { if isRuneCount(n) { // len([]rune(s)) is rewritten to runtime.countrunes(s) later. - n.Left().SetLeft(o.expr(n.Left().Left(), nil)) + conv := n.(*ir.UnaryExpr).Left().(*ir.ConvExpr) + conv.SetLeft(o.expr(conv.Left(), nil)) } else { o.call(n) } if lhs == nil || lhs.Op() != ir.ONAME || instrumenting { - n = o.copyExpr(n) + return o.copyExpr(n) } + return n case ir.OAPPEND: // Check for append(x, make([]T, y)...) . if isAppendOfMake(n) { - n.List().SetFirst(o.expr(n.List().First(), nil)) // order x - n.List().Second().SetLeft(o.expr(n.List().Second().Left(), nil)) // order y + n.List().SetFirst(o.expr(n.List().First(), nil)) // order x + mk := n.List().Second().(*ir.MakeExpr) + mk.SetLeft(o.expr(mk.Left(), nil)) // order y } else { o.exprList(n.List()) } if lhs == nil || lhs.Op() != ir.ONAME && !samesafeexpr(lhs, n.List().First()) { - n = o.copyExpr(n) + return o.copyExpr(n) } + return n case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR: n.SetLeft(o.expr(n.Left(), nil)) @@ -1255,39 +1298,44 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { max = o.cheapExpr(max) n.SetSliceBounds(low, high, max) if lhs == nil || lhs.Op() != ir.ONAME && !samesafeexpr(lhs, n.Left()) { - n = o.copyExpr(n) + return o.copyExpr(n) } + return n case ir.OCLOSURE: + n := n.(*ir.ClosureExpr) if n.Transient() && len(n.Func().ClosureVars) > 0 { prealloc[n] = o.newTemp(closureType(n), false) } + return n - case ir.OSLICELIT, ir.OCALLPART: + case ir.OCALLPART: + n := n.(*ir.CallPartExpr) n.SetLeft(o.expr(n.Left(), nil)) - n.SetRight(o.expr(n.Right(), nil)) + if n.Transient() { + t := partialCallType(n) + prealloc[n] = o.newTemp(t, false) + } + return n + + case ir.OSLICELIT: o.exprList(n.List()) - o.exprList(n.Rlist()) if n.Transient() { - var t *types.Type - switch n.Op() { - case ir.OSLICELIT: - t = types.NewArray(n.Type().Elem(), ir.Int64Val(n.Right())) - case ir.OCALLPART: - t = partialCallType(n) - } + t := types.NewArray(n.Type().Elem(), ir.Int64Val(n.Right())) prealloc[n] = o.newTemp(t, false) } + return n case ir.ODOTTYPE, ir.ODOTTYPE2: n.SetLeft(o.expr(n.Left(), nil)) if !isdirectiface(n.Type()) || instrumenting { - n = o.copyExprClear(n) + return o.copyExprClear(n) } + return n case ir.ORECV: n.SetLeft(o.expr(n.Left(), nil)) - n = o.copyExprClear(n) + return o.copyExprClear(n) case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: n.SetLeft(o.expr(n.Left(), nil)) @@ -1300,10 +1348,10 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { // buffer during conversion. String comparison does not // memorize the strings for later use, so it is safe. if n.Left().Op() == ir.OBYTES2STR { - n.Left().SetOp(ir.OBYTES2STRTMP) + n.Left().(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP) } if n.Right().Op() == ir.OBYTES2STR { - n.Right().SetOp(ir.OBYTES2STRTMP) + n.Right().(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP) } case t.IsStruct() || t.IsArray(): @@ -1312,6 +1360,8 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { n.SetLeft(o.addrTemp(n.Left())) n.SetRight(o.addrTemp(n.Right())) } + return n + case ir.OMAPLIT: // Order map by converting: // map[int]int{ @@ -1330,11 +1380,9 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { // See issue 26552. entries := n.List().Slice() statics := entries[:0] - var dynamics []ir.Node + var dynamics []*ir.KeyExpr for _, r := range entries { - if r.Op() != ir.OKEY { - base.Fatalf("OMAPLIT entry not OKEY: %v\n", r) - } + r := r.(*ir.KeyExpr) if !isStaticCompositeLiteral(r.Left()) || !isStaticCompositeLiteral(r.Right()) { dynamics = append(dynamics, r) @@ -1343,7 +1391,7 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { // Recursively ordering some static entries can change them to dynamic; // e.g., OCONVIFACE nodes. See #31777. - r = o.expr(r, nil) + r = o.expr(r, nil).(*ir.KeyExpr) if !isStaticCompositeLiteral(r.Left()) || !isStaticCompositeLiteral(r.Right()) { dynamics = append(dynamics, r) continue @@ -1354,7 +1402,7 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { n.PtrList().Set(statics) if len(dynamics) == 0 { - break + return n } // Emit the creation of the map (with all its static entries). @@ -1362,18 +1410,17 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { as := ir.Nod(ir.OAS, m, n) typecheck(as, ctxStmt) o.stmt(as) - n = m // Emit eval+insert of dynamic entries, one at a time. for _, r := range dynamics { - as := ir.Nod(ir.OAS, ir.Nod(ir.OINDEX, n, r.Left()), r.Right()) + as := ir.Nod(ir.OAS, ir.Nod(ir.OINDEX, m, r.Left()), r.Right()) typecheck(as, ctxStmt) // Note: this converts the OINDEX to an OINDEXMAP o.stmt(as) } + return m } - base.Pos = lno - return n + // No return - type-assertions above. Each case must return for itself. } // as2 orders OAS2XXXX nodes. It creates temporaries to ensure left-to-right assignment. @@ -1384,7 +1431,7 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node { // tmp1, tmp2, tmp3 = ... // a, b, a = tmp1, tmp2, tmp3 // This is necessary to ensure left to right assignment order. -func (o *Order) as2(n ir.Node) { +func (o *Order) as2(n *ir.AssignListStmt) { tmplist := []ir.Node{} left := []ir.Node{} for ni, l := range n.List().Slice() { @@ -1406,7 +1453,7 @@ func (o *Order) as2(n ir.Node) { // okAs2 orders OAS2XXX with ok. // Just like as2, this also adds temporaries to ensure left-to-right assignment. -func (o *Order) okAs2(n ir.Node) { +func (o *Order) okAs2(n *ir.AssignListStmt) { var tmp1, tmp2 ir.Node if !ir.IsBlank(n.List().First()) { typ := n.Rlist().First().Type() diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go index b7d0c1adc4..0302ffcc94 100644 --- a/src/cmd/compile/internal/ir/stmt.go +++ b/src/cmd/compile/internal/ir/stmt.go @@ -137,8 +137,8 @@ type AssignOpStmt struct { IncDec bool // actually ++ or -- } -func NewAssignOpStmt(pos src.XPos, op Op, x, y Node) *AssignOpStmt { - n := &AssignOpStmt{AsOp: op, X: x, Y: y} +func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt { + n := &AssignOpStmt{AsOp: asOp, X: x, Y: y} n.pos = pos n.op = OASOP return n