Skip to content
Merged
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ jobs:
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DBUILD_PHASAR_CLANG=OFF \
-DPHASAR_USE_Z3=ON \
-DPHASAR_BUILD_MODULES=ON \
-DPHASAR_LLVM_VERSION=${{ matrix.llvm-version }} \
${{ matrix.flags }} \
-G Ninja
Expand Down
1 change: 1 addition & 0 deletions BreakingChanges.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## development HEAD

- `IntraMonoProblem` and `InterMonoProblem`, and all reference-implementations of these problems do not receive a TypeHierarchy-pointer anymore in the ctor.
- Requiring C++20 instead of C++17
- Type-traits and other templates that are specialized now use `requires` instead of `enable_if`, wherever possible. This may reduce the number of (defaulted) template parameters in some cases.
- The `AdjacencyList` struct now now has one more template argument to denote the intege-like `vertex_t` type. It is the second template argument (which previously was the EdgeType). The edge-type is now denoted by the *third* template argument.
Expand Down
4 changes: 4 additions & 0 deletions include/phasar/ControlFlow.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
#ifndef PHASAR_CONTROLFLOW_H
#define PHASAR_CONTROLFLOW_H

#include "phasar/ControlFlow/CFG.h"
#include "phasar/ControlFlow/CFGBase.h"
#include "phasar/ControlFlow/CallGraph.h"
#include "phasar/ControlFlow/CallGraphAnalysisType.h"
#include "phasar/ControlFlow/CallGraphBase.h"
#include "phasar/ControlFlow/ICFG.h"
#include "phasar/ControlFlow/ICFGBase.h"
#include "phasar/ControlFlow/SparseCFGBase.h"
#include "phasar/ControlFlow/SparseCFGProvider.h"
#include "phasar/ControlFlow/SpecialMemberFunctionType.h"

#endif // PHASAR_CONTROLFLOW_H
90 changes: 90 additions & 0 deletions include/phasar/ControlFlow/CFG.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/******************************************************************************
* Copyright (c) 2026 Fabian Schiebel.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of LICENSE.txt.
*
* Contributors:
* Fabian Schiebel and others
*****************************************************************************/
#pragma once

#include "phasar/Utils/TypeTraits.h"

#include "llvm/Support/raw_ostream.h"

#include <concepts>
#include <utility>

