diff --git a/include/phasar/ControlFlow/CallGraph.h b/include/phasar/ControlFlow/CallGraph.h index e2a03fec25..79350de642 100644 --- a/include/phasar/ControlFlow/CallGraph.h +++ b/include/phasar/ControlFlow/CallGraph.h @@ -177,8 +177,7 @@ template class CallGraphBuilder { [[nodiscard]] FunctionVertexTy *addFunctionVertex(f_t Fun) { auto [It, Inserted] = CG.CallersOf.try_emplace(std::move(Fun), nullptr); if (Inserted) { - auto Cap = CG.FunVertexOwner.capacity(); - assert(CG.FunVertexOwner.size() < Cap && + assert(CG.FunVertexOwner.size() < CG.FunVertexOwner.capacity() && "Trying to add more than MaxNumFunctions Function Vertices"); It->second = &CG.FunVertexOwner.emplace_back(); } diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h new file mode 100644 index 0000000000..9a73977f97 --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h @@ -0,0 +1,158 @@ +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDEALIASINFOTABULATIONPROBLEM_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDEALIASINFOTABULATIONPROBLEM_H + +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" + +#include + +// Forward declaration of types for which we only use its pointer or ref type +namespace llvm { +class Instruction; +class Function; +class Value; +} // namespace llvm + +namespace psr { + +namespace detail { +class IDEAliasAwareDefaultFlowFunctionsImpl + : private IDENoAliasDefaultFlowFunctionsImpl { +public: + using typename IDENoAliasDefaultFlowFunctionsImpl::d_t; + using typename IDENoAliasDefaultFlowFunctionsImpl::f_t; + using typename IDENoAliasDefaultFlowFunctionsImpl::FlowFunctionPtrType; + using typename IDENoAliasDefaultFlowFunctionsImpl::FlowFunctionType; + using typename IDENoAliasDefaultFlowFunctionsImpl::n_t; + + using IDENoAliasDefaultFlowFunctionsImpl::isFunctionModeled; + + [[nodiscard]] constexpr LLVMAliasInfoRef getAliasInfo() const noexcept { + return AS; + } + + constexpr IDEAliasAwareDefaultFlowFunctionsImpl(LLVMAliasInfoRef AS) noexcept + : AS(AS) { + assert(AS && "You must provide an alias information handle!"); + } + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunctionImpl(n_t Curr, + n_t /*Succ*/); + [[nodiscard]] FlowFunctionPtrType getCallFlowFunctionImpl(n_t CallInst, + f_t CalleeFun); + [[nodiscard]] FlowFunctionPtrType getRetFlowFunctionImpl(n_t CallSite, + f_t /*CalleeFun*/, + n_t ExitInst, + n_t /*RetSite*/); + [[nodiscard]] FlowFunctionPtrType + getCallToRetFlowFunctionImpl(n_t CallSite, n_t /*RetSite*/, + llvm::ArrayRef /*Callees*/); + +private: + LLVMAliasInfoRef AS; +}; +} // namespace detail + +template +class DefaultAliasAwareIDEProblem + : public IDETabulationProblem, + protected detail::IDEAliasAwareDefaultFlowFunctionsImpl { +public: + using ProblemAnalysisDomain = AnalysisDomainTy; + using d_t = typename AnalysisDomainTy::d_t; + using n_t = typename AnalysisDomainTy::n_t; + using f_t = typename AnalysisDomainTy::f_t; + using t_t = typename AnalysisDomainTy::t_t; + using v_t = typename AnalysisDomainTy::v_t; + using l_t = typename AnalysisDomainTy::l_t; + using i_t = typename AnalysisDomainTy::i_t; + using db_t = typename AnalysisDomainTy::db_t; + + using ConfigurationTy = HasNoConfigurationType; + + using FlowFunctionType = FlowFunction; + using FlowFunctionPtrType = typename FlowFunctionType::FlowFunctionPtrType; + + using container_type = typename FlowFunctionType::container_type; + + /// Constructs an IDETabulationProblem with the usual arguments + alias + /// information. + /// + /// \note It is useful to use an instance of FilteredAliasSet for the alias + /// information to lower suprious aliases + explicit DefaultAliasAwareIDEProblem( + const ProjectIRDBBase *IRDB, LLVMAliasInfoRef AS, + std::vector EntryPoints, + std::optional + ZeroValue) noexcept(std::is_nothrow_move_constructible_v) + : IDETabulationProblem(IRDB, std::move(EntryPoints), + std::move(ZeroValue)), + detail::IDEAliasAwareDefaultFlowFunctionsImpl(AS) {} + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, + n_t Succ) override { + return getNormalFlowFunctionImpl(Curr, Succ); + } + + [[nodiscard]] FlowFunctionPtrType + getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { + return getCallFlowFunctionImpl(CallInst, CalleeFun); + } + + [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, + f_t CalleeFun, + n_t ExitInst, + n_t RetSite) override { + return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); + } + + [[nodiscard]] FlowFunctionPtrType + getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees) override { + return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); + } +}; + +class DefaultAliasAwareIFDSProblem + : public IFDSTabulationProblem, + protected detail::IDEAliasAwareDefaultFlowFunctionsImpl { +public: + /// Constructs an IFDSTabulationProblem with the usual arguments + alias + /// information. + /// + /// \note It is useful to use an instance of FilteredAliasSet for the alias + /// information to lower suprious aliases + explicit DefaultAliasAwareIFDSProblem( + const ProjectIRDBBase *IRDB, LLVMAliasInfoRef AS, + std::vector EntryPoints, + d_t ZeroValue) noexcept(std::is_nothrow_move_constructible_v) + : IFDSTabulationProblem(IRDB, std::move(EntryPoints), ZeroValue), + detail::IDEAliasAwareDefaultFlowFunctionsImpl(AS) {} + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, + n_t Succ) override { + return getNormalFlowFunctionImpl(Curr, Succ); + } + + [[nodiscard]] FlowFunctionPtrType + getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { + return getCallFlowFunctionImpl(CallInst, CalleeFun); + } + + [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, + f_t CalleeFun, + n_t ExitInst, + n_t RetSite) override { + return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); + } + + [[nodiscard]] FlowFunctionPtrType + getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees) override { + return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); + } +}; + +} // namespace psr + +#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h new file mode 100644 index 0000000000..1865f749f2 --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h @@ -0,0 +1,124 @@ +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDENOALIASINFOTABULATIONPROBLEM_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDENOALIASINFOTABULATIONPROBLEM_H + +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" +#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" + +namespace llvm { +class Value; +class Instruction; +class Function; +} // namespace llvm + +namespace psr { + +namespace detail { +class IDENoAliasDefaultFlowFunctionsImpl { +public: + using d_t = const llvm::Value *; + using n_t = const llvm::Instruction *; + using f_t = const llvm::Function *; + using FlowFunctionType = FlowFunction; + using FlowFunctionPtrType = typename FlowFunctionType::FlowFunctionPtrType; + + virtual ~IDENoAliasDefaultFlowFunctionsImpl() = default; + + /// True, if the analysis knows this function, either because it is analyzed, + /// or because we have external information about it. + [[nodiscard]] virtual bool isFunctionModeled(f_t Fun) const; + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunctionImpl(n_t Curr, + n_t /*Succ*/); + [[nodiscard]] FlowFunctionPtrType getCallFlowFunctionImpl(n_t CallInst, + f_t CalleeFun); + [[nodiscard]] FlowFunctionPtrType getRetFlowFunctionImpl(n_t CallSite, + f_t /*CalleeFun*/, + n_t ExitInst, + n_t /*RetSite*/); + [[nodiscard]] FlowFunctionPtrType + getCallToRetFlowFunctionImpl(n_t CallSite, n_t /*RetSite*/, + llvm::ArrayRef /*Callees*/); +}; +} // namespace detail + +template +class DefaultNoAliasIDEProblem + : public IDETabulationProblem, + protected detail::IDENoAliasDefaultFlowFunctionsImpl { +public: + using ProblemAnalysisDomain = AnalysisDomainTy; + using d_t = typename AnalysisDomainTy::d_t; + using n_t = typename AnalysisDomainTy::n_t; + using f_t = typename AnalysisDomainTy::f_t; + using t_t = typename AnalysisDomainTy::t_t; + using v_t = typename AnalysisDomainTy::v_t; + using l_t = typename AnalysisDomainTy::l_t; + using i_t = typename AnalysisDomainTy::i_t; + using db_t = typename AnalysisDomainTy::db_t; + + using ConfigurationTy = HasNoConfigurationType; + + using FlowFunctionType = FlowFunction; + using FlowFunctionPtrType = typename FlowFunctionType::FlowFunctionPtrType; + + using IDETabulationProblem::IDETabulationProblem; + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, + n_t Succ) override { + return getNormalFlowFunctionImpl(Curr, Succ); + } + + [[nodiscard]] FlowFunctionPtrType + getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { + return getCallFlowFunctionImpl(CallInst, CalleeFun); + } + + [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, + f_t CalleeFun, + n_t ExitInst, + n_t RetSite) override { + return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); + } + + [[nodiscard]] FlowFunctionPtrType + getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees) override { + return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); + } +}; + +class DefaultNoAliasIFDSProblem + : public IFDSTabulationProblem, + protected detail::IDENoAliasDefaultFlowFunctionsImpl { +public: + using IFDSTabulationProblem::IFDSTabulationProblem; + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, + n_t Succ) override { + return getNormalFlowFunctionImpl(Curr, Succ); + } + + [[nodiscard]] FlowFunctionPtrType + getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { + return getCallFlowFunctionImpl(CallInst, CalleeFun); + } + + [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, + f_t CalleeFun, + n_t ExitInst, + n_t RetSite) override { + return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); + } + + [[nodiscard]] FlowFunctionPtrType + getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees) override { + return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); + } +}; + +} // namespace psr + +#endif diff --git a/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h index 91599637c9..b18cadbecb 100644 --- a/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h @@ -66,7 +66,8 @@ class FilteredLLVMAliasSet { typename = std::enable_if_t< std::is_constructible_v>> explicit FilteredLLVMAliasSet(ArgsT &&...Args) - : FilteredLLVMAliasSet(std::forward(Args)...) {} + : FilteredLLVMAliasSet( + std::make_unique(std::forward(Args)...)) {} // --- API Functions: diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp new file mode 100644 index 0000000000..ed18352cee --- /dev/null +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp @@ -0,0 +1,102 @@ +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" + +#include "llvm/IR/Instructions.h" + +using namespace psr; + +using FFTemplates = + FlowFunctionTemplates; +using container_type = FFTemplates::container_type; + +auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( + n_t Curr, n_t Succ) -> FlowFunctionPtrType { + + if (const auto *Store = llvm::dyn_cast(Curr)) { + container_type Gen; + + auto AliasSet = AS.getAliasSet(Store->getPointerOperand(), Store); + Gen.insert(AliasSet->begin(), AliasSet->end()); + Gen.insert(Store->getValueOperand()); + + return FFTemplates::lambdaFlow( + [Store, Gen{std::move(Gen)}](d_t Source) -> container_type { + if (Store->getPointerOperand() == Source) { + return {}; + } + if (Store->getValueOperand() == Source) { + return Gen; + } + + return {Source}; + }); + } + + return this->IDENoAliasDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( + Curr, Succ); +} + +auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getCallFlowFunctionImpl( + n_t CallInst, f_t CalleeFun) -> FlowFunctionPtrType { + return this->IDENoAliasDefaultFlowFunctionsImpl::getCallFlowFunctionImpl( + CallInst, CalleeFun); +} + +static void populateWithMayAliases(LLVMAliasInfoRef AS, container_type &Facts, + const llvm::Instruction *Context) { + container_type Tmp = Facts; + for (const auto *Fact : Facts) { + auto Aliases = AS.getAliasSet(Fact, Context); + for (const auto *Alias : *Aliases) { + if (const auto *Inst = llvm::dyn_cast(Alias)) { + if (Inst->getParent() == Context->getParent() && + Context->comesBefore(Inst)) { + // We will see that inst later + continue; + } + } + + if (const auto *Load = llvm::dyn_cast(Alias)) { + // Handle at least one level of indirection... + const auto *PointerOp = Load->getPointerOperand()->stripPointerCasts(); + Tmp.insert(PointerOp); + } + + Tmp.insert(Alias); + } + } + + Facts = std::move(Tmp); +} + +auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getRetFlowFunctionImpl( + n_t CallSite, f_t /*CalleeFun*/, n_t ExitInst, n_t /*RetSite*/) + -> FlowFunctionPtrType { + container_type Gen; + + if (const auto *Call = llvm::dyn_cast(CallSite)) { + const auto PostProcessFacts = [AS = AS, Call](container_type &Facts) { + populateWithMayAliases(AS, Facts, Call); + }; + + return mapFactsToCaller( + Call, ExitInst, + [](d_t Param, d_t Source) { + return Param == Source && Param->getType()->isPointerTy(); + }, + {}, {}, true, true, PostProcessFacts); + } + + return FFTemplates::killAllFlows(); +} + +auto detail::IDEAliasAwareDefaultFlowFunctionsImpl:: + getCallToRetFlowFunctionImpl(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees) + -> FlowFunctionPtrType { + return this->IDENoAliasDefaultFlowFunctionsImpl::getCallToRetFlowFunctionImpl( + CallSite, RetSite, Callees); +} diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultNoaliasIDEFlowFunctions.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultNoaliasIDEFlowFunctions.cpp new file mode 100644 index 0000000000..538fb22924 --- /dev/null +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultNoaliasIDEFlowFunctions.cpp @@ -0,0 +1,87 @@ +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Operator.h" + +using namespace psr; + +using FFTemplates = + FlowFunctionTemplates; + +bool detail::IDENoAliasDefaultFlowFunctionsImpl::isFunctionModeled( + f_t Fun) const { + return !Fun->isDeclaration(); +} + +auto detail::IDENoAliasDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( + n_t Curr, n_t /*Succ*/) -> FlowFunctionPtrType { + + if (const auto *Store = llvm::dyn_cast(Curr)) { + return strongUpdateStore(Store); + } + if (const auto *Load = llvm::dyn_cast(Curr)) { + return FFTemplates::generateFlowIf(Load, [Load](d_t Source) { + return Source == Load->getPointerOperand(); + }); + } + if (const auto *UnaryOp = llvm::dyn_cast(Curr)) { + return FFTemplates::generateFlow(UnaryOp, UnaryOp->getOperand(0)); + } + if (const auto *BinaryOp = llvm::dyn_cast(Curr)) { + return FFTemplates::generateFlowIf(BinaryOp, [BinaryOp](d_t Source) { + return Source == BinaryOp->getOperand(0) || + Source == BinaryOp->getOperand(1); + }); + } + if (const auto *GetElementPtr = llvm::dyn_cast(Curr)) { + return FFTemplates::generateFlow(GetElementPtr, + GetElementPtr->getPointerOperand()); + } + + return FFTemplates::identityFlow(); +} + +auto detail::IDENoAliasDefaultFlowFunctionsImpl::getCallFlowFunctionImpl( + n_t CallInst, f_t CalleeFun) -> FlowFunctionPtrType { + if (const auto *CallSite = llvm::dyn_cast(CallInst)) { + return mapFactsToCallee(CallSite, CalleeFun); + } + + return FFTemplates::killAllFlows(); +} + +auto detail::IDENoAliasDefaultFlowFunctionsImpl::getRetFlowFunctionImpl( + n_t CallSite, f_t /*CalleeFun*/, n_t ExitInst, n_t /*RetSite*/) + -> FlowFunctionPtrType { + + if (const auto *Call = llvm::dyn_cast(CallSite)) { + return mapFactsToCaller(Call, ExitInst, [](d_t Param, d_t Source) { + return Param == Source && Param->getType()->isPointerTy(); + }); + } + + return FFTemplates::killAllFlows(); +} + +auto detail::IDENoAliasDefaultFlowFunctionsImpl::getCallToRetFlowFunctionImpl( + n_t CallSite, n_t /*RetSite*/, llvm::ArrayRef Callees) + -> FlowFunctionPtrType { + + if (llvm::any_of(Callees, + [this](f_t Callee) { return !isFunctionModeled(Callee); })) { + // Cannot strongly update, if we don't know the callee + return FFTemplates::identityFlow(); + } + + if (const auto *Call = llvm::dyn_cast(CallSite)) { + return mapFactsAlongsideCallSite( + Call, [](d_t Arg) { return !Arg->getType()->isPointerTy(); }, false); + } + + return FFTemplates::identityFlow(); +} diff --git a/test/llvm_test_code/pure_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/CMakeLists.txt new file mode 100644 index 0000000000..60b08c0034 --- /dev/null +++ b/test/llvm_test_code/pure_flow/CMakeLists.txt @@ -0,0 +1,18 @@ +set(NoMem2regSources + all_flow_functions_01.cpp + int_call_no_params_01.cpp + int_call_one_param_01.cpp + int_call_pointer_param_01.cpp + void_call_no_params_01.cpp + void_call_one_param_01.cpp + void_call_pointer_param_01.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) + +subdirlist(subdirs ${CMAKE_CURRENT_SOURCE_DIR}) +foreach(subdir ${subdirs}) + add_subdirectory(${subdir}) +endforeach(subdir) diff --git a/test/llvm_test_code/pure_flow/all_flow_functions_01.cpp b/test/llvm_test_code/pure_flow/all_flow_functions_01.cpp new file mode 100644 index 0000000000..0cf43f662d --- /dev/null +++ b/test/llvm_test_code/pure_flow/all_flow_functions_01.cpp @@ -0,0 +1,15 @@ +#include + +int call(const int *One, const int Second) { + int ToReturn = 1; + printf("call(%d, %d)\nreturn %d", *One, Second, ToReturn); + return ToReturn; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int Two = 2; + int ReturnInt = call(&One, Two); + printf("ReturnInt: %d\n", ReturnInt); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/call_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/call_flow/CMakeLists.txt new file mode 100644 index 0000000000..5798e97fbf --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_flow/CMakeLists.txt @@ -0,0 +1,8 @@ +set(NoMem2regSources + call_flow_01.cpp + call_flow_02.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) diff --git a/test/llvm_test_code/pure_flow/call_flow/call_flow_01.cpp b/test/llvm_test_code/pure_flow/call_flow/call_flow_01.cpp new file mode 100644 index 0000000000..e21f965083 --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_flow/call_flow_01.cpp @@ -0,0 +1,12 @@ +#include + +void call(int ZeroArg, int OneArg) {} + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + int Two = 2; + + call(Zero, One); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/call_flow/call_flow_02.cpp b/test/llvm_test_code/pure_flow/call_flow/call_flow_02.cpp new file mode 100644 index 0000000000..90c5f6304f --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_flow/call_flow_02.cpp @@ -0,0 +1,23 @@ +#include + +void call(int *One) {} +void secondCall(int *One, int **Two, int ***Three) {} + +int main(int /*argc*/, char * /*argv*/[]) { + + int One = 1; + int Two = 2; + int Three = 3; + + int *PtrToOne = &One; + int *PtrToTwo = &Two; + int **PtrPtrToTwo = &PtrToTwo; + int *PtrToThree = &Three; + int **PtrPtrToThree = &PtrToThree; + int ***PtrPtrPtrToThree = &PtrPtrToThree; + + call(PtrToOne); + secondCall(PtrToOne, PtrPtrToTwo, PtrPtrPtrToThree); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/call_to_ret_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/call_to_ret_flow/CMakeLists.txt new file mode 100644 index 0000000000..36d041b6ed --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_to_ret_flow/CMakeLists.txt @@ -0,0 +1,8 @@ +set(NoMem2regSources + call_to_ret_flow_01.cpp + call_to_ret_flow_02.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) diff --git a/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_01.cpp b/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_01.cpp new file mode 100644 index 0000000000..1797a073c7 --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_01.cpp @@ -0,0 +1,12 @@ +#include + +int call(int Zero, int One) { return Zero + One; } + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + + int CallReturn = call(Zero, One); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_02.cpp b/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_02.cpp new file mode 100644 index 0000000000..4d014bcfa3 --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_02.cpp @@ -0,0 +1,19 @@ +#include + +void callTwo() {} +void callOne() {} +void call() { + int Three = 3; + callOne(); + int Four = 4; + callTwo(); +} + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + call(); + int Two = 2; + callOne(); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/int_call_no_params_01.cpp b/test/llvm_test_code/pure_flow/int_call_no_params_01.cpp new file mode 100644 index 0000000000..d2b3277a75 --- /dev/null +++ b/test/llvm_test_code/pure_flow/int_call_no_params_01.cpp @@ -0,0 +1,12 @@ +#include + +int call() { + printf("call()\n"); + return 1; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int ReturnInt = call(); + printf("ReturnInt: %d\n", ReturnInt); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/int_call_one_param_01.cpp b/test/llvm_test_code/pure_flow/int_call_one_param_01.cpp new file mode 100644 index 0000000000..97912f62fe --- /dev/null +++ b/test/llvm_test_code/pure_flow/int_call_one_param_01.cpp @@ -0,0 +1,12 @@ +#include + +int call(int Integer) { + printf("call(%d)\n", Integer); + return 1; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int ReturnInt = call(1); + printf("ReturnInt: %d\n", ReturnInt); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/int_call_pointer_param_01.cpp b/test/llvm_test_code/pure_flow/int_call_pointer_param_01.cpp new file mode 100644 index 0000000000..0d7a114460 --- /dev/null +++ b/test/llvm_test_code/pure_flow/int_call_pointer_param_01.cpp @@ -0,0 +1,13 @@ +#include + +int call(const int *Integer) { + printf("call(%d)\n", *Integer); + return 1; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int ReturnInt = call(&One); + printf("ReturnInt: %d\n", ReturnInt); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/normal_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/normal_flow/CMakeLists.txt new file mode 100644 index 0000000000..225ec21a61 --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/CMakeLists.txt @@ -0,0 +1,10 @@ +set(NoMem2regSources + normal_flow_01.cpp + normal_flow_02.cpp + normal_flow_03.cpp + normal_flow_04.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) diff --git a/test/llvm_test_code/pure_flow/normal_flow/normal_flow_01.cpp b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_01.cpp new file mode 100644 index 0000000000..a788a4187c --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_01.cpp @@ -0,0 +1,9 @@ +#include + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int Two = 2; + int *OnePtr = &One; + int &TwoAddr = Two; + return 0; +} diff --git a/test/llvm_test_code/pure_flow/normal_flow/normal_flow_02.cpp b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_02.cpp new file mode 100644 index 0000000000..965f099801 --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_02.cpp @@ -0,0 +1,14 @@ +#include + +void call() {} + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int Two = 2; + + call(); + + int Three = 3; + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/normal_flow/normal_flow_03.cpp b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_03.cpp new file mode 100644 index 0000000000..2bcbe573ba --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_03.cpp @@ -0,0 +1,16 @@ +#include + +struct StructOne { + int One = 1; +}; + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int OtherOne = One; + int MinusOne = !One; + int Two = One + One; + int *PtrToOne = &One; + StructOne ForGEP = StructOne(); + int GEP = ForGEP.One; + return 0; +} diff --git a/test/llvm_test_code/pure_flow/normal_flow/normal_flow_04.cpp b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_04.cpp new file mode 100644 index 0000000000..9012516ff0 --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_04.cpp @@ -0,0 +1,15 @@ +#include + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + + int *PtrToOne = &One; + int **PtrPtrToOne = &PtrToOne; + int ***PtrPtrPtrToOne = &PtrPtrToOne; + + int Deref1 = *PtrToOne; + int Deref2 = **PtrPtrToOne; + int Deref3 = ***PtrPtrPtrToOne; + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/ret_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/ret_flow/CMakeLists.txt new file mode 100644 index 0000000000..018af3009b --- /dev/null +++ b/test/llvm_test_code/pure_flow/ret_flow/CMakeLists.txt @@ -0,0 +1,9 @@ +set(NoMem2regSources + ret_flow_01.cpp + ret_flow_02.cpp + ret_flow_03.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) diff --git a/test/llvm_test_code/pure_flow/ret_flow/ret_flow_01.cpp b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_01.cpp new file mode 100644 index 0000000000..a3e3d0a6a0 --- /dev/null +++ b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_01.cpp @@ -0,0 +1,18 @@ +#include + +int getTwo() { return 2; } + +int call(int ZeroArg, int OneArg) { + int TwoInCall = getTwo(); + return ZeroArg + OneArg; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + int Two = 2; + + int CallReturn = call(Zero, One); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/ret_flow/ret_flow_02.cpp b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_02.cpp new file mode 100644 index 0000000000..06dc62def8 --- /dev/null +++ b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_02.cpp @@ -0,0 +1,27 @@ +#include + +int getTwo() { return 2; } + +int newThree() { + int FourInFunc = 4; + return 3; +} + +int call(int ZeroArg, int OneArg) { + int TwoInCall = getTwo(); + int ThreeInCall = 3; + + ThreeInCall = newThree(); + + return ZeroArg + OneArg; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + int Two = 2; + + int CallReturn = call(Zero, One); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/ret_flow/ret_flow_03.cpp b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_03.cpp new file mode 100644 index 0000000000..d540ffc27b --- /dev/null +++ b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_03.cpp @@ -0,0 +1,32 @@ +#include + +int GlobalFour = 4; + +const int *newThree(const int *ThreeArg) { + const int *NewThreePtrInFunc = &(*ThreeArg); + return NewThreePtrInFunc; +} + +int *getFourPtr() { return &GlobalFour; } + +int &getFourAddr() { return GlobalFour; } + +int call(int &ZeroArg, const int *OneArg) { + int TwoInCall = 2; + int ThreeInCall = 3; + int *ThreePtrInCall = &ThreeInCall; + const int *NewThreeInCall = newThree(ThreePtrInCall); + + return ZeroArg + *OneArg + TwoInCall + *NewThreeInCall + *getFourPtr() + + getFourAddr(); +} + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + int Two = 2; + + int CallReturn = call(Zero, &One); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/void_call_no_params_01.cpp b/test/llvm_test_code/pure_flow/void_call_no_params_01.cpp new file mode 100644 index 0000000000..3c3111e249 --- /dev/null +++ b/test/llvm_test_code/pure_flow/void_call_no_params_01.cpp @@ -0,0 +1,8 @@ +#include + +void call() { printf("call()"); } + +int main(int /*argc*/, char * /*argv*/[]) { + call(); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/void_call_one_param_01.cpp b/test/llvm_test_code/pure_flow/void_call_one_param_01.cpp new file mode 100644 index 0000000000..71589e5f83 --- /dev/null +++ b/test/llvm_test_code/pure_flow/void_call_one_param_01.cpp @@ -0,0 +1,8 @@ +#include + +void call(int Integer) { printf("call(%d)\n", Integer); } + +int main(int /*argc*/, char * /*argv*/[]) { + call(1); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/void_call_pointer_param_01.cpp b/test/llvm_test_code/pure_flow/void_call_pointer_param_01.cpp new file mode 100644 index 0000000000..0b181aee9d --- /dev/null +++ b/test/llvm_test_code/pure_flow/void_call_pointer_param_01.cpp @@ -0,0 +1,9 @@ +#include + +void call(const int *Integer) { printf("call(%d)\n", *Integer); } + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + call(&One); + return 0; +} diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt b/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt index 5c191d3856..dcda34ffdd 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(Problems) set(IfdsIdeSources + DefaultFlowFunctionTest.cpp EdgeFunctionComposerTest.cpp EdgeFunctionSingletonCacheTest.cpp InteractiveIDESolverTest.cpp diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp new file mode 100644 index 0000000000..71d12e3129 --- /dev/null +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp @@ -0,0 +1,852 @@ +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/raw_ostream.h" + +#include "TestConfig.h" +#include "gtest/gtest.h" + +#include + +using namespace psr; + +namespace { + +class IDEAliasImpl : public DefaultAliasAwareIFDSProblem { +public: + IDEAliasImpl(LLVMProjectIRDB *IRDB) + : DefaultAliasAwareIFDSProblem(IRDB, &PT, {}, {}), PT(IRDB){}; + + [[nodiscard]] InitialSeeds initialSeeds() override { + return {}; + }; + +private: + FilteredLLVMAliasSet PT; +}; + +class IDENoAliasImpl : public DefaultNoAliasIFDSProblem { +public: + IDENoAliasImpl(LLVMProjectIRDB *IRDB) + : DefaultNoAliasIFDSProblem(IRDB, {}, {}){}; + + [[nodiscard]] InitialSeeds initialSeeds() override { + return {}; + }; +}; + +std::set +getNormalFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, + const llvm::Value *Arg) { + const auto AliasNormalFlowFunc = + AliasImpl.getNormalFlowFunction(Instr, nullptr); + const auto AliasLLVMValueSet = AliasNormalFlowFunc->computeTargets(Arg); + return AliasLLVMValueSet; +} + +std::set +getNormalFlowValueSet(const llvm::Instruction *Instr, + IDENoAliasImpl &NoAliasImpl, const llvm::Value *Arg) { + const auto NoAliasNormalFlowFunc = + NoAliasImpl.getNormalFlowFunction(Instr, nullptr); + const auto NoAliasLLVMValueSet = NoAliasNormalFlowFunc->computeTargets(Arg); + return NoAliasLLVMValueSet; +} + +std::set +getCallFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, + const llvm::Value *Arg, const llvm::Function *CalleeFunc) { + const auto AliasCallFlowFunc = + AliasImpl.getCallFlowFunction(Instr, CalleeFunc); + std::set AliasLLVMValueSet = + AliasCallFlowFunc->computeTargets(Arg); + return AliasLLVMValueSet; +} + +std::set +getCallFlowValueSet(const llvm::Instruction *Instr, IDENoAliasImpl &NoAliasImpl, + const llvm::Value *Arg, const llvm::Function *CalleeFunc) { + const auto NoAliasCallFlowFunc = + NoAliasImpl.getCallFlowFunction(Instr, CalleeFunc); + std::set NoAliasLLVMValueSet = + NoAliasCallFlowFunc->computeTargets(Arg); + return NoAliasLLVMValueSet; +} + +std::set +getRetFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, + const llvm::Value *Arg, const llvm::Instruction *ExitInst) { + const auto AliasRetFlowFunc = + AliasImpl.getRetFlowFunction(Instr, nullptr, ExitInst, nullptr); + std::set AliasLLVMValueSet = + AliasRetFlowFunc->computeTargets(Arg); + return AliasLLVMValueSet; +} + +std::set +getRetFlowValueSet(const llvm::Instruction *Instr, IDENoAliasImpl &NoAliasImpl, + const llvm::Value *Arg, const llvm::Instruction *ExitInst) { + const auto NoAliasRetFlowFunc = + NoAliasImpl.getRetFlowFunction(Instr, nullptr, ExitInst, nullptr); + std::set NoAliasLLVMValueSet = + NoAliasRetFlowFunc->computeTargets(Arg); + return NoAliasLLVMValueSet; +} + +std::set +getCallToRetFlowValueSet(const llvm::Instruction *Instr, + IDEAliasImpl &AliasImpl, const llvm::Value *Arg) { + const auto AliasCallToRetFlowFunc = + AliasImpl.getCallToRetFlowFunction(Instr, nullptr, {}); + const auto AliasLLVMValueSet = AliasCallToRetFlowFunc->computeTargets(Arg); + return AliasLLVMValueSet; +} + +std::set +getCallToRetFlowValueSet(const llvm::Instruction *Instr, + IDENoAliasImpl &NoAliasImpl, const llvm::Value *Arg) { + const auto NoAliasCallToRetFlowFunc = + NoAliasImpl.getCallToRetFlowFunction(Instr, nullptr, {}); + const auto NoAliasLLVMValueSet = + NoAliasCallToRetFlowFunc->computeTargets(Arg); + return NoAliasLLVMValueSet; +} + +std::string stringifyValueSet(const std::set &Vals) { + std::string Ret; + llvm::raw_string_ostream ROS(Ret); + + ROS << "{ "; + + llvm::interleaveComma( + Vals, ROS, [&ROS](const auto *Val) { ROS << llvmIRToString(Val); }); + + ROS << " }"; + + return Ret; +} + +TEST(PureFlow, NormalFlow01) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/normal_flow/normal_flow_01_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + const auto *MainFunc = IRDB.getFunction("main"); + // %0 + const auto *PercentZero = MainFunc->getArg(0); + // %1 + const auto *PercentOne = MainFunc->getArg(1); + // %.addr = alloca i32, align 4, !psr.id !216; | ID: 1 + const auto *Instr1 = IRDB.getInstruction(1); + // %.addr1 = alloca ptr, align 8, !psr.id !217; | ID: 2 + const auto *Instr2 = IRDB.getInstruction(2); + //%One = alloca i32, align 4, !psr.id !218; | ID: 3 + const auto *Instr3 = IRDB.getInstruction(3); + // %Two = alloca i32, align 4, !psr.id !219; | ID: 4 + const auto *Instr4 = IRDB.getInstruction(4); + // %OnePtr = alloca ptr, align 8, !psr.id !220; | ID: 5 + const auto *Instr5 = IRDB.getInstruction(5); + // %TwoAddr = alloca ptr, align 8, !psr.id !221; | ID: 6 + const auto *Instr6 = IRDB.getInstruction(6); + + // store i32 %0, ptr %.addr, align 4 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + EXPECT_EQ((std::set{PercentZero, Instr1}), + getNormalFlowValueSet(Instr8, AliasImpl, PercentZero)); + EXPECT_EQ((std::set{PercentZero, Instr1}), + getNormalFlowValueSet(Instr8, NoAliasImpl, PercentZero)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr8, AliasImpl, Instr1)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr8, NoAliasImpl, Instr1)); + + // store ptr %1, ptr %.addr1, align 8 + const auto *Instr10 = IRDB.getInstruction(10); + ASSERT_TRUE(Instr10); + EXPECT_EQ((std::set{PercentOne, Instr2}), + getNormalFlowValueSet(Instr10, AliasImpl, PercentOne)); + EXPECT_EQ((std::set{PercentOne, Instr2}), + getNormalFlowValueSet(Instr10, NoAliasImpl, PercentOne)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr10, AliasImpl, Instr2)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr10, NoAliasImpl, Instr2)); + + // store ptr %One, ptr %OnePtr, align 8, !dbg !225 + const auto *Instr17 = IRDB.getInstruction(17); + ASSERT_TRUE(Instr17); + EXPECT_EQ((std::set{Instr3, Instr5}), + getNormalFlowValueSet(Instr17, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr5}), + getNormalFlowValueSet(Instr17, NoAliasImpl, Instr3)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr17, AliasImpl, Instr5)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr17, NoAliasImpl, Instr5)); + + // store ptr %Two, ptr %TwoAddr, align 8, !dbg !228 + const auto *Instr19 = IRDB.getInstruction(19); + ASSERT_TRUE(Instr19); + EXPECT_EQ((std::set{Instr4, Instr6}), + getNormalFlowValueSet(Instr19, AliasImpl, Instr4)); + EXPECT_EQ((std::set{Instr4, Instr6}), + getNormalFlowValueSet(Instr19, NoAliasImpl, Instr4)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr19, AliasImpl, Instr6)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr19, NoAliasImpl, Instr6)); + + // Other arg + EXPECT_EQ(std::set{Instr19}, + getNormalFlowValueSet(Instr19, AliasImpl, Instr19)); + EXPECT_EQ(std::set{Instr19}, + getNormalFlowValueSet(Instr19, NoAliasImpl, Instr19)); +} + +TEST(PureFlow, NormalFlow02) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/normal_flow/normal_flow_02_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + const auto *MainFunc = IRDB.getFunction("main"); + ASSERT_TRUE(MainFunc); + // %0 + const auto *PercentZero = MainFunc->getArg(0); + ASSERT_TRUE(PercentZero); + // %1 + const auto *PercentOne = MainFunc->getArg(1); + ASSERT_TRUE(PercentOne); + // %.addr = alloca i32, align 4, !psr.id !221; | ID: 2 + const auto *Instr2 = IRDB.getInstruction(2); + ASSERT_TRUE(Instr2); + // %.addr1 = alloca ptr, align 8, !psr.id !222; | ID: 3 + const auto *Instr3 = IRDB.getInstruction(3); + ASSERT_TRUE(Instr3); + + // store i32 %0, ptr %.addr, align 4, !psr.id !227; | ID: 8 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + EXPECT_EQ((std::set{PercentZero, Instr2}), + getNormalFlowValueSet(Instr8, AliasImpl, PercentZero)); + EXPECT_EQ((std::set{PercentZero, Instr2}), + getNormalFlowValueSet(Instr8, NoAliasImpl, PercentZero)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr8, AliasImpl, Instr2)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr8, NoAliasImpl, Instr2)); + + // store ptr %1, ptr %.addr1, align 8, !psr.id !231; | ID: 10 + const auto *Instr10 = IRDB.getInstruction(10); + ASSERT_TRUE(Instr10); + EXPECT_EQ((std::set{PercentOne, Instr3}), + getNormalFlowValueSet(Instr10, AliasImpl, PercentOne)); + EXPECT_EQ((std::set{PercentOne, Instr3}), + getNormalFlowValueSet(Instr10, NoAliasImpl, PercentOne)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr10, AliasImpl, Instr3)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr10, NoAliasImpl, Instr3)); + + // Other arg + EXPECT_EQ(std::set{Instr10}, + getNormalFlowValueSet(Instr10, AliasImpl, Instr10)); + EXPECT_EQ(std::set{Instr10}, + getNormalFlowValueSet(Instr10, NoAliasImpl, Instr10)); +} + +TEST(PureFlow, NormalFlow03) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/normal_flow/normal_flow_03_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %One = alloca i32, align 4, !psr.id !222; | ID: 3 + const auto *Instr3 = IRDB.getInstruction(3); + ASSERT_TRUE(Instr3); + // %ForGEP = alloca %struct.StructOne, align 4, !psr.id !227; | ID: 8 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + // %2 = load i32, ptr %One, align 4, !dbg !245, !psr.id !246; | ID: 18 + const auto *Instr18 = IRDB.getInstruction(18); + ASSERT_TRUE(Instr18); + + EXPECT_EQ((std::set{Instr3, Instr18}), + getNormalFlowValueSet(Instr18, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr18}), + getNormalFlowValueSet(Instr18, NoAliasImpl, Instr3)); + // %3 = load i32, ptr %One, align 4, !dbg !251, !psr.id !252; | ID: 21 + const auto *Instr21 = IRDB.getInstruction(21); + ASSERT_TRUE(Instr21); + EXPECT_EQ((std::set{Instr3, Instr21}), + getNormalFlowValueSet(Instr21, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr21}), + getNormalFlowValueSet(Instr21, NoAliasImpl, Instr3)); + + // %tobool = icmp ne i32 %3, 0, !dbg !251, !psr.id !253; | ID: 22 + const auto *Instr22 = IRDB.getInstruction(22); + ASSERT_TRUE(Instr22); + // %lnot = xor i1 %tobool, true, !dbg !254, !psr.id !255; | ID: 23 + const auto *Instr23 = IRDB.getInstruction(23); + ASSERT_TRUE(Instr23); + EXPECT_EQ((std::set{Instr22, Instr23}), + getNormalFlowValueSet(Instr23, AliasImpl, Instr22)); + EXPECT_EQ((std::set{Instr22, Instr23}), + getNormalFlowValueSet(Instr23, NoAliasImpl, Instr22)); + + // %4 = load i32, ptr %One, align 4, !dbg !261, !psr.id !262; | ID: 27 + const auto *Instr27 = IRDB.getInstruction(27); + ASSERT_TRUE(Instr27); + EXPECT_EQ((std::set{Instr3, Instr27}), + getNormalFlowValueSet(Instr27, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr27}), + getNormalFlowValueSet(Instr27, NoAliasImpl, Instr3)); + + // %5 = load i32, ptr %One, align 4, !dbg !263, !psr.id !264; | ID: 28 + const auto *Instr28 = IRDB.getInstruction(28); + ASSERT_TRUE(Instr28); + EXPECT_EQ((std::set{Instr3, Instr28}), + getNormalFlowValueSet(Instr28, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr28}), + getNormalFlowValueSet(Instr28, NoAliasImpl, Instr3)); + + // %add = add nsw i32 %4, %5, !dbg !265, !psr.id !266; | ID: 29 + const auto *Instr29 = IRDB.getInstruction(29); + ASSERT_TRUE(Instr29); + EXPECT_EQ((std::set{Instr27, Instr29}), + getNormalFlowValueSet(Instr29, AliasImpl, Instr27)); + EXPECT_EQ((std::set{Instr27, Instr29}), + getNormalFlowValueSet(Instr29, NoAliasImpl, Instr27)); + EXPECT_EQ((std::set{Instr28, Instr29}), + getNormalFlowValueSet(Instr29, AliasImpl, Instr28)); + EXPECT_EQ((std::set{Instr28, Instr29}), + getNormalFlowValueSet(Instr29, NoAliasImpl, Instr28)); + + // %One2 = getelementptr inbounds %struct.StructOne, ptr %ForGEP, i32 0, i32 + // 0, !dbg !282, !psr.id !283; | ID: 37 + const auto *Instr37 = IRDB.getInstruction(37); + ASSERT_TRUE(Instr37); + EXPECT_EQ((std::set{Instr8, Instr37}), + getNormalFlowValueSet(Instr37, AliasImpl, Instr8)); + EXPECT_EQ((std::set{Instr8, Instr37}), + getNormalFlowValueSet(Instr37, NoAliasImpl, Instr8)); + + // %6 = load i32, ptr %One2, align 4, !dbg !282, !psr.id !284; | ID: 38 + const auto *Instr38 = IRDB.getInstruction(38); + ASSERT_TRUE(Instr38); + EXPECT_EQ((std::set{Instr37, Instr38}), + getNormalFlowValueSet(Instr38, AliasImpl, Instr37)); + EXPECT_EQ((std::set{Instr37, Instr38}), + getNormalFlowValueSet(Instr38, NoAliasImpl, Instr37)); +} + +TEST(PureFlow, NormalFlow04) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/normal_flow/normal_flow_04_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %Deref1 = alloca i32, align 4, !psr.id !222; | ID: 7 + const auto *Instr7 = IRDB.getInstruction(7); + ASSERT_TRUE(Instr7); + // %Deref2 = alloca i32, align 4, !psr.id !223; | ID: 8 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + // %Deref3 = alloca i32, align 4, !psr.id !224; | ID: 9 + const auto *Instr9 = IRDB.getInstruction(9); + ASSERT_TRUE(Instr9); + // %3 = load i32, ptr %2, align 4, !dbg !258, !psr.id !259; | ID: 25 + const auto *Instr25 = IRDB.getInstruction(25); + ASSERT_TRUE(Instr25); + // %6 = load i32, ptr %5, align 4, !dbg !268, !psr.id !269; | ID: 30 + const auto *Instr30 = IRDB.getInstruction(30); + ASSERT_TRUE(Instr30); + // %10 = load i32, ptr %9, align 4, !dbg !280, !psr.id !281; | ID: 36 + const auto *Instr36 = IRDB.getInstruction(36); + ASSERT_TRUE(Instr36); + + // store i32 %3, ptr %Deref1, align 4, !dbg !254, !psr.id !260; | ID: 26 + const auto *Instr26 = IRDB.getInstruction(26); + ASSERT_TRUE(Instr26); + EXPECT_EQ((std::set{Instr25, Instr7}), + getNormalFlowValueSet(Instr26, AliasImpl, Instr25)); + EXPECT_EQ((std::set{Instr25, Instr7}), + getNormalFlowValueSet(Instr26, NoAliasImpl, Instr25)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr26, AliasImpl, Instr7)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr26, NoAliasImpl, Instr7)); + + // store i32 %6, ptr %Deref2, align 4, !dbg !262, !psr.id !270; | ID: 31 + const auto *Instr31 = IRDB.getInstruction(31); + ASSERT_TRUE(Instr31); + EXPECT_EQ((std::set{Instr8, Instr30}), + getNormalFlowValueSet(Instr31, AliasImpl, Instr30)); + EXPECT_EQ((std::set{Instr8, Instr30}), + getNormalFlowValueSet(Instr31, NoAliasImpl, Instr30)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr31, AliasImpl, Instr8)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr31, NoAliasImpl, Instr8)); + + // store i32 %10, ptr %Deref3, align 4, !dbg !272, !psr.id !282; | ID: 37 + const auto *Instr37 = IRDB.getInstruction(37); + ASSERT_TRUE(Instr37); + EXPECT_EQ((std::set{Instr9, Instr36}), + getNormalFlowValueSet(Instr37, AliasImpl, Instr36)); + EXPECT_EQ((std::set{Instr9, Instr36}), + getNormalFlowValueSet(Instr37, NoAliasImpl, Instr36)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr37, AliasImpl, Instr9)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr37, NoAliasImpl, Instr9)); +} + +/* + CallFlow +*/ + +TEST(PureFlow, CallFlow01) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/call_flow/call_flow_01_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // call void @_Z4callii(i32 noundef %2, i32 noundef %3), !dbg !261, !psr.id + // !262; | ID: 26 + const auto *Instr26 = IRDB.getInstruction(26); + ASSERT_TRUE(Instr26); + const auto *FuncForInstr26 = IRDB.getFunction("_Z4callii"); + ASSERT_TRUE(FuncForInstr26); + ASSERT_EQ(FuncForInstr26->arg_size(), 2); + const auto *Param0 = FuncForInstr26->getArg(0); + const auto *Param1 = FuncForInstr26->getArg(1); + + if (const auto *CallSite = llvm::dyn_cast(Instr26)) { + EXPECT_EQ(std::set{Param0}, + getCallFlowValueSet(Instr26, AliasImpl, + CallSite->getArgOperand(0), FuncForInstr26)); + EXPECT_EQ(std::set{Param1}, + getCallFlowValueSet(Instr26, AliasImpl, + CallSite->getArgOperand(1), FuncForInstr26)); + EXPECT_EQ(std::set{Param0}, + getCallFlowValueSet(Instr26, NoAliasImpl, + CallSite->getArgOperand(0), FuncForInstr26)); + EXPECT_EQ(std::set{Param1}, + getCallFlowValueSet(Instr26, NoAliasImpl, + CallSite->getArgOperand(1), FuncForInstr26)); + } else { + FAIL(); + } +} + +TEST(PureFlow, CallFlow02) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/call_flow/call_flow_02_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %One = alloca i32, align 4, !psr.id !251; | ID: 17 + const auto *Instr17 = IRDB.getInstruction(17); + ASSERT_TRUE(Instr17); + // %Two = alloca i32, align 4, !psr.id !252; | ID: 18 + const auto *Instr18 = IRDB.getInstruction(18); + ASSERT_TRUE(Instr18); + // %Three = alloca i32, align 4, !psr.id !253; | ID: 19 + const auto *Instr19 = IRDB.getInstruction(19); + ASSERT_TRUE(Instr19); + // %PtrToOne = alloca ptr, align 8, !psr.id !254; | ID: 20 + const auto *Instr20 = IRDB.getInstruction(20); + ASSERT_TRUE(Instr20); + // %PtrToTwo = alloca ptr, align 8, !psr.id !255; | ID: 21 + const auto *Instr21 = IRDB.getInstruction(21); + ASSERT_TRUE(Instr21); + // %PtrPtrToTwo = alloca ptr, align 8, !psr.id !256; | ID: 22 + const auto *Instr22 = IRDB.getInstruction(22); + ASSERT_TRUE(Instr22); + // %PtrToThree = alloca ptr, align 8, !psr.id !257; | ID: 23 + const auto *Instr23 = IRDB.getInstruction(23); + ASSERT_TRUE(Instr23); + // %PtrPtrToThree = alloca ptr, align 8, !psr.id !258; | ID: 24 + const auto *Instr24 = IRDB.getInstruction(24); + ASSERT_TRUE(Instr24); + // %PtrPtrPtrToThree = alloca ptr, align 8, !psr.id !259; | ID: 25 + const auto *Instr25 = IRDB.getInstruction(25); + ASSERT_TRUE(Instr25); + // call void @_Z4callPi(ptr noundef %2), !dbg !307, !psr.id !308; | ID: 50 + const auto *Instr50 = IRDB.getInstruction(50); + ASSERT_TRUE(Instr50); + // call void @_Z10secondCallPiPS_PS0_(ptr noundef %3, ptr noundef %4, ptr + // noundef %5), !dbg !315, !psr.id !316; | ID: 54 + const auto *Instr54 = IRDB.getInstruction(54); + ASSERT_TRUE(Instr54); + + // call function + const auto *CallFunc = IRDB.getFunction("_Z4callPi"); + ASSERT_TRUE(CallFunc); + // second call function + const auto *SecondCallFunc = IRDB.getFunction("_Z10secondCallPiPS_PS0_"); + ASSERT_TRUE(SecondCallFunc); + + const auto *CSCallFunc = llvm::cast(Instr50); + const auto *CSSecondCallFunc = llvm::cast(Instr54); + + EXPECT_EQ((std::set{CallFunc->getArg(0)}), + getCallFlowValueSet(Instr50, AliasImpl, + CSCallFunc->getArgOperand(0), CallFunc)); + EXPECT_EQ((std::set{CallFunc->getArg(0)}), + getCallFlowValueSet(Instr50, NoAliasImpl, + CSCallFunc->getArgOperand(0), CallFunc)); + + EXPECT_EQ((std::set{SecondCallFunc->getArg(0)}), + getCallFlowValueSet(Instr54, AliasImpl, + CSSecondCallFunc->getArgOperand(0), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(0)}), + getCallFlowValueSet(Instr54, NoAliasImpl, + CSSecondCallFunc->getArgOperand(0), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(1)}), + getCallFlowValueSet(Instr54, AliasImpl, + CSSecondCallFunc->getArgOperand(1), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(1)}), + getCallFlowValueSet(Instr54, NoAliasImpl, + CSSecondCallFunc->getArgOperand(1), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(2)}), + getCallFlowValueSet(Instr54, AliasImpl, + CSSecondCallFunc->getArgOperand(2), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(2)}), + getCallFlowValueSet(Instr54, NoAliasImpl, + CSSecondCallFunc->getArgOperand(2), + SecondCallFunc)); +} + +/* + RetFlow +*/ + +TEST(PureFlow, RetFlow01) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/ret_flow/ret_flow_01_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %Two = alloca i32, align 4, !psr.id !251; | ID: 20 + const auto *UnusedValue = IRDB.getValueFromId(20); + + // %call = call noundef i32 @_Z6getTwov(), !dbg !231, !psr.id !232; | ID: 9 + const auto *Instr9 = IRDB.getInstruction(9); + ASSERT_TRUE(Instr9); + // ret i32 2, !dbg !212, !psr.id !213; | ID: 0 + const auto *Instr0 = IRDB.getInstruction(0); + ASSERT_TRUE(Instr0); + + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr9, AliasImpl, UnusedValue, Instr0)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr9, NoAliasImpl, UnusedValue, Instr0)); + + // %call = call noundef i32 @_Z4callii(i32 noundef %2, i32 noundef %3), !dbg + // !281, !psr.id !282; | ID: 36 + const auto *Instr36 = IRDB.getInstruction(36); + ASSERT_TRUE(Instr36); + // ret i32 %add, !dbg !240, !psr.id !241; | ID: 14 + const auto *Instr14 = IRDB.getInstruction(14); + ASSERT_TRUE(Instr14); + const auto *FuncZ4callii = IRDB.getFunction("_Z4callii"); + + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr36, AliasImpl, FuncZ4callii->getArg(0), Instr14)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr36, AliasImpl, FuncZ4callii->getArg(1), Instr14)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr36, NoAliasImpl, FuncZ4callii->getArg(0), + Instr14)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr36, NoAliasImpl, FuncZ4callii->getArg(1), + Instr14)); + + // negative tests + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr9, AliasImpl, FuncZ4callii->getArg(1), Instr0)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr9, NoAliasImpl, FuncZ4callii->getArg(1), Instr0)); +} + +TEST(PureFlow, RetFlow02) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/ret_flow/ret_flow_02_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %Two = alloca i32, align 4, !psr.id !268; | ID: 29 + const auto *UnusedValue = IRDB.getValueFromId(29); + + // %call = call noundef i32 @_Z6getTwov(), !dbg !240, !psr.id !241; | ID: 14 + const auto *Instr14 = IRDB.getInstruction(14); + ASSERT_TRUE(Instr14); + // ret i32 2, !dbg !212, !psr.id !213; | ID: 0 + const auto *Instr0 = IRDB.getInstruction(0); + ASSERT_TRUE(Instr0); + + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr14, AliasImpl, UnusedValue, Instr0)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr14, NoAliasImpl, UnusedValue, Instr0)); + + // %call1 = call noundef i32 @_Z8newThreev(), !dbg !247, !psr.id !248; | ID: + // 18 + const auto *Instr18 = IRDB.getInstruction(18); + ASSERT_TRUE(Instr18); + // ret i32 3, !dbg !220, !psr.id !221; | ID: 4 + const auto *Instr4 = IRDB.getInstruction(4); + ASSERT_TRUE(Instr4); + + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr18, AliasImpl, UnusedValue, Instr4)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr18, NoAliasImpl, UnusedValue, Instr4)); + + // %call = call noundef i32 @_Z4callii(i32 noundef %2, i32 noundef %3), !dbg + // !298, !psr.id !299; | ID: 45 + const auto *Instr45 = IRDB.getInstruction(45); + ASSERT_TRUE(Instr45); + // ret i32 %add, !dbg !257, !psr.id !258; | ID: 23 + const auto *Instr23 = IRDB.getInstruction(23); + ASSERT_TRUE(Instr23); + const auto *FuncZ4callii = IRDB.getFunction("_Z4callii"); + + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr45, AliasImpl, FuncZ4callii->getArg(0), Instr23)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr45, AliasImpl, FuncZ4callii->getArg(1), Instr23)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr45, NoAliasImpl, FuncZ4callii->getArg(0), + Instr23)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr45, NoAliasImpl, FuncZ4callii->getArg(1), + Instr23)); + + // negative tests + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr14, AliasImpl, FuncZ4callii->getArg(1), Instr0)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr14, NoAliasImpl, FuncZ4callii->getArg(1), + Instr0)); +} + +TEST(PureFlow, RetFlow03) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/ret_flow/ret_flow_03_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %ThreeInCall = alloca i32, align 4, !psr.id !254; | ID: 15 + const auto *Instr15 = IRDB.getValueFromId(15); + ASSERT_TRUE(Instr15); + + // %ThreePtrInCall = alloca ptr, align 8, !psr.id !255; | ID: 16 + const auto *Instr16 = IRDB.getValueFromId(16); + ASSERT_TRUE(Instr16); + + // %0 = load ptr, ptr %ThreePtrInCall, align 8, !dbg !280, !psr.id !281; | ID: + // 29 + const auto *Instr29 = IRDB.getValueFromId(29); + ASSERT_TRUE(Instr29); + + // %call = call noundef ptr @_Z8newThreePKi(ptr noundef %0), !dbg !282, + // !psr.id !283; | ID: 30 + const auto *Instr30 = IRDB.getValueFromId(30); + ASSERT_TRUE(Instr30); + + // ret ptr %1, !dbg !234, !psr.id !235; | ID: 9 + const auto *Instruction9 = IRDB.getInstruction(9); + ASSERT_TRUE(Instruction9); + // %call = call noundef ptr @_Z8newThreePKi(ptr noundef %0), !dbg !282, + // !psr.id !283; | ID: 30 + const auto *Instruction30 = IRDB.getInstruction(30); + ASSERT_TRUE(Instruction30); + const auto *FunctionZ8newThreePKi = IRDB.getFunction("_Z8newThreePKi"); + + const auto &Got = getRetFlowValueSet( + Instruction30, AliasImpl, FunctionZ8newThreePKi->getArg(0), Instruction9); + + EXPECT_EQ((std::set{Instr15, Instr16, Instr29, Instr30}), + Got) + << stringifyValueSet(Got); + + EXPECT_EQ(std::set{Instr29}, + getRetFlowValueSet(Instruction30, NoAliasImpl, + FunctionZ8newThreePKi->getArg(0), Instruction9)); + + // ret ptr @GlobalFour, !dbg !240, !psr.id !241; | ID: 10 + const auto *Instruction10 = IRDB.getInstruction(10); + ASSERT_TRUE(Instruction10); + // %call3 = call noundef ptr @_Z10getFourPtrv(), !dbg !304, !psr.id !305; | + // ID: 42 + const auto *Instruction42 = IRDB.getInstruction(42); + ASSERT_TRUE(Instruction42); + + // ret ptr @GlobalFour, !dbg !246, !psr.id !247; | ID: 11 + const auto *Instruction11 = IRDB.getInstruction(11); + ASSERT_TRUE(Instruction11); + // %call5 = call noundef nonnull align 4 dereferenceable(4) ptr + // @_Z11getFourAddrv(), !dbg !310, !psr.id !311; | ID: 45 + const auto *Instruction45 = IRDB.getInstruction(45); + ASSERT_TRUE(Instruction45); + + // ret i32 %add6, !dbg !315, !psr.id !316; | ID: 48 + const auto *Instruction48 = IRDB.getInstruction(48); + ASSERT_TRUE(Instruction48); + // %call = call noundef i32 @_Z4callRiPKi(ptr noundef nonnull align 4 + // dereferenceable(4) %Zero, ptr noundef %One), !dbg !352, !psr.id !353; | ID: + // 68 + const auto *Instruction68 = IRDB.getInstruction(68); + ASSERT_TRUE(Instruction68); + const auto *FuncZ4callRiPKi = IRDB.getFunction("_Z4callRiPKi"); + // %Zero = alloca i32, align 4, !psr.id !324; | ID: 52 + const auto *Instr52 = IRDB.getInstruction(52); + ASSERT_TRUE(Instr52); + + // %One = alloca i32, align 4, !psr.id !325; | ID: 53 + const auto *Instr53 = IRDB.getInstruction(53); + ASSERT_TRUE(Instr53); + + EXPECT_EQ(std::set{Instr52}, + getRetFlowValueSet(Instruction68, AliasImpl, + FuncZ4callRiPKi->getArg(0), Instruction48)); + EXPECT_EQ(std::set{Instr52}, + getRetFlowValueSet(Instruction68, NoAliasImpl, + FuncZ4callRiPKi->getArg(0), Instruction48)); + EXPECT_EQ(std::set{Instr53}, + getRetFlowValueSet(Instruction68, AliasImpl, + FuncZ4callRiPKi->getArg(1), Instruction48)); + EXPECT_EQ(std::set{Instr53}, + getRetFlowValueSet(Instruction68, NoAliasImpl, + FuncZ4callRiPKi->getArg(1), Instruction48)); +} + +/* + CallToRetFlow +*/ + +TEST(PureFlow, CallToRetFlow01) { + LLVMProjectIRDB IRDB( + {unittest::PathToLLTestFiles + + "pure_flow/call_to_ret_flow/call_to_ret_flow_01_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // store i32 0, ptr %Zero, align 4, !dbg !252, !psr.id !254; | ID: 22 + const auto *Instr22 = IRDB.getInstruction(22); + ASSERT_TRUE(Instr22); + + // store i32 1, ptr %One, align 4, !dbg !256, !psr.id !258; | ID: 24 + const auto *Instr24 = IRDB.getInstruction(24); + ASSERT_TRUE(Instr24); + + // ret i32 0, !dbg !269, !psr.id !270; | ID: 30 + const auto *Instr30 = IRDB.getInstruction(30); + ASSERT_TRUE(Instr30); + + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr30, AliasImpl, Instr22)); + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr30, NoAliasImpl, Instr22)); + + EXPECT_EQ(std::set{Instr24}, + getCallToRetFlowValueSet(Instr30, AliasImpl, Instr24)); + EXPECT_EQ(std::set{Instr24}, + getCallToRetFlowValueSet(Instr30, NoAliasImpl, Instr24)); + + EXPECT_EQ(std::set{Instr30}, + getCallToRetFlowValueSet(Instr30, AliasImpl, Instr30)); + EXPECT_EQ(std::set{Instr30}, + getCallToRetFlowValueSet(Instr30, NoAliasImpl, Instr30)); +} + +TEST(PureFlow, CallToRetFlow02) { + LLVMProjectIRDB IRDB( + {unittest::PathToLLTestFiles + + "pure_flow/call_to_ret_flow/call_to_ret_flow_02_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // store i32 3, ptr %Three, align 4, !dbg !223, !psr.id !225; | ID: 5 + const auto *Instr5 = IRDB.getInstruction(5); + ASSERT_TRUE(Instr5); + + // store i32 4, ptr %Four, align 4, !dbg !229, !psr.id !231; | ID: 8 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + + // ret void, !dbg !234, !psr.id !235; | ID: 10 + const auto *Instr10 = IRDB.getInstruction(10); + ASSERT_TRUE(Instr10); + + EXPECT_EQ(std::set{Instr5}, + getCallToRetFlowValueSet(Instr10, AliasImpl, Instr5)); + EXPECT_EQ(std::set{Instr5}, + getCallToRetFlowValueSet(Instr10, NoAliasImpl, Instr5)); + + EXPECT_EQ(std::set{Instr8}, + getCallToRetFlowValueSet(Instr10, AliasImpl, Instr8)); + EXPECT_EQ(std::set{Instr8}, + getCallToRetFlowValueSet(Instr10, NoAliasImpl, Instr8)); + + // store i32 1, ptr %One, align 4, !dbg !255, !psr.id !257; | ID: 22 + const auto *Instr22 = IRDB.getInstruction(22); + ASSERT_TRUE(Instr22); + + // store i32 2, ptr %Two, align 4, !dbg !261, !psr.id !263; | ID: 25 + const auto *Instr25 = IRDB.getInstruction(25); + ASSERT_TRUE(Instr25); + + // ret i32 0, !dbg !266, !psr.id !267; | ID: 27 + const auto *Instr27 = IRDB.getInstruction(27); + ASSERT_TRUE(Instr27); + + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr27, AliasImpl, Instr22)); + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr27, NoAliasImpl, Instr22)); + + EXPECT_EQ(std::set{Instr25}, + getCallToRetFlowValueSet(Instr27, AliasImpl, Instr25)); + EXPECT_EQ(std::set{Instr25}, + getCallToRetFlowValueSet(Instr27, NoAliasImpl, Instr25)); + + EXPECT_EQ(std::set{Instr27}, + getCallToRetFlowValueSet(Instr27, AliasImpl, Instr27)); + EXPECT_EQ(std::set{Instr27}, + getCallToRetFlowValueSet(Instr27, NoAliasImpl, Instr27)); +} + +}; // namespace + +// main function for the test case +int main(int Argc, char **Argv) { + ::testing::InitGoogleTest(&Argc, Argv); + return RUN_ALL_TESTS(); +}