[llvm] r326316 - [IR] - Make User construction exception safe

Klaus Kretzschmar via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 28 03:32:23 PST 2018


Author: kkretzschmar
Date: Wed Feb 28 03:32:23 2018
New Revision: 326316

URL: http://llvm.org/viewvc/llvm-project?rev=326316&view=rev
Log:
[IR] - Make User construction exception safe

There are many instruction ctors that call the setName method of the Value base class, which can throw a bad_alloc exception in OOM situations. 
In such situations special User delete operators are called which are not implemented yet.

Example:
 Lets look at the construction of a CallInst instruction during IR generation:

static CallInst *Create(FunctionType *Ty, Value *Func, ArrayRef<Value *> Args, .. ){
...

return new (TotalOps, DescriptorBytes) CallInst(Ty, Func, Args, Bundles, NameStr, InsertBefore);

}

CallInst::CalInst(Value* Func, ...) {
...
Op<-1>() = Func;
....
setName(name); // throws
...
}
Op<-1>() returns a reference to a Use object of the CallInst instruction and the operator= inserts this use object into the UseList of Func. 
The same object is removed from that UseList by calling the User::operator delete If the CallInst object is deleted. 
Since setName can throw a bad_alloc exception (if LLVM_ENABLE_EXCEPTIONS is switched on), the unwind chain runs into assertions ("Constructor throws?") in 
special User::operator deletes operators:

operator delete(void* Usr, unsigned)
operator delete(void* Usr, unsigned, bool)
This situation can be fixed by simlpy calling the User::operator delete(void*) in these unimplemented methods.

To ensure that this additional call succeeds all information that is necessary to calculate the storage pointer from the Usr address 
must be restored in the special case that a sublass has changed this information, e.g. GlobalVariable can change the NumberOfOperands.

Reviewd by: rnk

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




Modified:
    llvm/trunk/include/llvm/IR/GlobalVariable.h
    llvm/trunk/include/llvm/IR/User.h

Modified: llvm/trunk/include/llvm/IR/GlobalVariable.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/GlobalVariable.h?rev=326316&r1=326315&r2=326316&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/GlobalVariable.h (original)
+++ llvm/trunk/include/llvm/IR/GlobalVariable.h Wed Feb 28 03:32:23 2018
@@ -68,9 +68,6 @@ public:
 
   ~GlobalVariable() {
     dropAllReferences();
-
-    // FIXME: needed by operator delete
-    setGlobalVariableNumOperands(1);
   }
 
   // allocate space for exactly one operand
@@ -78,6 +75,16 @@ public:
     return User::operator new(s, 1);
   }
 
+  // delete space for exactly one operand as created in the corresponding new operator
+  void operator delete(void *ptr){
+    assert(ptr != nullptr && "must not be nullptr");
+    User *Obj = static_cast<User *>(ptr);
+    // Number of operands can be set to 0 after construction and initialization. Make sure
+    // that number of operands is reset to 1, as this is needed in User::operator delete
+    Obj->setGlobalVariableNumOperands(1);
+    User::operator delete(Obj);
+  }
+
   /// Provide fast operand accessors
   DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
 

Modified: llvm/trunk/include/llvm/IR/User.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/User.h?rev=326316&r1=326315&r2=326316&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/User.h (original)
+++ llvm/trunk/include/llvm/IR/User.h Wed Feb 28 03:32:23 2018
@@ -99,13 +99,29 @@ public:
 
   /// \brief Free memory allocated for User and Use objects.
   void operator delete(void *Usr);
-  /// \brief Placement delete - required by std, but never called.
-  void operator delete(void*, unsigned) {
+  /// \brief Placement delete - required by std, called if the ctor throws.
+  void operator delete(void *Usr, unsigned) {
+    // 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
   }
-  /// \brief Placement delete - required by std, but never called.
-  void operator delete(void*, unsigned, bool) {
+  /// \brief Placement delete - required by std, called if the ctor throws.
+  void operator delete(void *Usr, unsigned, bool) {
+    // 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
   }
 
 protected:




More information about the llvm-commits mailing list