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

daisytuner / sdfglib / 16439520920

22 Jul 2025 08:53AM UTC coverage: 65.927% (+0.8%) from 65.094%
16439520920

Pull #153

github

web-flow
Merge a0eea6968 into abe57c083
Pull Request #153: Restricts memlets to contiguous memory

211 of 300 new or added lines in 29 files covered. (70.33%)

66 existing lines in 7 files now uncovered.

8314 of 12611 relevant lines covered (65.93%)

128.5 hits per line

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

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

27
      };
1,651✔
28

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

32
      };
381✔
33

34
User::~User() {
3,689✔
35

36
};
3,689✔
37

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

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

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

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

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

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

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

72
ForUser::ForUser(
375✔
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) {
375✔
82

83
      };
375✔
84

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

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

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

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

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

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

118
std::pair<graph::Vertex, graph::Vertex> Users::traverse(data_flow::DataFlowGraph& dataflow) {
194✔
119
    graph::Vertex first = boost::graph_traits<graph::Graph>::null_vertex();
194✔
120
    graph::Vertex last = boost::graph_traits<graph::Graph>::null_vertex();
194✔
121
    for (auto node : dataflow.topological_sort()) {
499✔
122
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
305✔
123
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
199✔
124
                if (dataflow.in_degree(*node) > 0) {
199✔
125
                    Use use = Use::WRITE;
110✔
126

127
                    // Check if the pointer itself is moved (overwritten)
128
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
218✔
129
                        if (iedge.type() == data_flow::MemletType::Reference ||
219✔
130
                            iedge.type() == data_flow::MemletType::Dereference_Src) {
109✔
131
                            use = Use::MOVE;
2✔
132
                            break;
2✔
133
                        }
134
                    }
135

136
                    auto v = boost::add_vertex(this->graph_);
110✔
137
                    this->add_user(std::make_unique<User>(v, access_node->data(), access_node, &dataflow, use));
110✔
138

139
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
110✔
140
                        boost::add_edge(last, v, this->graph_);
85✔
141
                    } else {
85✔
142
                        first = v;
25✔
143
                    }
144
                    last = v;
110✔
145
                }
110✔
146
                if (dataflow.out_degree(*access_node) > 0) {
199✔
147
                    Use use = Use::READ;
93✔
148

149
                    // Check if the pointer itself is viewed (aliased)
150
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
184✔
151
                        if (oedge.type() == data_flow::MemletType::Reference ||
185✔
152
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
92✔
153
                            use = Use::VIEW;
2✔
154
                            break;
2✔
155
                        }
156
                    }
157

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

161
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
93✔
162
                        boost::add_edge(last, v, this->graph_);
24✔
163
                    } else {
24✔
164
                        first = v;
69✔
165
                    }
166
                    last = v;
93✔
167
                }
93✔
168
            }
199✔
169
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
305✔
170
            if (tasklet->is_conditional()) {
106✔
UNCOV
171
                auto& condition = tasklet->condition();
×
UNCOV
172
                for (auto& atom : symbolic::atoms(condition)) {
×
UNCOV
173
                    auto v = boost::add_vertex(this->graph_);
×
UNCOV
174
                    this->add_user(std::make_unique<User>(v, atom->get_name(), tasklet, &dataflow, Use::READ));
×
UNCOV
175
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
×
UNCOV
176
                        boost::add_edge(last, v, this->graph_);
×
UNCOV
177
                    } else {
×
UNCOV
178
                        first = v;
×
179
                    }
UNCOV
180
                    last = v;
×
181
                }
UNCOV
182
            }
×
183
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
106✔
184
            for (auto& symbol : library_node->symbols()) {
×
185
                auto v = boost::add_vertex(this->graph_);
×
186
                this->add_user(std::make_unique<User>(v, symbol->get_name(), library_node, &dataflow, Use::READ));
×
187
                if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
×
188
                    boost::add_edge(last, v, this->graph_);
×
189
                } else {
×
190
                    first = v;
×
191
                }
