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

daisytuner / sdfglib / 15852980623

24 Jun 2025 02:16PM UTC coverage: 64.412% (+0.3%) from 64.145%
15852980623

push

github

web-flow
Merge pull request #72 from daisytuner/capture-instrumentation

Capture instrumentation

363 of 446 new or added lines in 19 files covered. (81.39%)

100 existing lines in 5 files now uncovered.

8389 of 13024 relevant lines covered (64.41%)

116.79 hits per line

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

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

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

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

21
namespace sdfg {
22
namespace analysis {
23

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

27
      };
1,355✔
28

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

33
      };
426✔
34

35
User::~User() {
3,247✔
36

37
};
3,247✔
38

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

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

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

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

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

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

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

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

80
      };
315✔
81

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

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

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

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

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

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

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

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

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

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

154
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
102✔
155
                        boost::add_edge(last, v, this->graph_);
26✔
156
                    } else {
26✔
157
                        first = v;
76✔
158
                    }
159
                    last = v;
102✔
160
                }
102✔
161
            }
194✔
162
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
285✔
163
            if (tasklet->is_conditional()) {
91✔
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
        }
91✔
178

179
        for (auto& oedge : dataflow.out_edges(*node)) {
483✔
180
            std::unordered_set<std::string> used;
198✔
181
            for (auto dim : oedge.subset()) {
387✔
182
                for (auto atom : symbolic::atoms(dim)) {
415✔
183
                    if (used.find(atom->get_name()) != used.end()) {
226✔
184
                        continue;
×
185
                    }
186
                    used.insert(atom->get_name());
226✔
187

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

338
        auto subgraph = this->traverse(loop_stmt->root());
7✔
339

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

447
    throw std::invalid_argument("Invalid control flow node type");
×
448
};
470✔
449

450
Users::Users(StructuredSDFG& sdfg)
103✔
451
    : Analysis(sdfg), node_(sdfg.root()) {
103✔
452

453
      };
103✔
454

455
Users::Users(StructuredSDFG& sdfg, structured_control_flow::ControlFlowNode& node)
×
456
    : Analysis(sdfg), node_(node) {
×
457

458
      };
×
459

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

472
    reads_.clear();
103✔
473
    writes_.clear();
103✔
474
    views_.clear();
103✔
475
    moves_.clear();
103✔
476

477
    this->traverse(node_);
103✔
478
    if (this->users_.empty()) {
103✔
479
        return;
×
480
    }
481

482
    // Require a single source
483
    for (auto& entry : this->users_) {
1,884✔
484
        if (boost::in_degree(entry.first, this->graph_) == 0) {
1,781✔
485
            if (this->source_ != nullptr) {
103✔
486
                throw InvalidSDFGException("Users Analysis: Non-unique source node");
×
487
            }
488
            this->source_ = entry.second.get();
103✔
489
        }
103✔
490
    }
491
    if (this->source_ == nullptr) {
103✔
492
        throw InvalidSDFGException("Users Analysis: No source node");
×
493
    }
494

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

514
        this->sink_ = this->users_.at(v).get();
×
515
    } else {
×
516
        this->sink_ = sinks.front();
103✔
517
    }
518

519
    // Collect sub structures
520
    for (auto& entry : this->users_) {
1,884✔
521
        auto container = entry.second->container();
1,781✔
522
        switch (entry.second->use()) {
1,781✔
523
            case Use::READ: {
524
                if (this->reads_.find(container) == this->reads_.end()) {
563✔
525
                    this->reads_.insert({container, {}});
262✔
526
                }
262✔
527
                this->reads_[container].push_back(entry.second.get());
563✔
528
                break;
563✔
529
            }
530
            case Use::WRITE: {
531
                if (this->writes_.find(container) == this->writes_.end()) {
284✔
532
                    this->writes_.insert({container, {}});
190✔
533
                }
190✔
534
                this->writes_[container].push_back(entry.second.get());
284✔
535
                break;
284✔
536
            }
537
            case Use::VIEW: {
538
                if (this->views_.find(container) == this->views_.end()) {
1✔
539
                    this->views_.insert({container, {}});
1✔
540
                }
1✔
541
                this->views_[container].push_back(entry.second.get());
1✔
542
                break;
1✔
543
            }
544
            case Use::MOVE: {
545
                if (this->moves_.find(container) == this->moves_.end()) {
1✔
546
                    this->moves_.insert({container, {}});
1✔
547
                }
1✔
548
                this->moves_[container].push_back(entry.second.get());
1✔
549
                break;
1✔
550
            }
551
            default:
552
                break;
932✔
553
        }
554
    }
1,781✔
555

556
    this->init_dom_tree();
103✔
557
};
103✔
558

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

