//===--- MoveOnlyObjectCheckerUtils.h -------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// /// /// This is for shared code in between the move only object checker and the move /// only address checker. This is needed since the move only address checker /// uses the move only object checker to check values loaded from allocations /// that it is analyzing. /// //===----------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_MANDATORY_MOVEONLYOBJECTCHECKERUTILS_H #define SWIFT_SILOPTIMIZER_MANDATORY_MOVEONLYOBJECTCHECKERUTILS_H #include "swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h" #include "llvm/Support/Compiler.h" #include "MoveOnlyBorrowToDestructureUtils.h" namespace swift { namespace siloptimizer { class DiagnosticEmitter; /// Wrapper around CanonicalizeOSSALifetime that we use to specialize its /// interface for our purposes. struct OSSACanonicalizer { /// A per mark must check, vector of uses that copy propagation says need a /// copy and thus are not final consuming uses. SmallVector consumingUsesNeedingCopy; /// A per mark must check, vector of consuming uses that copy propagation says /// are actual last uses. SmallVector consumingBoundaryUsers; /// A list of non-consuming boundary uses. SmallVector nonConsumingBoundaryUsers; CanonicalizeOSSALifetime canonicalizer; OSSACanonicalizer(SILFunction *fn, DominanceInfo *domTree, DeadEndBlocksAnalysis *deadEndBlocksAnalysis, InstructionDeleter &deleter) : canonicalizer(DontPruneDebugInsts, MaximizeLifetime_t(!fn->shouldOptimize()), fn, nullptr /*accessBlockAnalysis*/, deadEndBlocksAnalysis, domTree, nullptr /*calleeAnalysis*/, deleter) {} void clear() { consumingUsesNeedingCopy.clear(); consumingBoundaryUsers.clear(); nonConsumingBoundaryUsers.clear(); } struct LivenessState { OSSACanonicalizer &parent; CanonicalizeOSSALifetime::LivenessState canonicalizerState; LivenessState(OSSACanonicalizer &parent, SILValue def) : parent(parent), canonicalizerState(parent.canonicalizer, def, {}) {} ~LivenessState() { parent.clear(); } }; /// MARK: The following APIs require an active on-stack instance of /// LivenessState: /// /// OSSACanonicalizer::LivenessState livenessState(canonicalizer, value); /// Perform computeLiveness, computeBoundaryData, and rewriteLifetimes in one /// fell swoop. bool canonicalize(); bool computeLiveness() { return canonicalizer.computeLiveness(); } void computeBoundaryData(SILValue value); void rewriteLifetimes() { canonicalizer.rewriteLifetimes(); } void findOriginalBoundary(PrunedLivenessBoundary &resultingFoundBoundary) { canonicalizer.findOriginalBoundary(resultingFoundBoundary); } bool foundAnyConsumingUses() const { return consumingUsesNeedingCopy.size() || consumingBoundaryUsers.size(); } bool foundConsumingUseRequiringCopy() const { return consumingUsesNeedingCopy.size(); } bool foundFinalConsumingUses() const { return consumingBoundaryUsers.size(); } /// Helper method for hasPartialApplyConsumingUse and /// hasNonPartialApplyConsumingUse. static bool isPartialApplyUser(SILInstruction *user) { // If our user is an owned moveonlywrapper to copyable value inst, search // through it for a partial_apply user. if (auto *mtci = dyn_cast(user)) { if (mtci->hasOwnedInitialKind()) { return llvm::any_of(mtci->getUses(), [](Operand *use) { return isa(use->getUser()); }); } } return isa(user); } static bool isNotPartialApplyUser(SILInstruction *user) { // If our user is an owned moveonlywrapper to copyable value inst, search // through it for a partial_apply user. if (auto *mtci = dyn_cast(user)) { if (mtci->hasOwnedInitialKind()) { return llvm::any_of(mtci->getUses(), [](Operand *use) { return !isa(use->getUser()); }); } } return !isa(user); } bool hasPartialApplyConsumingUse() const { auto test = OSSACanonicalizer::isPartialApplyUser; return llvm::any_of(consumingUsesNeedingCopy, test) || llvm::any_of(consumingBoundaryUsers, test); } bool hasNonPartialApplyConsumingUse() const { auto test = OSSACanonicalizer::isNotPartialApplyUser; return llvm::any_of(consumingUsesNeedingCopy, test) || llvm::any_of(consumingBoundaryUsers, test); } struct DropDeinitFilter { bool operator()(SILInstruction *inst) const { return isa(inst); } }; using DropDeinitIter = llvm::filter_iterator; using DropDeinitRange = iterator_range; /// Returns a range of final uses of the mark_unresolved_non_copyable_value /// that are drop_deinit DropDeinitRange getDropDeinitUses() const { return llvm::make_filter_range(consumingBoundaryUsers, DropDeinitFilter()); } }; /// Search for candidate object mark_unresolved_non_copyable_values. If we find /// one that does not fit a pattern that we understand, emit an error diagnostic /// telling the programmer that the move checker did not know how to recognize /// this code pattern. /// /// \returns true if we deleted a mark_unresolved_non_copyable_value inst that /// we didn't recognize after emitting the diagnostic. /// /// To check if an error was emitted call \p /// diagnosticEmitter.getDiagnosticCount(). /// /// NOTE: This is the routine used by the move only object checker to find mark /// must checks to process. bool searchForCandidateObjectMarkUnresolvedNonCopyableValueInsts( SILFunction *fn, llvm::SmallSetVector &moveIntroducersToProcess, DiagnosticEmitter &diagnosticEmitter); struct MoveOnlyObjectChecker { DiagnosticEmitter &diagnosticEmitter; DominanceInfo *domTree; DeadEndBlocksAnalysis *deadEndBlocksAnalysis; PostOrderAnalysis *poa; borrowtodestructure::IntervalMapAllocator &allocator; /// Returns true if we changed the IR in any way. Check with \p /// diagnosticEmitter to see if we emitted any diagnostics. bool check(llvm::SmallSetVector &instsToCheck); }; } // namespace siloptimizer } // namespace swift #endif