Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/MC/MCCodeView.cpp
35232 views
1
//===- MCCodeView.h - Machine Code CodeView support -------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// Holds state from .cv_file and .cv_loc directives for later emission.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "llvm/MC/MCCodeView.h"
14
#include "llvm/ADT/STLExtras.h"
15
#include "llvm/ADT/StringExtras.h"
16
#include "llvm/DebugInfo/CodeView/CodeView.h"
17
#include "llvm/DebugInfo/CodeView/Line.h"
18
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
19
#include "llvm/MC/MCAssembler.h"
20
#include "llvm/MC/MCContext.h"
21
#include "llvm/MC/MCObjectStreamer.h"
22
#include "llvm/MC/MCValue.h"
23
#include "llvm/Support/EndianStream.h"
24
25
using namespace llvm;
26
using namespace llvm::codeview;
27
28
CodeViewContext::~CodeViewContext() {
29
// If someone inserted strings into the string table but never actually
30
// emitted them somewhere, clean up the fragment.
31
if (!InsertedStrTabFragment && StrTabFragment)
32
StrTabFragment->destroy();
33
}
34
35
/// This is a valid number for use with .cv_loc if we've already seen a .cv_file
36
/// for it.
37
bool CodeViewContext::isValidFileNumber(unsigned FileNumber) const {
38
unsigned Idx = FileNumber - 1;
39
if (Idx < Files.size())
40
return Files[Idx].Assigned;
41
return false;
42
}
43
44
bool CodeViewContext::addFile(MCStreamer &OS, unsigned FileNumber,
45
StringRef Filename,
46
ArrayRef<uint8_t> ChecksumBytes,
47
uint8_t ChecksumKind) {
48
assert(FileNumber > 0);
49
auto FilenameOffset = addToStringTable(Filename);
50
Filename = FilenameOffset.first;
51
unsigned Idx = FileNumber - 1;
52
if (Idx >= Files.size())
53
Files.resize(Idx + 1);
54
55
if (Filename.empty())
56
Filename = "<stdin>";
57
58
if (Files[Idx].Assigned)
59
return false;
60
61
FilenameOffset = addToStringTable(Filename);
62
Filename = FilenameOffset.first;
63
unsigned Offset = FilenameOffset.second;
64
65
auto ChecksumOffsetSymbol =
66
OS.getContext().createTempSymbol("checksum_offset", false);
67
Files[Idx].StringTableOffset = Offset;
68
Files[Idx].ChecksumTableOffset = ChecksumOffsetSymbol;
69
Files[Idx].Assigned = true;
70
Files[Idx].Checksum = ChecksumBytes;
71
Files[Idx].ChecksumKind = ChecksumKind;
72
73
return true;
74
}
75
76
MCCVFunctionInfo *CodeViewContext::getCVFunctionInfo(unsigned FuncId) {
77
if (FuncId >= Functions.size())
78
return nullptr;
79
if (Functions[FuncId].isUnallocatedFunctionInfo())
80
return nullptr;
81
return &Functions[FuncId];
82
}
83
84
bool CodeViewContext::recordFunctionId(unsigned FuncId) {
85
if (FuncId >= Functions.size())
86
Functions.resize(FuncId + 1);
87
88
// Return false if this function info was already allocated.
89
if (!Functions[FuncId].isUnallocatedFunctionInfo())
90
return false;
91
92
// Mark this as an allocated normal function, and leave the rest alone.
93
Functions[FuncId].ParentFuncIdPlusOne = MCCVFunctionInfo::FunctionSentinel;
94
return true;
95
}
96
97
bool CodeViewContext::recordInlinedCallSiteId(unsigned FuncId, unsigned IAFunc,
98
unsigned IAFile, unsigned IALine,
99
unsigned IACol) {
100
if (FuncId >= Functions.size())
101
Functions.resize(FuncId + 1);
102
103
// Return false if this function info was already allocated.
104
if (!Functions[FuncId].isUnallocatedFunctionInfo())
105
return false;
106
107
MCCVFunctionInfo::LineInfo InlinedAt;
108
InlinedAt.File = IAFile;
109
InlinedAt.Line = IALine;
110
InlinedAt.Col = IACol;
111
112
// Mark this as an inlined call site and record call site line info.
113
MCCVFunctionInfo *Info = &Functions[FuncId];
114
Info->ParentFuncIdPlusOne = IAFunc + 1;
115
Info->InlinedAt = InlinedAt;
116
117
// Walk up the call chain adding this function id to the InlinedAtMap of all
118
// transitive callers until we hit a real function.
119
while (Info->isInlinedCallSite()) {
120
InlinedAt = Info->InlinedAt;
121
Info = getCVFunctionInfo(Info->getParentFuncId());
122
Info->InlinedAtMap[FuncId] = InlinedAt;
123
}
124
125
return true;
126
}
127
128
void CodeViewContext::recordCVLoc(MCContext &Ctx, const MCSymbol *Label,
129
unsigned FunctionId, unsigned FileNo,
130
unsigned Line, unsigned Column,
131
bool PrologueEnd, bool IsStmt) {
132
addLineEntry(MCCVLoc{
133
Label, FunctionId, FileNo, Line, Column, PrologueEnd, IsStmt});
134
}
135
136
MCDataFragment *CodeViewContext::getStringTableFragment() {
137
if (!StrTabFragment) {
138
StrTabFragment = MCCtx->allocFragment<MCDataFragment>();
139
// Start a new string table out with a null byte.
140
StrTabFragment->getContents().push_back('\0');
141
}
142
return StrTabFragment;
143
}
144
145
std::pair<StringRef, unsigned> CodeViewContext::addToStringTable(StringRef S) {
146
SmallVectorImpl<char> &Contents = getStringTableFragment()->getContents();
147
auto Insertion =
148
StringTable.insert(std::make_pair(S, unsigned(Contents.size())));
149
// Return the string from the table, since it is stable.
150
std::pair<StringRef, unsigned> Ret =
151
std::make_pair(Insertion.first->first(), Insertion.first->second);
152
if (Insertion.second) {
153
// The string map key is always null terminated.
154
Contents.append(Ret.first.begin(), Ret.first.end() + 1);
155
}
156
return Ret;
157
}
158
159
unsigned CodeViewContext::getStringTableOffset(StringRef S) {
160
// A string table offset of zero is always the empty string.
161
if (S.empty())
162
return 0;
163
auto I = StringTable.find(S);
164
assert(I != StringTable.end());
165
return I->second;
166
}
167
168
void CodeViewContext::emitStringTable(MCObjectStreamer &OS) {
169
MCContext &Ctx = OS.getContext();
170
MCSymbol *StringBegin = Ctx.createTempSymbol("strtab_begin", false),
171
*StringEnd = Ctx.createTempSymbol("strtab_end", false);
172
173
OS.emitInt32(uint32_t(DebugSubsectionKind::StringTable));
174
OS.emitAbsoluteSymbolDiff(StringEnd, StringBegin, 4);
175
OS.emitLabel(StringBegin);
176
177
// Put the string table data fragment here, if we haven't already put it
178
// somewhere else. If somebody wants two string tables in their .s file, one
179
// will just be empty.
180
if (!InsertedStrTabFragment) {
181
OS.insert(getStringTableFragment());
182
InsertedStrTabFragment = true;
183
}
184
185
OS.emitValueToAlignment(Align(4), 0);
186
187
OS.emitLabel(StringEnd);
188
}
189
190
void CodeViewContext::emitFileChecksums(MCObjectStreamer &OS) {
191
// Do nothing if there are no file checksums. Microsoft's linker rejects empty
192
// CodeView substreams.
193
if (Files.empty())
194
return;
195
196
MCContext &Ctx = OS.getContext();
197
MCSymbol *FileBegin = Ctx.createTempSymbol("filechecksums_begin", false),
198
*FileEnd = Ctx.createTempSymbol("filechecksums_end", false);
199
200
OS.emitInt32(uint32_t(DebugSubsectionKind::FileChecksums));
201
OS.emitAbsoluteSymbolDiff(FileEnd, FileBegin, 4);
202
OS.emitLabel(FileBegin);
203
204
unsigned CurrentOffset = 0;
205
206
// Emit an array of FileChecksum entries. We index into this table using the
207
// user-provided file number. Each entry may be a variable number of bytes
208
// determined by the checksum kind and size.
209
for (auto File : Files) {
210
OS.emitAssignment(File.ChecksumTableOffset,
211
MCConstantExpr::create(CurrentOffset, Ctx));
212
CurrentOffset += 4; // String table offset.
213
if (!File.ChecksumKind) {
214
CurrentOffset +=
215
4; // One byte each for checksum size and kind, then align to 4 bytes.
216
} else {
217
CurrentOffset += 2; // One byte each for checksum size and kind.
218
CurrentOffset += File.Checksum.size();
219
CurrentOffset = alignTo(CurrentOffset, 4);
220
}
221
222
OS.emitInt32(File.StringTableOffset);
223
224
if (!File.ChecksumKind) {
225
// There is no checksum. Therefore zero the next two fields and align
226
// back to 4 bytes.
227
OS.emitInt32(0);
228
continue;
229
}
230
OS.emitInt8(static_cast<uint8_t>(File.Checksum.size()));
231
OS.emitInt8(File.ChecksumKind);
232
OS.emitBytes(toStringRef(File.Checksum));
233
OS.emitValueToAlignment(Align(4));
234
}
235
236
OS.emitLabel(FileEnd);
237
238
ChecksumOffsetsAssigned = true;
239
}
240
241
// Output checksum table offset of the given file number. It is possible that
242
// not all files have been registered yet, and so the offset cannot be
243
// calculated. In this case a symbol representing the offset is emitted, and
244
// the value of this symbol will be fixed up at a later time.
245
void CodeViewContext::emitFileChecksumOffset(MCObjectStreamer &OS,
246
unsigned FileNo) {
247
unsigned Idx = FileNo - 1;
248
249
if (Idx >= Files.size())
250
Files.resize(Idx + 1);
251
252
if (ChecksumOffsetsAssigned) {
253
OS.emitSymbolValue(Files[Idx].ChecksumTableOffset, 4);
254
return;
255
}
256
257
const MCSymbolRefExpr *SRE =
258
MCSymbolRefExpr::create(Files[Idx].ChecksumTableOffset, OS.getContext());
259
260
OS.emitValueImpl(SRE, 4);
261
}
262
263
void CodeViewContext::addLineEntry(const MCCVLoc &LineEntry) {
264
size_t Offset = MCCVLines.size();
265
auto I = MCCVLineStartStop.insert(
266
{LineEntry.getFunctionId(), {Offset, Offset + 1}});
267
if (!I.second)
268
I.first->second.second = Offset + 1;
269
MCCVLines.push_back(LineEntry);
270
}
271
272
std::vector<MCCVLoc>
273
CodeViewContext::getFunctionLineEntries(unsigned FuncId) {
274
std::vector<MCCVLoc> FilteredLines;
275
size_t LocBegin;
276
size_t LocEnd;
277
std::tie(LocBegin, LocEnd) = getLineExtentIncludingInlinees(FuncId);
278
if (LocBegin >= LocEnd) {
279
return FilteredLines;
280
}
281
282
MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(FuncId);
283
for (size_t Idx = LocBegin; Idx != LocEnd; ++Idx) {
284
unsigned LocationFuncId = MCCVLines[Idx].getFunctionId();
285
if (LocationFuncId == FuncId) {
286
// This was a .cv_loc directly for FuncId, so record it.
287
FilteredLines.push_back(MCCVLines[Idx]);
288
} else {
289
// Check if the current location is inlined in this function. If it is,
290
// synthesize a statement .cv_loc at the original inlined call site.
291
auto I = SiteInfo->InlinedAtMap.find(LocationFuncId);
292
if (I != SiteInfo->InlinedAtMap.end()) {
293
MCCVFunctionInfo::LineInfo &IA = I->second;
294
// Only add the location if it differs from the previous location.
295
// Large inlined calls will have many .cv_loc entries and we only need
296
// one line table entry in the parent function.
297
if (FilteredLines.empty() ||
298
FilteredLines.back().getFileNum() != IA.File ||
299
FilteredLines.back().getLine() != IA.Line ||
300
FilteredLines.back().getColumn() != IA.Col) {
301
FilteredLines.push_back(MCCVLoc(MCCVLines[Idx].getLabel(), FuncId,
302
IA.File, IA.Line, IA.Col, false,
303
false));
304
}
305
}
306
}
307
}
308
return FilteredLines;
309
}
310
311
std::pair<size_t, size_t> CodeViewContext::getLineExtent(unsigned FuncId) {
312
auto I = MCCVLineStartStop.find(FuncId);
313
// Return an empty extent if there are no cv_locs for this function id.
314
if (I == MCCVLineStartStop.end())
315
return {~0ULL, 0};
316
return I->second;
317
}
318
319
std::pair<size_t, size_t>
320
CodeViewContext::getLineExtentIncludingInlinees(unsigned FuncId) {
321
size_t LocBegin;
322
size_t LocEnd;
323
std::tie(LocBegin, LocEnd) = getLineExtent(FuncId);
324
325
// Include all child inline call sites in our extent.
326
MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(FuncId);
327
if (SiteInfo) {
328
for (auto &KV : SiteInfo->InlinedAtMap) {
329
unsigned ChildId = KV.first;
330
auto Extent = getLineExtent(ChildId);
331
LocBegin = std::min(LocBegin, Extent.first);
332
LocEnd = std::max(LocEnd, Extent.second);
333
}
334
}
335
336
return {LocBegin, LocEnd};
337
}
338
339
ArrayRef<MCCVLoc> CodeViewContext::getLinesForExtent(size_t L, size_t R) {
340
if (R <= L)
341
return std::nullopt;
342
if (L >= MCCVLines.size())
343
return std::nullopt;
344
return ArrayRef(&MCCVLines[L], R - L);
345
}
346
347
void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS,
348
unsigned FuncId,
349
const MCSymbol *FuncBegin,
350
const MCSymbol *FuncEnd) {
351
MCContext &Ctx = OS.getContext();
352
MCSymbol *LineBegin = Ctx.createTempSymbol("linetable_begin", false),
353
*LineEnd = Ctx.createTempSymbol("linetable_end", false);
354
355
OS.emitInt32(uint32_t(DebugSubsectionKind::Lines));
356
OS.emitAbsoluteSymbolDiff(LineEnd, LineBegin, 4);
357
OS.emitLabel(LineBegin);
358
OS.emitCOFFSecRel32(FuncBegin, /*Offset=*/0);
359
OS.emitCOFFSectionIndex(FuncBegin);
360
361
// Actual line info.
362
std::vector<MCCVLoc> Locs = getFunctionLineEntries(FuncId);
363
bool HaveColumns = any_of(Locs, [](const MCCVLoc &LineEntry) {
364
return LineEntry.getColumn() != 0;
365
});
366
OS.emitInt16(HaveColumns ? int(LF_HaveColumns) : 0);
367
OS.emitAbsoluteSymbolDiff(FuncEnd, FuncBegin, 4);
368
369
for (auto I = Locs.begin(), E = Locs.end(); I != E;) {
370
// Emit a file segment for the run of locations that share a file id.
371
unsigned CurFileNum = I->getFileNum();
372
auto FileSegEnd =
373
std::find_if(I, E, [CurFileNum](const MCCVLoc &Loc) {
374
return Loc.getFileNum() != CurFileNum;
375
});
376
unsigned EntryCount = FileSegEnd - I;
377
OS.AddComment(
378
"Segment for file '" +
379
Twine(getStringTableFragment()
380
->getContents()[Files[CurFileNum - 1].StringTableOffset]) +
381
"' begins");
382
OS.emitCVFileChecksumOffsetDirective(CurFileNum);
383
OS.emitInt32(EntryCount);
384
uint32_t SegmentSize = 12;
385
SegmentSize += 8 * EntryCount;
386
if (HaveColumns)
387
SegmentSize += 4 * EntryCount;
388
OS.emitInt32(SegmentSize);
389
390
for (auto J = I; J != FileSegEnd; ++J) {
391
OS.emitAbsoluteSymbolDiff(J->getLabel(), FuncBegin, 4);
392
unsigned LineData = J->getLine();
393
if (J->isStmt())
394
LineData |= LineInfo::StatementFlag;
395
OS.emitInt32(LineData);
396
}
397
if (HaveColumns) {
398
for (auto J = I; J != FileSegEnd; ++J) {
399
OS.emitInt16(J->getColumn());
400
OS.emitInt16(0);
401
}
402
}
403
I = FileSegEnd;
404
}
405
OS.emitLabel(LineEnd);
406
}
407
408
static bool compressAnnotation(uint32_t Data, SmallVectorImpl<char> &Buffer) {
409
if (isUInt<7>(Data)) {
410
Buffer.push_back(Data);
411
return true;
412
}
413
414
if (isUInt<14>(Data)) {
415
Buffer.push_back((Data >> 8) | 0x80);
416
Buffer.push_back(Data & 0xff);
417
return true;
418
}
419
420
if (isUInt<29>(Data)) {
421
Buffer.push_back((Data >> 24) | 0xC0);
422
Buffer.push_back((Data >> 16) & 0xff);
423
Buffer.push_back((Data >> 8) & 0xff);
424
Buffer.push_back(Data & 0xff);
425
return true;
426
}
427
428
return false;
429
}
430
431
static bool compressAnnotation(BinaryAnnotationsOpCode Annotation,
432
SmallVectorImpl<char> &Buffer) {
433
return compressAnnotation(static_cast<uint32_t>(Annotation), Buffer);
434
}
435
436
static uint32_t encodeSignedNumber(uint32_t Data) {
437
if (Data >> 31)
438
return ((-Data) << 1) | 1;
439
return Data << 1;
440
}
441
442
void CodeViewContext::emitInlineLineTableForFunction(MCObjectStreamer &OS,
443
unsigned PrimaryFunctionId,
444
unsigned SourceFileId,
445
unsigned SourceLineNum,
446
const MCSymbol *FnStartSym,
447
const MCSymbol *FnEndSym) {
448
// Create and insert a fragment into the current section that will be encoded
449
// later.
450
auto *F = MCCtx->allocFragment<MCCVInlineLineTableFragment>(
451
PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym);
452
OS.insert(F);
453
}
454
455
MCFragment *CodeViewContext::emitDefRange(
456
MCObjectStreamer &OS,
457
ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges,
458
StringRef FixedSizePortion) {
459
// Create and insert a fragment into the current section that will be encoded
460
// later.
461
auto *F =
462
MCCtx->allocFragment<MCCVDefRangeFragment>(Ranges, FixedSizePortion);
463
OS.insert(F);
464
return F;
465
}
466
467
static unsigned computeLabelDiff(const MCAssembler &Asm, const MCSymbol *Begin,
468
const MCSymbol *End) {
469
MCContext &Ctx = Asm.getContext();
470
MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None;
471
const MCExpr *BeginRef = MCSymbolRefExpr::create(Begin, Variant, Ctx),
472
*EndRef = MCSymbolRefExpr::create(End, Variant, Ctx);
473
const MCExpr *AddrDelta =
474
MCBinaryExpr::create(MCBinaryExpr::Sub, EndRef, BeginRef, Ctx);
475
int64_t Result;
476
bool Success = AddrDelta->evaluateKnownAbsolute(Result, Asm);
477
assert(Success && "failed to evaluate label difference as absolute");
478
(void)Success;
479
assert(Result >= 0 && "negative label difference requested");
480
assert(Result < UINT_MAX && "label difference greater than 2GB");
481
return unsigned(Result);
482
}
483
484
void CodeViewContext::encodeInlineLineTable(const MCAssembler &Asm,
485
MCCVInlineLineTableFragment &Frag) {
486
size_t LocBegin;
487
size_t LocEnd;
488
std::tie(LocBegin, LocEnd) = getLineExtentIncludingInlinees(Frag.SiteFuncId);
489
490
if (LocBegin >= LocEnd)
491
return;
492
ArrayRef<MCCVLoc> Locs = getLinesForExtent(LocBegin, LocEnd);
493
if (Locs.empty())
494
return;
495
496
// Check that the locations are all in the same section.
497
#ifndef NDEBUG
498
const MCSection *FirstSec = &Locs.front().getLabel()->getSection();
499
for (const MCCVLoc &Loc : Locs) {
500
if (&Loc.getLabel()->getSection() != FirstSec) {
501
errs() << ".cv_loc " << Loc.getFunctionId() << ' ' << Loc.getFileNum()
502
<< ' ' << Loc.getLine() << ' ' << Loc.getColumn()
503
<< " is in the wrong section\n";
504
llvm_unreachable(".cv_loc crosses sections");
505
}
506
}
507
#endif
508
509
// Make an artificial start location using the function start and the inlinee
510
// lines start location information. All deltas start relative to this
511
// location.
512
MCCVLoc StartLoc = Locs.front();
513
StartLoc.setLabel(Frag.getFnStartSym());
514
StartLoc.setFileNum(Frag.StartFileId);
515
StartLoc.setLine(Frag.StartLineNum);
516
bool HaveOpenRange = false;
517
518
const MCSymbol *LastLabel = Frag.getFnStartSym();
519
MCCVFunctionInfo::LineInfo LastSourceLoc, CurSourceLoc;
520
LastSourceLoc.File = Frag.StartFileId;
521
LastSourceLoc.Line = Frag.StartLineNum;
522
523
MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(Frag.SiteFuncId);
524
525
SmallVectorImpl<char> &Buffer = Frag.getContents();
526
Buffer.clear(); // Clear old contents if we went through relaxation.
527
for (const MCCVLoc &Loc : Locs) {
528
// Exit early if our line table would produce an oversized InlineSiteSym
529
// record. Account for the ChangeCodeLength annotation emitted after the
530
// loop ends.
531
constexpr uint32_t InlineSiteSize = 12;
532
constexpr uint32_t AnnotationSize = 8;
533
size_t MaxBufferSize = MaxRecordLength - InlineSiteSize - AnnotationSize;
534
if (Buffer.size() >= MaxBufferSize)
535
break;
536
537
if (Loc.getFunctionId() == Frag.SiteFuncId) {
538
CurSourceLoc.File = Loc.getFileNum();
539
CurSourceLoc.Line = Loc.getLine();
540
} else {
541
auto I = SiteInfo->InlinedAtMap.find(Loc.getFunctionId());
542
if (I != SiteInfo->InlinedAtMap.end()) {
543
// This .cv_loc is from a child inline call site. Use the source
544
// location of the inlined call site instead of the .cv_loc directive
545
// source location.
546
CurSourceLoc = I->second;
547
} else {
548
// We've hit a cv_loc not attributed to this inline call site. Use this
549
// label to end the PC range.
550
if (HaveOpenRange) {
551
unsigned Length = computeLabelDiff(Asm, LastLabel, Loc.getLabel());
552
compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeLength, Buffer);
553
compressAnnotation(Length, Buffer);
554
LastLabel = Loc.getLabel();
555
}
556
HaveOpenRange = false;
557
continue;
558
}
559
}
560
561
// Skip this .cv_loc if we have an open range and this isn't a meaningful
562
// source location update. The current table format does not support column
563
// info, so we can skip updates for those.
564
if (HaveOpenRange && CurSourceLoc.File == LastSourceLoc.File &&
565
CurSourceLoc.Line == LastSourceLoc.Line)
566
continue;
567
568
HaveOpenRange = true;
569
570
if (CurSourceLoc.File != LastSourceLoc.File) {
571
unsigned FileOffset = static_cast<const MCConstantExpr *>(
572
Files[CurSourceLoc.File - 1]
573
.ChecksumTableOffset->getVariableValue())
574
->getValue();
575
compressAnnotation(BinaryAnnotationsOpCode::ChangeFile, Buffer);
576
compressAnnotation(FileOffset, Buffer);
577
}
578
579
int LineDelta = CurSourceLoc.Line - LastSourceLoc.Line;
580
unsigned EncodedLineDelta = encodeSignedNumber(LineDelta);
581
unsigned CodeDelta = computeLabelDiff(Asm, LastLabel, Loc.getLabel());
582
if (EncodedLineDelta < 0x8 && CodeDelta <= 0xf) {
583
// The ChangeCodeOffsetAndLineOffset combination opcode is used when the
584
// encoded line delta uses 3 or fewer set bits and the code offset fits
585
// in one nibble.
586
unsigned Operand = (EncodedLineDelta << 4) | CodeDelta;
587
compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset,
588
Buffer);
589
compressAnnotation(Operand, Buffer);
590
} else {
591
// Otherwise use the separate line and code deltas.
592
if (LineDelta != 0) {
593
compressAnnotation(BinaryAnnotationsOpCode::ChangeLineOffset, Buffer);
594
compressAnnotation(EncodedLineDelta, Buffer);
595
}
596
compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeOffset, Buffer);
597
compressAnnotation(CodeDelta, Buffer);
598
}
599
600
LastLabel = Loc.getLabel();
601
LastSourceLoc = CurSourceLoc;
602
}
603
604
assert(HaveOpenRange);
605
606
unsigned EndSymLength =
607
computeLabelDiff(Asm, LastLabel, Frag.getFnEndSym());
608
unsigned LocAfterLength = ~0U;
609
ArrayRef<MCCVLoc> LocAfter = getLinesForExtent(LocEnd, LocEnd + 1);
610
if (!LocAfter.empty()) {
611
// Only try to compute this difference if we're in the same section.
612
const MCCVLoc &Loc = LocAfter[0];
613
if (&Loc.getLabel()->getSection() == &LastLabel->getSection())
614
LocAfterLength = computeLabelDiff(Asm, LastLabel, Loc.getLabel());
615
}
616
617
compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeLength, Buffer);
618
compressAnnotation(std::min(EndSymLength, LocAfterLength), Buffer);
619
}
620
621
void CodeViewContext::encodeDefRange(const MCAssembler &Asm,
622
MCCVDefRangeFragment &Frag) {
623
MCContext &Ctx = Asm.getContext();
624
SmallVectorImpl<char> &Contents = Frag.getContents();
625
Contents.clear();
626
SmallVectorImpl<MCFixup> &Fixups = Frag.getFixups();
627
Fixups.clear();
628
raw_svector_ostream OS(Contents);
629
630
// Compute all the sizes up front.
631
SmallVector<std::pair<unsigned, unsigned>, 4> GapAndRangeSizes;
632
const MCSymbol *LastLabel = nullptr;
633
for (std::pair<const MCSymbol *, const MCSymbol *> Range : Frag.getRanges()) {
634
unsigned GapSize =
635
LastLabel ? computeLabelDiff(Asm, LastLabel, Range.first) : 0;
636
unsigned RangeSize = computeLabelDiff(Asm, Range.first, Range.second);
637
GapAndRangeSizes.push_back({GapSize, RangeSize});
638
LastLabel = Range.second;
639
}
640
641
// Write down each range where the variable is defined.
642
for (size_t I = 0, E = Frag.getRanges().size(); I != E;) {
643
// If the range size of multiple consecutive ranges is under the max,
644
// combine the ranges and emit some gaps.
645
const MCSymbol *RangeBegin = Frag.getRanges()[I].first;
646
unsigned RangeSize = GapAndRangeSizes[I].second;
647
size_t J = I + 1;
648
for (; J != E; ++J) {
649
unsigned GapAndRangeSize = GapAndRangeSizes[J].first + GapAndRangeSizes[J].second;
650
if (RangeSize + GapAndRangeSize > MaxDefRange)
651
break;
652
RangeSize += GapAndRangeSize;
653
}
654
unsigned NumGaps = J - I - 1;
655
656
support::endian::Writer LEWriter(OS, llvm::endianness::little);
657
658
unsigned Bias = 0;
659
// We must split the range into chunks of MaxDefRange, this is a fundamental
660
// limitation of the file format.
661
do {
662
uint16_t Chunk = std::min((uint32_t)MaxDefRange, RangeSize);
663
664
const MCSymbolRefExpr *SRE = MCSymbolRefExpr::create(RangeBegin, Ctx);
665
const MCBinaryExpr *BE =
666
MCBinaryExpr::createAdd(SRE, MCConstantExpr::create(Bias, Ctx), Ctx);
667
668
// Each record begins with a 2-byte number indicating how large the record
669
// is.
670
StringRef FixedSizePortion = Frag.getFixedSizePortion();
671
// Our record is a fixed sized prefix and a LocalVariableAddrRange that we
672
// are artificially constructing.
673
size_t RecordSize = FixedSizePortion.size() +
674
sizeof(LocalVariableAddrRange) + 4 * NumGaps;
675
// Write out the record size.
676
LEWriter.write<uint16_t>(RecordSize);
677
// Write out the fixed size prefix.
678
OS << FixedSizePortion;
679
// Make space for a fixup that will eventually have a section relative
680
// relocation pointing at the offset where the variable becomes live.
681
Fixups.push_back(MCFixup::create(Contents.size(), BE, FK_SecRel_4));
682
LEWriter.write<uint32_t>(0); // Fixup for code start.
683
// Make space for a fixup that will record the section index for the code.
684
Fixups.push_back(MCFixup::create(Contents.size(), BE, FK_SecRel_2));
685
LEWriter.write<uint16_t>(0); // Fixup for section index.
686
// Write down the range's extent.
687
LEWriter.write<uint16_t>(Chunk);
688
689
// Move on to the next range.
690
Bias += Chunk;
691
RangeSize -= Chunk;
692
} while (RangeSize > 0);
693
694
// Emit the gaps afterwards.
695
assert((NumGaps == 0 || Bias <= MaxDefRange) &&
696
"large ranges should not have gaps");
697
unsigned GapStartOffset = GapAndRangeSizes[I].second;
698
for (++I; I != J; ++I) {
699
unsigned GapSize, RangeSize;
700
assert(I < GapAndRangeSizes.size());
701
std::tie(GapSize, RangeSize) = GapAndRangeSizes[I];
702
LEWriter.write<uint16_t>(GapStartOffset);
703
LEWriter.write<uint16_t>(GapSize);
704
GapStartOffset += GapSize + RangeSize;
705
}
706
}
707
}
708
709