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

daisytuner / sdfglib / 16069945621

04 Jul 2025 08:56AM UTC coverage: 64.375% (-0.2%) from 64.606%
16069945621

push

github

web-flow
Merge pull request #137 from daisytuner/clang-format

runs clang-format on codebase

609 of 827 new or added lines in 63 files covered. (73.64%)

46 existing lines in 30 files now uncovered.

8578 of 13325 relevant lines covered (64.38%)

177.24 hits per line

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

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

27
      };
2,115✔
28

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

32
      };
664✔
33

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

36
};
5,008✔
37

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

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

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

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

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

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

68
    // Use of symbol
69
    return {{}};
12✔
70
};
495✔
71

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

83
      };
550✔
84

85
bool ForUser::is_init() const { return this->is_init_; };
554✔
86

87
bool ForUser::is_condition() const { return this->is_condition_; };
432✔
88

89
bool ForUser::is_update() const { return this->is_update_; };
229✔
90

91
void Users::init_dom_tree() {
155✔
92
    this->dom_tree_.clear();
155✔
93
    this->pdom_tree_.clear();
155✔
94

95
    // Compute dominator-tree
96
    auto dom_tree = graph::dominator_tree(this->graph_, this->source_->vertex_);
155✔
97
    for (auto& entry : dom_tree) {
2,934✔
98
        User* first = this->users_.at(entry.first).get();
2,779✔
99
        User* second = nullptr;
2,779✔
100
        if (entry.second != boost::graph_traits<graph::Graph>::null_vertex()) {
2,779✔
101
            second = this->users_.at(entry.second).get();
2,624✔
102
        }
2,624✔
103
        this->dom_tree_.insert({first, second});
2,779✔
104
    }
105

106
    // Compute post-dominator-tree
107
    auto pdom_tree = graph::post_dominator_tree(this->graph_, this->sink_->vertex_);
155✔
108
    for (auto& entry : pdom_tree) {
2,934✔
109
        User* first = this->users_.at(entry.first).get();
2,779✔
110
        User* second = nullptr;
2,779✔
111
        if (entry.second != boost::graph_traits<graph::Graph>::null_vertex()) {
2,779✔
112
            second = this->users_.at(entry.second).get();
2,624✔
113
        }
2,624✔
114
        this->pdom_tree_.insert({first, second});
2,779✔
115
    }
116
};
155✔
117

118
std::pair<graph::Vertex, graph::Vertex> Users::traverse(data_flow::DataFlowGraph& dataflow) {
243✔
119
    graph::Vertex first = boost::graph_traits<graph::Graph>::null_vertex();
243✔
120
    graph::Vertex last = boost::graph_traits<graph::Graph>::null_vertex();
243✔
121
    for (auto node : dataflow.topological_sort()) {
715✔
122
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
472✔
123
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
314✔
124
                if (dataflow.in_degree(*node) > 0) {
314✔
125
                    Use use = Use::WRITE;
160✔
126
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
319✔
127
                        if (iedge.src_conn() == "refs" || iedge.dst_conn() == "refs") {
160✔
128
                            use = Use::MOVE;
1✔
129
                            break;
1✔
130
                        }
131
                    }
132

133
                    auto v = boost::add_vertex(this->graph_);
160✔
134
                    this->add_user(std::make_unique<User>(v, access_node->data(), access_node, &dataflow, use));
160✔
135

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

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

155
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
158✔
156
                        boost::add_edge(last, v, this->graph_);
44✔
157
                    } else {
44✔
158
                        first = v;
114✔
159
                    }
160
                    last = v;
158✔
161
                }
158✔
162
            }
314✔
163
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
472✔
164
            if (tasklet->is_conditional()) {
158✔
165
                auto& condition = tasklet->condition();
5✔
166
                for (auto& atom : symbolic::atoms(condition)) {
10✔
167
                    auto v = boost::add_vertex(this->graph_);
5✔
168
                    this->add_user(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)) {
158✔
178
            for (auto& symbol : library_node->symbols()) {
×
179
                auto v = boost::add_vertex(this->graph_);
×
NEW
180
                this->add_user(std::make_unique<User>(v, symbol->get_name(), library_node, &dataflow, Use::READ));
×
181
                if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
×
182
                    boost::add_edge(last, v, this->graph_);
×
183
                } else {
×
184
                    first = v;
×
185
                }
186
                last = v;
×
187
            }
188
        }
