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

daisytuner / sdfglib / 15796425724

21 Jun 2025 02:00PM UTC coverage: 64.059% (-0.5%) from 64.591%
15796425724

push

github

web-flow
Merge pull request #95 from daisytuner/data-dependencies

Extends Data Dependency Analysis

993 of 1197 new or added lines in 19 files covered. (82.96%)

55 existing lines in 5 files now uncovered.

8058 of 12579 relevant lines covered (64.06%)

150.28 hits per line

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

73.26
/src/analysis/users.cpp
1
#include "sdfg/analysis/users.h"
2

3
#include <cassert>
4
#include <list>
5
#include <memory>
6
#include <string>
7
#include <unordered_map>
8
#include <unordered_set>
9
#include <vector>
10

11
#include "sdfg/data_flow/memlet.h"
12
#include "sdfg/element.h"
13
#include "sdfg/graph/graph.h"
14
#include "sdfg/structured_control_flow/for.h"
15
#include "sdfg/structured_control_flow/map.h"
16
#include "sdfg/structured_control_flow/sequence.h"
17
#include "sdfg/structured_sdfg.h"
18
#include "sdfg/symbolic/sets.h"
19
#include "sdfg/symbolic/symbolic.h"
20

21
namespace sdfg {
22
namespace analysis {
23

24
User::User(graph::Vertex vertex, const std::string& container, Element* element, Use use)
2,005✔
25
    : vertex_(vertex), container_(container), use_(use), element_(element) {
2,005✔
26

27
      };
2,005✔
28

29
User::User(graph::Vertex vertex, const std::string& container, Element* element,
560✔
30
           data_flow::DataFlowGraph* parent, Use use)
31
    : vertex_(vertex), container_(container), use_(use), element_(element), parent_(parent) {
560✔
32

33
      };
560✔
34

35
User::~User() {
4,604✔
36

37
};
4,604✔
38

39
Use User::use() const { return this->use_; };
20,579✔
40

41
std::string& User::container() { return this->container_; };
116,027✔
42

43
Element* User::element() { return this->element_; };
4,548✔
44

45
data_flow::DataFlowGraph* User::parent() { return this->parent_; };
3✔
46

47
const std::vector<data_flow::Subset> User::subsets() const {
406✔
48
    if (this->container_ == "") {
406✔
49
        return {};
×
50
    }
51

52
    if (auto access_node = dynamic_cast<data_flow::AccessNode*>(this->element_)) {
406✔
53
        auto& graph = *this->parent_;
394✔
54
        if (this->use_ == Use::READ || this->use_ == Use::VIEW) {
394✔
55
            std::vector<data_flow::Subset> subsets;
72✔
56
            for (auto& iedge : graph.out_edges(*access_node)) {
148✔
57
                subsets.push_back(iedge.subset());
76✔
58
            }
59
            return subsets;
72✔
60
        } else if (this->use_ == Use::WRITE || this->use_ == Use::MOVE) {
394✔
61
            std::vector<data_flow::Subset> subsets;
322✔
62
            for (auto& oedge : graph.in_edges(*access_node)) {
644✔
63
                subsets.push_back(oedge.subset());
322✔
64
            }
65
            return subsets;
322✔
66
        }
322✔
67
    }
×
68

69
    // Use of symbol
70
    return {{}};
12✔
71
};
406✔
72

73
ForUser::ForUser(graph::Vertex vertex, const std::string& container, Element* element, Use use,
526✔
74
                 bool is_init, bool is_condition, bool is_update)
75
    : User(vertex, container, element, use),
526✔
76
      is_init_(is_init),
526✔
77
      is_condition_(is_condition),
526✔
78
      is_update_(is_update) {
1,052✔
79

80
      };
526✔
81

82
bool ForUser::is_init() const { return this->is_init_; };
530✔
83

84
bool ForUser::is_condition() const { return this->is_condition_; };
414✔
85

86
bool ForUser::is_update() const { return this->is_update_; };
213✔
87

88
void Users::init_dom_tree() {
145✔
89
    this->dom_tree_.clear();
145✔
90
    this->pdom_tree_.clear();
145✔
91

92
    // Compute dominator-tree
93
    auto dom_tree = graph::dominator_tree(this->graph_, this->source_->vertex_);
145✔
94
    for (auto& entry : dom_tree) {
2,710✔
95
        User* first = this->users_.at(entry.first).get();
2,565✔
96
        User* second = nullptr;
2,565✔
97
        if (entry.second != boost::graph_traits<graph::Graph>::null_vertex()) {
2,565✔
98
            second = this->users_.at(entry.second).get();
2,420✔
99
        }
2,420✔
100
        this->dom_tree_.insert({first, second});
2,565✔
101
    }
102

103
    // Compute post-dominator-tree
104
    auto pdom_tree = graph::post_dominator_tree(this->graph_, this->sink_->vertex_);
145✔
105
    for (auto& entry : pdom_tree) {
2,710✔
106
        User* first = this->users_.at(entry.first).get();
2,565✔
107
        User* second = nullptr;
2,565✔
108
        if (entry.second != boost::graph_traits<graph::Graph>::null_vertex()) {
2,565✔
109
            second = this->users_.at(entry.second).get();
2,420✔
110
        }
2,420✔
111
        this->pdom_tree_.insert({first, second});
2,565✔
112
    }
113
};
145✔
114

115
std::pair<graph::Vertex, graph::Vertex> Users::traverse(data_flow::DataFlowGraph& dataflow) {
226✔
116
    graph::Vertex first = boost::graph_traits<graph::Graph>::null_vertex();
226✔
117
    graph::Vertex last = boost::graph_traits<graph::Graph>::null_vertex();
226✔
118
    for (auto node : dataflow.topological_sort()) {
635✔
119
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
409✔
120
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
269✔
121
                if (dataflow.in_degree(*node) > 0) {
269✔
122
                    Use use = Use::WRITE;
141✔
123
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
281✔
124
                        if (iedge.src_conn() == "refs" || iedge.dst_conn() == "refs") {
141✔
125
                            use = Use::MOVE;
1✔
126
                            break;
1✔
127
                        }
128
                    }
129

130
                    auto v = boost::add_vertex(this->graph_);
141✔
131
                    this->add_user(std::make_unique<User>(v, access_node->data(), access_node,
141✔
132
                                                          &dataflow, use));
141✔
133

134
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
141✔
135
                        boost::add_edge(last, v, this->graph_);
117✔
136
                    } else {
117✔
137
                        first = v;
24✔
138
                    }
139
                    last = v;
141✔
140
                }
141✔
141
                if (dataflow.out_degree(*access_node) > 0) {
269✔
142
                    Use use = Use::READ;
132✔
143
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
267✔
144
                        if (oedge.src_conn() == "refs" || oedge.dst_conn() == "refs") {
136✔
145
                            use = Use::VIEW;
1✔
146
                            break;
1✔
147
                        }
148
                    }
149

150
                    auto v = boost::add_vertex(this->graph_);
132✔
151
                    this->add_user(std::make_unique<User>(v, access_node->data(), access_node,
132✔
152
                                                          &dataflow, use));
132✔
153

154
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
132✔
155
                        boost::add_edge(last, v, this->graph_);
34✔
156
                    } else {
34✔
157
                        first = v;
98✔
158
                    }
159
                    last = v;
132✔
160
                }
