1
2
3
4
5 package obj
6
7 import (
8 "cmd/internal/goobj"
9 "cmd/internal/objabi"
10 "encoding/binary"
11 "fmt"
12 "log"
13 )
14
15
16
17
18
19
20
21
22
23
24
25 func funcpctab(ctxt *Link, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) *LSym {
26 dbg := desc == ctxt.Debugpcln
27 dst := []byte{}
28 sym := &LSym{
29 Type: objabi.SRODATA,
30 Attribute: AttrContentAddressable | AttrPcdata,
31 }
32
33 if dbg {
34 ctxt.Logf("funcpctab %s [valfunc=%s]\n", func_.Name, desc)
35 }
36
37 val := int32(-1)
38 oldval := val
39 fn := func_.Func()
40 if fn.Text == nil {
41
42 return sym
43 }
44
45 pc := fn.Text.Pc
46
47 if dbg {
48 ctxt.Logf("%6x %6d %v\n", uint64(pc), val, fn.Text)
49 }
50
51 buf := make([]byte, binary.MaxVarintLen32)
52 started := false
53 for p := fn.Text; p != nil; p = p.Link {
54
55 val = valfunc(ctxt, func_, val, p, 0, arg)
56
57 if val == oldval && started {
58 val = valfunc(ctxt, func_, val, p, 1, arg)
59 if dbg {
60 ctxt.Logf("%6x %6s %v\n", uint64(p.Pc), "", p)
61 }
62 continue
63 }
64
65
66
67
68
69 if p.Link != nil && p.Link.Pc == p.Pc {
70 val = valfunc(ctxt, func_, val, p, 1, arg)
71 if dbg {
72 ctxt.Logf("%6x %6s %v\n", uint64(p.Pc), "", p)
73 }
74 continue
75 }
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 if dbg {
92 ctxt.Logf("%6x %6d %v\n", uint64(p.Pc), val, p)
93 }
94
95 if started {
96 pcdelta := (p.Pc - pc) / int64(ctxt.Arch.MinLC)
97 n := binary.PutUvarint(buf, uint64(pcdelta))
98 dst = append(dst, buf[:n]...)
99 pc = p.Pc
100 }
101
102 delta := val - oldval
103 n := binary.PutVarint(buf, int64(delta))
104 dst = append(dst, buf[:n]...)
105 oldval = val
106 started = true
107 val = valfunc(ctxt, func_, val, p, 1, arg)
108 }
109
110 if started {
111 if dbg {
112 ctxt.Logf("%6x done\n", uint64(fn.Text.Pc+func_.Size))
113 }
114 v := (func_.Size - pc) / int64(ctxt.Arch.MinLC)
115 if v < 0 {
116 ctxt.Diag("negative pc offset: %v", v)
117 }
118 n := binary.PutUvarint(buf, uint64(v))
119 dst = append(dst, buf[:n]...)
120
121 dst = append(dst, 0)
122 }
123
124 if dbg {
125 ctxt.Logf("wrote %d bytes to %p\n", len(dst), dst)
126 for _, p := range dst {
127 ctxt.Logf(" %02x", p)
128 }
129 ctxt.Logf("\n")
130 }
131
132 sym.Size = int64(len(dst))
133 sym.P = dst
134 return sym
135 }
136
137
138
139
140
141 func pctofileline(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 {
142 if p.As == ATEXT || p.As == ANOP || p.Pos.Line() == 0 || phase == 1 {
143 return oldval
144 }
145 f, l := ctxt.getFileIndexAndLine(p.Pos)
146 if arg == nil {
147 return l
148 }
149 pcln := arg.(*Pcln)
150 pcln.UsedFiles[goobj.CUFileIndex(f)] = struct{}{}
151 return int32(f)
152 }
153
154
155
156 type pcinlineState struct {
157 globalToLocal map[int]int
158 localTree InlTree
159 }
160
161
162
163 func (s *pcinlineState) addBranch(ctxt *Link, globalIndex int) int {
164 if globalIndex < 0 {
165 return -1
166 }
167
168 localIndex, ok := s.globalToLocal[globalIndex]
169 if ok {
170 return localIndex
171 }
172
173
174
175
176
177 call := ctxt.InlTree.nodes[globalIndex]
178 call.Parent = s.addBranch(ctxt, call.Parent)
179 localIndex = len(s.localTree.nodes)
180 s.localTree.nodes = append(s.localTree.nodes, call)
181 s.globalToLocal[globalIndex] = localIndex
182 return localIndex
183 }
184
185 func (s *pcinlineState) setParentPC(ctxt *Link, globalIndex int, pc int32) {
186 localIndex, ok := s.globalToLocal[globalIndex]
187 if !ok {
188
189
190
191
192
193 return
194 }
195 s.localTree.setParentPC(localIndex, pc)
196 }
197
198
199
200
201 func (s *pcinlineState) pctoinline(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 {
202 if phase == 1 {
203 return oldval
204 }
205
206 posBase := ctxt.PosTable.Pos(p.Pos).Base()
207 if posBase == nil {
208 return -1
209 }
210
211 globalIndex := posBase.InliningIndex()
212 if globalIndex < 0 {
213 return -1
214 }
215
216 if s.globalToLocal == nil {
217 s.globalToLocal = make(map[int]int)
218 }
219
220 return int32(s.addBranch(ctxt, globalIndex))
221 }
222
223
224
225
226
227 func pctospadj(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 {
228 if oldval == -1 {
229 oldval = 0
230 }
231 if phase == 0 {
232 return oldval
233 }
234 if oldval+p.Spadj < -10000 || oldval+p.Spadj > 1100000000 {
235 ctxt.Diag("overflow in spadj: %d + %d = %d", oldval, p.Spadj, oldval+p.Spadj)
236 ctxt.DiagFlush()
237 log.Fatalf("bad code")
238 }
239
240 return oldval + p.Spadj
241 }
242
243
244
245
246
247
248 func pctopcdata(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 {
249 if phase == 0 || p.As != APCDATA || p.From.Offset != int64(arg.(uint32)) {
250 return oldval
251 }
252 if int64(int32(p.To.Offset)) != p.To.Offset {
253 ctxt.Diag("overflow in PCDATA instruction: %v", p)
254 ctxt.DiagFlush()
255 log.Fatalf("bad code")
256 }
257
258 return int32(p.To.Offset)
259 }
260
261 func linkpcln(ctxt *Link, cursym *LSym) {
262 pcln := &cursym.Func().Pcln
263 pcln.UsedFiles = make(map[goobj.CUFileIndex]struct{})
264
265 npcdata := 0
266 nfuncdata := 0
267 for p := cursym.Func().Text; p != nil; p = p.Link {
268
269
270
271
272 if p.As == APCDATA && p.From.Offset >= int64(npcdata) && p.To.Offset != -1 {
273 npcdata = int(p.From.Offset + 1)
274 }
275
276
277 if p.As == AFUNCDATA && p.From.Offset >= int64(nfuncdata) {
278 nfuncdata = int(p.From.Offset + 1)
279 }
280 }
281
282 pcln.Pcdata = make([]*LSym, npcdata)
283 pcln.Funcdata = make([]*LSym, nfuncdata)
284
285 pcln.Pcsp = funcpctab(ctxt, cursym, "pctospadj", pctospadj, nil)
286 pcln.Pcfile = funcpctab(ctxt, cursym, "pctofile", pctofileline, pcln)
287 pcln.Pcline = funcpctab(ctxt, cursym, "pctoline", pctofileline, nil)
288
289
290
291 fn := cursym.Func()
292 inlMarkProgs := make(map[*Prog]struct{}, len(fn.InlMarks))
293 for _, inlMark := range fn.InlMarks {
294 inlMarkProgs[inlMark.p] = struct{}{}
295 }
296 for p := fn.Text; p != nil; p = p.Link {
297 delete(inlMarkProgs, p)
298 }
299 if len(inlMarkProgs) > 0 {
300 ctxt.Diag("one or more instructions used as inline markers are no longer reachable")
301 }
302
303 pcinlineState := new(pcinlineState)
304 pcln.Pcinline = funcpctab(ctxt, cursym, "pctoinline", pcinlineState.pctoinline, nil)
305 for _, inlMark := range fn.InlMarks {
306 pcinlineState.setParentPC(ctxt, int(inlMark.id), int32(inlMark.p.Pc))
307 }
308 pcln.InlTree = pcinlineState.localTree
309 if ctxt.Debugpcln == "pctoinline" && len(pcln.InlTree.nodes) > 0 {
310 ctxt.Logf("-- inlining tree for %s:\n", cursym)
311 dumpInlTree(ctxt, pcln.InlTree)
312 ctxt.Logf("--\n")
313 }
314
315
316 havepc := make([]uint32, (npcdata+31)/32)
317 havefunc := make([]uint32, (nfuncdata+31)/32)
318 for p := fn.Text; p != nil; p = p.Link {
319 if p.As == AFUNCDATA {
320 if (havefunc[p.From.Offset/32]>>uint64(p.From.Offset%32))&1 != 0 {
321 ctxt.Diag("multiple definitions for FUNCDATA $%d", p.From.Offset)
322 }
323 havefunc[p.From.Offset/32] |= 1 << uint64(p.From.Offset%32)
324 }
325
326 if p.As == APCDATA && p.To.Offset != -1 {
327 havepc[p.From.Offset/32] |= 1 << uint64(p.From.Offset%32)
328 }
329 }
330
331
332 for i := 0; i < npcdata; i++ {
333 if (havepc[i/32]>>uint(i%32))&1 == 0 {
334
335 pcln.Pcdata[i] = &LSym{
336 Type: objabi.SRODATA,
337 Attribute: AttrContentAddressable | AttrPcdata,
338 }
339 } else {
340 pcln.Pcdata[i] = funcpctab(ctxt, cursym, "pctopcdata", pctopcdata, interface{}(uint32(i)))
341 }
342 }
343
344
345 if nfuncdata > 0 {
346 for p := fn.Text; p != nil; p = p.Link {
347 if p.As != AFUNCDATA {
348 continue
349 }
350 i := int(p.From.Offset)
351 if p.To.Type != TYPE_MEM || p.To.Offset != 0 {
352 panic(fmt.Sprintf("bad funcdata: %v", p))
353 }
354 pcln.Funcdata[i] = p.To.Sym
355 }
356 }
357 }
358
359
360 type PCIter struct {
361 p []byte
362 PC uint32
363 NextPC uint32
364 PCScale uint32
365 Value int32
366 start bool
367 Done bool
368 }
369
370
371 func NewPCIter(pcScale uint32) *PCIter {
372 it := new(PCIter)
373 it.PCScale = pcScale
374 return it
375 }
376
377
378 func (it *PCIter) Next() {
379 it.PC = it.NextPC
380 if it.Done {
381 return
382 }
383 if len(it.p) == 0 {
384 it.Done = true
385 return
386 }
387
388
389 val, n := binary.Varint(it.p)
390 if n <= 0 {
391 log.Fatalf("bad Value varint in pciterNext: read %v", n)
392 }
393 it.p = it.p[n:]
394
395 if val == 0 && !it.start {
396 it.Done = true
397 return
398 }
399
400 it.start = false
401 it.Value += int32(val)
402
403
404 pc, n := binary.Uvarint(it.p)
405 if n <= 0 {
406 log.Fatalf("bad pc varint in pciterNext: read %v", n)
407 }
408 it.p = it.p[n:]
409
410 it.NextPC = it.PC + uint32(pc)*it.PCScale
411 }
412
413
414
415 func (it *PCIter) Init(p []byte) {
416 it.p = p
417 it.PC = 0
418 it.NextPC = 0
419 it.Value = -1
420 it.start = true
421 it.Done = false
422 it.Next()
423 }
424
View as plain text