[llvm] [IR] Use alloc markers for operator delete variants (PR #138261)

Marc Auberer via llvm-commits llvm-commits at lists.llvm.org
Fri May 2 05:32:57 PDT 2025


https://github.com/marcauberer created https://github.com/llvm/llvm-project/pull/138261

Follow-up to https://github.com/llvm/llvm-project/pull/129914
This fixes the problem properly by using the data via the allocation markers.

PS: Sorry for the long delay ;)

>From 9998ee33be06bd167f4554da2e91f5492a2497d3 Mon Sep 17 00:00:00 2001
From: Marc Auberer <marc.auberer at chillibits.com>
Date: Sat, 26 Apr 2025 01:10:41 +0200
Subject: [PATCH] [IR] Use alloc markers for operator delete variants

---
 llvm/include/llvm/IR/User.h | 42 +++---------------
 llvm/lib/IR/User.cpp        | 86 +++++++++++++++++++++++++++----------
 2 files changed, 71 insertions(+), 57 deletions(-)

diff --git a/llvm/include/llvm/IR/User.h b/llvm/include/llvm/IR/User.h
index 39e1314bd8130..634b14341d243 100644
--- a/llvm/include/llvm/IR/User.h
+++ b/llvm/include/llvm/IR/User.h
@@ -54,7 +54,10 @@ class User : public Value {
   void *operator new(size_t Size) = delete;
 
   /// Indicates this User has operands "hung off" in another allocation.
-  struct HungOffOperandsAllocMarker {};
+  struct HungOffOperandsAllocMarker {
+    /// The number of operands for this User.
+    const unsigned NumOps;
+  };
 
   /// Indicates this User has operands co-allocated.
   struct IntrusiveOperandsAllocMarker {
@@ -145,42 +148,11 @@ class User : public Value {
   /// Free memory allocated for User and Use objects.
   void operator delete(void *Usr);
   /// Placement delete - required by std, called if the ctor throws.
-  void operator delete(void *Usr, HungOffOperandsAllocMarker) {
-    // Note: If a subclass manipulates the information which is required to
-    // calculate the Usr memory pointer, e.g. NumUserOperands, the operator
-    // delete of that subclass has to restore the changed information to the
-    // original value, since the dtor of that class is not called if the ctor
-    // fails.
-    User::operator delete(Usr);
-
-#ifndef LLVM_ENABLE_EXCEPTIONS
-    llvm_unreachable("Constructor throws?");
-#endif
-  }
+  void operator delete(void *Usr, HungOffOperandsAllocMarker);
   /// Placement delete - required by std, called if the ctor throws.
-  void operator delete(void *Usr, IntrusiveOperandsAllocMarker) {
-    // Note: If a subclass manipulates the information which is required to calculate the
-    // Usr memory pointer, e.g. NumUserOperands, the operator delete of that subclass has
-    // to restore the changed information to the original value, since the dtor of that class
-    // is not called if the ctor fails.
-    User::operator delete(Usr);
-
-#ifndef LLVM_ENABLE_EXCEPTIONS
-    llvm_unreachable("Constructor throws?");
-#endif
-  }
+  void operator delete(void *Usr, IntrusiveOperandsAllocMarker Marker);
   /// Placement delete - required by std, called if the ctor throws.
-  void operator delete(void *Usr, IntrusiveOperandsAndDescriptorAllocMarker) {
-    // Note: If a subclass manipulates the information which is required to calculate the
-    // Usr memory pointer, e.g. NumUserOperands, the operator delete of that subclass has
-    // to restore the changed information to the original value, since the dtor of that class
-    // is not called if the ctor fails.
-    User::operator delete(Usr);
-
-#ifndef LLVM_ENABLE_EXCEPTIONS
-    llvm_unreachable("Constructor throws?");
-#endif
-  }
+  void operator delete(void *Usr, IntrusiveOperandsAndDescriptorAllocMarker);
 
 protected:
   template <int Idx, typename U> static Use &OpFrom(const U *that) {
diff --git a/llvm/lib/IR/User.cpp b/llvm/lib/IR/User.cpp
index ab44cb4b8a3f7..fea8aa6a02338 100644
--- a/llvm/lib/IR/User.cpp
+++ b/llvm/lib/IR/User.cpp
@@ -20,13 +20,14 @@ class BasicBlock;
 
 bool User::replaceUsesOfWith(Value *From, Value *To) {
   bool Changed = false;
-  if (From == To) return Changed;   // Duh what?
+  if (From == To)
+    return Changed; // Duh what?
 
   assert((!isa<Constant>(this) || isa<GlobalValue>(this)) &&
          "Cannot call User::replaceUsesOfWith on a constant!");
 
   for (unsigned i = 0, E = getNumOperands(); i != E; ++i)
-    if (getOperand(i) == From) {  // Is This operand is pointing to oldval?
+    if (getOperand(i) == From) { // Is This operand is pointing to oldval?
       // The side effects of this setOperand call include linking to
       // "To", adding "this" to the uses list of To, and
       // most importantly, removing "this" from the use list of "From".
@@ -57,7 +58,7 @@ void User::allocHungoffUses(unsigned N, bool IsPhi) {
   size_t size = N * sizeof(Use);
   if (IsPhi)
     size += N * sizeof(BasicBlock *);
-  Use *Begin = static_cast<Use*>(::operator new(size));
+  Use *Begin = static_cast<Use *>(::operator new(size));
   Use *End = Begin + N;
   setOperandList(Begin);
   for (; Begin != End; Begin++)
@@ -89,7 +90,6 @@ void User::growHungoffUses(unsigned NewNumUses, bool IsPhi) {
   Use::zap(OldOps, OldOps + OldNumUses, true);
 }
 
-
 // This is a private struct used by `User` to track the co-allocated descriptor
 // section.
 struct DescriptorInfo {
@@ -191,28 +191,70 @@ void *User::operator new(size_t Size, HungOffOperandsAllocMarker) {
 LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE void User::operator delete(void *Usr) {
   // Hung off uses use a single Use* before the User, while other subclasses
   // use a Use[] allocated prior to the user.
-  User *Obj = static_cast<User *>(Usr);
+  const auto *Obj = static_cast<User *>(Usr);
   if (Obj->HasHungOffUses) {
-    assert(!Obj->HasDescriptor && "not supported!");
-
-    Use **HungOffOperandList = static_cast<Use **>(Usr) - 1;
-    // drop the hung off uses.
-    Use::zap(*HungOffOperandList, *HungOffOperandList + Obj->NumUserOperands,
-             /* Delete */ true);
-    ::operator delete(HungOffOperandList);
+    const HungOffOperandsAllocMarker Marker{
+        Obj->NumUserOperands,
+    };
+    operator delete(Usr, Marker);
   } else if (Obj->HasDescriptor) {
-    Use *UseBegin = static_cast<Use *>(Usr) - Obj->NumUserOperands;
-    Use::zap(UseBegin, UseBegin + Obj->NumUserOperands, /* Delete */ false);
-
-    auto *DI = reinterpret_cast<DescriptorInfo *>(UseBegin) - 1;
-    uint8_t *Storage = reinterpret_cast<uint8_t *>(DI) - DI->SizeInBytes;
-    ::operator delete(Storage);
+    const IntrusiveOperandsAndDescriptorAllocMarker Marker{
+        Obj->NumUserOperands,
+        Obj->HasDescriptor,
+    };
+    operator delete(Usr, Marker);
   } else {
-    Use *Storage = static_cast<Use *>(Usr) - Obj->NumUserOperands;
-    Use::zap(Storage, Storage + Obj->NumUserOperands,
-             /* Delete */ false);
-    ::operator delete(Storage);
+    const IntrusiveOperandsAllocMarker Marker{
+        Obj->NumUserOperands,
+    };
+    operator delete(Usr, Marker);
   }
 }
 
+// Repress memory sanitization, due to use-after-destroy by operator
+// delete. Bug report 24578 identifies this issue.
+LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE void
+User::operator delete(void *Usr, const HungOffOperandsAllocMarker Marker) {
+  // Note: If a subclass manipulates the information which is required to
+  // calculate the Usr memory pointer, e.g. NumUserOperands, the operator
+  // delete of that subclass has to restore the changed information to the
+  // original value, since the dtor of that class is not called if the ctor
+  // fails.
+  Use **HungOffOperandList = static_cast<Use **>(Usr) - 1;
+  // drop the hung off uses.
+  Use::zap(*HungOffOperandList, *HungOffOperandList + Marker.NumOps,
+           /* Delete */ true);
+  ::operator delete(HungOffOperandList);
+}
+
+// Repress memory sanitization, due to use-after-destroy by operator
+// delete. Bug report 24578 identifies this issue.
+LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE void
+User::operator delete(void *Usr, const IntrusiveOperandsAllocMarker Marker) {
+  // Note: If a subclass manipulates the information which is required to
+  // calculate the Usr memory pointer, e.g. NumUserOperands, the operator delete
+  // of that subclass has to restore the changed information to the original
+  // value, since the dtor of that class is not called if the ctor fails.
+  Use *Storage = static_cast<Use *>(Usr) - Marker.NumOps;
+  Use::zap(Storage, Storage + Marker.NumOps, /* Delete */ false);
+  ::operator delete(Storage);
+}
+
+// Repress memory sanitization, due to use-after-destroy by operator
+// delete. Bug report 24578 identifies this issue.
+LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE void
+User::operator delete(void *Usr,
+                      const IntrusiveOperandsAndDescriptorAllocMarker Marker) {
+  // Note: If a subclass manipulates the information which is required to
+  // calculate the Usr memory pointer, e.g. NumUserOperands, the operator delete
+  // of that subclass has to restore the changed information to the original
+  // value, since the dtor of that class is not called if the ctor fails.
+  Use *UseBegin = static_cast<Use *>(Usr) - Marker.NumOps;
+  Use::zap(UseBegin, UseBegin + Marker.NumOps, /* Delete */ false);
+
+  auto *DI = reinterpret_cast<DescriptorInfo *>(UseBegin) - 1;
+  uint8_t *Storage = reinterpret_cast<uint8_t *>(DI) - DI->SizeInBytes;
+  ::operator delete(Storage);
+}
+
 } // namespace llvm



More information about the llvm-commits mailing list