132✔
161
            }
269✔
162
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
409✔
163
            if (tasklet->is_conditional()) {
140✔
164
                auto& condition = tasklet->condition();
5✔
165
                for (auto& atom : symbolic::atoms(condition)) {
10✔
166
                    auto v = boost::add_vertex(this->graph_);
5✔
167
                    this->add_user(
5✔
168
                        std::make_unique<User>(v, atom->get_name(), tasklet, &dataflow, Use::READ));
5✔
169
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
5✔
170
                        boost::add_edge(last, v, this->graph_);
3✔
171
                    } else {
3✔
172
                        first = v;
2✔
173
                    }
174
                    last = v;
5✔
175
                }
176
            }
5✔
177
        }
140✔
178

179
        for (auto& oedge : dataflow.out_edges(*node)) {
685✔
180
            std::unordered_set<std::string> used;
276✔
181
            for (auto dim : oedge.subset()) {
544✔
182
                for (auto atom : symbolic::atoms(dim)) {
550✔
183
                    if (used.find(atom->get_name()) != used.end()) {
282✔
184
                        continue;
×
185
                    }
186
                    used.insert(atom->get_name());
282✔
187

188
                    auto v = boost::add_vertex(this->graph_);
282✔
189
                    this->add_user(
282✔
190
                        std::make_unique<User>(v, atom->get_name(), &oedge, &dataflow, Use::READ));
282✔
191
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
282✔
192
                        boost::add_edge(last, v, this->graph_);
271✔
193
                    } else {
271✔
194
                        first = v;
11✔
195
                    }
196
                    last = v;
282✔
197
                }
282✔
198
            }
268✔
199
        }
276✔
200
    }
201

202
    return {first, last};
226✔
203
};
×
204

205
std::pair<graph::Vertex, graph::Vertex> Users::traverse(
678✔
206
    structured_control_flow::ControlFlowNode& node) {
207
    if (auto block_stmt = dynamic_cast<structured_control_flow::Block*>(&node)) {
678✔
208
        // NOP
209
        auto s = boost::add_vertex(this->graph_);
226✔
210
        this->users_.insert({s, std::make_unique<User>(s, "", block_stmt, Use::NOP)});
226✔
211
        this->entries_.insert({block_stmt, this->users_.at(s).get()});
226✔
212

213
        // NOP
214
        auto t = boost::add_vertex(this->graph_);
226✔
215
        this->users_.insert({t, std::make_unique<User>(t, "", block_stmt, Use::NOP)});
226✔
216
        this->exits_.insert({block_stmt, this->users_.at(t).get()});
226✔
217

218
        auto& dataflow = block_stmt->dataflow();
226✔
219
        auto subgraph = this->traverse(dataflow);
226✔
220

221
        // May be empty
222
        if (subgraph.first == boost::graph_traits<graph::Graph>::null_vertex()) {
226✔
223
            boost::add_edge(s, t, this->graph_);
91✔
224
            return {s, t};
91✔
225
        }
226

227
        boost::add_edge(s, subgraph.first, this->graph_);
135✔
228
        boost::add_edge(subgraph.second, t, this->graph_);
135✔
229

230
        return {s, t};
135✔
231
    } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(&node)) {
452✔
232
        auto s = boost::add_vertex(this->graph_);
306✔
233
        this->users_.insert({s, std::make_unique<User>(s, "", sequence_stmt, Use::NOP)});
306✔
234
        this->entries_.insert({sequence_stmt, this->users_.at(s).get()});
306✔
235

236
        graph::Vertex current = s;
306✔
237
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
682✔
238
            auto child = sequence_stmt->at(i);
376✔
239

240
            auto subgraph = this->traverse(child.first);
376✔
241
            boost::add_edge(current, subgraph.first, this->graph_);
376✔
242
            current = subgraph.second;
376✔
243

244
            std::unordered_set<std::string> used;
376✔
245
            for (auto& entry : child.second.assignments()) {
456✔
246
                for (auto atom : symbolic::atoms(entry.second)) {
106✔
247
                    if (symbolic::is_pointer(atom)) {
26✔
248
                        continue;
×
249
                    }
250
                    if (used.find(atom->get_name()) != used.end()) {
26✔
251
                        continue;
×
252
                    }
253
                    used.insert(atom->get_name());
26✔
254

255
                    auto v = boost::add_vertex(this->graph_);
26✔
256
                    this->add_user(
26✔
257
                        std::make_unique<User>(v, atom->get_name(), &child.second, Use::READ));
26✔
258

259
                    boost::add_edge(current, v, this->graph_);
26✔
260
                    current = v;
26✔
261
                }
26✔
262
            }
263

264
            for (auto& entry : child.second.assignments()) {
456✔
265
                auto v = boost::add_vertex(this->graph_);
80✔
266
                this->add_user(
80✔
267
                    std::make_unique<User>(v, entry.first->get_name(), &child.second, Use::WRITE));
80✔
268

269
                boost::add_edge(current, v, this->graph_);
80✔
270
                current = v;
80✔
271
            }
272
        }