namespace psr {

template <typename T>
concept InstructionClassifier =
requires(const T &IC, typename T::n_t Inst, typename T::n_t Succ) {
{ IC.isCallSite(Inst) } -> std::convertible_to<bool>;
{ IC.isFieldLoad(Inst) } -> std::convertible_to<bool>;
{ IC.isFieldStore(Inst) } -> std::convertible_to<bool>;
{ IC.isFallThroughSuccessor(Inst, Succ) } -> std::convertible_to<bool>;
{ IC.isBranchTarget(Inst, Succ) } -> std::convertible_to<bool>;
};

template <typename T>
concept CFG = requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
typename T::n_t;
typename T::f_t;

/// Returns the function that contains the given instruction Inst.
// TODO: Actually belongs into ProjectIRDB!
{ CF.getFunctionOf(Inst) } -> std::convertible_to<typename T::f_t>;
/// Returns an iterable range of all instructions of the given function that
/// are part of the control-flow graph.
// TODO: We should have sth like this in the ProjectIRDB as well!
{ CF.getAllInstructionsOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;

/// Returns an iterable range of all successor instructions of Inst in the
/// CFG.
/// NOTE: This function is typically being called in a hot part of the
/// analysis and should therefore be highly optimized for performance.
{ CF.getSuccsOf(Inst) } -> psr::is_iterable_over_v<typename T::n_t>;

/// Returns an iterable range of all starting instructions of the given
/// function. For a forward-CFG, this is typically a singleton range.
{ CF.getStartPointsOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;

/// Returns whether the given Inst is a root node of the CFG
{ CF.isStartPoint(Inst) } -> std::convertible_to<bool>;

/// Returns whether the given Inst is a leaf node of the CFG
{ CF.isExitInst(Inst) } -> std::convertible_to<bool>;

requires InstructionClassifier<T>;
};

template <typename T>
concept BidiCFG =
CFG<T> && requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
/// Returns an iterable range of all predecessor instructions of Inst in
/// the CFG
{ CF.getPredsOf(Inst) } -> psr::is_iterable_over_v<typename T::n_t>;

/// Returns an iterable range of all exit instructions (often return
/// instructions) of the given function. For a backward-CFG, this is
/// typically a singleton range
{ CF.getExitPointsOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
};

template <typename T>
concept CFGDump = requires(const T &CF, typename T::n_t Inst,
typename T::f_t Fun, llvm::raw_ostream &OS) {
{ CF.getStatementId(Inst) } -> psr::is_string_like_v;
{ CF.getFunctionName(Fun) } -> psr::is_string_like_v;
{ CF.getDemangledFunctionName(Fun) } -> psr::is_string_like_v;
CF.print(Fun, OS);
};

template <typename T>
concept CFGEdgesProvider = requires(const T &CF, typename T::f_t Fun) {
{
CF.getAllControlFlowEdges(Fun)
} -> psr::is_iterable_over_v<std::pair<typename T::n_t, typename T::n_t>>;
};
} // namespace psr
7 changes: 4 additions & 3 deletions include/phasar/ControlFlow/CFGBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#ifndef PHASAR_CONTROLFLOW_CFGBASE_H
#define PHASAR_CONTROLFLOW_CFGBASE_H

#include "phasar/ControlFlow/CFG.h"
#include "phasar/Utils/ByRef.h"
#include "phasar/Utils/CRTPUtils.h"
#include "phasar/Utils/TypeTraits.h"
Expand Down Expand Up @@ -139,9 +140,9 @@ template <typename Derived> class CFGBase : public CRTPBase<Derived> {

template <typename ICF, typename Domain>
// NOLINTNEXTLINE(readability-identifier-naming)
concept is_cfg_v = is_crtp_base_of_v<CFGBase, ICF> &&
std::is_same_v<typename ICF::n_t, typename Domain::n_t> &&
std::is_same_v<typename ICF::f_t, typename Domain::f_t>;
concept is_cfg_v = BidiCFG<ICF> && CFGDump<ICF> && CFGEdgesProvider<ICF> &&
std::same_as<typename ICF::n_t, typename Domain::n_t> &&
std::same_as<typename ICF::f_t, typename Domain::f_t>;

} // namespace psr

Expand Down
47 changes: 47 additions & 0 deletions include/phasar/ControlFlow/CallGraphBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,54 @@
#include "phasar/Utils/CRTPUtils.h"
#include "phasar/Utils/TypeTraits.h"

#include <concepts>
#include <type_traits>

namespace psr {

template <typename T>
concept IsCallGraph =
requires(const T &CG, typename T::n_t Inst, typename T::f_t Fun) {
typename T::n_t;
typename T::f_t;

/// Returns an iterable range of all possible callee candidates at the
/// given call-site induced by the used call-graph.
///
/// NOTE: This function is typically called in a hot part of the analysis
/// and should therefore be very fast
{
CG.getCalleesOfCallAt(Inst)
} -> psr::is_iterable_over_v<typename T::f_t>;

/// Returns an iterable range of all possible call-site candidates that
/// may call the given function induced by the used call-graph.
{ CG.getCallersOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;

/// A range of all functions that are vertices in the call-graph. The
/// number of vertex functions can be retrieved by
/// getNumVertexFunctions().
{
CG.getAllVertexFunctions()
} -> psr::is_iterable_over_v<typename T::f_t>;

/// A range of all call-sites that are vertices in the call-graph. The
/// number of vertex-callsites can be retrived by getNumVertexCallSites().
{
CG.getAllVertexCallSites()
} -> psr::is_iterable_over_v<typename T::n_t>;

{ CG.getNumVertexFunctions() } -> std::convertible_to<size_t>;
{ CG.getNumVertexCallSites() } -> std::convertible_to<size_t>;

/// Same as getNumVertexFunctions()
{ CG.size() } noexcept -> std::convertible_to<size_t>;
{ CG.empty() } noexcept -> std::convertible_to<bool>;
};

template <typename T>
concept IsCallGraphRef = IsCallGraph<std::remove_cvref_t<T>>;

template <typename T> struct CGTraits {
// using n_t
// using f_t
Expand Down
64 changes: 64 additions & 0 deletions include/phasar/ControlFlow/ICFG.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/******************************************************************************
* Copyright (c) 2026 Fabian Schiebel.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of LICENSE.txt.
*
* Contributors:
* Fabian Schiebel and others
*****************************************************************************/
#pragma once

#include "phasar/ControlFlow/CallGraphBase.h"
#include "phasar/Utils/Nullable.h"
#include "phasar/Utils/TypeTraits.h"

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"

#include <concepts>

namespace psr {
template <typename T>
concept ICFG = requires(const T &ICF, llvm::StringRef Name,
typename T::n_t Inst, typename T::f_t Fun) {
typename T::f_t;
typename T::n_t;

// TODO: Should not be duplicated with ProjectIRDB
{ ICF.getAllFunctions() } -> is_iterable_over_v<typename T::f_t>;
// TODO: Should not be duplicated with ProjectIRDB
{ ICF.getFunction(Name) } -> std::convertible_to<Nullable<typename T::f_t>>;

{ ICF.isIndirectFunctionCall(Inst) } -> std::convertible_to<bool>;
{ ICF.isVirtualFunctionCall(Inst) } -> std::convertible_to<bool>;

/// Gets the underlying call-graph
{ ICF.getCallGraph() } -> psr::IsCallGraphRef;
/// Returns an iterable range of all possible callee candidates at the given
/// call-site induced by the used call-graph. Same as
/// getCallGraph().getCalleesOfCallAt(Inst)
{ ICF.getCalleesOfCallAt(Inst) } -> psr::is_iterable_over_v<typename T::f_t>;
/// Returns an iterable range of all possible call-site candidates that may
/// call the given function induced by the used call-graph. Same as
/// getCallGraph().getCallersOf(Fun)
{ ICF.getCallersOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;

/// Returns an iterable range of all call-instruction in the given function
{ ICF.getCallsFromWithin(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;

/// Returns an iterable range of all instructions in all functions of the ICFG
/// that are neither call-sites nor start-points of a function
// TODO: Get rid of this function
{ ICF.allNonCallStartNodes() } -> psr::is_iterable_over_v<typename T::n_t>;

/// The total number of call-sites in the ICFG. Same as
/// getCallGraph().getNumVertexCallSites()
{ ICF.getNumCallSites() } noexcept -> std::convertible_to<size_t>;
};

template <typename T>
concept ICFGDump = requires(const T &ICF, llvm::raw_ostream &OS) {
ICF.print(OS);
ICF.printAsJson(OS);
};
} // namespace psr
3 changes: 2 additions & 1 deletion include/phasar/ControlFlow/ICFGBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "phasar/ControlFlow/CFGBase.h"
#include "phasar/ControlFlow/CallGraphBase.h"
#include "phasar/ControlFlow/ICFG.h"
#include "phasar/Utils/CRTPUtils.h"
#include "phasar/Utils/TypeTraits.h"

Expand Down Expand Up @@ -118,7 +119,7 @@ template <typename Derived> class ICFGBase : public CRTPBase<Derived> {
/// from the given analysis-Domain
template <typename ICF, typename Domain>
// NOLINTNEXTLINE(readability-identifier-naming)
concept is_icfg_v = is_crtp_base_of_v<ICFGBase, ICF> &&
concept is_icfg_v = ICFG<ICF> && ICFGDump<ICF> &&
std::is_same_v<typename ICF::n_t, typename Domain::n_t> &&
std::is_same_v<typename ICF::f_t, typename Domain::f_t>;

Expand Down
1 change: 1 addition & 0 deletions include/phasar/DB.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define PHASAR_DB_H

#include "phasar/Config/phasar-config.h"
#include "phasar/DB/ProjectIRDB.h"
#include "phasar/DB/ProjectIRDBBase.h"

#endif // PHASAR_DB_H
Loading
Loading