192
                last = v;
×
193
            }
194
        }
×
195

196
        for (auto& oedge : dataflow.out_edges(*node)) {
505✔
197
            std::unordered_set<std::string> used;
200✔
198
            for (auto dim : oedge.subset()) {
404✔
199
                for (auto atom : symbolic::atoms(dim)) {
382✔
200
                    if (used.find(atom->get_name()) != used.end()) {
178✔
201
                        continue;
×
202
                    }
203
                    used.insert(atom->get_name());
178✔
204

205
                    auto v = boost::add_vertex(this->graph_);
178✔
206
                    this->add_user(std::make_unique<User>(v, atom->get_name(), &oedge, &dataflow, Use::READ));
178✔
207
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
178✔
208
                        boost::add_edge(last, v, this->graph_);
168✔
209
                    } else {
168✔
210
                        first = v;
10✔
211
                    }
212
                    last = v;
178✔
213
                }
178✔
214
            }
204✔
215
        }
200✔
216
    }
217

218
    return {first, last};
194✔
219
};
×
220

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

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

233
        auto& dataflow = block_stmt->dataflow();
194✔
234
        auto subgraph = this->traverse(dataflow);
194✔
235

236
        // May be empty
237
        if (subgraph.first == boost::graph_traits<graph::Graph>::null_vertex()) {
194✔
238
            boost::add_edge(s, t, this->graph_);
90✔
239
            return {s, t};
90✔
240
        }
241

242
        boost::add_edge(s, subgraph.first, this->graph_);
104✔
243
        boost::add_edge(subgraph.second, t, this->graph_);
104✔
244

245
        return {s, t};
104✔
246
    } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(&node)) {
383✔
247
        auto s = boost::add_vertex(this->graph_);
264✔
248
        this->users_.insert({s, std::make_unique<User>(s, "", sequence_stmt, Use::NOP)});
264✔
249
        this->entries_.insert({sequence_stmt, this->users_.at(s).get()});
264✔
250

251
        graph::Vertex current = s;
264✔
252
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
581✔
253
            auto child = sequence_stmt->at(i);
317✔
254

255
            auto subgraph = this->traverse(child.first);
317✔
256
            boost::add_edge(current, subgraph.first, this->graph_);
317✔
257
            current = subgraph.second;
317✔
258

259
            std::unordered_set<std::string> used;
317✔
260
            for (auto& entry : child.second.assignments()) {
396✔
261
                for (auto atom : symbolic::atoms(entry.second)) {
105✔
262
                    if (symbolic::is_pointer(atom)) {
26✔
263
                        continue;
×
264
                    }
265
                    if (used.find(atom->get_name()) != used.end()) {
26✔
266
                        continue;
×
267
                    }
268
                    used.insert(atom->get_name());
26✔
269

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

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

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

282
                boost::add_edge(current, v, this->graph_);
79✔
283
                current = v;
79✔
284
            }
285
        }
317✔
286

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

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

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

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

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

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

317
                this->add_user(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)) {
119✔
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)) {
85✔
369
        // NOP
370
        auto s = boost::add_vertex(this->graph_);
77✔
371
        this->users_.insert({s, std::make_unique<User>(s, "", for_stmt, Use::NOP)});
77✔
372
        auto last = s;
77✔
373
        this->entries_.insert({for_stmt, this->users_.at(s).get()});
77✔
374

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

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

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

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

400
            boost::add_edge(last, v, this->graph_);
135✔
401
            boost::add_edge(v, t, this->graph_);
135✔
402
            last = v;
135✔
403
        }
135✔
404

405
        auto subgraph = this->traverse(for_stmt->root());
77✔
406
        boost::add_edge(last, subgraph.first, this->graph_);
77✔
407

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

417
        auto update_v = boost::add_vertex(this->graph_);
77✔
418
        this->add_user(std::make_unique<
77✔
419
                       ForUser>(update_v, for_stmt->indvar()->get_name(), for_stmt, Use::WRITE, false, false, true));
