Skip to content

Commit

Permalink
Cleanup FlowFunction Templates (#550)
Browse files Browse the repository at this point in the history
* Abstract away creating flow functions based on the common templates from FlowFunctions.h. TODO: LLVMFlowFunctions.h + move custom flow functions to lambdaFlow in order to get rid of the shared_ptr occurrences in analysis code + document the flow-function templates

* Rewrite and simplify LLVMFlowFunctions

* Add comments to the flow function templates in FlowFunctions.h

* Mark old flow function templates as deprecated

* FIx mapFactsAlongsideCallSite() + make callbacks of flow function tempaltes type-safe

* Modernize flow functions of LCA

* Documentation comments on LLVMFlowFunctions

* Split parameter-predicate and return-value-predicate for the mapFactsToCaller flow function based on review comment
  • Loading branch information
fabianbs96 authored Dec 22, 2022
1 parent 5a9c59f commit c484261
Show file tree
Hide file tree
Showing 14 changed files with 1,384 additions and 893 deletions.
1,157 changes: 853 additions & 304 deletions include/phasar/PhasarLLVM/DataFlowSolver/IfdsIde/FlowFunctions.h

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ class IDETabulationProblem : public FlowFunctions<AnalysisDomainTy, Container>,
virtual bool setSoundness(Soundness /*S*/) { return false; }

protected:
typename FlowFunctions<AnalysisDomainTy, Container>::FlowFunctionPtrType
generateFromZero(d_t FactToGenerate) {
return generateFlow(std::move(FactToGenerate), getZeroValue());
}

const db_t *IRDB{};
std::vector<std::string> EntryPoints;
std::optional<d_t> ZeroValue;
Expand Down
718 changes: 367 additions & 351 deletions include/phasar/PhasarLLVM/DataFlowSolver/IfdsIde/LLVMFlowFunctions.h

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ template <typename EdgeFactType = std::string,
class IDEInstInteractionAnalysisT
: public IDETabulationProblem<
IDEInstInteractionAnalysisDomain<EdgeFactType>> {
using IDETabulationProblem<
IDEInstInteractionAnalysisDomain<EdgeFactType>>::generateFromZero;

public:
using AnalysisDomainTy = IDEInstInteractionAnalysisDomain<EdgeFactType>;

Expand Down Expand Up @@ -242,7 +245,7 @@ class IDEInstInteractionAnalysisT
//
if (const auto *Alloca = llvm::dyn_cast<llvm::AllocaInst>(Curr)) {
PHASAR_LOG_LEVEL(DFADEBUG, "AllocaInst");
return std::make_shared<Gen<d_t>>(Alloca, this->getZeroValue());
return generateFromZero(Alloca);
}

// Handle indirect taints, i. e., propagate values that depend on branch
Expand Down Expand Up @@ -414,7 +417,7 @@ class IDEInstInteractionAnalysisT
// 0 y x
//
if (const auto *Load = llvm::dyn_cast<llvm::LoadInst>(Curr)) {
return std::make_shared<Gen<d_t>>(Load, Load->getPointerOperand());
return generateFlow<d_t>(Load, Load->getPointerOperand());
}
// Handle store instructions
//
Expand Down Expand Up @@ -564,11 +567,11 @@ class IDEInstInteractionAnalysisT
f_t DestFun) override {
if (this->ICF->isHeapAllocatingFunction(DestFun)) {
// Kill add facts and model the effects in getCallToRetFlowFunction().
return KillAll<d_t>::getInstance();
return killAllFlows<d_t>();
}
if (DestFun->isDeclaration()) {
// We don't have anything that we could analyze, kill all facts.
return KillAll<d_t>::getInstance();
return killAllFlows<d_t>();
}
const auto *CS = llvm::cast<llvm::CallBase>(CallSite);
// Map actual to formal parameters.
Expand Down Expand Up @@ -609,7 +612,7 @@ class IDEInstInteractionAnalysisT
return {};
}
// Pass ZeroValue as is, if desired
if (LLVMZeroValue::getInstance()->isLLVMZeroValue(Source)) {
if (LLVMZeroValue::isLLVMZeroValue(Source)) {
return {Source};
}
container_type Res;
Expand Down Expand Up @@ -678,10 +681,10 @@ class IDEInstInteractionAnalysisT
SRetFormals.insert(DestFun->getArg(Idx));
}
}
auto GenSRetFormals = std::make_shared<GenAllAndKillAllOthers<d_t>>(
SRetFormals, this->getZeroValue());
return std::make_shared<Union<d_t>>(
std::vector<FlowFunctionPtrType>({MapFactsToCalleeFF, GenSRetFormals}));

