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

daisytuner / sdfglib / 15898736035

26 Jun 2025 09:54AM UTC coverage: 65.01% (-0.1%) from 65.152%
15898736035

push

github

web-flow
Merge pull request #108 from daisytuner/library-node-symbols

Library node symbols

3 of 32 new or added lines in 4 files covered. (9.38%)

4 existing lines in 3 files now uncovered.

8487 of 13055 relevant lines covered (65.01%)

139.12 hits per line

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

73.73
/src/analysis/users.cpp
1
#include "sdfg/analysis/users.h"
2

3
#include <cassert>
4
#include <list>
5
#include <memory>
6
#include <string>
7
#include <unordered_map>
8
#include <unordered_set>
9
#include <vector>
10

11
#include "sdfg/data_flow/memlet.h"
12
#include "sdfg/element.h"
13
#include "sdfg/graph/graph.h"
14
#include "sdfg/structured_control_flow/for.h"
15
#include "sdfg/structured_control_flow/map.h"
16
#include "sdfg/structured_control_flow/sequence.h"
17
#include "sdfg/structured_sdfg.h"
18
#include "sdfg/symbolic/sets.h"
19
#include "sdfg/symbolic/symbolic.h"
20

21
namespace sdfg {
22
namespace analysis {
23

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

27
      };
2,009✔
28

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

33
      };
592✔
34

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

37
};
4,696✔
38

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

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

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

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

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

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

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

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

80
      };
506✔
81

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

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

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

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

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

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

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

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

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

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

154
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
143✔
155
                        boost::add_edge(last, v, this->graph_);
38✔
156
                    } else {
38✔
157
                        first = v;
105✔
158
                    }
159
                    last = v;
143✔
160
                }
143✔
161
            }
290✔
162
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
439✔
163
            if (tasklet->is_conditional()) {
149✔
164
                auto& condition = tasklet->condition();
5✔
165
                for (auto& atom : symbolic::atoms(condition)) {
10✔
166
                    auto v = boost::add_vertex(this->graph_);
5✔
167
                    this->add_user(
5✔
168
                        std::make_unique<User>(v, atom->get_name(), tasklet, &dataflow, Use::READ));
5✔
169
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
5✔
170
                        boost::add_edge(last, v, this->graph_);
3✔
171
                    } else {
3✔
172
                        first = v;
2✔
173
                    }
174
                    last = v;
5✔
175
                }
176
            }
5✔
177
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
149✔
NEW
178
            for (auto& symbol : library_node->symbols()) {
×
NEW
179
                auto v = boost::add_vertex(this->graph_);
×
NEW
180
                this->add_user(std::make_unique<User>(v, symbol->get_name(), library_node,
×
NEW
181
                                                      &dataflow, Use::READ));
×
NEW
182
                if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
×
NEW
183
                    boost::add_edge(last, v, this->graph_);
×
NEW
184
                } else {
×
NEW
185
                    first = v;
×
186
                }
NEW
187
                last = v;
×
188
            }
UNCOV
189
        }
×
190

191
        for (auto& oedge : dataflow.out_edges(*node)) {
736✔
192
            std::unordered_set<std::string> used;
297✔
193
            for (auto dim : oedge.subset()) {
575✔
194
                for (auto atom : symbolic::atoms(dim)) {
571✔
195
                    if (used.find(atom->get_name()) != used.end()) {
293✔
196
                        continue;
×
197
                    }
198
                    used.insert(atom->get_name());
293✔
199

200
                    auto v = boost::add_vertex(this->graph_);
293✔
201
                    this->add_user(
293✔
202
                        std::make_unique<User>(v, atom->get_name(), &oedge, &dataflow, Use::READ));
293✔
203
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
293✔
204
                        boost::add_edge(last, v, this->graph_);
281✔
205
                    } else {
281✔
206
                        first = v;
12✔
207
                    }
208
                    last = v;
293✔
209
                }
293✔
210
            }
278✔
211
        }
297✔
212
    }
213

214
    return {first, last};
234✔
215
};
×
216