77✔
420

421
        if (end != boost::graph_traits<graph::Graph>::null_vertex()) {
77✔
422
            boost::add_edge(end, update_v, this->graph_);
77✔
423
        }
77✔
424
        end = update_v;
77✔
425

426
        if (end != boost::graph_traits<graph::Graph>::null_vertex()) {
77✔
427
            boost::add_edge(end, t, this->graph_);
77✔
428
        }
77✔
429

430
        // Back edge
431
        boost::add_edge(t, last, this->graph_);
77✔
432

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

456
    throw std::invalid_argument("Invalid control flow node type");
×
457
};
577✔
458

459
Users::Users(StructuredSDFG& sdfg)
130✔
460
    : Analysis(sdfg), node_(sdfg.root()) {
130✔
461

462
      };
130✔
463

464
Users::Users(StructuredSDFG& sdfg, structured_control_flow::ControlFlowNode& node)
×
465
    : Analysis(sdfg), node_(node) {
×
466

467
      };
×
468

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

481
    reads_.clear();
130✔
482
    writes_.clear();
130✔
483
    views_.clear();
130✔
484
    moves_.clear();
130✔
485

486
    this->traverse(node_);
130✔
487
    if (this->users_.empty()) {
130✔
488
        return;
×
489
    }
490

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

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

523
        this->sink_ = this->users_.at(v).get();
×
524
    } else {
×
525
        this->sink_ = sinks.front();
130✔
526
    }
527

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

565
    this->init_dom_tree();
130✔
566
};
130✔
567

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

577
    return us;
×
578
};
×
579

580
std::vector<User*> Users::uses(const std::string& container) const {
36✔
581
    std::vector<User*> us;
36✔
582
    for (auto& entry : this->users_) {
751✔
583
        if (entry.second->container() != container) {
715✔
584
            continue;
526✔
585
        }
586
        if (entry.second->use() == Use::NOP) {
189✔
587
            continue;
×
588
        }
589
        us.push_back(entry.second.get());
189✔
590
    }
591

592
    return us;
36✔
593
};
36✔
594

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

604
    return us;
×
605
};
×
606

607
std::vector<User*> Users::writes(const std::string& container) const {
48✔
608
    if (this->writes_.find(container) == this->writes_.end()) {
48✔
609
        return {};
25✔
610
    } else {
611
        return this->writes_.at(container);
23✔
612
    }
613
};
48✔
614

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

624
    return us;
×
625
};
×
626

627
std::vector<User*> Users::reads(const std::string& container) const {
33✔
628
    if (this->reads_.find(container) == this->reads_.end()) {
33✔
629
        return {};
13✔
630
    } else {
631
        return this->reads_.at(container);
20✔
632
    }
633
};
33✔
634

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

644
    return us;
×
645
};
×
646

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

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

664
    return us;
×
665
};
×
666

667
std::vector<User*> Users::moves(const std::string& container) const {
57✔
668
    if (this->moves_.find(container) == this->moves_.end()) {
57✔
669
        return {};
55✔
670
    } else {
671
        return this->moves_.at(container);
2✔
672
    }
673
};
57✔
674

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

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

691
bool Users::dominates(User& user1, User& user) {
34✔
692
    auto dominator = this->dom_tree_.at(&user);
34✔
693
    while (dominator != nullptr) {
117✔
694
        if (dominator == &user1) {
110✔
695
            return true;
27✔
696
        }
697
        dominator = this->dom_tree_.at(dominator);
83✔
698
    }
699
    return false;
7✔
700
};
34✔
701

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

713
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
15✔
714
    std::unordered_set<User*> uses;
15✔
715
    std::unordered_set<User*> visited;
15✔
716
    std::list<User*> queue = {&user2};
15✔
717
    while (!queue.empty()) {
96✔
718
        auto current = queue.front();
81✔
719
        queue.pop_front();
81✔
720

721
        // Stop conditions
722
        if (current == &user1) {
81✔
723
            continue;
15✔
724
        }
725

726
        if (visited.find(current) != visited.end()) {
66✔
727
            continue;
2✔
728
        }
729
        visited.insert(current);
64✔
730

731
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
64✔
732
            uses.insert(current);
10✔
733
        }
