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

daisytuner / sdfglib / 16779684622

06 Aug 2025 02:21PM UTC coverage: 64.3% (-1.0%) from 65.266%
16779684622

push

github

web-flow
Merge pull request #172 from daisytuner/opaque-pointers

Opaque pointers, typed memlets, untyped tasklet connectors

330 of 462 new or added lines in 38 files covered. (71.43%)

382 existing lines in 30 files now uncovered.

8865 of 13787 relevant lines covered (64.3%)

116.73 hits per line

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

49.7
/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) {}
4✔
15

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

23
        // Loop detected
24
        if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
15✔
UNCOV
25
            this->loops_.insert(while_stmt);
×
UNCOV
26
            this->loop_tree_[while_stmt] = parent_loop;
×
27
        } else if (auto loop_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(current)) {
15✔
28
            this->loops_.insert(loop_stmt);
5✔
29
            auto res = this->indvars_.insert({loop_stmt->indvar()->get_name(), loop_stmt});
5✔
30
            if (!res.second) {
5✔
UNCOV
31
                throw sdfg::InvalidSDFGException("Found multiple loops with same indvar");
×
32
            }
33
            this->loop_tree_[loop_stmt] = parent_loop;
5✔
34
        }
5✔
35

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

62
void LoopAnalysis::run(AnalysisManager& analysis_manager) {
4✔
63
    this->loops_.clear();
4✔
64
    this->loop_tree_.clear();
4✔
65
    this->indvars_.clear();
4✔
66
    this->run(this->sdfg_.root(), nullptr);
4✔
67
}
4✔
68

69
const std::unordered_set<structured_control_flow::ControlFlowNode*> LoopAnalysis::loops() const { return this->loops_; }
3✔
70

UNCOV
71
structured_control_flow::ControlFlowNode* LoopAnalysis::find_loop_by_indvar(const std::string& indvar) {
×
UNCOV
72
    return this->indvars_.at(indvar);
×
73
}
74

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

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

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

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

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

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

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

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

147
    return bound;
6✔
148
}
6✔
149

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

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

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

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

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

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

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

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

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

UNCOV
253
    return paths;
×
UNCOV
254
};
×
255

256
} // namespace analysis
257
} // 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