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

daisytuner / sdfglib / 15717109772

17 Jun 2025 08:16PM UTC coverage: 64.66% (+0.08%) from 64.576%
15717109772

push

github

web-flow
Merge pull request #89 from daisytuner/leftover-logs

removes leftover logs

7992 of 12360 relevant lines covered (64.66%)

142.43 hits per line

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

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

27
      };
1,802✔
28

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

33
      };
604✔
34

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

37
};
4,373✔
38

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

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

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

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

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

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

69
    // Use of symbol
70
    return {{}};
90✔
71
};
437✔
72

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

80
      };
439✔
81

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

293
        std::unordered_set<std::string> used;
22✔
294
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
61✔
295
            auto& condition = if_else_stmt->at(i).second;
39✔
296
            for (auto atom : symbolic::atoms(condition)) {
79✔
297
                if (used.find(atom->get_name()) != used.end()) {
40✔
298
                    continue;
15✔
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
            }
40✔
310
        }
39✔
311

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

424
        return {s, t};
87✔
425
    } else if (auto cont_stmt = dynamic_cast<structured_control_flow::Continue*>(&node)) {
11✔
426
        // Approximated by general back edge in loop scope
427
        auto v = boost::add_vertex(this->graph_);
5✔
428
        this->users_.insert({v, std::make_unique<User>(v, "", cont_stmt, Use::NOP)});
5✔
429
        this->entries_.insert({cont_stmt, this->users_.at(v).get()});
5✔
430
        this->exits_.insert({cont_stmt, this->users_.at(v).get()});
5✔
431
        return {v, v};
5✔
432
    } else if (auto br_stmt = dynamic_cast<structured_control_flow::Break*>(&node)) {
6✔
433
        // Approximated by general back edge in loop scope
434
        auto v = boost::add_vertex(this->graph_);
5✔
435
        this->users_.insert({v, std::make_unique<User>(v, "", br_stmt, Use::NOP)});
5✔
436
        this->entries_.insert({br_stmt, this->users_.at(v).get()});
5✔
437
        this->exits_.insert({br_stmt, this->users_.at(v).get()});
5✔
438
        return {v, v};
5✔
439
    } else if (auto ret_stmt = dynamic_cast<structured_control_flow::Return*>(&node)) {
1✔
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
    } else if (auto map_stmt = dynamic_cast<structured_control_flow::Map*>(&node)) {
1✔
446
        // NOP
447
        auto s = boost::add_vertex(this->graph_);
1✔
448
        this->users_.insert({s, std::make_unique<User>(s, "", map_stmt, Use::NOP)});
1✔
449
        auto last = s;
1✔
450
        this->entries_.insert({map_stmt, this->users_.at(s).get()});
1✔
451

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

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

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

470
        return {s, t};
1✔
471
    }
472

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

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

479
      };
138✔
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) {
138✔
487
    users_.clear();
138✔
488
    graph_.clear();
138✔
489
    source_ = nullptr;
138✔
490
    sink_ = nullptr;
138✔
491
    dom_tree_.clear();
138✔
492
    pdom_tree_.clear();
138✔
493
    users_by_sdfg_.clear();
138✔
494
    users_by_sdfg_loop_condition_.clear();
138✔
495
    users_by_sdfg_loop_init_.clear();
138✔
496
    users_by_sdfg_loop_update_.clear();
138✔
497

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

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

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

521
    std::list<User*> sinks;
138✔
522
    for (auto& entry : this->users_) {
2,544✔
523
        if (boost::out_degree(entry.first, this->graph_) == 0) {
2,406✔
524
            sinks.push_back(entry.second.get());
138✔
525
        }
138✔
526
    }
527
    if (sinks.size() == 0) {
138✔
528
        throw InvalidSDFGException("Users Analysis: No sink node");
×
529
    }
530
    if (sinks.size() > 1) {
138✔
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();
138✔
543
    }
544

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

582
    this->init_dom_tree();
138✔
583
};
138✔
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 {
421✔
598
    std::vector<User*> us;
421✔
599
    for (auto& entry : this->users_) {
45,869✔
600
        if (entry.second->container() != container) {
45,448✔
601
            continue;
40,467✔
602
        }
603
        if (entry.second->use() == Use::NOP) {
4,981✔
604
            continue;
×
605
        }
606
        us.push_back(entry.second.get());
4,981✔
607
    }
608

609
    return us;
421✔
610
};
421✔
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 {
52✔
625
    if (this->writes_.find(container) == this->writes_.end()) {
52✔
626
        return {};
25✔
627
    } else {
628
        return this->writes_.at(container);
27✔
629
    }
630
};
52✔
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 {
39✔
645
    if (this->reads_.find(container) == this->reads_.end()) {
39✔
646
        return {};
13✔
647
    } else {
648
        return this->reads_.at(container);
26✔
649
    }
650
};
39✔
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) {
104✔
693
    if (auto data_node = dynamic_cast<data_flow::DataFlowNode*>(user->element())) {
104✔
694
        return static_cast<structured_control_flow::Block*>(data_node->get_parent().get_parent());
90✔
695
    } else if (auto transition =
14✔
696
                   dynamic_cast<structured_control_flow::Transition*>(user->element())) {
14✔
697
        return &transition->parent();
×
698
    } else {
699
        return dynamic_cast<structured_control_flow::ControlFlowNode*>(user->element());
14✔
700
    }
701
}
104✔
702

