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

daisytuner / sdfglib / 15843927066

24 Jun 2025 07:17AM UTC coverage: 64.145% (-0.1%) from 64.276%
15843927066

push

github

web-flow
Merge pull request #103 from daisytuner/map-redefinition

adds new map definition into passes

34 of 38 new or added lines in 6 files covered. (89.47%)

2 existing lines in 2 files now uncovered.

8167 of 12732 relevant lines covered (64.15%)

138.22 hits per line

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

75.0
/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)
1,940✔
25
    : vertex_(vertex), container_(container), use_(use), element_(element) {
1,940✔
26

27
      };
1,940✔
28

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

33
      };
569✔
34

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

37
};
4,537✔
38

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

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

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

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

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

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

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

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

80
      };
481✔
81

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

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

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

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

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

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

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

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

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

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

154
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
137✔
155
                        boost::add_edge(last, v, this->graph_);
37✔
156
                    } else {
37✔
157
                        first = v;
100✔
158
                    }
159
                    last = v;
137✔
160
                }
137✔
161
            }
276✔
162
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
418✔
163
            if (tasklet->is_conditional()) {
142✔
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
        }
142✔
178

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

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

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

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

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

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

221
        // May be empty
222
        if (subgraph.first == boost::graph_traits<graph::Graph>::null_vertex()) {
228✔
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_);
137✔
228
        boost::add_edge(subgraph.second, t, this->graph_);
137✔
229

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

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

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

244
            std::unordered_set<std::string> used;
371✔
245
            for (auto& entry : child.second.assignments()) {
451✔
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()) {
451✔
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
        }
371✔
273

274
        if (current == boost::graph_traits<graph::Graph>::null_vertex()) {
301✔
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_);
301✔
280
        this->users_.insert({t, std::make_unique<User>(t, "", sequence_stmt, Use::NOP)});
301✔
281
        boost::add_edge(current, t, this->graph_);
301✔
282
        this->exits_.insert({sequence_stmt, this->users_.at(t).get()});
301✔
283

284
        return {s, t};
301✔
285
    } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(&node)) {
139✔
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)) {
139✔
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::StructuredLoop*>(&node)) {
105✔
357
        // NOP
358
        auto s = boost::add_vertex(this->graph_);
97✔
359
        this->users_.insert({s, std::make_unique<User>(s, "", for_stmt, Use::NOP)});
97✔
360
        auto last = s;
97✔
361
        this->entries_.insert({for_stmt, this->users_.at(s).get()});
97✔
362

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

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

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

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

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

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

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

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

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

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

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

424
        return {s, t};
97✔
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()};
×
445
    }
446

447
    throw std::invalid_argument("Invalid control flow node type");
×
448
};
668✔
449

450
Users::Users(StructuredSDFG& sdfg)
147✔
451
    : Analysis(sdfg), node_(sdfg.root()) {
147✔
452

453
      };
147✔
454

455
Users::Users(StructuredSDFG& sdfg, structured_control_flow::ControlFlowNode& node)
×
456
    : Analysis(sdfg), node_(node) {
×
457

458
      };
×
459

