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

daisytuner / sdfglib / 15613171590

12 Jun 2025 02:21PM UTC coverage: 62.821% (+2.0%) from 60.871%
15613171590

Pull #73

github

web-flow
Merge 6ac34a9df into 0380faed7
Pull Request #73: Integrates transformations again

8 of 18 new or added lines in 6 files covered. (44.44%)

1 existing line in 1 file now uncovered.

7281 of 11590 relevant lines covered (62.82%)

105.12 hits per line

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

69.91
/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/symbolic.h"
19

20
namespace sdfg {
21
namespace analysis {
22

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

26
      };
1,909✔
27

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

32
      };
1,005✔
33

34
User::~User() {
5,414✔
35

36
};
5,414✔
37

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

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

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

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

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

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

68
    // Use of symbol
69
    return {{}};
18✔
70
};
271✔
71

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

79
      };
414✔
80

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

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

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

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

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

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

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

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

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

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

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

178
        for (auto& oedge : dataflow.out_edges(*node)) {
1,167✔
179
            std::unordered_set<std::string> used;
484✔
180
            for (auto dim : oedge.subset()) {
846✔
181
                for (auto atom : symbolic::atoms(dim)) {
882✔
182
                    if (used.find(atom->get_name()) != used.end()) {
520✔
183
                        continue;
×
184
                    }
185
                    used.insert(atom->get_name());
520✔
186

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

201
    return {first, last};
279✔
202
};
×
203

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

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

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

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

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

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

235
        graph::Vertex current = s;
292✔
236
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
694✔
237
            auto child = sequence_stmt->at(i);
402✔
238

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

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

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

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

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

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

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

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

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

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

292
        std::unordered_set<std::string> used;
22✔
293
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
62✔
294
            auto& condition = if_else_stmt->at(i).second;
40✔
295
            for (auto atom : symbolic::atoms(condition)) {
80✔
296
                if (used.find(atom->get_name()) != used.end()) {
40✔
297
                    continue;
16✔
298
                }
299
                used.insert(atom->get_name());
24✔
300

301
                auto v = boost::add_vertex(this->graph_);
24✔
302

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

306
                boost::add_edge(last, v, this->graph_);
24✔
307
                last = v;
24✔
308
            }
40✔
309
        }
40✔
310

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

478
      };
113✔
479

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

483
      };
48✔
484

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

497
    reads_.clear();
161✔
498
    writes_.clear();
161✔
499
    views_.clear();
161✔
500
    moves_.clear();
161✔
501

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

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

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

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

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

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

591
    return us;
×
592
};
×
593

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

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

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

618
    return us;
×
619
};
×
620

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

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

638
    return us;
×
639
};
×
640

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

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

658
    return us;
×
659
};
×
660

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

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

678
    return us;
×
679
};
×
680

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

689
/****** Domination Analysis ******/
690

691
bool Users::dominates(User& user1, User& user) {
36✔
692
    if (this->dom_tree_.empty()) {
36✔
693
        this->init_dom_tree();
22✔
694
    }
22✔
695
    auto dominator = this->dom_tree_.at(&user);
36✔
696
    while (dominator != nullptr) {
127✔
697
        if (dominator == &user1) {
118✔
698
            return true;
27✔
699
        }
700
        dominator = this->dom_tree_.at(dominator);
91✔
701
    }
702
    return false;
9✔
703
};
36✔
704

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

