mirror of https://github.com/golang/go.git
328 lines
7.3 KiB
Go
328 lines
7.3 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"
|
|
"cmd/compile/internal/typecheck"
|
|
)
|
|
|
|
// 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.ORECOVER,
|
|
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
|
|
}
|
|
|
|
// walkGoDefer walks an OGO or ODEFER node.
|
|
func walkGoDefer(n *ir.GoDeferStmt) ir.Node {
|
|
var init ir.Nodes
|
|
switch call := n.Call; call.Op() {
|
|
case ir.OPRINT, ir.OPRINTN:
|
|
call := call.(*ir.CallExpr)
|
|
n.Call = wrapCall(call, &init)
|
|
|
|
case ir.ODELETE:
|
|
call := call.(*ir.CallExpr)
|
|
if mapfast(call.Args[0].Type()) == mapslow {
|
|
n.Call = wrapCall(call, &init)
|
|
} else {
|
|
n.Call = walkExpr(call, &init)
|
|
}
|
|
|
|
case ir.OCOPY:
|
|
call := call.(*ir.BinaryExpr)
|
|
n.Call = walkCopy(call, &init, true)
|
|
|
|
case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
|
|
call := call.(*ir.CallExpr)
|
|
if len(call.KeepAlive) > 0 {
|
|
n.Call = wrapCall(call, &init)
|
|
} else {
|
|
n.Call = walkExpr(call, &init)
|
|
}
|
|
|
|
default:
|
|
n.Call = walkExpr(call, &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
|
|
}
|
|
|
|
// Rewrite
|
|
// go builtin(x, y, z)
|
|
// into
|
|
// go func(a1, a2, a3) {
|
|
// builtin(a1, a2, a3)
|
|
// }(x, y, z)
|
|
// for print, println, and delete.
|
|
//
|
|
// Rewrite
|
|
// go f(x, y, uintptr(unsafe.Pointer(z)))
|
|
// into
|
|
// go func(a1, a2, a3) {
|
|
// f(a1, a2, uintptr(a3))
|
|
// }(x, y, unsafe.Pointer(z))
|
|
// for function contains unsafe-uintptr arguments.
|
|
|
|
var wrapCall_prgen int
|
|
|
|
// The result of wrapCall MUST be assigned back to n, e.g.
|
|
// n.Left = wrapCall(n.Left, init)
|
|
func wrapCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
|
|
if len(n.Init()) != 0 {
|
|
walkStmtList(n.Init())
|
|
init.Append(ir.TakeInit(n)...)
|
|
}
|
|
|
|
isBuiltinCall := n.Op() != ir.OCALLFUNC && n.Op() != ir.OCALLMETH && n.Op() != ir.OCALLINTER
|
|
|
|
// Turn f(a, b, []T{c, d, e}...) back into f(a, b, c, d, e).
|
|
if !isBuiltinCall && n.IsDDD {
|
|
last := len(n.Args) - 1
|
|
if va := n.Args[last]; va.Op() == ir.OSLICELIT {
|
|
va := va.(*ir.CompLitExpr)
|
|
n.Args = append(n.Args[:last], va.List...)
|
|
n.IsDDD = false
|
|
}
|
|
}
|
|
|
|
wrapArgs := n.Args
|
|
// If there's a receiver argument, it needs to be passed through the wrapper too.
|
|
if n.Op() == ir.OCALLMETH || n.Op() == ir.OCALLINTER {
|
|
recv := n.X.(*ir.SelectorExpr).X
|
|
wrapArgs = append([]ir.Node{recv}, wrapArgs...)
|
|
}
|
|
|
|
// origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion.
|
|
origArgs := make([]ir.Node, len(wrapArgs))
|
|
var funcArgs []*ir.Field
|
|
for i, arg := range wrapArgs {
|
|
s := typecheck.LookupNum("a", i)
|
|
if !isBuiltinCall && arg.Op() == ir.OCONVNOP && arg.Type().IsUintptr() && arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
|
|
origArgs[i] = arg
|
|
arg = arg.(*ir.ConvExpr).X
|
|
wrapArgs[i] = arg
|
|
}
|
|
funcArgs = append(funcArgs, ir.NewField(base.Pos, s, nil, arg.Type()))
|
|
}
|
|
t := ir.NewFuncType(base.Pos, nil, funcArgs, nil)
|
|
|
|
wrapCall_prgen++
|
|
sym := typecheck.LookupNum("wrap·", wrapCall_prgen)
|
|
fn := typecheck.DeclFunc(sym, t)
|
|
|
|
args := ir.ParamNames(t.Type())
|
|
for i, origArg := range origArgs {
|
|
if origArg == nil {
|
|
continue
|
|
}
|
|
args[i] = ir.NewConvExpr(base.Pos, origArg.Op(), origArg.Type(), args[i])
|
|
}
|
|
if n.Op() == ir.OCALLMETH || n.Op() == ir.OCALLINTER {
|
|
// Move wrapped receiver argument back to its appropriate place.
|
|
recv := typecheck.Expr(args[0])
|
|
n.X.(*ir.SelectorExpr).X = recv
|
|
args = args[1:]
|
|
}
|
|
call := ir.NewCallExpr(base.Pos, n.Op(), n.X, args)
|
|
if !isBuiltinCall {
|
|
call.SetOp(ir.OCALL)
|
|
call.IsDDD = n.IsDDD
|
|
}
|
|
fn.Body = []ir.Node{call}
|
|
|
|
typecheck.FinishFuncBody()
|
|
|
|
typecheck.Func(fn)
|
|
typecheck.Stmts(fn.Body)
|
|
typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
|
|
|
|
call = ir.NewCallExpr(base.Pos, ir.OCALL, fn.Nname, wrapArgs)
|
|
return walkExpr(typecheck.Stmt(call), init)
|
|
}
|