460
void Users::run(analysis::AnalysisManager& analysis_manager) {
147✔
461
    users_.clear();
147✔
462
    graph_.clear();
147✔
463
    source_ = nullptr;
147✔
464
    sink_ = nullptr;
147✔
465
    dom_tree_.clear();
147✔
466
    pdom_tree_.clear();
147✔
467
    users_by_sdfg_.clear();
147✔
468
    users_by_sdfg_loop_condition_.clear();
147✔
469
    users_by_sdfg_loop_init_.clear();
147✔
470
    users_by_sdfg_loop_update_.clear();
147✔
471

472
    reads_.clear();
147✔
473
    writes_.clear();
147✔
474
    views_.clear();
147✔
475
    moves_.clear();
147✔
476

477
    this->traverse(node_);
147✔
478
    if (this->users_.empty()) {
147✔
479
        return;
×
480
    }
481

482
    // Require a single source
483
    for (auto& entry : this->users_) {
2,656✔
484
        if (boost::in_degree(entry.first, this->graph_) == 0) {
2,509✔
485
            if (this->source_ != nullptr) {
147✔
486
                throw InvalidSDFGException("Users Analysis: Non-unique source node");
×
487
            }
488
            this->source_ = entry.second.get();
147✔
489
        }
147✔
490
    }
491
    if (this->source_ == nullptr) {
147✔
492
        throw InvalidSDFGException("Users Analysis: No source node");
×
493
    }
494

495
    std::list<User*> sinks;
147✔
496
    for (auto& entry : this->users_) {
2,656✔
497
        if (boost::out_degree(entry.first, this->graph_) == 0) {
2,509✔
498
            sinks.push_back(entry.second.get());
147✔
499
        }
147✔
500
    }
501
    if (sinks.size() == 0) {
147✔
502
        throw InvalidSDFGException("Users Analysis: No sink node");
×
503
    }
504
    if (sinks.size() > 1) {
147✔
505
        // add artificial sink
506
        auto v = boost::add_vertex(this->graph_);
×
507
        auto sink = std::make_unique<User>(v, "", nullptr, Use::NOP);
×
508
        this->users_.insert({v, std::move(sink)});
×
509
        for (auto sink : sinks) {
×
510
            boost::add_edge(sink->vertex_, v, this->graph_);
×
511
        }
512
        sinks.clear();
×
513

514
        this->sink_ = this->users_.at(v).get();
×
515
    } else {
×
516
        this->sink_ = sinks.front();
147✔
517
    }
518

519
    // Collect sub structures
520
    for (auto& entry : this->users_) {
2,656✔
521
        auto container = entry.second->container();
2,509✔
522
        switch (entry.second->use()) {
2,509✔
523
            case Use::READ: {
524
                if (this->reads_.find(container) == this->reads_.end()) {
763✔
525
                    this->reads_.insert({container, {}});
357✔
526
                }
357✔
527
                this->reads_[container].push_back(entry.second.get());
763✔
528
                break;
763✔
529
            }
530
            case Use::WRITE: {
531
                if (this->writes_.find(container) == this->writes_.end()) {
416✔
532
                    this->writes_.insert({container, {}});
273✔
533
                }
273✔
534
                this->writes_[container].push_back(entry.second.get());
416✔
535
                break;
416✔
536
            }
537
            case Use::VIEW: {
538
                if (this->views_.find(container) == this->views_.end()) {
1✔
539
                    this->views_.insert({container, {}});
1✔
540
                }
1✔
541
                this->views_[container].push_back(entry.second.get());
1✔
542
                break;
1✔
543
            }
544
            case Use::MOVE: {
545
                if (this->moves_.find(container) == this->moves_.end()) {
1✔
546
                    this->moves_.insert({container, {}});
1✔
547
                }
1✔
548
                this->moves_[container].push_back(entry.second.get());
1✔
549
                break;
1✔
550
            }
551
            default:
552
                break;
1,328✔
553
        }
554
    }
2,509✔
555

556
    this->init_dom_tree();
147✔
557
};
147✔
558

559
std::vector<User*> Users::uses() const {
×
560
    std::vector<User*> us;
×
561
    for (auto& entry : this->users_) {
×
562
        if (entry.second->use() == Use::NOP) {
×
563
            continue;
×
564
        }
565
        us.push_back(entry.second.get());
×
566
    }
567

568
    return us;
×
569
};
×
570

571
std::vector<User*> Users::uses(const std::string& container) const {
352✔
572
    std::vector<User*> us;
352✔
573
    for (auto& entry : this->users_) {
41,950✔
574
        if (entry.second->container() != container) {
41,598✔
575
            continue;
37,000✔
576
        }
577
        if (entry.second->use() == Use::NOP) {
4,598✔
578
            continue;
×
579
        }
580
        us.push_back(entry.second.get());
4,598✔
581
    }
582

583
    return us;
352✔
584
};
352✔
585

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

595
    return us;
×
596
};
×
597

598
std::vector<User*> Users::writes(const std::string& container) const {
48✔
599
    if (this->writes_.find(container) == this->writes_.end()) {
48✔
600
        return {};
25✔
601
    } else {
602
        return this->writes_.at(container);
23✔
603
    }
604
};
48✔
605

606
std::vector<User*> Users::reads() const {
×
607
    std::vector<User*> us;
×
608
    for (auto& entry : this->users_) {
×
609
        if (entry.second->use() != Use::READ) {
×
610
            continue;
×
611
        }
612
        us.push_back(entry.second.get());
×
613
    }
614

615
    return us;
×
616
};
×
617