719
bool Users::is_dominated_by(User& user, Use use) {
26✔
720
    auto dominator = this->dom_tree_.at(&user);
26✔
721
    while (dominator != nullptr) {
101✔
722
        if (dominator->use() != use) {
82✔
723
            dominator = this->dom_tree_.at(dominator);
65✔
724
            continue;
65✔
725
        }
726
        if (dominator->container() != user.container()) {
17✔
727
            dominator = this->dom_tree_.at(dominator);
8✔
728
            continue;
8✔
729
        }
730

731
        // Compare subsets
732
        auto& subsets = user.subsets();
9✔
733

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

750
        // Now find for every subset of user, if there is a subset of dominator that is the same
751
        auto& subsets_dominator = dominator->subsets();
8✔
752
        bool all_subsets_dominated = true;
8✔
753
        for (auto& subset : subsets) {
15✔
754
            bool subset_dominated = false;
8✔
755
            for (auto& subset_dominator : subsets_dominator) {
9✔
756
                if (subset.size() != subset_dominator.size()) {
8✔
757
                    continue;
×
758
                }
759
                bool dominated = true;
8✔
760
                for (size_t i = 0; i < subset.size(); i++) {
9✔
761
                    if (!symbolic::eq(subset[i], subset_dominator[i])) {
2✔
762
                        dominated = false;
1✔
763
                        break;
1✔
764
                    }
765
                }
1✔
766
                if (dominated) {
8✔
767
                    subset_dominated = true;
7✔
768
                    break;
7✔
769
                }
770
            }
771
            if (!subset_dominated) {
8✔
772
                all_subsets_dominated = false;
1✔
773
                break;
1✔
774
            }
775
        }
776
        if (all_subsets_dominated) {
8✔
777
            return true;
7✔
778
        }
779
        dominator = this->dom_tree_.at(dominator);
1✔
780
    }
9✔
781
    return false;
19✔
782
}
26✔
783

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

792
        // Stop conditions
793
        if (current == &user1) {
1,243✔
794
            continue;
81✔
795
        }
796

797
        if (visited.find(current) != visited.end()) {
1,162✔
798
            continue;
71✔
799
        }
800
        visited.insert(current);
1,091✔
801

802
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
1,091✔
803
            uses.insert(current);
681✔
804
        }
681✔
805

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

816
    return uses;
81✔
817
};
81✔
818

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

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

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

844
    return uses;
×
845
};
×
846

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

859
UsersView::UsersView(Users& users, structured_control_flow::ControlFlowNode& node) : users_(users) {
58✔
860
    auto& entry = users.entries_.at(&node);
58✔
861
    auto& exit = users.exits_.at(&node);
58✔
862
    this->entry_ = entry;
58✔
863
    this->exit_ = exit;
58✔
864
    this->sub_users_ = users.all_uses_between(*entry, *exit);
58✔
865

866
    if (this->users_.dom_tree_.empty()) {
58✔
867
        this->users_.init_dom_tree();
41✔
868
    }
41✔
869
    auto& dom_tree = this->users_.dom_tree_;
58✔
870

871
    for (auto& user : this->sub_users_) {
720✔
872
        this->sub_dom_tree_[user] = dom_tree[user];
662✔
873
    }
874
    this->sub_dom_tree_[this->entry_] = nullptr;
58✔
875

876
    if (this->users_.pdom_tree_.empty()) {
58✔
877
        this->users_.init_dom_tree();
×
878
    }
×
879
    auto& pdom_tree = this->users_.pdom_tree_;
58✔
880
    for (auto& user : this->sub_users_) {
720✔
881
        this->sub_pdom_tree_[user] = pdom_tree[user];
662✔
882
    }
883
    this->sub_pdom_tree_[this->exit_] = nullptr;
58✔
884
};
58✔
885

886
std::vector<User*> UsersView::uses() const {
×
887
    std::vector<User*> us;
×
888
    for (auto& user : this->sub_users_) {
×
889
        if (user->use() == Use::NOP) {
×
890
            continue;
×
891
        }
892
        us.push_back(user);
×
893
    }
894

895
    return us;
×
896
};
×
897

898
std::vector<User*> UsersView::uses(const std::string& container) const {
8✔
899
    std::vector<User*> us;
8✔
900
    for (auto& user : this->sub_users_) {
46✔
901
        if (user->container() != container) {
38✔
902
            continue;
24✔
903
        }
904
        if (user->use() == Use::NOP) {
14✔
905
            continue;
×
906
        }
907
        us.push_back(user);
14✔
908
    }
909

910
    return us;
8✔
911
};
8✔
912

