// 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 } } }