376✔
273

274
        if (current == boost::graph_traits<graph::Graph>::null_vertex()) {
306✔
275
            this->exits_.insert({sequence_stmt, this->users_.at(s).get()});
×
276
            return {s, current};
×
277
        }
278

279
        auto t = boost::add_vertex(this->graph_);
306✔
280
        this->users_.insert({t, std::make_unique<User>(t, "", sequence_stmt, Use::NOP)});
306✔
281
        boost::add_edge(current, t, this->graph_);
306✔
282
        this->exits_.insert({sequence_stmt, this->users_.at(t).get()});
306✔
283

284
        return {s, t};
306✔
285
    } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(&node)) {
146✔
286
        // NOP
287
        auto s = boost::add_vertex(this->graph_);
27✔
288
        this->users_.insert({s, std::make_unique<User>(s, "", if_else_stmt, Use::NOP)});
27✔
289
        this->entries_.insert({if_else_stmt, this->users_.at(s).get()});
27✔
290

291
        graph::Vertex last = s;
27✔
292

293
        std::unordered_set<std::string> used;
27✔
294
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
73✔
295
            auto& condition = if_else_stmt->at(i).second;
46✔
296
            for (auto atom : symbolic::atoms(condition)) {
85✔
297
                if (used.find(atom->get_name()) != used.end()) {
39✔
298
                    continue;
14✔
299
                }
300
                used.insert(atom->get_name());
25✔
301

302
                auto v = boost::add_vertex(this->graph_);
25✔
303

304
                this->add_user(
25✔
305
                    std::make_unique<User>(v, atom->get_name(), if_else_stmt, Use::READ));
25✔
306

307
                boost::add_edge(last, v, this->graph_);
25✔
308
                last = v;
25✔
309
            }
39✔
310
        }
46✔
311

312
        // NOP
313
        auto t = boost::add_vertex(this->graph_);
27✔
314
        this->users_.insert({t, std::make_unique<User>(t, "", if_else_stmt, Use::NOP)});
27✔
315
        this->exits_.insert({if_else_stmt, this->users_.at(t).get()});
27✔
316

317
        // Forward edge: Potentially missing else case
318
        if (!if_else_stmt->is_complete()) {
27✔
319
            boost::add_edge(last, t, this->graph_);
8✔
320
        }
8✔
321

322
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
73✔
323
            auto branch = if_else_stmt->at(i);
46✔
324
            auto subgraph = this->traverse(branch.first);
46✔
325
            boost::add_edge(last, subgraph.first, this->graph_);
46✔
326
            if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
46✔
327
                boost::add_edge(subgraph.second, t, this->graph_);
46✔
328
            }
46✔
329
        }
46✔
330

331
        return {s, t};
27✔
332
    } else if (auto loop_stmt = dynamic_cast<structured_control_flow::While*>(&node)) {
146✔
333
        // NOP
334
        auto s = boost::add_vertex(this->graph_);
7✔
335
        this->users_.insert({s, std::make_unique<User>(s, "", loop_stmt, Use::NOP)});
7✔
336
        this->entries_.insert({loop_stmt, this->users_.at(s).get()});
7✔
337

338
        auto subgraph = this->traverse(loop_stmt->root());
7✔
339

340
        // NOP
341
        auto t = boost::add_vertex(this->graph_);
7✔
342
        this->users_.insert({t, std::make_unique<User>(t, "", loop_stmt, Use::NOP)});
7✔
343
        this->exits_.insert({loop_stmt, this->users_.at(t).get()});
7✔
344

345
        boost::add_edge(s, subgraph.first, this->graph_);
7✔
346
        if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
7✔
347
            boost::add_edge(subgraph.second, t, this->graph_);
7✔
348
        }
7✔
349

350
        // Empty loop
351
        boost::add_edge(s, t, this->graph_);
7✔
352
        // Back edge
353
        boost::add_edge(t, s, this->graph_);
7✔
354

355
        return {s, t};
7✔
356
    } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(&node)) {
112✔
357
        // NOP
358
        auto s = boost::add_vertex(this->graph_);
104✔
359
        this->users_.insert({s, std::make_unique<User>(s, "", for_stmt, Use::NOP)});
104✔
360
        auto last = s;
104✔
361
        this->entries_.insert({for_stmt, this->users_.at(s).get()});
104✔
362

363
        // NOP
364
        auto t = boost::add_vertex(this->graph_);
104✔
365
        this->users_.insert({t, std::make_unique<User>(t, "", for_stmt, Use::NOP)});
104✔
366
        this->exits_.insert({for_stmt, this->users_.at(t).get()});
104✔
367

368
        // Init
369
        for (auto atom : symbolic::atoms(for_stmt->init())) {
115✔
370
            auto v = boost::add_vertex(this->graph_);
11✔
371
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ, true,
11✔
372
                                                     false, false));
11✔
373
            boost::add_edge(last, v, this->graph_);
11✔
374
            last = v;
11✔
375
        }
11✔
376
        // Indvar
377
        auto v = boost::add_vertex(this->graph_);
104✔
378
        this->add_user(std::make_unique<ForUser>(v, for_stmt->indvar()->get_name(), for_stmt,
208✔
379
                                                 Use::WRITE, true, false, false));
104✔
380

381
        boost::add_edge(last, v, this->graph_);
104✔
382
        last = v;
104✔
383

384
        // Condition