703
/****** Domination Analysis ******/
704

705
bool Users::dominates(User& user1, User& user) {
40✔
706
    auto dominator = this->dom_tree_.at(&user);
40✔
707
    while (dominator != nullptr) {
149✔
708
        if (dominator == &user1) {
139✔
709
            return true;
30✔
710
        }
711
        dominator = this->dom_tree_.at(dominator);
109✔
712
    }
713
    return false;
10✔
714
};
40✔
715

716
bool Users::post_dominates(User& user1, User& user) {
20✔
717
    auto dominator = this->pdom_tree_.at(&user);
20✔
718
    while (dominator != nullptr) {
68✔
719
        if (dominator == &user1) {
57✔
720
            return true;
9✔
721
        }
722
        dominator = this->pdom_tree_.at(dominator);
48✔
723
    }
724
    return false;
11✔
725
};
20✔
726

727
bool Users::is_dominated_by(User& user, Use use, const symbolic::Assumptions& assums) {
28✔
728
    auto dominator = this->dom_tree_.at(&user);
28✔
729
    while (dominator != nullptr) {
117✔
730
        if (dominator->use() != use) {
97✔
731
            dominator = this->dom_tree_.at(dominator);
72✔
732
            continue;
72✔
733
        }
734
        if (dominator->container() != user.container()) {
25✔
735
            dominator = this->dom_tree_.at(dominator);
15✔
736
            continue;
15✔
737
        }
738

739
        // Compare subsets
740
        auto& subsets = user.subsets();
10✔
741

742
        // Collect all relevant symbols
743
        std::unordered_set<std::string> symbols;
10✔
744
        for (auto& subset : subsets) {
20✔
745
            for (auto& dim : subset) {
14✔
746
                auto atoms = symbolic::atoms(dim);
4✔
747
                for (auto& atom : atoms) {
7✔
748
                    symbols.insert(atom->get_name());
3✔
749
                }
750
            }
4✔
751
        }
752
        // If symbols are not constant, we cannot compare the subsets
753
        if (!this->is_constant(symbols, *dominator, user)) {
10✔
754
            dominator = this->dom_tree_.at(dominator);
1✔
755
            continue;
1✔
756
        }
757

758
        // Now find for every subset of user, if there is a subset of dominator that is the same
759
        auto& subsets_dominator = dominator->subsets();
9✔
760
        bool all_subsets_dominated = true;
9✔
761
        for (auto& subset : subsets) {
17✔
762
            bool subset_dominated = false;
9✔
763
            for (auto& subset_dominator : subsets_dominator) {
10✔
764
                bool dominated = symbolic::is_equivalent(subset, subset_dominator, {}, assums);
9✔
765
                if (dominated) {
9✔
766
                    subset_dominated = true;
8✔
767
                    break;
8✔
768
                }
769
            }
770
            if (!subset_dominated) {
9✔
771
                all_subsets_dominated = false;
1✔
772
                break;
1✔
773
            }
774
        }
775
        if (all_subsets_dominated) {
9✔
776
            return true;
8✔
777
        }
778
        dominator = this->dom_tree_.at(dominator);
1✔
779
    }
10✔
780
    return false;
20✔
781
}
28✔
782

783
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
24✔
784
    std::unordered_set<User*> uses;
24✔
785
    std::unordered_set<User*> visited;
24✔
786
    std::list<User*> queue = {&user2};
