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

daisytuner / sdfglib / 15619235800

12 Jun 2025 07:29PM UTC coverage: 63.07% (+0.03%) from 63.04%
15619235800

push

github

web-flow
Merge pull request #74 from daisytuner/extended-expression-equivalence

adds extended check for equivalence of expressions

142 of 225 new or added lines in 2 files covered. (63.11%)

1 existing line in 1 file now uncovered.

7487 of 11871 relevant lines covered (63.07%)

103.28 hits per line

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

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

27
      };
1,921✔
28

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

33
      };
1,013✔
34

35
User::~User() {
5,454✔
36

37
};
5,454✔
38

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

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

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

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

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

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

69
    // Use of symbol
70
    return {{}};
18✔
71
};
273✔
72

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

80
      };
414✔
81

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

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

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

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

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

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

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

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

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

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

154
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
256✔
155
                        boost::add_edge(last, v, this->graph_);
64✔
156
                    } else {
64✔
157
                        first = v;
192✔
158
                    }
159
                    last = v;
256✔
160
                }
256✔
161
            }
470✔
162
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
689✔
163
            if (tasklet->is_conditional()) {
219✔
164
                auto& condition = tasklet->condition();
12✔
165
                for (auto& atom : symbolic::atoms(condition)) {
24✔
166
                    auto v = boost::add_vertex(this->graph_);
12✔
167
                    this->add_user(
12✔
168
                        std::make_unique<User>(v, atom->get_name(), tasklet, &dataflow, Use::READ));
12✔
169
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
12✔
170
                        boost::add_edge(last, v, this->graph_);
6✔
171
                    } else {
6✔
172
                        first = v;
6✔
173
                    }
174
                    last = v;
12✔
175
                }
176
            }
12✔
177
        }
219✔
178

179
        for (auto& oedge : dataflow.out_edges(*node)) {
1,177✔
180
            std::unordered_set<std::string> used;
488✔
181
            for (auto dim : oedge.subset()) {
854✔
182
                for (auto atom : symbolic::atoms(dim)) {
890✔
183
                    if (used.find(atom->get_name()) != used.end()) {
524✔
184
                        continue;
×
185
                    }
186
                    used.insert(atom->get_name());
524✔
187

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

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

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

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

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

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

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

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

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

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

244
            std::unordered_set<std::string> used;
405✔
245
            for (auto& entry : child.second.assignments()) {
470✔
246
                for (auto atom : symbolic::atoms(entry.second)) {
92✔
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()) {
470✔
265
                auto v = boost::add_vertex(this->graph_);
65✔
266
                this->add_user(
65✔
267
                    std::make_unique<User>(v, entry.first->get_name(), &child.second, Use::WRITE));
65✔
268

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

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

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

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

293
        std::unordered_set<std::string> used;
23✔
294
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
64✔
295
            auto& condition = if_else_stmt->at(i).second;
41✔
296
            for (auto atom : symbolic::atoms(condition)) {
83✔
297
                if (used.find(atom->get_name()) != used.end()) {
42✔
298
                    continue;
16✔
299
                }
300
                used.insert(atom->get_name());
26✔
301

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

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

307
                boost::add_edge(last, v, this->graph_);
26✔
308
                last = v;
26✔
309
            }
42✔
310
        }
41✔
311

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

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

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

331
        return {s, t};
23✔
332
    } else if (auto loop_stmt = dynamic_cast<structured_control_flow::While*>(&node)) {
124✔
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)) {
91✔
357
        // NOP
358
        auto s = boost::add_vertex(this->graph_);
80✔
359
        this->users_.insert({s, std::make_unique<User>(s, "", for_stmt, Use::NOP)});
80✔
360
        auto last = s;
80✔
361
        this->entries_.insert({for_stmt, this->users_.at(s).get()});
80✔
362

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

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

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

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

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

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

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

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

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

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

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

424
        return {s, t};
80✔
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
};
699✔
475

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

479
      };
114✔
480

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

484
      };
