// Copyright 2020 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 ir import ( "cmd/compile/internal/base" "cmd/compile/internal/types" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" "fmt" "strings" "unicode/utf8" ) // A Func corresponds to a single function in a Go program // (and vice versa: each function is denoted by exactly one *Func). // // There are multiple nodes that represent a Func in the IR. // // The ONAME node (Func.Nname) is used for plain references to it. // The ODCLFUNC node (the Func itself) is used for its declaration code. // The OCLOSURE node (Func.OClosure) is used for a reference to a // function literal. // // An imported function will have an ONAME node which points to a Func // with an empty body. // A declared function or method has an ODCLFUNC (the Func itself) and an ONAME. // A function literal is represented directly by an OCLOSURE, but it also // has an ODCLFUNC (and a matching ONAME) representing the compiled // underlying form of the closure, which accesses the captured variables // using a special data structure passed in a register. // // A method declaration is represented like functions, except f.Sym // will be the qualified method name (e.g., "T.m"). // // A method expression (T.M) is represented as an OMETHEXPR node, // in which n.Left and n.Right point to the type and method, respectively. // Each distinct mention of a method expression in the source code // constructs a fresh node. // // A method value (t.M) is represented by ODOTMETH/ODOTINTER // when it is called directly and by OMETHVALUE otherwise. // These are like method expressions, except that for ODOTMETH/ODOTINTER, // the method name is stored in Sym instead of Right. // Each OMETHVALUE ends up being implemented as a new // function, a bit like a closure, with its own ODCLFUNC. // The OMETHVALUE uses n.Func to record the linkage to // the generated ODCLFUNC, but there is no // pointer from the Func back to the OMETHVALUE. type Func struct { miniNode Body Nodes Nname *Name // ONAME node OClosure *ClosureExpr // OCLOSURE node // ONAME nodes for all params/locals for this func/closure, does NOT // include closurevars until transforming closures during walk. // Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs, // with PPARAMs and PPARAMOUTs in order corresponding to the function signature. // Anonymous and blank params are declared as ~pNN (for PPARAMs) and ~rNN (for PPARAMOUTs). Dcl []*Name // ClosureVars lists the free variables that are used within a // function literal, but formally declared in an enclosing // function. The variables in this slice are the closure function's // own copy of the variables, which are used within its function // body. They will also each have IsClosureVar set, and will have // Byval set if they're captured by value. ClosureVars []*Name // Enclosed functions that need to be compiled. // Populated during walk. Closures []*Func // Parents records the parent scope of each scope within a // function. The root scope (0) has no parent, so the i'th // scope's parent is stored at Parents[i-1]. Parents []ScopeID // Marks records scope boundary changes. Marks []Mark FieldTrack map[*obj.LSym]struct{} DebugInfo interface{} LSym *obj.LSym // Linker object in this function's native ABI (Func.ABI) Inl *Inline // funcLitGen and goDeferGen track how many closures have been // created in this function for function literals and go/defer // wrappers, respectively. Used by closureName for creating unique // function names. // // Tracking goDeferGen separately avoids wrappers throwing off // function literal numbering (e.g., runtime/trace_test.TestTraceSymbolize.func11). funcLitGen int32 goDeferGen int32 Label int32 // largest auto-generated label in this function Endlineno src.XPos WBPos src.XPos // position of first write barrier; see SetWBPos Pragma PragmaFlag // go:xxx function annotations flags bitset16 // ABI is a function's "definition" ABI. This is the ABI that // this function's generated code is expecting to be called by. // // For most functions, this will be obj.ABIInternal. It may be // a different ABI for functions defined in assembly or ABI wrappers. // // This is included in the export data and tracked across packages. ABI obj.ABI // ABIRefs is the set of ABIs by which this function is referenced. // For ABIs other than this function's definition ABI, the // compiler generates ABI wrapper functions. This is only tracked // within a package. ABIRefs obj.ABISet NumDefers int32 // number of defer calls in the function NumReturns int32 // number of explicit returns in the function // NWBRCalls records the LSyms of functions called by this // function for go:nowritebarrierrec analysis. Only filled in // if nowritebarrierrecCheck != nil. NWBRCalls *[]SymAndPos // For wrapper functions, WrappedFunc point to the original Func. // Currently only used for go/defer wrappers. WrappedFunc *Func // WasmImport is used by the //go:wasmimport directive to store info about // a WebAssembly function import. WasmImport *WasmImport } // WasmImport stores metadata associated with the //go:wasmimport pragma. type WasmImport struct { Module string Name string } // NewFunc returns a new Func with the given name and type. // // fpos is the position of the "func" token, and npos is the position // of the name identifier. // // TODO(mdempsky): I suspect there's no need for separate fpos and // npos. func NewFunc(fpos, npos src.XPos, sym *types.Sym, typ *types.Type) *Func { name := NewNameAt(npos, sym, typ) name.Class = PFUNC sym.SetFunc(true) fn := &Func{Nname: name} fn.pos = fpos fn.op = ODCLFUNC // Most functions are ABIInternal. The importer or symabis // pass may override this. fn.ABI = obj.ABIInternal fn.SetTypecheck(1) name.Func = fn return fn } func (f *Func) isStmt() {} func (n *Func) copy() Node { panic(n.no("copy")) } func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) } func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) } func (n *Func) editChildrenWithHidden(edit func(Node) Node) { editNodes(n.Body, edit) } func (f *Func) Type() *types.Type { return f.Nname.Type() } func (f *Func) Sym() *types.Sym { return f.Nname.Sym() } func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() } func (f *Func) LinksymABI(abi obj.ABI) *obj.LSym { return f.Nname.LinksymABI(abi) } // An Inline holds fields used for function bodies that can be inlined. type Inline struct { Cost int32 // heuristic cost of inlining this function // Copy of Func.Dcl for use during inlining. This copy is needed // because the function's Dcl may change from later compiler // transformations. This field is also populated when a function // from another package is imported and inlined. Dcl []*Name HaveDcl bool // whether we've loaded Dcl // Function properties, encoded as a string (these are used for // making inlining decisions). See cmd/compile/internal/inline/inlheur. Properties string // CanDelayResults reports whether it's safe for the inliner to delay // initializing the result parameters until immediately before the // "return" statement. CanDelayResults bool } // A Mark represents a scope boundary. type Mark struct { // Pos is the position of the token that marks the scope // change. Pos src.XPos // Scope identifies the innermost scope to the right of Pos. Scope ScopeID } // A ScopeID represents a lexical scope within a function. type ScopeID int32 const ( funcDupok = 1 << iota // duplicate definitions ok funcWrapper // hide frame from users (elide in tracebacks, don't count as a frame for recover()) funcABIWrapper // is an ABI wrapper (also set flagWrapper) funcNeedctxt // function uses context register (has closure variables) // true if closure inside a function; false if a simple function or a // closure in a global variable initialization funcIsHiddenClosure funcIsDeadcodeClosure // true if closure is deadcode funcHasDefer // contains a defer statement funcNilCheckDisabled // disable nil checks when compiling this function funcInlinabilityChecked // inliner has already determined whether the function is inlinable funcNeverReturns // function never returns (in most cases calls panic(), os.Exit(), or equivalent) funcOpenCodedDeferDisallowed // can't do open-coded defers funcClosureResultsLost // closure is called indirectly and we lost track of its results; used by escape analysis funcPackageInit // compiler emitted .init func for package ) type SymAndPos struct { Sym *obj.LSym // LSym of callee Pos src.XPos // line of call } func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 } func (f *Func) Wrapper() bool { return f.flags&funcWrapper != 0 } func (f *Func) ABIWrapper() bool { return f.flags&funcABIWrapper != 0 } func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 } func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 } func (f *Func) IsDeadcodeClosure() bool { return f.flags&funcIsDeadcodeClosure != 0 } func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 } func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 } func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 } func (f *Func) NeverReturns() bool { return f.flags&funcNeverReturns != 0 } func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 } func (f *Func) ClosureResultsLost() bool { return f.flags&funcClosureResultsLost != 0 } func (f *Func) IsPackageInit() bool { return f.flags&funcPackageInit != 0 } func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) } func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) } func (f *Func) SetABIWrapper(b bool) { f.flags.set(funcABIWrapper, b) } func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) } func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) } func (f *Func) SetIsDeadcodeClosure(b bool) { f.flags.set(funcIsDeadcodeClosure, b) } func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) } func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) } func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) } func (f *Func) SetNeverReturns(b bool) { f.flags.set(funcNeverReturns, b) } func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) } func (f *Func) SetClosureResultsLost(b bool) { f.flags.set(funcClosureResultsLost, b) } func (f *Func) SetIsPackageInit(b bool) { f.flags.set(funcPackageInit, b) } func (f *Func) SetWBPos(pos src.XPos) { if base.Debug.WB != 0 { base.WarnfAt(pos, "write barrier") } if !f.WBPos.IsKnown() { f.WBPos = pos } } // FuncName returns the name (without the package) of the function f. func FuncName(f *Func) string { if f == nil || f.Nname == nil { return "" } return f.Sym().Name } // PkgFuncName returns the name of the function referenced by f, with package // prepended. // // This differs from the compiler's internal convention where local functions // lack a package. This is primarily useful when the ultimate consumer of this // is a human looking at message. func PkgFuncName(f *Func) string { if f == nil || f.Nname == nil { return "" } s := f.Sym() pkg := s.Pkg return pkg.Path + "." + s.Name } // LinkFuncName returns the name of the function f, as it will appear in the // symbol table of the final linked binary. func LinkFuncName(f *Func) string { if f == nil || f.Nname == nil { return "" } s := f.Sym() pkg := s.Pkg return objabi.PathToPrefix(pkg.Path) + "." + s.Name } // ParseLinkFuncName parsers a symbol name (as returned from LinkFuncName) back // to the package path and local symbol name. func ParseLinkFuncName(name string) (pkg, sym string, err error) { pkg, sym = splitPkg(name) if pkg == "" { return "", "", fmt.Errorf("no package path in name") } pkg, err = objabi.PrefixToPath(pkg) // unescape if err != nil { return "", "", fmt.Errorf("malformed package path: %v", err) } return pkg, sym, nil } // Borrowed from x/mod. func modPathOK(r rune) bool { if r < utf8.RuneSelf { return r == '-' || r == '.' || r == '_' || r == '~' || '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' } return false } func escapedImportPathOK(r rune) bool { return modPathOK(r) || r == '+' || r == '/' || r == '%' } // splitPkg splits the full linker symbol name into package and local symbol // name. func splitPkg(name string) (pkgpath, sym string) { // package-sym split is at first dot after last the / that comes before // any characters illegal in a package path. lastSlashIdx := 0 for i, r := range name { // Catches cases like: // * example.foo[sync/atomic.Uint64]. // * example%2ecom.foo[sync/atomic.Uint64]. // // Note that name is still escaped; unescape occurs after splitPkg. if !escapedImportPathOK(r) { break } if r == '/' { lastSlashIdx = i } } for i := lastSlashIdx; i < len(name); i++ { r := name[i] if r == '.' { return name[:i], name[i+1:] } } return "", name } var CurFunc *Func // WithFunc invokes do with CurFunc and base.Pos set to curfn and // curfn.Pos(), respectively, and then restores their previous values // before returning. func WithFunc(curfn *Func, do func()) { oldfn, oldpos := CurFunc, base.Pos defer func() { CurFunc, base.Pos = oldfn, oldpos }() CurFunc, base.Pos = curfn, curfn.Pos() do() } func FuncSymName(s *types.Sym) string { return s.Name + "·f" } // ClosureDebugRuntimeCheck applies boilerplate checks for debug flags // and compiling runtime. func ClosureDebugRuntimeCheck(clo *ClosureExpr) { if base.Debug.Closure > 0 { if clo.Esc() == EscHeap { base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars) } else { base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars) } } if base.Flag.CompilingRuntime && clo.Esc() == EscHeap && !clo.IsGoWrap { base.ErrorfAt(clo.Pos(), 0, "heap-allocated closure %s, not allowed in runtime", FuncName(clo.Func)) } } // IsTrivialClosure reports whether closure clo has an // empty list of captured vars. func IsTrivialClosure(clo *ClosureExpr) bool { return len(clo.Func.ClosureVars) == 0 } // globClosgen is like Func.Closgen, but for the global scope. var globClosgen int32 // closureName generates a new unique name for a closure within outerfn at pos. func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym { pkg := types.LocalPkg outer := "glob." var prefix string switch why { default: base.FatalfAt(pos, "closureName: bad Op: %v", why) case OCLOSURE: if outerfn == nil || outerfn.OClosure == nil { prefix = "func" } case OGO: prefix = "gowrap" case ODEFER: prefix = "deferwrap" } gen := &globClosgen // There may be multiple functions named "_". In those // cases, we can't use their individual Closgens as it // would lead to name clashes. if outerfn != nil && !IsBlank(outerfn.Nname) { pkg = outerfn.Sym().Pkg outer = FuncName(outerfn) if why == OCLOSURE { gen = &outerfn.funcLitGen } else { gen = &outerfn.goDeferGen } } // If this closure was created due to inlining, then incorporate any // inlined functions' names into the closure's linker symbol name // too (#60324). if inlIndex := base.Ctxt.InnermostPos(pos).Base().InliningIndex(); inlIndex >= 0 { names := []string{outer} base.Ctxt.InlTree.AllParents(inlIndex, func(call obj.InlinedCall) { names = append(names, call.Name) }) outer = strings.Join(names, ".") } *gen++ return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen)) } // NewClosureFunc creates a new Func to represent a function literal // with the given type. // // fpos the position used for the underlying ODCLFUNC and ONAME, // whereas cpos is the position used for the OCLOSURE. They're // separate because in the presence of inlining, the OCLOSURE node // should have an inline-adjusted position, whereas the ODCLFUNC and // ONAME must not. // // outerfn is the enclosing function, if any. The returned function is // appending to pkg.Funcs. // // why is the reason we're generating this Func. It can be OCLOSURE // (for a normal function literal) or OGO or ODEFER (for wrapping a // call expression that has parameters or results). func NewClosureFunc(fpos, cpos src.XPos, why Op, typ *types.Type, outerfn *Func, pkg *Package) *Func { fn := NewFunc(fpos, fpos, closureName(outerfn, cpos, why), typ) fn.SetIsHiddenClosure(outerfn != nil) if outerfn != nil { fn.SetDupok(outerfn.Dupok()) // if the outer function is dupok, so is the closure } clo := &ClosureExpr{Func: fn} clo.op = OCLOSURE clo.pos = cpos clo.SetType(typ) clo.SetTypecheck(1) fn.OClosure = clo fn.Nname.Defn = fn pkg.Funcs = append(pkg.Funcs, fn) return fn } // IsFuncPCIntrinsic returns whether n is a direct call of internal/abi.FuncPCABIxxx functions. func IsFuncPCIntrinsic(n *CallExpr) bool { if n.Op() != OCALLFUNC || n.Fun.Op() != ONAME { return false } fn := n.Fun.(*Name).Sym() return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") && fn.Pkg.Path == "internal/abi" } // IsIfaceOfFunc inspects whether n is an interface conversion from a direct // reference of a func. If so, it returns referenced Func; otherwise nil. // // This is only usable before walk.walkConvertInterface, which converts to an // OMAKEFACE. func IsIfaceOfFunc(n Node) *Func { if n, ok := n.(*ConvExpr); ok && n.Op() == OCONVIFACE { if name, ok := n.X.(*Name); ok && name.Op() == ONAME && name.Class == PFUNC { return name.Func } } return nil } // FuncPC returns a uintptr-typed expression that evaluates to the PC of a // function as uintptr, as returned by internal/abi.FuncPC{ABI0,ABIInternal}. // // n should be a Node of an interface type, as is passed to // internal/abi.FuncPC{ABI0,ABIInternal}. // // TODO(prattmic): Since n is simply an interface{} there is no assertion that // it is actually a function at all. Perhaps we should emit a runtime type // assertion? func FuncPC(pos src.XPos, n Node, wantABI obj.ABI) Node { if !n.Type().IsInterface() { base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s expects an interface value, got %v", wantABI, n.Type()) } if fn := IsIfaceOfFunc(n); fn != nil { name := fn.Nname abi := fn.ABI if abi != wantABI { base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s expects an %v function, %s is defined as %v", wantABI, wantABI, name.Sym().Name, abi) } var e Node = NewLinksymExpr(pos, name.LinksymABI(abi), types.Types[types.TUINTPTR]) e = NewAddrExpr(pos, e) e.SetType(types.Types[types.TUINTPTR].PtrTo()) e = NewConvExpr(pos, OCONVNOP, types.Types[types.TUINTPTR], e) e.SetTypecheck(1) return e } // fn is not a defined function. It must be ABIInternal. // Read the address from func value, i.e. *(*uintptr)(idata(fn)). if wantABI != obj.ABIInternal { base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s does not accept func expression, which is ABIInternal", wantABI) } var e Node = NewUnaryExpr(pos, OIDATA, n) e.SetType(types.Types[types.TUINTPTR].PtrTo()) e.SetTypecheck(1) e = NewStarExpr(pos, e) e.SetType(types.Types[types.TUINTPTR]) e.SetTypecheck(1) return e } // DeclareParams creates Names for all of the parameters in fn's // signature and adds them to fn.Dcl. // // If setNname is true, then it also sets types.Field.Nname for each // parameter. func (fn *Func) DeclareParams(setNname bool) { if fn.Dcl != nil { base.FatalfAt(fn.Pos(), "%v already has Dcl", fn) } declareParams := func(params []*types.Field, ctxt Class, prefix string, offset int) { for i, param := range params { sym := param.Sym if sym == nil || sym.IsBlank() { sym = fn.Sym().Pkg.LookupNum(prefix, i) } name := NewNameAt(param.Pos, sym, param.Type) name.Class = ctxt name.Curfn = fn fn.Dcl[offset+i] = name if setNname { param.Nname = name } } } sig := fn.Type() params := sig.RecvParams() results := sig.Results() fn.Dcl = make([]*Name, len(params)+len(results)) declareParams(params, PPARAM, "~p", 0) declareParams(results, PPARAMOUT, "~r", len(params)) }