blob: 9d5c213085c2935d44244a13279002af17514b2c [file] [log] [blame]
//===- DWARFLinkerTypeUnit.cpp --------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "DWARFLinkerTypeUnit.h"
#include "DIEGenerator.h"
#include "DWARFEmitterImpl.h"
#include "llvm/Support/LEB128.h"
using namespace llvm;
using namespace llvm::dwarflinker_parallel;
TypeUnit::TypeUnit(LinkingGlobalData &GlobalData, unsigned ID,
std::optional<uint16_t> Language, dwarf::FormParams Format,
endianness Endianess)
: DwarfUnit(GlobalData, ID, ""), Language(Language),
AcceleratorRecords(&GlobalData.getAllocator()) {
UnitName = "__artificial_type_unit";
setOutputFormat(Format, Endianess);
// Create line table prologue.
LineTable.Prologue.FormParams = getFormParams();
LineTable.Prologue.MinInstLength = 1;
LineTable.Prologue.MaxOpsPerInst = 1;
LineTable.Prologue.DefaultIsStmt = 1;
LineTable.Prologue.LineBase = -5;
LineTable.Prologue.LineRange = 14;
LineTable.Prologue.OpcodeBase = 13;
LineTable.Prologue.StandardOpcodeLengths = {0, 1, 1, 1, 1, 0,
0, 0, 1, 0, 0, 1};
getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
}
void TypeUnit::createDIETree(BumpPtrAllocator &Allocator) {
prepareDataForTreeCreation();
// TaskGroup is created here as internal code has calls to
// PerThreadBumpPtrAllocator which should be called from the task group task.
parallel::TaskGroup TG;
TG.spawn([&]() {
SectionDescriptor &DebugInfoSection =
getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
SectionDescriptor &DebugLineSection =
getOrCreateSectionDescriptor(DebugSectionKind::DebugLine);
DIEGenerator DIETreeGenerator(Allocator, *this);
OffsetsPtrVector PatchesOffsets;
// Create a Die for artificial compilation unit for types.
DIE *UnitDIE = DIETreeGenerator.createDIE(dwarf::DW_TAG_compile_unit, 0);
uint64_t OutOffset = getDebugInfoHeaderSize();
UnitDIE->setOffset(OutOffset);
SmallString<200> ProducerString;
ProducerString += "llvm DWARFLinkerParallel library version ";
DebugInfoSection.notePatchWithOffsetUpdate(
DebugStrPatch{
{OutOffset},
GlobalData.getStringPool().insert(ProducerString.str()).first},
PatchesOffsets);
OutOffset += DIETreeGenerator
.addStringPlaceholderAttribute(dwarf::DW_AT_producer,
dwarf::DW_FORM_strp)
.second;
if (Language) {
OutOffset += DIETreeGenerator
.addScalarAttribute(dwarf::DW_AT_language,
dwarf::DW_FORM_data2, *Language)
.second;
}
DebugInfoSection.notePatchWithOffsetUpdate(
DebugStrPatch{{OutOffset},
GlobalData.getStringPool().insert(getUnitName()).first},
PatchesOffsets);
OutOffset += DIETreeGenerator
.addStringPlaceholderAttribute(dwarf::DW_AT_name,
dwarf::DW_FORM_strp)
.second;
if (!LineTable.Prologue.FileNames.empty()) {
DebugInfoSection.notePatchWithOffsetUpdate(
DebugOffsetPatch{OutOffset, &DebugLineSection}, PatchesOffsets);
OutOffset += DIETreeGenerator
.addScalarAttribute(dwarf::DW_AT_stmt_list,
dwarf::DW_FORM_sec_offset, 0xbaddef)
.second;
}
DebugInfoSection.notePatchWithOffsetUpdate(
DebugStrPatch{{OutOffset}, GlobalData.getStringPool().insert("").first},
PatchesOffsets);
OutOffset += DIETreeGenerator
.addStringPlaceholderAttribute(dwarf::DW_AT_comp_dir,
dwarf::DW_FORM_strp)
.second;
if (!DebugStringIndexMap.empty()) {
// Type unit is assumed to be emitted first. Thus we can use direct value
// for DW_AT_str_offsets_base attribute(No need to fix it up with unit
// offset value).
OutOffset += DIETreeGenerator
.addScalarAttribute(dwarf::DW_AT_str_offsets_base,
dwarf::DW_FORM_sec_offset,
getDebugStrOffsetsHeaderSize())
.second;
}
UnitDIE->setSize(OutOffset - UnitDIE->getOffset() + 1);
OutOffset =
finalizeTypeEntryRec(UnitDIE->getOffset(), UnitDIE, Types.getRoot());
// Update patch offsets.
for (uint64_t *OffsetPtr : PatchesOffsets)
*OffsetPtr += getULEB128Size(UnitDIE->getAbbrevNumber());
setOutUnitDIE(UnitDIE);
});
}
void TypeUnit::prepareDataForTreeCreation() {
SectionDescriptor &DebugInfoSection =
getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
// Type unit data created parallelly. So the order of data is not
// deterministic. Order data here if we need deterministic output.
parallel::TaskGroup TG;
if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
TG.spawn([&]() {
// Sort types to have a deterministic output.
Types.sortTypes();
});
}
TG.spawn([&]() {
if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
// Sort decl type patches to have a deterministic output.
std::function<bool(const DebugTypeDeclFilePatch &LHS,
const DebugTypeDeclFilePatch &RHS)>
PatchesComparator = [&](const DebugTypeDeclFilePatch &LHS,
const DebugTypeDeclFilePatch &RHS) {
return LHS.Directory->first() < RHS.Directory->first() ||
(!(RHS.Directory->first() < LHS.Directory->first()) &&
LHS.FilePath->first() < RHS.FilePath->first());
};
// Sort patches to have a deterministic output.
DebugInfoSection.ListDebugTypeDeclFilePatch.sort(PatchesComparator);
}
// Update DW_AT_decl_file attribute
dwarf::Form DeclFileForm =
getScalarFormForValue(
DebugInfoSection.ListDebugTypeDeclFilePatch.size())
.first;
DebugInfoSection.ListDebugTypeDeclFilePatch.forEach(
[&](DebugTypeDeclFilePatch &Patch) {
TypeEntryBody *TypeEntry = Patch.TypeName->getValue().load();
assert(TypeEntry &&
formatv("No data for type {0}", Patch.TypeName->getKey())
.str()
.c_str());
if (&TypeEntry->getFinalDie() != Patch.Die)
return;
uint32_t FileIdx =
addFileNameIntoLinetable(Patch.Directory, Patch.FilePath);
unsigned DIESize = Patch.Die->getSize();
DIEGenerator DIEGen(Patch.Die, Types.getThreadLocalAllocator(),
*this);
DIESize += DIEGen
.addScalarAttribute(dwarf::DW_AT_decl_file,
DeclFileForm, FileIdx)
.second;
Patch.Die->setSize(DIESize);
});
});
if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
// Sort patches to have a deterministic output.
TG.spawn([&]() {
forEach([&](SectionDescriptor &OutSection) {
std::function<bool(const DebugStrPatch &LHS, const DebugStrPatch &RHS)>
StrPatchesComparator =
[&](const DebugStrPatch &LHS, const DebugStrPatch &RHS) {
return LHS.String->getKey() < RHS.String->getKey();
};
OutSection.ListDebugStrPatch.sort(StrPatchesComparator);
std::function<bool(const DebugTypeStrPatch &LHS,
const DebugTypeStrPatch &RHS)>
TypeStrPatchesComparator = [&](const DebugTypeStrPatch &LHS,
const DebugTypeStrPatch &RHS) {
return LHS.String->getKey() < RHS.String->getKey();
};
OutSection.ListDebugTypeStrPatch.sort(TypeStrPatchesComparator);
});
});
}
if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
// Sort patches to have a deterministic output.
TG.spawn([&]() {
forEach([&](SectionDescriptor &OutSection) {
std::function<bool(const DebugLineStrPatch &LHS,
const DebugLineStrPatch &RHS)>
LineStrPatchesComparator = [&](const DebugLineStrPatch &LHS,
const DebugLineStrPatch &RHS) {
return LHS.String->getKey() < RHS.String->getKey();
};
OutSection.ListDebugLineStrPatch.sort(LineStrPatchesComparator);
std::function<bool(const DebugTypeLineStrPatch &LHS,
const DebugTypeLineStrPatch &RHS)>
TypeLineStrPatchesComparator =
[&](const DebugTypeLineStrPatch &LHS,
const DebugTypeLineStrPatch &RHS) {
return LHS.String->getKey() < RHS.String->getKey();
};
OutSection.ListDebugTypeLineStrPatch.sort(TypeLineStrPatchesComparator);
});
});
}
}
uint64_t TypeUnit::finalizeTypeEntryRec(uint64_t OutOffset, DIE *OutDIE,
TypeEntry *Entry) {
bool HasChildren = !Entry->getValue().load()->Children.empty();
DIEGenerator DIEGen(OutDIE, Types.getThreadLocalAllocator(), *this);
OutOffset += DIEGen.finalizeAbbreviations(HasChildren, nullptr);
OutOffset += OutDIE->getSize() - 1;
if (HasChildren) {
Entry->getValue().load()->Children.forEach([&](TypeEntry *ChildEntry) {
DIE *ChildDIE = &ChildEntry->getValue().load()->getFinalDie();
DIEGen.addChild(ChildDIE);
ChildDIE->setOffset(OutOffset);
OutOffset = finalizeTypeEntryRec(OutOffset, ChildDIE, ChildEntry);
});
// End of children marker.
OutOffset += sizeof(int8_t);
}
OutDIE->setSize(OutOffset - OutDIE->getOffset());
return OutOffset;
}
uint32_t TypeUnit::addFileNameIntoLinetable(StringEntry *Dir,
StringEntry *FileName) {
uint32_t DirIdx = 0;
if (Dir->first() == "") {
DirIdx = 0;
} else {
DirectoriesMapTy::iterator DirEntry = DirectoriesMap.find(Dir);
if (DirEntry == DirectoriesMap.end()) {
// We currently do not support more than UINT32_MAX directories.
assert(LineTable.Prologue.IncludeDirectories.size() < UINT32_MAX);
DirIdx = LineTable.Prologue.IncludeDirectories.size();
DirectoriesMap.insert({Dir, DirIdx});
LineTable.Prologue.IncludeDirectories.push_back(
DWARFFormValue::createFromPValue(dwarf::DW_FORM_string,
Dir->getKeyData()));
} else {
DirIdx = DirEntry->second;
}
if (getVersion() < 5)
DirIdx++;
}
uint32_t FileIdx = 0;
FilenamesMapTy::iterator FileEntry = FileNamesMap.find({FileName, DirIdx});
if (FileEntry == FileNamesMap.end()) {
// We currently do not support more than UINT32_MAX files.
assert(LineTable.Prologue.FileNames.size() < UINT32_MAX);
FileIdx = LineTable.Prologue.FileNames.size();
FileNamesMap.insert({{FileName, DirIdx}, FileIdx});
LineTable.Prologue.FileNames.push_back(DWARFDebugLine::FileNameEntry());
LineTable.Prologue.FileNames.back().Name = DWARFFormValue::createFromPValue(
dwarf::DW_FORM_string, FileName->getKeyData());
LineTable.Prologue.FileNames.back().DirIdx = DirIdx;
} else {
FileIdx = FileEntry->second;
}
return getVersion() < 5 ? FileIdx + 1 : FileIdx;
}
std::pair<dwarf::Form, uint8_t>
TypeUnit::getScalarFormForValue(uint64_t Value) const {
if (Value > 0xFFFFFFFF)
return std::make_pair(dwarf::DW_FORM_data8, 8);
if (Value > 0xFFFF)
return std::make_pair(dwarf::DW_FORM_data4, 4);
if (Value > 0xFF)
return std::make_pair(dwarf::DW_FORM_data2, 2);
return std::make_pair(dwarf::DW_FORM_data1, 1);
}
uint8_t TypeUnit::getSizeByAttrForm(dwarf::Form Form) const {
if (Form == dwarf::DW_FORM_data1)
return 1;
if (Form == dwarf::DW_FORM_data2)
return 2;
if (Form == dwarf::DW_FORM_data4)
return 4;
if (Form == dwarf::DW_FORM_data8)
return 8;
if (Form == dwarf::DW_FORM_data16)
return 16;
llvm_unreachable("Unsupported Attr Form");
}
Error TypeUnit::finishCloningAndEmit(std::optional<Triple> TargetTriple) {
BumpPtrAllocator Allocator;
createDIETree(Allocator);
if (getGlobalData().getOptions().NoOutput || (getOutUnitDIE() == nullptr))
return Error::success();
// Create sections ahead so that they should not be created asynchronously
// later.
getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
getOrCreateSectionDescriptor(DebugSectionKind::DebugLine);
getOrCreateSectionDescriptor(DebugSectionKind::DebugStrOffsets);
getOrCreateSectionDescriptor(DebugSectionKind::DebugAbbrev);
if (llvm::is_contained(GlobalData.getOptions().AccelTables,
DWARFLinker::AccelTableKind::Pub)) {
getOrCreateSectionDescriptor(DebugSectionKind::DebugPubNames);
getOrCreateSectionDescriptor(DebugSectionKind::DebugPubTypes);
}
SmallVector<std::function<Error(void)>> Tasks;
// Add task for emitting .debug_line section.
if (!LineTable.Prologue.FileNames.empty()) {
Tasks.push_back([&]() -> Error {
assert(TargetTriple.has_value());
return emitDebugLine(*TargetTriple, LineTable);
});
}
// Add task for emitting .debug_info section.
Tasks.push_back([&]() -> Error { return emitDebugInfo(*TargetTriple); });
// Add task for emitting Pub accelerator sections.
if (llvm::is_contained(GlobalData.getOptions().AccelTables,
DWARFLinker::AccelTableKind::Pub)) {
Tasks.push_back([&]() -> Error {
emitPubAccelerators();
return Error::success();
});
}
// Add task for emitting .debug_str_offsets section.
Tasks.push_back([&]() -> Error { return emitDebugStringOffsetSection(); });
// Add task for emitting .debug_abbr section.
Tasks.push_back([&]() -> Error { return emitAbbreviations(); });
if (auto Err = parallelForEachError(
Tasks, [&](std::function<Error(void)> F) { return F(); }))
return Err;
return Error::success();
}