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 }