913
std::vector<User*> UsersView::writes() const {
50✔
914
    std::vector<User*> us;
50✔
915
    for (auto& user : this->sub_users_) {
700✔
916
        if (user->use() != Use::WRITE) {
650✔
917
            continue;
512✔
918
        }
919
        us.push_back(user);
138✔
920
    }
921

922
    return us;
50✔
923
};
50✔
924

925
std::vector<User*> UsersView::writes(const std::string& container) const {
82✔
926
    std::vector<User*> us;
82✔
927
    for (auto& user : this->sub_users_) {
2,772✔
928
        if (user->container() != container) {
2,690✔
929
            continue;
2,539✔
930
        }
931
        if (user->use() != Use::WRITE) {
151✔
932
            continue;
72✔
933
        }
934
        us.push_back(user);
79✔
935
    }
936

937
    return us;
82✔
938
};
82✔
939

940
std::vector<User*> UsersView::reads() const {
47✔
941
    std::vector<User*> us;
47✔
942
    for (auto& user : this->sub_users_) {
686✔
943
        if (user->use() != Use::READ) {
639✔
944
            continue;
136✔
945
        }
946
        us.push_back(user);
503✔
947
    }
948

949
    return us;
47✔
950
};
47✔
951

952
std::vector<User*> UsersView::reads(const std::string& container) const {
54✔
953
    std::vector<User*> us;
54✔
954
    for (auto& user : this->sub_users_) {
1,861✔
955
        if (user->container() != container) {
1,807✔
956
            continue;
1,699✔
957
        }
958
        if (user->use() != Use::READ) {
108✔
959
            continue;
58✔
960
        }
961
        us.push_back(user);
50✔
962
    }
963

964
    return us;
54✔
965
};
54✔
966

967
std::vector<User*> UsersView::views() const {
47✔
968
    std::vector<User*> us;
47✔
969
    for (auto& user : this->sub_users_) {
686✔
970
        if (user->use() != Use::VIEW) {
639✔
971
            continue;
639✔
972
        }
973
        us.push_back(user);
×
974
    }
975

976
    return us;
47✔
977
};
47✔
978

979
std::vector<User*> UsersView::views(const std::string& container) const {
×
980
    std::vector<User*> us;
×
981
    for (auto& user : this->sub_users_) {
×
982
        if (user->container() != container) {
×
983
            continue;
×
984
        }
985
        if (user->use() != Use::VIEW) {
×
986
            continue;
×
987
        }
988
        us.push_back(user);
×
989
    }
990

991
    return us;
×
992
};
×
993

994
std::vector<User*> UsersView::moves() const {
47✔
995
    std::vector<User*> us;
47✔
996
    for (auto& user : this->sub_users_) {
686✔
997
        if (user->use() != Use::MOVE) {
639✔
998
            continue;
639✔
999
        }
1000
        us.push_back(user);
×
1001
    }
1002

1003
    return us;
47✔
1004
};
47✔
1005

1006
std::vector<User*> UsersView::moves(const std::string& container) const {
×
1007
    std::vector<User*> us;
×
1008
    for (auto& user : this->sub_users_) {
×
1009
        if (user->container() != container) {
×
1010
            continue;
×
1011
        }
1012
        if (user->use() != Use::MOVE) {
×
1013
            continue;
×
1014
        }
1015
        us.push_back(user);
×
1016
    }
1017

1018
    return us;
×
1019
};
×
1020

1021
bool UsersView::dominates(User& user1, User& user) {
×
1022
    auto dominator = this->sub_dom_tree_[&user];
×
1023
    while (dominator != nullptr) {
×
1024
        if (dominator == &user1) {
×
1025
            return true;
×
1026
        }
1027
        dominator = this->sub_dom_tree_[dominator];
×
1028
    }
1029
    return false;
×
1030
};
×
1031