48✔
485

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

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

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

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

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

545
    // Collect sub structures
546
    for (auto& entry : this->users_) {
3,096✔
547
        auto container = entry.second->container();
2,934✔
548
        switch (entry.second->use()) {
2,934✔
549
            case Use::READ: {
550
                if (this->reads_.find(container) == this->reads_.end()) {
1,097✔
551
                    this->reads_.insert({container, {}});
485✔
552
                }
485✔
553
                this->reads_[container].push_back(entry.second.get());
1,097✔
554
                break;
1,097✔
555
            }
556
            case Use::WRITE: {
557
                if (this->writes_.find(container) == this->writes_.end()) {
445✔
558
                    this->writes_.insert({container, {}});
311✔
559
                }
311✔
560
                this->writes_[container].push_back(entry.second.get());
445✔
561
                break;
445✔
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,388✔
579
        }
580
    }
2,934✔
581
};
162✔
582

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

592
    return us;
×
593
};
×
594

595
std::vector<User*> Users::uses(const std::string& container) const {
1✔
596
    std::vector<User*> us;
1✔
597
    for (auto& entry : this->users_) {
10✔
598
        if (entry.second->container() != container) {
9✔
599
            continue;
7✔
600
        }
601
        if (entry.second->use() == Use::NOP) {
2✔
602
            continue;
×
603
        }
604
        us.push_back(entry.second.get());
2✔
605
    }
606

607
    return us;
1✔
608
};
1✔
609

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

619
    return us;
×
620
};
×
621

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

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

639
    return us;
×
640
};
×
641

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

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

659
    return us;
×
660
};
×
661

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

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

679
    return us;
×
680
};
×
681

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

690
/****** Domination Analysis ******/
691

692
bool Users::dominates(User& user1, User& user) {
38✔
693
    if (this->dom_tree_.empty()) {
38✔
694
        this->init_dom_tree();
23✔
695
    }
23✔
696
    auto dominator = this->dom_tree_.at(&user);
38✔
697
    while (dominator != nullptr) {
143✔
698
        if (dominator == &user1) {
133✔
699
            return true;
28✔
700
        }
701
        dominator = this->dom_tree_.at(dominator);
105✔
702
    }
703
    return false;
10✔
704
};
38✔
705

706
bool Users::post_dominates(User& user1, User& user) {
20✔
707
    if (this->pdom_tree_.empty()) {
20✔
708
        this->init_dom_tree();
×
709
    }
×
710
    auto dominator = this->pdom_tree_.at(&user);
20✔
711
    while (dominator != nullptr) {
68✔
712
        if (dominator == &user1) {
57✔
713
            return true;
9✔
714
        }
715
        dominator = this->pdom_tree_.at(dominator);
48✔
716
    }
717
    return false;
11✔
718
};
20✔
719

720
bool Users::is_dominated_by(User& user, Use use, const symbolic::Assumptions& assums) {
28✔
721
    auto dominator = this->dom_tree_.at(&user);
28✔
722
    while (dominator != nullptr) {
117✔
723
        if (dominator->use() != use) {
97✔
724
            dominator = this->dom_tree_.at(dominator);
72✔
725
            continue;
72✔
726
        }
727
        if (dominator->container() != user.container()) {
25✔
728
            dominator = this->dom_tree_.at(dominator);
15✔
729
            continue;
15✔
730
        }
731

732
        // Compare subsets
733
        auto& subsets = user.subsets();
10✔
734

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

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

776
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
83✔
777
    std::unordered_set<User*> uses;
83✔
778
    std::unordered_set<User*> visited;
83✔
779
    std::list<User*> queue = {&user2};
83✔
780
    while (!queue.empty()) {
1,348✔
781
        auto current = queue.front();
1,265✔
782
        queue.pop_front();
1,265✔
783

784
        // Stop conditions
785
        if (current == &user1) {
1,265✔
786
            continue;
83✔
787
        }
788

789
        if (visited.find(current) != visited.end()) {
1,182✔
790
            continue;
71✔
791
        }
792
        visited.insert(current);
1,111✔
793

794
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
1,111✔
795
            uses.insert(current);
693✔
796
        }
693✔
797

798
        // Extend search
799
        // Backward search to utilize domination user1 over user
800
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
1,111✔
801
        auto edges = std::ranges::subrange(eb, ee);
2,222✔
802
        for (auto edge : edges) {
2,293✔
803
            auto v = boost::source(edge, this->graph_);
1,182✔
804
            queue.push_back(this->users_.at(v).get());
1,182✔
805
        }
806
    }
807

808
    return uses;
83✔
809
};
83✔
810

