...
▽

Source file src/cmd/internal/obj/inl.go

Documentation: cmd/internal/obj

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package obj
     6  
     7  import "cmd/internal/src"
     8  
     9  // InlTree is a collection of inlined calls. The Parent field of an
    10  // InlinedCall is the index of another InlinedCall in InlTree.
    11  //
    12  // The compiler maintains a global inlining tree and adds a node to it
    13  // every time a function is inlined. For example, suppose f() calls g()
    14  // and g has two calls to h(), and that f, g, and h are inlineable:
    15  //
    16  //	 1 func main() {
    17  //	 2     f()
    18  //	 3 }
    19  //	 4 func f() {
    20  //	 5     g()
    21  //	 6 }
    22  //	 7 func g() {
    23  //	 8     h()
    24  //	 9     h()
    25  //	10 }
    26  //	11 func h() {
    27  //	12     println("H")
    28  //	13 }
    29  //
    30  // Assuming the global tree starts empty, inlining will produce the
    31  // following tree:
    32  //
    33  //	[]InlinedCall{
    34  //	  {Parent: -1, Func: "f", Pos: <line 2>},
    35  //	  {Parent:  0, Func: "g", Pos: <line 5>},
    36  //	  {Parent:  1, Func: "h", Pos: <line 8>},
    37  //	  {Parent:  1, Func: "h", Pos: <line 9>},
    38  //	}
    39  //
    40  // The nodes of h inlined into main will have inlining indexes 2 and 3.
    41  //
    42  // Eventually, the compiler extracts a per-function inlining tree from
    43  // the global inlining tree (see pcln.go).
    44  type InlTree struct {
    45  	nodes []InlinedCall
    46  }
    47  
    48  // InlinedCall is a node in an InlTree.
    49  type InlinedCall struct {
    50  	Parent   int      // index of the parent in the InlTree or < 0 if outermost call
    51  	Pos      src.XPos // position of the inlined call
    52  	Func     *LSym    // function that was inlined
    53  	Name     string   // bare name of the function (w/o package prefix)
    54  	ParentPC int32    // PC of instruction just before inlined body. Only valid in local trees.
    55  }
    56  
    57  // Add adds a new call to the tree, returning its index.
    58  func (tree *InlTree) Add(parent int, pos src.XPos, func_ *LSym, name string) int {
    59  	r := len(tree.nodes)
    60  	call := InlinedCall{
    61  		Parent: parent,
    62  		Pos:    pos,
    63  		Func:   func_,
    64  		Name:   name,
    65  	}
    66  	tree.nodes = append(tree.nodes, call)
    67  	return r
    68  }
    69  
    70  // AllParents invokes do on each InlinedCall in the inlining call
    71  // stack, from outermost to innermost.
    72  //
    73  // That is, if inlIndex corresponds to f inlining g inlining h,
    74  // AllParents invokes do with the call for inlining g into f, and then
    75  // inlining h into g.
    76  func (tree *InlTree) AllParents(inlIndex int, do func(InlinedCall)) {
    77  	if inlIndex >= 0 {
    78  		call := tree.nodes[inlIndex]
    79  		tree.AllParents(call.Parent, do)
    80  		do(call)
    81  	}
    82  }
    83  
    84  func (tree *InlTree) Parent(inlIndex int) int {
    85  	return tree.nodes[inlIndex].Parent
    86  }
    87  
    88  func (tree *InlTree) InlinedFunction(inlIndex int) *LSym {
    89  	return tree.nodes[inlIndex].Func
    90  }
    91  
    92  func (tree *InlTree) InlinedFuncName(inlIndex int) string {
    93  	return tree.nodes[inlIndex].Name
    94  }
    95  
    96  func (tree *InlTree) InlinedFuncPkg(inlIndex int) string {
    97  	return tree.nodes[inlIndex].Func.Pkg
    98  }
    99  
   100  func (tree *InlTree) CallPos(inlIndex int) src.XPos {
   101  	return tree.nodes[inlIndex].Pos
   102  }
   103  
   104  func (tree *InlTree) setParentPC(inlIndex int, pc int32) {
   105  	tree.nodes[inlIndex].ParentPC = pc
   106  }
   107  
   108  // OutermostPos returns the outermost position corresponding to xpos,
   109  // which is where xpos was ultimately inlined to. In the example for
   110  // InlTree, main() contains inlined AST nodes from h(), but the
   111  // outermost position for those nodes is line 2.
   112  func (ctxt *Link) OutermostPos(xpos src.XPos) src.Pos {
   113  	pos := ctxt.InnermostPos(xpos)
   114  
   115  	outerxpos := xpos
   116  	for ix := pos.Base().InliningIndex(); ix >= 0; {
   117  		call := ctxt.InlTree.nodes[ix]
   118  		ix = call.Parent
   119  		outerxpos = call.Pos
   120  	}
   121  	return ctxt.PosTable.Pos(outerxpos)
   122  }
   123  
   124  // InnermostPos returns the innermost position corresponding to xpos,
   125  // that is, the code that is inlined and that inlines nothing else.
   126  // In the example for InlTree above, the code for println within h
   127  // would have an innermost position with line number 12, whether
   128  // h was not inlined, inlined into g, g-then-f, or g-then-f-then-main.
   129  // This corresponds to what someone debugging main, f, g, or h might
   130  // expect to see while single-stepping.
   131  func (ctxt *Link) InnermostPos(xpos src.XPos) src.Pos {
   132  	return ctxt.PosTable.Pos(xpos)
   133  }
   134  
   135  // AllPos invokes do with every position in the inlining call stack for xpos,
   136  // from outermost to innermost. That is, xpos corresponds to f inlining g inlining h,
   137  // AllPos invokes do with the position in f, then the position in g, then the position in h.
   138  func (ctxt *Link) AllPos(xpos src.XPos, do func(src.Pos)) {
   139  	pos := ctxt.InnermostPos(xpos)
   140  	ctxt.InlTree.AllParents(pos.Base().InliningIndex(), func(call InlinedCall) {
   141  		do(ctxt.InnermostPos(call.Pos))
   142  	})
   143  	do(pos)
   144  }
   145  
   146  func dumpInlTree(ctxt *Link, tree InlTree) {
   147  	for i, call := range tree.nodes {
   148  		pos := ctxt.PosTable.Pos(call.Pos)
   149  		ctxt.Logf("%0d | %0d | %s (%s) pc=%d\n", i, call.Parent, call.Func, pos, call.ParentPC)
   150  	}
   151  }
   152  

View as plain text