• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daisytuner / sdfglib / 17638700812

11 Sep 2025 08:26AM UTC coverage: 59.686% (-0.07%) from 59.755%
17638700812

Pull #223

github

web-flow
Merge bfe09f68b into f744ac9f5
Pull Request #223: update loop analysis API

0 of 19 new or added lines in 1 file covered. (0.0%)

2 existing lines in 1 file now uncovered.

9755 of 16344 relevant lines covered (59.69%)

114.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

42.63
/src/analysis/loop_analysis.cpp
1
#include "sdfg/analysis/loop_analysis.h"
2
#include <unordered_set>
3
#include <vector>
4

5
#include "sdfg/analysis/assumptions_analysis.h"
6
#include "sdfg/structured_control_flow/structured_loop.h"
7
#include "sdfg/symbolic/conjunctive_normal_form.h"
8
#include "sdfg/symbolic/series.h"
9

10
namespace sdfg {
11
namespace analysis {
12

13
LoopAnalysis::LoopAnalysis(StructuredSDFG& sdfg) : Analysis(sdfg) {}
8✔
14

15
void LoopAnalysis::
16
    run(structured_control_flow::ControlFlowNode& scope, structured_control_flow::ControlFlowNode* parent_loop) {
24✔
17
    std::list<structured_control_flow::ControlFlowNode*> queue = {&scope};
24✔
18
    while (!queue.empty()) {
69✔
19
        auto current = queue.front();
45✔
20
        queue.pop_front();
45✔
21

22
        // Loop detected
23
        if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
45✔
24
            this->loops_.insert(while_stmt);
×
25
            this->loop_tree_[while_stmt] = parent_loop;
×
26
        } else if (auto loop_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(current)) {
45✔
27
            this->loops_.insert(loop_stmt);
16✔
28
            this->loop_tree_[loop_stmt] = parent_loop;
16✔
29
        }
16✔
30

31
        if (dynamic_cast<structured_control_flow::Block*>(current)) {
45✔
32
            continue;
5✔
33
        } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
40✔
34
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
45✔
35
                queue.push_back(&sequence_stmt->at(i).first);
21✔
36
            }
21✔
37
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
40✔
38
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
39
                queue.push_back(&if_else_stmt->at(i).first);
×
40
            }
×
41
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
16✔
42
            this->run(while_stmt->root(), while_stmt);
×
43
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(current)) {
16✔
44
            this->run(for_stmt->root(), for_stmt);
16✔
45
        } else if (dynamic_cast<structured_control_flow::Break*>(current)) {
16✔
46
            continue;
×
47
        } else if (dynamic_cast<structured_control_flow::Continue*>(current)) {
×
48
            continue;
×
49
        } else if (dynamic_cast<structured_control_flow::Return*>(current)) {
×
50
            continue;
×
51
        } else {
52
            throw std::runtime_error("Unsupported control flow node type");
×
53
        }
54
    }
55
}
24✔
56

57
void LoopAnalysis::run(AnalysisManager& analysis_manager) {
8✔
58
    this->loops_.clear();
8✔
59
    this->loop_tree_.clear();
8✔
60
    this->run(this->sdfg_.root(), nullptr);
8✔
61
}
8✔
62

63
const std::unordered_set<structured_control_flow::ControlFlowNode*> LoopAnalysis::loops() const { return this->loops_; }
7✔
64

65
structured_control_flow::ControlFlowNode* LoopAnalysis::find_loop_by_indvar(const std::string& indvar) {
×
66
    for (auto& loop : this->loops_) {
×
67
        if (auto loop_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(loop)) {
×
68
            if (loop_stmt->indvar()->get_name() == indvar) {
×
69
                return loop;
×
70
            }
71
        }
×
72
    }
73
    return nullptr;
×
74
}
×
75

76
bool LoopAnalysis::is_monotonic(structured_control_flow::StructuredLoop* loop, AssumptionsAnalysis& assumptions_analysis) {
80✔
77
    auto assums = assumptions_analysis.get(*loop, true);
80✔
78

79
    return symbolic::series::is_monotonic(loop->update(), loop->indvar(), assums);
80✔
80
}
80✔
81

82
bool LoopAnalysis::is_contiguous(structured_control_flow::StructuredLoop* loop, AssumptionsAnalysis& assumptions_analysis) {
15✔
83
    auto assums = assumptions_analysis.get(*loop, true);
15✔
84

85
    return symbolic::series::is_contiguous(loop->update(), loop->indvar(), assums);
15✔
86
}
15✔
87