24✔
787
    while (!queue.empty()) {
150✔
788
        auto current = queue.front();
126✔
789
        queue.pop_front();
126✔
790

791
        // Stop conditions
792
        if (current == &user1) {
126✔
793
            continue;
24✔
794
        }
795

796
        if (visited.find(current) != visited.end()) {
102✔
797
            continue;
2✔
798
        }
799
        visited.insert(current);
100✔
800

801
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
100✔
802
            uses.insert(current);
27✔
803
        }
27✔
804

805
        // Extend search
806
        // Backward search to utilize domination user1 over user
807
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
100✔
808
        auto edges = std::ranges::subrange(eb, ee);
200✔
809
        for (auto edge : edges) {
202✔
810
            auto v = boost::source(edge, this->graph_);
102✔
811
            queue.push_back(this->users_.at(v).get());
102✔
812
        }
813
    }
814

815
    return uses;
24✔
816
};
24✔
817

818
const std::unordered_set<User*> Users::all_uses_after(User& user) {
×
819
    std::unordered_set<User*> uses;
×
820
    std::unordered_set<User*> visited;
×
821
    std::list<User*> queue = {&user};
×
822
    while (!queue.empty()) {
×
823
        auto current = queue.front();
×
824
        queue.pop_front();
×
825
        if (visited.find(current) != visited.end()) {
×
826
            continue;
×
827
        }
828
        visited.insert(current);
×
829

830
        if (current != &user && current->use() != Use::NOP) {
×
831
            uses.insert(current);
×
832
        }
×
833

834
        // Extend search
835
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
836
        auto edges = std::ranges::subrange(eb, ee);
×
837
        for (auto edge : edges) {
×
838
            auto v = boost::target(edge, this->graph_);
×
839
            queue.push_back(this->users_.at(v).get());
×
840
        }
841
    }
842

843
    return uses;
×
844
};
×
845

846
bool Users::is_constant(const std::unordered_set<std::string>& containers, User& user1,
10✔
847
                        User& user2) {
848
    for (auto& user : this->all_uses_between(user1, user2)) {
25✔
849
        if (user->use() == Use::WRITE || user->use() == Use::MOVE) {
15✔
850
            if (containers.find(user->container()) != containers.end()) {
3✔
851
                return false;
1✔
852
            }
853
        }
2✔
854
    }
855
    return true;
9✔
856
}
10✔
857

858
UsersView::UsersView(Users& users, structured_control_flow::ControlFlowNode& node) : users_(users) {
142✔
859
    this->entry_ = users.entries_.at(&node);
142✔
860
    this->exit_ = users.exits_.at(&node);
142✔
861

862
    // Collect sub users
863
    std::unordered_set<User*> visited;
142✔
864
    std::list<User*> queue = {this->exit_};
142✔
865
    while (!queue.empty()) {
2,775✔
866
        auto current = queue.front();
2,633✔
867
        queue.pop_front();
2,633✔
868

869
        // Stop conditions
870
        if (current == this->entry_) {
2,633✔
871
            continue;
142✔
872
        }
873

874
        if (visited.find(current) != visited.end()) {
2,491✔
875
            continue;
159✔
876
        }
877
        visited.insert(current);
2,332✔
878

879
        this->sub_users_.insert(current);
2,332✔
880

881
        // Extend search
882
        // Backward search to utilize domination user1 over user
883
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
2,332✔
884
        auto edges = std::ranges::subrange(eb, ee);
4,664✔
885
        for (auto edge : edges) {
4,823✔
886
            auto v = boost::source(edge, users.graph_);
2,491✔
887
            queue.push_back(users.users_.at(v).get());
2,491✔
888
        }
889
    }
890

891
    // Collect sub dom tree
892
    auto& dom_tree = this->users_.dom_tree_;
142✔
893

894
    for (auto& user : this->sub_users_) {
2,474✔
895
        this->sub_dom_tree_[user] = dom_tree[user];
2,332✔
896
    }
897
    this->sub_dom_tree_[this->entry_] = nullptr;
142✔
898

899
    // Collect sub post dom tree
900
    auto& pdom_tree = this->users_.pdom_tree_;
142✔
901
    for (auto& user : this->sub_users_) {
2,474✔
902
        this->sub_pdom_tree_[user] = pdom_tree[user];
2,332✔
903
    }
904
    this->sub_pdom_tree_[this->exit_] = nullptr;
142✔
905
};
142✔
906

907
std::vector<User*> UsersView::uses() const {
56✔
908
    std::vector<User*> us;
56✔
909
    for (auto& user : this->sub_users_) {
1,066✔
910
        if (user->use() == Use::NOP) {
1,010✔
911
            continue;
358✔
912
        }
913
        us.push_back(user);
652✔
914
    }
915

916
    return us;
56✔
917
};
56✔
918

