#include "DIEHash.h"
#include "ByteStreamer.h"
#include "DwarfCompileUnit.h"
#include "DwarfDebug.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "dwarfdebug"
static StringRef getDIEStringAttr(const DIE &Die, uint16_t Attr) {
for (const auto &V : Die.values())
if (V.getAttribute() == Attr)
return V.getDIEString().getString();
return StringRef("");
}
void DIEHash::addString(StringRef Str) {
LLVM_DEBUG(dbgs() << "Adding string " << Str << " to hash.\n");
Hash.update(Str);
Hash.update(makeArrayRef((uint8_t)'\0'));
}
void DIEHash::addULEB128(uint64_t Value) {
LLVM_DEBUG(dbgs() << "Adding ULEB128 " << Value << " to hash.\n");
do {
uint8_t Byte = Value & 0x7f;
Value >>= 7;
if (Value != 0)
Byte |= 0x80; Hash.update(Byte);
} while (Value != 0);
}
void DIEHash::addSLEB128(int64_t Value) {
LLVM_DEBUG(dbgs() << "Adding ULEB128 " << Value << " to hash.\n");
bool More;
do {
uint8_t Byte = Value & 0x7f;
Value >>= 7;
More = !((((Value == 0) && ((Byte & 0x40) == 0)) ||
((Value == -1) && ((Byte & 0x40) != 0))));
if (More)
Byte |= 0x80; Hash.update(Byte);
} while (More);
}
void DIEHash::addParentContext(const DIE &Parent) {
LLVM_DEBUG(dbgs() << "Adding parent context to hash...\n");
SmallVector<const DIE *, 1> Parents;
const DIE *Cur = &Parent;
while (Cur->getParent()) {
Parents.push_back(Cur);
Cur = Cur->getParent();
}
assert(Cur->getTag() == dwarf::DW_TAG_compile_unit ||
Cur->getTag() == dwarf::DW_TAG_type_unit);
for (const DIE *Die : llvm::reverse(Parents)) {
addULEB128('C');
addULEB128(Die->getTag());
StringRef Name = getDIEStringAttr(*Die, dwarf::DW_AT_name);
LLVM_DEBUG(dbgs() << "... adding context: " << Name << "\n");
if (!Name.empty())
addString(Name);
}
}
void DIEHash::collectAttributes(const DIE &Die, DIEAttrs &Attrs) {
for (const auto &V : Die.values()) {
LLVM_DEBUG(dbgs() << "Attribute: "
<< dwarf::AttributeString(V.getAttribute())
<< " added.\n");
switch (V.getAttribute()) {
#define HANDLE_DIE_HASH_ATTR(NAME) \
case dwarf::NAME: \
Attrs.NAME = V; \
break;
#include "DIEHashAttributes.def"
default:
break;
}
}
}
void DIEHash::hashShallowTypeReference(dwarf::Attribute Attribute,
const DIE &Entry, StringRef Name) {
addULEB128('N');
addULEB128(Attribute);
if (const DIE *Parent = Entry.getParent())
addParentContext(*Parent);
addULEB128('E');
addString(Name);
}
void DIEHash::hashRepeatedTypeReference(dwarf::Attribute Attribute,
unsigned DieNumber) {
addULEB128('R');
addULEB128(Attribute);
addULEB128(DieNumber);
}
void DIEHash::hashDIEEntry(dwarf::Attribute Attribute, dwarf::Tag Tag,
const DIE &Entry) {
assert(Tag != dwarf::DW_TAG_friend && "No current LLVM clients emit friend "
"tags. Add support here when there's "
"a use case");
if ((Tag == dwarf::DW_TAG_pointer_type ||
Tag == dwarf::DW_TAG_reference_type ||
Tag == dwarf::DW_TAG_rvalue_reference_type ||
Tag == dwarf::DW_TAG_ptr_to_member_type) &&
Attribute == dwarf::DW_AT_type) {
StringRef Name = getDIEStringAttr(Entry, dwarf::DW_AT_name);
if (!Name.empty()) {
hashShallowTypeReference(Attribute, Entry, Name);
return;
}
}
unsigned &DieNumber = Numbering[&Entry];
if (DieNumber) {
hashRepeatedTypeReference(Attribute, DieNumber);
return;
}
addULEB128('T');
addULEB128(Attribute);
DieNumber = Numbering.size();
computeHash(Entry);
}
void DIEHash::hashRawTypeReference(const DIE &Entry) {
unsigned &DieNumber = Numbering[&Entry];
if (DieNumber) {
addULEB128('R');
addULEB128(DieNumber);
return;
}
DieNumber = Numbering.size();
addULEB128('T');
computeHash(Entry);
}
void DIEHash::hashBlockData(const DIE::const_value_range &Values) {
for (const auto &V : Values)
if (V.getType() == DIEValue::isBaseTypeRef) {
const DIE &C =
*CU->ExprRefedBaseTypes[V.getDIEBaseTypeRef().getIndex()].Die;
StringRef Name = getDIEStringAttr(C, dwarf::DW_AT_name);
assert(!Name.empty() &&
"Base types referenced from DW_OP_convert should have a name");
hashNestedType(C, Name);
} else
Hash.update((uint64_t)V.getDIEInteger().getValue());
}
void DIEHash::hashLocList(const DIELocList &LocList) {
HashingByteStreamer Streamer(*this);
DwarfDebug &DD = *AP->getDwarfDebug();
const DebugLocStream &Locs = DD.getDebugLocs();
const DebugLocStream::List &List = Locs.getList(LocList.getValue());
for (const DebugLocStream::Entry &Entry : Locs.getEntries(List))
DD.emitDebugLocEntry(Streamer, Entry, List.CU);
}
void DIEHash::hashAttribute(const DIEValue &Value, dwarf::Tag Tag) {
dwarf::Attribute Attribute = Value.getAttribute();
switch (Value.getType()) {
case DIEValue::isNone:
llvm_unreachable("Expected valid DIEValue");
case DIEValue::isEntry:
hashDIEEntry(Attribute, Tag, Value.getDIEEntry().getEntry());
break;
case DIEValue::isInteger: {
addULEB128('A');
addULEB128(Attribute);
switch (Value.getForm()) {
case dwarf::DW_FORM_data1:
case dwarf::DW_FORM_data2:
case dwarf::DW_FORM_data4:
case dwarf::DW_FORM_data8:
case dwarf::DW_FORM_udata:
case dwarf::DW_FORM_sdata:
addULEB128(dwarf::DW_FORM_sdata);
addSLEB128((int64_t)Value.getDIEInteger().getValue());
break;
case dwarf::DW_FORM_flag_present:
case dwarf::DW_FORM_flag:
addULEB128(dwarf::DW_FORM_flag);
addULEB128((int64_t)Value.getDIEInteger().getValue());
break;
default:
llvm_unreachable("Unknown integer form!");
}
break;
}
case DIEValue::isString:
addULEB128('A');
addULEB128(Attribute);
addULEB128(dwarf::DW_FORM_string);
addString(Value.getDIEString().getString());
break;
case DIEValue::isInlineString:
addULEB128('A');
addULEB128(Attribute);
addULEB128(dwarf::DW_FORM_string);
addString(Value.getDIEInlineString().getString());
break;
case DIEValue::isBlock:
case DIEValue::isLoc:
case DIEValue::isLocList:
addULEB128('A');
addULEB128(Attribute);
addULEB128(dwarf::DW_FORM_block);
if (Value.getType() == DIEValue::isBlock) {
addULEB128(Value.getDIEBlock().computeSize(AP->getDwarfFormParams()));
hashBlockData(Value.getDIEBlock().values());
} else if (Value.getType() == DIEValue::isLoc) {
addULEB128(Value.getDIELoc().computeSize(AP->getDwarfFormParams()));
hashBlockData(Value.getDIELoc().values());
} else {
hashLocList(Value.getDIELocList());
}
break;
case DIEValue::isExpr:
case DIEValue::isLabel:
case DIEValue::isBaseTypeRef:
case DIEValue::isDelta:
case DIEValue::isAddrOffset:
llvm_unreachable("Add support for additional value types.");
}
}
void DIEHash::hashAttributes(const DIEAttrs &Attrs, dwarf::Tag Tag) {
#define HANDLE_DIE_HASH_ATTR(NAME) \
{ \
if (Attrs.NAME) \
hashAttribute(Attrs.NAME, Tag); \
}
#include "DIEHashAttributes.def"
}
void DIEHash::addAttributes(const DIE &Die) {
DIEAttrs Attrs = {};
collectAttributes(Die, Attrs);
hashAttributes(Attrs, Die.getTag());
}
void DIEHash::hashNestedType(const DIE &Die, StringRef Name) {
addULEB128('S');
addULEB128(Die.getTag());
addString(Name);
}
void DIEHash::computeHash(const DIE &Die) {
addULEB128('D');
addULEB128(Die.getTag());
addAttributes(Die);
for (const auto &C : Die.children()) {
if (isType(C.getTag()) || (C.getTag() == dwarf::DW_TAG_subprogram && isType(C.getParent()->getTag()))) {
StringRef Name = getDIEStringAttr(C, dwarf::DW_AT_name);
if (!Name.empty()) {
hashNestedType(C, Name);
continue;
}
}
computeHash(C);
}
Hash.update(makeArrayRef((uint8_t)'\0'));
}
uint64_t DIEHash::computeCUSignature(StringRef DWOName, const DIE &Die) {
Numbering.clear();
Numbering[&Die] = 1;
if (!DWOName.empty())
Hash.update(DWOName);
computeHash(Die);
MD5::MD5Result Result;
Hash.final(Result);
return Result.high();
}
uint64_t DIEHash::computeTypeSignature(const DIE &Die) {
Numbering.clear();
Numbering[&Die] = 1;
if (const DIE *Parent = Die.getParent())
addParentContext(*Parent);
computeHash(Die);
MD5::MD5Result Result;
Hash.final(Result);
return Result.high();
}