88
symbolic::Expression LoopAnalysis::
89
    canonical_bound(structured_control_flow::StructuredLoop* loop, AssumptionsAnalysis& assumptions_analysis) {
13✔
90
    if (!LoopAnalysis::is_monotonic(loop, assumptions_analysis)) {
13✔
91
        return SymEngine::null;
×
92
    }
93

94
    symbolic::CNF cnf;
13✔
95
    try {
96
        cnf = symbolic::conjunctive_normal_form(loop->condition());
13✔
97
    } catch (const std::runtime_error& e) {
13✔
98
        return SymEngine::null;
×
99
    }
×
100

101
    bool has_complex_clauses = false;
13✔
102
    for (auto& clause : cnf) {
28✔
103
        if (clause.size() > 1) {
15✔
104
            has_complex_clauses = true;
×
105
            break;
×
106
        }
107
    }
108
    if (has_complex_clauses) {
13✔
109
        return SymEngine::null;
×
110
    }
111

112
    auto indvar = loop->indvar();
13✔
113
    symbolic::Expression bound = SymEngine::null;
13✔
114
    for (auto& clause : cnf) {
28✔
115
        for (auto& literal : clause) {
30✔
116
            if (SymEngine::is_a<SymEngine::StrictLessThan>(*literal)) {
15✔
117
                auto lt = SymEngine::rcp_dynamic_cast<const SymEngine::StrictLessThan>(literal);
13✔
118
                auto lhs = lt->get_args()[0];
13✔
119
                auto rhs = lt->get_args()[1];
13✔
120
                if (SymEngine::eq(*lhs, *indvar)) {
13✔
121
                    if (bound == SymEngine::null) {
13✔
122
                        bound = rhs;
12✔
123
                    } else {
12✔
124
                        bound = symbolic::min(bound, rhs);
1✔
125
                    }
126
                } else {
13✔
127
                    return SymEngine::null;
×
128
                }
129
            } else if (SymEngine::is_a<SymEngine::LessThan>(*literal)) {
15✔
130
                auto le = SymEngine::rcp_dynamic_cast<const SymEngine::LessThan>(literal);
2✔
131
                auto lhs = le->get_args()[0];
2✔
132
                auto rhs = le->get_args()[1];
2✔
133
                if (SymEngine::eq(*lhs, *indvar)) {
2✔
134
                    if (bound == SymEngine::null) {
2✔
135
                        bound = symbolic::add(rhs, symbolic::one());
1✔
136
                    } else {
1✔
137
                        bound = symbolic::min(bound, symbolic::add(rhs, symbolic::one()));
1✔
138
                    }
139
                } else {
2✔
140
                    return SymEngine::null;
×
141
                }
142
            } else {
2✔
143
                return SymEngine::null;
×
144
            }
145
        }
146
    }
147

148
    return bound;
13✔
149
}
13✔
150

151
symbolic::Integer LoopAnalysis::stride(structured_control_flow::StructuredLoop* loop) {
×
152
    auto expr = loop->update();
×
153
    auto indvar = loop->indvar();
×
154

155
    if (SymEngine::is_a<SymEngine::Add>(*expr)) {
×
156
        auto add_expr = SymEngine::rcp_static_cast<const SymEngine::Add>(expr);
×
157
        if (add_expr->get_args().size() != 2) {
×
158
            return SymEngine::null;
×
159
        }
160
        auto arg1 = add_expr->get_args()[0];
×
161
        auto arg2 = add_expr->get_args()[1];
×
162
        if (symbolic::eq(arg1, indvar)) {
×
163
            if (SymEngine::is_a<SymEngine::Integer>(*arg2)) {
×
164
                return SymEngine::rcp_static_cast<const SymEngine::Integer>(arg2);
×
165
            }
166
        }
×
167
        if (symbolic::eq(arg2, indvar)) {
×
168
            if (SymEngine::is_a<SymEngine::Integer>(*arg1)) {
×
169
                return SymEngine::rcp_static_cast<const SymEngine::Integer>(arg1);
×
170
            }
171
        }
×
172
    }
×
173
    return SymEngine::null;
×
174
}
×
175

176
const std::unordered_map<structured_control_flow::ControlFlowNode*, structured_control_flow::ControlFlowNode*>&
177
LoopAnalysis::loop_tree() const {
×
178
    return this->loop_tree_;
×
179
}
180

181
structured_control_flow::ControlFlowNode* LoopAnalysis::parent_loop(structured_control_flow::ControlFlowNode* loop
×
182
) const {
183
    return this->loop_tree_.at(loop);
×
184
}
185