919
std::vector<User*> UsersView::uses(const std::string& container) const {
428✔
920
    std::vector<User*> us;
428✔
921
    for (auto& user : this->sub_users_) {
39,846✔
922
        if (user->container() != container) {
39,418✔
923
            continue;
35,134✔
924
        }
925
        if (user->use() == Use::NOP) {
4,284✔
926
            continue;
×
927
        }
928
        us.push_back(user);
4,284✔
929
    }
930

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

934
std::vector<User*> UsersView::writes() const {
77✔
935
    std::vector<User*> us;
77✔
936
    for (auto& user : this->sub_users_) {
1,366✔
937
        if (user->use() != Use::WRITE) {
1,289✔
938
            continue;
1,107✔
939
        }
940
        us.push_back(user);
182✔
941
    }
942

943
    return us;
77✔
944
};
77✔
945

946
std::vector<User*> UsersView::writes(const std::string& container) const {
149✔
947
    std::vector<User*> us;
149✔
948
    for (auto& user : this->sub_users_) {
4,749✔
949
        if (user->container() != container) {
4,600✔
950
            continue;
4,262✔
951
        }
952
        if (user->use() != Use::WRITE) {
338✔
953
            continue;
192✔
954
        }
955
        us.push_back(user);
146✔
956
    }
957

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

961
std::vector<User*> UsersView::reads() const {
43✔
962
    std::vector<User*> us;
43✔
963
    for (auto& user : this->sub_users_) {
973✔
964
        if (user->use() != Use::READ) {
930✔
965
            continue;
435✔
966
        }
967
        us.push_back(user);
495✔
968
    }
969

970
    return us;
43✔
971
};
43✔
972

973
std::vector<User*> UsersView::reads(const std::string& container) const {
95✔
974
    std::vector<User*> us;
95✔
975
    for (auto& user : this->sub_users_) {
3,118✔
976
        if (user->container() != container) {
3,023✔
977
            continue;
2,818✔
978
        }
979
        if (user->use() != Use::READ) {
205✔
980
            continue;
106✔
981
        }
982
        us.push_back(user);
99✔
983
    }
984

985
    return us;
95✔
986
};
95✔
987

988
std::vector<User*> UsersView::views() const {
74✔
989
    std::vector<User*> us;
74✔
990
    for (auto& user : this->sub_users_) {
1,339✔
991
        if (user->use() != Use::VIEW) {
1,265✔
992
            continue;
1,265✔
993
        }
994
        us.push_back(user);
×
995
    }
996

997
    return us;
74✔
998
};
74✔
999

1000
std::vector<User*> UsersView::views(const std::string& container) const {
×
1001
    std::vector<User*> us;
×
1002
    for (auto& user : this->sub_users_) {
×
1003
        if (user->container() != container) {
×
1004
            continue;
×
1005
        }
1006
        if (user->use() != Use::VIEW) {
×
1007
            continue;
×
1008
        }
1009
        us.push_back(user);
×
1010
    }
1011

1012
    return us;
×
1013
};
×
1014

1015
std::vector<User*> UsersView::moves() const {
74✔
1016
    std::vector<User*> us;
74✔
1017
    for (auto& user : this->sub_users_) {
1,339✔
1018
        if (user->use() != Use::MOVE) {
1,265✔
1019
            continue;
1,265✔
1020
        }
1021
        us.push_back(user);
×
1022
    }
1023

1024
    return us;
74✔
1025
};
74✔
1026

1027
std::vector<User*> UsersView::moves(const std::string& container) const {
×
1028
    std::vector<User*> us;
×
1029
    for (auto& user : this->sub_users_) {
×
1030
        if (user->container() != container) {
×
1031
            continue;
×
1032
        }
1033
        if (user->use() != Use::MOVE) {
×
1034
            continue;
×
1035
        }
1036
        us.push_back(user);
×
1037
    }
1038

1039
    return us;
×
1040
};
×
1041

1042
bool UsersView::dominates(User& user1, User& user) {
×
1043
    auto dominator = this->sub_dom_tree_[&user];
×
1044
    while (dominator != nullptr) {
×
1045
        if (dominator == &user1) {
×
1046
            return true;
×
1047
        }
1048
        dominator = this->sub_dom_tree_[dominator];
×
1049
    }
1050
    return false;
×
1051
};
×
1052

1053
bool UsersView::post_dominates(User& user1, User& user) {
×
1054
    auto dominator = this->sub_pdom_tree_[&user];
×
1055
    while (dominator != nullptr) {
×
1056
        if (dominator == &user1) {
×
1057
            return true;
×
1058
        }
1059
        dominator = this->sub_pdom_tree_[dominator];
×
1060
    }
1061
    return false;
×
1062
};
×
1063

1064
bool UsersView::is_dominated_by(User& user, Use use, const symbolic::Assumptions& assums) {
49✔
1065
    auto dominator = this->sub_dom_tree_.at(&user);
49✔
1066
    while (dominator != nullptr) {
323✔
1067
        if (dominator->use() != use) {
305✔
1068
            dominator = this->sub_dom_tree_.at(dominator);
261✔
1069
            continue;
261✔
1070
        }
1071
        if (dominator->container() != user.container()) {
44✔
1072
            dominator = this->sub_dom_tree_.at(dominator);
13✔
1073
            continue;
13✔
1074
        }
1075

1076
        // Compare subsets
1077
        auto& subsets = user.subsets();
31✔
1078

1079
        // Collect all relevant symbols
1080
        std::unordered_set<std::string> symbols;
31✔
1081
        for (auto& subset : subsets) {
62✔
1082
            for (auto& dim : subset) {
31✔
1083
                auto atoms = symbolic::atoms(dim);
×
1084
                for (auto& atom : atoms) {
×
1085
                    symbols.insert(atom->get_name());
×
1086
                }
1087
            }
×
1088
        }
1089
        // If symbols are not constant, we cannot compare the subsets
1090
        if (!this->is_constant(symbols, *dominator, user)) {
31✔
1091
            dominator = this->sub_dom_tree_.at(dominator);
×
1092
            continue;
×
1093
        }
1094

1095
        // Now find for every subset of user, if there is a subset of dominator that is the same
1096
        auto& subsets_dominator = dominator->subsets();
31✔
1097
        bool all_subsets_dominated = true;
31✔
1098
        for (auto& subset : subsets) {
62✔
1099
            bool subset_dominated = false;
31✔
1100
            for (auto& subset_dominator : subsets_dominator) {
31✔
1101
                bool dominated = symbolic::is_equivalent(subset, subset_dominator, {}, assums);
31✔
1102
                if (dominated) {
31✔
1103
                    subset_dominated = true;
31✔
1104
                    break;
31✔
1105
                }
1106
            }
1107
            if (!subset_dominated) {
31✔
1108
                all_subsets_dominated = false;
×
1109
                break;
×
1110
            }
1111
        }
1112
        if (all_subsets_dominated) {
31✔
1113
            return true;
31✔
1114
        }
1115
        dominator = this->sub_dom_tree_.at(dominator);
×
1116
    }
31✔
1117
    return false;
18✔
1118
}
49✔
1119

1120
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
31✔
1121
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
31✔
1122
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
31✔
1123

1124
    std::unordered_set<User*> uses;
31✔
1125
    std::unordered_set<User*> visited;
31✔
1126
    std::list<User*> queue = {&user1};
31✔
1127
    while (!queue.empty()) {
399✔
1128
        auto current = queue.front();
368✔
1129
        queue.pop_front();
368✔
1130
        if (visited.find(current) != visited.end()) {
368✔
1131
            continue;
49✔
1132
        }
1133
        visited.insert(current);
319✔
1134

1135
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
319✔
1136
            uses.insert(current);
139✔
1137
        }
139✔
1138

1139
        // Stop conditions
1140
        if (current == exit_) {
319✔
1141
            continue;
28✔
1142
        }
1143

1144
        if (current == &user2) {
291✔
1145
            continue;
31✔
1146
        }
1147

1148
        // Extend search
1149
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
260✔
1150
        auto edges = std::ranges::subrange(eb, ee);
520✔
1151
        for (auto edge : edges) {
597✔
1152
            auto v = boost::target(edge, this->users_.graph_);
337✔
1153
            queue.push_back(this->users_.users_.at(v).get());
337✔
1154
        }
1155
    }
1156

1157
    return uses;
31✔
1158
};
31✔
1159

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