618
std::vector<User*> Users::reads(const std::string& container) const {
33✔
619
    if (this->reads_.find(container) == this->reads_.end()) {
33✔
620
        return {};
13✔
621
    } else {
622
        return this->reads_.at(container);
20✔
623
    }
624
};
33✔
625

626
std::vector<User*> Users::views() const {
×
627
    std::vector<User*> us;
×
628
    for (auto& entry : this->users_) {
×
629
        if (entry.second->use() != Use::VIEW) {
×
630
            continue;
×
631
        }
632
        us.push_back(entry.second.get());
×
633
    }
634

635
    return us;
×
636
};
×
637

638
std::vector<User*> Users::views(const std::string& container) const {
53✔
639
    if (this->views_.find(container) == this->views_.end()) {
53✔
640
        return {};
53✔
641
    } else {
642
        return this->views_.at(container);
×
643
    }
644
};
53✔
645

646
std::vector<User*> Users::moves() const {
×
647
    std::vector<User*> us;
×
648
    for (auto& entry : this->users_) {
×
649
        if (entry.second->use() != Use::MOVE) {
×
650
            continue;
×
651
        }
652
        us.push_back(entry.second.get());
×
653
    }
654

655
    return us;
×
656
};
×
657

658
std::vector<User*> Users::moves(const std::string& container) const {
53✔
659
    if (this->moves_.find(container) == this->moves_.end()) {
53✔
660
        return {};
52✔
661
    } else {
662
        return this->moves_.at(container);
1✔
663
    }
664
};
53✔
665

666
structured_control_flow::ControlFlowNode* Users::scope(User* user) {
156✔
667
    if (auto data_node = dynamic_cast<data_flow::DataFlowNode*>(user->element())) {
156✔
668
        return static_cast<structured_control_flow::Block*>(data_node->get_parent().get_parent());
156✔
669
    } else if (auto memlet = dynamic_cast<data_flow::Memlet*>(user->element())) {
×
670
        return static_cast<structured_control_flow::Block*>(memlet->get_parent().get_parent());
×
671
    } else if (auto transition =
×
672
                   dynamic_cast<structured_control_flow::Transition*>(user->element())) {
×
673
        return &transition->parent();
×
674
    } else {
675
        return dynamic_cast<structured_control_flow::ControlFlowNode*>(user->element());
×
676
    }
677
}
156✔
678

679
/****** Domination Analysis ******/
680

681
bool Users::dominates(User& user1, User& user) {
32✔
682
    auto dominator = this->dom_tree_.at(&user);
32✔
683
    while (dominator != nullptr) {
109✔
684
        if (dominator == &user1) {
103✔
685
            return true;
26✔
686
        }
687
        dominator = this->dom_tree_.at(dominator);
77✔
688
    }
689
    return false;
6✔
690
};
32✔
691

692
bool Users::post_dominates(User& user1, User& user) {
12✔
693
    auto dominator = this->pdom_tree_.at(&user);
12✔
694
    while (dominator != nullptr) {
29✔
695
        if (dominator == &user1) {
23✔
696
            return true;
6✔
697
        }
698
        dominator = this->pdom_tree_.at(dominator);
17✔
699
    }
700
    return false;
6✔
701
};
12✔
702

703
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
14✔
704
    std::unordered_set<User*> uses;
14✔
705
    std::unordered_set<User*> visited;
14✔
706
    std::list<User*> queue = {&user2};
14✔
707
    while (!queue.empty()) {
90✔
708
        auto current = queue.front();
76✔
709
        queue.pop_front();
76✔
710

711
        // Stop conditions
712
        if (current == &user1) {
76✔
713
            continue;
14✔
714
        }
715

716
        if (visited.find(current) != visited.end()) {
62✔
717
            continue;
2✔
718
        }
719
        visited.insert(current);
60✔
720

721
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
60✔
722
            uses.insert(current);
9✔
723
        }
9✔
724

725
        // Extend search
726
        // Backward search to utilize domination user1 over user
727
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
60✔
728
        auto edges = std::ranges::subrange(eb, ee);
120✔
729
        for (auto edge : edges) {
122✔
730
            auto v = boost::source(edge, this->graph_);
62✔
731
            queue.push_back(this->users_.at(v).get());
62✔
732
        }
