Add DWARF for discriminated unions
n Rust, an enum that carries data in the variants is, essentially, a
discriminated union. Furthermore, the Rust compiler will perform
space optimizations on such enums in some situations. Previously,
DWARF for these constructs was emitted using a hack (a magic field
name); but this approach stopped working when more space optimizations
were added in https://github.com/rust-lang/rust/pull/45225.
This patch changes LLVM to allow discriminated unions to be
represented in DWARF. It adds createDiscriminatedUnionType and
createDiscriminatedMemberType to DIBuilder and then arranges for this
to be emitted using DWARF's DW_TAG_variant_part and DW_TAG_variant.
Note that DWARF requires that a discriminated union be represented as
a structure with a variant part. However, as Rust only needs to emit
pure discriminated unions, this is what I chose to expose on
DIBuilder.
Patch by Tom Tromey!
Differential Revision: https://reviews.llvm.org/D42082
llvm-svn: 324426
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 226e482..2833ef4 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -4156,7 +4156,8 @@
OPTIONAL(runtimeLang, DwarfLangField, ); \
OPTIONAL(vtableHolder, MDField, ); \
OPTIONAL(templateParams, MDField, ); \
- OPTIONAL(identifier, MDStringField, );
+ OPTIONAL(identifier, MDStringField, ); \
+ OPTIONAL(discriminator, MDField, );
PARSE_MD_FIELDS();
#undef VISIT_MD_FIELDS
@@ -4166,7 +4167,7 @@
Context, *identifier.Val, tag.Val, name.Val, file.Val, line.Val,
scope.Val, baseType.Val, size.Val, align.Val, offset.Val, flags.Val,
elements.Val, runtimeLang.Val, vtableHolder.Val,
- templateParams.Val)) {
+ templateParams.Val, discriminator.Val)) {
Result = CT;
return false;
}
@@ -4177,7 +4178,8 @@
DICompositeType,
(Context, tag.Val, name.Val, file.Val, line.Val, scope.Val, baseType.Val,
size.Val, align.Val, offset.Val, flags.Val, elements.Val,
- runtimeLang.Val, vtableHolder.Val, templateParams.Val, identifier.Val));
+ runtimeLang.Val, vtableHolder.Val, templateParams.Val, identifier.Val,
+ discriminator.Val));
return false;
}
diff --git a/llvm/lib/BinaryFormat/Dwarf.cpp b/llvm/lib/BinaryFormat/Dwarf.cpp
index 169e50e..bea3df8 100644
--- a/llvm/lib/BinaryFormat/Dwarf.cpp
+++ b/llvm/lib/BinaryFormat/Dwarf.cpp
@@ -393,16 +393,6 @@
return StringRef();
}
-StringRef llvm::dwarf::DiscriminantString(unsigned Discriminant) {
- switch (Discriminant) {
- case DW_DSC_label:
- return "DW_DSC_label";
- case DW_DSC_range:
- return "DW_DSC_range";
- }
- return StringRef();
-}
-
StringRef llvm::dwarf::LNStandardString(unsigned Standard) {
switch (Standard) {
default:
@@ -563,8 +553,6 @@
return InlineCodeString(Val);
case DW_AT_ordering:
return ArrayOrderString(Val);
- case DW_AT_discr_value:
- return DiscriminantString(Val);
}
return StringRef();
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 5250ac5..c0ec80e 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1246,7 +1246,7 @@
break;
}
case bitc::METADATA_COMPOSITE_TYPE: {
- if (Record.size() != 16)
+ if (Record.size() < 16 || Record.size() > 17)
return error("Invalid record");
// If we have a UUID and this is not a forward declaration, lookup the
@@ -1269,6 +1269,7 @@
unsigned RuntimeLang = Record[12];
Metadata *VTableHolder = nullptr;
Metadata *TemplateParams = nullptr;
+ Metadata *Discriminator = nullptr;
auto *Identifier = getMDString(Record[15]);
// If this module is being parsed so that it can be ThinLTO imported
// into another module, composite types only need to be imported
@@ -1289,13 +1290,15 @@
Elements = getMDOrNull(Record[11]);
VTableHolder = getDITypeRefOrNull(Record[13]);
TemplateParams = getMDOrNull(Record[14]);
+ if (Record.size() > 16)
+ Discriminator = getMDOrNull(Record[16]);
}
DICompositeType *CT = nullptr;
if (Identifier)
CT = DICompositeType::buildODRType(
Context, *Identifier, Tag, Name, File, Line, Scope, BaseType,
SizeInBits, AlignInBits, OffsetInBits, Flags, Elements, RuntimeLang,
- VTableHolder, TemplateParams);
+ VTableHolder, TemplateParams, Discriminator);
// Create a node if we didn't get a lazy ODR type.
if (!CT)
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 4cb38d7..76add4f 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1526,6 +1526,7 @@
Record.push_back(VE.getMetadataOrNullID(N->getVTableHolder()));
Record.push_back(VE.getMetadataOrNullID(N->getTemplateParams().get()));
Record.push_back(VE.getMetadataOrNullID(N->getRawIdentifier()));
+ Record.push_back(VE.getMetadataOrNullID(N->getDiscriminator()));
Stream.EmitRecord(bitc::METADATA_COMPOSITE_TYPE, Record, Abbrev);
Record.clear();
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 889b123..0e6ed1c 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -946,9 +946,24 @@
case dwarf::DW_TAG_enumeration_type:
constructEnumTypeDIE(Buffer, CTy);
break;
+ case dwarf::DW_TAG_variant_part:
case dwarf::DW_TAG_structure_type:
case dwarf::DW_TAG_union_type:
case dwarf::DW_TAG_class_type: {
+ // Emit the discriminator for a variant part.
+ DIDerivedType *Discriminator = nullptr;
+ if (Tag == dwarf::DW_TAG_variant_part) {
+ Discriminator = CTy->getDiscriminator();
+ if (Discriminator) {
+ // DWARF says:
+ // If the variant part has a discriminant, the discriminant is
+ // represented by a separate debugging information entry which is
+ // a child of the variant part entry.
+ DIE &DiscMember = constructMemberDIE(Buffer, Discriminator);
+ addDIEEntry(Buffer, dwarf::DW_AT_discr, DiscMember);
+ }
+ }
+
// Add elements to structure type.
DINodeArray Elements = CTy->getElements();
for (const auto *Element : Elements) {
@@ -962,6 +977,18 @@
addType(ElemDie, resolve(DDTy->getBaseType()), dwarf::DW_AT_friend);
} else if (DDTy->isStaticMember()) {
getOrCreateStaticMemberDIE(DDTy);
+ } else if (Tag == dwarf::DW_TAG_variant_part) {
+ // When emitting a variant part, wrap each member in
+ // DW_TAG_variant.
+ DIE &Variant = createAndAddDIE(dwarf::DW_TAG_variant, Buffer);
+ if (const ConstantInt *CI =
+ dyn_cast_or_null<ConstantInt>(DDTy->getDiscriminantValue())) {
+ if (isUnsignedDIType(DD, resolve(Discriminator->getBaseType())))
+ addUInt(Variant, dwarf::DW_AT_discr_value, None, CI->getZExtValue());
+ else
+ addSInt(Variant, dwarf::DW_AT_discr_value, None, CI->getSExtValue());
+ }
+ constructMemberDIE(Variant, DDTy);
} else {
constructMemberDIE(Buffer, DDTy);
}
@@ -981,6 +1008,11 @@
if (unsigned PropertyAttributes = Property->getAttributes())
addUInt(ElemDie, dwarf::DW_AT_APPLE_property_attribute, None,
PropertyAttributes);
+ } else if (auto *Composite = dyn_cast<DICompositeType>(Element)) {
+ if (Composite->getTag() == dwarf::DW_TAG_variant_part) {
+ DIE &VariantPart = createAndAddDIE(Composite->getTag(), Buffer);
+ constructTypeDIE(VariantPart, Composite);
+ }
}
}
@@ -1430,7 +1462,7 @@
}
}
-void DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) {
+DIE &DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) {
DIE &MemberDie = createAndAddDIE(DT->getTag(), Buffer);
StringRef Name = DT->getName();
if (!Name.empty())
@@ -1535,6 +1567,8 @@
if (DT->isArtificial())
addFlag(MemberDie, dwarf::DW_AT_artificial);
+
+ return MemberDie;
}
DIE *DwarfUnit::getOrCreateStaticMemberDIE(const DIDerivedType *DT) {
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
index 505edd1..5de3963 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
@@ -341,7 +341,7 @@
void constructSubrangeDIE(DIE &Buffer, const DISubrange *SR, DIE *IndexTy);
void constructArrayTypeDIE(DIE &Buffer, const DICompositeType *CTy);
void constructEnumTypeDIE(DIE &Buffer, const DICompositeType *CTy);
- void constructMemberDIE(DIE &Buffer, const DIDerivedType *DT);
+ DIE &constructMemberDIE(DIE &Buffer, const DIDerivedType *DT);
void constructTemplateTypeParameterDIE(DIE &Buffer,
const DITemplateTypeParameter *TP);
void constructTemplateValueParameterDIE(DIE &Buffer,
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 4ab925b..d641a63 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -1691,6 +1691,7 @@
Printer.printMetadata("vtableHolder", N->getRawVTableHolder());
Printer.printMetadata("templateParams", N->getRawTemplateParams());
Printer.printString("identifier", N->getIdentifier());
+ Printer.printMetadata("discriminator", N->getRawDiscriminator());
Out << ")";
}
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index 5ef3cdd..ed3e281 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -333,6 +333,19 @@
return nullptr;
}
+DIDerivedType *DIBuilder::createVariantMemberType(DIScope *Scope, StringRef Name,
+ DIFile *File, unsigned LineNumber,
+ uint64_t SizeInBits,
+ uint32_t AlignInBits,
+ uint64_t OffsetInBits,
+ Constant *Discriminant,
+ DINode::DIFlags Flags, DIType *Ty) {
+ return DIDerivedType::get(VMContext, dwarf::DW_TAG_member, Name, File,
+ LineNumber, getNonCompileUnitScope(Scope), Ty,
+ SizeInBits, AlignInBits, OffsetInBits, None, Flags,
+ getConstantOrNull(Discriminant));
+}
+
DIDerivedType *DIBuilder::createBitFieldMemberType(
DIScope *Scope, StringRef Name, DIFile *File, unsigned LineNumber,
uint64_t SizeInBits, uint64_t OffsetInBits, uint64_t StorageOffsetInBits,
@@ -458,6 +471,18 @@
return R;
}
+DICompositeType *DIBuilder::createVariantPart(
+ DIScope *Scope, StringRef Name, DIFile *File, unsigned LineNumber,
+ uint64_t SizeInBits, uint32_t AlignInBits, DINode::DIFlags Flags,
+ DIDerivedType *Discriminator, DINodeArray Elements, StringRef UniqueIdentifier) {
+ auto *R = DICompositeType::get(
+ VMContext, dwarf::DW_TAG_variant_part, Name, File, LineNumber,
+ getNonCompileUnitScope(Scope), nullptr, SizeInBits, AlignInBits, 0, Flags,
+ Elements, 0, nullptr, nullptr, UniqueIdentifier, Discriminator);
+ trackIfUnresolved(R);
+ return R;
+}
+
DISubroutineType *DIBuilder::createSubroutineType(DITypeRefArray ParameterTypes,
DINode::DIFlags Flags,
unsigned CC) {
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index b6eda6a..db097e1 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -305,17 +305,18 @@
unsigned Line, Metadata *Scope, Metadata *BaseType, uint64_t SizeInBits,
uint32_t AlignInBits, uint64_t OffsetInBits, DIFlags Flags,
Metadata *Elements, unsigned RuntimeLang, Metadata *VTableHolder,
- Metadata *TemplateParams, MDString *Identifier, StorageType Storage,
- bool ShouldCreate) {
+ Metadata *TemplateParams, MDString *Identifier, Metadata *Discriminator,
+ StorageType Storage, bool ShouldCreate) {
assert(isCanonical(Name) && "Expected canonical MDString");
// Keep this in sync with buildODRType.
DEFINE_GETIMPL_LOOKUP(
DICompositeType, (Tag, Name, File, Line, Scope, BaseType, SizeInBits,
AlignInBits, OffsetInBits, Flags, Elements, RuntimeLang,
- VTableHolder, TemplateParams, Identifier));
+ VTableHolder, TemplateParams, Identifier, Discriminator));
Metadata *Ops[] = {File, Scope, Name, BaseType,
- Elements, VTableHolder, TemplateParams, Identifier};
+ Elements, VTableHolder, TemplateParams, Identifier,
+ Discriminator};
DEFINE_GETIMPL_STORE(DICompositeType, (Tag, Line, RuntimeLang, SizeInBits,
AlignInBits, OffsetInBits, Flags),
Ops);
@@ -326,7 +327,7 @@
Metadata *File, unsigned Line, Metadata *Scope, Metadata *BaseType,
uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits,
DIFlags Flags, Metadata *Elements, unsigned RuntimeLang,
- Metadata *VTableHolder, Metadata *TemplateParams) {
+ Metadata *VTableHolder, Metadata *TemplateParams, Metadata *Discriminator) {
assert(!Identifier.getString().empty() && "Expected valid identifier");
if (!Context.isODRUniquingDebugTypes())
return nullptr;
@@ -335,7 +336,7 @@
return CT = DICompositeType::getDistinct(
Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits,
AlignInBits, OffsetInBits, Flags, Elements, RuntimeLang,
- VTableHolder, TemplateParams, &Identifier);
+ VTableHolder, TemplateParams, &Identifier, Discriminator);
// Only mutate CT if it's a forward declaration and the new operands aren't.
assert(CT->getRawIdentifier() == &Identifier && "Wrong ODR identifier?");
@@ -346,7 +347,8 @@
CT->mutate(Tag, Line, RuntimeLang, SizeInBits, AlignInBits, OffsetInBits,
Flags);
Metadata *Ops[] = {File, Scope, Name, BaseType,
- Elements, VTableHolder, TemplateParams, &Identifier};
+ Elements, VTableHolder, TemplateParams, &Identifier,
+ Discriminator};
assert((std::end(Ops) - std::begin(Ops)) == (int)CT->getNumOperands() &&
"Mismatched number of operands");
for (unsigned I = 0, E = CT->getNumOperands(); I != E; ++I)
@@ -360,7 +362,7 @@
Metadata *File, unsigned Line, Metadata *Scope, Metadata *BaseType,
uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits,
DIFlags Flags, Metadata *Elements, unsigned RuntimeLang,
- Metadata *VTableHolder, Metadata *TemplateParams) {
+ Metadata *VTableHolder, Metadata *TemplateParams, Metadata *Discriminator) {
assert(!Identifier.getString().empty() && "Expected valid identifier");
if (!Context.isODRUniquingDebugTypes())
return nullptr;
@@ -369,7 +371,7 @@
CT = DICompositeType::getDistinct(
Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits,
AlignInBits, OffsetInBits, Flags, Elements, RuntimeLang, VTableHolder,
- TemplateParams, &Identifier);
+ TemplateParams, &Identifier, Discriminator);
return CT;
}
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index e981a80..db7f368 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -499,18 +499,20 @@
Metadata *VTableHolder;
Metadata *TemplateParams;
MDString *Identifier;
+ Metadata *Discriminator;
MDNodeKeyImpl(unsigned Tag, MDString *Name, Metadata *File, unsigned Line,
Metadata *Scope, Metadata *BaseType, uint64_t SizeInBits,
uint32_t AlignInBits, uint64_t OffsetInBits, unsigned Flags,
Metadata *Elements, unsigned RuntimeLang,
Metadata *VTableHolder, Metadata *TemplateParams,
- MDString *Identifier)
+ MDString *Identifier, Metadata *Discriminator)
: Tag(Tag), Name(Name), File(File), Line(Line), Scope(Scope),
BaseType(BaseType), SizeInBits(SizeInBits), OffsetInBits(OffsetInBits),
AlignInBits(AlignInBits), Flags(Flags), Elements(Elements),
RuntimeLang(RuntimeLang), VTableHolder(VTableHolder),
- TemplateParams(TemplateParams), Identifier(Identifier) {}
+ TemplateParams(TemplateParams), Identifier(Identifier),
+ Discriminator(Discriminator) {}
MDNodeKeyImpl(const DICompositeType *N)
: Tag(N->getTag()), Name(N->getRawName()), File(N->getRawFile()),
Line(N->getLine()), Scope(N->getRawScope()),
@@ -519,7 +521,8 @@
Flags(N->getFlags()), Elements(N->getRawElements()),
RuntimeLang(N->getRuntimeLang()), VTableHolder(N->getRawVTableHolder()),
TemplateParams(N->getRawTemplateParams()),
- Identifier(N->getRawIdentifier()) {}
+ Identifier(N->getRawIdentifier()),
+ Discriminator(N->getRawDiscriminator()) {}
bool isKeyOf(const DICompositeType *RHS) const {
return Tag == RHS->getTag() && Name == RHS->getRawName() &&
@@ -532,7 +535,8 @@
RuntimeLang == RHS->getRuntimeLang() &&
VTableHolder == RHS->getRawVTableHolder() &&
TemplateParams == RHS->getRawTemplateParams() &&
- Identifier == RHS->getRawIdentifier();
+ Identifier == RHS->getRawIdentifier() &&
+ Discriminator == RHS->getRawDiscriminator();
}
unsigned getHashValue() const {
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 27781e4..3b07ba3 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -945,7 +945,8 @@
N.getTag() == dwarf::DW_TAG_structure_type ||
N.getTag() == dwarf::DW_TAG_union_type ||
N.getTag() == dwarf::DW_TAG_enumeration_type ||
- N.getTag() == dwarf::DW_TAG_class_type,
+ N.getTag() == dwarf::DW_TAG_class_type ||
+ N.getTag() == dwarf::DW_TAG_variant_part,
"invalid tag", &N);
AssertDI(isScope(N.getRawScope()), "invalid scope", &N, N.getRawScope());
@@ -966,6 +967,11 @@
AssertDI(N.getFile() && !N.getFile()->getFilename().empty(),
"class/union requires a filename", &N, N.getFile());
}
+
+ if (auto *D = N.getRawDiscriminator()) {
+ AssertDI(isa<DIDerivedType>(D) && N.getTag() == dwarf::DW_TAG_variant_part,
+ "discriminator can only appear on variant part");
+ }
}
void Verifier::visitDISubroutineType(const DISubroutineType &N) {