568
    return us;
×
569
};
×
570

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

583
    return us;
352✔
584
};
352✔
585

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

595
    return us;
×
596
};
×
597

598
std::vector<User*> Users::writes(const std::string& container) const {
55✔
599
    if (this->writes_.find(container) == this->writes_.end()) {
55✔
600
        return {};
27✔
601
    } else {
602
        return this->writes_.at(container);
28✔
603
    }
604
};
55✔
605

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

615
    return us;
×
616
};
×
617

618
std::vector<User*> Users::reads(const std::string& container) const {
40✔
619
    if (this->reads_.find(container) == this->reads_.end()) {
40✔
620
        return {};
17✔
621
    } else {
622
        return this->reads_.at(container);
23✔
623
    }
624
};
40✔
625

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

635
    return us;
×
636
};
×
637

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

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

655
    return us;
×
656
};
×
657

658
std::vector<User*> Users::moves(const std::string& container) const {
60✔
659
    if (this->moves_.find(container) == this->moves_.end()) {
60✔
660
        return {};
59✔
661
    } else {
662
        return this->moves_.at(container);
1✔
663
    }
664
};
60✔
665

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

682
/****** Domination Analysis ******/
683

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

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

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

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

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

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

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

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

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

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

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

766
    return uses;
×
767
};
×
768

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

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

780
        // Stop conditions
781
        if (current == this->entry_) {
1,993✔
782
            continue;
131✔
783
        }
784

785
        if (visited.find(current) != visited.end()) {
1,862✔
786
            continue;
60✔
787
        }
788
        visited.insert(current);
1,802✔
789

790
        this->sub_users_.insert(current);
1,802✔
791

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

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

805
    for (auto& user : this->sub_users_) {
1,933✔
806
        this->sub_dom_tree_[user] = dom_tree[user];
1,802✔
807
    }
808
    this->sub_dom_tree_[this->entry_] = nullptr;
131✔
809

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

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

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

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

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

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

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

857
std::vector<User*> UsersView::writes(const std::string& container) const {
94✔
858
    std::vector<User*> us;
94✔
859
    for (auto& user : this->sub_users_) {
3,645✔
860
        if (user->container() != container) {
3,551✔
861
            continue;
3,414✔
862
        }
863
        if (user->use() != Use::WRITE) {
137✔
864
            continue;
59✔
865
        }
866
        us.push_back(user);
78✔
867
    }
868

869
    return us;
94✔
870
};
94✔
871

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

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

884
std::vector<User*> UsersView::reads(const std::string& container) const {
46✔
885
    std::vector<User*> us;
46✔
886
    for (auto& user : this->sub_users_) {
2,381✔
887
        if (user->container() != container) {
2,335✔
888
            continue;
2,243✔
889
        }
890
        if (user->use() != Use::READ) {
92✔
891
            continue;
50✔
892
        }
893
        us.push_back(user);
42✔
894
    }
895

896
    return us;
46✔
897
};
46✔
898

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

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

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

923
    return us;
×
924
};
×
925

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

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

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

950
    return us;
×
951
};
×
952

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

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

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

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

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

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

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

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

1012
    return uses;
×
1013
};
×
1014

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

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

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

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

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

1046
    return uses;
×
1047
};
×
1048

1049
User* Users::get_user(const std::string& container, Element* element, Use use, bool is_init,
209✔
1050
                      bool is_condition, bool is_update) {
1051
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element)) {
209✔
1052
        if (is_init) {
77✔
1053
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
18✔
1054
            return tmp;
18✔
1055
        } else if (is_condition) {
59✔
1056
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
29✔
1057
        } else if (is_update) {
30✔
1058
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
30✔
1059
        }
1060
    }
×
1061
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
132✔
1062
    return tmp;
132✔
1063
}
209✔
1064

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

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

1087
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
849✔
1088
        target_structure->insert({user_ptr->container(), {}});
507✔
1089
    }
507✔
1090
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
1,698✔
1091
        (*target_structure)[user_ptr->container()].end()) {
849✔
1092
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
783✔
1093
    }
783✔
1094
    target_structure->at(user_ptr->container())
849✔
1095
        .at(user_ptr->element())
849✔
1096
        .insert({user_ptr->use(), user_ptr});
849✔
1097
}
849✔
1098

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

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

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

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