385
        for (auto atom : symbolic::atoms(for_stmt->condition())) {
307✔
386
            auto v = boost::add_vertex(this->graph_);
203✔
387
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ,
203✔
388
                                                     false, true, false));
203✔
389

390
            boost::add_edge(last, v, this->graph_);
203✔
391
            boost::add_edge(v, t, this->graph_);
203✔
392
            last = v;
203✔
393
        }
203✔
394

395
        auto subgraph = this->traverse(for_stmt->root());
104✔
396
        boost::add_edge(last, subgraph.first, this->graph_);
104✔
397

398
        // Update
399
        auto end = subgraph.second;
104✔
400
        for (auto atom : symbolic::atoms(for_stmt->update())) {
208✔
401
            auto v = boost::add_vertex(this->graph_);
104✔
402
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ,
104✔
403
                                                     false, false, true));
104✔
404
            boost::add_edge(end, v, this->graph_);
104✔
405
            end = v;
104✔
406
        }
104✔
407

408
        auto update_v = boost::add_vertex(this->graph_);
104✔
409
        this->add_user(std::make_unique<ForUser>(update_v, for_stmt->indvar()->get_name(), for_stmt,
208✔
410
                                                 Use::WRITE, false, false, true));
104✔
411

412
        if (end != boost::graph_traits<graph::Graph>::null_vertex()) {
104✔
413
            boost::add_edge(end, update_v, this->graph_);
104✔
414
        }
104✔
415
        end = update_v;
104✔
416

417
        if (end != boost::graph_traits<graph::Graph>::null_vertex()) {
104✔
418
            boost::add_edge(end, t, this->graph_);
104✔
419
        }
104✔
420

421
        // Back edge
422
        boost::add_edge(t, last, this->graph_);
104✔
423

424
        return {s, t};
104✔
425
    } else if (auto cont_stmt = dynamic_cast<structured_control_flow::Continue*>(&node)) {
8✔
426
        // Approximated by general back edge in loop scope
427
        auto v = boost::add_vertex(this->graph_);
4✔
428
        this->users_.insert({v, std::make_unique<User>(v, "", cont_stmt, Use::NOP)});
4✔
429
        this->entries_.insert({cont_stmt, this->users_.at(v).get()});
4✔
430
        this->exits_.insert({cont_stmt, this->users_.at(v).get()});
4✔
431
        return {v, v};
4✔
432
    } else if (auto br_stmt = dynamic_cast<structured_control_flow::Break*>(&node)) {
4✔
433
        // Approximated by general back edge in loop scope
434
        auto v = boost::add_vertex(this->graph_);
4✔
435
        this->users_.insert({v, std::make_unique<User>(v, "", br_stmt, Use::NOP)});
4✔
436
        this->entries_.insert({br_stmt, this->users_.at(v).get()});
4✔
437
        this->exits_.insert({br_stmt, this->users_.at(v).get()});
4✔
438
        return {v, v};
4✔
UNCOV
439
    } else if (auto ret_stmt = dynamic_cast<structured_control_flow::Return*>(&node)) {
×
440
        auto v = boost::add_vertex(this->graph_);
×
441
        this->users_.insert({v, std::make_unique<User>(v, "", ret_stmt, Use::NOP)});
×
442
        this->entries_.insert({ret_stmt, this->users_.at(v).get()});
×
443
        this->exits_.insert({ret_stmt, this->users_.at(v).get()});
×
444
        return {v, boost::graph_traits<graph::Graph>::null_vertex()};
×
UNCOV
445
    } else if (auto map_stmt = dynamic_cast<structured_control_flow::Map*>(&node)) {
×
446
        // NOP
UNCOV
447
        auto s = boost::add_vertex(this->graph_);
×
UNCOV
448
        this->users_.insert({s, std::make_unique<User>(s, "", map_stmt, Use::NOP)});
×
UNCOV
449
        auto last = s;
×
UNCOV
450
        this->entries_.insert({map_stmt, this->users_.at(s).get()});
×
451

452
        // Indvar
UNCOV
453
        auto v = boost::add_vertex(this->graph_);
×
UNCOV
454
        this->add_user(
×
UNCOV
455
            std::make_unique<User>(v, map_stmt->indvar()->get_name(), map_stmt, Use::WRITE));
×
UNCOV
456
        boost::add_edge(last, v, this->graph_);
×
UNCOV
457
        last = v;
×
458

459
        // Root
UNCOV
460
        auto subgraph = this->traverse(map_stmt->root());
×
UNCOV
461
        boost::add_edge(last, subgraph.first, this->graph_);
×
462

463
        // NOP
UNCOV
464
        auto t = boost::add_vertex(this->graph_);
×
UNCOV
465
        this->users_.insert({t, std::make_unique<User>(t, "", map_stmt, Use::NOP)});
×
UNCOV
466
        last = t;
×
UNCOV
467
        boost::add_edge(subgraph.second, last, this->graph_);
×
UNCOV
468
        this->exits_.insert({map_stmt, this->users_.at(t).get()});
×
469

UNCOV
470
        return {s, t};
×
471
    }
472

473
    throw std::invalid_argument("Invalid control flow node type");
×
474
};
678✔
475

476
Users::Users(StructuredSDFG& sdfg)
145✔
477
    : Analysis(sdfg), node_(sdfg.root()) {
145✔
478

479
      };
145✔
480

481
Users::Users(StructuredSDFG& sdfg, structured_control_flow::ControlFlowNode& node)
×
482
    : Analysis(sdfg), node_(node) {
×
483

484
      };
×
485

