Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "phasar/DataFlow/IfdsIde/EdgeFunction.h"
#include "phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h"

#include <concepts>

namespace psr {

/// Default implementation of EdgeFunctionSingletonCache.
Expand Down Expand Up @@ -53,6 +55,7 @@ class DefaultEdgeFunctionSingletonCacheImpl
void erase(const EdgeFunctionTy &EF) noexcept override { Cache.erase(&EF); }

template <typename... ArgTys>
requires std::constructible_from<EdgeFunctionTy, ArgTys...>
[[nodiscard]] EdgeFunction<L> createEdgeFunction(ArgTys &&...Args) {
return CachedEdgeFunction<EdgeFunctionTy>{
EdgeFunctionTy{std::forward<ArgTys>(Args)...}, this};
Expand Down
205 changes: 158 additions & 47 deletions include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_FIELDSENSALLOCSITESAWAREIFDSPROBLEM_H
#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_FIELDSENSALLOCSITESAWAREIFDSPROBLEM_H

#include "phasar/DataFlow/IfdsIde/EdgeFunction.h"
#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h"
#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h"
#include "phasar/Domain/BinaryDomain.h"
Expand All @@ -19,15 +20,20 @@
#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h"
#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h"
#include "phasar/Utils/Compressor.h"
#include "phasar/Utils/Fn.h"
#include "phasar/Utils/Logger.h"
#include "phasar/Utils/MapUtils.h"
#include "phasar/Utils/SmallArraySet.h"
#include "phasar/Utils/StrongTypeDef.h"
#include "phasar/Utils/TableWrappers.h"
#include "phasar/Utils/TypeTraits.h"
#include "phasar/Utils/TypedVector.h"
#include "phasar/Utils/Utilities.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Operator.h"
Expand All @@ -37,21 +43,16 @@
#include <cstdint>
#include <type_traits>

namespace psr::cfl_fieldsens {

/// \file
/// Implements field-sensitivity after the paper "Boosting the performance
/// of alias-aware IFDS analysis with CFL-based environment transformers" by Li
/// et al. <https://doi.org/10.1145/3689804>

// NOLINTNEXTLINE(performance-enum-size)
enum class FieldStringNodeId : uint32_t {
None = 0,
};
PHASAR_STRONG_TYPEDEF(psr::cfl_fieldsens, uint32_t, FieldStringNodeId,
None = 0);

[[nodiscard]] inline llvm::hash_code hash_value(FieldStringNodeId NId) {
return llvm::hash_value(std::underlying_type_t<FieldStringNodeId>(NId));
}
PHASAR_STRONG_TYPEDEF(psr::cfl_fieldsens, uint32_t, KillSetId, Empty = 0);
namespace psr::cfl_fieldsens {

struct FieldStringNode {
FieldStringNodeId Next{};
Expand Down Expand Up @@ -93,6 +94,8 @@ namespace cfl_fieldsens {
/// Interns the Store- and Load field-strings
class FieldStringManager {
public:
static constexpr int32_t TopOffset = INT32_MIN;

FieldStringManager();

[[nodiscard]] FieldStringNodeId intern(FieldStringNode Nod) {
Expand Down Expand Up @@ -127,44 +130,75 @@ class FieldStringManager {
return Depth[NId];
}

[[nodiscard]] KillSetId internKills(SmallArraySet<int32_t, 2> &&Kills) {
return KillsCompressor.getOrInsert(std::move(Kills));
}

[[nodiscard]] KillSetId addKill(KillSetId KS, int32_t Offs) {
if (Offs == TopOffset || KillsCompressor[KS].contains(Offs)) {
return KS;
}

auto Kills = KillsCompressor[KS];
Kills.insert(Offs);
return KillsCompressor.getOrInsert(std::move(Kills));
}

[[nodiscard]] bool isKilledBy(KillSetId KS, int32_t Offs) const {
if (Offs == TopOffset || KS == KillSetId::Empty) {
return false;
}
if (!KillsCompressor.inbounds(KS)) [[unlikely]] {
return false;
}

return KillsCompressor[KS].contains(Offs);
}

[[nodiscard]] const auto &kills(KillSetId KS) const {
return KillsCompressor[KS];
}

void reserve(size_t ExpectedCapacity) {
NodeCompressor.reserve(ExpectedCapacity);
Depth.reserve(ExpectedCapacity);
}

private:
Compressor<FieldStringNode, FieldStringNodeId> NodeCompressor{};
TypedVector<FieldStringNodeId, uint32_t> Depth{};
Compressor<SmallArraySet<int32_t, 2>, KillSetId> KillsCompressor{};
};

/// A single CFL Field-Access String consisting of: gep, loads, kills, and
/// stores
struct AccessPath {
static constexpr int32_t TopOffset = INT32_MIN;
static constexpr int32_t TopOffset = FieldStringManager::TopOffset;

FieldStringNodeId Loads{};
FieldStringNodeId Stores{};
llvm::SmallDenseSet<int32_t, 2> Kills{};
KillSetId Kills{};
// Add an offset for pending GEPs; INT32_MIN is Top
int32_t Offset = {0};
int32_t EmptyTombstone = 0;
int32_t Offset{};

[[nodiscard]] bool empty() const noexcept {
return Loads == FieldStringNodeId::None &&
Stores == FieldStringNodeId::None && Kills.empty() && Offset == 0;
}

[[nodiscard]] bool kills(int32_t Off) const {
return Off != TopOffset && Kills.contains(Off);
Stores == FieldStringNodeId::None && Kills == KillSetId::Empty &&
Offset == 0;
}

[[nodiscard]] constexpr bool
operator==(const AccessPath &Other) const noexcept {
return EmptyTombstone == Other.EmptyTombstone && Loads == Other.Loads &&
Stores == Other.Stores && Kills == Other.Kills;
}
operator==(const AccessPath &Other) const noexcept = default;

bool operator!=(const AccessPath &Other) const noexcept {
return !(*this == Other);
friend constexpr size_t hash_value(const AccessPath &FieldString) noexcept {
size_t HC = 37;
HC = HC * 31 + size_t(FieldString.Loads);
HC = HC * 31 + size_t(FieldString.Stores);
HC = HC * 31 + size_t(FieldString.Kills);
HC = HC * 31 + size_t(FieldString.Offset);
return HC;
}

friend size_t hash_value(const AccessPath &FieldString) noexcept;

friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
const AccessPath &FieldString);

Expand All @@ -174,32 +208,27 @@ struct AccessPath {
struct AccessPathDMI {
static AccessPath getEmptyKey() {
AccessPath Ret{};
Ret.EmptyTombstone = 1;
Ret.Loads = FieldStringNodeId(UINT32_MAX);
return Ret;
}
static AccessPath getTombstoneKey() {
AccessPath Ret{};
Ret.EmptyTombstone = 2;
Ret.Loads = FieldStringNodeId(UINT32_MAX);
Ret.Stores = FieldStringNodeId(UINT32_MAX);
return Ret;
}
static auto getHashValue(const AccessPath &FieldString) noexcept {
static auto getHashValue(AccessPath FieldString) noexcept {
return hash_value(FieldString);
}
static bool isEqual(const AccessPath &L, const AccessPath &R) noexcept {
if (L.EmptyTombstone != R.EmptyTombstone) {
return false;
}
if (L.EmptyTombstone) {
return true;
}
return L == R;
}
static bool isEqual(AccessPath L, AccessPath R) noexcept { return L == R; }
};

/// An edge-value consisting of a set if CFL field access strings.
struct IFDSEdgeValue {
using container_type = llvm::SmallDenseSet<AccessPath, 2, AccessPathDMI>;

[[clang::require_explicit_initialization]] FieldStringManager *Mgr{};
llvm::SmallDenseSet<AccessPath, 2, AccessPathDMI> Paths;
container_type Paths;

static constexpr llvm::StringLiteral LogCategory = "IFDSEdgeValue";

Expand All @@ -214,7 +243,7 @@ struct IFDSEdgeValue {
return !(*this == Other);
}

[[nodiscard]] friend auto hash_value(const IFDSEdgeValue EV) {
[[nodiscard]] friend auto hash_value(const IFDSEdgeValue &EV) {
return llvm::hash_combine_range(EV.Paths.begin(), EV.Paths.end());
}

Expand Down Expand Up @@ -363,18 +392,78 @@ bool filterFieldSensFacts(
return true;
}

struct CFLFieldSensEdgeFunctionImpl {
using l_t = LatticeDomain<IFDSEdgeValue>;
[[clang::require_explicit_initialization]] IFDSEdgeValue Transform;
[[clang::require_explicit_initialization]] uint8_t DepthKLimit{};

bool operator==(const CFLFieldSensEdgeFunctionImpl &Other) const noexcept {
assert(DepthKLimit == Other.DepthKLimit);
return Transform == Other.Transform;
}

friend auto hash_value(const CFLFieldSensEdgeFunctionImpl &EF) noexcept {
return hash_value(EF.Transform);
}

[[nodiscard]] static auto from(IFDSEdgeValue &&Txn, uint8_t DepthKLimit) {
return CFLFieldSensEdgeFunctionImpl{
.Transform = std::move(Txn),
.DepthKLimit = DepthKLimit,
};
}

[[nodiscard]] static auto from(AccessPath Txn, FieldStringManager &Mgr,
uint8_t DepthKLimit) {
return CFLFieldSensEdgeFunctionImpl{
.Transform = {.Mgr = &Mgr, .Paths = {Txn}},
.DepthKLimit = DepthKLimit,
};
}

[[nodiscard]] static auto fromEpsilon(uint8_t DepthKLimit,
FieldStringManager &Mgr) {
return CFLFieldSensEdgeFunctionImpl{
.Transform = IFDSEdgeValue::epsilon(&Mgr),
.DepthKLimit = DepthKLimit,
};
}
};

struct CFLFieldSensEdgeFunction {
using l_t = LatticeDomain<IFDSEdgeValue>;
[[clang::require_explicit_initialization]] const CFLFieldSensEdgeFunctionImpl
*Impl{};

[[nodiscard]] l_t computeTarget(l_t Source) const {
assert(Impl != nullptr);
Source.onValue(fn<&IFDSEdgeValue::applyTransforms>, Impl->Transform,
Impl->DepthKLimit);
return Source;
}

constexpr friend bool
operator==(CFLFieldSensEdgeFunction L,
CFLFieldSensEdgeFunction R) noexcept = default;

friend auto hash_value(CFLFieldSensEdgeFunction EF) noexcept {
return llvm::hash_value(EF.Impl);
}

friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
CFLFieldSensEdgeFunction EF);
};

} // namespace cfl_fieldsens

/// An IFDS-Problem adaptor that makes any field-insensitive IFDS analysis
/// field-sensitive. Just wrap your IFDS problem with
/// FieldSensAllocSitesAwareIFDSProblem and use the IterativeIDESolver instead
/// of the IFDSSolver.
/// field-sensitive. Just wrap your IFDS problem with CFLFieldSensIFDSProblem
/// and use the IterativeIDESolver instead of the IFDSSolver.
///
/// The only thing to change in your usual IFDS problem is not to kill data-flow
/// facts when only parts of the fields should be killed. This is now handled by
/// the FieldSensAllocSitesAwareIFDSProblem. For that, provide a
/// FieldSensAllocSitesAwareIFDSProblemConfig with a proper KillsAt
/// implementation.
/// the CFLFieldSensIFDSProblem. For that, provide a CFLFieldSensIFDSProblem
/// with a proper KillsAt implementation.
class CFLFieldSensIFDSProblem
: public IDETabulationProblem<cfl_fieldsens::IFDSDomain> {
using Base = IDETabulationProblem<cfl_fieldsens::IFDSDomain>;
Expand Down Expand Up @@ -426,7 +515,10 @@ class CFLFieldSensIFDSProblem
: Base(assertNotNull(UserProblem).getProjectIRDB(),
assertNotNull(UserProblem).getEntryPoints(),
UserProblem->getZeroValue()),
UserProblem(UserProblem), Config(std::move(Config)) {}
UserProblem(UserProblem), Config(std::move(Config)) {
Mgr.reserve(UserProblem->getProjectIRDB()->getNumInstructions());
regCounters();
}

/// Constructs an IDETabulationProblem with the usual arguments, forwarded
/// from UserProblem and tries to automatically derive the config from
Expand Down Expand Up @@ -487,6 +579,10 @@ class CFLFieldSensIFDSProblem
uint8_t DepthKLimit,
const llvm::DataLayout &DL);

EdgeFunction<l_t> getLoadEdgeFunction(d_t CurrNode, d_t PointerOp,
uint8_t DepthKLimit,
const llvm::DataLayout &DL);

EdgeFunction<l_t> getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ,
d_t SuccNode) override;

Expand Down Expand Up @@ -516,10 +612,25 @@ class CFLFieldSensIFDSProblem
[[nodiscard]] const auto &base() const noexcept { return *UserProblem; }

private:
using EFConstPtr = const cfl_fieldsens::CFLFieldSensEdgeFunctionImpl *;
using EFResultPtr = llvm::PointerIntPair<EFConstPtr, 2>;

[[nodiscard]] EdgeFunction<l_t>
makeEF(cfl_fieldsens::CFLFieldSensEdgeFunctionImpl &&EF);
[[nodiscard]] EFResultPtr
makeEFPtr(cfl_fieldsens::CFLFieldSensEdgeFunctionImpl &&EF);

static void regCounters() noexcept;

IFDSTabulationProblem<LLVMIFDSAnalysisDomainDefault> *UserProblem{};
cfl_fieldsens::FieldStringManager Mgr{};
cfl_fieldsens::IFDSProblemConfig Config{};

UnorderedSet<cfl_fieldsens::CFLFieldSensEdgeFunctionImpl> EFInternCache{};

llvm::DenseMap<std::pair<EFConstPtr, EFConstPtr>, EFResultPtr> ExtendCache{};
llvm::DenseMap<std::pair<EFConstPtr, EFConstPtr>, EFResultPtr> CombineCache{};

uint8_t DepthKLimit = 5; // Original from the paper
};
} // namespace psr
Expand Down
6 changes: 6 additions & 0 deletions include/phasar/PhasarLLVM/Utils/LLVMShorthands.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,12 @@ getPointerIndicesOfType(const llvm::DIType *Ty, const llvm::DataLayout &DL);
getPointerIndicesOfType(const llvm::DIType *Ty, const llvm::DataLayout &DL,
PointerIndicesCache &PIC);

[[nodiscard]] bool walkLoadChainTo(const llvm::Value *Start,
const llvm::Value *Target,
const llvm::DataLayout &DL,
uint32_t MaxDepth,
llvm::function_ref<void(int64_t)> OnDeref);

/**
* Retrieves String annotation value as per
* <https://llvm.org/docs/LangRef.html#llvm-var-annotation-intrinsic>
Expand Down
3 changes: 3 additions & 0 deletions include/phasar/Utils/Compressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ class Compressor<T, IdT> {
assert(Elem != nullptr);
if constexpr (has_llvm_dense_map_info<T>) {
return llvm::DenseMapInfo<T>::getHashValue(*Elem);
} else if constexpr (is_llvm_hashable_v<T>) {
using llvm::hash_value;
return hash_value(*Elem);
} else {
return std::hash<T>{}(*Elem);
}
Expand Down
Loading
Loading