1163
    std::unordered_set<User*> uses;
×
1164
    std::unordered_set<User*> visited;
×
1165
    std::list<User*> queue = {&user};
×
1166
    while (!queue.empty()) {
×
1167
        auto current = queue.front();
×
1168
        queue.pop_front();
×
1169
        if (visited.find(current) != visited.end()) {
×
1170
            continue;
×
1171
        }
1172
        visited.insert(current);
×
1173

1174
        if (current != &user && current->use() != Use::NOP) {
×
1175
            uses.insert(current);
×
1176
        }
×
1177

1178
        if (current == exit_) {
×
1179
            continue;
×
1180
        }
1181

1182
        // Extend search
1183
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1184
        auto edges = std::ranges::subrange(eb, ee);
×
1185
        for (auto edge : edges) {
×
1186
            auto v = boost::target(edge, this->users_.graph_);
×
1187
            queue.push_back(this->users_.users_.at(v).get());
×
1188
        }
1189
    }
1190

1191
    return uses;
×
1192
};
×
1193

1194
bool UsersView::is_constant(const std::unordered_set<std::string>& containers, User& user1,
31✔
1195
                            User& user2) {
1196
    for (auto& user : this->all_uses_between(user1, user2)) {
170✔
1197
        if (user->use() == Use::WRITE || user->use() == Use::MOVE) {
139✔
1198
            if (containers.find(user->container()) != containers.end()) {
7✔
1199
                return false;
×
1200
            }
1201
        }
7✔
1202
    }
1203
    return true;
31✔
1204
}
31✔
1205

