Home | History | Annotate | Download | only in 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 }
     54 
     55 // Add adds a new call to the tree, returning its index.
     56 func (tree *InlTree) Add(parent int, pos src.XPos, func_ *LSym) int {
     57 	r := len(tree.nodes)
     58 	call := InlinedCall{
     59 		Parent: parent,
     60 		Pos:    pos,
     61 		Func:   func_,
     62 	}
     63 	tree.nodes = append(tree.nodes, call)
     64 	return r
     65 }
     66 
     67 func (tree *InlTree) Parent(inlIndex int) int {
     68 	return tree.nodes[inlIndex].Parent
     69 }
     70 
     71 func (tree *InlTree) InlinedFunction(inlIndex int) *LSym {
     72 	return tree.nodes[inlIndex].Func
     73 }
     74 
     75 func (tree *InlTree) CallPos(inlIndex int) src.XPos {
     76 	return tree.nodes[inlIndex].Pos
     77 }
     78 
     79 // OutermostPos returns the outermost position corresponding to xpos,
     80 // which is where xpos was ultimately inlined to. In the example for
     81 // InlTree, main() contains inlined AST nodes from h(), but the
     82 // outermost position for those nodes is line 2.
     83 func (ctxt *Link) OutermostPos(xpos src.XPos) src.Pos {
     84 	pos := ctxt.InnermostPos(xpos)
     85 
     86 	outerxpos := xpos
     87 	for ix := pos.Base().InliningIndex(); ix >= 0; {
     88 		call := ctxt.InlTree.nodes[ix]
     89 		ix = call.Parent
     90 		outerxpos = call.Pos
     91 	}
     92 	return ctxt.PosTable.Pos(outerxpos)
     93 }
     94 
     95 // InnermostPos returns the innermost position corresponding to xpos,
     96 // that is, the code that is inlined and that inlines nothing else.
     97 // In the example for InlTree above, the code for println within h
     98 // would have an innermost position with line number 12, whether
     99 // h was not inlined, inlined into g, g-then-f, or g-then-f-then-main.
    100 // This corresponds to what someone debugging main, f, g, or h might
    101 // expect to see while single-stepping.
    102 func (ctxt *Link) InnermostPos(xpos src.XPos) src.Pos {
    103 	return ctxt.PosTable.Pos(xpos)
    104 }
    105 
    106 func dumpInlTree(ctxt *Link, tree InlTree) {
    107 	for i, call := range tree.nodes {
    108 		pos := ctxt.PosTable.Pos(call.Pos)
    109 		ctxt.Logf("%0d | %0d | %s (%s)\n", i, call.Parent, call.Func, pos)
    110 	}
    111 }
    112