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

daisytuner / sdfglib / 20498791379

24 Dec 2025 12:27PM UTC coverage: 39.109% (+0.05%) from 39.061%
20498791379

push

github

web-flow
Merge pull request #406 from daisytuner/loop-info-inst

move loopnest index into loop info

13537 of 44946 branches covered (30.12%)

Branch coverage included in aggregate %.

15 of 46 new or added lines in 9 files covered. (32.61%)

1 existing line in 1 file now uncovered.

11669 of 19504 relevant lines covered (59.83%)

84.09 hits per line

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

54.9
/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/map.h"
7
#include "sdfg/structured_control_flow/structured_loop.h"
8
#include "sdfg/structured_control_flow/while.h"
9
#include "sdfg/symbolic/conjunctive_normal_form.h"
10
#include "sdfg/symbolic/series.h"
11

12
namespace sdfg {
13
namespace analysis {
14

15
LoopAnalysis::LoopAnalysis(StructuredSDFG& sdfg) : Analysis(sdfg), loops_(), loop_tree_(DFSLoopComparator(&loops_)) {}
65!
16

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

24
        // Loop detected
25
        if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
397!
26
            this->loops_.push_back(while_stmt);
2!
27
            this->loop_tree_[while_stmt] = parent_loop;
2!
28
        } else if (auto loop_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(current)) {
397!
29
            this->loops_.push_back(loop_stmt);
120!
30
            this->loop_tree_[loop_stmt] = parent_loop;
120!
31
        }
120✔
32

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

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

65
    // Loop info for outermost loops
66
    int loopnest_index = 0;
65✔
67
    for (const auto& [loop, parent] : this->loop_tree_) {
411✔
68
        if (parent != nullptr) {
122✔
69
            continue;
47✔
70
        }
71

72
        LoopInfo info;
75✔
73
        info.loopnest_index = loopnest_index;
75✔
74
        loopnest_index++;
75✔
75

76
        auto descendants = this->descendants(loop);
75✔
77
        descendants.insert(loop);
75!
78

79
        // Structure of loop nest
80
        info.num_loops = descendants.size();
75✔
81
        info.max_depth = 0;
75✔
82
        for (const auto& path : this->loop_tree_paths(loop)) {
153!
83
            info.max_depth = std::max(info.max_depth, path.size());
78!
84
        }
85

86
        info.is_perfectly_nested = true;
75✔
87
        auto current = loop;
75✔
88
        while (true) {
112✔
89
            auto children = this->children(current);
112!
90
            if (children.empty()) {
112✔
91
                break;
68✔
92
            }
93

94
            if (children.size() > 1) {
44✔
95
                info.is_perfectly_nested = false;
3✔
96
                break;
3✔
97
            }
98

99
            auto child = children[0];
41✔
100
            structured_control_flow::Sequence* root = nullptr;
41✔
101

102
            if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
41!
103
                root = &while_stmt->root();
×
104
            } else if (auto loop_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(current)) {
41!
105
                root = &loop_stmt->root();
41!
106
            }
41✔
107

108
            if (root == nullptr || root->size() != 1 || &root->at(0).first != child) {
41!
109
                info.is_perfectly_nested = false;
4✔
110
                break;
4✔
111
            }
112

113
            current = child;
37✔
114
        }
112✔
115

116
        // Count types of loops
117
        info.num_maps = 0;
75✔
118
        info.num_fors = 0;
75✔
119
        info.num_whiles = 0;
75✔
120
        for (auto node : descendants) {
197✔
121
            if (dynamic_cast<structured_control_flow::Map*>(node)) {
122!
122
                info.num_maps++;
20✔
123
            } else if (dynamic_cast<structured_control_flow::StructuredLoop*>(node)) {
122!
124
                info.num_fors++;
100✔
125
            } else if (dynamic_cast<structured_control_flow::While*>(node)) {
102!
126
                info.num_whiles++;
2✔
127
            }
2✔
128
        }
129
        info.is_perfectly_parallel = (info.num_loops == info.num_maps);
75✔
130

131
        // Classifiy loop nest
132
        info.is_elementwise = false;
75✔
133
        if (info.is_perfectly_nested && info.is_perfectly_parallel) {
75✔
134
            bool all_contiguous = true;
8✔
135
            for (auto node : descendants) {
16✔
136
                if (auto loop_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(node)) {
11!
137
                    bool is_contiguous =
11✔
138
                        symbolic::series::is_contiguous(loop_stmt->update(), loop_stmt->indvar(), symbolic::Assumptions());
11!
139
                    if (!is_contiguous) {
11✔
140
                        all_contiguous = false;
3✔
141
                        break;
3✔
142
                    }
143
                } else {
8✔
144
                    all_contiguous = false;
×
145
                    break;
×
146
                }
147
            }
148
            info.is_elementwise = all_contiguous;
8✔
149
        }
8✔
150

151
        // Criterion: Loop must not have side-effecting body
152
        structured_control_flow::Sequence* root = nullptr;
75✔
153
        if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(loop)) {
75!
154
            root = &while_stmt->root();
1!
155
        } else if (auto loop_stmt = dynamic_cast<structured_control_flow::For*>(loop)) {
75!
156
            root = &loop_stmt->root();
64!
157
        }
