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

daisytuner / sdfglib / 15612270835

12 Jun 2025 01:44PM UTC coverage: 60.871% (-0.8%) from 61.71%
15612270835

push

github

web-flow
Merge pull request #68 from daisytuner/loop-types

refactors symbolic analysis into polynomials, extreme values and cnf

638 of 862 new or added lines in 24 files covered. (74.01%)

334 existing lines in 20 files now uncovered.

6571 of 10795 relevant lines covered (60.87%)

100.35 hits per line

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

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

26
      };
1,518✔
27

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

32
      };
919✔
33

34
User::~User() {
4,624✔
35

36
};
4,624✔
37

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

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

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

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

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

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

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

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

79
      };
250✔
80

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

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

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

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

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

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

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

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

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

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

153
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
231✔
154
                        boost::add_edge(last, v, this->graph_);
62✔
155
                    } else {
62✔
156
                        first = v;
169✔
157
                    }
158
                    last = v;
231✔
159
                }
231✔
160
            }
422✔
161
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
618✔
162
            if (tasklet->is_conditional()) {
196✔
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
        }
196✔
177

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

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

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

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

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

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

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

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

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

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

243
            std::unordered_set<std::string> used;
349✔
244
            for (auto& entry : child.second.assignments()) {
414✔
245
                for (auto atom : symbolic::atoms(entry.second)) {
92✔
246
                    if (symbolic::is_pointer(atom)) {
27✔
UNCOV
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()) {
414✔
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
        }
349✔
272

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

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

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

292
        std::unordered_set<std::string> used;
19✔
293
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
53✔
294
            auto& condition = if_else_stmt->at(i).second;
34✔
295
            for (auto atom : symbolic::atoms(condition)) {
68✔
296
                if (used.find(atom->get_name()) != used.end()) {
34✔
297
                    continue;
13✔
298
                }
299
                used.insert(atom->get_name());
21✔
300

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

423
        return {s, t};
51✔
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
};
582✔
474

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

478
      };
102✔
479

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

483
      };
35✔
484

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

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

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

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

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

544
    // Collect sub structures
545
    for (auto& entry : this->users_) {
2,574✔
546
        auto container = entry.second->container();
2,437✔
547
        switch (entry.second->use()) {
2,437✔
548
            case Use::READ: {
549
                if (this->reads_.find(container) == this->reads_.end()) {
915✔
550
                    this->reads_.insert({container, {}});
398✔
551
                }
398✔
552
                this->reads_[container].push_back(entry.second.get());
915✔
553
                break;
915✔
554
            }
555
            case Use::WRITE: {
556
                if (this->writes_.find(container) == this->writes_.end()) {
364✔
557
                    this->writes_.insert({container, {}});
259✔
558
                }
259✔
559
                this->writes_[container].push_back(entry.second.get());
364✔
560
                break;
364✔
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,154✔
578
        }
579
    }
2,437✔
580
};
137✔
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✔
NEW
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) {
61✔
785
    std::unordered_set<User*> uses;
61✔
786
    std::unordered_set<User*> visited;
61✔
787
    std::list<User*> queue = {&user2};
61✔
788
    while (!queue.empty()) {
980✔
789
        auto current = queue.front();
919✔
790
        queue.pop_front();
919✔
791

792
        // Stop conditions
793
        if (current == &user1) {
919✔
794
            continue;
61✔
795
        }
796

797
        if (visited.find(current) != visited.end()) {
858✔
798
            continue;
26✔
799
        }
800
        visited.insert(current);
832✔
801

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

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

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

NEW
819
const std::unordered_set<User*> Users::all_uses_after(User& user) {
×
820
    std::unordered_set<User*> uses;
×
821
    std::unordered_set<User*> visited;
×
NEW
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

NEW
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)) {
16✔
850
        if (user->use() == Use::WRITE || user->use() == Use::MOVE) {
7✔
851
            if (containers.find(user->container()) != containers.end()) {
2✔
852
                return false;
1✔
853
            }
854
        }
1✔
855
    }
856
    return true;
8✔
857
}
9✔
858

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

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

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

876
    if (this->users_.pdom_tree_.empty()) {
38✔
NEW
877
        this->users_.init_dom_tree();
×
UNCOV
878
    }
×
879
    auto& pdom_tree = this->users_.pdom_tree_;
38✔
880
    for (auto& user : this->sub_users_) {
549✔
881
        this->sub_pdom_tree_[user] = pdom_tree[user];
511✔
882
    }
883
    this->sub_pdom_tree_[this->exit_] = nullptr;
38✔
884
};
38✔
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

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

UNCOV
910
    return us;
×
UNCOV
911
};
×
912

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

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

925
std::vector<User*> UsersView::writes(const std::string& container) const {
67✔
926
    std::vector<User*> us;
67✔
927
    for (auto& user : this->sub_users_) {
2,565✔
928
        if (user->container() != container) {
2,498✔
929
            continue;
2,373✔
930
        }
931
        if (user->use() != Use::WRITE) {
125✔
932
            continue;
59✔
933
        }
934
        us.push_back(user);
66✔
935
    }
936

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

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

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

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

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

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

976
    return us;
35✔
977
};
35✔
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 {
35✔
995
    std::vector<User*> us;
35✔
996
    for (auto& user : this->sub_users_) {
546✔
997
        if (user->use() != Use::MOVE) {
511✔
998
            continue;
511✔
999
        }
1000
        us.push_back(user);
×
1001
    }
1002

1003
    return us;
35✔
1004
};
35✔
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) {
×
NEW
1022
    auto dominator = this->sub_dom_tree_[&user];
×
1023
    while (dominator != nullptr) {
×
1024
        if (dominator == &user1) {
×
1025
            return true;
×
1026
        }
NEW
1027
        dominator = this->sub_dom_tree_[dominator];
×
1028
    }
1029
    return false;
×
1030
};
×
1031

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

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

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

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

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

NEW
1108
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
1109
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
NEW
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

NEW
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

NEW
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

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

1151
    std::unordered_set<User*> uses;
×
1152
    std::unordered_set<User*> visited;
×
NEW
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

NEW
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

NEW
1182
bool UsersView::is_constant(const std::unordered_set<std::string>& containers, User& user1,
×
1183
                            User& user2) {
NEW
1184
    for (auto& user : this->all_uses_between(user1, user2)) {
×
NEW
1185
        if (user->use() == Use::WRITE || user->use() == Use::MOVE) {
×
NEW
1186
            if (containers.find(user->container()) != containers.end()) {
×
NEW
1187
                return false;
×
1188
            }
UNCOV
1189
        }
×
1190
    }
NEW
1191
    return true;
×
NEW
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
        }
NEW
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,283✔
1211
    auto vertex = user->vertex_;
1,283✔
1212
    this->users_.insert({vertex, std::move(user)});
1,283✔
1213

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

1232
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,283✔
1233
        target_structure->insert({user_ptr->container(), {}});
620✔
1234
    }
620✔
1235
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
2,566✔
1236
        (*target_structure)[user_ptr->container()].end()) {
1,283✔
1237
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
1,223✔
1238
    }
1,223✔
1239
    target_structure->at(user_ptr->container())
1,283✔
1240
        .at(user_ptr->element())
1,283✔
1241
        .insert({user_ptr->use(), user_ptr});
1,283✔
1242
}
1,283✔
1243

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

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

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

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

1282
    std::unordered_set<std::string> locals;
35✔
1283
    for (auto& entry : elements) {
73✔
1284
        locals.insert(entry.first);
38✔
1285
    }
1286
    return locals;
35✔
1287
};
35✔
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