| // 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 | |
| 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 transforming closures during walk. | |
| // Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs, | |
| // with PPARAMs and PPARAMOUTs in order corresponding to the function signature. | |
| // However, as anonymous or blank PPARAMs are not actually declared, | |
| // they are omitted from Dcl. | |
| // Anonymous and blank PPARAMOUTs are declared as ~rNN and ~bNN Names, respectively. | |
| 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 | |
| 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 (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 (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 | |
| // 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 "<nil>" | |
| } | |
| 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 "<nil>" | |
| } | |
| 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" | |
| } | |
| // 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 | |
| } |