github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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 "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 }