Move OperandList to be allocated prior to User for hung off subclasses.

For hung off uses, we need a Use* to tell use where the operands are.
This was User::OperandList but we want to remove that to save space
of all subclasses which aren't making use of 'hung off uses'.

Hung off uses now allocate their own 'OperandList' Use* in the
User::new which they call.

getOperandList() now uses the hung off uses bit to work out where the
Use* for the OperandList lives.  If a User has hung off uses, then this
bit tells them to go back a single Use* from the User* and use that
value as the OperandList.

If a User has no hung off uses, then we get the first operand by
subtracting (NumOperands * sizeof(Use)) from the User this pointer.

This saves a pointer from User and all subclasses.  Given the average
size of a subclass of User is 112 or 128 bytes, this saves around 7% of space
With malloc tending to align to 16-bytes the real saving is typically more like 3.5%.

On 'opt -O2 verify-uselistorder.lto.bc', peak memory usage prior to this change
is 149MB and after is 143MB so the savings are around 2.5% of peak.

Looking at some passes which allocate many Instructions and Values, parseIR drops
from 54.25MB to 52.21MB while the Inliner calls to Instruction::clone() drops
from 28.20MB to 27.05MB.

Reviewed by Duncan Exon Smith.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@239623 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Pete Cooper
2015-06-12 17:48:18 +00:00
parent 8b351e4040
commit a6ff22119f
2 changed files with 40 additions and 31 deletions

View File

@@ -90,19 +90,20 @@ void *User::operator new(size_t Size, unsigned Us) {
Use *Start = static_cast<Use*>(Storage);
Use *End = Start + Us;
User *Obj = reinterpret_cast<User*>(End);
Obj->setOperandList(Start);
Obj->HasHungOffUses = false;
Obj->NumUserOperands = Us;
Obj->HasHungOffUses = false;
Use::initTags(Start, End);
return Obj;
}
void *User::operator new(size_t Size) {
void *Storage = ::operator new(Size);
User *Obj = reinterpret_cast<User*>(Storage);
Obj->setOperandList(nullptr);
Obj->HasHungOffUses = true;
// Allocate space for a single Use*
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;
*HungOffOperandList = nullptr;
return Obj;
}
@@ -111,11 +112,21 @@ void *User::operator new(size_t Size) {
//===----------------------------------------------------------------------===//
void User::operator delete(void *Usr) {
User *Start = static_cast<User*>(Usr);
Use *Storage = static_cast<Use*>(Usr) - Start->NumUserOperands;
// If there were hung-off uses, they will have been freed already and
// NumOperands reset to 0, so here we just free the User itself.
::operator delete(Storage);
// 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);
if (Obj->HasHungOffUses) {
Use **HungOffOperandList = static_cast<Use **>(Usr) - 1;
// drop the hung off uses.
Use::zap(*HungOffOperandList, *HungOffOperandList + Obj->NumUserOperands,
/* Delete */ true);
::operator delete(HungOffOperandList);
} else {
Use *Storage = static_cast<Use *>(Usr) - Obj->NumUserOperands;
Use::zap(Storage, Storage + Obj->NumUserOperands,
/* Delete */ false);
::operator delete(Storage);
}
}
//===----------------------------------------------------------------------===//