Skip to content

Commit 08f0764

Browse files
committed
Remove use of tuple for multiresult type storage
Move the results in line with the op instead. This results in each operation having its own types recorded vs single tuple type, but comes at benefit that every mutation doesn't incurs uniquing. Ran into cases where updating result type of operation led to very large memory usage. Differential Revision: https://reviews.llvm.org/D97652
1 parent 415c0cd commit 08f0764

File tree

3 files changed

+45
-39
lines changed

3 files changed

+45
-39
lines changed

mlir/include/mlir/IR/Operation.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace mlir {
2727
/// 'Block' class.
2828
class alignas(8) Operation final
2929
: public llvm::ilist_node_with_parent<Operation, Block>,
30-
private llvm::TrailingObjects<Operation, BlockOperand, Region,
30+
private llvm::TrailingObjects<Operation, BlockOperand, Region, Type,
3131
detail::OperandStorage> {
3232
public:
3333
/// Create a new Operation with the specific fields.
@@ -651,7 +651,7 @@ class alignas(8) Operation final
651651

652652
/// Returns a pointer to the use list for the given trailing result.
653653
detail::TrailingOpResult *getTrailingResult(unsigned resultNumber) {
654-
// Trailing results are stored in reverse order after(before in memory) the
654+
// Trailing results are stored in reverse order after (before in memory) the
655655
// inline results.
656656
return reinterpret_cast<detail::TrailingOpResult *>(
657657
getInlineResult(OpResult::getMaxInlineResults() - 1)) -
@@ -695,11 +695,18 @@ class alignas(8) Operation final
695695
/// states recorded here:
696696
/// - 0 results : The type below is null.
697697
/// - 1 result : The single result type is held here.
698-
/// - N results : The type here is a tuple holding the result types.
699-
/// Note: We steal a bit for 'hasSingleResult' from somewhere else so that we
700-
/// can use 'resultType` in an ArrayRef<Type>.
698+
/// - N results : The type here is empty and instead the result types are held
699+
/// in trailing storage.
701700
bool hasSingleResult : 1;
702-
Type resultType;
701+
702+
/// Union representing either the Type of a single result operation (if
703+
/// hasSingleResult) or the number of result types for multi-result.
704+
union {
705+
// Type, set if single result Operation.
706+
Type type = nullptr;
707+
// Size, set if not a single result Operation.
708+
unsigned size;
709+
} resultTypeOrSize;
703710

704711
/// This holds the name of the operation.
705712
OperationName name;
@@ -720,12 +727,15 @@ class alignas(8) Operation final
720727
friend class llvm::ilist_node_with_parent<Operation, Block>;
721728

722729
// This stuff is used by the TrailingObjects template.
723-
friend llvm::TrailingObjects<Operation, BlockOperand, Region,
730+
friend llvm::TrailingObjects<Operation, BlockOperand, Region, Type,
724731
detail::OperandStorage>;
725732
size_t numTrailingObjects(OverloadToken<BlockOperand>) const {
726733
return numSuccs;
727734
}
728735
size_t numTrailingObjects(OverloadToken<Region>) const { return numRegions; }
736+
size_t numTrailingObjects(OverloadToken<Type>) const {
737+
return hasSingleResult ? 0 : resultTypeOrSize.size;
738+
}
729739
};
730740

731741
inline raw_ostream &operator<<(raw_ostream &os, Operation &op) {

mlir/lib/IR/Operation.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,11 @@ Operation *Operation::create(Location location, OperationName name,
123123
// into account the size of the operation, its trailing objects, and its
124124
// prefixed objects.
125125
size_t byteSize =
126-
totalSizeToAlloc<BlockOperand, Region, detail::OperandStorage>(
127-
numSuccessors, numRegions, needsOperandStorage ? 1 : 0) +
126+
totalSizeToAlloc<BlockOperand, Region, Type, detail::OperandStorage>(
127+
numSuccessors, numRegions,
128+
// Result type storage only needed if there is not 0 or 1 results.
129+
resultTypes.size() == 1 ? 0 : resultTypes.size(),
130+
needsOperandStorage ? 1 : 0) +
128131
detail::OperandStorage::additionalAllocSize(numOperands);
129132
size_t prefixByteSize = llvm::alignTo(
130133
Operation::prefixAllocSize(numTrailingResults, numInlineResults),
@@ -172,13 +175,18 @@ Operation::Operation(Location location, OperationName name,
172175
assert(attributes && "unexpected null attribute dictionary");
173176
assert(llvm::all_of(resultTypes, [](Type t) { return t; }) &&
174177
"unexpected null result type");
175-
if (!resultTypes.empty()) {
176-
// If there is a single result it is stored in-place, otherwise use a tuple.
178+
if (resultTypes.empty()) {
179+
resultTypeOrSize.size = 0;
180+
} else {
181+
// If there is a single result it is stored in-place, otherwise use trailing
182+
// type storage.
177183
hasSingleResult = resultTypes.size() == 1;
178-
if (hasSingleResult)
179-
resultType = resultTypes.front();
180-
else
181-
resultType = TupleType::get(location->getContext(), resultTypes);
184+
if (hasSingleResult) {
185+
resultTypeOrSize.type = resultTypes.front();
186+
} else {
187+
resultTypeOrSize.size = resultTypes.size();
188+
llvm::copy(resultTypes, getTrailingObjects<Type>());
189+
}
182190
}
183191
}
184192

@@ -545,17 +553,15 @@ void Operation::dropAllDefinedValueUses() {
545553

546554
/// Return the number of results held by this operation.
547555
unsigned Operation::getNumResults() {
548-
if (!resultType)
549-
return 0;
550-
return hasSingleResult ? 1 : resultType.cast<TupleType>().size();
556+
if (hasSingleResult)
557+
return 1;
558+
return resultTypeOrSize.size;
551559
}
552560

553561
auto Operation::getResultTypes() -> result_type_range {
554-
if (!resultType)
555-
return llvm::None;
556562
if (hasSingleResult)
557-
return resultType;
558-
return resultType.cast<TupleType>().getTypes();
563+
return resultTypeOrSize.type;
564+
return ArrayRef<Type>(getTrailingObjects<Type>(), resultTypeOrSize.size);
559565
}
560566

561567
void Operation::setSuccessor(Block *block, unsigned index) {

mlir/lib/IR/Value.cpp

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,21 @@ Type Value::getType() const {
3939
OpResult result = cast<OpResult>();
4040
Operation *owner = result.getOwner();
4141
if (owner->hasSingleResult)
42-
return owner->resultType;
43-
return owner->resultType.cast<TupleType>().getType(result.getResultNumber());
42+
return owner->resultTypeOrSize.type;
43+
return owner->getResultTypes()[result.getResultNumber()];
4444
}
4545

4646
/// Mutate the type of this Value to be of the specified type.
4747
void Value::setType(Type newType) {
4848
if (BlockArgument arg = dyn_cast<BlockArgument>())
4949
return arg.setType(newType);
50-
OpResult result = cast<OpResult>();
5150

52-
// If the owner has a single result, simply update it directly.
51+
OpResult result = cast<OpResult>();
5352
Operation *owner = result.getOwner();
54-
if (owner->hasSingleResult) {
55-
owner->resultType = newType;
56-
return;
57-
}
58-
unsigned resultNo = result.getResultNumber();
59-
60-
// Otherwise, rebuild the tuple if the new type is different from the current.
61-
auto curTypes = owner->resultType.cast<TupleType>().getTypes();
62-
if (curTypes[resultNo] == newType)
63-
return;
64-
auto newTypes = llvm::to_vector<4>(curTypes);
65-
newTypes[resultNo] = newType;
66-
owner->resultType = TupleType::get(newType.getContext(), newTypes);
53+
if (owner->hasSingleResult)
54+
owner->resultTypeOrSize.type = newType;
55+
else
56+
owner->getTrailingObjects<Type>()[result.getResultNumber()] = newType;
6757
}
6858

6959
/// If this value is the result of an Operation, return the operation that

0 commit comments

Comments
 (0)