DebugInfo: Introduce the notion of "form classes"
authorAlexey Samsonov <samsonov@google.com>
Mon, 28 Oct 2013 23:01:48 +0000 (23:01 +0000)
committerAlexey Samsonov <samsonov@google.com>
Mon, 28 Oct 2013 23:01:48 +0000 (23:01 +0000)
Summary:
Use DWARF4 table of form classes to fetch attributes from DIE
in a more consistent way. This shouldn't change the functionality and
serves as a refactoring for upcoming change: DW_AT_high_pc has different
semantics depending on its form class.

Reviewers: dblaikie, echristo

Reviewed By: echristo

CC: echristo, llvm-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1961

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@193553 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/DebugInfo/DWARFFormValue.h
lib/DebugInfo/DWARFContext.cpp
lib/DebugInfo/DWARFDebugInfoEntry.cpp
lib/DebugInfo/DWARFDebugInfoEntry.h
lib/DebugInfo/DWARFFormValue.cpp
lib/DebugInfo/DWARFUnit.cpp
unittests/DebugInfo/DWARFFormValueTest.cpp

index c8d600839a1cef5dd47abb64e565f4691f195575..a9d1ec0b325140d7696342ce907e385bc840d4c0 100644 (file)
@@ -10,6 +10,7 @@
 #ifndef LLVM_DEBUGINFO_DWARFFORMVALUE_H
 #define LLVM_DEBUGINFO_DWARFFORMVALUE_H
 