1032
bool UsersView::post_dominates(User& user1, User& user) {
×
1033
    auto dominator = this->sub_pdom_tree_[&user];
×
1034
    while (dominator != nullptr) {
×
1035
        if (dominator == &user1) {
×
1036
            return true;
×
1037
        }
1038
        dominator = this->sub_pdom_tree_[dominator];
×
1039
    }
1040
    return false;
×
1041
};
×
1042

1043
bool UsersView::is_dominated_by(User& user, Use use) {
×
1044
    auto dominator = this->sub_dom_tree_.at(&user);
×
1045
    while (dominator != nullptr) {
×
1046
        if (dominator->use() != use) {
×
1047
            dominator = this->sub_dom_tree_.at(dominator);
×
1048
            continue;
×
1049
        }
1050
        if (dominator->container() != user.container()) {
×
1051
            dominator = this->sub_dom_tree_.at(dominator);
×
1052
            continue;
×
1053
        }
1054

1055
        // Compare subsets
1056
        auto& subsets = user.subsets();
×
1057

1058
        // Collect all relevant symbols
1059
        std::unordered_set<std::string> symbols;
×
1060
        for (auto& subset : subsets) {
×
1061
            for (auto& dim : subset) {
×
1062
                auto atoms = symbolic::atoms(dim);
×
1063
                for (auto& atom : atoms) {
×
1064
                    symbols.insert(atom->get_name());
×
1065
                }
1066
            }
×
1067
        }
1068
        // If symbols are not constant, we cannot compare the subsets
1069
        if (!this->is_constant(symbols, *dominator, user)) {
×
1070
            dominator = this->sub_dom_tree_.at(dominator);
×
1071
            continue;
×
1072
        }
1073

1074
        // Now find for every subset of user, if there is a subset of dominator that is the same
1075
        auto& subsets_dominator = dominator->subsets();
×
1076
        bool all_subsets_dominated = true;
×
1077
        for (auto& subset : subsets) {
×
1078
            bool subset_dominated = false;
×
1079
            for (auto& subset_dominator : subsets_dominator) {
×
1080
                if (subset.size() != subset_dominator.size()) {
×
1081
                    continue;
×
1082
                }
1083
                bool dominated = true;
×
1084
                for (size_t i = 0; i < subset.size(); i++) {
×
1085
                    if (!symbolic::eq(subset[i], subset_dominator[i])) {
×
1086
                        dominated = false;
×
1087
                        break;
×
1088
                    }
1089
                }
×
1090
                if (dominated) {
×
1091
                    subset_dominated = true;
×
1092
                    break;
×
1093
                }
1094
            }
1095
            if (!subset_dominated) {
×
1096
                all_subsets_dominated = false;
×
1097
                break;
×
1098
            }
1099
        }
1100
        if (all_subsets_dominated) {
×
1101
            return true;
×
1102
        }
1103
        dominator = this->sub_dom_tree_.at(dominator);
×
1104
    }
×
1105
    return false;
×
1106
}
×
1107

1108
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
1109
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
1110
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
1111

1112
    std::unordered_set<User*> uses;
×
1113
    std::unordered_set<User*> visited;
×
1114
    std::list<User*> queue = {&user1};
×
1115
    while (!queue.empty()) {
×
1116
        auto current = queue.front();
×
1117
        queue.pop_front();
×
1118
        if (visited.find(current) != visited.end()) {
×
1119
            continue;
×
1120
        }
1121
        visited.insert(current);
×
1122

1123
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
1124
            uses.insert(current);
×
1125
        }
×
1126

1127
        // Stop conditions
1128
        if (current == exit_) {
×
1129
            continue;
×
1130
        }
1131

1132
        if (current == &user2) {
×
1133
            continue;
×
1134
        }
1135

1136
        // Extend search
1137
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1138
        auto edges = std::ranges::subrange(eb, ee);