217
std::pair<graph::Vertex, graph::Vertex> Users::traverse(
690✔
218
    structured_control_flow::ControlFlowNode& node) {
219
    if (auto block_stmt = dynamic_cast<structured_control_flow::Block*>(&node)) {
690✔
220
        // NOP
221
        auto s = boost::add_vertex(this->graph_);
234✔
222
        this->users_.insert({s, std::make_unique<User>(s, "", block_stmt, Use::NOP)});
234✔
223
        this->entries_.insert({block_stmt, this->users_.at(s).get()});
234✔
224

225
        // NOP
226
        auto t = boost::add_vertex(this->graph_);
234✔
227
        this->users_.insert({t, std::make_unique<User>(t, "", block_stmt, Use::NOP)});
234✔
228
        this->exits_.insert({block_stmt, this->users_.at(t).get()});
234✔
229

230
        auto& dataflow = block_stmt->dataflow();
234✔
231
        auto subgraph = this->traverse(dataflow);
234✔
232

233
        // May be empty
234
        if (subgraph.first == boost::graph_traits<graph::Graph>::null_vertex()) {
234✔
235
            boost::add_edge(s, t, this->graph_);
91✔
236
            return {s, t};
91✔
237
        }
238

239
        boost::add_edge(s, subgraph.first, this->graph_);
143✔
240
        boost::add_edge(subgraph.second, t, this->graph_);
143✔
241

242
        return {s, t};
143✔
243
    } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(&node)) {
456✔
244
        auto s = boost::add_vertex(this->graph_);
311✔
245
        this->users_.insert({s, std::make_unique<User>(s, "", sequence_stmt, Use::NOP)});
311✔
246
        this->entries_.insert({sequence_stmt, this->users_.at(s).get()});
311✔
247

248
        graph::Vertex current = s;
311✔
249
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
694✔
250
            auto child = sequence_stmt->at(i);
383✔
251

252
            auto subgraph = this->traverse(child.first);
383✔
253
            boost::add_edge(current, subgraph.first, this->graph_);
383✔
254
            current = subgraph.second;
383✔
255

256
            std::unordered_set<std::string> used;
383✔
257
            for (auto& entry : child.second.assignments()) {
463✔
258
                for (auto atom : symbolic::atoms(entry.second)) {
106✔
259
                    if (symbolic::is_pointer(atom)) {
26✔
260
                        continue;
×
261
                    }
262
                    if (used.find(atom->get_name()) != used.end()) {
26✔
263
                        continue;
×
264
                    }
265
                    used.insert(atom->get_name());
26✔
266

267
                    auto v = boost::add_vertex(this->graph_);
26✔
268
                    this->add_user(
26✔
269
                        std::make_unique<User>(v, atom->get_name(), &child.second, Use::READ));
26✔
270

271
                    boost::add_edge(current, v, this->graph_);
26✔
272
                    current = v;
26✔
273
                }
26✔
274
            }
275

276
            for (auto& entry : child.second.assignments()) {
463✔
277
                auto v = boost::add_vertex(this->graph_);
80✔
278
                this->add_user(
80✔
279
                    std::make_unique<User>(v, entry.first->get_name(), &child.second, Use::WRITE));
80✔
280

281
                boost::add_edge(current, v, this->graph_);
80✔
282
                current = v;
80✔
283
            }
284
        }
383✔
285

286
        if (current == boost::graph_traits<graph::Graph>::null_vertex()) {
311✔
287
            this->exits_.insert({sequence_stmt, this->users_.at(s).get()});
×
288
            return {s, current};
×
289
        }
290

291
        auto t = boost::add_vertex(this->graph_);
311✔
292
        this->users_.insert({t, std::make_unique<User>(t, "", sequence_stmt, Use::NOP)});
311✔
293
        boost::add_edge(current, t, this->graph_);
311✔
294
        this->exits_.insert({sequence_stmt, this->users_.at(t).get()});
311✔
295

296
        return {s, t};
311✔
297
    } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(&node)) {
145✔
298
        // NOP
299
        auto s = boost::add_vertex(this->graph_);
27✔
300
        this->users_.insert({s, std::make_unique<User>(s, "", if_else_stmt, Use::NOP)});
27✔
301
        this->entries_.insert({if_else_stmt, this->users_.at(s).get()});
27✔
302

303
        graph::Vertex last = s;
27✔
304

305
        std::unordered_set<std::string> used;
27✔
306
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
73✔
307
            auto& condition = if_else_stmt->at(i).second;
46✔
308
            for (auto atom : symbolic::atoms(condition)) {
85✔
309
                if (used.find(atom->get_name()) != used.end()) {
39✔
310
                    continue;
14✔
311
                }
312
                used.insert(atom->get_name());
25✔
313

314
                auto v = boost::add_vertex(this->graph_);
25✔
315

316
                this->add_user(
25✔
317
                    std::make_unique<User>(v, atom->get_name(), if_else_stmt, Use::READ));
25✔
318

319
                boost::add_edge(last, v, this->graph_);
25✔
320
                last = v;
25✔
321
            }
39✔
322
        }
46✔
323

324
        // NOP
325
        auto t = boost::add_vertex(this->graph_);
27✔
326
        this->users_.insert({t, std::make_unique<User>(t, "", if_else_stmt, Use::NOP)});
27✔
327
        this->exits_.insert({if_else_stmt, this->users_.at(t).get()});
27✔
328

329
        // Forward edge: Potentially missing else case
330
        if (!if_else_stmt->is_complete()) {
27✔
331
            boost::add_edge(last, t, this->graph_);
8✔
332
        }
8✔
333

334
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
73✔
335
            auto branch = if_else_stmt->at(i);
46✔
336
            auto subgraph = this->traverse(branch.first);
46✔
337
            boost::add_edge(last, subgraph.first, this->graph_);