811
const std::unordered_set<User*> Users::all_uses_after(User& user) {
×
812
    std::unordered_set<User*> uses;
×
813
    std::unordered_set<User*> visited;
×
814
    std::list<User*> queue = {&user};
×
815
    while (!queue.empty()) {
×
816
        auto current = queue.front();
×
817
        queue.pop_front();
×
818
        if (visited.find(current) != visited.end()) {
×
819
            continue;
×
820
        }
821
        visited.insert(current);
×
822

823
        if (current != &user && current->use() != Use::NOP) {
×
824
            uses.insert(current);
×
825
        }
×
826

827
        // Extend search
828
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
829
        auto edges = std::ranges::subrange(eb, ee);
×
830
        for (auto edge : edges) {
×
831
            auto v = boost::target(edge, this->graph_);
×
832
            queue.push_back(this->users_.at(v).get());
×
833
        }
834
    }
835

836
    return uses;
×
837
};
×
838

839
bool Users::is_constant(const std::unordered_set<std::string>& containers, User& user1,
10✔
840
                        User& user2) {
841
    for (auto& user : this->all_uses_between(user1, user2)) {
23✔
842
        if (user->use() == Use::WRITE || user->use() == Use::MOVE) {
13✔
843
            if (containers.find(user->container()) != containers.end()) {
2✔
844
                return false;
1✔
845
            }
846
        }
1✔
847
    }
848
    return true;
9✔
849
}
10✔
850

851
UsersView::UsersView(Users& users, structured_control_flow::ControlFlowNode& node) : users_(users) {
59✔
852
    auto& entry = users.entries_.at(&node);
59✔
853
    auto& exit = users.exits_.at(&node);
59✔
854
    this->entry_ = entry;
59✔
855
    this->exit_ = exit;
59✔
856
    this->sub_users_ = users.all_uses_between(*entry, *exit);
59✔
857

858
    if (this->users_.dom_tree_.empty()) {
59✔
859
        this->users_.init_dom_tree();
41✔
860
    }
41✔
861
    auto& dom_tree = this->users_.dom_tree_;
59✔
862

863
    for (auto& user : this->sub_users_) {
725✔
864
        this->sub_dom_tree_[user] = dom_tree[user];
666✔
865
    }
866
    this->sub_dom_tree_[this->entry_] = nullptr;
59✔
867

868
    if (this->users_.pdom_tree_.empty()) {
59✔
869
        this->users_.init_dom_tree();
×
870
    }
×
871
    auto& pdom_tree = this->users_.pdom_tree_;
59✔
872
    for (auto& user : this->sub_users_) {
725✔
873
        this->sub_pdom_tree_[user] = pdom_tree[user];
666✔
874
    }
875
    this->sub_pdom_tree_[this->exit_] = nullptr;
59✔
876
};
59✔
877

878
std::vector<User*> UsersView::uses() const {
×
879
    std::vector<User*> us;
×
880
    for (auto& user : this->sub_users_) {
×
881
        if (user->use() == Use::NOP) {
×
882
            continue;
×
883
        }
884
        us.push_back(user);
×
885
    }
886

887
    return us;
×
888
};
×
889