return unionFlows(std::move(MapFactsToCalleeFF),
generateManyFlowsAndKillAllOthers(std::move(SRetFormals),
this->getZeroValue()));
}

inline FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun,
Expand Down Expand Up @@ -712,7 +715,7 @@ class IDEInstInteractionAnalysisT

std::set<IDEIIAFlowFact> computeTargets(IDEIIAFlowFact Source) override {
// Pass ZeroValue as is, if desired
if (LLVMZeroValue::getInstance()->isLLVMZeroValue(Source.getBase())) {
if (LLVMZeroValue::isLLVMZeroValue(Source.getBase())) {
return {Source};
}
// Pass global variables as is, if desired
Expand Down Expand Up @@ -778,11 +781,9 @@ class IDEInstInteractionAnalysisT
// Generate the respective callsite. The callsite will receive its
// value from this very return instruction cf.
// getReturnEdgeFunction().
auto ConstantRetGen = std::make_shared<GenAndKillAllOthers<d_t>>(
CallSite, this->getZeroValue());
return std::make_shared<Union<d_t>>(
std::vector<FlowFunctionPtrType>(
{MapFactsToCallerFF, ConstantRetGen}));
return unionFlows(std::move(MapFactsToCallerFF),
generateFlowAndKillAllOthers<d_t>(
CallSite, this->getZeroValue()));
}
}
}
Expand Down Expand Up @@ -811,7 +812,7 @@ class IDEInstInteractionAnalysisT
// v v
// 0 x
//
return std::make_shared<Gen<d_t>>(CallSite, this->getZeroValue());
return generateFromZero(CallSite);
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions include/phasar/Utils/TypeTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,29 @@ constexpr bool is_string_like_v = std::is_convertible_v<T, std::string_view>;
template <template <typename> typename Base, typename Derived>
constexpr bool is_crtp_base_of_v = // NOLINT
detail::is_crtp_base_of<Base, Derived>::value;

#if __cplusplus < 202002L
template <typename T> struct type_identity { using type = T; }; // NOLINT
#else
template <typename T> using type_identity = std::type_identity<T>;
#endif

template <typename T> using type_identity_t = typename type_identity<T>::type;

struct TrueFn {
template <typename... Args>
[[nodiscard]] bool operator()(const Args &.../*unused*/) const noexcept {
return true;
}
};

struct FalseFn {
template <typename... Args>
[[nodiscard]] bool operator()(const Args &.../*unused*/) const noexcept {
return false;
}
};

// NOLINTEND(readability-identifier-naming)
} // namespace psr

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ IDEExtendedTaintAnalysis::getNormalFlowFunction(n_t Curr,
}