733
    }
734

735
    return uses;
14✔
736
};
14✔
737

738
const std::unordered_set<User*> Users::all_uses_after(User& user) {
×
739
    std::unordered_set<User*> uses;
×
740
    std::unordered_set<User*> visited;
×
741
    std::list<User*> queue = {&user};
×
742
    while (!queue.empty()) {
×
743
        auto current = queue.front();
×
744
        queue.pop_front();
×
745
        if (visited.find(current) != visited.end()) {
×
746
            continue;
×
747
        }
748
        visited.insert(current);
×
749

750
        if (current != &user && current->use() != Use::NOP) {
×
751
            uses.insert(current);
×
752
        }
×
753

754
        // Extend search
755
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
756
        auto edges = std::ranges::subrange(eb, ee);
×
757
        for (auto edge : edges) {
×
758
            auto v = boost::target(edge, this->graph_);
×
759
            queue.push_back(this->users_.at(v).get());
×
760
        }
761
    }
762

763
    return uses;
×
764
};
×
765

766
UsersView::UsersView(Users& users, structured_control_flow::ControlFlowNode& node) : users_(users) {
145✔
767
    this->entry_ = users.entries_.at(&node);
145✔
768
    this->exit_ = users.exits_.at(&node);
145✔
769

770
    // Collect sub users
771
    std::unordered_set<User*> visited;
145✔
772
    std::list<User*> queue = {this->exit_};
145✔
773
    while (!queue.empty()) {
2,202✔
774
        auto current = queue.front();
2,057✔
775
        queue.pop_front();
2,057✔
776

777
        // Stop conditions
778
        if (current == this->entry_) {
2,057✔
779
            continue;
145✔
780
        }
781

782
        if (visited.find(current) != visited.end()) {
1,912✔
783
            continue;
60✔
784
        }
785
        visited.insert(current);
1,852✔
786

787
        this->sub_users_.insert(current);
1,852✔
788

789
        // Extend search
790
        // Backward search to utilize domination user1 over user
791
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
1,852✔
792
        auto edges = std::ranges::subrange(eb, ee);
3,704✔
793
        for (auto edge : edges) {
3,764✔
794
            auto v = boost::source(edge, users.graph_);
1,912✔
795
            queue.push_back(users.users_.at(v).get());
1,912✔
796
        }
797
    }
798

799
    // Collect sub dom tree
800
    auto& dom_tree = this->users_.dom_tree_;
145✔
801

802
    for (auto& user : this->sub_users_) {
1,997✔
803
        this->sub_dom_tree_[user] = dom_tree[user];
1,852✔
804
    }
805
    this->sub_dom_tree_[this->entry_] = nullptr;
145✔
806

807
    // Collect sub post dom tree
808
    auto& pdom_tree = this->users_.pdom_tree_;
145✔
809
    for (auto& user : this->sub_users_) {
1,997✔
810
        this->sub_pdom_tree_[user] = pdom_tree[user];
1,852✔
811
    }
812
    this->sub_pdom_tree_[this->exit_] = nullptr;
145✔
813
};
145✔
814

815
std::vector<User*> UsersView::uses() const {
55✔
816
    std::vector<User*> us;
55✔
817
    for (auto& user : this->sub_users_) {
942✔
818
        if (user->use() == Use::NOP) {
887✔
819
            continue;
323✔
820
        }
821
        us.push_back(user);
564✔
822
    }
823

824
    return us;
55✔
825
};
55✔
826

827
std::vector<User*> UsersView::uses(const std::string& container) const {
359✔
828
    std::vector<User*> us;
359✔
829
    for (auto& user : this->sub_users_) {
37,416✔
830
        if (user->container() != container) {
37,057✔
831
            continue;
33,116✔
832
        }
833
        if (user->use() == Use::NOP) {
3,941✔
834
            continue;
×
835
        }
836
        us.push_back(user);
3,941✔
837
    }
838

839
    return us;
359✔
840
};
359✔
841

842
std::vector<User*> UsersView::writes() const {
41✔
843
    std::vector<User*> us;
41✔
844
    for (auto& user : this->sub_users_) {
831✔
845
        if (user->use() != Use::WRITE) {
790✔
846
            continue;
688✔
847
        }
848
        us.push_back(user);
102✔
849
    }
850

851
    return us;
41✔
852
};
41✔
853

