Files
TheRock/compiler/amd-llvm/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

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