Skip to content

Commit

Permalink
[IR] - Make User construction exception safe
Browse files Browse the repository at this point in the history
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

llvm-svn: 326316
  • Loading branch information
kkretzsch committed Feb 28, 2018
1 parent 7c35de1 commit 60f5736
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 7 deletions.
13 changes: 10 additions & 3 deletions llvm/include/llvm/IR/GlobalVariable.h
Expand Up @@ -68,16 +68,23 @@ class GlobalVariable : public GlobalObject, public ilist_node<GlobalVariable> {

~GlobalVariable() {
dropAllReferences();

// FIXME: needed by operator delete
setGlobalVariableNumOperands(1);
}

// allocate space for exactly one operand
void *operator new(size_t s) {
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);

Expand Down
24 changes: 20 additions & 4 deletions llvm/include/llvm/IR/User.h
Expand Up @@ -99,13 +99,29 @@ class User : public Value {

/// \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:
Expand Down

0 comments on commit 60f5736

Please sign in to comment.