Path: blob/main/contrib/llvm-project/llvm/lib/MC/MCCodeView.cpp
35232 views
//===- MCCodeView.h - Machine Code CodeView support -------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Holds state from .cv_file and .cv_loc directives for later emission.9//10//===----------------------------------------------------------------------===//1112#include "llvm/MC/MCCodeView.h"13#include "llvm/ADT/STLExtras.h"14#include "llvm/ADT/StringExtras.h"15#include "llvm/DebugInfo/CodeView/CodeView.h"16#include "llvm/DebugInfo/CodeView/Line.h"17#include "llvm/DebugInfo/CodeView/SymbolRecord.h"18#include "llvm/MC/MCAssembler.h"19#include "llvm/MC/MCContext.h"20#include "llvm/MC/MCObjectStreamer.h"21#include "llvm/MC/MCValue.h"22#include "llvm/Support/EndianStream.h"2324using namespace llvm;25using namespace llvm::codeview;2627CodeViewContext::~CodeViewContext() {28// If someone inserted strings into the string table but never actually29// emitted them somewhere, clean up the fragment.30if (!InsertedStrTabFragment && StrTabFragment)31StrTabFragment->destroy();32}3334/// This is a valid number for use with .cv_loc if we've already seen a .cv_file35/// for it.36bool CodeViewContext::isValidFileNumber(unsigned FileNumber) const {37unsigned Idx = FileNumber - 1;38if (Idx < Files.size())39return Files[Idx].Assigned;40return false;41}4243bool CodeViewContext::addFile(MCStreamer &OS, unsigned FileNumber,44StringRef Filename,45ArrayRef<uint8_t> ChecksumBytes,46uint8_t ChecksumKind) {47assert(FileNumber > 0);48auto FilenameOffset = addToStringTable(Filename);49Filename = FilenameOffset.first;50unsigned Idx = FileNumber - 1;51if (Idx >= Files.size())52Files.resize(Idx + 1);5354if (Filename.empty())55Filename = "<stdin>";5657if (Files[Idx].Assigned)58return false;5960FilenameOffset = addToStringTable(Filename);61Filename = FilenameOffset.first;62unsigned Offset = FilenameOffset.second;6364auto ChecksumOffsetSymbol =65OS.getContext().createTempSymbol("checksum_offset", false);66Files[Idx].StringTableOffset = Offset;67Files[Idx].ChecksumTableOffset = ChecksumOffsetSymbol;68Files[Idx].Assigned = true;69Files[Idx].Checksum = ChecksumBytes;70Files[Idx].ChecksumKind = ChecksumKind;7172return true;73}7475MCCVFunctionInfo *CodeViewContext::getCVFunctionInfo(unsigned FuncId) {76if (FuncId >= Functions.size())77return nullptr;78if (Functions[FuncId].isUnallocatedFunctionInfo())79return nullptr;80return &Functions[FuncId];81}8283bool CodeViewContext::recordFunctionId(unsigned FuncId) {84if (FuncId >= Functions.size())85Functions.resize(FuncId + 1);8687// Return false if this function info was already allocated.88if (!Functions[FuncId].isUnallocatedFunctionInfo())89return false;9091// Mark this as an allocated normal function, and leave the rest alone.92Functions[FuncId].ParentFuncIdPlusOne = MCCVFunctionInfo::FunctionSentinel;93return true;94}9596bool CodeViewContext::recordInlinedCallSiteId(unsigned FuncId, unsigned IAFunc,97unsigned IAFile, unsigned IALine,98unsigned IACol) {99if (FuncId >= Functions.size())100Functions.resize(FuncId + 1);101102// Return false if this function info was already allocated.103if (!Functions[FuncId].isUnallocatedFunctionInfo())104return false;105106MCCVFunctionInfo::LineInfo InlinedAt;107InlinedAt.File = IAFile;108InlinedAt.Line = IALine;109InlinedAt.Col = IACol;110111// Mark this as an inlined call site and record call site line info.112MCCVFunctionInfo *Info = &Functions[FuncId];113Info->ParentFuncIdPlusOne = IAFunc + 1;114Info->InlinedAt = InlinedAt;115116// Walk up the call chain adding this function id to the InlinedAtMap of all117// transitive callers until we hit a real function.118while (Info->isInlinedCallSite()) {119InlinedAt = Info->InlinedAt;120Info = getCVFunctionInfo(Info->getParentFuncId());121Info->InlinedAtMap[FuncId] = InlinedAt;122}123124return true;125}126127void CodeViewContext::recordCVLoc(MCContext &Ctx, const MCSymbol *Label,128unsigned FunctionId, unsigned FileNo,129unsigned Line, unsigned Column,130bool PrologueEnd, bool IsStmt) {131addLineEntry(MCCVLoc{132Label, FunctionId, FileNo, Line, Column, PrologueEnd, IsStmt});133}134135MCDataFragment *CodeViewContext::getStringTableFragment() {136if (!StrTabFragment) {137StrTabFragment = MCCtx->allocFragment<MCDataFragment>();138// Start a new string table out with a null byte.139StrTabFragment->getContents().push_back('\0');140}141return StrTabFragment;142}143144std::pair<StringRef, unsigned> CodeViewContext::addToStringTable(StringRef S) {145SmallVectorImpl<char> &Contents = getStringTableFragment()->getContents();146auto Insertion =147StringTable.insert(std::make_pair(S, unsigned(Contents.size())));148// Return the string from the table, since it is stable.149std::pair<StringRef, unsigned> Ret =150std::make_pair(Insertion.first->first(), Insertion.first->second);151if (Insertion.second) {152// The string map key is always null terminated.153Contents.append(Ret.first.begin(), Ret.first.end() + 1);154}155return Ret;156}157158unsigned CodeViewContext::getStringTableOffset(StringRef S) {159// A string table offset of zero is always the empty string.160if (S.empty())161return 0;162auto I = StringTable.find(S);163assert(I != StringTable.end());164return I->second;165}166167void CodeViewContext::emitStringTable(MCObjectStreamer &OS) {168MCContext &Ctx = OS.getContext();169MCSymbol *StringBegin = Ctx.createTempSymbol("strtab_begin", false),170*StringEnd = Ctx.createTempSymbol("strtab_end", false);171172OS.emitInt32(uint32_t(DebugSubsectionKind::StringTable));173OS.emitAbsoluteSymbolDiff(StringEnd, StringBegin, 4);174OS.emitLabel(StringBegin);175176// Put the string table data fragment here, if we haven't already put it177// somewhere else. If somebody wants two string tables in their .s file, one178// will just be empty.179if (!InsertedStrTabFragment) {180OS.insert(getStringTableFragment());181InsertedStrTabFragment = true;182}183184OS.emitValueToAlignment(Align(4), 0);185186OS.emitLabel(StringEnd);187}188189void CodeViewContext::emitFileChecksums(MCObjectStreamer &OS) {190// Do nothing if there are no file checksums. Microsoft's linker rejects empty191// CodeView substreams.192if (Files.empty())193return;194195MCContext &Ctx = OS.getContext();196MCSymbol *FileBegin = Ctx.createTempSymbol("filechecksums_begin", false),197*FileEnd = Ctx.createTempSymbol("filechecksums_end", false);198199OS.emitInt32(uint32_t(DebugSubsectionKind::FileChecksums));200OS.emitAbsoluteSymbolDiff(FileEnd, FileBegin, 4);201OS.emitLabel(FileBegin);202203unsigned CurrentOffset = 0;204205// Emit an array of FileChecksum entries. We index into this table using the206// user-provided file number. Each entry may be a variable number of bytes207// determined by the checksum kind and size.208for (auto File : Files) {209OS.emitAssignment(File.ChecksumTableOffset,210MCConstantExpr::create(CurrentOffset, Ctx));211CurrentOffset += 4; // String table offset.212if (!File.ChecksumKind) {213CurrentOffset +=2144; // One byte each for checksum size and kind, then align to 4 bytes.215} else {216CurrentOffset += 2; // One byte each for checksum size and kind.217CurrentOffset += File.Checksum.size();218CurrentOffset = alignTo(CurrentOffset, 4);219}220221OS.emitInt32(File.StringTableOffset);222223if (!File.ChecksumKind) {224// There is no checksum. Therefore zero the next two fields and align225// back to 4 bytes.226OS.emitInt32(0);227continue;228}229OS.emitInt8(static_cast<uint8_t>(File.Checksum.size()));230OS.emitInt8(File.ChecksumKind);231OS.emitBytes(toStringRef(File.Checksum));232OS.emitValueToAlignment(Align(4));233}234235OS.emitLabel(FileEnd);236237ChecksumOffsetsAssigned = true;238}239240// Output checksum table offset of the given file number. It is possible that241// not all files have been registered yet, and so the offset cannot be242// calculated. In this case a symbol representing the offset is emitted, and243// the value of this symbol will be fixed up at a later time.244void CodeViewContext::emitFileChecksumOffset(MCObjectStreamer &OS,245unsigned FileNo) {246unsigned Idx = FileNo - 1;247248if (Idx >= Files.size())249Files.resize(Idx + 1);250251if (ChecksumOffsetsAssigned) {252OS.emitSymbolValue(Files[Idx].ChecksumTableOffset, 4);253return;254}255256const MCSymbolRefExpr *SRE =257MCSymbolRefExpr::create(Files[Idx].ChecksumTableOffset, OS.getContext());258259OS.emitValueImpl(SRE, 4);260}261262void CodeViewContext::addLineEntry(const MCCVLoc &LineEntry) {263size_t Offset = MCCVLines.size();264auto I = MCCVLineStartStop.insert(265{LineEntry.getFunctionId(), {Offset, Offset + 1}});266if (!I.second)267I.first->second.second = Offset + 1;268MCCVLines.push_back(LineEntry);269}270271std::vector<MCCVLoc>272CodeViewContext::getFunctionLineEntries(unsigned FuncId) {273std::vector<MCCVLoc> FilteredLines;274size_t LocBegin;275size_t LocEnd;276std::tie(LocBegin, LocEnd) = getLineExtentIncludingInlinees(FuncId);277if (LocBegin >= LocEnd) {278return FilteredLines;279}280281MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(FuncId);282for (size_t Idx = LocBegin; Idx != LocEnd; ++Idx) {283unsigned LocationFuncId = MCCVLines[Idx].getFunctionId();284if (LocationFuncId == FuncId) {285// This was a .cv_loc directly for FuncId, so record it.286FilteredLines.push_back(MCCVLines[Idx]);287} else {288// Check if the current location is inlined in this function. If it is,289// synthesize a statement .cv_loc at the original inlined call site.290auto I = SiteInfo->InlinedAtMap.find(LocationFuncId);291if (I != SiteInfo->InlinedAtMap.end()) {292MCCVFunctionInfo::LineInfo &IA = I->second;293// Only add the location if it differs from the previous location.294// Large inlined calls will have many .cv_loc entries and we only need295// one line table entry in the parent function.296if (FilteredLines.empty() ||297FilteredLines.back().getFileNum() != IA.File ||298FilteredLines.back().getLine() != IA.Line ||299FilteredLines.back().getColumn() != IA.Col) {300FilteredLines.push_back(MCCVLoc(MCCVLines[Idx].getLabel(), FuncId,301IA.File, IA.Line, IA.Col, false,302false));303}304}305}306}307return FilteredLines;308}309310std::pair<size_t, size_t> CodeViewContext::getLineExtent(unsigned FuncId) {311auto I = MCCVLineStartStop.find(FuncId);312// Return an empty extent if there are no cv_locs for this function id.313if (I == MCCVLineStartStop.end())314return {~0ULL, 0};315return I->second;316}317318std::pair<size_t, size_t>319CodeViewContext::getLineExtentIncludingInlinees(unsigned FuncId) {320size_t LocBegin;321size_t LocEnd;322std::tie(LocBegin, LocEnd) = getLineExtent(FuncId);323324// Include all child inline call sites in our extent.325MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(FuncId);326if (SiteInfo) {327for (auto &KV : SiteInfo->InlinedAtMap) {328unsigned ChildId = KV.first;329auto Extent = getLineExtent(ChildId);330LocBegin = std::min(LocBegin, Extent.first);331LocEnd = std::max(LocEnd, Extent.second);332}333}334335return {LocBegin, LocEnd};336}337338ArrayRef<MCCVLoc> CodeViewContext::getLinesForExtent(size_t L, size_t R) {339if (R <= L)340return std::nullopt;341if (L >= MCCVLines.size())342return std::nullopt;343return ArrayRef(&MCCVLines[L], R - L);344}345346void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS,347unsigned FuncId,348const MCSymbol *FuncBegin,349const MCSymbol *FuncEnd) {350MCContext &Ctx = OS.getContext();351MCSymbol *LineBegin = Ctx.createTempSymbol("linetable_begin", false),352*LineEnd = Ctx.createTempSymbol("linetable_end", false);353354OS.emitInt32(uint32_t(DebugSubsectionKind::Lines));355OS.emitAbsoluteSymbolDiff(LineEnd, LineBegin, 4);356OS.emitLabel(LineBegin);357OS.emitCOFFSecRel32(FuncBegin, /*Offset=*/0);358OS.emitCOFFSectionIndex(FuncBegin);359360// Actual line info.361std::vector<MCCVLoc> Locs = getFunctionLineEntries(FuncId);362bool HaveColumns = any_of(Locs, [](const MCCVLoc &LineEntry) {363return LineEntry.getColumn() != 0;364});365OS.emitInt16(HaveColumns ? int(LF_HaveColumns) : 0);366OS.emitAbsoluteSymbolDiff(FuncEnd, FuncBegin, 4);367368for (auto I = Locs.begin(), E = Locs.end(); I != E;) {369// Emit a file segment for the run of locations that share a file id.370unsigned CurFileNum = I->getFileNum();371auto FileSegEnd =372std::find_if(I, E, [CurFileNum](const MCCVLoc &Loc) {373return Loc.getFileNum() != CurFileNum;374});375unsigned EntryCount = FileSegEnd - I;376OS.AddComment(377"Segment for file '" +378Twine(getStringTableFragment()379->getContents()[Files[CurFileNum - 1].StringTableOffset]) +380"' begins");381OS.emitCVFileChecksumOffsetDirective(CurFileNum);382OS.emitInt32(EntryCount);383uint32_t SegmentSize = 12;384SegmentSize += 8 * EntryCount;385if (HaveColumns)386SegmentSize += 4 * EntryCount;387OS.emitInt32(SegmentSize);388389for (auto J = I; J != FileSegEnd; ++J) {390OS.emitAbsoluteSymbolDiff(J->getLabel(), FuncBegin, 4);391unsigned LineData = J->getLine();392if (J->isStmt())393LineData |= LineInfo::StatementFlag;394OS.emitInt32(LineData);395}396if (HaveColumns) {397for (auto J = I; J != FileSegEnd; ++J) {398OS.emitInt16(J->getColumn());399OS.emitInt16(0);400}401}402I = FileSegEnd;403}404OS.emitLabel(LineEnd);405}406407static bool compressAnnotation(uint32_t Data, SmallVectorImpl<char> &Buffer) {408if (isUInt<7>(Data)) {409Buffer.push_back(Data);410return true;411}412413if (isUInt<14>(Data)) {414Buffer.push_back((Data >> 8) | 0x80);415Buffer.push_back(Data & 0xff);416return true;417}418419if (isUInt<29>(Data)) {420Buffer.push_back((Data >> 24) | 0xC0);421Buffer.push_back((Data >> 16) & 0xff);422Buffer.push_back((Data >> 8) & 0xff);423Buffer.push_back(Data & 0xff);424return true;425}426427return false;428}429430static bool compressAnnotation(BinaryAnnotationsOpCode Annotation,431SmallVectorImpl<char> &Buffer) {432return compressAnnotation(static_cast<uint32_t>(Annotation), Buffer);433}434435static uint32_t encodeSignedNumber(uint32_t Data) {436if (Data >> 31)437return ((-Data) << 1) | 1;438return Data << 1;439}440441void CodeViewContext::emitInlineLineTableForFunction(MCObjectStreamer &OS,442unsigned PrimaryFunctionId,443unsigned SourceFileId,444unsigned SourceLineNum,445const MCSymbol *FnStartSym,446const MCSymbol *FnEndSym) {447// Create and insert a fragment into the current section that will be encoded448// later.449auto *F = MCCtx->allocFragment<MCCVInlineLineTableFragment>(450PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym);451OS.insert(F);452}453454MCFragment *CodeViewContext::emitDefRange(455MCObjectStreamer &OS,456ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges,457StringRef FixedSizePortion) {458// Create and insert a fragment into the current section that will be encoded459// later.460auto *F =461MCCtx->allocFragment<MCCVDefRangeFragment>(Ranges, FixedSizePortion);462OS.insert(F);463return F;464}465466static unsigned computeLabelDiff(const MCAssembler &Asm, const MCSymbol *Begin,467const MCSymbol *End) {468MCContext &Ctx = Asm.getContext();469MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None;470const MCExpr *BeginRef = MCSymbolRefExpr::create(Begin, Variant, Ctx),471*EndRef = MCSymbolRefExpr::create(End, Variant, Ctx);472const MCExpr *AddrDelta =473MCBinaryExpr::create(MCBinaryExpr::Sub, EndRef, BeginRef, Ctx);474int64_t Result;475bool Success = AddrDelta->evaluateKnownAbsolute(Result, Asm);476assert(Success && "failed to evaluate label difference as absolute");477(void)Success;478assert(Result >= 0 && "negative label difference requested");479assert(Result < UINT_MAX && "label difference greater than 2GB");480return unsigned(Result);481}482483void CodeViewContext::encodeInlineLineTable(const MCAssembler &Asm,484MCCVInlineLineTableFragment &Frag) {485size_t LocBegin;486size_t LocEnd;487std::tie(LocBegin, LocEnd) = getLineExtentIncludingInlinees(Frag.SiteFuncId);488489if (LocBegin >= LocEnd)490return;491ArrayRef<MCCVLoc> Locs = getLinesForExtent(LocBegin, LocEnd);492if (Locs.empty())493return;494495// Check that the locations are all in the same section.496#ifndef NDEBUG497const MCSection *FirstSec = &Locs.front().getLabel()->getSection();498for (const MCCVLoc &Loc : Locs) {499if (&Loc.getLabel()->getSection() != FirstSec) {500errs() << ".cv_loc " << Loc.getFunctionId() << ' ' << Loc.getFileNum()501<< ' ' << Loc.getLine() << ' ' << Loc.getColumn()502<< " is in the wrong section\n";503llvm_unreachable(".cv_loc crosses sections");504}505}506#endif507508// Make an artificial start location using the function start and the inlinee509// lines start location information. All deltas start relative to this510// location.511MCCVLoc StartLoc = Locs.front();512StartLoc.setLabel(Frag.getFnStartSym());513StartLoc.setFileNum(Frag.StartFileId);514StartLoc.setLine(Frag.StartLineNum);515bool HaveOpenRange = false;516517const MCSymbol *LastLabel = Frag.getFnStartSym();518MCCVFunctionInfo::LineInfo LastSourceLoc, CurSourceLoc;519LastSourceLoc.File = Frag.StartFileId;520LastSourceLoc.Line = Frag.StartLineNum;521522MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(Frag.SiteFuncId);523524SmallVectorImpl<char> &Buffer = Frag.getContents();525Buffer.clear(); // Clear old contents if we went through relaxation.526for (const MCCVLoc &Loc : Locs) {527// Exit early if our line table would produce an oversized InlineSiteSym528// record. Account for the ChangeCodeLength annotation emitted after the529// loop ends.530constexpr uint32_t InlineSiteSize = 12;531constexpr uint32_t AnnotationSize = 8;532size_t MaxBufferSize = MaxRecordLength - InlineSiteSize - AnnotationSize;533if (Buffer.size() >= MaxBufferSize)534break;535536if (Loc.getFunctionId() == Frag.SiteFuncId) {537CurSourceLoc.File = Loc.getFileNum();538CurSourceLoc.Line = Loc.getLine();539} else {540auto I = SiteInfo->InlinedAtMap.find(Loc.getFunctionId());541if (I != SiteInfo->InlinedAtMap.end()) {542// This .cv_loc is from a child inline call site. Use the source543// location of the inlined call site instead of the .cv_loc directive544// source location.545CurSourceLoc = I->second;546} else {547// We've hit a cv_loc not attributed to this inline call site. Use this548// label to end the PC range.549if (HaveOpenRange) {550unsigned Length = computeLabelDiff(Asm, LastLabel, Loc.getLabel());551compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeLength, Buffer);552compressAnnotation(Length, Buffer);553LastLabel = Loc.getLabel();554}555HaveOpenRange = false;556continue;557}558}559560// Skip this .cv_loc if we have an open range and this isn't a meaningful561// source location update. The current table format does not support column562// info, so we can skip updates for those.563if (HaveOpenRange && CurSourceLoc.File == LastSourceLoc.File &&564CurSourceLoc.Line == LastSourceLoc.Line)565continue;566567HaveOpenRange = true;568569if (CurSourceLoc.File != LastSourceLoc.File) {570unsigned FileOffset = static_cast<const MCConstantExpr *>(571Files[CurSourceLoc.File - 1]572.ChecksumTableOffset->getVariableValue())573->getValue();574compressAnnotation(BinaryAnnotationsOpCode::ChangeFile, Buffer);575compressAnnotation(FileOffset, Buffer);576}577578int LineDelta = CurSourceLoc.Line - LastSourceLoc.Line;579unsigned EncodedLineDelta = encodeSignedNumber(LineDelta);580unsigned CodeDelta = computeLabelDiff(Asm, LastLabel, Loc.getLabel());581if (EncodedLineDelta < 0x8 && CodeDelta <= 0xf) {582// The ChangeCodeOffsetAndLineOffset combination opcode is used when the583// encoded line delta uses 3 or fewer set bits and the code offset fits584// in one nibble.585unsigned Operand = (EncodedLineDelta << 4) | CodeDelta;586compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset,587Buffer);588compressAnnotation(Operand, Buffer);589} else {590// Otherwise use the separate line and code deltas.591if (LineDelta != 0) {592compressAnnotation(BinaryAnnotationsOpCode::ChangeLineOffset, Buffer);593compressAnnotation(EncodedLineDelta, Buffer);594}595compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeOffset, Buffer);596compressAnnotation(CodeDelta, Buffer);597}598599LastLabel = Loc.getLabel();600LastSourceLoc = CurSourceLoc;601}602603assert(HaveOpenRange);604605unsigned EndSymLength =606computeLabelDiff(Asm, LastLabel, Frag.getFnEndSym());607unsigned LocAfterLength = ~0U;608ArrayRef<MCCVLoc> LocAfter = getLinesForExtent(LocEnd, LocEnd + 1);609if (!LocAfter.empty()) {610// Only try to compute this difference if we're in the same section.611const MCCVLoc &Loc = LocAfter[0];612if (&Loc.getLabel()->getSection() == &LastLabel->getSection())613LocAfterLength = computeLabelDiff(Asm, LastLabel, Loc.getLabel());614}615616compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeLength, Buffer);617compressAnnotation(std::min(EndSymLength, LocAfterLength), Buffer);618}619620void CodeViewContext::encodeDefRange(const MCAssembler &Asm,621MCCVDefRangeFragment &Frag) {622MCContext &Ctx = Asm.getContext();623SmallVectorImpl<char> &Contents = Frag.getContents();624Contents.clear();625SmallVectorImpl<MCFixup> &Fixups = Frag.getFixups();626Fixups.clear();627raw_svector_ostream OS(Contents);628629// Compute all the sizes up front.630SmallVector<std::pair<unsigned, unsigned>, 4> GapAndRangeSizes;631const MCSymbol *LastLabel = nullptr;632for (std::pair<const MCSymbol *, const MCSymbol *> Range : Frag.getRanges()) {633unsigned GapSize =634LastLabel ? computeLabelDiff(Asm, LastLabel, Range.first) : 0;635unsigned RangeSize = computeLabelDiff(Asm, Range.first, Range.second);636GapAndRangeSizes.push_back({GapSize, RangeSize});637LastLabel = Range.second;638}639640// Write down each range where the variable is defined.641for (size_t I = 0, E = Frag.getRanges().size(); I != E;) {642// If the range size of multiple consecutive ranges is under the max,643// combine the ranges and emit some gaps.644const MCSymbol *RangeBegin = Frag.getRanges()[I].first;645unsigned RangeSize = GapAndRangeSizes[I].second;646size_t J = I + 1;647for (; J != E; ++J) {648unsigned GapAndRangeSize = GapAndRangeSizes[J].first + GapAndRangeSizes[J].second;649if (RangeSize + GapAndRangeSize > MaxDefRange)650break;651RangeSize += GapAndRangeSize;652}653unsigned NumGaps = J - I - 1;654655support::endian::Writer LEWriter(OS, llvm::endianness::little);656657unsigned Bias = 0;658// We must split the range into chunks of MaxDefRange, this is a fundamental659// limitation of the file format.660do {661uint16_t Chunk = std::min((uint32_t)MaxDefRange, RangeSize);662663const MCSymbolRefExpr *SRE = MCSymbolRefExpr::create(RangeBegin, Ctx);664const MCBinaryExpr *BE =665MCBinaryExpr::createAdd(SRE, MCConstantExpr::create(Bias, Ctx), Ctx);666667// Each record begins with a 2-byte number indicating how large the record668// is.669StringRef FixedSizePortion = Frag.getFixedSizePortion();670// Our record is a fixed sized prefix and a LocalVariableAddrRange that we671// are artificially constructing.672size_t RecordSize = FixedSizePortion.size() +673sizeof(LocalVariableAddrRange) + 4 * NumGaps;674// Write out the record size.675LEWriter.write<uint16_t>(RecordSize);676// Write out the fixed size prefix.677OS << FixedSizePortion;678// Make space for a fixup that will eventually have a section relative679// relocation pointing at the offset where the variable becomes live.680Fixups.push_back(MCFixup::create(Contents.size(), BE, FK_SecRel_4));681LEWriter.write<uint32_t>(0); // Fixup for code start.682// Make space for a fixup that will record the section index for the code.683Fixups.push_back(MCFixup::create(Contents.size(), BE, FK_SecRel_2));684LEWriter.write<uint16_t>(0); // Fixup for section index.685// Write down the range's extent.686LEWriter.write<uint16_t>(Chunk);687688// Move on to the next range.689Bias += Chunk;690RangeSize -= Chunk;691} while (RangeSize > 0);692693// Emit the gaps afterwards.694assert((NumGaps == 0 || Bias <= MaxDefRange) &&695"large ranges should not have gaps");696unsigned GapStartOffset = GapAndRangeSizes[I].second;697for (++I; I != J; ++I) {698unsigned GapSize, RangeSize;699assert(I < GapAndRangeSizes.size());700std::tie(GapSize, RangeSize) = GapAndRangeSizes[I];701LEWriter.write<uint16_t>(GapStartOffset);702LEWriter.write<uint16_t>(GapSize);703GapStartOffset += GapSize + RangeSize;704}705}706}707708709