Skip to content

Commit c4ca07a

Browse files
authored
Concept Interfaces (#811)
* Add concepts for ProjectIRDB and IRDomain * Add comcepts for CFG * Add concepts for ICFG * Fix and update module-header and c++20-module files * LLVM 18 compatibility * Add missing functioality to IRDB and change calls to the respective functions of CFG/ICFG to IRDB, wherever easily possible
1 parent 764550e commit c4ca07a

File tree

86 files changed

+693
-191
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+693
-191
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ jobs:
7070
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
7171
-DBUILD_PHASAR_CLANG=OFF \
7272
-DPHASAR_USE_Z3=ON \
73+
-DPHASAR_BUILD_MODULES=ON \
7374
-DPHASAR_LLVM_VERSION=${{ matrix.llvm-version }} \
7475
${{ matrix.flags }} \
7576
-G Ninja

BreakingChanges.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## development HEAD
44

5+
- `IntraMonoProblem` and `InterMonoProblem`, and all reference-implementations of these problems do not receive a TypeHierarchy-pointer anymore in the ctor.
56
- Requiring C++20 instead of C++17
67
- 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.
78
- 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.

include/phasar/ControlFlow.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@
1010
#ifndef PHASAR_CONTROLFLOW_H
1111
#define PHASAR_CONTROLFLOW_H
1212

13+
#include "phasar/ControlFlow/CFG.h"
1314
#include "phasar/ControlFlow/CFGBase.h"
1415
#include "phasar/ControlFlow/CallGraph.h"
1516
#include "phasar/ControlFlow/CallGraphAnalysisType.h"
1617
#include "phasar/ControlFlow/CallGraphBase.h"
18+
#include "phasar/ControlFlow/ICFG.h"
1719
#include "phasar/ControlFlow/ICFGBase.h"
20+
#include "phasar/ControlFlow/SparseCFGBase.h"
21+
#include "phasar/ControlFlow/SparseCFGProvider.h"
1822
#include "phasar/ControlFlow/SpecialMemberFunctionType.h"
1923

2024
#endif // PHASAR_CONTROLFLOW_H

include/phasar/ControlFlow/CFG.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/******************************************************************************
2+
* Copyright (c) 2026 Fabian Schiebel.
3+
* All rights reserved. This program and the accompanying materials are made
4+
* available under the terms of LICENSE.txt.
5+
*
6+
* Contributors:
7+
* Fabian Schiebel and others
8+
*****************************************************************************/
9+
#pragma once
10+
11+
#include "phasar/Utils/TypeTraits.h"
12+
13+
#include "llvm/Support/raw_ostream.h"
14+
15+
#include <concepts>
16+
#include <utility>
17+
18+
namespace psr {
19+
20+
template <typename T>
21+
concept InstructionClassifier =
22+
requires(const T &IC, typename T::n_t Inst, typename T::n_t Succ) {
23+
{ IC.isCallSite(Inst) } -> std::convertible_to<bool>;
24+
{ IC.isFieldLoad(Inst) } -> std::convertible_to<bool>;
25+
{ IC.isFieldStore(Inst) } -> std::convertible_to<bool>;
26+
{ IC.isFallThroughSuccessor(Inst, Succ) } -> std::convertible_to<bool>;
27+
{ IC.isBranchTarget(Inst, Succ) } -> std::convertible_to<bool>;
28+
};
29+
30+
template <typename T>
31+
concept CFG = requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
32+
typename T::n_t;
33+
typename T::f_t;
34+
35+
/// Returns the function that contains the given instruction Inst.
36+
// TODO: Actually belongs into ProjectIRDB!
37+
{ CF.getFunctionOf(Inst) } -> std::convertible_to<typename T::f_t>;
38+
/// Returns an iterable range of all instructions of the given function that
39+
/// are part of the control-flow graph.
40+
// TODO: We should have sth like this in the ProjectIRDB as well!
41+
{ CF.getAllInstructionsOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
42+
43+
/// Returns an iterable range of all successor instructions of Inst in the
44+
/// CFG.
45+
/// NOTE: This function is typically being called in a hot part of the
46+
/// analysis and should therefore be highly optimized for performance.
47+
{ CF.getSuccsOf(Inst) } -> psr::is_iterable_over_v<typename T::n_t>;
48+
49+
/// Returns an iterable range of all starting instructions of the given
50+
/// function. For a forward-CFG, this is typically a singleton range.
51+
{ CF.getStartPointsOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
52+
53+
/// Returns whether the given Inst is a root node of the CFG
54+
{ CF.isStartPoint(Inst) } -> std::convertible_to<bool>;
55+
56+
/// Returns whether the given Inst is a leaf node of the CFG
57+
{ CF.isExitInst(Inst) } -> std::convertible_to<bool>;
58+
59+
requires InstructionClassifier<T>;
60+
};
61+
62+
template <typename T>
63+
concept BidiCFG =
64+
CFG<T> && requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
65+
/// Returns an iterable range of all predecessor instructions of Inst in
66+
/// the CFG
67+
{ CF.getPredsOf(Inst) } -> psr::is_iterable_over_v<typename T::n_t>;
68+
69+
/// Returns an iterable range of all exit instructions (often return
70+
/// instructions) of the given function. For a backward-CFG, this is
71+
/// typically a singleton range
72+
{ CF.getExitPointsOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
73+
};
74+
75+
template <typename T>
76+
concept CFGDump = requires(const T &CF, typename T::n_t Inst,
77+
typename T::f_t Fun, llvm::raw_ostream &OS) {
78+
{ CF.getStatementId(Inst) } -> psr::is_string_like_v;
79+
{ CF.getFunctionName(Fun) } -> psr::is_string_like_v;
80+
{ CF.getDemangledFunctionName(Fun) } -> psr::is_string_like_v;
81+
CF.print(Fun, OS);
82+
};
83+
84+
template <typename T>
85+
concept CFGEdgesProvider = requires(const T &CF, typename T::f_t Fun) {
86+
{
87+
CF.getAllControlFlowEdges(Fun)
88+
} -> psr::is_iterable_over_v<std::pair<typename T::n_t, typename T::n_t>>;
89+
};
90+
} // namespace psr

include/phasar/ControlFlow/CFGBase.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#ifndef PHASAR_CONTROLFLOW_CFGBASE_H
1111
#define PHASAR_CONTROLFLOW_CFGBASE_H
1212

13+
#include "phasar/ControlFlow/CFG.h"
1314
#include "phasar/Utils/ByRef.h"
1415
#include "phasar/Utils/CRTPUtils.h"
1516
#include "phasar/Utils/TypeTraits.h"
@@ -139,9 +140,9 @@ template <typename Derived> class CFGBase : public CRTPBase<Derived> {
139140

140141
template <typename ICF, typename Domain>
141142
// NOLINTNEXTLINE(readability-identifier-naming)
142-
concept is_cfg_v = is_crtp_base_of_v<CFGBase, ICF> &&
143-
std::is_same_v<typename ICF::n_t, typename Domain::n_t> &&
144-
std::is_same_v<typename ICF::f_t, typename Domain::f_t>;
143+
concept is_cfg_v = BidiCFG<ICF> && CFGDump<ICF> && CFGEdgesProvider<ICF> &&
144+
std::same_as<typename ICF::n_t, typename Domain::n_t> &&
145+
std::same_as<typename ICF::f_t, typename Domain::f_t>;
145146

146147
} // namespace psr
147148

include/phasar/ControlFlow/CallGraphBase.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,54 @@
1414
#include "phasar/Utils/CRTPUtils.h"
1515
#include "phasar/Utils/TypeTraits.h"
1616

17+
#include <concepts>
18+
#include <type_traits>
19+
1720
namespace psr {
21+
22+
template <typename T>
23+
concept IsCallGraph =
24+
requires(const T &CG, typename T::n_t Inst, typename T::f_t Fun) {
25+
typename T::n_t;
26+
typename T::f_t;
27+
28+
/// Returns an iterable range of all possible callee candidates at the
29+
/// given call-site induced by the used call-graph.
30+
///
31+
/// NOTE: This function is typically called in a hot part of the analysis
32+
/// and should therefore be very fast
33+
{
34+
CG.getCalleesOfCallAt(Inst)
35+
} -> psr::is_iterable_over_v<typename T::f_t>;
36+
37+
/// Returns an iterable range of all possible call-site candidates that
38+
/// may call the given function induced by the used call-graph.
39+
{ CG.getCallersOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
40+
41+
/// A range of all functions that are vertices in the call-graph. The
42+
/// number of vertex functions can be retrieved by
43+
/// getNumVertexFunctions().
44+
{
45+
CG.getAllVertexFunctions()
46+
} -> psr::is_iterable_over_v<typename T::f_t>;
47+
48+
/// A range of all call-sites that are vertices in the call-graph. The
49+
/// number of vertex-callsites can be retrived by getNumVertexCallSites().
50+
{
51+
CG.getAllVertexCallSites()
52+
} -> psr::is_iterable_over_v<typename T::n_t>;
53+
54+
{ CG.getNumVertexFunctions() } -> std::convertible_to<size_t>;
55+
{ CG.getNumVertexCallSites() } -> std::convertible_to<size_t>;
56+
57+
/// Same as getNumVertexFunctions()
58+
{ CG.size() } noexcept -> std::convertible_to<size_t>;
59+
{ CG.empty() } noexcept -> std::convertible_to<bool>;
60+
};
61+
62+
template <typename T>
63+
concept IsCallGraphRef = IsCallGraph<std::remove_cvref_t<T>>;
64+
1865
template <typename T> struct CGTraits {
1966
// using n_t
2067
// using f_t

include/phasar/ControlFlow/ICFG.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/******************************************************************************
2+
* Copyright (c) 2026 Fabian Schiebel.
3+
* All rights reserved. This program and the accompanying materials are made
4+
* available under the terms of LICENSE.txt.
5+
*
6+
* Contributors:
7+
* Fabian Schiebel and others
8+
*****************************************************************************/
9+
#pragma once
10+
11+
#include "phasar/ControlFlow/CallGraphBase.h"
12+
#include "phasar/Utils/Nullable.h"
13+
#include "phasar/Utils/TypeTraits.h"
14+
15+
#include "llvm/ADT/StringRef.h"
16+
#include "llvm/Support/raw_ostream.h"
17+
18+
#include <concepts>
19+
20+
namespace psr {
21+
template <typename T>
22+
concept ICFG = requires(const T &ICF, llvm::StringRef Name,
23+
typename T::n_t Inst, typename T::f_t Fun) {
24+
typename T::f_t;
25+
typename T::n_t;
26+
27+
// TODO: Should not be duplicated with ProjectIRDB
28+
{ ICF.getAllFunctions() } -> is_iterable_over_v<typename T::f_t>;
29+
// TODO: Should not be duplicated with ProjectIRDB
30+
{ ICF.getFunction(Name) } -> std::convertible_to<Nullable<typename T::f_t>>;
31+
32+
{ ICF.isIndirectFunctionCall(Inst) } -> std::convertible_to<bool>;
33+
{ ICF.isVirtualFunctionCall(Inst) } -> std::convertible_to<bool>;
34+
35+
/// Gets the underlying call-graph
36+
{ ICF.getCallGraph() } -> psr::IsCallGraphRef;
37+
/// Returns an iterable range of all possible callee candidates at the given
38+
/// call-site induced by the used call-graph. Same as
39+
/// getCallGraph().getCalleesOfCallAt(Inst)
40+
{ ICF.getCalleesOfCallAt(Inst) } -> psr::is_iterable_over_v<typename T::f_t>;
41+
/// Returns an iterable range of all possible call-site candidates that may
42+
/// call the given function induced by the used call-graph. Same as
43+
/// getCallGraph().getCallersOf(Fun)
44+
{ ICF.getCallersOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
45+
46+
/// Returns an iterable range of all call-instruction in the given function
47+
{ ICF.getCallsFromWithin(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
48+
49+
/// Returns an iterable range of all instructions in all functions of the ICFG
50+
/// that are neither call-sites nor start-points of a function
51+
// TODO: Get rid of this function
52+
{ ICF.allNonCallStartNodes() } -> psr::is_iterable_over_v<typename T::n_t>;
53+
54+
/// The total number of call-sites in the ICFG. Same as
55+
/// getCallGraph().getNumVertexCallSites()
56+
{ ICF.getNumCallSites() } noexcept -> std::convertible_to<size_t>;
57+
};
58+
59+
template <typename T>
60+
concept ICFGDump = requires(const T &ICF, llvm::raw_ostream &OS) {
61+
ICF.print(OS);
62+
ICF.printAsJson(OS);
63+
};
64+
} // namespace psr

include/phasar/ControlFlow/ICFGBase.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "phasar/ControlFlow/CFGBase.h"
1414
#include "phasar/ControlFlow/CallGraphBase.h"
15+
#include "phasar/ControlFlow/ICFG.h"
1516
#include "phasar/Utils/CRTPUtils.h"
1617
#include "phasar/Utils/TypeTraits.h"
1718

@@ -118,7 +119,7 @@ template <typename Derived> class ICFGBase : public CRTPBase<Derived> {
118119
/// from the given analysis-Domain
119120
template <typename ICF, typename Domain>
120121
// NOLINTNEXTLINE(readability-identifier-naming)
121-
concept is_icfg_v = is_crtp_base_of_v<ICFGBase, ICF> &&
122+
concept is_icfg_v = ICFG<ICF> && ICFGDump<ICF> &&
122123
std::is_same_v<typename ICF::n_t, typename Domain::n_t> &&
123124
std::is_same_v<typename ICF::f_t, typename Domain::f_t>;
124125

include/phasar/DB.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define PHASAR_DB_H
1212

1313
#include "phasar/Config/phasar-config.h"
14+
#include "phasar/DB/ProjectIRDB.h"
1415
#include "phasar/DB/ProjectIRDBBase.h"
1516

1617
#endif // PHASAR_DB_H

0 commit comments

Comments
 (0)