×
1139
        for (auto edge : edges) {
×
1140
            auto v = boost::target(edge, this->users_.graph_);
×
1141
            queue.push_back(this->users_.users_.at(v).get());
×
1142
        }
1143
    }
1144

1145
    return uses;
×
1146
};
×
1147

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

1151
    std::unordered_set<User*> uses;
×
1152
    std::unordered_set<User*> visited;
×
1153
    std::list<User*> queue = {&user};
×
1154
    while (!queue.empty()) {
×
1155
        auto current = queue.front();
×
1156
        queue.pop_front();
×
1157
        if (visited.find(current) != visited.end()) {
×
1158
            continue;
×
1159
        }
1160
        visited.insert(current);
×
1161

1162
        if (current != &user && current->use() != Use::NOP) {
×
1163
            uses.insert(current);
×
1164
        }
×
1165

1166
        if (current == exit_) {
×
1167
            continue;
×
1168
        }
1169

1170
        // Extend search
1171
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1172
        auto edges = std::ranges::subrange(eb, ee);
×
1173
        for (auto edge : edges) {
×
1174
            auto v = boost::target(edge, this->users_.graph_);
×
1175
            queue.push_back(this->users_.users_.at(v).get());
×
1176
        }
1177
    }
1178

1179
    return uses;
×
1180
};
×
1181

1182
bool UsersView::is_constant(const std::unordered_set<std::string>& containers, User& user1,
×
1183
                            User& user2) {
1184
    for (auto& user : this->all_uses_between(user1, user2)) {
×
1185
        if (user->use() == Use::WRITE || user->use() == Use::MOVE) {
×
1186
            if (containers.find(user->container()) != containers.end()) {
×
1187
                return false;
×
1188
            }
1189
        }
×
1190
    }
1191
    return true;
×
1192
}
×
1193

1194
User* Users::get_user(const std::string& container, Element* element, Use use, bool is_init,
310✔
1195
                      bool is_condition, bool is_update) {
1196
    if (auto for_loop = dynamic_cast<structured_control_flow::For*>(element)) {
310✔
1197
        if (is_init) {
38✔
1198
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
10✔
1199
            return tmp;
10✔
1200
        } else if (is_condition) {
28✔
1201
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
10✔
1202
        } else if (is_update) {
18✔
1203
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
18✔
1204
        }
1205
    }
×
1206
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
272✔
1207
    return tmp;
272✔
1208
}
310✔
1209

1210
void Users::add_user(std::unique_ptr<User> user) {
1,536✔
1211
    auto vertex = user->vertex_;
1,536✔
1212
    this->users_.insert({vertex, std::move(user)});
1,536✔
1213

1214
    auto user_ptr = this->users_.at(vertex).get();
1,536✔
1215
    auto* target_structure = &users_by_sdfg_;
1,536✔
1216
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,536✔
1217
        auto for_loop = dynamic_cast<structured_control_flow::For*>(user_ptr->element());
414✔
1218
        if (for_loop == nullptr) {
414✔
1219
            throw std::invalid_argument("Invalid user type");
×
1220
        }
1221
        if (for_user->is_init()) {
414✔
1222
            target_structure = &users_by_sdfg_loop_init_;
93✔
1223
        } else if (for_user->is_condition()) {
414✔
1224
            target_structure = &users_by_sdfg_loop_condition_;
161✔
1225
        } else if (for_user->is_update()) {
321✔
1226
            target_structure = &users_by_sdfg_loop_update_;
160✔
1227
        } else {
160✔
1228
            throw std::invalid_argument("Invalid user type");
×
1229
        }
1230
    }
414✔
1231

1232
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,536✔
1233
        target_structure->insert({user_ptr->container(), {}});
791✔
1234
    }
791✔
1235
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
3,072✔
1236
        (*target_structure)[user_ptr->container()].end()) {
1,536✔
1237
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
1,447✔
1238
    }
1,447✔
1239
    target_structure->at(user_ptr->container())
1,536✔
1240
        .at(user_ptr->element())