×
189

190
        for (auto& oedge : dataflow.out_edges(*node)) {
793✔
191
            std::unordered_set<std::string> used;
321✔
192
            for (auto dim : oedge.subset()) {
616✔
193
                for (auto atom : symbolic::atoms(dim)) {
636✔
194
                    if (used.find(atom->get_name()) != used.end()) {
341✔
195
                        continue;
×
196
                    }
197
                    used.insert(atom->get_name());
341✔
198

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

212
    return {first, last};
243✔
213
};
×
214

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

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

227
        auto& dataflow = block_stmt->dataflow();
243✔
228
        auto subgraph = this->traverse(dataflow);
243✔
229

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

236
        boost::add_edge(s, subgraph.first, this->graph_);
152✔
237
        boost::add_edge(subgraph.second, t, this->graph_);
152✔
238

239
        return {s, t};
152✔
240
    } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(&node)) {
478✔
241
        auto s = boost::add_vertex(this->graph_);
324✔
242
        this->users_.insert({s, std::make_unique<User>(s, "", sequence_stmt, Use::NOP)});
324✔
243
        this->entries_.insert({sequence_stmt, this->users_.at(s).get()});
324✔
244

245
        graph::Vertex current = s;
324✔
246
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
725✔
247
            auto child = sequence_stmt->at(i);
401✔
248

249
            auto subgraph = this->traverse(child.first);
401✔
250
            boost::add_edge(current, subgraph.first, this->graph_);
401✔
251
            current = subgraph.second;
401✔
252

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

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

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

272
            for (auto& entry : child.second.assignments()) {
481✔
273
                auto v = boost::add_vertex(this->graph_);
80✔
274
                this->add_user(std::make_unique<User>(v, entry.first->get_name(), &child.second, Use::WRITE));
80✔
275

276
                boost::add_edge(current, v, this->graph_);
80✔
277
                current = v;
80✔
278
            }
279
        }
401✔
280

281
        if (current == boost::graph_traits<graph::Graph>::null_vertex()) {
324✔
282
            this->exits_.insert({sequence_stmt, this->users_.at(s).get()});
×
283
            return {s, current};
×
284
        }
285

286
        auto t = boost::add_vertex(this->graph_);
324✔
287
        this->users_.insert({t, std::make_unique<User>(t, "", sequence_stmt, Use::NOP)});
324✔
288
        boost::add_edge(current, t, this->graph_);
324✔
289
        this->exits_.insert({sequence_stmt, this->users_.at(t).get()});
324✔
290

291
        return {s, t};
324✔
292
    } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(&node)) {
154✔
293
        // NOP
294
        auto s = boost::add_vertex(this->graph_);
27✔
295
        this->users_.insert({s, std::make_unique<User>(s, "", if_else_stmt, Use::NOP)});
27✔
296
        this->entries_.insert({if_else_stmt, this->users_.at(s).get()});
27✔
297

298
        graph::Vertex last = s;
27✔
299

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

309
                auto v = boost::add_vertex(this->graph_);
25✔
310

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

313
                boost::add_edge(last, v, this->graph_);
25✔
314
                last = v;
25✔
315
            }
39✔
316
        }
46✔
317

318
        // NOP
319
        auto t = boost::add_vertex(this->graph_);
27✔
320
        this->users_.insert({t, std::make_unique<User>(t, "", if_else_stmt, Use::NOP)});
27✔
321
        this->exits_.insert({if_else_stmt, this->users_.at(t).get()});
27✔
322

323
        // Forward edge: Potentially missing else case
324
        if (!if_else_stmt->is_complete()) {
27✔
325
            boost::add_edge(last, t, this->graph_);
8✔
326
        }
8✔
327

328
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
73✔
329
            auto branch = if_else_stmt->at(i);
46✔
330
            auto subgraph = this->traverse(branch.first);
46✔
331
            boost::add_edge(last, subgraph.first, this->graph_);
46✔
332
            if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
46✔
333
                boost::add_edge(subgraph.second, t, this->graph_);
46✔
334
            }
46✔
335
        }
46✔
336

337
        return {s, t};
