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