[llvm] 53f51da - [ADT] Allow K to be incomplete during DenseMap<K*, V> instantiation

Reid Kleckner via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 28 14:24:11 PST 2020


Author: Reid Kleckner
Date: 2020-02-28T14:24:04-08:00
New Revision: 53f51da09e4530a193b7c17aece6daace6f6b8f5

URL: https://github.com/llvm/llvm-project/commit/53f51da09e4530a193b7c17aece6daace6f6b8f5
DIFF: https://github.com/llvm/llvm-project/commit/53f51da09e4530a193b7c17aece6daace6f6b8f5.diff

LOG: [ADT] Allow K to be incomplete during DenseMap<K*, V> instantiation

DenseMap requires two sentinel values for keys: empty and tombstone
values. To avoid undefined behavior, LLVM aligns the two sentinel
pointers to alignof(T). This requires T to be complete, which is
needlessly restrictive.

Instead, assume that DenseMap pointer keys have a maximum alignment of
4096, and use the same sentinel values for all pointer keys. The new
sentinels are:
  empty:     static_cast<uintptr_t>(-1) << 12
  tombstone: static_cast<uintptr_t>(-2) << 12

These correspond to the addresses of -4096 and -8192. Hopefully, such a
key is never inserted into a DenseMap.

I encountered this while looking at making clang's SourceManager not
require FileManager.h, but it has several maps keyed on classes defined
in FileManager.h. FileManager depends on various LLVM FS headers, which
cumulatively take ~200ms to parse, and are generally not needed.

Reviewed By: hans

Differential Revision: https://reviews.llvm.org/D75301

Added: 
    

Modified: 
    llvm/include/llvm/ADT/DenseMapInfo.h
    llvm/unittests/ADT/DenseMapTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/DenseMapInfo.h b/llvm/include/llvm/ADT/DenseMapInfo.h
index 48ddbf686f29..4005888c1c80 100644
--- a/llvm/include/llvm/ADT/DenseMapInfo.h
+++ b/llvm/include/llvm/ADT/DenseMapInfo.h
@@ -16,7 +16,6 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/PointerLikeTypeTraits.h"
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
@@ -32,18 +31,28 @@ struct DenseMapInfo {
   //static bool isEqual(const T &LHS, const T &RHS);
 };
 
-// Provide DenseMapInfo for all pointers.
+// Provide DenseMapInfo for all pointers. Come up with sentinel pointer values
+// that are aligned to alignof(T) bytes, but try to avoid requiring T to be
+// complete. This allows clients to instantiate DenseMap<T*, ...> with forward
+// declared key types. Assume that no pointer key type requires more than 4096
+// bytes of alignment.
 template<typename T>
 struct DenseMapInfo<T*> {
+  // The following should hold, but it would require T to be complete:
+  // static_assert(alignof(T) <= (1 << Log2MaxAlign),
+  //               "DenseMap does not support pointer keys requiring more than "
+  //               "Log2MaxAlign bits of alignment");
+  static constexpr uintptr_t Log2MaxAlign = 12;
+
   static inline T* getEmptyKey() {
     uintptr_t Val = static_cast<uintptr_t>(-1);
-    Val <<= PointerLikeTypeTraits<T*>::NumLowBitsAvailable;
+    Val <<= Log2MaxAlign;
     return reinterpret_cast<T*>(Val);
   }
 
   static inline T* getTombstoneKey() {
     uintptr_t Val = static_cast<uintptr_t>(-2);
-    Val <<= PointerLikeTypeTraits<T*>::NumLowBitsAvailable;
+    Val <<= Log2MaxAlign;
     return reinterpret_cast<T*>(Val);
   }
 

diff  --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index 1ff12a6366cb..9cd974f5bac3 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -622,4 +622,28 @@ TEST(DenseMapCustomTest, ConstTest) {
   EXPECT_NE(Map.find(B), Map.end());
   EXPECT_NE(Map.find(C), Map.end());
 }
+
+struct IncompleteStruct;
+
+TEST(DenseMapCustomTest, OpaquePointerKey) {
+  // Test that we can use a pointer to an incomplete type as a DenseMap key.
+  // This is an important build time optimization, since many classes have
+  // DenseMap members.
+  DenseMap<IncompleteStruct *, int> Map;
+  int Keys[3] = {0, 0, 0};
+  IncompleteStruct *K1 = reinterpret_cast<IncompleteStruct *>(&Keys[0]);
+  IncompleteStruct *K2 = reinterpret_cast<IncompleteStruct *>(&Keys[1]);
+  IncompleteStruct *K3 = reinterpret_cast<IncompleteStruct *>(&Keys[2]);
+  Map.insert({K1, 1});
+  Map.insert({K2, 2});
+  Map.insert({K3, 3});
+  EXPECT_EQ(Map.count(K1), 1u);
+  EXPECT_EQ(Map[K1], 1);
+  EXPECT_EQ(Map[K2], 2);
+  EXPECT_EQ(Map[K3], 3);
+  Map.clear();
+  EXPECT_EQ(Map.find(K1), Map.end());
+  EXPECT_EQ(Map.find(K2), Map.end());
+  EXPECT_EQ(Map.find(K3), Map.end());
+}
 }


        


More information about the llvm-commits mailing list