46✔
338
            if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
46✔
339
                boost::add_edge(subgraph.second, t, this->graph_);
46✔
340
            }
46✔
341
        }
46✔
342

343
        return {s, t};
27✔
344
    } else if (auto loop_stmt = dynamic_cast<structured_control_flow::While*>(&node)) {
145✔
345
        // NOP
346
        auto s = boost::add_vertex(this->graph_);
7✔
347
        this->users_.insert({s, std::make_unique<User>(s, "", loop_stmt, Use::NOP)});
7✔
348
        this->entries_.insert({loop_stmt, this->users_.at(s).get()});
7✔
349

350
        auto subgraph = this->traverse(loop_stmt->root());
7✔
351

352
        // NOP
353
        auto t = boost::add_vertex(this->graph_);
7✔
354
        this->users_.insert({t, std::make_unique<User>(t, "", loop_stmt, Use::NOP)});
7✔
355
        this->exits_.insert({loop_stmt, this->users_.at(t).get()});
7✔
356

357
        boost::add_edge(s, subgraph.first, this->graph_);
7✔
358
        if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
7✔
359
            boost::add_edge(subgraph.second, t, this->graph_);
7✔
360
        }
7✔
361

362
        // Empty loop
363
        boost::add_edge(s, t, this->graph_);
7✔
364
        // Back edge
365
        boost::add_edge(t, s, this->graph_);
7✔
366

367
        return {s, t};
7✔
368
    } else if (auto for_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(&node)) {
111✔
369
        // NOP
370
        auto s = boost::add_vertex(this->graph_);
103✔
371
        this->users_.insert({s, std::make_unique<User>(s, "", for_stmt, Use::NOP)});
103✔
372
        auto last = s;
103✔
373
        this->entries_.insert({for_stmt, this->users_.at(s).get()});
103✔
374

375
        // NOP
376
        auto t = boost::add_vertex(this->graph_);
103✔
377
        this->users_.insert({t, std::make_unique<User>(t, "", for_stmt, Use::NOP)});
103✔
378
        this->exits_.insert({for_stmt, this->users_.at(t).get()});
103✔
379

380
        // Init
381
        for (auto atom : symbolic::atoms(for_stmt->init())) {
110✔
382
            auto v = boost::add_vertex(this->graph_);
7✔
383
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ, true,
7✔
384
                                                     false, false));
7✔
385
            boost::add_edge(last, v, this->graph_);
7✔
386
            last = v;
7✔
387
        }
7✔
388
        // Indvar
389
        auto v = boost::add_vertex(this->graph_);
103✔
390
        this->add_user(std::make_unique<ForUser>(v, for_stmt->indvar()->get_name(), for_stmt,
206✔
391
                                                 Use::WRITE, true, false, false));
103✔
392

393
        boost::add_edge(last, v, this->graph_);
103✔
394
        last = v;
103✔
395

396
        // Condition
397
        for (auto atom : symbolic::atoms(for_stmt->condition())) {
293✔
398
            auto v = boost::add_vertex(this->graph_);
190✔
399
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ,
190✔
400
                                                     false, true, false));
190✔
401

402
            boost::add_edge(last, v, this->graph_);
190✔
403
            boost::add_edge(v, t, this->graph_);
190✔
404
            last = v;
190✔
405
        }
190✔
406

407
        auto subgraph = this->traverse(for_stmt->root());
103✔
408
        boost::add_edge(last, subgraph.first, this->graph_);
103✔
409

410
        // Update
411
        auto end = subgraph.second;
103✔
412
        for (auto atom : symbolic::atoms(for_stmt->update())) {
206✔
413
            auto v = boost::add_vertex(this->graph_);
103✔
414
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ,
103✔
415
                                                     false, false, true));
103✔
416
            boost::add_edge(end, v, this->graph_);
103✔
417
            end = v;
103✔
418
        }
103✔
419

420
        auto update_v = boost::add_vertex(this->graph_);
103✔
421
        this->add_user(std::make_unique<ForUser>(update_v, for_stmt->indvar()->get_name(), for_stmt,
206✔
422
                                                 Use::WRITE, false, false, true));
103✔
423

424
        if (end != boost::graph_traits<graph::Graph>::null_vertex()) {
103✔
425
            boost::add_edge(end, update_v, this->graph_);
103✔
426
        }
103✔
427
        end = update_v;
103✔
428

429
        if (end != boost::graph_traits<graph::Graph>::null_vertex()) {
103✔
430
            boost::add_edge(end, t, this->graph_);
103✔
431
        }
103✔
432

433
        // Back edge
434
        boost::add_edge(t, last, this->graph_);
103✔
435

436
        return {s, t};