890
std::vector<User*> UsersView::uses(const std::string& container) const {
8✔
891
    std::vector<User*> us;
8✔
892
    for (auto& user : this->sub_users_) {
46✔
893
        if (user->container() != container) {
38✔
894
            continue;
24✔
895
        }
896
        if (user->use() == Use::NOP) {
14✔
897
            continue;
×
898
        }
899
        us.push_back(user);
14✔
900
    }
901

902
    return us;
8✔
903
};
8✔
904

905
std::vector<User*> UsersView::writes() const {
50✔
906
    std::vector<User*> us;
50✔
907
    for (auto& user : this->sub_users_) {
700✔
908
        if (user->use() != Use::WRITE) {
650✔
909
            continue;
512✔
910
        }
911
        us.push_back(user);
138✔
912
    }
913

914
    return us;
50✔
915
};
50✔
916

917
std::vector<User*> UsersView::writes(const std::string& container) const {
84✔
918
    std::vector<User*> us;
84✔
919
    for (auto& user : this->sub_users_) {
2,782✔
920
        if (user->container() != container) {
2,698✔
921
            continue;
2,545✔
922
        }
923
        if (user->use() != Use::WRITE) {
153✔
924
            continue;
74✔
925
        }
926
        us.push_back(user);
79✔
927
    }
928

929
    return us;
84✔
930
};
84✔
931

932
std::vector<User*> UsersView::reads() const {
47✔
933
    std::vector<User*> us;
47✔
934
    for (auto& user : this->sub_users_) {
686✔
935
        if (user->use() != Use::READ) {
639✔
936
            continue;
136✔
937
        }
938
        us.push_back(user);
503✔
939
    }
940

941
    return us;
47✔
942
};
47✔
943

944
std::vector<User*> UsersView::reads(const std::string& container) const {
54✔
945
    std::vector<User*> us;
54✔
946
    for (auto& user : this->sub_users_) {
1,861✔
947
        if (user->container() != container) {
1,807✔
948
            continue;
1,699✔
949
        }
950
        if (user->use() != Use::READ) {
108✔
951
            continue;
58✔
952
        }
953
        us.push_back(user);
50✔
954
    }
955

956
    return us;
54✔
957
};
54✔
958

959
std::vector<User*> UsersView::views() const {
47✔
960
    std::vector<User*> us;
47✔
961
    for (auto& user : this->sub_users_) {
686✔
962
        if (user->use() != Use::VIEW) {
639✔
963
            continue;
639✔
964
        }
965
        us.push_back(user);
×
966
    }
967

968
    return us;
47✔
969
};
47✔
970

971
std::vector<User*> UsersView::views(const std::string& container) const {
×
972
    std::vector<User*> us;
×
973
    for (auto& user : this->sub_users_) {
×
974
        if (user->container() != container) {
×
975
            continue;
×
976
        }
977
        if (user->use() != Use::VIEW) {
×
978
            continue;
×
979
        }
980
        us.push_back(user);
×
981
    }
982

983
    return us;
×
984
};
×
985

986
std::vector<User*> UsersView::moves() const {
47✔
987
    std::vector<User*> us;
47✔
988
    for (auto& user : this->sub_users_) {
686✔
989
        if (user->use() != Use::MOVE) {
639✔
990
            continue;
639✔
991
        }
992
        us.push_back(user);
×
993
    }
994

995
    return us;
47✔
996
};
47✔
997

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

1010
    return us;
×
1011
};
×
1012

1013
bool UsersView::dominates(User& user1, User& user) {
×
1014
    auto dominator = this->sub_dom_tree_[&user];
×
1015
    while (dominator != nullptr) {
×
1016
        if (dominator == &user1) {
×
1017
            return true;
×
1018
        }
1019
        dominator = this->sub_dom_tree_[dominator];
×
1020
    }
1021
    return false;
×
1022
};
×
1023

