mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
194 lines
6.9 KiB
C++
194 lines
6.9 KiB
C++
//===--- 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/OSSACanonicalizeOwned.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
|
|
#include "MoveOnlyBorrowToDestructureUtils.h"
|
|
|
|
namespace swift {
|
|
namespace siloptimizer {
|
|
|
|
class DiagnosticEmitter;
|
|
|
|
/// Wrapper around OSSACanonicalizeOwned 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;
|
|
|
|
OSSACanonicalizeOwned 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;
|
|
OSSACanonicalizeOwned::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;
|
|
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<MarkUnresolvedNonCopyableValueInst *, 32>
|
|
&instsToCheck);
|
|
};
|
|
|
|
} // namespace siloptimizer
|
|
} // namespace swift
|
|
|
|
#endif
|