...
Run Format

Source file src/cmd/compile/internal/ssa/numberlines.go

Documentation: cmd/compile/internal/ssa

     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  	"cmd/internal/obj"
     9  	"cmd/internal/src"
    10  	"math"
    11  )
    12  
    13  func isPoorStatementOp(op Op) bool {
    14  	switch op {
    15  	// Note that Nilcheck often vanishes, but when it doesn't, you'd love to start the statement there
    16  	// so that a debugger-user sees the stop before the panic, and can examine the value.
    17  	case OpAddr, OpLocalAddr, OpOffPtr, OpStructSelect, OpConstBool, OpConst8, OpConst16, OpConst32, OpConst64, OpConst32F, OpConst64F:
    18  		return true
    19  	}
    20  	return false
    21  }
    22  
    23  // LosesStmtMark reports whether a prog with op as loses its statement mark on the way to DWARF.
    24  // The attributes from some opcodes are lost in translation.
    25  // TODO: this is an artifact of how funcpctab combines information for instructions at a single PC.
    26  // Should try to fix it there.
    27  func LosesStmtMark(as obj.As) bool {
    28  	// is_stmt does not work for these; it DOES for ANOP even though that generates no code.
    29  	return as == obj.APCDATA || as == obj.AFUNCDATA
    30  }
    31  
    32  // nextGoodStatementIndex returns an index at i or later that is believed
    33  // to be a good place to start the statement for b.  This decision is
    34  // based on v's Op, the possibility of a better later operation, and
    35  // whether the values following i are the same line as v.
    36  // If a better statement index isn't found, then i is returned.
    37  func nextGoodStatementIndex(v *Value, i int, b *Block) int {
    38  	// If the value is the last one in the block, too bad, it will have to do
    39  	// (this assumes that the value ordering vaguely corresponds to the source
    40  	// program execution order, which tends to be true directly after ssa is
    41  	// first built.
    42  	if i >= len(b.Values)-1 {
    43  		return i
    44  	}
    45  	// Only consider the likely-ephemeral/fragile opcodes expected to vanish in a rewrite.
    46  	if !isPoorStatementOp(v.Op) {
    47  		return i
    48  	}
    49  	// Look ahead to see what the line number is on the next thing that could be a boundary.
    50  	for j := i + 1; j < len(b.Values); j++ {
    51  		if b.Values[j].Pos.IsStmt() == src.PosNotStmt { // ignore non-statements
    52  			continue
    53  		}
    54  		if b.Values[j].Pos.Line() == v.Pos.Line() {
    55  			return j
    56  		}
    57  		return i
    58  	}
    59  	return i
    60  }
    61  
    62  // notStmtBoundary indicates which value opcodes can never be a statement
    63  // boundary because they don't correspond to a user's understanding of a
    64  // statement boundary.  Called from *Value.reset(), and *Func.newValue(),
    65  // located here to keep all the statement boundary heuristics in one place.
    66  // Note: *Value.reset() filters out OpCopy because of how that is used in
    67  // rewrite.
    68  func notStmtBoundary(op Op) bool {
    69  	switch op {
    70  	case OpCopy, OpPhi, OpVarKill, OpVarDef, OpUnknown, OpFwdRef, OpArg:
    71  		return true
    72  	}
    73  	return false
    74  }
    75  
    76  func numberLines(f *Func) {
    77  	po := f.Postorder()
    78  	endlines := make(map[ID]src.XPos)
    79  	last := uint(0)              // uint follows type of XPos.Line()
    80  	first := uint(math.MaxInt32) // unsigned, but large valid int when cast
    81  	note := func(line uint) {
    82  		if line < first {
    83  			first = line
    84  		}
    85  		if line > last {
    86  			last = line
    87  		}
    88  	}
    89  
    90  	// Visit in reverse post order so that all non-loop predecessors come first.
    91  	for j := len(po) - 1; j >= 0; j-- {
    92  		b := po[j]
    93  		// Find the first interesting position and check to see if it differs from any predecessor
    94  		firstPos := src.NoXPos
    95  		firstPosIndex := -1
    96  		if b.Pos.IsStmt() != src.PosNotStmt {
    97  			note(b.Pos.Line())
    98  		}
    99  		for i := 0; i < len(b.Values); i++ {
   100  			v := b.Values[i]
   101  			if v.Pos.IsStmt() != src.PosNotStmt {
   102  				note(v.Pos.Line())
   103  				// skip ahead to better instruction for this line if possible
   104  				i = nextGoodStatementIndex(v, i, b)
   105  				v = b.Values[i]
   106  				firstPosIndex = i
   107  				firstPos = v.Pos
   108  				v.Pos = firstPos.WithDefaultStmt() // default to default
   109  				break
   110  			}
   111  		}
   112  
   113  		if firstPosIndex == -1 { // Effectively empty block, check block's own Pos, consider preds.
   114  			if b.Pos.IsStmt() != src.PosNotStmt {
   115  				b.Pos = b.Pos.WithIsStmt()
   116  				endlines[b.ID] = b.Pos
   117  				continue
   118  			}
   119  			line := src.NoXPos
   120  			for _, p := range b.Preds {
   121  				pbi := p.Block().ID
   122  				if endlines[pbi] != line {
   123  					if line == src.NoXPos {
   124  						line = endlines[pbi]
   125  						continue
   126  					} else {
   127  						line = src.NoXPos
   128  						break
   129  					}
   130  
   131  				}
   132  			}
   133  			endlines[b.ID] = line
   134  			continue
   135  		}
   136  		// check predecessors for any difference; if firstPos differs, then it is a boundary.
   137  		if len(b.Preds) == 0 { // Don't forget the entry block
   138  			b.Values[firstPosIndex].Pos = firstPos.WithIsStmt()
   139  		} else {
   140  			for _, p := range b.Preds {
   141  				pbi := p.Block().ID
   142  				if endlines[pbi] != firstPos {
   143  					b.Values[firstPosIndex].Pos = firstPos.WithIsStmt()
   144  					break
   145  				}
   146  			}
   147  		}
   148  		// iterate forward setting each new (interesting) position as a statement boundary.
   149  		for i := firstPosIndex + 1; i < len(b.Values); i++ {
   150  			v := b.Values[i]
   151  			if v.Pos.IsStmt() == src.PosNotStmt {
   152  				continue
   153  			}
   154  			note(v.Pos.Line())
   155  			// skip ahead if possible
   156  			i = nextGoodStatementIndex(v, i, b)
   157  			v = b.Values[i]
   158  			if v.Pos.Line() != firstPos.Line() || !v.Pos.SameFile(firstPos) {
   159  				firstPos = v.Pos
   160  				v.Pos = v.Pos.WithIsStmt()
   161  			} else {
   162  				v.Pos = v.Pos.WithDefaultStmt()
   163  			}
   164  		}
   165  		if b.Pos.IsStmt() != src.PosNotStmt && (b.Pos.Line() != firstPos.Line() || !b.Pos.SameFile(firstPos)) {
   166  			b.Pos = b.Pos.WithIsStmt()
   167  			firstPos = b.Pos
   168  		}
   169  		endlines[b.ID] = firstPos
   170  	}
   171  	f.cachedLineStarts = newBiasedSparseMap(int(first), int(last))
   172  }
   173  

View as plain text