64✔
158
        // Maps cannot have side effects by definition
159

160
        info.has_side_effects = false;
75✔
161
        if (root != nullptr) {
75✔
162
            std::list<const structured_control_flow::ControlFlowNode*> queue = {root};
65!
163
            while (!queue.empty()) {
283✔
164
                auto current = queue.front();
218✔
165
                queue.pop_front();
218✔
166

167
                if (auto block = dynamic_cast<const structured_control_flow::Block*>(current)) {
218!
168
                    for (auto& node : block->dataflow().nodes()) {
329!
169
                        if (auto library_node = dynamic_cast<const data_flow::LibraryNode*>(&node)) {
254!
170
                            if (library_node->side_effect()) {
×
171
                                info.has_side_effects = true;
×
172
                                break;
×
173
                            }
174
                        }
×
175
                    }
176
                } else if (auto seq = dynamic_cast<const structured_control_flow::Sequence*>(current)) {
218!
177
                    for (size_t i = 0; i < seq->size(); i++) {
218!
178
                        auto& child = seq->at(i).first;
114!
179
                        queue.push_back(&child);
114!
180
                    }
114✔
181
                } else if (auto ifelse = dynamic_cast<const structured_control_flow::IfElse*>(current)) {
143!
182
                    for (size_t i = 0; i < ifelse->size(); i++) {
7!
183
                        auto& branch = ifelse->at(i).first;
4!
184
                        queue.push_back(&branch);
4!
185
                    }
4✔
186
                } else if (auto loop = dynamic_cast<const structured_control_flow::For*>(current)) {
39!
187
                    queue.push_back(&loop->root());
35!
188
                } else if (auto while_stmt = dynamic_cast<const structured_control_flow::While*>(current)) {
36!
189
                    queue.push_back(&while_stmt->root());
×
190
                } else if (auto loop = dynamic_cast<const structured_control_flow::Map*>(current)) {
1!
191
                    continue;
1✔
192
                } else if (auto for_stmt = dynamic_cast<const structured_control_flow::Break*>(current)) {
×
193
                    continue;
×
194
                } else if (auto for_stmt = dynamic_cast<const structured_control_flow::Continue*>(current)) {
×
195
                    continue;
×
196
                } else if (auto for_stmt = dynamic_cast<const structured_control_flow::Return*>(current)) {
×
197
                    info.has_side_effects = true;
×
198
                    break;
×
199
                } else {
200
                    throw InvalidSDFGException("Unknown control flow node type in Loop Analysis.");
×
201
                }
202
            }
203
        }
65✔
204

205
        this->loop_infos_[loop] = info;
150!
206
    }
75✔
207
}
65✔
208

209
const std::vector<structured_control_flow::ControlFlowNode*> LoopAnalysis::loops() const { return this->loops_; }
4✔
210

211
LoopInfo LoopAnalysis::loop_info(structured_control_flow::ControlFlowNode* loop) const {
11✔
212
    if (this->loop_infos_.find(loop) == this->loop_infos_.end()) {
11!
NEW
213
        return LoopInfo();
×
214
    }
215
    return this->loop_infos_.at(loop);
11✔
216
}
11✔
217

218
structured_control_flow::ControlFlowNode* LoopAnalysis::find_loop_by_indvar(const std::string& indvar) {
×
219
    for (auto& loop : this->loops_) {
×
220
        if (auto loop_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(loop)) {
×
221
            if (loop_stmt->indvar()->get_name() == indvar) {
×
222
                return loop;
×
223
            }
224
        }
×
225
    }
226
    return nullptr;
×
227
}
×
228

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

232
    return symbolic::series::is_monotonic(loop->update(), loop->indvar(), assums);
75!
233
}
75✔
234

235
bool LoopAnalysis::is_contiguous(structured_control_flow::StructuredLoop* loop, AssumptionsAnalysis& assumptions_analysis) {
12✔
236
    auto assums = assumptions_analysis.get(*loop, true);
12✔
237

238
    return symbolic::series::is_contiguous(loop->update(), loop->indvar(), assums);
12!
239
}
12✔
240