854
std::vector<User*> UsersView::writes(const std::string& container) const {
99✔
855
    std::vector<User*> us;
99✔
856
    for (auto& user : this->sub_users_) {
3,664✔
857
        if (user->container() != container) {
3,565✔
858
            continue;
3,428✔
859
        }
860
        if (user->use() != Use::WRITE) {
137✔
861
            continue;
59✔
862
        }
863
        us.push_back(user);
78✔
864
    }
865

866
    return us;
99✔
867
};
99✔
868

869
std::vector<User*> UsersView::reads() const {
35✔
870
    std::vector<User*> us;
35✔
871
    for (auto& user : this->sub_users_) {
777✔
872
        if (user->use() != Use::READ) {
742✔
873
            continue;
331✔
874
        }
875
        us.push_back(user);
411✔
876
    }
877

878
    return us;
35✔
879
};
35✔
880

881
std::vector<User*> UsersView::reads(const std::string& container) const {
46✔
882
    std::vector<User*> us;
46✔
883
    for (auto& user : this->sub_users_) {
2,381✔
884
        if (user->container() != container) {
2,335✔
885
            continue;
2,243✔
886
        }
887
        if (user->use() != Use::READ) {
92✔
888
            continue;
50✔
889
        }
890
        us.push_back(user);
42✔
891
    }
892

893
    return us;
46✔
894
};
46✔
895

896
std::vector<User*> UsersView::views() const {
35✔
897
    std::vector<User*> us;
35✔
898
    for (auto& user : this->sub_users_) {
777✔
899
        if (user->use() != Use::VIEW) {
742✔
900
            continue;
742✔
901
        }
902
        us.push_back(user);
×
903
    }
904

905
    return us;
35✔
906
};
35✔
907

908
std::vector<User*> UsersView::views(const std::string& container) const {
×
909
    std::vector<User*> us;
×
910
    for (auto& user : this->sub_users_) {
×
911
        if (user->container() != container) {
×
912
            continue;
×
913
        }
914
        if (user->use() != Use::VIEW) {
×
915
            continue;
×
916
        }
917
        us.push_back(user);
×
918
    }
919

920
    return us;
×
921
};
×
922

923
std::vector<User*> UsersView::moves() const {
35✔
924
    std::vector<User*> us;
35✔
925
    for (auto& user : this->sub_users_) {
777✔
926
        if (user->use() != Use::MOVE) {
742✔
927
            continue;
742✔
928
        }
929
        us.push_back(user);
×
930
    }
931

932
    return us;
35✔
933
};
35✔
934

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

947
    return us;
×
948
};
×
949

950
bool UsersView::dominates(User& user1, User& user) {
×
951
    auto dominator = this->sub_dom_tree_[&user];
×
952
    while (dominator != nullptr) {
×
953
        if (dominator == &user1) {
×
954
            return true;
×
955
        }
956
        dominator = this->sub_dom_tree_[dominator];
×
957
    }
958
    return false;
×
959
};
×
960

961
bool UsersView::post_dominates(User& user1, User& user) {
×
962
    auto dominator = this->sub_pdom_tree_[&user];
×
963
    while (dominator != nullptr) {
×
964
        if (dominator == &user1) {
×
965
            return true;
×
966
        }
967
        dominator = this->sub_pdom_tree_[dominator];
×
968
    }
969
    return false;
×
970
};
×
971

972
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
973
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
974
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
975

976
    std::unordered_set<User*> uses;
×
977
    std::unordered_set<User*> visited;
×
978
    std::list<User*> queue = {&user1};
×
979
    while (!queue.empty()) {
×
980
        auto current = queue.front();
×
981
        queue.pop_front();
×
982
        if (visited.find(current) != visited.end()) {
×
983
            continue;
×
984
        }
985
        visited.insert(current);
×
986

987
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
988
            uses.insert(current);
×
989
        }
×
990

991
        // Stop conditions
992
        if (current == exit_) {
×
993
            continue;
×
994
        }
995

996
        if (current == &user2) {
×
997
            continue;
×
998
        }
999

1000
        // Extend search
1001
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1002
        auto edges = std::ranges::subrange(eb, ee);