10✔
734

735
        // Extend search
736
        // Backward search to utilize domination user1 over user
737
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
64✔
738
        auto edges = std::ranges::subrange(eb, ee);
128✔
739
        for (auto edge : edges) {
130✔
740
            auto v = boost::source(edge, this->graph_);
66✔
741
            queue.push_back(this->users_.at(v).get());
66✔
742
        }
743
    }
744

745
    return uses;
15✔
746
};
15✔
747

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

760
        if (current != &user && current->use() != Use::NOP) {
×
761
            uses.insert(current);
×
762
        }
×
763

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

773
    return uses;
×
774
};
×
775

776
UsersView::UsersView(Users& users, structured_control_flow::ControlFlowNode& node) : users_(users) {
79✔
777
    this->entry_ = users.entries_.at(&node);
79✔
778
    this->exit_ = users.exits_.at(&node);
79✔
779

780
    // Collect sub users
781
    std::unordered_set<User*> visited;
79✔
782
    std::list<User*> queue = {this->exit_};
79✔
783
    while (!queue.empty()) {
638✔
784
        auto current = queue.front();
559✔
785
        queue.pop_front();
559✔
786

787
        // Stop conditions
788
        if (current == this->entry_) {
559✔
789
            continue;
79✔
790
        }
791

792
        if (visited.find(current) != visited.end()) {
480✔
793
            continue;
24✔
794
        }
795
        visited.insert(current);
456✔
796

797
        this->sub_users_.insert(current);
456✔
798

799
        // Extend search
800
        // Backward search to utilize domination user1 over user
801
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
456✔
802
        auto edges = std::ranges::subrange(eb, ee);
912✔
803
        for (auto edge : edges) {
936✔
804
            auto v = boost::source(edge, users.graph_);
480✔
805
            queue.push_back(users.users_.at(v).get());
480✔
806
        }
807
    }
808

809
    // Collect sub dom tree
810
    auto& dom_tree = this->users_.dom_tree_;
79✔
811

812
    for (auto& user : this->sub_users_) {
535✔
813
        this->sub_dom_tree_[user] = dom_tree[user];
456✔
814
    }
815
    this->sub_dom_tree_[this->entry_] = nullptr;
79✔
816

817
    // Collect sub post dom tree
818
    auto& pdom_tree = this->users_.pdom_tree_;
79✔
819
    for (auto& user : this->sub_users_) {
535✔
820
        this->sub_pdom_tree_[user] = pdom_tree[user];
456✔
821
    }
822
    this->sub_pdom_tree_[this->exit_] = nullptr;
79✔
823
};
79✔
824

825
std::vector<User*> UsersView::uses() const {
20✔
826
    std::vector<User*> us;
20✔
827
    for (auto& user : this->sub_users_) {
165✔
828
        if (user->use() == Use::NOP) {
145✔
829
            continue;
92✔
830
        }
831
        us.push_back(user);
53✔
832
    }
833

834
    return us;
20✔
835
};
20✔
836

837
std::vector<User*> UsersView::uses(const std::string& container) const {
42✔
838
    std::vector<User*> us;
42✔
839
    for (auto& user : this->sub_users_) {
459✔
840
        if (user->container() != container) {
417✔
841
            continue;
305✔
842
        }
843
        if (user->use() == Use::NOP) {
112✔
844
            continue;
×
845
        }
846
        us.push_back(user);
112✔
847
    }
848

849
    return us;
42✔
850
};
42✔
851

852
std::vector<User*> UsersView::writes() const {
6✔
853
    std::vector<User*> us;
6✔
854
    for (auto& user : this->sub_users_) {
54✔
855
        if (user->use() != Use::WRITE) {
48✔
856
            continue;
46✔
857
        }
858
        us.push_back(user);
2✔
859
    }
860

861
    return us;
6✔
862
};
6✔
863