486
void Users::run(analysis::AnalysisManager& analysis_manager) {
145✔
487
    users_.clear();
145✔
488
    graph_.clear();
145✔
489
    source_ = nullptr;
145✔
490
    sink_ = nullptr;
145✔
491
    dom_tree_.clear();
145✔
492
    pdom_tree_.clear();
145✔
493
    users_by_sdfg_.clear();
145✔
494
    users_by_sdfg_loop_condition_.clear();
145✔
495
    users_by_sdfg_loop_init_.clear();
145✔
496
    users_by_sdfg_loop_update_.clear();
145✔
497

498
    reads_.clear();
145✔
499
    writes_.clear();
145✔
500
    views_.clear();
145✔
501
    moves_.clear();
145✔
502

503
    this->traverse(node_);
145✔
504
    if (this->users_.empty()) {
145✔
505
        return;
×
506
    }
507

508
    // Require a single source
509
    for (auto& entry : this->users_) {
2,710✔
510
        if (boost::in_degree(entry.first, this->graph_) == 0) {
2,565✔
511
            if (this->source_ != nullptr) {
145✔
512
                throw InvalidSDFGException("Users Analysis: Non-unique source node");
×
513
            }
514
            this->source_ = entry.second.get();
145✔
515
        }
145✔
516
    }
517
    if (this->source_ == nullptr) {
145✔
518
        throw InvalidSDFGException("Users Analysis: No source node");
×
519
    }
520

521
    std::list<User*> sinks;
145✔
522
    for (auto& entry : this->users_) {
2,710✔
523
        if (boost::out_degree(entry.first, this->graph_) == 0) {
2,565✔
524
            sinks.push_back(entry.second.get());
145✔
525
        }
145✔
526
    }
527
    if (sinks.size() == 0) {
145✔
528
        throw InvalidSDFGException("Users Analysis: No sink node");
×
529
    }
530
    if (sinks.size() > 1) {
145✔
531
        // add artificial sink
532
        auto v = boost::add_vertex(this->graph_);
×
533
        auto sink = std::make_unique<User>(v, "", nullptr, Use::NOP);
×
534
        this->users_.insert({v, std::move(sink)});
×
535
        for (auto sink : sinks) {
×
536
            boost::add_edge(sink->vertex_, v, this->graph_);
×
537
        }
538
        sinks.clear();
×
539

540
        this->sink_ = this->users_.at(v).get();
×
541
    } else {
×
542
        this->sink_ = sinks.front();
145✔
543
    }
544

545
    // Collect sub structures
546
    for (auto& entry : this->users_) {
2,710✔
547
        auto container = entry.second->container();
2,565✔
548
        switch (entry.second->use()) {
2,565✔
549
            case Use::READ: {
550
                if (this->reads_.find(container) == this->reads_.end()) {
787✔
551
                    this->reads_.insert({container, {}});
363✔
552
                }
363✔
553
                this->reads_[container].push_back(entry.second.get());
787✔
554
                break;
787✔
555
            }
556
            case Use::WRITE: {
557
                if (this->writes_.find(container) == this->writes_.end()) {
428✔
558
                    this->writes_.insert({container, {}});
278✔
559
                }
278✔
560
                this->writes_[container].push_back(entry.second.get());
428✔
561
                break;
428✔
562
            }
563
            case Use::VIEW: {
564
                if (this->views_.find(container) == this->views_.end()) {
1✔
565
                    this->views_.insert({container, {}});
1✔
566
                }
1✔
567
                this->views_[container].push_back(entry.second.get());
1✔
568
                break;
1✔
569
            }
570
            case Use::MOVE: {
571
                if (this->moves_.find(container) == this->moves_.end()) {
1✔
572
                    this->moves_.insert({container, {}});
1✔
573
                }
1✔
574
                this->moves_[container].push_back(entry.second.get());
1✔
575
                break;
1✔
576
            }
577
            default:
578
                break;
1,348✔
579
        }
580
    }
2,565✔
581

582
    this->init_dom_tree();
145✔
583
};
145✔
584

585
std::vector<User*> Users::uses() const {
×
586
    std::vector<User*> us;
×
587
    for (auto& entry : this->users_) {
×
588
        if (entry.second->use() == Use::NOP) {
×
589
            continue;
×
590
        }
591
        us.push_back(entry.second.get());
×
592
    }
593

594
    return us;
×
595
};
×
596

597
std::vector<User*> Users::uses(const std::string& container) const {
532✔
598
    std::vector<User*> us;
532✔
599
    for (auto& entry : this->users_) {
51,170✔
600
        if (entry.second->container() != container) {
50,638✔
601
            continue;
44,994✔
602
        }
603
        if (entry.second->use() == Use::NOP) {
5,644✔
604
            continue;
×
605
        }
606
        us.push_back(entry.second.get());
5,644✔
607
    }
608

609
    return us;
532✔
610
};
532✔
611

612
std::vector<User*> Users::writes() const {
×
613
    std::vector<User*> us;
×
614
    for (auto& entry : this->users_) {
×
615
        if (entry.second->use() != Use::WRITE) {
×
616
            continue;
×
617
        }
618
        us.push_back(entry.second.get());
×
619
    }
620

621
    return us;
×
622
};
×
623

624
std::vector<User*> Users::writes(const std::string& container) const {
48✔
625
    if (this->writes_.find(container) == this->writes_.end()) {
48✔
626
        return {};
25✔
627
    } else {
628
        return this->writes_.at(container);
23✔
629
    }
630
};
48✔
631

632
std::vector<User*> Users::reads() const {
×
633
    std::vector<User*> us;
×
634
    for (auto& entry : this->users_) {
×
635
        if (entry.second->use() != Use::READ) {
×
636
            continue;
×
637
        }
638
        us.push_back(entry.second.get());
×
639
    }
640

641
    return us;
×
642
};
×
643

644
std::vector<User*> Users::reads(const std::string& container) const {
33✔
645
    if (this->reads_.find(container) == this->reads_.end()) {
33✔
646
        return {};
13✔
647
    } else {
648
        return this->reads_.at(container);
20✔
649
    }
650
};
33✔
651

652
std::vector<User*> Users::views() const {
×
653
    std::vector<User*> us;
×
654
    for (auto& entry : this->users_) {
×
655
        if (entry.second->use() != Use::VIEW) {
×
656
            continue;
×
657
        }
658
        us.push_back(entry.second.get());
×
659
    }
660

661
    return us;
×
662
};
×
663

