Skip to content

[IR] Use alloc markers for operator delete variants #138261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 11 additions & 39 deletions llvm/include/llvm/IR/User.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,24 @@ 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.
unsigned NumOps;
};

/// Indicates this User has operands co-allocated.
struct IntrusiveOperandsAllocMarker {
/// The number of operands for this User.
const unsigned NumOps;
unsigned NumOps;
};

/// Indicates this User has operands and a descriptor co-allocated .
/// Indicates this User has operands and a descriptor co-allocated.
struct IntrusiveOperandsAndDescriptorAllocMarker {
/// The number of operands for this User.
const unsigned NumOps;
unsigned NumOps;
/// The number of bytes to allocate for the descriptor. Must be divisible by
/// `sizeof(void *)`.
const unsigned DescBytes;
unsigned DescBytes;
};

/// Information about how a User object was allocated, to be passed into the
Expand Down Expand Up @@ -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) {
Expand Down
70 changes: 45 additions & 25 deletions llvm/lib/IR/User.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ bool User::replaceUsesOfWith(Value *From, Value *To) {
"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".
Expand Down Expand Up @@ -146,9 +146,6 @@ void *User::allocateFixedOperandUser(size_t Size, unsigned Us,
Use *Start = reinterpret_cast<Use *>(Storage + DescBytesToAllocate);
Use *End = Start + Us;
User *Obj = reinterpret_cast<User *>(End);
Obj->NumUserOperands = Us;
Obj->HasHungOffUses = false;
Obj->HasDescriptor = DescBytes != 0;
for (; Start != End; Start++)
new (Start) Use(Obj);

Expand All @@ -175,9 +172,6 @@ void *User::operator new(size_t Size, HungOffOperandsAllocMarker) {
void *Storage = ::operator new(Size + sizeof(Use *));
Use **HungOffOperandList = static_cast<Use **>(Storage);
User *Obj = reinterpret_cast<User *>(HungOffOperandList + 1);
Obj->NumUserOperands = 0;
Obj->HasHungOffUses = true;
Obj->HasDescriptor = false;
*HungOffOperandList = nullptr;
return Obj;
}
Expand All @@ -191,28 +185,54 @@ 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.
void User::operator delete(void *Usr, HungOffOperandsAllocMarker Marker) {
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.
void User::operator delete(void *Usr, IntrusiveOperandsAllocMarker Marker) {
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.
void User::operator delete(void *Usr,
IntrusiveOperandsAndDescriptorAllocMarker Marker) {
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