2021-06-22 01:26:34 -07:00
|
|
|
// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package escape
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"cmd/compile/internal/base"
|
|
|
|
|
"cmd/compile/internal/ir"
|
|
|
|
|
"cmd/compile/internal/typecheck"
|
|
|
|
|
"cmd/compile/internal/types"
|
2021-06-21 22:35:01 -07:00
|
|
|
"cmd/internal/src"
|
2021-06-22 01:26:34 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// call evaluates a call expressions, including builtin calls. ks
|
|
|
|
|
// should contain the holes representing where the function callee's
|
2021-06-22 20:53:14 -07:00
|
|
|
// results flows.
|
|
|
|
|
func (e *escape) call(ks []hole, call ir.Node) {
|
2021-06-21 22:35:01 -07:00
|
|
|
var init ir.Nodes
|
|
|
|
|
e.callCommon(ks, call, &init, nil)
|
|
|
|
|
if len(init) != 0 {
|
cmd/compile: fix wrong escape analysis for go/defer generic calls
For go/defer calls like "defer f(x, y)", the compiler rewrites it to:
x1, y1 := x, y
defer func() { f(x1, y1) }()
However, if "f" needs runtime type information, the "RType" field will
refer to the outer ".dict" param, causing wrong liveness analysis.
To fix this, if "f" refers to outer ".dict", the dict param will be
copied to an autotmp, and "f" will refer to this autotmp instead.
Fixes #58341
Change-Id: I238b6e75441442b5540d39bc818205398e80c94d
Reviewed-on: https://go-review.googlesource.com/c/go/+/466035
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-02-07 19:28:15 +07:00
|
|
|
call.(ir.InitNode).PtrInit().Append(init...)
|
2021-06-21 22:35:01 -07:00
|
|
|
}
|
2021-06-22 20:53:14 -07:00
|
|
|
}
|
2021-06-22 01:26:34 -07:00
|
|
|
|
2021-06-21 22:35:01 -07:00
|
|
|
func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir.Func) {
|
|
|
|
|
|
|
|
|
|
// argumentPragma handles escape analysis of argument *argp to the
|
|
|
|
|
// given hole. If the function callee is known, pragma is the
|
|
|
|
|
// function's pragma flags; otherwise 0.
|
|
|
|
|
argumentFunc := func(fn *ir.Name, k hole, argp *ir.Node) {
|
|
|
|
|
e.rewriteArgument(argp, init, call, fn, wrapper)
|
2021-06-22 01:26:34 -07:00
|
|
|
|
2021-06-22 20:53:14 -07:00
|
|
|
e.expr(k.note(call, "call parameter"), *argp)
|
2021-06-22 01:26:34 -07:00
|
|
|
}
|
|
|
|
|
|
2021-06-21 22:35:01 -07:00
|
|
|
argument := func(k hole, argp *ir.Node) {
|
|
|
|
|
argumentFunc(nil, k, argp)
|
|
|
|
|
}
|
|
|
|
|
|
cmd/compile: fix wrong escape analysis for go/defer generic calls
For go/defer calls like "defer f(x, y)", the compiler rewrites it to:
x1, y1 := x, y
defer func() { f(x1, y1) }()
However, if "f" needs runtime type information, the "RType" field will
refer to the outer ".dict" param, causing wrong liveness analysis.
To fix this, if "f" refers to outer ".dict", the dict param will be
copied to an autotmp, and "f" will refer to this autotmp instead.
Fixes #58341
Change-Id: I238b6e75441442b5540d39bc818205398e80c94d
Reviewed-on: https://go-review.googlesource.com/c/go/+/466035
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-02-07 19:28:15 +07:00
|
|
|
argumentRType := func(rtypep *ir.Node) {
|
|
|
|
|
rtype := *rtypep
|
|
|
|
|
if rtype == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// common case: static rtype/itab argument, which can be evaluated within the wrapper instead.
|
|
|
|
|
if addr, ok := rtype.(*ir.AddrExpr); ok && addr.Op() == ir.OADDR && addr.X.Op() == ir.OLINKSYMOFFSET {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
e.wrapExpr(rtype.Pos(), rtypep, init, call, wrapper)
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-22 01:26:34 -07:00
|
|
|
switch call.Op() {
|
|
|
|
|
default:
|
|
|
|
|
ir.Dump("esc", call)
|
|
|
|
|
base.Fatalf("unexpected call op: %v", call.Op())
|
|
|
|
|
|
2023-08-11 17:29:08 -07:00
|
|
|
case ir.OCALLFUNC, ir.OCALLINTER:
|
2021-06-22 01:26:34 -07:00
|
|
|
call := call.(*ir.CallExpr)
|
2022-12-20 11:23:23 -08:00
|
|
|
typecheck.AssertFixedCall(call)
|
2021-06-22 01:26:34 -07:00
|
|
|
|
|
|
|
|
// Pick out the function callee, if statically known.
|
2021-06-21 22:35:01 -07:00
|
|
|
//
|
|
|
|
|
// TODO(mdempsky): Change fn from *ir.Name to *ir.Func, but some
|
|
|
|
|
// functions (e.g., runtime builtins, method wrappers, generated
|
|
|
|
|
// eq/hash functions) don't have it set. Investigate whether
|
|
|
|
|
// that's a concern.
|
2021-06-22 01:26:34 -07:00
|
|
|
var fn *ir.Name
|
|
|
|
|
switch call.Op() {
|
|
|
|
|
case ir.OCALLFUNC:
|
2023-08-11 18:21:22 -07:00
|
|
|
v := ir.StaticValue(call.X)
|
|
|
|
|
fn = ir.StaticCalleeName(v)
|
2021-06-22 01:26:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fntype := call.X.Type()
|
|
|
|
|
if fn != nil {
|
|
|
|
|
fntype = fn.Type()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ks != nil && fn != nil && e.inMutualBatch(fn) {
|
|
|
|
|
for i, result := range fn.Type().Results().FieldSlice() {
|
2023-08-15 16:45:52 -07:00
|
|
|
e.expr(ks[i], result.Nname.(*ir.Name))
|
2021-06-22 01:26:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-23 00:31:16 -07:00
|
|
|
var recvp *ir.Node
|
|
|
|
|
if call.Op() == ir.OCALLFUNC {
|
2021-06-22 01:26:34 -07:00
|
|
|
// Evaluate callee function expression.
|
2021-06-21 22:35:01 -07:00
|
|
|
//
|
2021-06-23 00:31:16 -07:00
|
|
|
// Note: We use argument and not argumentFunc, because while
|
|
|
|
|
// call.X here may be an argument to runtime.{new,defer}proc,
|
|
|
|
|
// it's not an argument to fn itself.
|
2023-08-15 16:45:52 -07:00
|
|
|
calleeK := e.discardHole()
|
|
|
|
|
if fn == nil { // unknown callee
|
|
|
|
|
for _, k := range ks {
|
|
|
|
|
if k.dst != &e.blankLoc {
|
|
|
|
|
// The results flow somewhere, but we don't statically
|
|
|
|
|
// know the callee function. If a closure flows here, we
|
|
|
|
|
// need to conservatively assume its results might flow to
|
|
|
|
|
// the heap.
|
|
|
|
|
calleeK = e.calleeHole()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
argument(calleeK, &call.X)
|
2021-06-23 00:31:16 -07:00
|
|
|
} else {
|
|
|
|
|
recvp = &call.X.(*ir.SelectorExpr).X
|
2021-06-22 01:26:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args := call.Args
|
2021-06-23 00:31:16 -07:00
|
|
|
if recv := fntype.Recv(); recv != nil {
|
|
|
|
|
if recvp == nil {
|
2023-06-13 23:01:11 +00:00
|
|
|
// Function call using method expression. Receiver argument is
|
2021-06-23 00:31:16 -07:00
|
|
|
// at the front of the regular arguments list.
|
|
|
|
|
recvp = &args[0]
|
|
|
|
|
args = args[1:]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
argumentFunc(fn, e.tagHole(ks, fn, recv), recvp)
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-22 01:26:34 -07:00
|
|
|
for i, param := range fntype.Params().FieldSlice() {
|
2021-06-21 22:35:01 -07:00
|
|
|
argumentFunc(fn, e.tagHole(ks, fn, param), &args[i])
|
2021-06-22 01:26:34 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-03 04:53:25 -07:00
|
|
|
case ir.OINLCALL:
|
|
|
|
|
call := call.(*ir.InlinedCallExpr)
|
|
|
|
|
e.stmts(call.Body)
|
|
|
|
|
for i, result := range call.ReturnVars {
|
|
|
|
|
k := e.discardHole()
|
|
|
|
|
if ks != nil {
|
|
|
|
|
k = ks[i]
|
|
|
|
|
}
|
|
|
|
|
e.expr(k, result)
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-22 01:26:34 -07:00
|
|
|
case ir.OAPPEND:
|
|
|
|
|
call := call.(*ir.CallExpr)
|
|
|
|
|
args := call.Args
|
|
|
|
|
|
|
|
|
|
// Appendee slice may flow directly to the result, if
|
|
|
|
|
// it has enough capacity. Alternatively, a new heap
|
|
|
|
|
// slice might be allocated, and all slice elements
|
|
|
|
|
// might flow to heap.
|
2023-08-15 16:45:52 -07:00
|
|
|
appendeeK := e.teeHole(ks[0], e.mutatorHole())
|
2021-06-22 01:26:34 -07:00
|
|
|
if args[0].Type().Elem().HasPointers() {
|
|
|
|
|
appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
|
|
|
|
|
}
|
2021-06-22 20:53:14 -07:00
|
|
|
argument(appendeeK, &args[0])
|
2021-06-22 01:26:34 -07:00
|
|
|
|
|
|
|
|
if call.IsDDD {
|
|
|
|
|
appendedK := e.discardHole()
|
|
|
|
|
if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() {
|
|
|
|
|
appendedK = e.heapHole().deref(call, "appended slice...")
|
|
|
|
|
}
|
2021-06-22 20:53:14 -07:00
|
|
|
argument(appendedK, &args[1])
|
2021-06-22 01:26:34 -07:00
|
|
|
} else {
|
2021-06-22 20:53:14 -07:00
|
|
|
for i := 1; i < len(args); i++ {
|
|
|
|
|
argument(e.heapHole(), &args[i])
|
2021-06-22 01:26:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
cmd/compile: fix wrong escape analysis for go/defer generic calls
For go/defer calls like "defer f(x, y)", the compiler rewrites it to:
x1, y1 := x, y
defer func() { f(x1, y1) }()
However, if "f" needs runtime type information, the "RType" field will
refer to the outer ".dict" param, causing wrong liveness analysis.
To fix this, if "f" refers to outer ".dict", the dict param will be
copied to an autotmp, and "f" will refer to this autotmp instead.
Fixes #58341
Change-Id: I238b6e75441442b5540d39bc818205398e80c94d
Reviewed-on: https://go-review.googlesource.com/c/go/+/466035
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-02-07 19:28:15 +07:00
|
|
|
argumentRType(&call.RType)
|
2021-06-22 01:26:34 -07:00
|
|
|
|
|
|
|
|
case ir.OCOPY:
|
|
|
|
|
call := call.(*ir.BinaryExpr)
|
2023-08-15 16:45:52 -07:00
|
|
|
argument(e.mutatorHole(), &call.X)
|
2021-06-22 01:26:34 -07:00
|
|
|
|
|
|
|
|
copiedK := e.discardHole()
|
|
|
|
|
if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() {
|
|
|
|
|
copiedK = e.heapHole().deref(call, "copied slice")
|
|
|
|
|
}
|
2021-06-22 20:53:14 -07:00
|
|
|
argument(copiedK, &call.Y)
|
cmd/compile: fix wrong escape analysis for go/defer generic calls
For go/defer calls like "defer f(x, y)", the compiler rewrites it to:
x1, y1 := x, y
defer func() { f(x1, y1) }()
However, if "f" needs runtime type information, the "RType" field will
refer to the outer ".dict" param, causing wrong liveness analysis.
To fix this, if "f" refers to outer ".dict", the dict param will be
copied to an autotmp, and "f" will refer to this autotmp instead.
Fixes #58341
Change-Id: I238b6e75441442b5540d39bc818205398e80c94d
Reviewed-on: https://go-review.googlesource.com/c/go/+/466035
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-02-07 19:28:15 +07:00
|
|
|
argumentRType(&call.RType)
|
2021-06-22 01:26:34 -07:00
|
|
|
|
|
|
|
|
case ir.OPANIC:
|
|
|
|
|
call := call.(*ir.UnaryExpr)
|
2021-06-22 20:53:14 -07:00
|
|
|
argument(e.heapHole(), &call.X)
|
2021-06-22 01:26:34 -07:00
|
|
|
|
|
|
|
|
case ir.OCOMPLEX:
|
|
|
|
|
call := call.(*ir.BinaryExpr)
|
2021-06-22 20:53:14 -07:00
|
|
|
argument(e.discardHole(), &call.X)
|
|
|
|
|
argument(e.discardHole(), &call.Y)
|
[dev.typeparams] cmd/compile: desugar ORECOVER into ORECOVERFP
Currently ORECOVER is a single operation that both (1) calculates
the (logical) caller frame pointer and (2) calls runtime.gorecover.
This is normally fine, but it's inconvenient for regabi, which wants
to wrap "defer recover()" into "defer func() { recover() }" and
needs (1) and (2) to happen at different times.
The current solution is to apply walkRecover early to split it into
the two steps, but calling it during order is a minor layering
violation. It works well today because the order and walk phases are
closely related anyway and walkRecover is relatively simple, but it
won't work for go/defer wrapping earlier into the frontend.
This CL adds a new, lower-level ORECOVERFP primitive, which represents
just part (2); and OGETCALLER{PC,SP} primitives, which provide a way
to compute (1) in the frontend too.
OGETCALLERPC isn't needed/used today, but it seems worth including for
completeness. Maybe it will be useful at some point for intrinsifying
runtime.getcaller{pc,sp}, like we already do for runtime.getg.
Change-Id: Iaa8ae51e09306c45c147b6759a5b7c24dcc317ca
Reviewed-on: https://go-review.googlesource.com/c/go/+/330192
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-22 13:44:18 -07:00
|
|
|
|
2023-08-11 17:29:08 -07:00
|
|
|
case ir.ODELETE, ir.OMAX, ir.OMIN, ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP:
|
2021-06-22 01:26:34 -07:00
|
|
|
call := call.(*ir.CallExpr)
|
2021-06-22 20:53:14 -07:00
|
|
|
for i := range call.Args {
|
|
|
|
|
argument(e.discardHole(), &call.Args[i])
|
2021-06-22 01:26:34 -07:00
|
|
|
}
|
cmd/compile: fix wrong escape analysis for go/defer generic calls
For go/defer calls like "defer f(x, y)", the compiler rewrites it to:
x1, y1 := x, y
defer func() { f(x1, y1) }()
However, if "f" needs runtime type information, the "RType" field will
refer to the outer ".dict" param, causing wrong liveness analysis.
To fix this, if "f" refers to outer ".dict", the dict param will be
copied to an autotmp, and "f" will refer to this autotmp instead.
Fixes #58341
Change-Id: I238b6e75441442b5540d39bc818205398e80c94d
Reviewed-on: https://go-review.googlesource.com/c/go/+/466035
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-02-07 19:28:15 +07:00
|
|
|
argumentRType(&call.RType)
|
[dev.typeparams] cmd/compile: desugar ORECOVER into ORECOVERFP
Currently ORECOVER is a single operation that both (1) calculates
the (logical) caller frame pointer and (2) calls runtime.gorecover.
This is normally fine, but it's inconvenient for regabi, which wants
to wrap "defer recover()" into "defer func() { recover() }" and
needs (1) and (2) to happen at different times.
The current solution is to apply walkRecover early to split it into
the two steps, but calling it during order is a minor layering
violation. It works well today because the order and walk phases are
closely related anyway and walkRecover is relatively simple, but it
won't work for go/defer wrapping earlier into the frontend.
This CL adds a new, lower-level ORECOVERFP primitive, which represents
just part (2); and OGETCALLER{PC,SP} primitives, which provide a way
to compute (1) in the frontend too.
OGETCALLERPC isn't needed/used today, but it seems worth including for
completeness. Maybe it will be useful at some point for intrinsifying
runtime.getcaller{pc,sp}, like we already do for runtime.getg.
Change-Id: Iaa8ae51e09306c45c147b6759a5b7c24dcc317ca
Reviewed-on: https://go-review.googlesource.com/c/go/+/330192
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-22 13:44:18 -07:00
|
|
|
|
2023-08-15 16:45:52 -07:00
|
|
|
case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
|
2021-06-22 01:26:34 -07:00
|
|
|
call := call.(*ir.UnaryExpr)
|
2021-06-22 20:53:14 -07:00
|
|
|
argument(e.discardHole(), &call.X)
|
2021-06-22 01:26:34 -07:00
|
|
|
|
2023-08-15 16:45:52 -07:00
|
|
|
case ir.OCLEAR:
|
|
|
|
|
call := call.(*ir.UnaryExpr)
|
|
|
|
|
argument(e.mutatorHole(), &call.X)
|
|
|
|
|
|
2023-01-17 01:29:02 +07:00
|
|
|
case ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
|
|
|
|
|
call := call.(*ir.UnaryExpr)
|
|
|
|
|
argument(ks[0], &call.X)
|
|
|
|
|
|
2022-08-16 17:52:13 +08:00
|
|
|
case ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
|
2021-06-22 01:26:34 -07:00
|
|
|
call := call.(*ir.BinaryExpr)
|
2021-06-22 20:53:14 -07:00
|
|
|
argument(ks[0], &call.X)
|
|
|
|
|
argument(e.discardHole(), &call.Y)
|
cmd/compile: fix wrong escape analysis for go/defer generic calls
For go/defer calls like "defer f(x, y)", the compiler rewrites it to:
x1, y1 := x, y
defer func() { f(x1, y1) }()
However, if "f" needs runtime type information, the "RType" field will
refer to the outer ".dict" param, causing wrong liveness analysis.
To fix this, if "f" refers to outer ".dict", the dict param will be
copied to an autotmp, and "f" will refer to this autotmp instead.
Fixes #58341
Change-Id: I238b6e75441442b5540d39bc818205398e80c94d
Reviewed-on: https://go-review.googlesource.com/c/go/+/466035
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-02-07 19:28:15 +07:00
|
|
|
argumentRType(&call.RType)
|
2021-06-22 01:26:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-21 22:35:01 -07:00
|
|
|
// goDeferStmt analyzes a "go" or "defer" statement.
|
|
|
|
|
//
|
|
|
|
|
// In the process, it also normalizes the statement to always use a
|
|
|
|
|
// simple function call with no arguments and no results. For example,
|
|
|
|
|
// it rewrites:
|
|
|
|
|
//
|
|
|
|
|
// defer f(x, y)
|
|
|
|
|
//
|
|
|
|
|
// into:
|
|
|
|
|
//
|
|
|
|
|
// x1, y1 := x, y
|
|
|
|
|
// defer func() { f(x1, y1) }()
|
2021-06-22 20:53:14 -07:00
|
|
|
func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
|
2021-06-21 22:35:01 -07:00
|
|
|
k := e.heapHole()
|
|
|
|
|
if n.Op() == ir.ODEFER && e.loopDepth == 1 {
|
|
|
|
|
// Top-level defer arguments don't escape to the heap,
|
|
|
|
|
// but they do need to last until they're invoked.
|
|
|
|
|
k = e.later(e.discardHole())
|
|
|
|
|
|
2021-06-22 20:53:14 -07:00
|
|
|
// force stack allocation of defer record, unless
|
|
|
|
|
// open-coded defers are used (see ssa.go)
|
|
|
|
|
n.SetEsc(ir.EscNever)
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-21 22:35:01 -07:00
|
|
|
call := n.Call
|
|
|
|
|
|
|
|
|
|
init := n.PtrInit()
|
|
|
|
|
init.Append(ir.TakeInit(call)...)
|
|
|
|
|
e.stmts(*init)
|
|
|
|
|
|
|
|
|
|
// If the function is already a zero argument/result function call,
|
|
|
|
|
// just escape analyze it normally.
|
runtime: drop function context from traceback
Currently, gentraceback tracks the closure context of the outermost
frame. This used to be important for "unstarted" calls to reflect
function stubs, where "unstarted" calls are either deferred functions
or the entry-point of a goroutine that hasn't run. Because reflect
function stubs have a dynamic argument map, we have to reach into
their closure context to fetch to map, and how to do this differs
depending on whether the function has started. This was discovered in
issue #25897.
However, as part of the register ABI, "go" and "defer" were made much
simpler, and any "go" or "defer" of a function that takes arguments or
returns results gets wrapped in a closure that provides those
arguments (and/or discards the results). Hence, we'll see that closure
instead of a direct call to a reflect stub, and can get its static
argument map without any trouble.
The one case where we may still see an unstarted reflect stub is if
the function takes no arguments and has no results, in which case the
compiler can optimize away the wrapper closure. But in this case we
know the argument map is empty: the compiler can apply this
optimization precisely because the target function has no argument
frame.
As a result, we no longer need to track the closure context during
traceback, so this CL drops all of that mechanism.
We still have to be careful about the unstarted case because we can't
reach into the function's locals frame to pull out its context
(because it has no locals frame). We double-check that in this case
we're at the function entry.
I would prefer to do this with some in-code PCDATA annotations of
where to find the dynamic argument map, but that's a lot of mechanism
to introduce for just this. It might make sense to consider this along
with #53609.
Finally, we beef up the test for this so it more reliably forces the
runtime down this path. It's fundamentally probabilistic, but this
tweak makes it better. Scheduler testing hooks (#54475) would make it
possible to write a reliable test for this.
For #54466, but it's a nice clean-up all on its own.
Change-Id: I16e4f2364ba2ea4b1fec1e27f971b06756e7b09f
Reviewed-on: https://go-review.googlesource.com/c/go/+/424254
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Auto-Submit: Austin Clements <austin@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
2022-08-15 10:41:03 -04:00
|
|
|
//
|
|
|
|
|
// Note that the runtime is aware of this optimization for
|
|
|
|
|
// "go" statements that start in reflect.makeFuncStub or
|
|
|
|
|
// reflect.methodValueCall.
|
2021-06-21 22:35:01 -07:00
|
|
|
if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC {
|
|
|
|
|
if sig := call.X.Type(); sig.NumParams()+sig.NumResults() == 0 {
|
|
|
|
|
if clo, ok := call.X.(*ir.ClosureExpr); ok && n.Op() == ir.OGO {
|
|
|
|
|
clo.IsGoWrap = true
|
|
|
|
|
}
|
|
|
|
|
e.expr(k, call.X)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a new no-argument function that we'll hand off to defer.
|
2023-08-16 18:56:41 -07:00
|
|
|
fn := ir.NewClosureFunc(n.Pos(), n.Pos(), types.NewSignature(nil, nil, nil), e.curfn, typecheck.Target)
|
2021-06-21 22:35:01 -07:00
|
|
|
fn.SetWrapper(true)
|
2023-08-15 16:45:52 -07:00
|
|
|
fn.SetEsc(escFuncTagged) // no params; effectively tagged already
|
2021-06-21 22:35:01 -07:00
|
|
|
fn.Body = []ir.Node{call}
|
2022-02-07 12:00:44 -05:00
|
|
|
if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC {
|
|
|
|
|
// If the callee is a named function, link to the original callee.
|
|
|
|
|
x := call.X
|
|
|
|
|
if x.Op() == ir.ONAME && x.(*ir.Name).Class == ir.PFUNC {
|
|
|
|
|
fn.WrappedFunc = call.X.(*ir.Name).Func
|
|
|
|
|
} else if x.Op() == ir.OMETHEXPR && ir.MethodExprFunc(x).Nname != nil {
|
|
|
|
|
fn.WrappedFunc = ir.MethodExprName(x).Func
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-21 22:35:01 -07:00
|
|
|
|
|
|
|
|
clo := fn.OClosure
|
2023-08-16 18:56:41 -07:00
|
|
|
|
2021-06-21 22:35:01 -07:00
|
|
|
if n.Op() == ir.OGO {
|
|
|
|
|
clo.IsGoWrap = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.callCommon(nil, call, init, fn)
|
|
|
|
|
e.closures = append(e.closures, closure{e.spill(k, clo), clo})
|
|
|
|
|
|
|
|
|
|
// Create new top level call to closure.
|
|
|
|
|
n.Call = ir.NewCallExpr(call.Pos(), ir.OCALL, clo, nil)
|
|
|
|
|
ir.WithFunc(e.curfn, func() {
|
|
|
|
|
typecheck.Stmt(n.Call)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// rewriteArgument rewrites the argument *argp of the given call expression.
|
|
|
|
|
// fn is the static callee function, if known.
|
|
|
|
|
// wrapper is the go/defer wrapper function for call, if any.
|
|
|
|
|
func (e *escape) rewriteArgument(argp *ir.Node, init *ir.Nodes, call ir.Node, fn *ir.Name, wrapper *ir.Func) {
|
|
|
|
|
var pragma ir.PragmaFlag
|
|
|
|
|
if fn != nil && fn.Func != nil {
|
|
|
|
|
pragma = fn.Func.Pragma
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// unsafeUintptr rewrites "uintptr(ptr)" arguments to syscall-like
|
|
|
|
|
// functions, so that ptr is kept alive and/or escaped as
|
|
|
|
|
// appropriate. unsafeUintptr also reports whether it modified arg0.
|
|
|
|
|
unsafeUintptr := func(arg0 ir.Node) bool {
|
|
|
|
|
if pragma&(ir.UintptrKeepAlive|ir.UintptrEscapes) == 0 {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the argument is really a pointer being converted to uintptr,
|
|
|
|
|
// 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 arg0.Op() != ir.OCONVNOP || !arg0.Type().IsUintptr() {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
arg := arg0.(*ir.ConvExpr)
|
|
|
|
|
|
|
|
|
|
if !arg.X.Type().IsUnsafePtr() {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create and declare a new pointer-typed temp variable.
|
|
|
|
|
tmp := e.wrapExpr(arg.Pos(), &arg.X, init, call, wrapper)
|
|
|
|
|
|
2023-08-15 16:45:52 -07:00
|
|
|
k := e.mutatorHole()
|
2021-06-21 22:35:01 -07:00
|
|
|
if pragma&ir.UintptrEscapes != 0 {
|
2023-08-15 16:45:52 -07:00
|
|
|
k = e.heapHole().note(arg, "//go:uintptrescapes")
|
2021-06-21 22:35:01 -07:00
|
|
|
}
|
2023-08-15 16:45:52 -07:00
|
|
|
e.flow(k, e.oldLoc(tmp))
|
2021-06-21 22:35:01 -07:00
|
|
|
|
|
|
|
|
if pragma&ir.UintptrKeepAlive != 0 {
|
|
|
|
|
call := call.(*ir.CallExpr)
|
|
|
|
|
|
|
|
|
|
// SSA implements CallExpr.KeepAlive using OpVarLive, which
|
|
|
|
|
// doesn't support PAUTOHEAP variables. I tried changing it to
|
|
|
|
|
// use OpKeepAlive, but that ran into issues of its own.
|
|
|
|
|
// For now, the easy solution is to explicitly copy to (yet
|
|
|
|
|
// another) new temporary variable.
|
|
|
|
|
keep := tmp
|
|
|
|
|
if keep.Class == ir.PAUTOHEAP {
|
|
|
|
|
keep = e.copyExpr(arg.Pos(), tmp, call.PtrInit(), wrapper, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keep.SetAddrtaken(true) // ensure SSA keeps the tmp variable
|
|
|
|
|
call.KeepAlive = append(call.KeepAlive, keep)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
visit := func(pos src.XPos, argp *ir.Node) {
|
[dev.typeparams] cmd/compile: optimize wrapping of constant arguments
When wrapping a go/defer statement like:
go f(g(), "x", 42)
we were wrapping it like:
_0, _1, _2, _3 := f, g(), "x", 42
go func() { _0(_1, _2, _3) }()
This is simple and general (and often necessary), but suboptimal in
some cases, such as this. Instead of evaluating the constant arguments
at the go/defer statement, and storing them into the closure context,
we can just keep them in the wrapped call expression.
This CL changes the code to instead generate (assuming f is a declared
function, not a function-typed variable):
_0 := g()
go func() { f(_0, "x", 42) }()
Change-Id: I2bdd4951e7ee93363e1656ecf9b5bd69a121c38a
Reviewed-on: https://go-review.googlesource.com/c/go/+/330332
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-23 00:49:03 -07:00
|
|
|
// Optimize a few common constant expressions. By leaving these
|
|
|
|
|
// untouched in the call expression, we let the wrapper handle
|
|
|
|
|
// evaluating them, rather than taking up closure context space.
|
|
|
|
|
switch arg := *argp; arg.Op() {
|
|
|
|
|
case ir.OLITERAL, ir.ONIL, ir.OMETHEXPR:
|
|
|
|
|
return
|
|
|
|
|
case ir.ONAME:
|
|
|
|
|
if arg.(*ir.Name).Class == ir.PFUNC {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-21 22:35:01 -07:00
|
|
|
if unsafeUintptr(*argp) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if wrapper != nil {
|
|
|
|
|
e.wrapExpr(pos, argp, init, call, wrapper)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:19:24 +07:00
|
|
|
// Peel away any slice literals for better escape analyze
|
|
|
|
|
// them. For example:
|
|
|
|
|
//
|
|
|
|
|
// go F([]int{a, b})
|
|
|
|
|
//
|
|
|
|
|
// If F doesn't escape its arguments, then the slice can
|
|
|
|
|
// be allocated on the new goroutine's stack.
|
|
|
|
|
//
|
|
|
|
|
// For variadic functions, the compiler has already rewritten:
|
|
|
|
|
//
|
|
|
|
|
// f(a, b, c)
|
|
|
|
|
//
|
|
|
|
|
// to:
|
|
|
|
|
//
|
|
|
|
|
// f([]T{a, b, c}...)
|
|
|
|
|
//
|
|
|
|
|
// So we need to look into slice elements to handle uintptr(ptr)
|
|
|
|
|
// arguments to syscall-like functions correctly.
|
2021-06-21 22:35:01 -07:00
|
|
|
if arg := *argp; arg.Op() == ir.OSLICELIT {
|
|
|
|
|
list := arg.(*ir.CompLitExpr).List
|
|
|
|
|
for i := range list {
|
2021-10-31 00:20:13 +07:00
|
|
|
el := &list[i]
|
|
|
|
|
if list[i].Op() == ir.OKEY {
|
|
|
|
|
el = &list[i].(*ir.KeyExpr).Value
|
|
|
|
|
}
|
|
|
|
|
visit(arg.Pos(), el)
|
2021-06-21 22:35:01 -07:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
visit(call.Pos(), argp)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// wrapExpr replaces *exprp with a temporary variable copy. If wrapper
|
|
|
|
|
// is non-nil, the variable will be captured for use within that
|
|
|
|
|
// function.
|
|
|
|
|
func (e *escape) wrapExpr(pos src.XPos, exprp *ir.Node, init *ir.Nodes, call ir.Node, wrapper *ir.Func) *ir.Name {
|
|
|
|
|
tmp := e.copyExpr(pos, *exprp, init, e.curfn, true)
|
|
|
|
|
|
|
|
|
|
if wrapper != nil {
|
|
|
|
|
// Currently for "defer i.M()" if i is nil it panics at the point
|
|
|
|
|
// of defer statement, not when deferred function is called. We
|
|
|
|
|
// need to do the nil check outside of the wrapper.
|
|
|
|
|
if call.Op() == ir.OCALLINTER && exprp == &call.(*ir.CallExpr).X.(*ir.SelectorExpr).X {
|
|
|
|
|
check := ir.NewUnaryExpr(pos, ir.OCHECKNIL, ir.NewUnaryExpr(pos, ir.OITAB, tmp))
|
|
|
|
|
init.Append(typecheck.Stmt(check))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.oldLoc(tmp).captured = true
|
|
|
|
|
|
2021-07-02 16:29:42 -07:00
|
|
|
tmp = ir.NewClosureVar(pos, wrapper, tmp)
|
2021-06-21 22:35:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*exprp = tmp
|
|
|
|
|
return tmp
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// copyExpr creates and returns a new temporary variable within fn;
|
|
|
|
|
// appends statements to init to declare and initialize it to expr;
|
|
|
|
|
// and escape analyzes the data flow if analyze is true.
|
|
|
|
|
func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes, fn *ir.Func, analyze bool) *ir.Name {
|
|
|
|
|
if ir.HasUniquePos(expr) {
|
|
|
|
|
pos = expr.Pos()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp := typecheck.TempAt(pos, fn, expr.Type())
|
|
|
|
|
|
|
|
|
|
stmts := []ir.Node{
|
|
|
|
|
ir.NewDecl(pos, ir.ODCL, tmp),
|
|
|
|
|
ir.NewAssignStmt(pos, tmp, expr),
|
|
|
|
|
}
|
|
|
|
|
typecheck.Stmts(stmts)
|
|
|
|
|
init.Append(stmts...)
|
|
|
|
|
|
|
|
|
|
if analyze {
|
2023-08-15 14:02:37 -07:00
|
|
|
e.newLoc(tmp, true)
|
2021-06-21 22:35:01 -07:00
|
|
|
e.stmts(stmts)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tmp
|
2021-06-22 20:53:14 -07:00
|
|
|
}
|
|
|
|
|
|
2021-06-22 01:26:34 -07:00
|
|
|
// tagHole returns a hole for evaluating an argument passed to param.
|
|
|
|
|
// ks should contain the holes representing where the function
|
|
|
|
|
// callee's results flows. fn is the statically-known callee function,
|
|
|
|
|
// if any.
|
|
|
|
|
func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole {
|
|
|
|
|
// If this is a dynamic call, we can't rely on param.Note.
|
|
|
|
|
if fn == nil {
|
|
|
|
|
return e.heapHole()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if e.inMutualBatch(fn) {
|
|
|
|
|
return e.addr(ir.AsNode(param.Nname))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call to previously tagged function.
|
|
|
|
|
|
|
|
|
|
var tagKs []hole
|
|
|
|
|
esc := parseLeaks(param.Note)
|
2023-08-15 16:45:52 -07:00
|
|
|
|
2021-06-22 01:26:34 -07:00
|
|
|
if x := esc.Heap(); x >= 0 {
|
|
|
|
|
tagKs = append(tagKs, e.heapHole().shift(x))
|
|
|
|
|
}
|
2023-08-15 16:45:52 -07:00
|
|
|
if x := esc.Mutator(); x >= 0 {
|
|
|
|
|
tagKs = append(tagKs, e.mutatorHole().shift(x))
|
|
|
|
|
}
|
|
|
|
|
if x := esc.Callee(); x >= 0 {
|
|
|
|
|
tagKs = append(tagKs, e.calleeHole().shift(x))
|
|
|
|
|
}
|
2021-06-22 01:26:34 -07:00
|
|
|
|
|
|
|
|
if ks != nil {
|
|
|
|
|
for i := 0; i < numEscResults; i++ {
|
|
|
|
|
if x := esc.Result(i); x >= 0 {
|
|
|
|
|
tagKs = append(tagKs, ks[i].shift(x))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.teeHole(tagKs...)
|
|
|
|
|
}
|