864
std::vector<User*> UsersView::writes(const std::string& container) const {
46✔
865
    std::vector<User*> us;
46✔
866
    for (auto& user : this->sub_users_) {
345✔
867
        if (user->container() != container) {
299✔
868
            continue;
279✔
869
        }
870
        if (user->use() != Use::WRITE) {
20✔
871
            continue;
3✔
872
        }
873
        us.push_back(user);
17✔
874
    }
875

876
    return us;
46✔
877
};
46✔
878

UNCOV
879
std::vector<User*> UsersView::reads() const {
×
UNCOV
880
    std::vector<User*> us;
×
UNCOV
881
    for (auto& user : this->sub_users_) {
×
UNCOV
882
        if (user->use() != Use::READ) {
×
UNCOV
883
            continue;
×
884
        }
UNCOV
885
        us.push_back(user);
×
886
    }
887

UNCOV
888
    return us;
×
UNCOV
889
};
×
890

891
std::vector<User*> UsersView::reads(const std::string& container) const {
7✔
892
    std::vector<User*> us;
7✔
893
    for (auto& user : this->sub_users_) {
189✔
894
        if (user->container() != container) {
182✔
895
            continue;
174✔
896
        }
897
        if (user->use() != Use::READ) {
8✔
898
            continue;
5✔
899
        }
900
        us.push_back(user);
3✔
901
    }
902

903
    return us;
7✔
904
};
7✔
905

UNCOV
906
std::vector<User*> UsersView::views() const {
×
UNCOV
907
    std::vector<User*> us;
×
UNCOV
908
    for (auto& user : this->sub_users_) {
×
UNCOV
909
        if (user->use() != Use::VIEW) {
×
UNCOV
910
            continue;
×
911
        }
912
        us.push_back(user);
×
913
    }
914

UNCOV
915
    return us;
×
UNCOV
916
};
×
917

918
std::vector<User*> UsersView::views(const std::string& container) const {
7✔
919
    std::vector<User*> us;
7✔
920
    for (auto& user : this->sub_users_) {
189✔
921
        if (user->container() != container) {
182✔
922
            continue;
174✔
923
        }
924
        if (user->use() != Use::VIEW) {
8✔
925
            continue;
8✔
926
        }
927
        us.push_back(user);
×
928
    }
929

930
    return us;
7✔
931
};
7✔
932

UNCOV
933
std::vector<User*> UsersView::moves() const {
×
UNCOV
934
    std::vector<User*> us;
×
UNCOV
935
    for (auto& user : this->sub_users_) {
×
UNCOV
936
        if (user->use() != Use::MOVE) {
×
UNCOV
937
            continue;
×
938
        }
939
        us.push_back(user);
×
940
    }
941

UNCOV
942
    return us;
×
UNCOV
943
};
×
944

945
std::vector<User*> UsersView::moves(const std::string& container) const {
7✔
946
    std::vector<User*> us;
7✔
947
    for (auto& user : this->sub_users_) {
189✔
948
        if (user->container() != container) {
182✔
949
            continue;
174✔
950
        }
951
        if (user->use() != Use::MOVE) {
8✔
952
            continue;
8✔
953
        }
954
        us.push_back(user);
×
955
    }
956

957
    return us;
7✔
958
};
7✔
959

960
bool UsersView::dominates(User& user1, User& user) {
×
961
    auto dominator = this->sub_dom_tree_[&user];
×
962
    while (dominator != nullptr) {
×
963
        if (dominator == &user1) {
×
964
            return true;
×
965
        }
966
        dominator = this->sub_dom_tree_[dominator];
×
967
    }
968
    return false;
×
969
};
×
970

971
bool UsersView::post_dominates(User& user1, User& user) {
×
972
    auto dominator = this->sub_pdom_tree_[&user];
×
973
    while (dominator != nullptr) {
×
974
        if (dominator == &user1) {
×
975
            return true;
×
976
        }
977
        dominator = this->sub_pdom_tree_[dominator];
×
978
    }
979
    return false;
×
980
};
×
981