1206
User* Users::get_user(const std::string& container, Element* element, Use use, bool is_init,
310✔
1207
                      bool is_condition, bool is_update) {
1208
    if (auto for_loop = dynamic_cast<structured_control_flow::For*>(element)) {
310✔
1209
        if (is_init) {
38✔
1210
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
10✔
1211
            return tmp;
10✔
1212
        } else if (is_condition) {
28✔
1213
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
10✔
1214
        } else if (is_update) {
18✔
1215
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
18✔
1216
        }
1217
    }
×
1218
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
272✔
1219
    return tmp;
272✔
1220
}
310✔
1221

1222
void Users::add_user(std::unique_ptr<User> user) {
1,166✔
1223
    auto vertex = user->vertex_;
1,166✔
1224
    this->users_.insert({vertex, std::move(user)});
1,166✔
1225

1226
    auto user_ptr = this->users_.at(vertex).get();
1,166✔
1227
    auto* target_structure = &users_by_sdfg_;
1,166✔
1228
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,166✔
1229
        auto for_loop = dynamic_cast<structured_control_flow::For*>(user_ptr->element());
439✔
1230
        if (for_loop == nullptr) {
439✔
1231
            throw std::invalid_argument("Invalid user type");
×
1232
        }
1233
        if (for_user->is_init()) {
439✔
1234
            target_structure = &users_by_sdfg_loop_init_;
94✔
1235
        } else if (for_user->is_condition()) {
439✔
1236
            target_structure = &users_by_sdfg_loop_condition_;
171✔
1237
        } else if (for_user->is_update()) {
345✔
1238
            target_structure = &users_by_sdfg_loop_update_;
174✔
1239
        } else {
174✔
1240
            throw std::invalid_argument("Invalid user type");
×
1241
        }
1242
    }
439✔
1243

1244
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,166✔
1245
        target_structure->insert({user_ptr->container(), {}});
694✔
1246
    }
694✔
1247
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
2,332✔
1248
        (*target_structure)[user_ptr->container()].end()) {
1,166✔
1249
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
1,070✔
1250
    }
1,070✔
1251
    target_structure->at(user_ptr->container())
1,166✔
1252
        .at(user_ptr->element())
1,166✔
1253
        .insert({user_ptr->use(), user_ptr});
1,166✔
1254
}
1,166✔
1255

1256
std::unordered_set<std::string> Users::locals(structured_control_flow::ControlFlowNode& node) {
56✔
1257
    auto& sdfg = this->sdfg_;
56✔
1258

1259
    // Locals have no uses outside of the node
1260
    // We can check this by comparing the number of uses of the container in the view and the total
1261
    // number of uses of the container in the users map.
1262
    std::unordered_set<std::string> locals;
56✔
1263
    UsersView view(*this, node);
56✔
1264
    for (auto& user : view.uses()) {
708✔
1265
        if (!sdfg.is_transient(user->container())) {
652✔
1266
            continue;
232✔
1267
        }
1268
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
420✔
1269
            locals.insert(user->container());
239✔
1270
        }
239✔
1271
    }
1272

1273
    return locals;
56✔
1274
};
56✔
1275

1276
}  // namespace analysis
1277
}  // 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