// Arrange that receive expressions only appear in direct assignments
// x = <-c or as standalone statements <-c, never in larger expressions.
-// TODO(rsc): The temporary introduction during multiple assignments
-// should be moved into this file, so that the temporaries can be cleaned
-// and so that conversions implicit in the OAS2FUNC and OAS2RECV
-// nodes can be made explicit and then have their temporaries cleaned.
-
-// TODO(rsc): Goto and multilevel break/continue can jump over
-// inserted VARKILL annotations. Work out a way to handle these.
-// The current implementation is safe, in that it will execute correctly.
-// But it won't reuse temporaries as aggressively as it might, and
-// it can result in unnecessary zeroing of those variables in the function
-// prologue.
-
// orderState holds state during the ordering process.
type orderState struct {
out []ir.Node // list of generated statements
edit func(ir.Node) ir.Node // cached closure of o.exprNoLHS
}
-// Order rewrites fn.Nbody to apply the ordering constraints
+// order rewrites fn.Nbody to apply the ordering constraints
// described in the comment at the top of the file.
func order(fn *ir.Func) {
if base.Flag.W > 1 {
s := fmt.Sprintf("\nbefore order %v", fn.Sym())
ir.DumpList(s, fn.Body)
}
-
+ ir.SetPos(fn) // Set reasonable position for instrumenting code. See issue 53688.
orderBlock(&fn.Body, map[string][]*ir.Name{})
}
}
o.free[key] = a[:len(a)-1]
} else {
- v = typecheck.Temp(t)
+ v = typecheck.TempAt(base.Pos, ir.CurFunc, t)
}
if clear {
o.append(ir.NewAssignStmt(base.Pos, v, nil))
if l == n.X {
return n
}
- a := ir.SepCopy(n).(*ir.UnaryExpr)
+ a := ir.Copy(n).(*ir.UnaryExpr)
a.X = l
return typecheck.Expr(a)
}
if l == n.X {
return n
}
- a := ir.SepCopy(n).(*ir.UnaryExpr)
+ a := ir.Copy(n).(*ir.UnaryExpr)
a.X = l
return typecheck.Expr(a)
if l == n.X {
return n
}
- a := ir.SepCopy(n).(*ir.SelectorExpr)
+ a := ir.Copy(n).(*ir.SelectorExpr)
a.X = l
return typecheck.Expr(a)
if l == n.X {
return n
}
- a := ir.SepCopy(n).(*ir.SelectorExpr)
+ a := ir.Copy(n).(*ir.SelectorExpr)
a.X = l
return typecheck.Expr(a)
if l == n.X {
return n
}
- a := ir.SepCopy(n).(*ir.StarExpr)
+ a := ir.Copy(n).(*ir.StarExpr)
a.X = l
return typecheck.Expr(a)
if l == n.X && r == n.Index {
return n
}
- a := ir.SepCopy(n).(*ir.IndexExpr)
+ a := ir.Copy(n).(*ir.IndexExpr)
a.X = l
a.Index = r
return typecheck.Expr(a)
}
}
-// isaddrokay reports whether it is okay to pass n's address to runtime routines.
-// Taking the address of a variable makes the liveness and optimization analyses
-// lose track of where the variable's lifetime ends. To avoid hurting the analyses
-// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay,
-// because we emit explicit VARKILL instructions marking the end of those
-// temporaries' lifetimes.
-func isaddrokay(n ir.Node) bool {
- return ir.IsAddressable(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.
// If the original argument n is not okay, addrTemp creates a tmp, emits
// tmp = n, and then returns tmp.
vstat = typecheck.Expr(vstat).(*ir.Name)
return vstat
}
- if isaddrokay(n) {
+ if ir.IsAddressable(n) {
return n
}
return o.copyExpr(n)
// mapKeyTemp prepares n to be a key in a map runtime call and returns n.
// It should only be used for map runtime calls which have *_fast* versions.
-func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
+// The first parameter is the position of n's containing node, for use in case
+// that n's position is not unique (e.g., if n is an ONAME).
+func (o *orderState) mapKeyTemp(outerPos src.XPos, t *types.Type, n ir.Node) ir.Node {
+ pos := outerPos
+ if ir.HasUniquePos(n) {
+ pos = n.Pos()
+ }
// Most map calls need to take the address of the key.
// Exception: map*_fast* calls. See golang.org/issue/19015.
alg := mapfast(t)
return n
case nt.Kind() == kt.Kind(), nt.IsPtrShaped() && kt.IsPtrShaped():
// can directly convert (e.g. named type to underlying type, or one pointer to another)
- return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONVNOP, kt, n))
+ return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, kt, n))
case nt.IsInteger() && kt.IsInteger():
// can directly convert (e.g. int32 to uint32)
if n.Op() == ir.OLITERAL && nt.IsSigned() {
n.SetType(kt)
return n
}
- return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONV, kt, n))
+ return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, kt, n))
default:
// Unsafe cast through memory.
// We'll need to do a load with type kt. Create a temporary of type kt to
tmp := o.newTemp(kt, true)
// *(*nt)(&tmp) = n
var e ir.Node = typecheck.NodAddr(tmp)
- e = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, nt.PtrTo(), e)
- e = ir.NewStarExpr(n.Pos(), e)
- o.append(ir.NewAssignStmt(base.Pos, e, n))
+ e = ir.NewConvExpr(pos, ir.OCONVNOP, nt.PtrTo(), e)
+ e = ir.NewStarExpr(pos, e)
+ o.append(ir.NewAssignStmt(pos, e, n))
return tmp
}
}
// For:
//
// x = m[string(k)]
-// x = m[T1{... Tn{..., string(k), ...}]
+// x = m[T1{... Tn{..., string(k), ...}}]
//
// where k is []byte, T1 to Tn is a nesting of struct and array literals,
// the allocation of backing bytes for the string can be avoided
o.temp = o.temp[:mark]
}
-// cleanTempNoPop emits VARKILL instructions to *out
-// for each temporary above the mark on the temporary stack.
-// It does not pop the temporaries from the stack.
-func (o *orderState) cleanTempNoPop(mark ordermarker) []ir.Node {
- var out []ir.Node
- for i := len(o.temp) - 1; i >= int(mark); i-- {
- n := o.temp[i]
- out = append(out, typecheck.Stmt(ir.NewUnaryExpr(base.Pos, ir.OVARKILL, n)))
- }
- return out
-}
-
-// cleanTemp emits VARKILL instructions for each temporary above the
-// mark on the temporary stack and removes them from the stack.
-func (o *orderState) cleanTemp(top ordermarker) {
- o.out = append(o.out, o.cleanTempNoPop(top)...)
- o.popTemp(top)
-}
-
// stmtList orders each of the statements in the list.
func (o *orderState) stmtList(l ir.Nodes) {
s := l
// freezes the counter when it reaches the value of 255. However, a range
// of experiments showed that that decreases overall performance.
o.append(ir.NewIfStmt(base.Pos,
- ir.NewBinaryExpr(base.Pos, ir.OEQ, counter, ir.NewInt(0xff)),
- []ir.Node{ir.NewAssignStmt(base.Pos, counter, ir.NewInt(1))},
- []ir.Node{ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))}))
+ ir.NewBinaryExpr(base.Pos, ir.OEQ, counter, ir.NewInt(base.Pos, 0xff)),
+ []ir.Node{ir.NewAssignStmt(base.Pos, counter, ir.NewInt(base.Pos, 1))},
+ []ir.Node{ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(base.Pos, 1))}))
}
// orderBlock orders the block of statements in n into a new slice,
// and then replaces the old slice in n with the new slice.
// free is a map that can be used to obtain temporary variables by type.
func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) {
+ if len(*n) != 0 {
+ // Set reasonable position for instrumenting code. See issue 53688.
+ // It would be nice if ir.Nodes had a position (the opening {, probably),
+ // but it doesn't. So we use the first statement's position instead.
+ ir.SetPos((*n)[0])
+ }
var order orderState
order.free = free
mark := order.markTemp()
order.edge()
order.stmtList(*n)
- order.cleanTemp(mark)
+ order.popTemp(mark)
*n = order.out
}
order.free = free
mark := order.markTemp()
order.stmt(n)
- order.cleanTemp(mark)
+ order.popTemp(mark)
return ir.NewBlockStmt(src.NoXPos, order.out)
}
}
n := nn.(*ir.CallExpr)
- typecheck.FixVariadicCall(n)
+ typecheck.AssertFixedCall(n)
- if isFuncPCIntrinsic(n) && isIfaceOfFunc(n.Args[0]) {
+ if ir.IsFuncPCIntrinsic(n) && ir.IsIfaceOfFunc(n.Args[0]) != nil {
// For internal/abi.FuncPCABIxxx(fn), if fn is a defined function,
// do not introduce temporaries here, so it is easier to rewrite it
// to symbol address reference later in walk.
return
}
- n.X = o.expr(n.X, nil)
+ n.Fun = o.expr(n.Fun, nil)
o.exprList(n.Args)
}
}
// stmt orders the statement n, appending to o.out.
-// Temporaries created during the statement are cleaned
-// up using VARKILL instructions as possible.
func (o *orderState) stmt(n ir.Node) {
if n == nil {
return
default:
base.Fatalf("order.stmt %v", n.Op())
- case ir.OVARKILL, ir.OVARLIVE, ir.OINLMARK:
+ case ir.OINLMARK:
o.out = append(o.out, n)
case ir.OAS:
n.X = o.expr(n.X, nil)
n.Y = o.expr(n.Y, n.X)
o.mapAssign(n)
- o.cleanTemp(t)
+ o.popTemp(t)
case ir.OASOP:
n := n.(*ir.AssignOpStmt)
r := o.expr(typecheck.Expr(ir.NewBinaryExpr(n.Pos(), n.AsOp, l2, n.Y)), nil)
as := typecheck.Stmt(ir.NewAssignStmt(n.Pos(), l1, r))
o.mapAssign(as)
- o.cleanTemp(t)
+ o.popTemp(t)
return
}
o.mapAssign(n)
- o.cleanTemp(t)
+ o.popTemp(t)
case ir.OAS2:
n := n.(*ir.AssignListStmt)
o.exprList(n.Lhs)
o.exprList(n.Rhs)
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
// Special: avoid copy of func call n.Right
case ir.OAS2FUNC:
o.call(call)
o.as2func(n)
}
- o.cleanTemp(t)
+ o.popTemp(t)
// Special: use temporary variables to hold result,
// so that runtime can take address of temporary.
r.Index = o.expr(r.Index, nil)
// See similar conversion for OINDEXMAP below.
_ = mapKeyReplaceStrConv(r.Index)
- r.Index = o.mapKeyTemp(r.X.Type(), r.Index)
+ r.Index = o.mapKeyTemp(r.Pos(), r.X.Type(), r.Index)
default:
base.Fatalf("order.stmt: %v", r.Op())
}
o.as2ok(n)
- o.cleanTemp(t)
+ o.popTemp(t)
// Special: does not save n onto out.
case ir.OBLOCK:
case ir.OBREAK,
ir.OCONTINUE,
ir.ODCL,
- ir.ODCLCONST,
- ir.ODCLTYPE,
ir.OFALL,
ir.OGOTO,
ir.OLABEL,
t := o.markTemp()
o.call(n)
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
case ir.OINLCALL:
n := n.(*ir.InlinedCallExpr)
}
}
- case ir.OCHECKNIL, ir.OCLOSE, ir.OPANIC, ir.ORECV:
+ case ir.OCHECKNIL, ir.OCLEAR, ir.OCLOSE, ir.OPANIC, ir.ORECV:
n := n.(*ir.UnaryExpr)
t := o.markTemp()
n.X = o.expr(n.X, nil)
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
case ir.OCOPY:
n := n.(*ir.BinaryExpr)
n.X = o.expr(n.X, nil)
n.Y = o.expr(n.Y, nil)
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
- case ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP:
+ case ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP:
n := n.(*ir.CallExpr)
t := o.markTemp()
o.call(n)
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
// Special: order arguments to inner call but not call itself.
case ir.ODEFER, ir.OGO:
o.init(n.Call)
o.call(n.Call)
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
case ir.ODELETE:
n := n.(*ir.CallExpr)
t := o.markTemp()
n.Args[0] = o.expr(n.Args[0], nil)
n.Args[1] = o.expr(n.Args[1], nil)
- n.Args[1] = o.mapKeyTemp(n.Args[0].Type(), n.Args[1])
+ n.Args[1] = o.mapKeyTemp(n.Pos(), n.Args[0].Type(), n.Args[1])
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
// Clean temporaries from condition evaluation at
// beginning of loop body and after for statement.
n := n.(*ir.ForStmt)
t := o.markTemp()
n.Cond = o.exprInPlace(n.Cond)
- n.Body.Prepend(o.cleanTempNoPop(t)...)
orderBlock(&n.Body, o.free)
n.Post = orderStmtInPlace(n.Post, o.free)
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
// Clean temporaries from condition at
// beginning of both branches.
n := n.(*ir.IfStmt)
t := o.markTemp()
n.Cond = o.exprInPlace(n.Cond)
- n.Body.Prepend(o.cleanTempNoPop(t)...)
- n.Else.Prepend(o.cleanTempNoPop(t)...)
o.popTemp(t)
orderBlock(&n.Body, o.free)
orderBlock(&n.Else, o.free)
// 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.X.Op() == ir.OSTR2BYTES {
- n.X.(*ir.ConvExpr).SetOp(ir.OSTR2BYTESTMP)
+ if x, ok := n.X.(*ir.ConvExpr); ok {
+ switch x.Op() {
+ case ir.OSTR2BYTES:
+ x.SetOp(ir.OSTR2BYTESTMP)
+ fallthrough
+ case ir.OSTR2BYTESTMP:
+ x.MarkNonNil() // "range []byte(nil)" is fine
+ }
}
t := o.markTemp()
orderBody := true
xt := typecheck.RangeExprType(n.X.Type())
- switch xt.Kind() {
+ switch k := xt.Kind(); {
default:
base.Fatalf("order.stmt range %v", n.Type())
- case types.TARRAY, types.TSLICE:
+ case types.IsInt[k]:
+ // Used only once, no need to copy.
+
+ case k == types.TARRAY, k == types.TSLICE:
if n.Value == nil || ir.IsBlank(n.Value) {
// for i := range x will only use x once, to compute len(x).
// No need to copy it.
}
fallthrough
- case types.TCHAN, types.TSTRING:
+ case k == types.TCHAN, k == types.TSTRING:
// chan, string, slice, array ranges use value multiple times.
// make copy.
r := n.X
n.X = o.copyExpr(r)
- case types.TMAP:
+ case k == types.TMAP:
if isMapClear(n) {
// Preserve the body of the map clear pattern so it can
// be detected during walk. The loop body will not be used
// n.Prealloc is the temp for the iterator.
// MapIterType contains pointers and needs to be zeroed.
- n.Prealloc = o.newTemp(reflectdata.MapIterType(xt), true)
+ n.Prealloc = o.newTemp(reflectdata.MapIterType(), true)
}
n.Key = o.exprInPlace(n.Key)
n.Value = o.exprInPlace(n.Value)
orderBlock(&n.Body, o.free)
}
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
case ir.ORETURN:
n := n.(*ir.ReturnStmt)
do(0, recv.X.Type().Elem())
do(1, types.Types[types.TBOOL])
if len(init) != 0 {
- ir.DumpList("ninit", r.Init())
+ ir.DumpList("ninit", init)
base.Fatalf("ninit on select recv")
}
orderBlock(ncas.PtrInit(), o.free)
// (The temporary cleaning must follow that ninit work.)
for _, cas := range n.Cases {
orderBlock(&cas.Body, o.free)
- cas.Body.Prepend(o.cleanTempNoPop(t)...)
// TODO(mdempsky): Is this actually necessary?
// walkSelect appears to walk Ninit.
n.Value = o.addrTemp(n.Value)
}
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
// TODO(rsc): Clean temporaries more aggressively.
// Note that because walkSwitch will rewrite some of the
}
o.out = append(o.out, n)
- o.cleanTemp(t)
+ o.popTemp(t)
}
base.Pos = lno
}
// key must be addressable
- n.Index = o.mapKeyTemp(n.X.Type(), n.Index)
+ n.Index = o.mapKeyTemp(n.Pos(), n.X.Type(), n.Index)
if needCopy {
return o.copyExpr(n)
}
// concrete type (not interface) argument might need an addressable
// temporary to pass to the runtime conversion routine.
- case ir.OCONVIFACE, ir.OCONVIDATA:
+ case ir.OCONVIFACE:
n := n.(*ir.ConvExpr)
n.X = o.expr(n.X, nil)
if n.X.Type().IsInterface() {
if _, _, needsaddr := dataWordFuncName(n.X.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
// Need a temp if we need to pass the address to the conversion function.
// We also process static composite literal node here, making a named static global
- // whose address we can put directly in an interface (see OCONVIFACE/OCONVIDATA case in walk).
+ // whose address we can put directly in an interface (see OCONVIFACE case in walk).
n.X = o.addrTemp(n.X)
}
return n
o.edge()
rhs := o.expr(n.Y, nil)
o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, rhs)))
- o.cleanTemp(t)
+ o.popTemp(t)
gen := o.out
o.out = saveout
ir.OMAKEMAP,
ir.OMAKESLICE,
ir.OMAKESLICECOPY,
+ ir.OMAX,
+ ir.OMIN,
ir.ONEW,
ir.OREAL,
ir.ORECOVERFP,
// Emit eval+insert of dynamic entries, one at a time.
for _, r := range dynamics {
- as := ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, r.Key), r.Value)
- typecheck.Stmt(as) // Note: this converts the OINDEX to an OINDEXMAP
+ lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, r.Key)).(*ir.IndexExpr)
+ base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs)
+ lhs.RType = n.RType
+
+ as := ir.NewAssignStmt(base.Pos, lhs, r.Value)
+ typecheck.Stmt(as)
o.stmt(as)
}
o.out = append(o.out, n)
o.stmt(typecheck.Stmt(as))
}
-
-// isFuncPCIntrinsic returns whether n is a direct call of internal/abi.FuncPCABIxxx functions.
-func isFuncPCIntrinsic(n *ir.CallExpr) bool {
- if n.Op() != ir.OCALLFUNC || n.X.Op() != ir.ONAME {
- return false
- }
- fn := n.X.(*ir.Name).Sym()
- return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") &&
- (fn.Pkg.Path == "internal/abi" || fn.Pkg == types.LocalPkg && base.Ctxt.Pkgpath == "internal/abi")
-}
-
-// isIfaceOfFunc returns whether n is an interface conversion from a direct reference of a func.
-func isIfaceOfFunc(n ir.Node) bool {
- return n.Op() == ir.OCONVIFACE && n.(*ir.ConvExpr).X.Op() == ir.ONAME && n.(*ir.ConvExpr).X.(*ir.Name).Class == ir.PFUNC
-}