blob: 0bf76680c4626546f00fee0890a4418a157c77b5 [file] [log] [blame]
// 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)
n.Call = wrapCall(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 {
undoVariadic(n)
}
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)
}
// undoVariadic turns a call to a variadic function of the form
//
// f(a, b, []T{c, d, e}...)
//
// back into
//
// f(a, b, c, d, e)
//
func undoVariadic(call *ir.CallExpr) {
if call.IsDDD {
last := len(call.Args) - 1
if va := call.Args[last]; va.Op() == ir.OSLICELIT {
va := va.(*ir.CompLitExpr)
call.Args = append(call.Args[:last], va.List...)
call.IsDDD = false
}
}
}