Skip to content

Commit 398fa8b

Browse files
committed
[region-isolation] Make PartitionOpEvaluator use CRTP instead of std::function callbacks.
Just a fixup requested by reviewers of incoming code that I wanted to do in a follow on commit.
1 parent df03cb4 commit 398fa8b

File tree

3 files changed

+275
-190
lines changed

3 files changed

+275
-190
lines changed

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 101 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -934,115 +934,73 @@ class Partition {
934934
/// A data structure that applies a series of PartitionOps to a single Partition
935935
/// that it modifies.
936936
///
937-
/// Apply the passed PartitionOp to this partition, performing its action. A
938-
/// `handleFailure` closure can optionally be passed in that will be called if
939-
/// a transferred region is required. The closure is given the PartitionOp
940-
/// that failed, and the index of the SIL value that was required but
941-
/// transferred. Additionally, a list of "nontransferrable" indices can be
942-
/// passed in along with a handleTransferNonTransferrable closure. In the
943-
/// event that a region containing one of the nontransferrable indices is
944-
/// transferred, the closure will be called with the offending transfer.
937+
/// Callers use CRTP to modify its behavior. Please see the definition below of
938+
/// a "blank" subclass PartitionOpEvaluatorBaseImpl for a description of the
939+
/// methods needing to be implemented by other CRTP subclasses.
940+
template <typename Impl>
945941
struct PartitionOpEvaluator {
942+
private:
943+
Impl &asImpl() { return *reinterpret_cast<Impl *>(this); }
944+
const Impl &asImpl() const { return *reinterpret_cast<const Impl *>(this); }
945+
946+
public:
946947
using Element = PartitionPrimitives::Element;
947948
using Region = PartitionPrimitives::Region;
948949
using TransferringOperandSetFactory =
949950
Partition::TransferringOperandSetFactory;
950951

952+
protected:
951953
TransferringOperandSetFactory &ptrSetFactory;
952954

953955
Partition &p;
954956

955-
/// If this PartitionOp evaluator should emit log statements.
956-
bool emitLog = true;
957-
958-
/// If set to a non-null function, then this callback will be called if we
959-
/// discover a transferred value was used after it was transferred.
960-
///
961-
/// The arguments passed to the closure are:
962-
///
963-
/// 1. The PartitionOp that required the element to be alive.
964-
///
965-
/// 2. The element in the PartitionOp that was asked to be alive.
966-
///
967-
/// 3. The operand of the instruction that originally transferred the
968-
/// region. Can be used to get the immediate value transferred or the
969-
/// transferring instruction.
970-
std::function<void(const PartitionOp &, Element, TransferringOperand)>
971-
failureCallback = nullptr;
972-
973-
/// A list of elements that cannot be transferred. Whenever we transfer, we
974-
/// check this list to see if we are transferring the element and then call
975-
/// transferNonTransferrableCallback. This should consist only of function
976-
/// arguments.
977-
ArrayRef<Element> nonTransferrableElements = {};
978-
979-
/// If set to a non-null function_ref, this is called if we detect a never
980-
/// transferred element that was passed to a transfer instruction.
981-
std::function<void(const PartitionOp &, Element)>
982-
transferredNonTransferrableCallback = nullptr;
983-
984-
/// If set to a non-null function_ref, then this is used to determine if an
985-
/// element is actor derived. If we determine that a region containing such an
986-
/// element is transferred, we emit an error since actor regions cannot be
987-
/// transferred.
988-
std::function<bool(Element)> isActorDerivedCallback = nullptr;
989-
990-
/// Check if the representative value of \p elt is closure captured at \p
991-
/// op.
992-
///
993-
/// NOTE: We actually just use the user of \p op in our callbacks. The reason
994-
/// why we do not just pass in that SILInstruction is that then we would need
995-
/// to access the instruction in the evaluator which creates a problem when
996-
/// since the operand we pass in is a dummy operand.
997-
std::function<bool(Element elt, Operand *op)> isClosureCapturedCallback =
998-
nullptr;
999-
957+
public:
1000958
PartitionOpEvaluator(Partition &p,
1001959
TransferringOperandSetFactory &ptrSetFactory)
1002960
: ptrSetFactory(ptrSetFactory), p(p) {}
1003961

1004-
/// A wrapper around the failure callback that checks if it is nullptr.
962+
/// Call shouldEmitVerboseLogging on our CRTP subclass.
963+
bool shouldEmitVerboseLogging() const {
964+
return asImpl().shouldEmitVerboseLogging();
965+
}
966+
967+
/// Call handleFailure on our CRTP subclass.
1005968
void handleFailure(const PartitionOp &op, Element elt,
1006969
TransferringOperand transferringOp) const {
1007-
if (!failureCallback)
1008-
return;
1009-
failureCallback(op, elt, transferringOp);
970+
return asImpl().handleFailure(op, elt, transferringOp);
1010971
}
1011972

1012-
/// A wrapper around transferNonTransferrableCallback that only calls it if it
1013-
/// is not null.
973+
/// Call handleTransferNonTransferrable on our CRTP subclass.
1014974
void handleTransferNonTransferrable(const PartitionOp &op,
1015975
Element elt) const {
1016-
if (!transferredNonTransferrableCallback)
1017-
return;
1018-
transferredNonTransferrableCallback(op, elt);
976+
return asImpl().handleTransferNonTransferrable(op, elt);
1019977
}
1020978

1021-
/// A wrapper around isActorDerivedCallback that returns false if
1022-
/// isActorDerivedCallback is nullptr and otherwise returns
1023-
/// isActorDerivedCallback().
979+
/// Call isActorDerived on our CRTP subclass.
1024980
bool isActorDerived(Element elt) const {
1025-
return bool(isActorDerivedCallback) && isActorDerivedCallback(elt);
981+
return asImpl().isActorDerived(elt);
1026982
}
1027983

1028-
/// A wraper around isClosureCapturedCallback that returns false if
1029-
/// isClosureCapturedCallback is nullptr and otherwise returns
1030-
/// isClosureCapturedCallback.
984+
/// Call isClosureCaptured on our CRTP subclass.
1031985
bool isClosureCaptured(Element elt, Operand *op) const {
1032-
return bool(isClosureCapturedCallback) &&
1033-
isClosureCapturedCallback(elt, op);
986+
return asImpl().isClosureCaptured(elt, op);
987+
}
988+
989+
/// Call getNonTransferrableElements() on our CRTP subclass.
990+
ArrayRef<Element> getNonTransferrableElements() const {
991+
return asImpl().getNonTransferrableElements();
1034992
}
1035993

1036994
/// Apply \p op to the partition op.
1037995
void apply(const PartitionOp &op) const {
1038-
if (emitLog) {
996+
if (shouldEmitVerboseLogging()) {
1039997
REGIONBASEDISOLATION_VERBOSE_LOG(llvm::dbgs() << "Applying: ";
1040998
op.print(llvm::dbgs()));
1041999
REGIONBASEDISOLATION_VERBOSE_LOG(llvm::dbgs() << " Before: ";
10421000
p.print(llvm::dbgs()));
10431001
}
10441002
SWIFT_DEFER {
1045-
if (emitLog) {
1003+
if (shouldEmitVerboseLogging()) {
10461004
REGIONBASEDISOLATION_VERBOSE_LOG(llvm::dbgs() << " After: ";
10471005
p.print(llvm::dbgs()));
10481006
}
@@ -1078,7 +1036,7 @@ struct PartitionOpEvaluator {
10781036

10791037
// check if any nontransferrables are transferred here, and handle the
10801038
// failure if so
1081-
for (Element nonTransferrable : nonTransferrableElements) {
1039+
for (Element nonTransferrable : getNonTransferrableElements()) {
10821040
assert(
10831041
p.isTrackingElement(nonTransferrable) &&
10841042
"nontransferrables should be function args and self, and therefore"
@@ -1167,6 +1125,75 @@ struct PartitionOpEvaluator {
11671125
}
11681126
};
11691127

1128+
/// A base implementation that can be used to default initialize CRTP
1129+
/// subclasses. Only used to implement base functionality for subclass
1130+
/// CRTPs. For true basic evaluation, use PartitionOpEvaluatorBasic below.
1131+
template <typename Subclass>
1132+
struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
1133+
using Element = PartitionPrimitives::Element;
1134+
using Region = PartitionPrimitives::Region;
1135+
using TransferringOperandSetFactory =
1136+
Partition::TransferringOperandSetFactory;
1137+
using Super = PartitionOpEvaluator<Subclass>;
1138+
1139+
PartitionOpEvaluatorBaseImpl(Partition &workingPartition,
1140+
TransferringOperandSetFactory &ptrSetFactory)
1141+
: Super(workingPartition, ptrSetFactory) {}
1142+
1143+
/// Should we emit extra verbose logging statements when evaluating
1144+
/// PartitionOps.
1145+
bool shouldEmitVerboseLogging() const { return true; }
1146+
1147+
/// A function called if we discover a transferred value was used after it
1148+
/// was transferred.
1149+
///
1150+
/// The arguments passed to the closure are:
1151+
///
1152+
/// 1. The PartitionOp that required the element to be alive.
1153+
///
1154+
/// 2. The element in the PartitionOp that was asked to be alive.
1155+
///
1156+
/// 3. The operand of the instruction that originally transferred the
1157+
/// region. Can be used to get the immediate value transferred or the
1158+
/// transferring instruction.
1159+
void handleFailure(const PartitionOp &op, Element elt,
1160+
TransferringOperand transferringOp) const {}
1161+
1162+
/// A list of elements that cannot be transferred. Whenever we transfer, we
1163+
/// check this list to see if we are transferring the element and then call
1164+
/// transferNonTransferrableCallback. This should consist only of function
1165+
/// arguments.
1166+
ArrayRef<Element> getNonTransferrableElements() const { return {}; }
1167+
1168+
/// This is called if we detect a never transferred element that was passed to
1169+
/// a transfer instruction.
1170+
void handleTransferNonTransferrable(const PartitionOp &op,
1171+
Element elt) const {}
1172+
1173+
/// This is used to determine if an element is actor derived. If we determine
1174+
/// that a region containing such an element is transferred, we emit an error
1175+
/// since actor regions cannot be transferred.
1176+
bool isActorDerived(Element elt) const { return false; }
1177+
1178+
/// Check if the representative value of \p elt is closure captured at \p
1179+
/// op.
1180+
///
1181+
/// NOTE: We actually just use the user of \p op in our callbacks. The reason
1182+
/// why we do not just pass in that SILInstruction is that then we would need
1183+
/// to access the instruction in the evaluator which creates a problem when
1184+
/// since the operand we pass in is a dummy operand.
1185+
bool isClosureCaptured(Element elt, Operand *op) const { return false; }
1186+
};
1187+
1188+
/// A subclass of PartitionOpEvaluatorBaseImpl that doesn't have any special
1189+
/// behavior.
1190+
struct PartitionOpEvaluatorBasic final
1191+
: PartitionOpEvaluatorBaseImpl<PartitionOpEvaluatorBasic> {
1192+
PartitionOpEvaluatorBasic(Partition &workingPartition,
1193+
TransferringOperandSetFactory &ptrSetFactory)
1194+
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory) {}
1195+
};
1196+
11701197
} // namespace swift
11711198

11721199
#endif // SWIFT_PARTITIONUTILS_H

0 commit comments

Comments
 (0)