github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/ssa/numberlines.go (about)

     1  // Copyright 2018 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 ssa
     6  
     7  import (
     8  	"github.com/gagliardetto/golang-go/cmd/internal/obj"
     9  	"github.com/gagliardetto/golang-go/cmd/internal/src"
    10  	"fmt"
    11  	"sort"
    12  )
    13  
    14  func isPoorStatementOp(op Op) bool {
    15  	switch op {
    16  	// Note that Nilcheck often vanishes, but when it doesn't, you'd love to start the statement there
    17  	// so that a debugger-user sees the stop before the panic, and can examine the value.
    18  	case OpAddr, OpLocalAddr, OpOffPtr, OpStructSelect, OpPhi, OpITab, OpIData,
    19  		OpIMake, OpStringMake, OpSliceMake, OpStructMake0, OpStructMake1, OpStructMake2, OpStructMake3, OpStructMake4,
    20  		OpConstBool, OpConst8, OpConst16, OpConst32, OpConst64, OpConst32F, OpConst64F:
    21  		return true
    22  	}
    23  	return false
    24  }
    25  
    26  // LosesStmtMark reports whether a prog with op as loses its statement mark on the way to DWARF.
    27  // The attributes from some opcodes are lost in translation.
    28  // TODO: this is an artifact of how funcpctab combines information for instructions at a single PC.
    29  // Should try to fix it there.
    30  func LosesStmtMark(as obj.As) bool {
    31  	// is_stmt does not work for these; it DOES for ANOP even though that generates no code.
    32  	return as == obj.APCDATA || as == obj.AFUNCDATA
    33  }
    34  
    35  // nextGoodStatementIndex returns an index at i or later that is believed
    36  // to be a good place to start the statement for b.  This decision is
    37  // based on v's Op, the possibility of a better later operation, and
    38  // whether the values following i are the same line as v.
    39  // If a better statement index isn't found, then i is returned.
    40  func nextGoodStatementIndex(v *Value, i int, b *Block) int {
    41  	// If the value is the last one in the block, too bad, it will have to do
    42  	// (this assumes that the value ordering vaguely corresponds to the source
    43  	// program execution order, which tends to be true directly after ssa is
    44  	// first built.
    45  	if i >= len(b.Values)-1 {
    46  		return i
    47  	}
    48  	// Skip the likely-ephemeral/fragile opcodes expected to vanish in a rewrite.
    49  	if !isPoorStatementOp(v.Op) {
    50  		return i
    51  	}
    52  	// Look ahead to see what the line number is on the next thing that could be a boundary.
    53  	for j := i + 1; j < len(b.Values); j++ {
    54  		u := b.Values[j]
    55  		if u.Pos.IsStmt() == src.PosNotStmt { // ignore non-statements
    56  			continue
    57  		}
    58  		if u.Pos.SameFileAndLine(v.Pos) {
    59  			if isPoorStatementOp(u.Op) {
    60  				continue // Keep looking, this is also not a good statement op
    61  			}
    62  			return j
    63  		}
    64  		return i
    65  	}
    66  	return i
    67  }
    68  
    69  // notStmtBoundary indicates which value opcodes can never be a statement
    70  // boundary because they don't correspond to a user's understanding of a
    71  // statement boundary.  Called from *Value.reset(), and *Func.newValue(),
    72  // located here to keep all the statement boundary heuristics in one place.
    73  // Note: *Value.reset() filters out OpCopy because of how that is used in
    74  // rewrite.
    75  func notStmtBoundary(op Op) bool {
    76  	switch op {
    77  	case OpCopy, OpPhi, OpVarKill, OpVarDef, OpVarLive, OpUnknown, OpFwdRef, OpArg:
    78  		return true
    79  	}
    80  	return false
    81  }
    82  
    83  func (b *Block) FirstPossibleStmtValue() *Value {
    84  	for _, v := range b.Values {
    85  		if notStmtBoundary(v.Op) {
    86  			continue
    87  		}
    88  		return v
    89  	}
    90  	return nil
    91  }
    92  
    93  func flc(p src.XPos) string {
    94  	if p == src.NoXPos {
    95  		return "none"
    96  	}
    97  	return fmt.Sprintf("(%d):%d:%d", p.FileIndex(), p.Line(), p.Col())
    98  }
    99  
   100  type fileAndPair struct {
   101  	f  int32
   102  	lp lineRange
   103  }
   104  
   105  type fileAndPairs []fileAndPair
   106  
   107  func (fap fileAndPairs) Len() int {
   108  	return len(fap)
   109  }
   110  func (fap fileAndPairs) Less(i, j int) bool {
   111  	return fap[i].f < fap[j].f
   112  }
   113  func (fap fileAndPairs) Swap(i, j int) {
   114  	fap[i], fap[j] = fap[j], fap[i]
   115  }
   116  
   117  // -d=ssa/number_lines/stats=1 (that bit) for line and file distribution statistics
   118  // -d=ssa/number_lines/debug for information about why particular values are marked as statements.
   119  func numberLines(f *Func) {
   120  	po := f.Postorder()
   121  	endlines := make(map[ID]src.XPos)
   122  	ranges := make(map[int]lineRange)
   123  	note := func(p src.XPos) {
   124  		line := uint32(p.Line())
   125  		i := int(p.FileIndex())
   126  		lp, found := ranges[i]
   127  		change := false
   128  		if line < lp.first || !found {
   129  			lp.first = line
   130  			change = true
   131  		}
   132  		if line > lp.last {
   133  			lp.last = line
   134  			change = true
   135  		}
   136  		if change {
   137  			ranges[i] = lp
   138  		}
   139  	}
   140  
   141  	// Visit in reverse post order so that all non-loop predecessors come first.
   142  	for j := len(po) - 1; j >= 0; j-- {
   143  		b := po[j]
   144  		// Find the first interesting position and check to see if it differs from any predecessor
   145  		firstPos := src.NoXPos
   146  		firstPosIndex := -1
   147  		if b.Pos.IsStmt() != src.PosNotStmt {
   148  			note(b.Pos)
   149  		}
   150  		for i := 0; i < len(b.Values); i++ {
   151  			v := b.Values[i]
   152  			if v.Pos.IsStmt() != src.PosNotStmt {
   153  				note(v.Pos)
   154  				// skip ahead to better instruction for this line if possible
   155  				i = nextGoodStatementIndex(v, i, b)
   156  				v = b.Values[i]
   157  				firstPosIndex = i
   158  				firstPos = v.Pos
   159  				v.Pos = firstPos.WithDefaultStmt() // default to default
   160  				break
   161  			}
   162  		}
   163  
   164  		if firstPosIndex == -1 { // Effectively empty block, check block's own Pos, consider preds.
   165  			line := src.NoXPos
   166  			for _, p := range b.Preds {
   167  				pbi := p.Block().ID
   168  				if !endlines[pbi].SameFileAndLine(line) {
   169  					if line == src.NoXPos {
   170  						line = endlines[pbi]
   171  						continue
   172  					} else {
   173  						line = src.NoXPos
   174  						break
   175  					}
   176  
   177  				}
   178  			}
   179  			// If the block has no statement itself and is effectively empty, tag it w/ predecessor(s) but not as a statement
   180  			if b.Pos.IsStmt() == src.PosNotStmt {
   181  				b.Pos = line
   182  				endlines[b.ID] = line
   183  				continue
   184  			}
   185  			// If the block differs from its predecessors, mark it as a statement
   186  			if line == src.NoXPos || !line.SameFileAndLine(b.Pos) {
   187  				b.Pos = b.Pos.WithIsStmt()
   188  				if f.pass.debug > 0 {
   189  					fmt.Printf("Mark stmt effectively-empty-block %s %s %s\n", f.Name, b, flc(b.Pos))
   190  				}
   191  			}
   192  			endlines[b.ID] = b.Pos
   193  			continue
   194  		}
   195  		// check predecessors for any difference; if firstPos differs, then it is a boundary.
   196  		if len(b.Preds) == 0 { // Don't forget the entry block
   197  			b.Values[firstPosIndex].Pos = firstPos.WithIsStmt()
   198  			if f.pass.debug > 0 {
   199  				fmt.Printf("Mark stmt entry-block %s %s %s %s\n", f.Name, b, b.Values[firstPosIndex], flc(firstPos))
   200  			}
   201  		} else { // differing pred
   202  			for _, p := range b.Preds {
   203  				pbi := p.Block().ID
   204  				if !endlines[pbi].SameFileAndLine(firstPos) {
   205  					b.Values[firstPosIndex].Pos = firstPos.WithIsStmt()
   206  					if f.pass.debug > 0 {
   207  						fmt.Printf("Mark stmt differing-pred %s %s %s %s, different=%s ending %s\n",
   208  							f.Name, b, b.Values[firstPosIndex], flc(firstPos), p.Block(), flc(endlines[pbi]))
   209  					}
   210  					break
   211  				}
   212  			}
   213  		}
   214  		// iterate forward setting each new (interesting) position as a statement boundary.
   215  		for i := firstPosIndex + 1; i < len(b.Values); i++ {
   216  			v := b.Values[i]
   217  			if v.Pos.IsStmt() == src.PosNotStmt {
   218  				continue
   219  			}
   220  			note(v.Pos)
   221  			// skip ahead if possible
   222  			i = nextGoodStatementIndex(v, i, b)
   223  			v = b.Values[i]
   224  			if !v.Pos.SameFileAndLine(firstPos) {
   225  				if f.pass.debug > 0 {
   226  					fmt.Printf("Mark stmt new line %s %s %s %s prev pos = %s\n", f.Name, b, v, flc(v.Pos), flc(firstPos))
   227  				}
   228  				firstPos = v.Pos
   229  				v.Pos = v.Pos.WithIsStmt()
   230  			} else {
   231  				v.Pos = v.Pos.WithDefaultStmt()
   232  			}
   233  		}
   234  		if b.Pos.IsStmt() != src.PosNotStmt && !b.Pos.SameFileAndLine(firstPos) {
   235  			if f.pass.debug > 0 {
   236  				fmt.Printf("Mark stmt end of block differs %s %s %s prev pos = %s\n", f.Name, b, flc(b.Pos), flc(firstPos))
   237  			}
   238  			b.Pos = b.Pos.WithIsStmt()
   239  			firstPos = b.Pos
   240  		}
   241  		endlines[b.ID] = firstPos
   242  	}
   243  	if f.pass.stats&1 != 0 {
   244  		// Report summary statistics on the shape of the sparse map about to be constructed
   245  		// TODO use this information to make sparse maps faster.
   246  		var entries fileAndPairs
   247  		for k, v := range ranges {
   248  			entries = append(entries, fileAndPair{int32(k), v})
   249  		}
   250  		sort.Sort(entries)
   251  		total := uint64(0)            // sum over files of maxline(file) - minline(file)
   252  		maxfile := int32(0)           // max(file indices)
   253  		minline := uint32(0xffffffff) // min over files of minline(file)
   254  		maxline := uint32(0)          // max over files of maxline(file)
   255  		for _, v := range entries {
   256  			if f.pass.stats > 1 {
   257  				f.LogStat("file", v.f, "low", v.lp.first, "high", v.lp.last)
   258  			}
   259  			total += uint64(v.lp.last - v.lp.first)
   260  			if maxfile < v.f {
   261  				maxfile = v.f
   262  			}
   263  			if minline > v.lp.first {
   264  				minline = v.lp.first
   265  			}
   266  			if maxline < v.lp.last {
   267  				maxline = v.lp.last
   268  			}
   269  		}
   270  		f.LogStat("SUM_LINE_RANGE", total, "MAXMIN_LINE_RANGE", maxline-minline, "MAXFILE", maxfile, "NFILES", len(entries))
   271  	}
   272  	// cachedLineStarts is an empty sparse map for values that are included within ranges.
   273  	f.cachedLineStarts = newXposmap(ranges)
   274  }