664
std::vector<User*> Users::views(const std::string& container) const {
53✔
665
    if (this->views_.find(container) == this->views_.end()) {
53✔
666
        return {};
53✔
667
    } else {
668
        return this->views_.at(container);
×
669
    }
670
};
53✔
671

672
std::vector<User*> Users::moves() const {
×
673
    std::vector<User*> us;
×
674
    for (auto& entry : this->users_) {
×
675
        if (entry.second->use() != Use::MOVE) {
×
676
            continue;
×
677
        }
678
        us.push_back(entry.second.get());
×
679
    }
680

681
    return us;
×
682
};
×
683

684
std::vector<User*> Users::moves(const std::string& container) const {
53✔
685
    if (this->moves_.find(container) == this->moves_.end()) {
53✔
686
        return {};
52✔
687
    } else {
688
        return this->moves_.at(container);
1✔
689
    }
690
};
53✔
691

692
structured_control_flow::ControlFlowNode* Users::scope(User* user) {
118✔
693
    if (auto data_node = dynamic_cast<data_flow::DataFlowNode*>(user->element())) {
118✔
694
        return static_cast<structured_control_flow::Block*>(data_node->get_parent().get_parent());
118✔
NEW
695
    } else if (auto memlet = dynamic_cast<data_flow::Memlet*>(user->element())) {
×
NEW
696
        return static_cast<structured_control_flow::Block*>(memlet->get_parent().get_parent());
×
UNCOV
697
    } else if (auto transition =
×
UNCOV
698
                   dynamic_cast<structured_control_flow::Transition*>(user->element())) {
×
699
        return &transition->parent();
×
700
    } else {
UNCOV
701
        return dynamic_cast<structured_control_flow::ControlFlowNode*>(user->element());
×
702
    }
703
}
118✔
704

705
/****** Domination Analysis ******/
706

707
bool Users::dominates(User& user1, User& user) {
32✔
708
    auto dominator = this->dom_tree_.at(&user);
32✔
709
    while (dominator != nullptr) {
109✔
710
        if (dominator == &user1) {
103✔
711
            return true;
26✔
712
        }
713
        dominator = this->dom_tree_.at(dominator);
77✔
714
    }
715
    return false;
6✔
716
};
32✔
717

718
bool Users::post_dominates(User& user1, User& user) {
12✔
719
    auto dominator = this->pdom_tree_.at(&user);
12✔
720
    while (dominator != nullptr) {
29✔
721
        if (dominator == &user1) {
23✔
722
            return true;
6✔
723
        }
724
        dominator = this->pdom_tree_.at(dominator);
17✔
725
    }
726
    return false;
6✔
727
};
12✔
728

729
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
14✔
730
    std::unordered_set<User*> uses;
14✔
731
    std::unordered_set<User*> visited;
14✔
732
    std::list<User*> queue = {&user2};
14✔
733
    while (!queue.empty()) {
90✔
734
        auto current = queue.front();
76✔
735
        queue.pop_front();
76✔
736

737
        // Stop conditions
738
        if (current == &user1) {
76✔
739
            continue;
14✔
740
        }
741

742
        if (visited.find(current) != visited.end()) {
62✔
743
            continue;
2✔
744
        }
745
        visited.insert(current);
60✔
746

747
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
60✔
748
            uses.insert(current);
9✔
749
        }
9✔
750

751
        // Extend search
752
        // Backward search to utilize domination user1 over user
753
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
60✔
754
        auto edges = std::ranges::subrange(eb, ee);
120✔
755
        for (auto edge : edges) {
122✔
756
            auto v = boost::source(edge, this->graph_);
62✔
757
            queue.push_back(this->users_.at(v).get());
62✔
758
        }
759
    }
760

761
    return uses;
14✔
762
};
14✔
763

764
const std::unordered_set<User*> Users::all_uses_after(User& user) {
×
765
    std::unordered_set<User*> uses;
×
766
    std::unordered_set<User*> visited;
×
767
    std::list<User*> queue = {&user};
×
768
    while (!queue.empty()) {
×
769
        auto current = queue.front();
×
770
        queue.pop_front();
×
771
        if (visited.find(current) != visited.end()) {
×
772
            continue;
×
773
        }
774
        visited.insert(current);
×
775

776
        if (current != &user && current->use() != Use::NOP) {
×
777
            uses.insert(current);
×
778
        }
×
779

780
        // Extend search
781
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
782
        auto edges = std::ranges::subrange(eb, ee);
×
783
        for (auto edge : edges) {
×
784
            auto v = boost::target(edge, this->graph_);
×
785
            queue.push_back(this->users_.at(v).get());
×
786
        }
787
    }
788

789
    return uses;
×
790
};
×
791

792
UsersView::UsersView(Users& users, structured_control_flow::ControlFlowNode& node) : users_(users) {
177✔
793
    this->entry_ = users.entries_.at(&node);
177✔
794
    this->exit_ = users.exits_.at(&node);
177✔
795

796
    // Collect sub users
797
    std::unordered_set<User*> visited;
177✔
798
    std::list<User*> queue = {this->exit_};
177✔
799
    while (!queue.empty()) {
3,225✔
800
        auto current = queue.front();
3,048✔
801
        queue.pop_front();
3,048✔
802

803
        // Stop conditions
804
        if (current == this->entry_) {
3,048✔
805
            continue;
177✔
806
        }
807

808
        if (visited.find(current) != visited.end()) {
2,871✔
809
            continue;
238✔
810
        }
811
        visited.insert(current);
2,633✔
812

813
        this->sub_users_.insert(current);
2,633✔
814

815
        // Extend search
816
        // Backward search to utilize domination user1 over user
817
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
2,633✔
818
        auto edges = std::ranges::subrange(eb, ee);
5,266✔
819
        for (auto edge : edges) {
5,504✔
820
            auto v = boost::source(edge, users.graph_);
2,871✔
821
            queue.push_back(users.users_.at(v).get());
2,871✔
822
        }
823
    }
824

825
    // Collect sub dom tree
826
    auto& dom_tree = this->users_.dom_tree_;
177✔
827

828
    for (auto& user : this->sub_users_) {
2,810✔
829
        this->sub_dom_tree_[user] = dom_tree[user];
2,633✔
830
    }
831
    this->sub_dom_tree_[this->entry_] = nullptr;
177✔
832

833
    // Collect sub post dom tree
834
    auto& pdom_tree = this->users_.pdom_tree_;
177✔
835
    for (auto& user : this->sub_users_) {
2,810✔
836
        this->sub_pdom_tree_[user] = pdom_tree[user];
2,633✔
837
    }
838
    this->sub_pdom_tree_[this->exit_] = nullptr;
177✔
839
};
177✔
840

