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