inl.go raw

   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 "github.com/twitchyliquid64/golang-asm/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  	ParentPC int32    // PC of instruction just before inlined body. Only valid in local trees.
  54  }
  55  
  56  // Add adds a new call to the tree, returning its index.
  57  func (tree *InlTree) Add(parent int, pos src.XPos, func_ *LSym) int {
  58  	r := len(tree.nodes)
  59  	call := InlinedCall{
  60  		Parent: parent,
  61  		Pos:    pos,
  62  		Func:   func_,
  63  	}
  64  	tree.nodes = append(tree.nodes, call)
  65  	return r
  66  }
  67  
  68  func (tree *InlTree) Parent(inlIndex int) int {
  69  	return tree.nodes[inlIndex].Parent
  70  }
  71  
  72  func (tree *InlTree) InlinedFunction(inlIndex int) *LSym {
  73  	return tree.nodes[inlIndex].Func
  74  }
  75  
  76  func (tree *InlTree) CallPos(inlIndex int) src.XPos {
  77  	return tree.nodes[inlIndex].Pos
  78  }
  79  
  80  func (tree *InlTree) setParentPC(inlIndex int, pc int32) {
  81  	tree.nodes[inlIndex].ParentPC = pc
  82  }
  83  
  84  // OutermostPos returns the outermost position corresponding to xpos,
  85  // which is where xpos was ultimately inlined to. In the example for
  86  // InlTree, main() contains inlined AST nodes from h(), but the
  87  // outermost position for those nodes is line 2.
  88  func (ctxt *Link) OutermostPos(xpos src.XPos) src.Pos {
  89  	pos := ctxt.InnermostPos(xpos)
  90  
  91  	outerxpos := xpos
  92  	for ix := pos.Base().InliningIndex(); ix >= 0; {
  93  		call := ctxt.InlTree.nodes[ix]
  94  		ix = call.Parent
  95  		outerxpos = call.Pos
  96  	}
  97  	return ctxt.PosTable.Pos(outerxpos)
  98  }
  99  
 100  // InnermostPos returns the innermost position corresponding to xpos,
 101  // that is, the code that is inlined and that inlines nothing else.
 102  // In the example for InlTree above, the code for println within h
 103  // would have an innermost position with line number 12, whether
 104  // h was not inlined, inlined into g, g-then-f, or g-then-f-then-main.
 105  // This corresponds to what someone debugging main, f, g, or h might
 106  // expect to see while single-stepping.
 107  func (ctxt *Link) InnermostPos(xpos src.XPos) src.Pos {
 108  	return ctxt.PosTable.Pos(xpos)
 109  }
 110  
 111  // AllPos returns a slice of the positions inlined at xpos, from
 112  // innermost (index zero) to outermost.  To avoid gratuitous allocation
 113  // the result is passed in and extended if necessary.
 114  func (ctxt *Link) AllPos(xpos src.XPos, result []src.Pos) []src.Pos {
 115  	pos := ctxt.InnermostPos(xpos)
 116  	result = result[:0]
 117  	result = append(result, ctxt.PosTable.Pos(xpos))
 118  	for ix := pos.Base().InliningIndex(); ix >= 0; {
 119  		call := ctxt.InlTree.nodes[ix]
 120  		ix = call.Parent
 121  		result = append(result, ctxt.PosTable.Pos(call.Pos))
 122  	}
 123  	return result
 124  }
 125  
 126  func dumpInlTree(ctxt *Link, tree InlTree) {
 127  	for i, call := range tree.nodes {
 128  		pos := ctxt.PosTable.Pos(call.Pos)
 129  		ctxt.Logf("%0d | %0d | %s (%s) pc=%d\n", i, call.Parent, call.Func, pos, call.ParentPC)
 130  	}
 131  }
 132