103✔
437
    } else if (auto cont_stmt = dynamic_cast<structured_control_flow::Continue*>(&node)) {
8✔
438
        // Approximated by general back edge in loop scope
439
        auto v = boost::add_vertex(this->graph_);
4✔
440
        this->users_.insert({v, std::make_unique<User>(v, "", cont_stmt, Use::NOP)});
4✔
441
        this->entries_.insert({cont_stmt, this->users_.at(v).get()});
4✔
442
        this->exits_.insert({cont_stmt, this->users_.at(v).get()});
4✔
443
        return {v, v};
4✔
444
    } else if (auto br_stmt = dynamic_cast<structured_control_flow::Break*>(&node)) {
4✔
445
        // Approximated by general back edge in loop scope
446
        auto v = boost::add_vertex(this->graph_);
4✔
447
        this->users_.insert({v, std::make_unique<User>(v, "", br_stmt, Use::NOP)});
4✔
448
        this->entries_.insert({br_stmt, this->users_.at(v).get()});
4✔
449
        this->exits_.insert({br_stmt, this->users_.at(v).get()});
4✔
450
        return {v, v};
4✔
451
    } else if (auto ret_stmt = dynamic_cast<structured_control_flow::Return*>(&node)) {
×
452
        auto v = boost::add_vertex(this->graph_);
×
453
        this->users_.insert({v, std::make_unique<User>(v, "", ret_stmt, Use::NOP)});
×
454
        this->entries_.insert({ret_stmt, this->users_.at(v).get()});
×
455
        this->exits_.insert({ret_stmt, this->users_.at(v).get()});
×
456
        return {v, boost::graph_traits<graph::Graph>::null_vertex()};
×
457
    }
458

459
    throw std::invalid_argument("Invalid control flow node type");
×
460
};
690✔
461

462
Users::Users(StructuredSDFG& sdfg)
151✔
463
    : Analysis(sdfg), node_(sdfg.root()) {
151✔
464

465
      };
151✔
466

467
Users::Users(StructuredSDFG& sdfg, structured_control_flow::ControlFlowNode& node)
×
468
    : Analysis(sdfg), node_(node) {
×
469

470
      };
×
471

472
void Users::run(analysis::AnalysisManager& analysis_manager) {
151✔
473
    users_.clear();
151✔
474
    graph_.clear();
151✔
475
    source_ = nullptr;
151✔
476
    sink_ = nullptr;
151✔
477
    dom_tree_.clear();
151✔
478
    pdom_tree_.clear();
151✔
479
    users_by_sdfg_.clear();
151✔
480
    users_by_sdfg_loop_condition_.clear();
151✔
481
    users_by_sdfg_loop_init_.clear();
151✔
482
    users_by_sdfg_loop_update_.clear();
151✔
483

484
    reads_.clear();
151✔
485
    writes_.clear();
151✔
486
    views_.clear();
151✔
487
    moves_.clear();
151✔
488

489
    this->traverse(node_);
151✔
490
    if (this->users_.empty()) {
151✔
491
        return;
×
492
    }
493

494
    // Require a single source
495
    for (auto& entry : this->users_) {
2,752✔
496
        if (boost::in_degree(entry.first, this->graph_) == 0) {
2,601✔
497
            if (this->source_ != nullptr) {
151✔
498
                throw InvalidSDFGException("Users Analysis: Non-unique source node");
×
499
            }
500
            this->source_ = entry.second.get();
151✔
501
        }
151✔
502
    }
503
    if (this->source_ == nullptr) {
151✔
504
        throw InvalidSDFGException("Users Analysis: No source node");
×
505
    }
506

507
    std::list<User*> sinks;
151✔
508
    for (auto& entry : this->users_) {
2,752✔
509
        if (boost::out_degree(entry.first, this->graph_) == 0) {
2,601✔
510
            sinks.push_back(entry.second.get());
151✔
511
        }
151✔
512
    }
513
    if (sinks.size() == 0) {
151✔
514
        throw InvalidSDFGException("Users Analysis: No sink node");
×
515
    }
516
    if (sinks.size() > 1) {
151✔
517
        // add artificial sink
518
        auto v = boost::add_vertex(this->graph_);
×
519
        auto sink = std::make_unique<User>(v, "", nullptr, Use::NOP);
×
520
        this->users_.insert({v, std::move(sink)});
×
521
        for (auto sink : sinks) {
×
522
            boost::add_edge(sink->vertex_, v, this->graph_);
×
523
        }
524
        sinks.clear();
×
525

526
        this->sink_ = this->users_.at(v).get();
×
527
    } else {
×
528
        this->sink_ = sinks.front();
151✔
529
    }
530

531
    // Collect sub structures
532
    for (auto& entry : this->users_) {
2,752✔
533
        auto container = entry.second->container();
2,601✔
534
        switch (entry.second->use()) {
2,601✔
535
            case Use::READ: {
536
                if (this->reads_.find(container) == this->reads_.end()) {
791✔
537
                    this->reads_.insert({container, {}});
370✔
538
                }
370✔
539
                this->reads_[container].push_back(entry.second.get());
791✔
540
                break;
791✔
541
            }
542
            case Use::WRITE: {
543
                if (this->writes_.find(container) == this->writes_.end()) {
436✔
544
                    this->writes_.insert({container, {}});
286✔
545
                }
286✔
546
                this->writes_[container].push_back(entry.second.get());
436✔
547
                break;
436✔
548
            }
549
            case Use::VIEW: {
550
                if (this->views_.find(container) == this->views_.end()) {
1✔
551
                    this->views_.insert({container, {}});
1✔
552
                }
1✔
553
                this->views_[container].push_back(entry.second.get());
1✔
554
                break;
1✔
555
            }
556
            case Use::MOVE: {
557
                if (this->moves_.find(container) == this->moves_.end()) {
1✔
558
                    this->moves_.insert({container, {}});
1✔
559
                }
1✔
560
                this->moves_[container].push_back(entry.second.get());
1✔
561
                break;
1✔
562
            }
563
            default:
564
                break;
1,372✔
565
        }
566
    }
2,601✔
567

568
    this->init_dom_tree();
151✔
569
};
151✔
570

