[mlir] Don't allocate an operand storage if the operation is known to never have operands
Certain classes of operations, such as FuncOp, are known to never have operands. This revision adds a bit to operation to detect this case and avoid allocating the unnecessary operand storage. This saves 1 word for each instance of these operations. Differential Revision: https://reviews.llvm.org/D78876
This commit is contained in:
parent
4dfd1b5fcb
commit
1956a8a7cb
|
@ -205,7 +205,9 @@ public:
|
|||
/// 'operands'.
|
||||
void setOperands(ValueRange operands);
|
||||
|
||||
unsigned getNumOperands() { return getOperandStorage().size(); }
|
||||
unsigned getNumOperands() {
|
||||
return LLVM_LIKELY(hasOperandStorage) ? getOperandStorage().size() : 0;
|
||||
}
|
||||
|
||||
Value getOperand(unsigned idx) { return getOpOperand(idx).get(); }
|
||||
void setOperand(unsigned idx, Value value) {
|
||||
|
@ -226,7 +228,8 @@ public:
|
|||
void eraseOperand(unsigned idx) { getOperandStorage().eraseOperand(idx); }
|
||||
|
||||
MutableArrayRef<OpOperand> getOpOperands() {
|
||||
return getOperandStorage().getOperands();
|
||||
return LLVM_LIKELY(hasOperandStorage) ? getOperandStorage().getOperands()
|
||||
: MutableArrayRef<OpOperand>();
|
||||
}
|
||||
|
||||
OpOperand &getOpOperand(unsigned idx) { return getOpOperands()[idx]; }
|
||||
|
@ -593,7 +596,7 @@ private:
|
|||
private:
|
||||
Operation(Location location, OperationName name, ArrayRef<Type> resultTypes,
|
||||
unsigned numSuccessors, unsigned numRegions,
|
||||
const NamedAttributeList &attributes);
|
||||
const NamedAttributeList &attributes, bool hasOperandStorage);
|
||||
|
||||
// Operations are deleted through the destroy() member because they are
|
||||
// allocated with malloc.
|
||||
|
@ -601,6 +604,7 @@ private:
|
|||
|
||||
/// Returns the operand storage object.
|
||||
detail::OperandStorage &getOperandStorage() {
|
||||
assert(hasOperandStorage && "expected operation to have operand storage");
|
||||
return *getTrailingObjects<detail::OperandStorage>();
|
||||
}
|
||||
|
||||
|
@ -633,7 +637,12 @@ private:
|
|||
mutable unsigned orderIndex = 0;
|
||||
|
||||
const unsigned numSuccs;
|
||||
const unsigned numRegions : 31;
|
||||
const unsigned numRegions : 30;
|
||||
|
||||
/// This bit signals whether this operation has an operand storage or not. The
|
||||
/// operand storage may be elided for operations that are known to never have
|
||||
/// operands.
|
||||
bool hasOperandStorage : 1;
|
||||
|
||||
/// This holds the result types of the operation. There are three different
|
||||
/// states recorded here:
|
||||
|
|
|
@ -105,20 +105,29 @@ Operation *Operation::create(Location location, OperationName name,
|
|||
unsigned numSuccessors = successors.size();
|
||||
unsigned numOperands = operands.size();
|
||||
|
||||
// If the operation is known to have no operands, don't allocate an operand
|
||||
// storage.
|
||||
bool needsOperandStorage = true;
|
||||
if (operands.empty()) {
|
||||
if (const AbstractOperation *abstractOp = name.getAbstractOperation())
|
||||
needsOperandStorage = !abstractOp->hasTrait<OpTrait::ZeroOperands>();
|
||||
}
|
||||
|
||||
// Compute the byte size for the operation and the operand storage.
|
||||
auto byteSize =
|
||||
totalSizeToAlloc<detail::InLineOpResult, detail::TrailingOpResult,
|
||||
BlockOperand, Region, detail::OperandStorage>(
|
||||
numInlineResults, numTrailingResults, numSuccessors, numRegions,
|
||||
/*detail::OperandStorage*/ 1);
|
||||
needsOperandStorage ? 1 : 0);
|
||||
byteSize +=
|
||||
llvm::alignTo(detail::OperandStorage::additionalAllocSize(numOperands),
|
||||
alignof(Operation));
|
||||
void *rawMem = malloc(byteSize);
|
||||
|
||||
// Create the new Operation.
|
||||
auto op = ::new (rawMem) Operation(location, name, resultTypes, numSuccessors,
|
||||
numRegions, attributes);
|
||||
Operation *op =
|
||||
::new (rawMem) Operation(location, name, resultTypes, numSuccessors,
|
||||
numRegions, attributes, needsOperandStorage);
|
||||
|
||||
assert((numSuccessors == 0 || !op->isKnownNonTerminator()) &&
|
||||
"unexpected successors in a non-terminator operation");
|
||||
|
@ -134,6 +143,7 @@ Operation *Operation::create(Location location, OperationName name,
|
|||
new (&op->getRegion(i)) Region(op);
|
||||
|
||||
// Initialize the operands.
|
||||
if (needsOperandStorage)
|
||||
new (&op->getOperandStorage()) detail::OperandStorage(op, operands);
|
||||
|
||||
// Initialize the successors.
|
||||
|
@ -146,9 +156,11 @@ Operation *Operation::create(Location location, OperationName name,
|
|||
|
||||
Operation::Operation(Location location, OperationName name,
|
||||
ArrayRef<Type> resultTypes, unsigned numSuccessors,
|
||||
unsigned numRegions, const NamedAttributeList &attributes)
|
||||
unsigned numRegions, const NamedAttributeList &attributes,
|
||||
bool hasOperandStorage)
|
||||
: location(location), numSuccs(numSuccessors), numRegions(numRegions),
|
||||
hasSingleResult(false), name(name), attrs(attributes) {
|
||||
hasOperandStorage(hasOperandStorage), hasSingleResult(false), name(name),
|
||||
attrs(attributes) {
|
||||
if (!resultTypes.empty()) {
|
||||
// If there is a single result it is stored in-place, otherwise use a tuple.
|
||||
hasSingleResult = resultTypes.size() == 1;
|
||||
|
@ -164,7 +176,8 @@ Operation::Operation(Location location, OperationName name,
|
|||
Operation::~Operation() {
|
||||
assert(block == nullptr && "operation destroyed but still in a block");
|
||||
|
||||
// Explicitly run the destructors for the operands and results.
|
||||
// Explicitly run the destructors for the operands.
|
||||
if (hasOperandStorage)
|
||||
getOperandStorage().~OperandStorage();
|
||||
|
||||
// Explicitly run the destructors for the successors.
|
||||
|
@ -225,7 +238,9 @@ void Operation::replaceUsesOfWith(Value from, Value to) {
|
|||
/// Replace the current operands of this operation with the ones provided in
|
||||
/// 'operands'.
|
||||
void Operation::setOperands(ValueRange operands) {
|
||||
getOperandStorage().setOperands(this, operands);
|
||||
if (LLVM_LIKELY(hasOperandStorage))
|
||||
return getOperandStorage().setOperands(this, operands);
|
||||
assert(operands.empty() && "setting operands without an operand storage");
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
Loading…
Reference in a new issue