1,536✔
1241
        .insert({user_ptr->use(), user_ptr});
1,536✔
1242
}
1,536✔
1243

1244
std::unordered_set<std::string> Users::locals(StructuredSDFG& sdfg,
48✔
1245
                                              structured_control_flow::ControlFlowNode& node) {
1246
    // Collect all node elements
1247
    Users local_users(sdfg, node);
48✔
1248
    analysis::AnalysisManager analysis_manager(sdfg_);
48✔
1249
    local_users.run(analysis_manager);
48✔
1250
    std::unordered_map<std::string, std::unordered_set<Element*>> elements;
48✔
1251
    for (auto& entry : local_users.users_) {
1,058✔
1252
        if (entry.second->use() == Use::NOP) {
1,010✔
1253
            continue;
370✔
1254
        }
1255
        if (!sdfg.is_transient(entry.second->container())) {
640✔
1256
            continue;
222✔
1257
        }
1258

1259
        if (elements.find(entry.second->container()) == elements.end()) {
418✔
1260
            elements[entry.second->container()] = {};
112✔
1261
        }
112✔
1262
        elements[entry.second->container()].insert(entry.second->element());
418✔
1263
    }
1264

1265
    // Determine locals
1266
    for (auto& entry : this->users_) {
1,716✔
1267
        if (entry.second->use() == Use::NOP) {
1,668✔
1268
            continue;
656✔
1269
        }
1270

1271
        auto& container = entry.second->container();
1,012✔
1272
        auto element = entry.second->element();
1,012✔
1273
        if (elements.find(container) == elements.end()) {
1,012✔
1274
            continue;
698✔
1275
        }
1276
        // used outside of node
1277
        if (elements[container].find(element) == elements[container].end()) {
314✔
1278
            elements.erase(container);
54✔
1279
        }
54✔
1280
    }
1281

1282
    std::unordered_set<std::string> locals;
48✔
1283
    for (auto& entry : elements) {
106✔
1284
        locals.insert(entry.first);
58✔
1285
    }
1286
    return locals;
48✔
1287
};
48✔
1288

1289
std::unordered_set<std::string> UsersView::locals(StructuredSDFG& sdfg,
×
1290
                                                  structured_control_flow::ControlFlowNode& node) {
1291
    // Collect all node elements
1292
    Users local_users(sdfg, node);
×
1293
    analysis::AnalysisManager analysis_manager(users_.sdfg_);
×
1294
    local_users.run(analysis_manager);
×
1295
    std::unordered_map<std::string, std::unordered_set<Element*>> elements;
×
1296
    for (auto& entry : local_users.users_) {
×
1297
        if (entry.second->use() == Use::NOP) {
×
1298
            continue;
×
1299
        }
1300
        if (!sdfg.is_transient(entry.second->container())) {
×
1301
            continue;
×
1302
        }
1303

1304
        if (elements.find(entry.second->container()) == elements.end()) {
×
1305
            elements[entry.second->container()] = {};
×
1306
        }
×
1307
        elements[entry.second->container()].insert(entry.second->element());
×
1308
    }
1309

1310
    // Determine locals
1311
    for (auto& entry : this->sub_users_) {
×
1312
        if (entry->use() == Use::NOP) {
×
1313
            continue;
×
1314
        }
1315

1316
        auto& container = entry->container();
×
1317
        auto element = entry->element();
×
1318
        if (elements.find(container) == elements.end()) {
×
1319
            continue;
×
1320
        }
1321
        // used outside of node
1322
        if (elements[container].find(element) == elements[container].end()) {
×
1323
            elements.erase(container);
×
1324
        }
×
1325
    }
1326

1327
    std::unordered_set<std::string> locals;
×
1328
    for (auto& entry : elements) {
×
1329
        locals.insert(entry.first);
×
1330
    }
1331
    return locals;
×
1332
};
×
1333

1334
}  // namespace analysis
1335
}  // 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