841
std::vector<User*> UsersView::uses() const {
69✔
842
    std::vector<User*> us;
69✔
843
    for (auto& user : this->sub_users_) {
1,326✔
844
        if (user->use() == Use::NOP) {
1,257✔
845
            continue;
461✔
846
        }
847
        us.push_back(user);
796✔
848
    }
849

850
    return us;
69✔
851
};
69✔
852

853
std::vector<User*> UsersView::uses(const std::string& container) const {
539✔
854
    std::vector<User*> us;
539✔
855
    for (auto& user : this->sub_users_) {
43,045✔
856
        if (user->container() != container) {
42,506✔
857
            continue;
37,691✔
858
        }
859
        if (user->use() == Use::NOP) {
4,815✔
860
            continue;
×
861
        }
862
        us.push_back(user);
4,815✔
863
    }
864

865
    return us;
539✔
866
};
539✔
867

868
std::vector<User*> UsersView::writes() const {
59✔
869
    std::vector<User*> us;
59✔
870
    for (auto& user : this->sub_users_) {
1,260✔
871
        if (user->use() != Use::WRITE) {
1,201✔
872
            continue;
1,031✔
873
        }
874
        us.push_back(user);
170✔
875
    }
876

877
    return us;
59✔
878
};
59✔
879

880
std::vector<User*> UsersView::writes(const std::string& container) const {
126✔
881
    std::vector<User*> us;
126✔
882
    for (auto& user : this->sub_users_) {
4,345✔
883
        if (user->container() != container) {
4,219✔
884
            continue;
4,028✔
885
        }
886
        if (user->use() != Use::WRITE) {
191✔
887
            continue;
86✔
888
        }
889
        us.push_back(user);
105✔
890
    }
891

892
    return us;
126✔
893
};
126✔
894

895
std::vector<User*> UsersView::reads() const {
53✔
896
    std::vector<User*> us;
53✔
897
    for (auto& user : this->sub_users_) {
1,206✔
898
        if (user->use() != Use::READ) {
1,153✔
899
            continue;
553✔
900
        }
901
        us.push_back(user);
600✔
902
    }
903

904
    return us;
53✔
905
};
53✔
906

907
std::vector<User*> UsersView::reads(const std::string& container) const {
64✔
908
    std::vector<User*> us;
64✔
909
    for (auto& user : this->sub_users_) {
2,810✔
910
        if (user->container() != container) {
2,746✔
911
            continue;
2,618✔
912
        }
913
        if (user->use() != Use::READ) {
128✔
914
            continue;
68✔
915
        }
916
        us.push_back(user);
60✔
917
    }
918

919
    return us;
64✔
920
};
64✔
921

922
std::vector<User*> UsersView::views() const {
53✔
923
    std::vector<User*> us;
53✔
924
    for (auto& user : this->sub_users_) {
1,206✔
925
        if (user->use() != Use::VIEW) {
1,153✔
926
            continue;
1,153✔
927
        }
928
        us.push_back(user);
×
929
    }
930

931
    return us;
53✔
932
};
53✔
933

934
std::vector<User*> UsersView::views(const std::string& container) const {
×
935
    std::vector<User*> us;
×
936
    for (auto& user : this->sub_users_) {
×
937
        if (user->container() != container) {
×
938
            continue;
×
939
        }
940
        if (user->use() != Use::VIEW) {
×
941
            continue;
×
942
        }
943
        us.push_back(user);
×
944
    }
945

946
    return us;
×
947
};
×
948

949
std::vector<User*> UsersView::moves() const {
53✔
950
    std::vector<User*> us;
53✔
951
    for (auto& user : this->sub_users_) {
1,206✔
952
        if (user->use() != Use::MOVE) {
1,153✔
953
            continue;
1,153✔
954
        }
955
        us.push_back(user);
×
956
    }
957

958
    return us;
53✔
959
};
53✔
960

961
std::vector<User*> UsersView::moves(const std::string& container) const {
×
962
    std::vector<User*> us;
×
963
    for (auto& user : this->sub_users_) {
×
964
        if (user->container() != container) {
×
965
            continue;
×
966
        }
967
        if (user->use() != Use::MOVE) {
×
968
            continue;
×
969
        }
970
        us.push_back(user);
×
971
    }
972

973
    return us;
×
974
};
×
975

976
bool UsersView::dominates(User& user1, User& user) {
×
977
    auto dominator = this->sub_dom_tree_[&user];
×
978
    while (dominator != nullptr) {
×
979
        if (dominator == &user1) {
×
980
            return true;
×
981
        }
982
        dominator = this->sub_dom_tree_[dominator];
×
983
    }
984
    return false;
×
985
};
×
986

987
bool UsersView::post_dominates(User& user1, User& user) {
×
988
    auto dominator = this->sub_pdom_tree_[&user];
×
989
    while (dominator != nullptr) {
×
990
        if (dominator == &user1) {
×
991
            return true;
×
992
        }
993
        dominator = this->sub_pdom_tree_[dominator];
×
994
    }
995
    return false;
×
996
};
×
997