186
const std::vector<structured_control_flow::ControlFlowNode*> LoopAnalysis::outermost_loops() const {
1✔
187
    std::vector<structured_control_flow::ControlFlowNode*> outermost_loops_;
1✔
188
    for (const auto& [loop, parent] : this->loop_tree_) {
3✔
189
        if (parent == nullptr) {
2✔
190
            outermost_loops_.push_back(loop);
1✔
191
        }
1✔
192
    }
193
    return outermost_loops_;
1✔
194
}
1✔
195

196
const std::vector<structured_control_flow::ControlFlowNode*> LoopAnalysis::outermost_maps() const {
×
197
    std::vector<structured_control_flow::ControlFlowNode*> outermost_maps_;
×
198
    for (const auto& [loop, parent] : this->loop_tree_) {
×
199
        if (dynamic_cast<structured_control_flow::Map*>(loop)) {
×
200
            auto ancestor = parent;
×
201
            while (true) {
×
202
                if (ancestor == nullptr) {
×
203
                    outermost_maps_.push_back(loop);
×
204
                    break;
×
205
                }
206
                if (dynamic_cast<structured_control_flow::Map*>(ancestor)) {
×
207
                    break;
×
208
                }
209
                ancestor = this->loop_tree_.at(ancestor);
×
210
            }
211
        }
×
212
    }
213
    return outermost_maps_;
×
214
}
×
215

216
std::vector<sdfg::structured_control_flow::ControlFlowNode*> LoopAnalysis::
NEW
217
    children(sdfg::structured_control_flow::ControlFlowNode* node) const {
×
218
    // Find unique child
NEW
219
    return this->children(node, this->loop_tree_);
×
220
};
221

UNCOV
222
std::vector<sdfg::structured_control_flow::ControlFlowNode*> LoopAnalysis::children(
×
223
    sdfg::structured_control_flow::ControlFlowNode* node,
224
    const std::unordered_map<
225
        sdfg::structured_control_flow::ControlFlowNode*,
226
        sdfg::structured_control_flow::ControlFlowNode*>& tree
227
) const {
228
    // Find unique child
229
    std::vector<sdfg::structured_control_flow::ControlFlowNode*> c;
×
230
    for (auto& entry : tree) {
×
231
        if (entry.second == node) {
×
232
            c.push_back(entry.first);
×
233
        }
×
234
    }
235
    return c;
×
236
};
×
237

238
std::list<std::vector<sdfg::structured_control_flow::ControlFlowNode*>> LoopAnalysis::
NEW
239
    loop_tree_paths(sdfg::structured_control_flow::ControlFlowNode* loop) const {
×
NEW
240
    return this->loop_tree_paths(loop, this->loop_tree_);
×
241
};
242

UNCOV
243
std::list<std::vector<sdfg::structured_control_flow::ControlFlowNode*>> LoopAnalysis::loop_tree_paths(
×
244
    sdfg::structured_control_flow::ControlFlowNode* loop,
245
    const std::unordered_map<
246
        sdfg::structured_control_flow::ControlFlowNode*,
247
        sdfg::structured_control_flow::ControlFlowNode*>& tree
248
) const {
249
    // Collect all paths in tree starting from loop recursively (DFS)
250
    std::list<std::vector<sdfg::structured_control_flow::ControlFlowNode*>> paths;
×
251
    auto children = this->children(loop, tree);
×
252
    if (children.empty()) {
×
253
        paths.push_back({loop});
×
254
        return paths;
×
255
    }
256

257
    for (auto& child : children) {
×
258
        auto p = this->loop_tree_paths(child, tree);
×
259
        for (auto& path : p) {
×
260
            path.insert(path.begin(), loop);
×
261
            paths.push_back(path);
×
262
        }
263
    }
×
264

265
    return paths;
×
266
};
×
267

268
std::unordered_set<sdfg::structured_control_flow::ControlFlowNode*> LoopAnalysis::
NEW
269
    descendants(sdfg::structured_control_flow::ControlFlowNode* loop) const {
×
NEW
270
    std::unordered_set<sdfg::structured_control_flow::ControlFlowNode*> desc;
×
NEW
271
    std::list<sdfg::structured_control_flow::ControlFlowNode*> queue = {loop};
×
NEW
272
    while (!queue.empty()) {
×
NEW
273
        auto current = queue.front();
×
NEW
274
        queue.pop_front();
×
NEW
275
        auto children = this->children(current, this->loop_tree_);
×
NEW
276
        for (auto& child : children) {
×
NEW
277
            if (desc.find(child) == desc.end()) {
×
NEW
278
                desc.insert(child);
×
NEW
279
                queue.push_back(child);
×
NEW
280
            }
×
281
        }
NEW
282
    }
×
NEW
283
    return desc;
×
NEW
284
}
×
285

286
} // namespace analysis
287
} // namespace sdfg
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc