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