UNCOV
998
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
UNCOV
999
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
UNCOV
1000
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
1001

UNCOV
1002
    std::unordered_set<User*> uses;
×
UNCOV
1003
    std::unordered_set<User*> visited;
×
UNCOV
1004
    std::list<User*> queue = {&user1};
×
UNCOV
1005
    while (!queue.empty()) {
×
UNCOV
1006
        auto current = queue.front();
×
UNCOV
1007
        queue.pop_front();
×
UNCOV
1008
        if (visited.find(current) != visited.end()) {
×
UNCOV
1009
            continue;
×
1010
        }
UNCOV
1011
        visited.insert(current);
×
1012

UNCOV
1013
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
UNCOV
1014
            uses.insert(current);
×
UNCOV
1015
        }
×
1016

1017
        // Stop conditions
UNCOV
1018
        if (current == exit_) {
×
UNCOV
1019
            continue;
×
1020
        }
1021

UNCOV
1022
        if (current == &user2) {
×
UNCOV
1023
            continue;
×
1024
        }
1025

1026
        // Extend search
UNCOV
1027
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
UNCOV
1028
        auto edges = std::ranges::subrange(eb, ee);
×
UNCOV
1029
        for (auto edge : edges) {
×
UNCOV
1030
            auto v = boost::target(edge, this->users_.graph_);
×
UNCOV
1031
            queue.push_back(this->users_.users_.at(v).get());
×
1032
        }
1033
    }
1034

UNCOV
1035
    return uses;
×
UNCOV
1036
};
×
1037

1038
std::unordered_set<User*> UsersView::all_uses_after(User& user) {
×
1039
    assert(this->sub_users_.find(&user) != this->sub_users_.end());
×
1040

1041
    std::unordered_set<User*> uses;
×
1042
    std::unordered_set<User*> visited;
×
1043
    std::list<User*> queue = {&user};
×
1044
    while (!queue.empty()) {
×
1045
        auto current = queue.front();
×
1046
        queue.pop_front();
×
1047
        if (visited.find(current) != visited.end()) {
×
1048
            continue;
×
1049
        }
1050
        visited.insert(current);
×
1051

1052
        if (current != &user && current->use() != Use::NOP) {
×
1053
            uses.insert(current);
×
1054
        }
×
1055

1056
        if (current == exit_) {
×
1057
            continue;
×
1058
        }
1059

1060
        // Extend search
1061
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1062
        auto edges = std::ranges::subrange(eb, ee);
×
1063
        for (auto edge : edges) {
×
1064
            auto v = boost::target(edge, this->users_.graph_);
×
1065
            queue.push_back(this->users_.users_.at(v).get());
×
1066
        }
1067
    }
1068

1069
    return uses;
×
1070
};
×
1071

1072
User* Users::get_user(const std::string& container, Element* element, Use use, bool is_init,
543✔
1073
                      bool is_condition, bool is_update) {
1074
    if (auto for_loop = dynamic_cast<structured_control_flow::For*>(element)) {
543✔
1075
        if (is_init) {
211✔
1076
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
47✔
1077
            return tmp;
47✔
1078
        } else if (is_condition) {
164✔
1079
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
76✔
1080
        } else if (is_update) {
88✔
1081
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
88✔
1082
        }
1083
    }
×
1084
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
332✔
1085
    return tmp;
332✔
1086
}
543✔
1087

1088
void Users::add_user(std::unique_ptr<User> user) {
1,217✔
1089
    auto vertex = user->vertex_;
1,217✔
1090
    this->users_.insert({vertex, std::move(user)});
1,217✔
1091

1092
    auto user_ptr = this->users_.at(vertex).get();
1,217✔
1093
    auto* target_structure = &users_by_sdfg_;
1,217✔
1094
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,217✔
1095
        auto for_loop = dynamic_cast<structured_control_flow::For*>(user_ptr->element());
526✔
1096
        if (for_loop == nullptr) {
526✔
1097
            throw std::invalid_argument("Invalid user type");
×
1098
        }
1099
        if (for_user->is_init()) {
526✔
1100
            target_structure = &users_by_sdfg_loop_init_;
115✔
1101
        } else if (for_user->is_condition()) {
526✔
1102
            target_structure = &users_by_sdfg_loop_condition_;
203✔
1103
        } else if (for_user->is_update()) {
411✔
1104
            target_structure = &users_by_sdfg_loop_update_;
208✔
1105
        } else {
208✔
1106
            throw std::invalid_argument("Invalid user type");
×
1107
        }
1108
    }
526✔
1109

1110
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,217✔
1111
        target_structure->insert({user_ptr->container(), {}});
743✔
1112
    }
743✔
1113
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
2,434✔
1114
        (*target_structure)[user_ptr->container()].end()) {
1,217✔
1115
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
1,108✔
1116
    }
1,108✔
1117
    target_structure->at(user_ptr->container())
1,217✔
1118
        .at(user_ptr->element())
1,217✔
1119
        .insert({user_ptr->use(), user_ptr});
1,217✔
1120
}
1,217✔
1121

1122
std::unordered_set<std::string> Users::locals(structured_control_flow::ControlFlowNode& node) {
69✔
1123
    auto& sdfg = this->sdfg_;
69✔
1124

1125
    // Locals have no uses outside of the node
1126
    // We can check this by comparing the number of uses of the container in the view and the total
1127
    // number of uses of the container in the users map.
1128
    std::unordered_set<std::string> locals;
69✔
1129
    UsersView view(*this, node);
69✔
1130
    for (auto& user : view.uses()) {
865✔
1131
        if (!sdfg.is_transient(user->container())) {
796✔
1132
            continue;
265✔
1133
        }
1134
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
531✔
1135
            locals.insert(user->container());
317✔
1136
        }
317✔
1137
    }
1138

1139
    return locals;
69✔
1140
};
69✔
1141

1142
}  // namespace analysis
1143
}  // 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