1
0
mirror of https://github.com/golang/go synced 2024-11-26 08:17:59 -07:00

cmd/compile: fix defer desugar keepalive arg handling buglet

Fix a bug in the go/defer desugar handling of keepalive arguments. The
go/defer wrapping code has special handling for calls whose arguments
are pointers that have been cast to "uintptr", so as to insure that
call "keepalive" machinery for such calls continues to work. This
patch fixes a bug in the special case code to insure that it doesn't
kick in for other situations where you have an unsafe.Pointer ->
uintptr argument (outside the keepalive context).

Fixes make.bat on windows with GOEXPERIMENT=regabidefer in effect.

Change-Id: I9db89c4c73f0db1235901a4fae57f62f88c94ac3
Reviewed-on: https://go-review.googlesource.com/c/go/+/304457
Trust: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Than McIntosh 2021-03-25 08:30:19 -04:00
parent 9f4d5c94b0
commit 53941b6150

View File

@ -1458,8 +1458,9 @@ var wrapGoDefer_prgen int
func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) { func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
call := n.Call call := n.Call
var callX ir.Node // thing being called var callX ir.Node // thing being called
var callArgs []ir.Node // call arguments var callArgs []ir.Node // call arguments
var keepAlive []*ir.Name // KeepAlive list from call, if present
// A helper to recreate the call within the closure. // A helper to recreate the call within the closure.
var mkNewCall func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node var mkNewCall func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node
@ -1470,6 +1471,7 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
case *ir.CallExpr: case *ir.CallExpr:
callX = x.X callX = x.X
callArgs = x.Args callArgs = x.Args
keepAlive = x.KeepAlive
mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node { mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
newcall := ir.NewCallExpr(pos, op, fun, args) newcall := ir.NewCallExpr(pos, op, fun, args)
newcall.IsDDD = x.IsDDD newcall.IsDDD = x.IsDDD
@ -1540,21 +1542,49 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
return argCopy return argCopy
} }
unsafeArgs := make([]*ir.Name, len(callArgs)) // getUnsafeArg looks for an unsafe.Pointer arg that has been
origArgs := callArgs // previously captured into the call's keepalive list, returning
// the name node for it if found.
getUnsafeArg := func(arg ir.Node) *ir.Name {
// Look for uintptr(unsafe.Pointer(name))
if arg.Op() != ir.OCONVNOP {
return nil
}
if !arg.Type().IsUintptr() {
return nil
}
if !arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
return nil
}
arg = arg.(*ir.ConvExpr).X
argname, ok := arg.(*ir.Name)
if !ok {
return nil
}
for i := range keepAlive {
if argname == keepAlive[i] {
return argname
}
}
return nil
}
// Copy the arguments to the function into temps. // Copy the arguments to the function into temps.
pos := n.Pos() //
outerfn := ir.CurFunc // For calls with uintptr(unsafe.Pointer(...)) args that are being
// kept alive (see code in (*orderState).call that does this), use
// the existing arg copy instead of creating a new copy.
unsafeArgs := make([]*ir.Name, len(callArgs))
origArgs := callArgs
var newNames []*ir.Name var newNames []*ir.Name
for i := range callArgs { for i := range callArgs {
arg := callArgs[i] arg := callArgs[i]
var argname *ir.Name var argname *ir.Name
if arg.Op() == ir.OCONVNOP && arg.Type().IsUintptr() && arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() { unsafeArgName := getUnsafeArg(arg)
// No need for copy here; orderState.call() above has already inserted one. if unsafeArgName != nil {
arg = arg.(*ir.ConvExpr).X // arg has been copied already, use keepalive copy
argname = arg.(*ir.Name) argname = unsafeArgName
unsafeArgs[i] = argname unsafeArgs[i] = unsafeArgName
} else { } else {
argname = mkArgCopy(arg) argname = mkArgCopy(arg)
} }
@ -1589,6 +1619,7 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
var noFuncArgs []*ir.Field var noFuncArgs []*ir.Field
noargst := ir.NewFuncType(base.Pos, nil, noFuncArgs, nil) noargst := ir.NewFuncType(base.Pos, nil, noFuncArgs, nil)
wrapGoDefer_prgen++ wrapGoDefer_prgen++
outerfn := ir.CurFunc
wrapname := fmt.Sprintf("%v·dwrap·%d", outerfn, wrapGoDefer_prgen) wrapname := fmt.Sprintf("%v·dwrap·%d", outerfn, wrapGoDefer_prgen)
sym := types.LocalPkg.Lookup(wrapname) sym := types.LocalPkg.Lookup(wrapname)
fn := typecheck.DeclFunc(sym, noargst) fn := typecheck.DeclFunc(sym, noargst)
@ -1622,7 +1653,7 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
if methSelectorExpr != nil { if methSelectorExpr != nil {
methSelectorExpr.X = capName(callX.Pos(), fn, methSelectorExpr.X.(*ir.Name)) methSelectorExpr.X = capName(callX.Pos(), fn, methSelectorExpr.X.(*ir.Name))
} }
ir.FinishCaptureNames(pos, outerfn, fn) ir.FinishCaptureNames(n.Pos(), outerfn, fn)
// This flags a builtin as opposed to a regular call. // This flags a builtin as opposed to a regular call.
irregular := (call.Op() != ir.OCALLFUNC && irregular := (call.Op() != ir.OCALLFUNC &&
@ -1650,7 +1681,7 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
typecheck.Target.Decls = append(typecheck.Target.Decls, fn) typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
// Create closure expr // Create closure expr
clo := ir.NewClosureExpr(pos, fn) clo := ir.NewClosureExpr(n.Pos(), fn)
fn.OClosure = clo fn.OClosure = clo
clo.SetType(fn.Type()) clo.SetType(fn.Type())
@ -1672,7 +1703,7 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
} }
// Create new top level call to closure over argless function. // Create new top level call to closure over argless function.
topcall := ir.NewCallExpr(pos, ir.OCALL, clo, []ir.Node{}) topcall := ir.NewCallExpr(n.Pos(), ir.OCALL, clo, []ir.Node{})
typecheck.Call(topcall) typecheck.Call(topcall)
// Tag the call to insure that directClosureCall doesn't undo our work. // Tag the call to insure that directClosureCall doesn't undo our work.