1024
bool UsersView::post_dominates(User& user1, User& user) {
×
1025
    auto dominator = this->sub_pdom_tree_[&user];
×
1026
    while (dominator != nullptr) {
×
1027
        if (dominator == &user1) {
×
1028
            return true;
×
1029
        }
1030
        dominator = this->sub_pdom_tree_[dominator];
×
1031
    }
1032
    return false;
×
1033
};
×
1034

NEW
1035
bool UsersView::is_dominated_by(User& user, Use use, const symbolic::Assumptions& assums) {
×
1036
    auto dominator = this->sub_dom_tree_.at(&user);
×
1037
    while (dominator != nullptr) {
×
1038
        if (dominator->use() != use) {
×
1039
            dominator = this->sub_dom_tree_.at(dominator);
×
1040
            continue;
×
1041
        }
1042
        if (dominator->container() != user.container()) {
×
1043
            dominator = this->sub_dom_tree_.at(dominator);
×
1044
            continue;
×
1045
        }
1046

1047
        // Compare subsets
1048
        auto& subsets = user.subsets();
×
1049

1050
        // Collect all relevant symbols
1051
        std::unordered_set<std::string> symbols;
×
1052
        for (auto& subset : subsets) {
×
1053
            for (auto& dim : subset) {
×
1054
                auto atoms = symbolic::atoms(dim);
×
1055
                for (auto& atom : atoms) {
×
1056
                    symbols.insert(atom->get_name());
×
1057
                }
1058
            }
×
1059
        }
1060
        // If symbols are not constant, we cannot compare the subsets
1061
        if (!this->is_constant(symbols, *dominator, user)) {
×
1062
            dominator = this->sub_dom_tree_.at(dominator);
×
1063
            continue;
×
1064
        }
1065

1066
        // Now find for every subset of user, if there is a subset of dominator that is the same
1067
        auto& subsets_dominator = dominator->subsets();
×
1068
        bool all_subsets_dominated = true;
×
1069
        for (auto& subset : subsets) {
×
1070
            bool subset_dominated = false;
×
1071
            for (auto& subset_dominator : subsets_dominator) {
×
NEW
1072
                bool dominated = symbolic::is_equivalent(subset, subset_dominator, {}, assums);
×
1073
                if (dominated) {
×
1074
                    subset_dominated = true;
×
1075
                    break;
×
1076
                }
1077
            }
1078
            if (!subset_dominated) {
×
1079
                all_subsets_dominated = false;
×
1080
                break;
×
1081
            }
1082
        }
1083
        if (all_subsets_dominated) {
×
1084
            return true;
×
1085
        }
1086
        dominator = this->sub_dom_tree_.at(dominator);
×
1087
    }
×
1088
    return false;
×
1089
}
×
1090

1091
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
1092
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
1093
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
1094

1095
    std::unordered_set<User*> uses;
×
1096
    std::unordered_set<User*> visited;
×
1097
    std::list<User*> queue = {&user1};
×
1098
    while (!queue.empty()) {
×
1099
        auto current = queue.front();
×
1100
        queue.pop_front();
×
1101
        if (visited.find(current) != visited.end()) {
×
1102
            continue;
×
1103
        }
1104
        visited.insert(current);
×
1105

1106
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
1107
            uses.insert(current);
×
1108
        }
×
1109

1110
        // Stop conditions
1111
        if (current == exit_) {
×
1112
            continue;
×
1113
        }
1114

1115
        if (current == &user2) {
×
1116
            continue;
×
1117
        }
1118

1119
        // Extend search
1120
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1121
        auto edges = std::ranges::subrange(eb, ee);
×
1122
        for (auto edge : edges) {
×
1123
            auto v = boost::target(edge, this->users_.graph_);
×
1124
            queue.push_back(this->users_.users_.at(v).get());
×
1125
        }