241
symbolic::Expression LoopAnalysis::
242
    canonical_bound(structured_control_flow::StructuredLoop* loop, AssumptionsAnalysis& assumptions_analysis) {
3✔
243
    if (!LoopAnalysis::is_monotonic(loop, assumptions_analysis)) {
3!
244
        return SymEngine::null;
×
245
    }
246

247
    symbolic::CNF cnf;
3✔
248
    try {
249
        cnf = symbolic::conjunctive_normal_form(loop->condition());
3!
250
    } catch (const std::runtime_error& e) {
3!
251
        return SymEngine::null;
×
252
    }
×
253

254
    bool has_complex_clauses = false;
3✔
255
    for (auto& clause : cnf) {
8✔
256
        if (clause.size() > 1) {
5!
257
            has_complex_clauses = true;
×
258
            break;
×
259
        }
260
    }
261
    if (has_complex_clauses) {
3!
262
        return SymEngine::null;
×
263
    }
264

265
    auto indvar = loop->indvar();
3!
266
    symbolic::Expression bound = SymEngine::null;
3!
267
    for (auto& clause : cnf) {
8✔
268
        for (auto& literal : clause) {
10✔
269
            if (SymEngine::is_a<SymEngine::StrictLessThan>(*literal)) {
5!
270
                auto lt = SymEngine::rcp_dynamic_cast<const SymEngine::StrictLessThan>(literal);
3!
271
                auto lhs = lt->get_args()[0];
3!
272
                auto rhs = lt->get_args()[1];
3!
273
                if (SymEngine::eq(*lhs, *indvar)) {
3!
274
                    if (bound == SymEngine::null) {
3!
275
                        bound = rhs;
2!
276
                    } else {
2✔
277
                        bound = symbolic::min(bound, rhs);
1!
278
                    }
279
                } else {
3✔
280
                    return SymEngine::null;
×
281
                }
282
            } else if (SymEngine::is_a<SymEngine::LessThan>(*literal)) {
5!
283
                auto le = SymEngine::rcp_dynamic_cast<const SymEngine::LessThan>(literal);
2!
284
                auto lhs = le->get_args()[0];
2!
285
                auto rhs = le->get_args()[1];
2!
286
                if (SymEngine::eq(*lhs, *indvar)) {
2!
287
                    if (bound == SymEngine::null) {
2!
288
                        bound = symbolic::add(rhs, symbolic::one());
1!
289
                    } else {
1✔
290
                        bound = symbolic::min(bound, symbolic::add(rhs, symbolic::one()));
1!
291
                    }
292
                } else {
2✔
293
                    return SymEngine::null;
×
294
                }
295
            } else {
2!
296
                return SymEngine::null;
×
297
            }
298
        }
299
    }
300

301
    return bound;
3✔
302
}
3✔
303

304
symbolic::Integer LoopAnalysis::stride(structured_control_flow::StructuredLoop* loop) {
×
305
    auto expr = loop->update();
×
306
    auto indvar = loop->indvar();
×
307

308
    if (SymEngine::is_a<SymEngine::Add>(*expr)) {
×
309
        auto add_expr = SymEngine::rcp_static_cast<const SymEngine::Add>(expr);
×
310
        if (add_expr->get_args().size() != 2) {
×
311
            return SymEngine::null;
×
312
        }
313
        auto arg1 = add_expr->get_args()[0];
×
314
        auto arg2 = add_expr->get_args()[1];
×
315
        if (symbolic::eq(arg1, indvar)) {
×
316
            if (SymEngine::is_a<SymEngine::Integer>(*arg2)) {
×
317
                return SymEngine::rcp_static_cast<const SymEngine::Integer>(arg2);
×
318
            }
319
        }
×
320
        if (symbolic::eq(arg2, indvar)) {
×
321
            if (SymEngine::is_a<SymEngine::Integer>(*arg1)) {
×
322
                return SymEngine::rcp_static_cast<const SymEngine::Integer>(arg1);
×
323
            }
324
        }
×
325
    }
×
326
    return SymEngine::null;
×
327
}
×
328

329
const std::map<structured_control_flow::ControlFlowNode*, structured_control_flow::ControlFlowNode*, DFSLoopComparator>&
330
LoopAnalysis::loop_tree() const {
8✔
331
    return this->loop_tree_;
8✔
332
}
333

334
structured_control_flow::ControlFlowNode* LoopAnalysis::parent_loop(structured_control_flow::ControlFlowNode* loop
×
335
) const {
336
    return this->loop_tree_.at(loop);
×
337
}
338

339
const std::vector<structured_control_flow::ControlFlowNode*> LoopAnalysis::outermost_loops() const {
6✔
340
    std::vector<structured_control_flow::ControlFlowNode*> outermost_loops_;
6✔
341
    for (const auto& [loop, parent] : this->loop_tree_) {
29✔
342
        if (parent == nullptr) {
23✔
343
            outermost_loops_.push_back(loop);
13!
344
        }
13✔
345
    }
346
    return outermost_loops_;
6✔
347
}
6!
348