27✔
338
    } else if (auto loop_stmt = dynamic_cast<structured_control_flow::While*>(&node)) {
154✔
339
        // NOP
340
        auto s = boost::add_vertex(this->graph_);
7✔
341
        this->users_.insert({s, std::make_unique<User>(s, "", loop_stmt, Use::NOP)});
7✔
342
        this->entries_.insert({loop_stmt, this->users_.at(s).get()});
7✔
343

344
        auto subgraph = this->traverse(loop_stmt->root());
7✔
345

346
        // NOP
347
        auto t = boost::add_vertex(this->graph_);
7✔
348
        this->users_.insert({t, std::make_unique<User>(t, "", loop_stmt, Use::NOP)});
7✔
349
        this->exits_.insert({loop_stmt, this->users_.at(t).get()});
7✔
350

351
        boost::add_edge(s, subgraph.first, this->graph_);
7✔
352
        if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
7✔
353
            boost::add_edge(subgraph.second, t, this->graph_);
7✔
354
        }
7✔
355

356
        // Empty loop
357
        boost::add_edge(s, t, this->graph_);
7✔
358
        // Back edge
359
        boost::add_edge(t, s, this->graph_);
7✔
360

361
        return {s, t};
7✔
362
    } else if (auto for_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(&node)) {
120✔
363
        // NOP
364
        auto s = boost::add_vertex(this->graph_);
112✔
365
        this->users_.insert({s, std::make_unique<User>(s, "", for_stmt, Use::NOP)});
112✔
366
        auto last = s;
112✔
367
        this->entries_.insert({for_stmt, this->users_.at(s).get()});
112✔
368

369
        // NOP
370
        auto t = boost::add_vertex(this->graph_);
112✔
371
        this->users_.insert({t, std::make_unique<User>(t, "", for_stmt, Use::NOP)});
112✔
372
        this->exits_.insert({for_stmt, this->users_.at(t).get()});
112✔
373

374
        // Init
375
        for (auto atom : symbolic::atoms(for_stmt->init())) {
121✔
376
            auto v = boost::add_vertex(this->graph_);
9✔
377
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ, true, false, false));
9✔
378
            boost::add_edge(last, v, this->graph_);
9✔
379
            last = v;
9✔
380
        }
9✔
381
        // Indvar
382
        auto v = boost::add_vertex(this->graph_);
112✔
383
        this->add_user(std::make_unique<
112✔
384
                       ForUser>(v, for_stmt->indvar()->get_name(), for_stmt, Use::WRITE, true, false, false));
112✔
385

386
        boost::add_edge(last, v, this->graph_);
112✔
387
        last = v;
112✔
388

389
        // Condition
390
        for (auto atom : symbolic::atoms(for_stmt->condition())) {
317✔
391
            auto v = boost::add_vertex(this->graph_);
205✔
392
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ, false, true, false));
205✔
393

394
            boost::add_edge(last, v, this->graph_);
205✔
395
            boost::add_edge(v, t, this->graph_);
205✔
396
            last = v;
205✔
397
        }
205✔
398

399
        auto subgraph = this->traverse(for_stmt->root());
112✔
400
        boost::add_edge(last, subgraph.first, this->graph_);
112✔
401

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

411
        auto update_v = boost::add_vertex(this->graph_);
112✔
412
        this->add_user(std::make_unique<
112✔
413
                       ForUser>(update_v, for_stmt->indvar()->get_name(), for_stmt, Use::WRITE, false, false, true));
112✔
414

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

420
        if (end != boost::graph_traits<graph::Graph>::null_vertex()) {
112✔
421
            boost::add_edge(end, t, this->graph_);
112✔
422
        }
112✔
423

424
        // Back edge
425
        boost::add_edge(t, last, this->graph_);
112✔
426

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

450
    throw std::invalid_argument("Invalid control flow node type");
×
451
};
721✔
452

453
Users::Users(StructuredSDFG& sdfg)
155✔
454
    : Analysis(sdfg), node_(sdfg.root()) {
155✔
455

456
      };
155✔
457

458
Users::Users(StructuredSDFG& sdfg, structured_control_flow::ControlFlowNode& node)
×
459
    : Analysis(sdfg), node_(node) {
×
460

461
      };
×
462