1126
    }
1127

1128
    return uses;
×
1129
};
×
1130

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

1134
    std::unordered_set<User*> uses;
×
1135
    std::unordered_set<User*> visited;
×
1136
    std::list<User*> queue = {&user};
×
1137
    while (!queue.empty()) {
×
1138
        auto current = queue.front();
×
1139
        queue.pop_front();
×
1140
        if (visited.find(current) != visited.end()) {
×
1141
            continue;
×
1142
        }
1143
        visited.insert(current);
×
1144

1145
        if (current != &user && current->use() != Use::NOP) {
×
1146
            uses.insert(current);
×
1147
        }
×
1148

1149
        if (current == exit_) {
×
1150
            continue;
×
1151
        }
1152

1153
        // Extend search
1154
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1155
        auto edges = std::ranges::subrange(eb, ee);
×
1156
        for (auto edge : edges) {
×
1157
            auto v = boost::target(edge, this->users_.graph_);
×
1158
            queue.push_back(this->users_.users_.at(v).get());
×
1159
        }
1160
    }
1161

1162
    return uses;
×
1163
};
×
1164

1165
bool UsersView::is_constant(const std::unordered_set<std::string>& containers, User& user1,
×
1166
                            User& user2) {
1167
    for (auto& user : this->all_uses_between(user1, user2)) {
×
1168
        if (user->use() == Use::WRITE || user->use() == Use::MOVE) {
×
1169
            if (containers.find(user->container()) != containers.end()) {
×
1170
                return false;
×
1171
            }
1172
        }
×
1173
    }
1174
    return true;
×
1175
}
×
1176

1177
User* Users::get_user(const std::string& container, Element* element, Use use, bool is_init,
310✔
1178
                      bool is_condition, bool is_update) {
1179
    if (auto for_loop = dynamic_cast<structured_control_flow::For*>(element)) {
310✔
1180
        if (is_init) {
38✔
1181
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
10✔
1182
            return tmp;
10✔
1183
        } else if (is_condition) {
28✔
1184
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
10✔
1185
        } else if (is_update) {
18✔
1186
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
18✔
1187
        }
1188
    }
×
1189
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
272✔
1190
    return tmp;
272✔
1191
}
310✔
1192

1193
void Users::add_user(std::unique_ptr<User> user) {
1,546✔
1194
    auto vertex = user->vertex_;
1,546✔
1195
    this->users_.insert({vertex, std::move(user)});
1,546✔
1196

1197
    auto user_ptr = this->users_.at(vertex).get();
1,546✔
1198
    auto* target_structure = &users_by_sdfg_;
1,546✔
1199
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,546✔
1200
        auto for_loop = dynamic_cast<structured_control_flow::For*>(user_ptr->element());
414✔
1201
        if (for_loop == nullptr) {
414✔
1202
            throw std::invalid_argument("Invalid user type");
×
1203
        }
1204
        if (for_user->is_init()) {
414✔
1205
            target_structure = &users_by_sdfg_loop_init_;
93✔
1206
        } else if (for_user->is_condition()) {
414✔
1207
            target_structure = &users_by_sdfg_loop_condition_;
161✔
1208
        } else if (for_user->is_update()) {
321✔
1209
            target_structure = &users_by_sdfg_loop_update_;
160✔
1210
        } else {
160✔
1211
            throw std::invalid_argument("Invalid user type");
×
1212
        }
1213
    }
414✔
1214

1215
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,546✔
1216
        target_structure->insert({user_ptr->container(), {}});
795✔
1217
    }
795✔
1218
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
3,092✔
1219
        (*target_structure)[user_ptr->container()].end()) {
1,546✔
1220
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
1,457✔
1221
    }
1,457✔
1222
    target_structure->at(user_ptr->container())
1,546✔
1223
        .at(user_ptr->element())