349
bool LoopAnalysis::is_outermost_loop(structured_control_flow::ControlFlowNode* loop) const {
×
350
    if (this->loop_tree_.find(loop) == this->loop_tree_.end()) {
×
351
        return false;
×
352
    }
353
    return this->loop_tree_.at(loop) == nullptr;
×
354
}
×
355

356
const std::vector<structured_control_flow::ControlFlowNode*> LoopAnalysis::outermost_maps() const {
×
357
    std::vector<structured_control_flow::ControlFlowNode*> outermost_maps_;
×
358
    for (const auto& [loop, parent] : this->loop_tree_) {
×
359
        if (dynamic_cast<structured_control_flow::Map*>(loop)) {
×
360
            auto ancestor = parent;
×
361
            while (true) {
×
362
                if (ancestor == nullptr) {
×
363
                    outermost_maps_.push_back(loop);
×
364
                    break;
×
365
                }
366
                if (dynamic_cast<structured_control_flow::Map*>(ancestor)) {
×
367
                    break;
×
368
                }
369
                ancestor = this->loop_tree_.at(ancestor);
×
370
            }
371
        }
×
372
    }
373
    return outermost_maps_;
×
374
}
×
375

376
std::vector<sdfg::structured_control_flow::ControlFlowNode*> LoopAnalysis::
377
    children(sdfg::structured_control_flow::ControlFlowNode* node) const {
115✔
378
    // Find unique child
379
    return this->children(node, this->loop_tree_);
115✔
380
};
381

382
std::vector<sdfg::structured_control_flow::ControlFlowNode*> LoopAnalysis::children(
468✔
383
    sdfg::structured_control_flow::ControlFlowNode* node,
384
    const std::map<
385
        sdfg::structured_control_flow::ControlFlowNode*,
386
        sdfg::structured_control_flow::ControlFlowNode*,
387
        DFSLoopComparator>& tree
388
) const {
389
    // Find unique child
390
    std::vector<sdfg::structured_control_flow::ControlFlowNode*> c;
468✔
391
    for (auto& entry : tree) {
1,707✔
392
        if (entry.second == node) {
1,239✔
393
            c.push_back(entry.first);
173!
394
        }
173✔
395
    }
396
    return c;
468✔
397
};
468!
398

399
std::list<std::vector<sdfg::structured_control_flow::ControlFlowNode*>> LoopAnalysis::
400
    loop_tree_paths(sdfg::structured_control_flow::ControlFlowNode* loop) const {
77✔
401
    return this->loop_tree_paths(loop, this->loop_tree_);
77✔
402
};
403

404
std::list<std::vector<sdfg::structured_control_flow::ControlFlowNode*>> LoopAnalysis::loop_tree_paths(
128✔
405
    sdfg::structured_control_flow::ControlFlowNode* loop,
406
    const std::map<
407
        sdfg::structured_control_flow::ControlFlowNode*,
408
        sdfg::structured_control_flow::ControlFlowNode*,
409
        DFSLoopComparator>& tree
410
) const {
411
    // Collect all paths in tree starting from loop recursively (DFS)
412
    std::list<std::vector<sdfg::structured_control_flow::ControlFlowNode*>> paths;
128✔
413
    auto children = this->children(loop, tree);
128!
414
    if (children.empty()) {
128✔
415
        paths.push_back({loop});
81!
416
        return paths;
81✔
417
    }
418

419
    for (auto& child : children) {
98✔
420
        auto p = this->loop_tree_paths(child, tree);
51!
421
        for (auto& path : p) {
102✔
422
            path.insert(path.begin(), loop);
51!
423
            paths.push_back(path);
51!
424
        }
425
    }
51✔
426

427
    return paths;
47✔
428
};
128!
429

430
std::unordered_set<sdfg::structured_control_flow::ControlFlowNode*> LoopAnalysis::
431
    descendants(sdfg::structured_control_flow::ControlFlowNode* loop) const {
154✔
432
    std::unordered_set<sdfg::structured_control_flow::ControlFlowNode*> desc;
154✔
433
    std::list<sdfg::structured_control_flow::ControlFlowNode*> queue = {loop};
154!
434
    while (!queue.empty()) {
379✔
435
        auto current = queue.front();
225✔
436
        queue.pop_front();
225✔
437
        auto children = this->children(current, this->loop_tree_);
225!
438
        for (auto& child : children) {
296✔
439
            if (desc.find(child) == desc.end()) {
71!
440
                desc.insert(child);
71!
441
                queue.push_back(child);
71!
442
            }
71✔
443
        }
444
    }
225✔
445
    return desc;
154✔
446
}
154!
447

448
} // namespace analysis
449
} // 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

© 2025 Coveralls, Inc