982
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
983
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
984
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
985

986
    std::unordered_set<User*> uses;
×
987
    std::unordered_set<User*> visited;
×
988
    std::list<User*> queue = {&user1};
×
989
    while (!queue.empty()) {
×
990
        auto current = queue.front();
×
991
        queue.pop_front();
×
992
        if (visited.find(current) != visited.end()) {
×
993
            continue;
×
994
        }
995
        visited.insert(current);
×
996

997
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
998
            uses.insert(current);
×
999
        }
×
1000

1001
        // Stop conditions
1002
        if (current == exit_) {
×
1003
            continue;
×
1004
        }
1005

1006
        if (current == &user2) {
×
1007
            continue;
×
1008
        }
1009

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

1019
    return uses;
×
1020
};
×
1021

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

1025
    std::unordered_set<User*> uses;
×
1026
    std::unordered_set<User*> visited;
×
1027
    std::list<User*> queue = {&user};
×
1028
    while (!queue.empty()) {
×
1029
        auto current = queue.front();
×
1030
        queue.pop_front();
×
1031
        if (visited.find(current) != visited.end()) {
×
1032
            continue;
×
1033
        }
1034
        visited.insert(current);
×
1035

1036
        if (current != &user && current->use() != Use::NOP) {
×
1037
            uses.insert(current);
×
1038
        }
×
1039

1040
        if (current == exit_) {
×
1041
            continue;
×
1042
        }
1043

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

1053
    return uses;
×
1054
};
×
1055

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

1072
void Users::add_user(std::unique_ptr<User> user) {
886✔
1073
    auto vertex = user->vertex_;
886✔
1074
    this->users_.insert({vertex, std::move(user)});
886✔
1075

1076
    auto user_ptr = this->users_.at(vertex).get();
886✔
1077
    auto* target_structure = &users_by_sdfg_;
886✔
1078
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
886✔
1079
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
375✔
1080
        if (for_loop == nullptr) {
375✔
1081
            throw std::invalid_argument("Invalid user type");
×
1082
        }
1083
        if (for_user->is_init()) {
375✔
1084
            target_structure = &users_by_sdfg_loop_init_;
86✔
1085
        } else if (for_user->is_condition()) {
375✔
1086
            target_structure = &users_by_sdfg_loop_condition_;
135✔
1087
        } else if (for_user->is_update()) {
289✔
1088
            target_structure = &users_by_sdfg_loop_update_;
154✔
1089
        } else {
154✔
1090
            throw std::invalid_argument("Invalid user type");
×
1091
        }
1092
    }
375✔
1093

1094
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
886✔
1095
        target_structure->insert({user_ptr->container(), {}});
567✔
1096
    }
567✔
1097
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
1,772✔
1098
        (*target_structure)[user_ptr->container()].end()) {
886✔
1099
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
804✔
1100
    }
804✔
1101
    target_structure->at(user_ptr->container()).at(user_ptr->element()).insert({user_ptr->use(), user_ptr});
886✔
1102
}
886✔
1103

1104
std::unordered_set<std::string> Users::locals(structured_control_flow::ControlFlowNode& node) {
20✔
1105
    auto& sdfg = this->sdfg_;
20✔
1106

1107
    // Locals have no uses outside of the node
1108
    // We can check this by comparing the number of uses of the container in the view and the total
1109
    // number of uses of the container in the users map.
1110
    std::unordered_set<std::string> locals;
20✔
1111
    UsersView view(*this, node);
20✔
1112
    for (auto& user : view.uses()) {
73✔
1113
        if (!sdfg.is_transient(user->container())) {
53✔
1114
            continue;
19✔
1115
        }
1116
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
34✔
1117
            locals.insert(user->container());
11✔
1118
        }
11✔
1119
    }
1120

1121
    return locals;
20✔
1122
};
20✔
1123

1124
} // namespace analysis
1125
} // 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