// 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/src" ) // 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") and // f.Func.Shortname is the bare method name (e.g., "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 OCALLPART otherwise. // These are like method expressions, except that for ODOTMETH/ODOTINTER, // the method name is stored in Sym instead of Right. // Each OCALLPART ends up being implemented as a new // function, a bit like a closure, with its own ODCLFUNC. // The OCALLPART uses n.Func to record the linkage to // the generated ODCLFUNC, but there is no // pointer from the Func back to the OCALLPART. type Func struct { miniNode typ *types.Type Body Nodes Iota int64 Nname *Name // ONAME node OClosure *ClosureExpr // OCLOSURE node Shortname *types.Sym // Extra entry code for the function. For example, allocate and initialize // memory for escaping parameters. Enter Nodes Exit Nodes // ONAME nodes for all params/locals for this func/closure, does NOT // include closurevars until transformclosure runs. Dcl []*Name ClosureEnter Nodes // list of ONAME nodes (or OADDR-of-ONAME nodes, for output parameters) of captured variables ClosureType Ntype // closure representation type ClosureVars []*Name // closure params; each has closurevar set // 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 Inl *Inline // Closgen tracks how many closures have been generated within // this function. Used by closurename for creating unique // function names. Closgen 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 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 } func NewFunc(pos src.XPos) *Func { f := new(Func) f.pos = pos f.op = ODCLFUNC f.Iota = -1 return f } func (f *Func) isStmt() {} func (f *Func) Type() *types.Type { return f.typ } func (f *Func) SetType(x *types.Type) { f.typ = x } func (f *Func) Sym() *types.Sym { return f.Nname.Sym() } func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() } // An Inline holds fields used for function bodies that can be inlined. type Inline struct { Cost int32 // heuristic cost of inlining this function // Copies of Func.Dcl and Nbody for use during inlining. Dcl []*Name Body []Node } // 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 // is method wrapper funcNeedctxt // function uses context register (has closure variables) funcReflectMethod // function calls reflect.Type.Method or MethodByName // true if closure inside a function; false if a simple function or a // closure in a global variable initialization funcIsHiddenClosure funcHasDefer // contains a defer statement funcNilCheckDisabled // disable nil checks when compiling this function funcInlinabilityChecked // inliner has already determined whether the function is inlinable funcExportInline // include inline body in export data funcInstrumentBody // add race/msan instrumentation during SSA construction funcOpenCodedDeferDisallowed // can't do open-coded defers funcClosureCalled // closure is only immediately called ) 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) Needctxt() bool { return f.flags&funcNeedctxt != 0 } func (f *Func) ReflectMethod() bool { return f.flags&funcReflectMethod != 0 } func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 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) ExportInline() bool { return f.flags&funcExportInline != 0 } func (f *Func) InstrumentBody() bool { return f.flags&funcInstrumentBody != 0 } func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 } func (f *Func) ClosureCalled() bool { return f.flags&funcClosureCalled != 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) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) } func (f *Func) SetReflectMethod(b bool) { f.flags.set(funcReflectMethod, b) } func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, 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) SetExportInline(b bool) { f.flags.set(funcExportInline, b) } func (f *Func) SetInstrumentBody(b bool) { f.flags.set(funcInstrumentBody, b) } func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) } func (f *Func) SetClosureCalled(b bool) { f.flags.set(funcClosureCalled, 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 n. 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 n, with package prepended. // This differs from the compiler's internal convention where local functions lack a package // because the ultimate consumer of this is a human looking at an IDE; package is only empty // if the compilation package is actually the empty string. func PkgFuncName(f *Func) string { if f == nil || f.Nname == nil { return "" } s := f.Sym() pkg := s.Pkg p := base.Ctxt.Pkgpath if pkg != nil && pkg.Path != "" { p = pkg.Path } if p == "" { return s.Name } return p + "." + s.Name } var CurFunc *Func func FuncSymName(s *types.Sym) string { return s.Name + "·f" } // NewFuncNameAt generates a new name node for a function or method. func NewFuncNameAt(pos src.XPos, s *types.Sym, fn *Func) *Name { if fn.Nname != nil { base.Fatalf("newFuncName - already have name") } n := NewNameAt(pos, s) n.SetFunc(fn) fn.Nname = n return n } // MarkFunc marks a node as a function. func MarkFunc(n *Name) { if n.Op() != ONAME || n.Class_ != Pxxx { base.Fatalf("expected ONAME/Pxxx node, got %v", n) } n.Class_ = PFUNC n.Sym().SetFunc(true) } // 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 { base.ErrorfAt(clo.Pos(), "heap-allocated closure, not allowed in runtime") } } // IsTrivialClosure reports whether closure clo has an // empty list of captured vars. func IsTrivialClosure(clo *ClosureExpr) bool { return len(clo.Func.ClosureVars) == 0 }