if (const auto *Phi = llvm::dyn_cast<llvm::PHINode>(Curr)) {
return makeLambdaFlow<d_t>([this, Phi](d_t Source) -> std::set<d_t> {
return lambdaFlow<d_t>([this, Phi](d_t Source) -> std::set<d_t> {
auto NumOps = Phi->getNumIncomingValues();
for (unsigned I = 0; I < NumOps; ++I) {
if (equivalent(Source, makeFlowFact(Phi->getIncomingValue(I)))) {
Expand All @@ -136,8 +136,8 @@ IDEExtendedTaintAnalysis::getStoreFF(const llvm::Value *PointerOp,
PointsToInfo<v_t, n_t>::PointsToSetPtrTy PTS = nullptr;

auto Mem = makeFlowFact(PointerOp);
return makeLambdaFlow<d_t>([this, TV, Mem, PTS, PointerOp, ValueOp, Store,
PALevel](d_t Source) mutable -> std::set<d_t> {
return lambdaFlow<d_t>([this, TV, Mem, PTS, PointerOp, ValueOp, Store,
PALevel](d_t Source) mutable -> std::set<d_t> {
if (Source->isZero()) {
std::set<d_t> Ret = {Source};
generateFromZero(Ret, Store, PointerOp, ValueOp,
Expand Down Expand Up @@ -282,8 +282,8 @@ auto IDEExtendedTaintAnalysis::handleConfig(const llvm::Instruction *Inst,
populateWithMayAliases(SourceConfig);
}

return makeLambdaFlow<d_t>([Inst, this, SourceConfig{std::move(SourceConfig)},
SinkConfig{std::move(SinkConfig)}](d_t Source) {
return lambdaFlow<d_t>([Inst, this, SourceConfig{std::move(SourceConfig)},
SinkConfig{std::move(SinkConfig)}](d_t Source) {
std::set<d_t> Ret = {Source};

if (Source->isZero()) {
Expand Down Expand Up @@ -314,8 +314,8 @@ IDEExtendedTaintAnalysis::getCallFlowFunction(n_t CallStmt, f_t DestFun) {
bool HasVarargs = Call->arg_size() > DestFun->arg_size();
const auto *const VA = HasVarargs ? getVAListTagOrNull(DestFun) : nullptr;

return makeLambdaFlow<d_t>([this, Call, DestFun,
VA](d_t Source) -> std::set<d_t> {
return lambdaFlow<d_t>([this, Call, DestFun,
VA](d_t Source) -> std::set<d_t> {
if (isZeroValue(Source)) {
return {Source};
}
Expand Down Expand Up @@ -403,7 +403,7 @@ IDEExtendedTaintAnalysis::getRetFlowFunction(n_t CallSite, f_t CalleeFun,
if (!CallSite) {
/// In case of unbalanced return, we may reach the artificial Global Ctor
/// caller that has no caller
return makeEF<KillIf<d_t>>([](d_t Source) {
return killFlowIf<d_t>([](d_t Source) {
return !llvm::isa_and_nonnull<llvm::GlobalValue>(Source->base());
});
}
Expand All @@ -430,11 +430,11 @@ IDEExtendedTaintAnalysis::getRetFlowFunction(n_t CallSite, f_t CalleeFun,
};

const auto *Call = llvm::cast<llvm::CallBase>(CallSite);
return makeLambdaFlow<d_t>([this, Call, CalleeFun,
ExitStmt{llvm::cast<llvm::ReturnInst>(ExitStmt)},
PTC{ArgPointsToCache(PT, Call->arg_size(),
HasPrecisePointsToInfo)}](
d_t Source) -> std::set<d_t> {
return lambdaFlow<d_t>([this, Call, CalleeFun,
ExitStmt{llvm::cast<llvm::ReturnInst>(ExitStmt)},
PTC{ArgPointsToCache(PT, Call->arg_size(),
HasPrecisePointsToInfo)}](
d_t Source) -> std::set<d_t> {
if (isZeroValue(Source)) {
return {Source};
}
Expand Down Expand Up @@ -506,7 +506,7 @@ IDEExtendedTaintAnalysis::getCallToRetFlowFunction(
// into
// // that function

// return makeLambdaFlow<d_t>([CallSite, this](d_t Source) -> std::set<d_t>
// return lambdaFlow<d_t>([CallSite, this](d_t Source) -> std::set<d_t>
// {
// if (isZeroValue(Source)) {
// return {};
Expand Down Expand Up @@ -550,7 +550,7 @@ IDEExtendedTaintAnalysis::getCallToRetFlowFunction(
return Identity<d_t>::getInstance();
}

return makeFF<Kill<d_t>>(getZeroValue());
return killFlow(getZeroValue());
}

IDEExtendedTaintAnalysis::FlowFunctionPtrType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h"
#include "phasar/PhasarLLVM/ControlFlow/SpecialMemberFunctionType.h"
#include "phasar/PhasarLLVM/DataFlowSolver/IfdsIde/EdgeFunctions.h"
#include "phasar/PhasarLLVM/DataFlowSolver/IfdsIde/FlowFunctions.h"
#include "phasar/PhasarLLVM/DataFlowSolver/IfdsIde/LLVMFlowFunctions.h"
#include "phasar/PhasarLLVM/DataFlowSolver/IfdsIde/LLVMZeroValue.h"
#include "phasar/PhasarLLVM/DataFlowSolver/IfdsIde/Problems/IDEGeneralizedLCA/BinaryEdgeFunction.h"
Expand All @@ -37,8 +38,8 @@ using namespace glca;

template <typename Fn, typename = std::enable_if_t<
std::is_invocable_v<Fn, IDEGeneralizedLCA::d_t>>>
inline std::shared_ptr<FlowFunction<IDEGeneralizedLCA::d_t>> flow(Fn Func) {
return makeLambdaFlow<IDEGeneralizedLCA::d_t>(std::forward<Fn>(Func));
inline auto flow(Fn Func) {
return lambdaFlow<IDEGeneralizedLCA::d_t>(std::forward<Fn>(Func));
}

IDEGeneralizedLCA::IDEGeneralizedLCA(const LLVMProjectIRDB *IRDB,
Expand All @@ -59,9 +60,10 @@ IDEGeneralizedLCA::getNormalFlowFunction(IDEGeneralizedLCA::n_t Curr,
const auto *ValueOp = Store->getValueOperand();
if (isConstant(ValueOp)) {
// llvm::outs() << "==> constant store" << std::endl;
return flow([=](IDEGeneralizedLCA::d_t Source)
-> std::set<IDEGeneralizedLCA::d_t> {
// llvm::outs() << "##> normal flow for: " << llvmIRToString(curr)
return lambdaFlow<d_t>([=](IDEGeneralizedLCA::d_t Source)
-> std::set<IDEGeneralizedLCA::d_t> {
// llvm::outs() << "##> normal lambdaFlow<d_t> for: " <<
// llvmIRToString(curr)
// << " with " << llvmIRToString(source) << std::endl;
if (Source == PointerOp) {
return {};
Expand All @@ -72,7 +74,7 @@ IDEGeneralizedLCA::getNormalFlowFunction(IDEGeneralizedLCA::n_t Curr,
return {Source};
});
}
return flow(
return lambdaFlow<d_t>(
[=](IDEGeneralizedLCA::d_t Source) -> std::set<IDEGeneralizedLCA::d_t> {
if (Source == PointerOp) {
return {};
Expand All @@ -84,7 +86,7 @@ IDEGeneralizedLCA::getNormalFlowFunction(IDEGeneralizedLCA::n_t Curr,
});
}
if (const auto *Load = llvm::dyn_cast<llvm::LoadInst>(Curr)) {
return flow(
return lambdaFlow<d_t>(
[=](IDEGeneralizedLCA::d_t Source) -> std::set<IDEGeneralizedLCA::d_t> {
// llvm::outs() << "LOAD " << llvmIRToString(curr) << std::endl;
// llvm::outs() << "\twith " << llvmIRToString(source) << " ==> ";
Expand All @@ -97,7 +99,7 @@ IDEGeneralizedLCA::getNormalFlowFunction(IDEGeneralizedLCA::n_t Curr,
});
}
if (const auto *Gep = llvm::dyn_cast<llvm::GetElementPtrInst>(Curr)) {
return flow(
return lambdaFlow<d_t>(
[=](IDEGeneralizedLCA::d_t Source) -> std::set<IDEGeneralizedLCA::d_t> {
if (Source == Gep->getPointerOperand()) {
return {Source, Gep};
Expand All @@ -111,7 +113,7 @@ IDEGeneralizedLCA::getNormalFlowFunction(IDEGeneralizedLCA::n_t Curr,
Cast->getSrcTy()->isFloatingPointTy()) &&
(Cast->getDestTy()->isIntegerTy() ||
Cast->getDestTy()->isFloatingPointTy())) {
return flow(
return lambdaFlow<d_t>(
[=](IDEGeneralizedLCA::d_t Source) -> std::set<IDEGeneralizedLCA::d_t> {
if (Source == Cast->getOperand(0)) {
return {Source, Cast};
Expand All @@ -127,7 +129,7 @@ IDEGeneralizedLCA::getNormalFlowFunction(IDEGeneralizedLCA::n_t Curr,
bool BothConst = LeftConst && RightConst;
bool NoneConst = !LeftConst && !RightConst;

return flow(
return lambdaFlow<d_t>(
[=](IDEGeneralizedLCA::d_t Source) -> std::set<IDEGeneralizedLCA::d_t> {
if (Source == Lhs || Source == Rhs ||
((BothConst || NoneConst) && isZeroValue(Source))) {
Expand All @@ -137,7 +139,7 @@ IDEGeneralizedLCA::getNormalFlowFunction(IDEGeneralizedLCA::n_t Curr,
});
} /*else if (llvm::isa<llvm::UnaryOperator>(curr)) {
auto op = curr->getOperand(0);
return flow([=](IDEGeneralizedLCA::d_t source)
return lambdaFlow<d_t>([=](IDEGeneralizedLCA::d_t source)
-> std::set<IDEGeneralizedLCA::d_t> {
if (source == op)
return {source, curr};
Expand All @@ -155,7 +157,7 @@ IDEGeneralizedLCA::getCallFlowFunction(IDEGeneralizedLCA::n_t CallStmt,
assert(llvm::isa<llvm::CallBase>(CallStmt));
if (isStringConstructor(DestMthd)) {
// kill all data-flow facts at calls to string constructors
return KillAll<IDEGeneralizedLCA::d_t>::getInstance();
return killAllFlows<d_t>();
}
return std::make_shared<MapFactsToCalleeFlowFunction>(
llvm::cast<llvm::CallBase>(CallStmt), DestMthd);
Expand All @@ -167,7 +169,8 @@ IDEGeneralizedLCA::getRetFlowFunction(IDEGeneralizedLCA::n_t CallSite,
IDEGeneralizedLCA::n_t ExitStmt,
IDEGeneralizedLCA::n_t /*RetSite*/) {
assert(llvm::isa<llvm::CallBase>(CallSite));
// llvm::outs() << "Ret flow: " << llvmIRToString(ExitStmt) << std::endl;
// llvm::outs() << "Ret flow: " << llvmIRToString(ExitStmt) <<
// std::endl;
/*return std::make_shared<MapFactsToCaller>(
llvm::ImmutableCallSite(callSite), calleeMthd, exitStmt,
[](const llvm::Value *v) -> bool {
Expand All @@ -181,16 +184,16 @@ std::shared_ptr<FlowFunction<IDEGeneralizedLCA::d_t>>
IDEGeneralizedLCA::getCallToRetFlowFunction(IDEGeneralizedLCA::n_t CallSite,
IDEGeneralizedLCA::n_t /*RetSite*/,
llvm::ArrayRef<f_t> /*Callees*/) {
// llvm::outs() << "CTR flow: " << llvmIRToString(CallSite) << std::endl;
// llvm::outs() << "CTR flow: " << llvmIRToString(CallSite) <<
// std::endl;
if (const auto *CS = llvm::dyn_cast<llvm::CallBase>(CallSite)) {
// check for ctor and then demangle function name and check for
// std::basic_string
if (isStringConstructor(CS->getCalledFunction())) {
// found std::string ctor
return std::make_shared<Gen<IDEGeneralizedLCA::d_t>>(CS->getArgOperand(0),
getZeroValue());
return generateFromZero(CS->getArgOperand(0));
}
// return flow([Call](IDEGeneralizedLCA::d_t Source)
// return lambdaFlow<d_t>([Call](IDEGeneralizedLCA::d_t Source)
// -> std::set<IDEGeneralizedLCA::d_t> {
// // llvm::outs() << "In getCallToRetFlowFunction\n";
// // llvm::outs() << llvmIRToString(Source) << '\n';
Expand Down
Loading

0 comments on commit c484261

Please sign in to comment.