×
1003
        for (auto edge : edges) {
×
1004
            auto v = boost::target(edge, this->users_.graph_);
×
1005
            queue.push_back(this->users_.users_.at(v).get());
×
1006
        }
1007
    }
1008

1009
    return uses;
×
1010
};
×
1011

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

1015
    std::unordered_set<User*> uses;
×
1016
    std::unordered_set<User*> visited;
×
1017
    std::list<User*> queue = {&user};
×
1018
    while (!queue.empty()) {
×
1019
        auto current = queue.front();
×
1020
        queue.pop_front();
×
1021
        if (visited.find(current) != visited.end()) {
×
1022
            continue;
×
1023
        }
1024
        visited.insert(current);
×
1025

1026
        if (current != &user && current->use() != Use::NOP) {
×
1027
            uses.insert(current);
×
1028
        }
×
1029

1030
        if (current == exit_) {
×
1031
            continue;
×
1032
        }
1033

1034
        // Extend search
1035
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1036
        auto edges = std::ranges::subrange(eb, ee);
×
1037
        for (auto edge : edges) {
×
1038
            auto v = boost::target(edge, this->users_.graph_);
×
1039
            queue.push_back(this->users_.users_.at(v).get());
×
1040
        }
1041
    }
1042

1043
    return uses;
×
1044
};
×
1045

1046
User* Users::get_user(const std::string& container, Element* element, Use use, bool is_init,
641✔
1047
                      bool is_condition, bool is_update) {
1048
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element)) {
641✔
1049
        if (is_init) {
272✔
1050
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
61✔
1051
            return tmp;
61✔
1052
        } else if (is_condition) {
211✔
1053
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
101✔
1054
        } else if (is_update) {
110✔
1055
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
110✔
1056
        }
1057
    }
×
1058
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
369✔
1059
    return tmp;
369✔
1060
}
641✔
1061

1062
void Users::add_user(std::unique_ptr<User> user) {
1,181✔
1063
    auto vertex = user->vertex_;
1,181✔
1064
    this->users_.insert({vertex, std::move(user)});
1,181✔
1065

1066
    auto user_ptr = this->users_.at(vertex).get();
1,181✔
1067
    auto* target_structure = &users_by_sdfg_;
1,181✔
1068
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,181✔
1069
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
481✔
1070
        if (for_loop == nullptr) {
481✔
1071
            throw std::invalid_argument("Invalid user type");
×
1072
        }
1073
        if (for_user->is_init()) {
481✔
1074
            target_structure = &users_by_sdfg_loop_init_;
103✔
1075
        } else if (for_user->is_condition()) {
481✔
1076
            target_structure = &users_by_sdfg_loop_condition_;
184✔
1077
        } else if (for_user->is_update()) {
378✔
1078
            target_structure = &users_by_sdfg_loop_update_;
194✔
1079
        } else {
194✔
1080
            throw std::invalid_argument("Invalid user type");
×
1081
        }
1082
    }
481✔
1083

1084
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,181✔
1085
        target_structure->insert({user_ptr->container(), {}});
724✔
1086
    }
724✔
1087
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
2,362✔
1088
        (*target_structure)[user_ptr->container()].end()) {
1,181✔
1089
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
1,079✔
1090
    }
1,079✔
1091
    target_structure->at(user_ptr->container())
1,181✔
1092
        .at(user_ptr->element())
1,181✔
1093
        .insert({user_ptr->use(), user_ptr});
1,181✔
1094
}
1,181✔
1095

1096
std::unordered_set<std::string> Users::locals(structured_control_flow::ControlFlowNode& node) {
55✔
1097
    auto& sdfg = this->sdfg_;
55✔
1098

1099
    // Locals have no uses outside of the node
1100
    // We can check this by comparing the number of uses of the container in the view and the total
1101
    // number of uses of the container in the users map.
1102
    std::unordered_set<std::string> locals;
55✔
1103
    UsersView view(*this, node);
55✔
1104
    for (auto& user : view.uses()) {
619✔
1105
        if (!sdfg.is_transient(user->container())) {
564✔
1106
            continue;
213✔
1107
        }
1108
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
351✔
1109
            locals.insert(user->container());
180✔
1110
        }
180✔
1111
    }
1112

1113
    return locals;
55✔
1114
};
55✔
1115

1116
}  // namespace analysis
1117
}  // 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