463
void Users::run(analysis::AnalysisManager& analysis_manager) {
155✔
464
    users_.clear();
155✔
465
    graph_.clear();
155✔
466
    source_ = nullptr;
155✔
467
    sink_ = nullptr;
155✔
468
    dom_tree_.clear();
155✔
469
    pdom_tree_.clear();
155✔
470
    users_by_sdfg_.clear();
155✔
471
    users_by_sdfg_loop_condition_.clear();
155✔
472
    users_by_sdfg_loop_init_.clear();
155✔
473
    users_by_sdfg_loop_update_.clear();
155✔
474

475
    reads_.clear();
155✔
476
    writes_.clear();
155✔
477
    views_.clear();
155✔
478
    moves_.clear();
155✔
479

480
    this->traverse(node_);
155✔
481
    if (this->users_.empty()) {
155✔
482
        return;
×
483
    }
484

485
    // Require a single source
486
    for (auto& entry : this->users_) {
2,934✔
487
        if (boost::in_degree(entry.first, this->graph_) == 0) {
2,779✔
488
            if (this->source_ != nullptr) {
155✔
489
                throw InvalidSDFGException("Users Analysis: Non-unique source node");
×
490
            }
491
            this->source_ = entry.second.get();
155✔
492
        }
155✔
493
    }
494
    if (this->source_ == nullptr) {
155✔
495
        throw InvalidSDFGException("Users Analysis: No source node");
×
496
    }
497

498
    std::list<User*> sinks;
155✔
499
    for (auto& entry : this->users_) {
2,934✔
500
        if (boost::out_degree(entry.first, this->graph_) == 0) {
2,779✔
501
            sinks.push_back(entry.second.get());
155✔
502
        }
155✔
503
    }
504
    if (sinks.size() == 0) {
155✔
505
        throw InvalidSDFGException("Users Analysis: No sink node");
×
506
    }
507
    if (sinks.size() > 1) {
155✔
508
        // add artificial sink
509
        auto v = boost::add_vertex(this->graph_);
×
510
        auto sink = std::make_unique<User>(v, "", nullptr, Use::NOP);
×
511
        this->users_.insert({v, std::move(sink)});
×
512
        for (auto sink : sinks) {
×
513
            boost::add_edge(sink->vertex_, v, this->graph_);
×
514
        }
515
        sinks.clear();
×
516

517
        this->sink_ = this->users_.at(v).get();
×
518
    } else {
×
519
        this->sink_ = sinks.front();
155✔
520
    }
521

522
    // Collect sub structures
523
    for (auto& entry : this->users_) {
2,934✔
524
        auto container = entry.second->container();
2,779✔
525
        switch (entry.second->use()) {
2,779✔
526
            case Use::READ: {
527
                if (this->reads_.find(container) == this->reads_.end()) {
880✔
528
                    this->reads_.insert({container, {}});
390✔
529
                }
390✔
530
                this->reads_[container].push_back(entry.second.get());
880✔
531
                break;
880✔
532
            }
533
            case Use::WRITE: {
534
                if (this->writes_.find(container) == this->writes_.end()) {
463✔
535
                    this->writes_.insert({container, {}});
302✔
536
                }
302✔
537
                this->writes_[container].push_back(entry.second.get());
463✔
538
                break;
463✔
539
            }
540
            case Use::VIEW: {
541
                if (this->views_.find(container) == this->views_.end()) {
1✔
542
                    this->views_.insert({container, {}});
1✔
543
                }
1✔
544
                this->views_[container].push_back(entry.second.get());
1✔
545
                break;
1✔
546
            }
547
            case Use::MOVE: {
548
                if (this->moves_.find(container) == this->moves_.end()) {
1✔
549
                    this->moves_.insert({container, {}});
1✔
550
                }
1✔
551
                this->moves_[container].push_back(entry.second.get());
1✔
552
                break;
1✔
553
            }
554
            default:
555
                break;
1,434✔
556
        }
557
    }
2,779✔
558

559
    this->init_dom_tree();
155✔
560
};
155✔
561

562
std::vector<User*> Users::uses() const {
×
563
    std::vector<User*> us;
×
564
    for (auto& entry : this->users_) {
×
565
        if (entry.second->use() == Use::NOP) {
×
566
            continue;
×
567
        }
568
        us.push_back(entry.second.get());
×
569
    }
570

571
    return us;
×
572
};
×
573

