pcln.go 9.55 KB
Newer Older
1
// Copyright 2013 The Go Authors. All rights reserved.
2 3 4 5 6
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package obj

7
import "log"
8

9 10
func addvarint(d *Pcdata, v uint32) {
	for ; v >= 0x80; v >>= 7 {
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
		d.P = append(d.P, uint8(v|0x80))
	}
	d.P = append(d.P, uint8(v))
}

// funcpctab writes to dst a pc-value table mapping the code in func to the values
// returned by valfunc parameterized by arg. The invocation of valfunc to update the
// current value is, for each p,
//
//	val = valfunc(func, val, p, 0, arg);
//	record val as value at p->pc;
//	val = valfunc(func, val, p, 1, arg);
//
// where func is the function, val is the current value, p is the instruction being
// considered, and arg can be used to further parameterize valfunc.
func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) {
27
	dbg := desc == ctxt.Debugpcln
28 29 30

	dst.P = dst.P[:0]

31
	if dbg {
32
		ctxt.Logf("funcpctab %s [valfunc=%s]\n", func_.Name, desc)
33 34
	}

35 36
	val := int32(-1)
	oldval := val
37 38 39 40
	if func_.Text == nil {
		return
	}

41
	pc := func_.Text.Pc
42

43
	if dbg {
44
		ctxt.Logf("%6x %6d %v\n", uint64(pc), val, func_.Text)
45 46
	}

47
	started := false
48 49
	var delta uint32
	for p := func_.Text; p != nil; p = p.Link {
50 51 52
		// Update val. If it's not changing, keep going.
		val = valfunc(ctxt, func_, val, p, 0, arg)

53
		if val == oldval && started {
54
			val = valfunc(ctxt, func_, val, p, 1, arg)
55
			if dbg {
56
				ctxt.Logf("%6x %6s %v\n", uint64(p.Pc), "", p)
57 58 59 60 61 62 63 64 65 66
			}
			continue
		}

		// If the pc of the next instruction is the same as the
		// pc of this instruction, this instruction is not a real
		// instruction. Keep going, so that we only emit a delta
		// for a true instruction boundary in the program.
		if p.Link != nil && p.Link.Pc == p.Pc {
			val = valfunc(ctxt, func_, val, p, 1, arg)
67
			if dbg {
68
				ctxt.Logf("%6x %6s %v\n", uint64(p.Pc), "", p)
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
			}
			continue
		}

		// The table is a sequence of (value, pc) pairs, where each
		// pair states that the given value is in effect from the current position
		// up to the given pc, which becomes the new current position.
		// To generate the table as we scan over the program instructions,
		// we emit a "(value" when pc == func->value, and then
		// each time we observe a change in value we emit ", pc) (value".
		// When the scan is over, we emit the closing ", pc)".
		//
		// The table is delta-encoded. The value deltas are signed and
		// transmitted in zig-zag form, where a complement bit is placed in bit 0,
		// and the pc deltas are unsigned. Both kinds of deltas are sent
		// as variable-length little-endian base-128 integers,
		// where the 0x80 bit indicates that the integer continues.

87
		if dbg {
88
			ctxt.Logf("%6x %6d %v\n", uint64(p.Pc), val, p)
89 90
		}

91
		if started {
92
			addvarint(dst, uint32((p.Pc-pc)/int64(ctxt.Arch.MinLC)))
93 94 95 96 97 98 99 100 101
			pc = p.Pc
		}

		delta = uint32(val) - uint32(oldval)
		if delta>>31 != 0 {
			delta = 1 | ^(delta << 1)
		} else {
			delta <<= 1
		}
102
		addvarint(dst, delta)
103
		oldval = val
104
		started = true
105 106 107
		val = valfunc(ctxt, func_, val, p, 1, arg)
	}

108
	if started {
109
		if dbg {
110
			ctxt.Logf("%6x done\n", uint64(func_.Text.Pc+func_.Size))
111
		}
112 113
		addvarint(dst, uint32((func_.Size-pc)/int64(ctxt.Arch.MinLC)))
		addvarint(dst, 0) // terminator
114 115
	}

116
	if dbg {
117
		ctxt.Logf("wrote %d bytes to %p\n", len(dst.P), dst)
118
		for i := 0; i < len(dst.P); i++ {
119
			ctxt.Logf(" %02x", dst.P[i])
120
		}
121
		ctxt.Logf("\n")
122 123 124 125 126
	}
}