+#include "llvm/ADT/Optional.h"
 #include "llvm/Support/DataExtractor.h"
 
 namespace llvm {
@@ -18,6 +19,21 @@ class DWARFUnit;
 class raw_ostream;
 
 class DWARFFormValue {
+public:
+  enum FormClass {
+    FC_Unknown,
+    FC_Address,
+    FC_Block,
+    FC_Constant,
+    FC_String,
+    FC_Flag,
+    FC_Reference,
+    FC_Indirect,
+    FC_SectionOffset,
+    FC_Exprloc
+  };
+
+private:
   struct ValueType {
     ValueType() : data(NULL) {
       uval = 0;
@@ -35,9 +51,10 @@ class DWARFFormValue {
   ValueType Value; // Contains all data for the form.
 
 public:
-  DWARFFormValue(uint16_t form = 0) : Form(form) {}
+  DWARFFormValue(uint16_t Form = 0) : Form(Form) {}
   uint16_t getForm() const { return Form; }
-  const ValueType& value() const { return Value; }
+  bool isFormClass(FormClass FC) const;
+
   void dump(raw_ostream &OS, const DWARFUnit *U) const;
   bool extractValue(DataExtractor data, uint32_t *offset_ptr,
                     const DWARFUnit *u);
@@ -45,11 +62,13 @@ public:
     return Value.data != NULL && Value.data == (const uint8_t*)Value.cstr;
   }
 
-  uint64_t getReference(const DWARFUnit *U) const;
-  uint64_t getUnsigned() const { return Value.uval; }
-  int64_t getSigned() const { return Value.sval; }
-  const char *getAsCString(const DWARFUnit *U) const;
-  uint64_t getAsAddress(const DWARFUnit *U) const;
+  /// getAsFoo functions below return the extracted value as Foo if only
+  /// DWARFFormValue has form class is suitable for representing Foo.
+  Optional<uint64_t> getAsReference(const DWARFUnit *U) const;
+  Optional<uint64_t> getAsUnsignedConstant() const;
+  Optional<const char *> getAsCString(const DWARFUnit *U) const;
+  Optional<uint64_t> getAsAddress(const DWARFUnit *U) const;
+  Optional<uint64_t> getAsSectionOffset() const;
 
   bool skipValue(DataExtractor debug_info_data, uint32_t *offset_ptr,
                  const DWARFUnit *u) const;
index 9dc8f149933d4db53be5439f1dac5e884364c97e..6056de7a35c2c151c436a0e9b0d98d0ff0be5522 100644 (file)
@@ -102,8 +102,8 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType) {
       DWARFCompileUnit *cu = getCompileUnitAtIndex(i);
       savedAddressByteSize = cu->getAddressByteSize();
       unsigned stmtOffset =
-        cu->getCompileUnitDIE()->getAttributeValueAsUnsigned(cu, DW_AT_stmt_list,
-                                                             -1U);
+          cu->getCompileUnitDIE()->getAttributeValueAsSectionOffset(
+              cu, DW_AT_stmt_list, -1U);
       if (stmtOffset != -1U) {
         DataExtractor lineData(getLineSection().Data, isLittleEndian(),
                                savedAddressByteSize);
@@ -262,8 +262,8 @@ DWARFContext::getLineTableForCompileUnit(DWARFCompileUnit *cu) {
     Line.reset(new DWARFDebugLine(&getLineSection().Relocs));
 
   unsigned stmtOffset =
-    cu->getCompileUnitDIE()->getAttributeValueAsUnsigned(cu, DW_AT_stmt_list,
-                                                         -1U);
+      cu->getCompileUnitDIE()->getAttributeValueAsSectionOffset(
+          cu, DW_AT_stmt_list, -1U);
   if (stmtOffset == -1U)
     return 0; // No line table for this compile unit.
 
index 9bb49df5d1309c5cdf661a4aa8b9569b7343c436..e8b8748a9d73499b7c6364cd48c7ba00598acd57 100644 (file)
@@ -160,8 +160,11 @@ bool DWARFDebugInfoEntryMinimal::extract(const DWARFUnit *U,
         ((Attr == DW_AT_entry_pc) || (Attr == DW_AT_low_pc))) {
       DWARFFormValue FormValue(Form);
       if (FormValue.extractValue(DebugInfoData, OffsetPtr, U)) {
-        if (Attr == DW_AT_low_pc || Attr == DW_AT_entry_pc)
-          const_cast<DWARFUnit *>(U)->setBaseAddress(FormValue.getUnsigned());
+        if (Attr == DW_AT_low_pc || Attr == DW_AT_entry_pc) {
+          Optional<uint64_t> BaseAddr = FormValue.getAsAddress(U);
+          if (BaseAddr.hasValue())
+            const_cast<DWARFUnit *>(U)->setBaseAddress(BaseAddr.getValue());
+        }
       }
     } else if (!DWARFFormValue::skipValue(Form, DebugInfoData, OffsetPtr, U)) {
       // Restore the original offset.
@@ -210,33 +213,46 @@ bool DWARFDebugInfoEntryMinimal::getAttributeValue(
 const char *DWARFDebugInfoEntryMinimal::getAttributeValueAsString(
     const DWARFUnit *U, const uint16_t Attr, const char *FailValue) const {
   DWARFFormValue FormValue;
-  if (getAttributeValue(U, Attr, FormValue))
-    return FormValue.getAsCString(U);
-  return FailValue;
+  if (!getAttributeValue(U, Attr, FormValue))
+    return FailValue;
+  Optional<const char *> Result = FormValue.getAsCString(U);
+  return Result.hasValue() ? Result.getValue() : FailValue;
 }
 
 uint64_t DWARFDebugInfoEntryMinimal::getAttributeValueAsAddress(
     const DWARFUnit *U, const uint16_t Attr, uint64_t FailValue) const {
   DWARFFormValue FormValue;
-  if (getAttributeValue(U, Attr, FormValue))
-    return FormValue.getAsAddress(U);
-  return FailValue;
+  if (!getAttributeValue(U, Attr, FormValue))
+    return FailValue;
+  Optional<uint64_t> Result = FormValue.getAsAddress(U);
+  return Result.hasValue() ? Result.getValue() : FailValue;
 }
 
-uint64_t DWARFDebugInfoEntryMinimal::getAttributeValueAsUnsigned(
+uint64_t DWARFDebugInfoEntryMinimal::getAttributeValueAsUnsignedConstant(
     const DWARFUnit *U, const uint16_t Attr, uint64_t FailValue) const {
   DWARFFormValue FormValue;
-  if (getAttributeValue(U, Attr, FormValue))
-    return FormValue.getUnsigned();
-  return FailValue;
+  if (!getAttributeValue(U, Attr, FormValue))
+    return FailValue;
+  Optional<uint64_t> Result = FormValue.getAsUnsignedConstant();
+  return Result.hasValue() ? Result.getValue() : FailValue;
 }
 
 uint64_t DWARFDebugInfoEntryMinimal::getAttributeValueAsReference(
     const DWARFUnit *U, const uint16_t Attr, uint64_t FailValue) const {
   DWARFFormValue FormValue;
-  if (getAttributeValue(U, Attr, FormValue))
-      return FormValue.getReference(U);
-  return FailValue;
+  if (!getAttributeValue(U, Attr, FormValue))
+    return FailValue;
+  Optional<uint64_t> Result = FormValue.getAsReference(U);
+  return Result.hasValue() ? Result.getValue() : FailValue;
+}
+
+uint64_t DWARFDebugInfoEntryMinimal::getAttributeValueAsSectionOffset(
+    const DWARFUnit *U, const uint16_t Attr, uint64_t FailValue) const {
+  DWARFFormValue FormValue;
+  if (!getAttributeValue(U, Attr, FormValue))
+    return FailValue;
+  Optional<uint64_t> Result = FormValue.getAsSectionOffset();
+  return Result.hasValue() ? Result.getValue() : FailValue;
 }
 
 bool DWARFDebugInfoEntryMinimal::getLowAndHighPC(const DWARFUnit *U,
@@ -277,7 +293,8 @@ bool DWARFDebugInfoEntryMinimal::addressRangeContainsAddress(
   if (getLowAndHighPC(U, LowPC, HighPC))
     return (LowPC <= Address && Address <= HighPC);
   // Try to get address ranges from .debug_ranges section.
-  uint32_t RangesOffset = getAttributeValueAsReference(U, DW_AT_ranges, -1U);
+  uint32_t RangesOffset =
+      getAttributeValueAsSectionOffset(U, DW_AT_ranges, -1U);
   if (RangesOffset != -1U) {
     DWARFDebugRangeList RangeList;
     if (U->extractRangeList(RangesOffset, RangeList))
@@ -325,9 +342,9 @@ void DWARFDebugInfoEntryMinimal::getCallerFrame(const DWARFUnit *U,
                                                 uint32_t &CallFile,
                                                 uint32_t &CallLine,
                                                 uint32_t &CallColumn) const {
-  CallFile = getAttributeValueAsUnsigned(U, DW_AT_call_file, 0);
-  CallLine = getAttributeValueAsUnsigned(U, DW_AT_call_line, 0);
-  CallColumn = getAttributeValueAsUnsigned(U, DW_AT_call_column, 0);
+  CallFile = getAttributeValueAsUnsignedConstant(U, DW_AT_call_file, 0);
+  CallLine = getAttributeValueAsUnsignedConstant(U, DW_AT_call_line, 0);
+  CallColumn = getAttributeValueAsUnsignedConstant(U, DW_AT_call_column, 0);
 }
 
 DWARFDebugInfoEntryInlinedChain
index e1d88eead770a43284ac24d12c5061de8090ddac..e073d87b2b5e6154526d76e99f18a114cccbeb08 100644 (file)
@@ -129,12 +129,17 @@ public:
   uint64_t getAttributeValueAsAddress(const DWARFUnit *U, const uint16_t Attr,
                                       uint64_t FailValue) const;
 
-  uint64_t getAttributeValueAsUnsigned(const DWARFUnit *U, const uint16_t Attr,
-                                       uint64_t FailValue) const;
+  uint64_t getAttributeValueAsUnsignedConstant(const DWARFUnit *U,
+                                               const uint16_t Attr,
+                                               uint64_t FailValue) const;
 
   uint64_t getAttributeValueAsReference(const DWARFUnit *U, const uint16_t Attr,
                                         uint64_t FailValue) const;
 
+  uint64_t getAttributeValueAsSectionOffset(const DWARFUnit *U,
+                                            const uint16_t Attr,
+                                            uint64_t FailValue) const;
+
   /// Retrieves DW_AT_low_pc and DW_AT_high_pc from CU.
   /// Returns true if both attributes are present.
   bool getLowAndHighPC(const DWARFUnit *U, uint64_t &LowPC,
index 0dddb7eb4a7de743e79cca79c9d7f2685697a260..1a59dd8d0cf15bfc9583ec52203d624c18968abe 100644 (file)
@@ -10,6 +10,8 @@
 #include "llvm/DebugInfo/DWARFFormValue.h"
 #include "DWARFCompileUnit.h"
 #include "DWARFContext.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Dwarf.h"
 #include "llvm/Support/Format.h"
@@ -74,6 +76,57 @@ DWARFFormValue::getFixedFormSizes(uint8_t AddrSize, uint16_t Version) {
   return 0;
 }
 
+static const DWARFFormValue::FormClass DWARF4FormClasses[] = {
+  DWARFFormValue::FC_Unknown,       // 0x0
+  DWARFFormValue::FC_Address,       // 0x01 DW_FORM_addr
+  DWARFFormValue::FC_Unknown,       // 0x02 unused
+  DWARFFormValue::FC_Block,         // 0x03 DW_FORM_block2
+  DWARFFormValue::FC_Block,         // 0x04 DW_FORM_block4
+  DWARFFormValue::FC_Constant,      // 0x05 DW_FORM_data2
+  // --- These can be FC_SectionOffset in DWARF3 and below:
+  DWARFFormValue::FC_Constant,      // 0x06 DW_FORM_data4
+  DWARFFormValue::FC_Constant,      // 0x07 DW_FORM_data8
+  // ---
+  DWARFFormValue::FC_String,        // 0x08 DW_FORM_string
+  DWARFFormValue::FC_Block,         // 0x09 DW_FORM_block
+  DWARFFormValue::FC_Block,         // 0x0a DW_FORM_block1
+  DWARFFormValue::FC_Constant,      // 0x0b DW_FORM_data1
+  DWARFFormValue::FC_Flag,          // 0x0c DW_FORM_flag
+  DWARFFormValue::FC_Constant,      // 0x0d DW_FORM_sdata
+  DWARFFormValue::FC_String,        // 0x0e DW_FORM_strp
+  DWARFFormValue::FC_Constant,      // 0x0f DW_FORM_udata
+  DWARFFormValue::FC_Reference,     // 0x10 DW_FORM_ref_addr
+  DWARFFormValue::FC_Reference,     // 0x11 DW_FORM_ref1
+  DWARFFormValue::FC_Reference,     // 0x12 DW_FORM_ref2
+  DWARFFormValue::FC_Reference,     // 0x13 DW_FORM_ref4
+  DWARFFormValue::FC_Reference,     // 0x14 DW_FORM_ref8
+  DWARFFormValue::FC_Reference,     // 0x15 DW_FORM_ref_udata
+  DWARFFormValue::FC_Indirect,      // 0x16 DW_FORM_indirect
+  DWARFFormValue::FC_SectionOffset, // 0x17 DW_FORM_sec_offset
+  DWARFFormValue::FC_Exprloc,       // 0x18 DW_FORM_exprloc
+  DWARFFormValue::FC_Flag,          // 0x19 DW_FORM_flag_present
+  DWARFFormValue::FC_Reference,     // 0x20 DW_FORM_ref_sig8
+};
+
+bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const {
+  // First, check DWARF4 form classes.
+  if (Form < ArrayRef<FormClass>(DWARF4FormClasses).size() &&
+      DWARF4FormClasses[Form] == FC)
+    return true;
+  // Check for some DWARF5 forms.
+  if (Form == DW_FORM_GNU_addr_index)
+    return (FC == FC_Address);
+  if (Form == DW_FORM_GNU_str_index)
+    return (FC == FC_String);
+  // In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section offset.
+  // Don't check for DWARF version here, as some producers may still do this
+  // by mistake.
+  if ((Form == DW_FORM_data4 || Form == DW_FORM_data8) &&
+      FC == FC_SectionOffset)
+    return true;
+  return false;
+}
+
 bool DWARFFormValue::extractValue(DataExtractor data, uint32_t *offset_ptr,
                                   const DWARFUnit *cu) {
   bool indirect = false;
@@ -155,10 +208,6 @@ bool DWARFFormValue::extractValue(DataExtractor data, uint32_t *offset_ptr,
       break;
     case DW_FORM_string:
       Value.cstr = data.getCStr(offset_ptr);
-      // Set the string value to also be the data for inlined cstr form
-      // values only so we can tell the differnence between DW_FORM_string
-      // and DW_FORM_strp form values
-      Value.data = (const uint8_t*)Value.cstr;
       break;
     case DW_FORM_indirect:
       Form = data.getULEB128(offset_ptr);
@@ -313,7 +362,7 @@ void
 DWARFFormValue::dump(raw_ostream &OS, const DWARFUnit *cu) const {
   DataExtractor debug_str_data(cu->getStringSection(), true, 0);
   DataExtractor debug_str_offset_data(cu->getStringOffsetSection(), true, 0);
-  uint64_t uvalue = getUnsigned();
+  uint64_t uvalue = Value.uval;
   bool cu_relative_offset = false;
 
   switch (Form) {
@@ -336,7 +385,7 @@ DWARFFormValue::dump(raw_ostream &OS, const DWARFUnit *cu) const {
   case DW_FORM_data8:     OS << format("0x%016" PRIx64, uvalue); break;
   case DW_FORM_string:
     OS << '"';
-    OS.write_escaped(getAsCString(NULL));
+    OS.write_escaped(Value.cstr);
     OS << '"';
     break;
   case DW_FORM_exprloc:
@@ -368,24 +417,24 @@ DWARFFormValue::dump(raw_ostream &OS, const DWARFUnit *cu) const {
     }
     break;
 
-  case DW_FORM_sdata:     OS << getSigned();   break;
-  case DW_FORM_udata:     OS << getUnsigned(); break;
+  case DW_FORM_sdata:     OS << Value.sval; break;
+  case DW_FORM_udata:     OS << Value.uval; break;
   case DW_FORM_strp: {
     OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)uvalue);
-    const char* dbg_str = getAsCString(cu);
-    if (dbg_str) {
+    Optional<const char *> DbgStr = getAsCString(cu);
+    if (DbgStr.hasValue()) {
       OS << '"';
-      OS.write_escaped(dbg_str);
+      OS.write_escaped(DbgStr.getValue());
       OS << '"';
     }
     break;
   }
   case DW_FORM_GNU_str_index: {
     OS << format(" indexed (%8.8x) string = ", (uint32_t)uvalue);
-    const char *dbg_str = getAsCString(cu);
-    if (dbg_str) {
+    Optional<const char *> DbgStr = getAsCString(cu);
+    if (DbgStr.hasValue()) {
       OS << '"';
-      OS.write_escaped(dbg_str);
+      OS.write_escaped(DbgStr.getValue());
       OS << '"';
     }
     break;
@@ -434,47 +483,67 @@ DWARFFormValue::dump(raw_ostream &OS, const DWARFUnit *cu) const {
     OS << format(" => {0x%8.8" PRIx64 "}", uvalue + (cu ? cu->getOffset() : 0));
 }
 
-const char *DWARFFormValue::getAsCString(const DWARFUnit *CU) const {
-  if (isInlinedCStr())
+Optional<const char *> DWARFFormValue::getAsCString(const DWARFUnit *U) const {
+  if (!isFormClass(FC_String))
+    return None;
+  if (Form == DW_FORM_string)
     return Value.cstr;
-  if (!CU)
-    return NULL;
+  if (U == 0)
+    return None;
   uint32_t Offset = Value.uval;
   if (Form == DW_FORM_GNU_str_index) {
     uint32_t StrOffset;
-    if (!CU->getStringOffsetSectionItem(Offset, StrOffset))
-      return NULL;
+    if (!U->getStringOffsetSectionItem(Offset, StrOffset))
+      return None;
     Offset = StrOffset;
   }
-  return CU->getStringExtractor().getCStr(&Offset);
+  if (const char *Str = U->getStringExtractor().getCStr(&Offset)) {
+    return Str;
+  }
+  return None;
 }
 
-uint64_t DWARFFormValue::getAsAddress(const DWARFUnit *CU) const {
-  if (!CU)
-    return 0;
+Optional<uint64_t> DWARFFormValue::getAsAddress(const DWARFUnit *U) const {
+  if (!isFormClass(FC_Address))
+    return None;
   if (Form == DW_FORM_GNU_addr_index) {
     uint32_t Index = Value.uval;
-    uint64_t Address;
-    if (!CU->getAddrOffsetSectionItem(Index, Address))
-      return 0;
-    return Address;
+    uint64_t Result;
+    if (U == 0 || !U->getAddrOffsetSectionItem(Index, Result))
+      return None;
+    return Result;
   }
   return Value.uval;
 }
 
-uint64_t DWARFFormValue::getReference(const DWARFUnit *cu) const {
-  uint64_t die_offset = Value.uval;
+Optional<uint64_t> DWARFFormValue::getAsReference(const DWARFUnit *U) const {
+  if (!isFormClass(FC_Reference))
+    return None;
   switch (Form) {
   case DW_FORM_ref1:
   case DW_FORM_ref2:
   case DW_FORM_ref4:
   case DW_FORM_ref8:
   case DW_FORM_ref_udata:
-      die_offset += (cu ? cu->getOffset() : 0);
-      break;
+    if (U == 0)
+      return None;
+    return Value.uval + U->getOffset();
+  case DW_FORM_ref_addr:
+    return Value.uval;
+  // FIXME: Add proper support for DW_FORM_ref_sig8
   default:
-      break;
+    return Value.uval;
   }
+}
 
-  return die_offset;
+Optional<uint64_t> DWARFFormValue::getAsSectionOffset() const {
+  if (!isFormClass(FC_SectionOffset))
+    return None;
+  return Value.uval;
+}
+
+Optional<uint64_t> DWARFFormValue::getAsUnsignedConstant() const {
+  if (!isFormClass(FC_Constant) || Form == DW_FORM_sdata)
+    return None;
+  return Value.uval;
 }
index 770c65b433fb352d0a497419f89bd9d6efdd993f..25062cd922afd65f9e9e8fd4f0f97efb105e0d49 100644 (file)
@@ -144,7 +144,7 @@ uint64_t DWARFUnit::getDWOId() {
   if (DieArray.empty())
     return FailValue;
   return DieArray[0]
-      .getAttributeValueAsUnsigned(this, DW_AT_GNU_dwo_id, FailValue);
+      .getAttributeValueAsUnsignedConstant(this, DW_AT_GNU_dwo_id, FailValue);
 }
 
 void DWARFUnit::setDIERelations() {
@@ -251,14 +251,14 @@ size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
   // If CU DIE was just parsed, copy several attribute values from it.
   if (!HasCUDie) {
     uint64_t BaseAddr =
-      DieArray[0].getAttributeValueAsUnsigned(this, DW_AT_low_pc, -1U);
-    if (BaseAddr == -1U)
-      BaseAddr = DieArray[0].getAttributeValueAsUnsigned(this, DW_AT_entry_pc, 0);
+        DieArray[0].getAttributeValueAsAddress(this, DW_AT_low_pc, -1ULL);
+    if (BaseAddr == -1ULL)
+      BaseAddr = DieArray[0].getAttributeValueAsAddress(this, DW_AT_entry_pc, 0);
     setBaseAddress(BaseAddr);
-    AddrOffsetSectionBase =
-        DieArray[0].getAttributeValueAsReference(this, DW_AT_GNU_addr_base, 0);
-    RangeSectionBase =
-        DieArray[0].getAttributeValueAsReference(this, DW_AT_GNU_ranges_base, 0);
+    AddrOffsetSectionBase = DieArray[0].getAttributeValueAsSectionOffset(
+        this, DW_AT_GNU_addr_base, 0);
+    RangeSectionBase = DieArray[0].getAttributeValueAsSectionOffset(
+        this, DW_AT_GNU_ranges_base, 0);
   }
 
   setDIERelations();
index 04b859bdb9db81787b4923bee3279e937c898b80..e7216b3726f3d5cbf2bcce740fe1801659f38df5 100644 (file)
@@ -28,4 +28,21 @@ TEST(DWARFFormValue, FixedFormSizes) {
   EXPECT_EQ(0, DWARFFormValue::getFixedFormSizes(16, 2));
 }
 
+bool isFormClass(uint16_t Form, DWARFFormValue::FormClass FC) {
+  return DWARFFormValue(Form).isFormClass(FC);
+}
+
+TEST(DWARFFormValue, FormClass) {
+  EXPECT_TRUE(isFormClass(DW_FORM_addr, DWARFFormValue::FC_Address));
+  EXPECT_FALSE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_Address));
+  EXPECT_TRUE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_Constant));
+  EXPECT_TRUE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_SectionOffset));
+  EXPECT_TRUE(
+      isFormClass(DW_FORM_sec_offset, DWARFFormValue::FC_SectionOffset));
+  EXPECT_TRUE(isFormClass(DW_FORM_GNU_str_index, DWARFFormValue::FC_String));
+  EXPECT_TRUE(isFormClass(DW_FORM_GNU_addr_index, DWARFFormValue::FC_Address));
+  EXPECT_FALSE(isFormClass(DW_FORM_ref_addr, DWARFFormValue::FC_Address));
+  EXPECT_TRUE(isFormClass(DW_FORM_ref_addr, DWARFFormValue::FC_Reference));
+}
+
 } // end anonymous namespace