571
std::vector<User*> Users::uses() const {
×
572
    std::vector<User*> us;
×
573
    for (auto& entry : this->users_) {
×
574
        if (entry.second->use() == Use::NOP) {
×
575
            continue;
×
576
        }
577
        us.push_back(entry.second.get());
×
578
    }
579

580
    return us;
×
581
};
×
582

583
std::vector<User*> Users::uses(const std::string& container) const {
352✔
584
    std::vector<User*> us;
352✔
585
    for (auto& entry : this->users_) {
41,950✔
586
        if (entry.second->container() != container) {
41,598✔
587
            continue;
37,000✔
588
        }
589
        if (entry.second->use() == Use::NOP) {
4,598✔
590
            continue;
×
591
        }
592
        us.push_back(entry.second.get());
4,598✔
593
    }
594

595
    return us;
352✔
596
};
352✔
597

598
std::vector<User*> Users::writes() const {
×
599
    std::vector<User*> us;
×
600
    for (auto& entry : this->users_) {
×
601
        if (entry.second->use() != Use::WRITE) {
×
602
            continue;
×
603
        }
604
        us.push_back(entry.second.get());
×
605
    }
606

607
    return us;
×
608
};
×
609

610
std::vector<User*> Users::writes(const std::string& container) const {
55✔
611
    if (this->writes_.find(container) == this->writes_.end()) {
55✔
612
        return {};
27✔
613
    } else {
614
        return this->writes_.at(container);
28✔
615
    }
616
};
55✔
617

618
std::vector<User*> Users::reads() const {
×
619
    std::vector<User*> us;
×
620
    for (auto& entry : this->users_) {
×
621
        if (entry.second->use() != Use::READ) {
×
622
            continue;
×
623
        }
624
        us.push_back(entry.second.get());
×
625
    }
626

627
    return us;
×
628
};
×
629

630
std::vector<User*> Users::reads(const std::string& container) const {
40✔
631
    if (this->reads_.find(container) == this->reads_.end()) {
40✔
632
        return {};
17✔
633
    } else {
634
        return this->reads_.at(container);
23✔
635
    }
636
};
40✔
637

638
std::vector<User*> Users::views() const {
×
639
    std::vector<User*> us;
×
640
    for (auto& entry : this->users_) {
×
641
        if (entry.second->use() != Use::VIEW) {
×
642
            continue;
×
643
        }
644
        us.push_back(entry.second.get());
×
645
    }
646

647
    return us;
×
648
};
×
649

650
std::vector<User*> Users::views(const std::string& container) const {
60✔
651
    if (this->views_.find(container) == this->views_.end()) {
60✔
652
        return {};
60✔
653
    } else {
654
        return this->views_.at(container);
×
655
    }
656
};
60✔
657

658
std::vector<User*> Users::moves() const {
×
659
    std::vector<User*> us;
×
660
    for (auto& entry : this->users_) {
×
661
        if (entry.second->use() != Use::MOVE) {
×
662
            continue;
×
663
        }
664
        us.push_back(entry.second.get());
×
665
    }
666

667
    return us;
×
668
};
×
669

670
std::vector<User*> Users::moves(const std::string& container) const {
60✔
671
    if (this->moves_.find(container) == this->moves_.end()) {
60✔
672
        return {};
59✔
673
    } else {
674
        return this->moves_.at(container);
1✔
675
    }
676
};
60✔
677

