go/src/cmd/compile/internal/walk/stmt.go
Matthew Dempsky 0a0e3a3dea [dev.typeparams] cmd/compile: move call logic from order.go to escape
This CL moves two bits of related code from order.go to escape
analysis:

1. The recognition of "unsafe uintptr" arguments passed to
syscall-like functions.

2. The wrapping of go/defer function calls in parameter-free function
literals.

As with previous CLs, it would be nice to push this logic even further
forward, but for now escape analysis seems most pragmatic.

A couple side benefits:

1. It allows getting rid of the uintptrEscapesHack kludge.

2. When inserting wrappers, we can move some expressions into the
wrapper and escape analyze them better. For example, the test
expectation changes are all due to slice literals in go/defer calls
where the slice is now constructed at the call site, and can now be
stack allocated.

Change-Id: I73679bcad7fa8d61d2fc52d4cea0dc5ff0de8c0c
Reviewed-on: https://go-review.googlesource.com/c/go/+/330330
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 16:48:12 +00:00

224 lines
4.6 KiB
Go

// Copyright 2009 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 walk
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
)
// The result of walkStmt MUST be assigned back to n, e.g.
// n.Left = walkStmt(n.Left)
func walkStmt(n ir.Node) ir.Node {
if n == nil {
return n
}
ir.SetPos(n)
walkStmtList(n.Init())
switch n.Op() {
default:
if n.Op() == ir.ONAME {
n := n.(*ir.Name)
base.Errorf("%v is not a top level statement", n.Sym())
} else {
base.Errorf("%v is not a top level statement", n.Op())
}
ir.Dump("nottop", n)
return n
case ir.OAS,
ir.OASOP,
ir.OAS2,
ir.OAS2DOTTYPE,
ir.OAS2RECV,
ir.OAS2FUNC,
ir.OAS2MAPR,
ir.OCLOSE,
ir.OCOPY,
ir.OCALLMETH,
ir.OCALLINTER,
ir.OCALL,
ir.OCALLFUNC,
ir.ODELETE,
ir.OSEND,
ir.OPRINT,
ir.OPRINTN,
ir.OPANIC,
ir.ORECOVERFP,
ir.OGETG:
if n.Typecheck() == 0 {
base.Fatalf("missing typecheck: %+v", n)
}
init := ir.TakeInit(n)
n = walkExpr(n, &init)
if n.Op() == ir.ONAME {
// copy rewrote to a statement list and a temp for the length.
// Throw away the temp to avoid plain values as statements.
n = ir.NewBlockStmt(n.Pos(), init)
init = nil
}
if len(init) > 0 {
switch n.Op() {
case ir.OAS, ir.OAS2, ir.OBLOCK:
n.(ir.InitNode).PtrInit().Prepend(init...)
default:
init.Append(n)
n = ir.NewBlockStmt(n.Pos(), init)
}
}
return n
// special case for a receive where we throw away
// the value received.
case ir.ORECV:
n := n.(*ir.UnaryExpr)
return walkRecv(n)
case ir.OBREAK,
ir.OCONTINUE,
ir.OFALL,
ir.OGOTO,
ir.OLABEL,
ir.ODCL,
ir.ODCLCONST,
ir.ODCLTYPE,
ir.OCHECKNIL,
ir.OVARDEF,
ir.OVARKILL,
ir.OVARLIVE:
return n
case ir.OBLOCK:
n := n.(*ir.BlockStmt)
walkStmtList(n.List)
return n
case ir.OCASE:
base.Errorf("case statement out of place")
panic("unreachable")
case ir.ODEFER:
n := n.(*ir.GoDeferStmt)
ir.CurFunc.SetHasDefer(true)
ir.CurFunc.NumDefers++
if ir.CurFunc.NumDefers > maxOpenDefers {
// Don't allow open-coded defers if there are more than
// 8 defers in the function, since we use a single
// byte to record active defers.
ir.CurFunc.SetOpenCodedDeferDisallowed(true)
}
if n.Esc() != ir.EscNever {
// If n.Esc is not EscNever, then this defer occurs in a loop,
// so open-coded defers cannot be used in this function.
ir.CurFunc.SetOpenCodedDeferDisallowed(true)
}
fallthrough
case ir.OGO:
n := n.(*ir.GoDeferStmt)
return walkGoDefer(n)
case ir.OFOR, ir.OFORUNTIL:
n := n.(*ir.ForStmt)
return walkFor(n)
case ir.OIF:
n := n.(*ir.IfStmt)
return walkIf(n)
case ir.ORETURN:
n := n.(*ir.ReturnStmt)
return walkReturn(n)
case ir.OTAILCALL:
n := n.(*ir.TailCallStmt)
return n
case ir.OINLMARK:
n := n.(*ir.InlineMarkStmt)
return n
case ir.OSELECT:
n := n.(*ir.SelectStmt)
walkSelect(n)
return n
case ir.OSWITCH:
n := n.(*ir.SwitchStmt)
walkSwitch(n)
return n
case ir.ORANGE:
n := n.(*ir.RangeStmt)
return walkRange(n)
}
// No return! Each case must return (or panic),
// to avoid confusion about what gets returned
// in the presence of type assertions.
}
func walkStmtList(s []ir.Node) {
for i := range s {
s[i] = walkStmt(s[i])
}
}
// walkFor walks an OFOR or OFORUNTIL node.
func walkFor(n *ir.ForStmt) ir.Node {
if n.Cond != nil {
init := ir.TakeInit(n.Cond)
walkStmtList(init)
n.Cond = walkExpr(n.Cond, &init)
n.Cond = ir.InitExpr(init, n.Cond)
}
n.Post = walkStmt(n.Post)
if n.Op() == ir.OFORUNTIL {
walkStmtList(n.Late)
}
walkStmtList(n.Body)
return n
}
// validGoDeferCall reports whether call is a valid call to appear in
// a go or defer statement; that is, whether it's a regular function
// call without arguments or results.
func validGoDeferCall(call ir.Node) bool {
if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC && len(call.KeepAlive) == 0 {
sig := call.X.Type()
return sig.NumParams()+sig.NumResults() == 0
}
return false
}
// walkGoDefer walks an OGO or ODEFER node.
func walkGoDefer(n *ir.GoDeferStmt) ir.Node {
if !validGoDeferCall(n.Call) {
base.FatalfAt(n.Pos(), "invalid %v call: %v", n.Op(), n.Call)
}
var init ir.Nodes
call := n.Call.(*ir.CallExpr)
call.X = walkExpr(call.X, &init)
if len(init) > 0 {
init.Append(n)
return ir.NewBlockStmt(n.Pos(), init)
}
return n
}
// walkIf walks an OIF node.
func walkIf(n *ir.IfStmt) ir.Node {
n.Cond = walkExpr(n.Cond, n.PtrInit())
walkStmtList(n.Body)
walkStmtList(n.Else)
return n
}