1,546✔
1224
        .insert({user_ptr->use(), user_ptr});
1,546✔
1225
}
1,546✔
1226

1227
std::unordered_set<std::string> Users::locals(StructuredSDFG& sdfg,
48✔
1228
                                              structured_control_flow::ControlFlowNode& node) {
1229
    // Collect all node elements
1230
    Users local_users(sdfg, node);
48✔
1231
    analysis::AnalysisManager analysis_manager(sdfg_);
48✔
1232
    local_users.run(analysis_manager);
48✔
1233
    std::unordered_map<std::string, std::unordered_set<Element*>> elements;
48✔
1234
    for (auto& entry : local_users.users_) {
1,058✔
1235
        if (entry.second->use() == Use::NOP) {
1,010✔
1236
            continue;
370✔
1237
        }
1238
        if (!sdfg.is_transient(entry.second->container())) {
640✔
1239
            continue;
222✔
1240
        }
1241

1242
        if (elements.find(entry.second->container()) == elements.end()) {
418✔
1243
            elements[entry.second->container()] = {};
112✔
1244
        }
112✔
1245
        elements[entry.second->container()].insert(entry.second->element());
418✔
1246
    }
1247

1248
    // Determine locals
1249
    for (auto& entry : this->users_) {
1,716✔
1250
        if (entry.second->use() == Use::NOP) {
1,668✔
1251
            continue;
656✔
1252
        }
1253

1254
        auto& container = entry.second->container();
1,012✔
1255
        auto element = entry.second->element();
1,012✔
1256
        if (elements.find(container) == elements.end()) {
1,012✔
1257
            continue;
703✔
1258
        }
1259
        // used outside of node
1260
        if (elements[container].find(element) == elements[container].end()) {
309✔
1261
            elements.erase(container);
54✔
1262
        }
54✔
1263
    }
1264

1265
    std::unordered_set<std::string> locals;
48✔
1266
    for (auto& entry : elements) {
106✔
1267
        locals.insert(entry.first);
58✔
1268
    }
1269
    return locals;
48✔
1270
};
48✔
1271

1272
std::unordered_set<std::string> UsersView::locals(StructuredSDFG& sdfg,
×
1273
                                                  structured_control_flow::ControlFlowNode& node) {
1274
    // Collect all node elements
1275
    Users local_users(sdfg, node);
×
1276
    analysis::AnalysisManager analysis_manager(users_.sdfg_);
×
1277
    local_users.run(analysis_manager);
×
1278
    std::unordered_map<std::string, std::unordered_set<Element*>> elements;
×
1279
    for (auto& entry : local_users.users_) {
×
1280
        if (entry.second->use() == Use::NOP) {
×
1281
            continue;
×
1282
        }
1283
        if (!sdfg.is_transient(entry.second->container())) {
×
1284
            continue;
×
1285
        }
1286

1287
        if (elements.find(entry.second->container()) == elements.end()) {
×
1288
            elements[entry.second->container()] = {};
×
1289
        }
×
1290
        elements[entry.second->container()].insert(entry.second->element());
×
1291
    }
1292

1293
    // Determine locals
1294
    for (auto& entry : this->sub_users_) {
×
1295
        if (entry->use() == Use::NOP) {
×
1296
            continue;
×
1297
        }
1298

1299
        auto& container = entry->container();
×
1300
        auto element = entry->element();
×
1301
        if (elements.find(container) == elements.end()) {
×
1302
            continue;
×
1303
        }
1304
        // used outside of node
1305
        if (elements[container].find(element) == elements[container].end()) {
×
1306
            elements.erase(container);
×
1307
        }
×
1308
    }
1309

1310
    std::unordered_set<std::string> locals;
×
1311
    for (auto& entry : elements) {
×
1312
        locals.insert(entry.first);
×
1313
    }
1314
    return locals;
×
1315
};
×
1316

1317
}  // namespace analysis
1318
}  // 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