574
std::vector<User*> Users::uses(const std::string& container) const {
352✔
575
    std::vector<User*> us;
352✔
576
    for (auto& entry : this->users_) {
41,950✔
577
        if (entry.second->container() != container) {
41,598✔
578
            continue;
37,000✔
579
        }
580
        if (entry.second->use() == Use::NOP) {
4,598✔
581
            continue;
×
582
        }
583
        us.push_back(entry.second.get());
4,598✔
584
    }
585

586
    return us;
352✔
587
};
352✔
588

589
std::vector<User*> Users::writes() const {
×
590
    std::vector<User*> us;
×
591
    for (auto& entry : this->users_) {
×
592
        if (entry.second->use() != Use::WRITE) {
×
593
            continue;
×
594
        }
595
        us.push_back(entry.second.get());
×
596
    }
597

598
    return us;
×
599
};
×
600

601
std::vector<User*> Users::writes(const std::string& container) const {
48✔
602
    if (this->writes_.find(container) == this->writes_.end()) {
48✔
603
        return {};
25✔
604
    } else {
605
        return this->writes_.at(container);
23✔
606
    }
607
};
48✔
608

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

618
    return us;
×
619
};
×
620

621
std::vector<User*> Users::reads(const std::string& container) const {
33✔
622
    if (this->reads_.find(container) == this->reads_.end()) {
33✔
623
        return {};
13✔
624
    } else {
625
        return this->reads_.at(container);
20✔
626
    }
627
};
33✔
628

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

638
    return us;
×
639
};
×
640

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

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

658
    return us;
×
659
};
×
660

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

669
structured_control_flow::ControlFlowNode* Users::scope(User* user) {
274✔
670
    if (auto data_node = dynamic_cast<data_flow::DataFlowNode*>(user->element())) {
274✔
671
        return static_cast<structured_control_flow::Block*>(data_node->get_parent().get_parent());
274✔
672
    } else if (auto memlet = dynamic_cast<data_flow::Memlet*>(user->element())) {
×
673
        return static_cast<structured_control_flow::Block*>(memlet->get_parent().get_parent());
×
NEW
674
    } else if (auto transition = dynamic_cast<structured_control_flow::Transition*>(user->element())) {
×
675
        return &transition->parent();
×
676
    } else {
NEW
677
        auto user_element = dynamic_cast<structured_control_flow::ControlFlowNode*>(user->element());
×
678
        assert(user_element != nullptr && "Users::scope: User element is not a ControlFlowNode");
×
679
        return user_element;
×
680
    }
681
}
274✔
682

683
/****** Domination Analysis ******/
684

685
bool Users::dominates(User& user1, User& user) {
32✔
686
    auto dominator = this->dom_tree_.at(&user);
32✔
687
    while (dominator != nullptr) {
109✔
688
        if (dominator == &user1) {
103✔
689
            return true;
26✔
690
        }
691
        dominator = this->dom_tree_.at(dominator);
77✔
692
    }
693
    return false;
6✔
694
};
32✔
695

696
bool Users::post_dominates(User& user1, User& user) {
12✔
697
    auto dominator = this->pdom_tree_.at(&user);
12✔
698
    while (dominator != nullptr) {
29✔
699
        if (dominator == &user1) {
23✔
700
            return true;
6✔
701
        }
702
        dominator = this->pdom_tree_.at(dominator);
17✔
703
    }
704
    return false;
6✔
705
};
12✔
706

707
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
14✔
708
    std::unordered_set<User*> uses;
14✔
709
    std::unordered_set<User*> visited;
14✔
710
    std::list<User*> queue = {&user2};
14✔
711
    while (!queue.empty()) {
90✔
712
        auto current = queue.front();
76✔
713
        queue.pop_front();
76✔
714

715
        // Stop conditions
716
        if (current == &user1) {
76✔
717
            continue;
14✔
718
        }
719

720
        if (visited.find(current) != visited.end()) {
62✔
721
            continue;
2✔
722
        }
723
        visited.insert(current);
60✔
724

725
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
60✔
726
            uses.insert(current);
9✔
727
        }
9✔
728

729
        // Extend search
730
        // Backward search to utilize domination user1 over user
731
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
60✔
732
        auto edges = std::ranges::subrange(eb, ee);
