diff --git a/mlir/include/mlir/IR/Operation.h b/mlir/include/mlir/IR/Operation.h --- a/mlir/include/mlir/IR/Operation.h +++ b/mlir/include/mlir/IR/Operation.h @@ -205,7 +205,9 @@ /// '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 @@ void eraseOperand(unsigned idx) { getOperandStorage().eraseOperand(idx); } MutableArrayRef getOpOperands() { - return getOperandStorage().getOperands(); + return LLVM_LIKELY(hasOperandStorage) ? getOperandStorage().getOperands() + : MutableArrayRef(); } OpOperand &getOpOperand(unsigned idx) { return getOpOperands()[idx]; } @@ -593,7 +596,7 @@ private: Operation(Location location, OperationName name, ArrayRef 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 @@ /// Returns the operand storage object. detail::OperandStorage &getOperandStorage() { + assert(hasOperandStorage && "expected operation to have operand storage"); return *getTrailingObjects(); } @@ -633,7 +637,12 @@ 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: diff --git a/mlir/lib/IR/Operation.cpp b/mlir/lib/IR/Operation.cpp --- a/mlir/lib/IR/Operation.cpp +++ b/mlir/lib/IR/Operation.cpp @@ -105,20 +105,29 @@ 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(); + } + // Compute the byte size for the operation and the operand storage. auto byteSize = totalSizeToAlloc( 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,7 +143,8 @@ new (&op->getRegion(i)) Region(op); // Initialize the operands. - new (&op->getOperandStorage()) detail::OperandStorage(op, operands); + if (needsOperandStorage) + new (&op->getOperandStorage()) detail::OperandStorage(op, operands); // Initialize the successors. auto blockOperands = op->getBlockOperands(); @@ -146,9 +156,11 @@ Operation::Operation(Location location, OperationName name, ArrayRef 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,8 +176,9 @@ Operation::~Operation() { assert(block == nullptr && "operation destroyed but still in a block"); - // Explicitly run the destructors for the operands and results. - getOperandStorage().~OperandStorage(); + // Explicitly run the destructors for the operands. + if (hasOperandStorage) + getOperandStorage().~OperandStorage(); // Explicitly run the destructors for the successors. for (auto &successor : getBlockOperands()) @@ -225,7 +238,9 @@ /// 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"); } //===----------------------------------------------------------------------===//