// pctofileline computes either the file number (arg == 0)
// or the line number (arg == 1) to use at p.
127
// Because p.Pos applies to p, phase == 0 (before p)
128 129
// takes care of the update.
func pctofileline(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 {
130
	if p.As == ATEXT || p.As == ANOP || p.Pos.Line() == 0 || phase == 1 {
131 132
		return oldval
	}
133
	f, l := linkgetlineFromPos(ctxt, p.Pos)
134
	if f == nil {
135
		//	print("getline failed for %s %v\n", ctxt->cursym->name, p);
136 137 138 139 140 141
		return oldval
	}

	if arg == nil {
		return l
	}
142
	pcln := arg.(*Pcln)
143 144 145 146 147

	if f == pcln.Lastfile {
		return int32(pcln.Lastindex)
	}

148
	for i, file := range pcln.File {
149 150
		if file == f {
			pcln.Lastfile = f
151 152
			pcln.Lastindex = i
			return int32(i)
153 154
		}
	}
155
	i := len(pcln.File)
156 157
	pcln.File = append(pcln.File, f)
	pcln.Lastfile = f
158 159
	pcln.Lastindex = i
	return int32(i)
160 161
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
// pcinlineState holds the state used to create a function's inlining
// tree and the PC-value table that maps PCs to nodes in that tree.
type pcinlineState struct {
	globalToLocal map[int]int
	localTree     InlTree
}

// addBranch adds a branch from the global inlining tree in ctxt to
// the function's local inlining tree, returning the index in the local tree.
func (s *pcinlineState) addBranch(ctxt *Link, globalIndex int) int {
	if globalIndex < 0 {
		return -1
	}

	localIndex, ok := s.globalToLocal[globalIndex]
	if ok {
		return localIndex
	}

	// Since tracebacks don't include column information, we could
	// use one node for multiple calls of the same function on the
	// same line (e.g., f(x) + f(y)). For now, we use one node for
	// each inlined call.
	call := ctxt.InlTree.nodes[globalIndex]
	call.Parent = s.addBranch(ctxt, call.Parent)
	localIndex = len(s.localTree.nodes)
	s.localTree.nodes = append(s.localTree.nodes, call)
	s.globalToLocal[globalIndex] = localIndex
	return localIndex
}

// pctoinline computes the index into the local inlining tree to use at p.
// If p is not the result of inlining, pctoinline returns -1. Because p.Pos
// applies to p, phase == 0 (before p) takes care of the update.
func (s *pcinlineState) pctoinline(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 {
	if phase == 1 {
		return oldval
	}

	posBase := ctxt.PosTable.Pos(p.Pos).Base()
	if posBase == nil {
		return -1
	}

	globalIndex := posBase.InliningIndex()
	if globalIndex < 0 {
		return -1
	}

	if s.globalToLocal == nil {
		s.globalToLocal = make(map[int]int)
	}

	return int32(s.addBranch(ctxt, globalIndex))
}

218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
// pctospadj computes the sp adjustment in effect.
// It is oldval plus any adjustment made by p itself.
// The adjustment by p takes effect only after p, so we
// apply the change during phase == 1.
func pctospadj(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 {
	if oldval == -1 { // starting
		oldval = 0
	}
	if phase == 0 {
		return oldval
	}
	if oldval+p.Spadj < -10000 || oldval+p.Spadj > 1100000000 {
		ctxt.Diag("overflow in spadj: %d + %d = %d", oldval, p.Spadj, oldval+p.Spadj)
		log.Fatalf("bad code")
	}

	return oldval + p.Spadj
}

// pctopcdata computes the pcdata value in effect at p.
// A PCDATA instruction sets the value in effect at future
// non-PCDATA instructions.
// Since PCDATA instructions have no width in the final code,
// it does not matter which phase we use for the update.
func pctopcdata(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 {
243
	if phase == 0 || p.As != APCDATA || p.From.Offset != int64(arg.(uint32)) {
244 245 246 247 248 249 250 251 252 253 254
		return oldval
	}
	if int64(int32(p.To.Offset)) != p.To.Offset {
		ctxt.Diag("overflow in PCDATA instruction: %v", p)
		log.Fatalf("bad code")
	}

	return int32(p.To.Offset)
}

func linkpcln(ctxt *Link, cursym *LSym) {
255
	pcln := &cursym.Pcln
256

257 258 259
	npcdata := 0
	nfuncdata := 0
	for p := cursym.Text; p != nil; p = p.Link {
260 261 262 263 264
		// Find the highest ID of any used PCDATA table. This ignores PCDATA table
		// that consist entirely of "-1", since that's the assumed default value.
		//   From.Offset is table ID
		//   To.Offset is data
		if p.As == APCDATA && p.From.Offset >= int64(npcdata) && p.To.Offset != -1 { // ignore -1 as we start at -1, if we only see -1, nothing changed
265 266
			npcdata = int(p.From.Offset + 1)
		}
267 268
		// Find the highest ID of any FUNCDATA table.
		//   From.Offset is table ID
269
		if p.As == AFUNCDATA && p.From.Offset >= int64(nfuncdata) {
270 271 272 273 274 275 276 277 278 279 280 281 282 283
			nfuncdata = int(p.From.Offset + 1)
		}
	}

	pcln.Pcdata = make([]Pcdata, npcdata)
	pcln.Pcdata = pcln.Pcdata[:npcdata]
	pcln.Funcdata = make([]*LSym, nfuncdata)
	pcln.Funcdataoff = make([]int64, nfuncdata)
	pcln.Funcdataoff = pcln.Funcdataoff[:nfuncdata]

	funcpctab(ctxt, &pcln.Pcsp, cursym, "pctospadj", pctospadj, nil)
	funcpctab(ctxt, &pcln.Pcfile, cursym, "pctofile", pctofileline, pcln)
	funcpctab(ctxt, &pcln.Pcline, cursym, "pctoline", pctofileline, nil)

284 285 286
	pcinlineState := new(pcinlineState)
	funcpctab(ctxt, &pcln.Pcinline, cursym, "pctoinline", pcinlineState.pctoinline, nil)
	pcln.InlTree = pcinlineState.localTree
287 288 289 290 291
	if ctxt.Debugpcln == "pctoinline" && len(pcln.InlTree.nodes) > 0 {
		ctxt.Logf("-- inlining tree for %s:\n", cursym)
		dumpInlTree(ctxt, pcln.InlTree)
		ctxt.Logf("--\n")
	}
292

293 294 295
	// tabulate which pc and func data we have.
	havepc := make([]uint32, (npcdata+31)/32)
	havefunc := make([]uint32, (nfuncdata+31)/32)
296
	for p := cursym.Text; p != nil; p = p.Link {
297
		if p.As == AFUNCDATA {
298 299 300 301 302 303
			if (havefunc[p.From.Offset/32]>>uint64(p.From.Offset%32))&1 != 0 {
				ctxt.Diag("multiple definitions for FUNCDATA $%d", p.From.Offset)
			}
			havefunc[p.From.Offset/32] |= 1 << uint64(p.From.Offset%32)
		}

304
		if p.As == APCDATA && p.To.Offset != -1 {
305 306 307 308 309
			havepc[p.From.Offset/32] |= 1 << uint64(p.From.Offset%32)
		}
	}

	// pcdata.
310
	for i := 0; i < npcdata; i++ {
311 312 313 314 315 316 317 318
		if (havepc[i/32]>>uint(i%32))&1 == 0 {
			continue
		}
		funcpctab(ctxt, &pcln.Pcdata[i], cursym, "pctopcdata", pctopcdata, interface{}(uint32(i)))
	}

	// funcdata
	if nfuncdata > 0 {
319 320
		var i int
		for p := cursym.Text; p != nil; p = p.Link {
321
			if p.As == AFUNCDATA {
322 323
				i = int(p.From.Offset)
				pcln.Funcdataoff[i] = p.To.Offset
324
				if p.To.Type != TYPE_CONST {
325 326 327 328 329 330 331 332
					// TODO: Dedup.
					//funcdata_bytes += p->to.sym->size;
					pcln.Funcdata[i] = p.To.Sym
				}
			}
		}
	}
}