120✔
733
        for (auto edge : edges) {
122✔
734
            auto v = boost::source(edge, this->graph_);
62✔
735
            queue.push_back(this->users_.at(v).get());
62✔
736
        }
737
    }
738

739
    return uses;
14✔
740
};
14✔
741

742
const std::unordered_set<User*> Users::all_uses_after(User& user) {
×
743
    std::unordered_set<User*> uses;
×
744
    std::unordered_set<User*> visited;
×
745
    std::list<User*> queue = {&user};
×
746
    while (!queue.empty()) {
×
747
        auto current = queue.front();
×
748
        queue.pop_front();
×
749
        if (visited.find(current) != visited.end()) {
×
750
            continue;
×
751
        }
752
        visited.insert(current);
×
753

754
        if (current != &user && current->use() != Use::NOP) {
×
755
            uses.insert(current);
×
756
        }
×
757

758
        // Extend search
759
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
760
        auto edges = std::ranges::subrange(eb, ee);
×
761
        for (auto edge : edges) {
×
762
            auto v = boost::target(edge, this->graph_);
×
763
            queue.push_back(this->users_.at(v).get());
×
764
        }
765
    }
766

767
    return uses;
×
768
};
×
769

770
UsersView::UsersView(Users& users, structured_control_flow::ControlFlowNode& node) : users_(users) {
149✔
771
    this->entry_ = users.entries_.at(&node);
149✔
772
    this->exit_ = users.exits_.at(&node);
149✔
773

774
    // Collect sub users
775
    std::unordered_set<User*> visited;
149✔
776
    std::list<User*> queue = {this->exit_};
149✔
777
    while (!queue.empty()) {
2,310✔
778
        auto current = queue.front();
2,161✔
779
        queue.pop_front();
2,161✔
780

781
        // Stop conditions
782
        if (current == this->entry_) {
2,161✔
783
            continue;
149✔
784
        }
785

786
        if (visited.find(current) != visited.end()) {
2,012✔
787
            continue;
72✔
788
        }
789
        visited.insert(current);
1,940✔
790

791
        this->sub_users_.insert(current);
1,940✔
792

793
        // Extend search
794
        // Backward search to utilize domination user1 over user
795
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
1,940✔
796
        auto edges = std::ranges::subrange(eb, ee);
3,880✔
797
        for (auto edge : edges) {
3,952✔
798
            auto v = boost::source(edge, users.graph_);
2,012✔
799
            queue.push_back(users.users_.at(v).get());
2,012✔
800
        }
801
    }
802

803
    // Collect sub dom tree
804
    auto& dom_tree = this->users_.dom_tree_;
149✔
805

806
    for (auto& user : this->sub_users_) {
2,089✔
807
        this->sub_dom_tree_[user] = dom_tree[user];
1,940✔
808
    }
809
    this->sub_dom_tree_[this->entry_] = nullptr;
149✔
810

811
    // Collect sub post dom tree
812
    auto& pdom_tree = this->users_.pdom_tree_;
149✔
813
    for (auto& user : this->sub_users_) {
2,089✔
814
        this->sub_pdom_tree_[user] = pdom_tree[user];
1,940✔
815
    }
816
    this->sub_pdom_tree_[this->exit_] = nullptr;
149✔
817
};
149✔
818

819
std::vector<User*> UsersView::uses() const {
55✔
820
    std::vector<User*> us;
55✔
821
    for (auto& user : this->sub_users_) {
942✔
822
        if (user->use() == Use::NOP) {
887✔
823
            continue;
323✔
824
        }
825
        us.push_back(user);
564✔
826
    }
827

828
    return us;
55✔
829
};
55✔
830

831
std::vector<User*> UsersView::uses(const std::string& container) const {
359✔
832
    std::vector<User*> us;
359✔
833
    for (auto& user : this->sub_users_) {
37,416✔
834
        if (user->container() != container) {
37,057✔
835
            continue;
33,116✔
836
        }
837
        if (user->use() == Use::NOP) {
3,941✔
838
            continue;
×
839
        }
840
        us.push_back(user);
3,941✔
841
    }
842

843
    return us;
359✔
844
};
359✔
845

846
std::vector<User*> UsersView::writes() const {
41✔
847
    std::vector<User*> us;
41✔
848
    for (auto& user : this->sub_users_) {
831✔
849
        if (user->use() != Use::WRITE) {
790✔
850
            continue;
688✔
851
        }
852
        us.push_back(user);
102✔
853
    }
854

855
    return us;
41✔
856
};
41✔
857

