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 := getFileIndexAndLine(ctxt, 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 if _, ok := inlMarkProgs[p]; ok {
298 delete(inlMarkProgs, p)
299 }
300 }
301 if len(inlMarkProgs) > 0 {
302 ctxt.Diag("one or more instructions used as inline markers are no longer reachable")
303 }
304
305 pcinlineState := new(pcinlineState)
306 pcln.Pcinline = funcpctab(ctxt, cursym, "pctoinline", pcinlineState.pctoinline, nil)
307 for _, inlMark := range fn.InlMarks {
308 pcinlineState.setParentPC(ctxt, int(inlMark.id), int32(inlMark.p.Pc))
309 }
310 pcln.InlTree = pcinlineState.localTree
311 if ctxt.Debugpcln == "pctoinline" && len(pcln.InlTree.nodes) > 0 {
312 ctxt.Logf("-- inlining tree for %s:\n", cursym)
313 dumpInlTree(ctxt, pcln.InlTree)
314 ctxt.Logf("--\n")
315 }
316
317
318 havepc := make([]uint32, (npcdata+31)/32)
319 havefunc := make([]uint32, (nfuncdata+31)/32)
320 for p := fn.Text; p != nil; p = p.Link {
321 if p.As == AFUNCDATA {
322 if (havefunc[p.From.Offset/32]>>uint64(p.From.Offset%32))&1 != 0 {
323 ctxt.Diag("multiple definitions for FUNCDATA $%d", p.From.Offset)
324 }
325 havefunc[p.From.Offset/32] |= 1 << uint64(p.From.Offset%32)
326 }
327
328 if p.As == APCDATA && p.To.Offset != -1 {
329 havepc[p.From.Offset/32] |= 1 << uint64(p.From.Offset%32)
330 }
331 }
332
333
334 for i := 0; i < npcdata; i++ {
335 if (havepc[i/32]>>uint(i%32))&1 == 0 {
336
337 pcln.Pcdata[i] = &LSym{
338 Type: objabi.SRODATA,
339 Attribute: AttrContentAddressable | AttrPcdata,
340 }
341 } else {
342 pcln.Pcdata[i] = funcpctab(ctxt, cursym, "pctopcdata", pctopcdata, interface{}(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