678
structured_control_flow::ControlFlowNode* Users::scope(User* user) {
164✔
679
    if (auto data_node = dynamic_cast<data_flow::DataFlowNode*>(user->element())) {
164✔
680
        return static_cast<structured_control_flow::Block*>(data_node->get_parent().get_parent());
164✔
681
    } else if (auto memlet = dynamic_cast<data_flow::Memlet*>(user->element())) {
×
682
        return static_cast<structured_control_flow::Block*>(memlet->get_parent().get_parent());
×
683
    } else if (auto transition =
×
684
                   dynamic_cast<structured_control_flow::Transition*>(user->element())) {
×
685
        return &transition->parent();
×
686
    } else {
NEW
687
        auto user_element =
×
NEW
688
            dynamic_cast<structured_control_flow::ControlFlowNode*>(user->element());
×
NEW
689
        assert(user_element != nullptr && "Users::scope: User element is not a ControlFlowNode");
×
UNCOV
690
        return user_element;
×
691
    }
692
}
164✔
693

694
/****** Domination Analysis ******/
695

696
bool Users::dominates(User& user1, User& user) {
32✔
697
    auto dominator = this->dom_tree_.at(&user);
32✔
698
    while (dominator != nullptr) {
109✔
699
        if (dominator == &user1) {
103✔
700
            return true;
26✔
701
        }
702
        dominator = this->dom_tree_.at(dominator);
77✔
703
    }
704
    return false;
6✔
705
};
32✔
706

707
bool Users::post_dominates(User& user1, User& user) {
12✔
708
    auto dominator = this->pdom_tree_.at(&user);
12✔
709
    while (dominator != nullptr) {
29✔
710
        if (dominator == &user1) {
23✔
711
            return true;
6✔
712
        }
713
        dominator = this->pdom_tree_.at(dominator);
17✔
714
    }
715
    return false;
6✔
716
};
12✔
717

718
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
14✔
719
    std::unordered_set<User*> uses;
14✔
720
    std::unordered_set<User*> visited;
14✔
721
    std::list<User*> queue = {&user2};
14✔
722
    while (!queue.empty()) {
90✔
723
        auto current = queue.front();
76✔
724
        queue.pop_front();
76✔
725

726
        // Stop conditions
727
        if (current == &user1) {
76✔
728
            continue;
14✔
729
        }
730

731
        if (visited.find(current) != visited.end()) {
62✔
732
            continue;
2✔
733
        }
734
        visited.insert(current);
60✔
735

736
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
60✔
737
            uses.insert(current);
9✔
738
        }
9✔
739

740
        // Extend search
741
        // Backward search to utilize domination user1 over user
742
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
60✔
743
        auto edges = std::ranges::subrange(eb, ee);
120✔
744
        for (auto edge : edges) {
122✔
745
            auto v = boost::source(edge, this->graph_);
62✔
746
            queue.push_back(this->users_.at(v).get());
62✔
747
        }
748
    }
749

750
    return uses;
14✔
751
};
14✔
752

753
const std::unordered_set<User*> Users::all_uses_after(User& user) {
×
754
    std::unordered_set<User*> uses;
×
755
    std::unordered_set<User*> visited;
×
756
    std::list<User*> queue = {&user};
×
757
    while (!queue.empty()) {
×
758
        auto current = queue.front();
×
759
        queue.pop_front();
×
760
        if (visited.find(current) != visited.end()) {
×
761
            continue;
×
762
        }
763
        visited.insert(current);
×
764

765
        if (current != &user && current->use() != Use::NOP) {
×
766
            uses.insert(current);
×
767
        }
×
768

769
        // Extend search
770
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
771
        auto edges = std::ranges::subrange(eb, ee);
×
772
        for (auto edge : edges) {
×
773
            auto v = boost::target(edge, this->graph_);
×
774
            queue.push_back(this->users_.at(v).get());
×
775
        }
776
    }
777

778
    return uses;
×
779
};
×
780

781
UsersView::UsersView(Users& users, structured_control_flow::ControlFlowNode& node) : users_(users) {
145✔
782
    this->entry_ = users.entries_.at(&node);
145✔
783
    this->exit_ = users.exits_.at(&node);
145✔
784

785
    // Collect sub users
786
    std::unordered_set<User*> visited;
145✔
787
    std::list<User*> queue = {this->exit_};
145✔
788
    while (!queue.empty()) {
2,202✔
789
        auto current = queue.front();
2,057✔
790
        queue.pop_front();
2,057✔
791

792
        // Stop conditions
793
        if (current == this->entry_) {
2,057✔
794
            continue;
145✔
795
        }
796

797
        if (visited.find(current) != visited.end()) {
1,912✔
798
            continue;
60✔
799
        }
800
        visited.insert(current);
1,852✔
801

802
        this->sub_users_.insert(current);
1,852✔
803

804
        // Extend search
805
        // Backward search to utilize domination user1 over user
806
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
1,852✔
807
        auto edges = std::ranges::subrange(eb, ee);
3,704✔
808
        for (auto edge : edges) {
3,764✔
809
            auto v = boost::source(edge, users.graph_);
1,912✔
810
            queue.push_back(users.users_.at(v).get());
1,912✔
811
        }
812
    }
813

814
    // Collect sub dom tree
815
    auto& dom_tree = this->users_.dom_tree_;
145✔
816

817
    for (auto& user : this->sub_users_) {
1,997✔
818
        this->sub_dom_tree_[user] = dom_tree[user];
1,852✔
819
    }
820
    this->sub_dom_tree_[this->entry_] = nullptr;
145✔
821

822
    // Collect sub post dom tree
823
    auto& pdom_tree = this->users_.pdom_tree_;
145✔
824
    for (auto& user : this->sub_users_) {
1,997✔
825
        this->sub_pdom_tree_[user] = pdom_tree[user];
1,852✔
826
    }
827
    this->sub_pdom_tree_[this->exit_] = nullptr;
145✔
828
};
145✔
829

