1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
//===--- 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<SILInstruction *, 32> consumingUsesNeedingCopy;
/// A per mark must check, vector of consuming uses that copy propagation says
/// are actual last uses.
SmallVector<SILInstruction *, 32> consumingBoundaryUsers;
/// A list of non-consuming boundary uses.
SmallVector<SILInstruction *, 32> nonConsumingBoundaryUsers;
CanonicalizeOSSALifetime canonicalizer;
OSSACanonicalizer(SILFunction *fn, DominanceInfo *domTree,
InstructionDeleter &deleter)
: canonicalizer(DontPruneDebugInsts,
MaximizeLifetime_t(!fn->shouldOptimize()), fn,
nullptr /*accessBlockAnalysis*/, 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<MoveOnlyWrapperToCopyableValueInst>(user)) {
if (mtci->hasOwnedInitialKind()) {
return llvm::any_of(mtci->getUses(), [](Operand *use) {
return isa<PartialApplyInst>(use->getUser());
});
}
}
return isa<PartialApplyInst>(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<MoveOnlyWrapperToCopyableValueInst>(user)) {
if (mtci->hasOwnedInitialKind()) {
return llvm::any_of(mtci->getUses(), [](Operand *use) {
return !isa<PartialApplyInst>(use->getUser());
});
}
}
return !isa<PartialApplyInst>(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<DropDeinitInst>(inst);
}
};
using DropDeinitIter =
llvm::filter_iterator<SILInstruction *const *, DropDeinitFilter>;
using DropDeinitRange = iterator_range<DropDeinitIter>;
/// 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<MarkUnresolvedNonCopyableValueInst *, 32>
&moveIntroducersToProcess,
DiagnosticEmitter &diagnosticEmitter);
struct MoveOnlyObjectChecker {
DiagnosticEmitter &diagnosticEmitter;
DominanceInfo *domTree;
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<MarkUnresolvedNonCopyableValueInst *, 32>
&instsToCheck);
};
} // namespace siloptimizer
} // namespace swift
#endif
|