858
std::vector<User*> UsersView::writes(const std::string& container) const {
106✔
859
    std::vector<User*> us;
106✔
860
    for (auto& user : this->sub_users_) {
3,853✔
861
        if (user->container() != container) {
3,747✔
862
            continue;
3,602✔
863
        }
864
        if (user->use() != Use::WRITE) {
145✔
865
            continue;
62✔
866
        }
867
        us.push_back(user);
83✔
868
    }
869

870
    return us;
106✔
871
};
106✔
872

873
std::vector<User*> UsersView::reads() const {
35✔
874
    std::vector<User*> us;
35✔
875
    for (auto& user : this->sub_users_) {
777✔
876
        if (user->use() != Use::READ) {
742✔
877
            continue;
331✔
878
        }
879
        us.push_back(user);
411✔
880
    }
881

882
    return us;
35✔
883
};
35✔
884

885
std::vector<User*> UsersView::reads(const std::string& container) const {
53✔
886
    std::vector<User*> us;
53✔
887
    for (auto& user : this->sub_users_) {
2,570✔
888
        if (user->container() != container) {
2,517✔
889
            continue;
2,417✔
890
        }
891
        if (user->use() != Use::READ) {
100✔
892
            continue;
55✔
893
        }
894
        us.push_back(user);
45✔
895
    }
896

897
    return us;
53✔
898
};
53✔
899

900
std::vector<User*> UsersView::views() const {
35✔
901
    std::vector<User*> us;
35✔
902
    for (auto& user : this->sub_users_) {
777✔
903
        if (user->use() != Use::VIEW) {
742✔
904
            continue;
742✔
905
        }
906
        us.push_back(user);
×
907
    }
908

909
    return us;
35✔
910
};
35✔
911

912
std::vector<User*> UsersView::views(const std::string& container) const {
7✔
913
    std::vector<User*> us;
7✔
914
    for (auto& user : this->sub_users_) {
189✔
915
        if (user->container() != container) {
182✔
916
            continue;
174✔
917
        }
918
        if (user->use() != Use::VIEW) {
8✔
919
            continue;
8✔
920
        }
921
        us.push_back(user);
×
922
    }
923

924
    return us;
7✔
925
};
7✔
926

927
std::vector<User*> UsersView::moves() const {
35✔
928
    std::vector<User*> us;
35✔
929
    for (auto& user : this->sub_users_) {
777✔
930
        if (user->use() != Use::MOVE) {
742✔
931
            continue;
742✔
932
        }
933
        us.push_back(user);
×
934
    }
935

936
    return us;
35✔
937
};
35✔
938

939
std::vector<User*> UsersView::moves(const std::string& container) const {
7✔
940
    std::vector<User*> us;
7✔
941
    for (auto& user : this->sub_users_) {
189✔
942
        if (user->container() != container) {
182✔
943
            continue;
174✔
944
        }
945
        if (user->use() != Use::MOVE) {
8✔
946
            continue;
8✔
947
        }
948
        us.push_back(user);
×
949
    }
950

951
    return us;
7✔
952
};
7✔
953

954
bool UsersView::dominates(User& user1, User& user) {
×
955
    auto dominator = this->sub_dom_tree_[&user];
×
956
    while (dominator != nullptr) {
×
957
        if (dominator == &user1) {
×
958
            return true;
×
959
        }
960
        dominator = this->sub_dom_tree_[dominator];
×
961
    }
962
    return false;
×
963
};
×
964

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

976
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
977
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
978
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
979

980
    std::unordered_set<User*> uses;
×
981
    std::unordered_set<User*> visited;
×
982
    std::list<User*> queue = {&user1};
×
983
    while (!queue.empty()) {
×
984
        auto current = queue.front();
×
985
        queue.pop_front();
×
986
        if (visited.find(current) != visited.end()) {
×
987
            continue;
×
988
        }
989
        visited.insert(current);
×
990

991
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
992
            uses.insert(current);
×
993
        }
×
994

995
        // Stop conditions
996
        if (current == exit_) {
×
997
            continue;
×
998
        }
999

1000
        if (current == &user2) {
×
1001
            continue;
×
1002
        }