830
std::vector<User*> UsersView::uses() const {
55✔
831
    std::vector<User*> us;
55✔
832
    for (auto& user : this->sub_users_) {
942✔
833
        if (user->use() == Use::NOP) {
887✔
834
            continue;
323✔
835
        }
836
        us.push_back(user);
564✔
837
    }
838

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

842
std::vector<User*> UsersView::uses(const std::string& container) const {
359✔
843
    std::vector<User*> us;
359✔
844
    for (auto& user : this->sub_users_) {
37,416✔
845
        if (user->container() != container) {
37,057✔
846
            continue;
33,116✔
847
        }
848
        if (user->use() == Use::NOP) {
3,941✔
849
            continue;
×
850
        }
851
        us.push_back(user);
3,941✔
852
    }
853

854
    return us;
359✔
855
};
359✔
856

857
std::vector<User*> UsersView::writes() const {
41✔
858
    std::vector<User*> us;
41✔
859
    for (auto& user : this->sub_users_) {
831✔
860
        if (user->use() != Use::WRITE) {
790✔
861
            continue;
688✔
862
        }
863
        us.push_back(user);
102✔
864
    }
865

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

869
std::vector<User*> UsersView::writes(const std::string& container) const {
99✔
870
    std::vector<User*> us;
99✔
871
    for (auto& user : this->sub_users_) {
3,664✔
872
        if (user->container() != container) {
3,565✔
873
            continue;
3,428✔
874
        }
875
        if (user->use() != Use::WRITE) {
137✔
876
            continue;
59✔
877
        }
878
        us.push_back(user);
78✔
879
    }
880

881
    return us;
99✔
882
};
99✔
883

884
std::vector<User*> UsersView::reads() const {
35✔
885
    std::vector<User*> us;
35✔
886
    for (auto& user : this->sub_users_) {
777✔
887
        if (user->use() != Use::READ) {
742✔
888
            continue;
331✔
889
        }
890
        us.push_back(user);
411✔
891
    }
892

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

896
std::vector<User*> UsersView::reads(const std::string& container) const {
46✔
897
    std::vector<User*> us;
46✔
898
    for (auto& user : this->sub_users_) {
2,381✔
899
        if (user->container() != container) {
2,335✔
900
            continue;
2,243✔
901
        }
902
        if (user->use() != Use::READ) {
92✔
903
            continue;
50✔
904
        }
905
        us.push_back(user);
42✔
906
    }
907

908
    return us;
46✔
909
};
46✔
910

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

920
    return us;
35✔
921
};
35✔
922

923
std::vector<User*> UsersView::views(const std::string& container) const {
×
924
    std::vector<User*> us;
×
925
    for (auto& user : this->sub_users_) {
×
926
        if (user->container() != container) {
×
927
            continue;
×
928
        }
929
        if (user->use() != Use::VIEW) {
×
930
            continue;
×
931
        }
932
        us.push_back(user);
×
933
    }
934

935
    return us;
×
936
};
×
937

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

947
    return us;
35✔
948
};
35✔
949

950
std::vector<User*> UsersView::moves(const std::string& container) const {
×
951
    std::vector<User*> us;
×
952
    for (auto& user : this->sub_users_) {
×
953
        if (user->container() != container) {
×
954
            continue;
×
955
        }
956
        if (user->use() != Use::MOVE) {
×
957
            continue;
×
958
        }
959
        us.push_back(user);
×
960
    }
961

962
    return us;
×
963
};
×
964

965
bool UsersView::dominates(User& user1, User& user) {
×
966
    auto dominator = this->sub_dom_tree_[&user];
×
967
    while (dominator != nullptr) {
×
968
        if (dominator == &user1) {
×
969
            return true;
×
970
        }
971
        dominator = this->sub_dom_tree_[dominator];
×
972
    }
973
    return false;
×
974
};
×
975

976
bool UsersView::post_dominates(User& user1, User& user) {
×
977
    auto dominator = this->sub_pdom_tree_[&user];
×
978
    while (dominator != nullptr) {
×
979
        if (dominator == &user1) {
×
980
            return true;
×
981
        }
982
        dominator = this->sub_pdom_tree_[dominator];
×
983
    }
984
    return false;
×
985
};
×
986

987
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
988
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
989
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
990

991
    std::unordered_set<User*> uses;
×
992
    std::unordered_set<User*> visited;
×
993
    std::list<User*> queue = {&user1};
×
994
    while (!queue.empty()) {
×
995
        auto current = queue.front();
×
996
        queue.pop_front();
×
997
        if (visited.find(current) != visited.end()) {
×
998
            continue;
×
999
        }
1000
        visited.insert(current);
×
1001

1002
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
1003
            uses.insert(current);
×
1004
        }
×
1005

1006
        // Stop conditions
1007
        if (current == exit_) {
×
1008
            continue;
×
1009
        }
1010

1011
        if (current == &user2) {
×
1012
            continue;
×
1013
        }
1014

1015
        // Extend search
1016
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1017
        auto edges = std::ranges::subrange(eb, ee);
×
1018
        for (auto edge : edges) {
×
1019
            auto v = boost::target(edge, this->users_.graph_);
×
1020
            queue.push_back(this->users_.users_.at(v).get());
×
1021
        }
1022
    }
