CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
225 lines
8.2 KiB
C++
225 lines
8.2 KiB
C++
//===- LiveOrigins.cpp - Live Origins Analysis -----------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
|
|
#include "Dataflow.h"
|
|
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
namespace clang::lifetimes::internal {
|
|
namespace {
|
|
|
|
/// The dataflow lattice for origin liveness analysis.
|
|
/// It tracks which origins are live, why they're live (which UseFact),
|
|
/// and the confidence level of that liveness.
|
|
struct Lattice {
|
|
LivenessMap LiveOrigins;
|
|
|
|
Lattice() : LiveOrigins(nullptr) {};
|
|
|
|
explicit Lattice(LivenessMap L) : LiveOrigins(L) {}
|
|
|
|
bool operator==(const Lattice &Other) const {
|
|
return LiveOrigins == Other.LiveOrigins;
|
|
}
|
|
|
|
bool operator!=(const Lattice &Other) const { return !(*this == Other); }
|
|
|
|
void dump(llvm::raw_ostream &OS, const OriginManager &OM) const {
|
|
if (LiveOrigins.isEmpty())
|
|
OS << " <empty>\n";
|
|
for (const auto &Entry : LiveOrigins) {
|
|
OriginID OID = Entry.first;
|
|
const LivenessInfo &Info = Entry.second;
|
|
OS << " ";
|
|
OM.dump(OID, OS);
|
|
OS << " is ";
|
|
switch (Info.Kind) {
|
|
case LivenessKind::Must:
|
|
OS << "definitely";
|
|
break;
|
|
case LivenessKind::Maybe:
|
|
OS << "maybe";
|
|
break;
|
|
case LivenessKind::Dead:
|
|
llvm_unreachable("liveness kind of live origins should not be dead.");
|
|
}
|
|
OS << " live at this point\n";
|
|
}
|
|
}
|
|
};
|
|
|
|
static SourceLocation GetFactLoc(CausingFactType F) {
|
|
if (const auto *UF = F.dyn_cast<const UseFact *>())
|
|
return UF->getUseExpr()->getExprLoc();
|
|
if (const auto *OEF = F.dyn_cast<const OriginEscapesFact *>()) {
|
|
if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
|
|
return ReturnEsc->getReturnExpr()->getExprLoc();
|
|
if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
|
|
return FieldEsc->getFieldDecl()->getLocation();
|
|
if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
|
|
return GlobalEsc->getGlobal()->getLocation();
|
|
}
|
|
llvm_unreachable("unhandled causing fact in PointerUnion");
|
|
}
|
|
|
|
/// The analysis that tracks which origins are live, with granular information
|
|
/// about the causing use fact and confidence level. This is a backward
|
|
/// analysis.
|
|
class AnalysisImpl
|
|
: public DataflowAnalysis<AnalysisImpl, Lattice, Direction::Backward> {
|
|
|
|
public:
|
|
AnalysisImpl(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
|
|
LivenessMap::Factory &SF)
|
|
: DataflowAnalysis(C, AC, F), FactMgr(F), Factory(SF) {}
|
|
using DataflowAnalysis<AnalysisImpl, Lattice, Direction::Backward>::transfer;
|
|
|
|
StringRef getAnalysisName() const { return "LiveOrigins"; }
|
|
|
|
Lattice getInitialState() { return Lattice(Factory.getEmptyMap()); }
|
|
|
|
/// Merges two lattices by combining liveness information.
|
|
/// When the same origin has different confidence levels, we take the lower
|
|
/// one.
|
|
Lattice join(Lattice L1, Lattice L2) const {
|
|
LivenessMap Merged = L1.LiveOrigins;
|
|
// Take the earliest Fact to make the join hermetic and commutative.
|
|
auto CombineCausingFact = [](CausingFactType A,
|
|
CausingFactType B) -> CausingFactType {
|
|
if (!A)
|
|
return B;
|
|
if (!B)
|
|
return A;
|
|
return GetFactLoc(A) < GetFactLoc(B) ? A : B;
|
|
};
|
|
auto CombineLivenessKind = [](LivenessKind K1,
|
|
LivenessKind K2) -> LivenessKind {
|
|
assert(K1 != LivenessKind::Dead && "LivenessKind should not be dead.");
|
|
assert(K2 != LivenessKind::Dead && "LivenessKind should not be dead.");
|
|
// Only return "Must" if both paths are "Must", otherwise Maybe.
|
|
if (K1 == LivenessKind::Must && K2 == LivenessKind::Must)
|
|
return LivenessKind::Must;
|
|
return LivenessKind::Maybe;
|
|
};
|
|
auto CombineLivenessInfo = [&](const LivenessInfo *L1,
|
|
const LivenessInfo *L2) -> LivenessInfo {
|
|
assert((L1 || L2) && "unexpectedly merging 2 empty sets");
|
|
if (!L1)
|
|
return LivenessInfo(L2->CausingFact, LivenessKind::Maybe);
|
|
if (!L2)
|
|
return LivenessInfo(L1->CausingFact, LivenessKind::Maybe);
|
|
return LivenessInfo(CombineCausingFact(L1->CausingFact, L2->CausingFact),
|
|
CombineLivenessKind(L1->Kind, L2->Kind));
|
|
};
|
|
return Lattice(utils::join(
|
|
L1.LiveOrigins, L2.LiveOrigins, Factory, CombineLivenessInfo,
|
|
// A symmetric join is required here. If an origin is live on one
|
|
// branch but not the other, its confidence must be demoted to `Maybe`.
|
|
utils::JoinKind::Symmetric));
|
|
}
|
|
|
|
/// A read operation makes the origin live with definite confidence, as it
|
|
/// dominates this program point. A write operation kills the liveness of
|
|
/// the origin since it overwrites the value.
|
|
Lattice transfer(Lattice In, const UseFact &UF) {
|
|
Lattice Out = In;
|
|
for (const OriginList *Cur = UF.getUsedOrigins(); Cur;
|
|
Cur = Cur->peelOuterOrigin()) {
|
|
OriginID OID = Cur->getOuterOriginID();
|
|
// Write kills liveness.
|
|
if (UF.isWritten()) {
|
|
Out = Lattice(Factory.remove(Out.LiveOrigins, OID));
|
|
} else {
|
|
// Read makes origin live with definite confidence (dominates this
|
|
// point).
|
|
Out = Lattice(Factory.add(Out.LiveOrigins, OID,
|
|
LivenessInfo(&UF, LivenessKind::Must)));
|
|
}
|
|
}
|
|
return Out;
|
|
}
|
|
|
|
/// An escaping origin (e.g., via return) makes the origin live with definite
|
|
/// confidence, as it dominates this program point.
|
|
Lattice transfer(Lattice In, const OriginEscapesFact &OEF) {
|
|
OriginID OID = OEF.getEscapedOriginID();
|
|
return Lattice(Factory.add(In.LiveOrigins, OID,
|
|
LivenessInfo(&OEF, LivenessKind::Must)));
|
|
}
|
|
|
|
/// Issuing a new loan to an origin kills its liveness.
|
|
Lattice transfer(Lattice In, const IssueFact &IF) {
|
|
return Lattice(Factory.remove(In.LiveOrigins, IF.getOriginID()));
|
|
}
|
|
|
|
/// An OriginFlow kills the liveness of the destination origin if `KillDest`
|
|
/// is true. Otherwise, it propagates liveness from destination to source.
|
|
Lattice transfer(Lattice In, const OriginFlowFact &OF) {
|
|
if (!OF.getKillDest())
|
|
return In;
|
|
return Lattice(Factory.remove(In.LiveOrigins, OF.getDestOriginID()));
|
|
}
|
|
|
|
Lattice transfer(Lattice In, const KillOriginFact &F) {
|
|
return Lattice(Factory.remove(In.LiveOrigins, F.getKilledOrigin()));
|
|
}
|
|
|
|
Lattice transfer(Lattice In, const ExpireFact &F) {
|
|
if (auto OID = F.getOriginID())
|
|
return Lattice(Factory.remove(In.LiveOrigins, *OID));
|
|
return In;
|
|
}
|
|
|
|
LivenessMap getLiveOriginsAt(ProgramPoint P) const {
|
|
return getState(P).LiveOrigins;
|
|
}
|
|
|
|
// Dump liveness values on all test points in the program.
|
|
void dump(llvm::raw_ostream &OS,
|
|
const llvm::StringMap<ProgramPoint> &TestPoints) const {
|
|
llvm::dbgs() << "==========================================\n";
|
|
llvm::dbgs() << getAnalysisName() << " results:\n";
|
|
llvm::dbgs() << "==========================================\n";
|
|
for (const auto &Entry : TestPoints) {
|
|
OS << "TestPoint: " << Entry.getKey() << "\n";
|
|
getState(Entry.getValue()).dump(OS, FactMgr.getOriginMgr());
|
|
}
|
|
}
|
|
|
|
private:
|
|
FactManager &FactMgr;
|
|
LivenessMap::Factory &Factory;
|
|
};
|
|
} // namespace
|
|
|
|
// PImpl wrapper implementation
|
|
class LiveOriginsAnalysis::Impl : public AnalysisImpl {
|
|
using AnalysisImpl::AnalysisImpl;
|
|
};
|
|
|
|
LiveOriginsAnalysis::LiveOriginsAnalysis(const CFG &C, AnalysisDeclContext &AC,
|
|
FactManager &F,
|
|
LivenessMap::Factory &SF)
|
|
: PImpl(std::make_unique<Impl>(C, AC, F, SF)) {
|
|
PImpl->run();
|
|
}
|
|
|
|
LiveOriginsAnalysis::~LiveOriginsAnalysis() = default;
|
|
|
|
LivenessMap LiveOriginsAnalysis::getLiveOriginsAt(ProgramPoint P) const {
|
|
return PImpl->getLiveOriginsAt(P);
|
|
}
|
|
|
|
void LiveOriginsAnalysis::dump(
|
|
llvm::raw_ostream &OS,
|
|
const llvm::StringMap<ProgramPoint> &TestPoints) const {
|
|
PImpl->dump(OS, TestPoints);
|
|
}
|
|
} // namespace clang::lifetimes::internal
|