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

daisytuner / sdfglib / 17973211493

24 Sep 2025 10:00AM UTC coverage: 61.252% (+0.03%) from 61.22%
17973211493

Pull #243

github

web-flow
Merge 10606d1fc into c36de2b31
Pull Request #243: Make loopindex stable

9 of 10 new or added lines in 3 files covered. (90.0%)

3 existing lines in 1 file now uncovered.

9559 of 15606 relevant lines covered (61.25%)

108.8 hits per line

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

64.21
/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), loops_(), loop_tree_(DFSLoopComparator(&loops_)) {}
17✔
14

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

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

31
        if (dynamic_cast<structured_control_flow::Block*>(current)) {
97✔
32
            continue;
5✔
33
        } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
92✔
34
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
98✔
35
                queue.push_back(&sequence_stmt->at(i).first);
43✔
36
            }
43✔
37
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
92✔
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)) {
37✔
42
            this->run(while_stmt->root(), while_stmt);
×
43
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(current)) {
37✔
44
            this->run(for_stmt->root(), for_stmt);
37✔
45
        } else if (dynamic_cast<structured_control_flow::Break*>(current)) {
37✔
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
}
54✔
56

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

63
const std::vector<structured_control_flow::ControlFlowNode*> LoopAnalysis::loops() const { return this->loops_; }
10✔
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) {
12✔
83
    auto assums = assumptions_analysis.get(*loop, true);
12✔
84

85
    return symbolic::series::is_contiguous(loop->update(), loop->indvar(), assums);
12✔
86
}
12✔
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::map<structured_control_flow::ControlFlowNode*, structured_control_flow::ControlFlowNode*, DFSLoopComparator>&
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::
217
    children(sdfg::structured_control_flow::ControlFlowNode* node) const {
3✔
218
    // Find unique child
219
    return this->children(node, this->loop_tree_);
3✔
220
};
221

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

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

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

259
    for (auto& child : children) {
7✔
260
        auto p = this->loop_tree_paths(child, tree);
4✔
261
        for (auto& path : p) {
8✔
262
            path.insert(path.begin(), loop);
4✔
263
            paths.push_back(path);
4✔
264
        }
265
    }
4✔
266

267
    return paths;
3✔
268
};
6✔
269

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

288
} // namespace analysis
289
} // 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