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

daisytuner / sdfglib / 16881279191

11 Aug 2025 01:20PM UTC coverage: 65.371% (-0.03%) from 65.402%
16881279191

push

github

web-flow
Merge pull request #190 from daisytuner/loops-duplicate-indvar-warning

Loop Analysis: relax requirement of unique indvars

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

1 existing line in 1 file now uncovered.

9133 of 13971 relevant lines covered (65.37%)

127.57 hits per line

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

47.37
/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/exceptions.h"
7
#include "sdfg/structured_control_flow/structured_loop.h"
8
#include "sdfg/symbolic/conjunctive_normal_form.h"
9
#include "sdfg/symbolic/series.h"
10

11
namespace sdfg {
12
namespace analysis {
13

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

149
    return bound;
20✔
150
}
20✔
151

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

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

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

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

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

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

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

233
std::list<std::vector<sdfg::structured_control_flow::ControlFlowNode*>> LoopAnalysis::loop_tree_paths(
×
234
    sdfg::structured_control_flow::ControlFlowNode* loop,
235
    const std::unordered_map<
236
        sdfg::structured_control_flow::ControlFlowNode*,
237
        sdfg::structured_control_flow::ControlFlowNode*>& tree
238
) const {
239
    // Collect all paths in tree starting from loop recursively (DFS)
240
    std::list<std::vector<sdfg::structured_control_flow::ControlFlowNode*>> paths;
×
241
    auto children = this->children(loop, tree);
×
242
    if (children.empty()) {
×
243
        paths.push_back({loop});
×
244
        return paths;
×
245
    }
246

247
    for (auto& child : children) {
×
248
        auto p = this->loop_tree_paths(child, tree);
×
249
        for (auto& path : p) {
×
250
            path.insert(path.begin(), loop);
×
251
            paths.push_back(path);
×
252
        }
253
    }
×
254

255
    return paths;
×
256
};
×
257

258
} // namespace analysis
259
} // 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