1003

1004
        // Extend search
1005
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1006
        auto edges = std::ranges::subrange(eb, ee);
×
1007
        for (auto edge : edges) {
×
1008
            auto v = boost::target(edge, this->users_.graph_);
×
1009
            queue.push_back(this->users_.users_.at(v).get());
×
1010
        }
1011
    }
1012

1013
    return uses;
×
1014
};
×
1015

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

1019
    std::unordered_set<User*> uses;
×
1020
    std::unordered_set<User*> visited;
×
1021
    std::list<User*> queue = {&user};
×
1022
    while (!queue.empty()) {
×
1023
        auto current = queue.front();
×
1024
        queue.pop_front();
×
1025
        if (visited.find(current) != visited.end()) {
×
1026
            continue;
×
1027
        }
1028
        visited.insert(current);
×
1029

1030
        if (current != &user && current->use() != Use::NOP) {
×
1031
            uses.insert(current);
×
1032
        }
×
1033

1034
        if (current == exit_) {
×
1035
            continue;
×
1036
        }
1037

1038
        // Extend search
1039
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1040
        auto edges = std::ranges::subrange(eb, ee);
×
1041
        for (auto edge : edges) {
×
1042
            auto v = boost::target(edge, this->users_.graph_);
×
1043
            queue.push_back(this->users_.users_.at(v).get());
×
1044
        }
1045
    }
1046

1047
    return uses;
×
1048
};
×
1049

1050
User* Users::
1051
    get_user(const std::string& container, Element* element, Use use, bool is_init, bool is_condition, bool is_update) {
757✔
1052
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element)) {
757✔
1053
        if (is_init) {
316✔
1054
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
72✔
1055
            return tmp;
72✔
1056
        } else if (is_condition) {
244✔
1057
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
116✔
1058
        } else if (is_update) {
128✔
1059
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
128✔
1060
        }
1061
    }
×
1062
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
441✔
1063
    return tmp;
441✔
1064
}
757✔
1065

1066
void Users::add_user(std::unique_ptr<User> user) {
1,345✔
1067
    auto vertex = user->vertex_;
1,345✔
1068
    this->users_.insert({vertex, std::move(user)});
1,345✔
1069

1070
    auto user_ptr = this->users_.at(vertex).get();
1,345✔
1071
    auto* target_structure = &users_by_sdfg_;
1,345✔
1072
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,345✔
1073
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
550✔
1074
        if (for_loop == nullptr) {
550✔
1075
            throw std::invalid_argument("Invalid user type");
×
1076
        }
1077
        if (for_user->is_init()) {
550✔
1078
            target_structure = &users_by_sdfg_loop_init_;
121✔
1079
        } else if (for_user->is_condition()) {
550✔
1080
            target_structure = &users_by_sdfg_loop_condition_;
205✔
1081
        } else if (for_user->is_update()) {
429✔
1082
            target_structure = &users_by_sdfg_loop_update_;
224✔
1083
        } else {
224✔
1084
            throw std::invalid_argument("Invalid user type");
×
1085
        }
1086
    }
550✔
1087

1088
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,345✔
1089
        target_structure->insert({user_ptr->container(), {}});
808✔
1090
    }
808✔
1091
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
2,690✔
1092
        (*target_structure)[user_ptr->container()].end()) {
1,345✔
1093
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
1,228✔
1094
    }
1,228✔
1095
    target_structure->at(user_ptr->container()).at(user_ptr->element()).insert({user_ptr->use(), user_ptr});
1,345✔
1096
}
1,345✔
1097

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

1101
    // Locals have no uses outside of the node
1102
    // We can check this by comparing the number of uses of the container in the view and the total
1103
    // number of uses of the container in the users map.
1104
    std::unordered_set<std::string> locals;
55✔
1105
    UsersView view(*this, node);
55✔
1106
    for (auto& user : view.uses()) {
619✔
1107
        if (!sdfg.is_transient(user->container())) {
564✔
1108
            continue;
213✔
1109
        }
1110
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
351✔
1111
            locals.insert(user->container());
180✔
1112
        }
180✔
1113
    }
1114

1115
    return locals;
55✔
1116
};
55✔
1117

1118
} // namespace analysis
1119
} // 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

© 2025 Coveralls, Inc