1023

1024
    return uses;
×
1025
};
×
1026

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

1030
    std::unordered_set<User*> uses;
×
1031
    std::unordered_set<User*> visited;
×
1032
    std::list<User*> queue = {&user};
×
1033
    while (!queue.empty()) {
×
1034
        auto current = queue.front();
×
1035
        queue.pop_front();
×
1036
        if (visited.find(current) != visited.end()) {
×
1037
            continue;
×
1038
        }
1039
        visited.insert(current);
×
1040

1041
        if (current != &user && current->use() != Use::NOP) {
×
1042
            uses.insert(current);
×
1043
        }
×
1044

1045
        if (current == exit_) {
×
1046
            continue;
×
1047
        }
1048

1049
        // Extend search
1050
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1051
        auto edges = std::ranges::subrange(eb, ee);
×
1052
        for (auto edge : edges) {
×
1053
            auto v = boost::target(edge, this->users_.graph_);
×
1054
            queue.push_back(this->users_.users_.at(v).get());
×
1055
        }
1056
    }
1057

1058
    return uses;
×
1059
};
×
1060

1061
User* Users::get_user(const std::string& container, Element* element, Use use, bool is_init,
641✔
1062
                      bool is_condition, bool is_update) {
1063
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element)) {
641✔
1064
        if (is_init) {
272✔
1065
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
61✔
1066
            return tmp;
61✔
1067
        } else if (is_condition) {
211✔
1068
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
101✔
1069
        } else if (is_update) {
110✔
1070
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
110✔
1071
        }
1072
    }
×
1073
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
369✔
1074
    return tmp;
369✔
1075
}
641✔
1076

1077
void Users::add_user(std::unique_ptr<User> user) {
1,229✔
1078
    auto vertex = user->vertex_;
1,229✔
1079
    this->users_.insert({vertex, std::move(user)});
1,229✔
1080

1081
    auto user_ptr = this->users_.at(vertex).get();
1,229✔
1082
    auto* target_structure = &users_by_sdfg_;
1,229✔
1083
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,229✔
1084
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
506✔
1085
        if (for_loop == nullptr) {
506✔
1086
            throw std::invalid_argument("Invalid user type");
×
1087
        }
1088
        if (for_user->is_init()) {
506✔
1089
            target_structure = &users_by_sdfg_loop_init_;
110✔
1090
        } else if (for_user->is_condition()) {
506✔
1091
            target_structure = &users_by_sdfg_loop_condition_;
190✔
1092
        } else if (for_user->is_update()) {
396✔
1093
            target_structure = &users_by_sdfg_loop_update_;
206✔
1094
        } else {
206✔
1095
            throw std::invalid_argument("Invalid user type");
×
1096
        }
1097
    }
506✔
1098

1099
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,229✔
1100
        target_structure->insert({user_ptr->container(), {}});
760✔
1101
    }
760✔
1102
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
2,458✔
1103
        (*target_structure)[user_ptr->container()].end()) {
1,229✔
1104
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
1,121✔
1105
    }
1,121✔
1106
    target_structure->at(user_ptr->container())
1,229✔
1107
        .at(user_ptr->element())
1,229✔
1108
        .insert({user_ptr->use(), user_ptr});
1,229✔
1109
}
1,229✔
1110

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

1114
    // Locals have no uses outside of the node
1115
    // We can check this by comparing the number of uses of the container in the view and the total
1116
    // number of uses of the container in the users map.
1117
    std::unordered_set<std::string> locals;
55✔
1118
    UsersView view(*this, node);
55✔
1119
    for (auto& user : view.uses()) {
619✔
1120
        if (!sdfg.is_transient(user->container())) {
564✔
1121
            continue;
213✔
1122
        }
1123
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
351✔
1124
            locals.insert(user->container());
180✔
1125
        }
180✔
1126
    }
1127

1128
    return locals;
55✔
1129
};
55✔
1130

1131
}  // namespace analysis
1132
}  // 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