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

daisytuner / docc / 26520678771

27 May 2026 03:22PM UTC coverage: 60.864% (-0.02%) from 60.886%
26520678771

Pull #719

github

web-flow
Merge 99c5e4f9d into 707dadcf8
Pull Request #719: Libnode ptr edges

961 of 1749 new or added lines in 52 files covered. (54.95%)

90 existing lines in 29 files now uncovered.

35222 of 57870 relevant lines covered (60.86%)

11043.61 hits per line

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

77.5
/sdfg/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)
25
    : vertex_(vertex), container_(container), use_(use), element_(element) {
23,967✔
26

27
      };
23,967✔
28

29
Use User::use() const { return this->use_; };
114,227✔
30

31
std::string& User::container() { return this->container_; };
721,967✔
32

33
Element* User::element() { return this->element_; };
53,782✔
34

35
const std::vector<data_flow::Subset>& User::subsets() const {
1,619✔
36
    if (this->subsets_cached_) {
1,619✔
37
        return this->subsets_;
763✔
38
    }
763✔
39

40
    if (this->container_ == "") {
856✔
41
        // No-op user
42
    } else if (auto access_node = dynamic_cast<data_flow::AccessNode*>(this->element_)) {
856✔
43
        auto& graph = access_node->get_parent();
591✔
44
        if (this->use_ == Use::READ || this->use_ == Use::VIEW) {
591✔
45
            for (auto& iedge : graph.out_edges(*access_node)) {
287✔
46
                this->subsets_.push_back(iedge.subset());
287✔
47
            }
287✔
48
        } else if (this->use_ == Use::WRITE || this->use_ == Use::MOVE) {
324✔
49
            for (auto& oedge : graph.in_edges(*access_node)) {
324✔
50
                this->subsets_.push_back(oedge.subset());
324✔
51
            }
324✔
52
        }
324✔
53
    } else {
591✔
54
        // Use of symbol
55
        this->subsets_.push_back({});
265✔
56
    }
265✔
57

58
    this->subsets_cached_ = true;
856✔
59
    return this->subsets_;
856✔
60
};
1,619✔
61

62
ForUser::ForUser(
63
    graph::Vertex vertex,
64
    const std::string& container,
65
    Element* element,
66
    Use use,
67
    bool is_init,
68
    bool is_condition,
69
    bool is_update
70
)
71
    : User(vertex, container, element, use), is_init_(is_init), is_condition_(is_condition), is_update_(is_update) {
6,793✔
72

73
      };
6,793✔
74

75
bool ForUser::is_init() const { return this->is_init_; };
6,797✔
76

77
bool ForUser::is_condition() const { return this->is_condition_; };
5,248✔
78

79
bool ForUser::is_update() const { return this->is_update_; };
2,655✔
80

81
std::pair<graph::Vertex, graph::Vertex> Users::traverse(data_flow::DataFlowGraph& dataflow) {
1,624✔
82
    graph::Vertex first = boost::graph_traits<graph::Graph>::null_vertex();
1,624✔
83
    graph::Vertex last = boost::graph_traits<graph::Graph>::null_vertex();
1,624✔
84
    for (auto node : dataflow.topological_sort()) {
4,641✔
85
        if (dynamic_cast<data_flow::ConstantNode*>(node) != nullptr) {
4,641✔
86
            continue;
219✔
87
        }
219✔
88

89
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
4,422✔
90
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
3,105✔
91
                if (dataflow.in_degree(*node) > 0) {
3,105✔
92
                    Use use = Use::WRITE;
1,305✔
93

94
                    // Check if the pointer itself is moved (overwritten)
95
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
1,305✔
96
                        if (iedge.type() == data_flow::MemletType::Reference ||
1,305✔
97
                            iedge.type() == data_flow::MemletType::Dereference_Src) {
1,305✔
98
                            use = Use::MOVE;
91✔
99
                            break;
91✔
100
                        }
91✔
101
                    }
1,305✔
102

103
                    auto v = boost::add_vertex(this->graph_);
1,305✔
104
                    this->add_user(std::make_unique<User>(v, access_node->data(), access_node, use));
1,305✔
105

106
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
1,305✔
107
                        boost::add_edge(last, v, this->graph_);
1,204✔
108
                    } else {
1,204✔
109
                        first = v;
101✔
110
                    }
101✔
111
                    last = v;
1,305✔
112
                }
1,305✔
113
                if (dataflow.out_degree(*access_node) > 0) {
3,105✔
114
                    Use use = Use::READ;
1,853✔
115

116
                    // Check if the pointer itself is viewed (aliased)
117
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
1,951✔
118
                        if (oedge.type() == data_flow::MemletType::Reference ||
1,951✔
119
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
1,951✔
120
                            use = Use::VIEW;
88✔
121
                            break;
88✔
122
                        }
88✔
123
                    }
1,951✔
124

125
                    auto v = boost::add_vertex(this->graph_);
1,853✔
126
                    this->add_user(std::make_unique<User>(v, access_node->data(), access_node, use));
1,853✔
127

128
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
1,853✔
129
                        boost::add_edge(last, v, this->graph_);
782✔
130
                    } else {
1,071✔
131
                        first = v;
1,071✔
132
                    }
1,071✔
133
                    last = v;
1,853✔
134
                }
1,853✔
135
            }
3,105✔
136
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
3,105✔
137
            for (auto& symbol : library_node->symbols()) {
222✔
138
                auto v = boost::add_vertex(this->graph_);
21✔
139
                this->add_user(std::make_unique<User>(v, symbol->get_name(), library_node, Use::READ));
21✔
140
                if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
21✔
141
                    boost::add_edge(last, v, this->graph_);
12✔
142
                } else {
12✔
143
                    first = v;
9✔
144
                }
9✔
145
                last = v;
21✔
146
            }
21✔
147
        }
222✔
148

149
        for (auto& oedge : dataflow.out_edges(*node)) {
4,422✔
150
            std::unordered_set<std::string> used;
3,160✔
151
            for (auto dim : oedge.subset()) {
3,160✔
152
                for (auto atom : symbolic::atoms(dim)) {
3,465✔
153
                    if (used.find(atom->get_name()) != used.end()) {
3,465✔
154
                        continue;
33✔
155
                    }
33✔
156
                    used.insert(atom->get_name());
3,432✔
157

158
                    auto v = boost::add_vertex(this->graph_);
3,432✔
159
                    this->add_user(std::make_unique<User>(v, atom->get_name(), &oedge, Use::READ));
3,432✔
160
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
3,432✔
161
                        boost::add_edge(last, v, this->graph_);
3,307✔
162
                    } else {
3,307✔
163
                        first = v;
125✔
164
                    }
125✔
165
                    last = v;
3,432✔
166
                }
3,432✔
167
            }
2,975✔
168
        }
3,160✔
169
    }
4,422✔
170

171
    return {first, last};
1,624✔
172
};
1,624✔
173

174
std::pair<graph::Vertex, graph::Vertex> Users::traverse(structured_control_flow::ControlFlowNode& node) {
5,088✔
175
    if (auto block_stmt = dynamic_cast<structured_control_flow::Block*>(&node)) {
5,088✔
176
        // NOP
177
        auto s = boost::add_vertex(this->graph_);
1,624✔
178
        this->users_.insert({s, std::make_unique<User>(s, "", block_stmt, Use::NOP)});
1,624✔
179
        this->entries_.insert({block_stmt, this->users_.at(s).get()});
1,624✔
180

181
        // NOP
182
        auto t = boost::add_vertex(this->graph_);
1,624✔
183
        this->users_.insert({t, std::make_unique<User>(t, "", block_stmt, Use::NOP)});
1,624✔
184
        this->exits_.insert({block_stmt, this->users_.at(t).get()});
1,624✔
185

186
        auto& dataflow = block_stmt->dataflow();
1,624✔
187
        auto subgraph = this->traverse(dataflow);
1,624✔
188

189
        // May be empty
190
        if (subgraph.first == boost::graph_traits<graph::Graph>::null_vertex()) {
1,624✔
191
            boost::add_edge(s, t, this->graph_);
318✔
192
            return {s, t};
318✔
193
        }
318✔
194

195
        boost::add_edge(s, subgraph.first, this->graph_);
1,306✔
196
        boost::add_edge(subgraph.second, t, this->graph_);
1,306✔
197

198
        return {s, t};
1,306✔
199
    } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(&node)) {
3,464✔
200
        auto s = boost::add_vertex(this->graph_);
2,040✔
201
        this->users_.insert({s, std::make_unique<User>(s, "", sequence_stmt, Use::NOP)});
2,040✔
202
        this->entries_.insert({sequence_stmt, this->users_.at(s).get()});
2,040✔
203

204
        graph::Vertex current = s;
2,040✔
205
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
5,082✔
206
            auto child = sequence_stmt->at(i);
3,054✔
207

208
            auto subgraph = this->traverse(child.first);
3,054✔
209
            boost::add_edge(current, subgraph.first, this->graph_);
3,054✔
210
            // Return node
211
            if (subgraph.second == boost::graph_traits<graph::Graph>::null_vertex()) {
3,054✔
212
                break;
12✔
213
            }
12✔
214
            current = subgraph.second;
3,042✔
215

216
            std::unordered_set<std::string> used;
3,042✔
217
            for (auto& entry : child.second.assignments()) {
3,042✔
218
                for (auto atom : symbolic::atoms(entry.second)) {
220✔
219
                    if (symbolic::is_pointer(atom)) {
117✔
220
                        continue;
×
221
                    }
×
222
                    if (used.find(atom->get_name()) != used.end()) {
117✔
223
                        continue;
8✔
224
                    }
8✔
225
                    used.insert(atom->get_name());
109✔
226

227
                    auto v = boost::add_vertex(this->graph_);
109✔
228
                    this->add_user(std::make_unique<User>(v, atom->get_name(), &child.second, Use::READ));
109✔
229

230
                    boost::add_edge(current, v, this->graph_);
109✔
231
                    current = v;
109✔
232
                }
109✔
233
            }
220✔
234

235
            for (auto& entry : child.second.assignments()) {
3,042✔
236
                auto v = boost::add_vertex(this->graph_);
220✔
237
                this->add_user(std::make_unique<User>(v, entry.first->get_name(), &child.second, Use::WRITE));
220✔
238

239
                boost::add_edge(current, v, this->graph_);
220✔
240
                current = v;
220✔
241
            }
220✔
242
        }
3,042✔
243

244
        if (current == boost::graph_traits<graph::Graph>::null_vertex()) {
2,040✔
245
            this->exits_.insert({sequence_stmt, this->users_.at(s).get()});
×
246
            return {s, current};
×
247
        }
×
248

249
        auto t = boost::add_vertex(this->graph_);
2,040✔
250
        this->users_.insert({t, std::make_unique<User>(t, "", sequence_stmt, Use::NOP)});
2,040✔
251
        boost::add_edge(current, t, this->graph_);
2,040✔
252
        this->exits_.insert({sequence_stmt, this->users_.at(t).get()});
2,040✔
253

254
        return {s, t};
2,040✔
255
    } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(&node)) {
2,040✔
256
        // NOP
257
        auto s = boost::add_vertex(this->graph_);
57✔
258
        this->users_.insert({s, std::make_unique<User>(s, "", if_else_stmt, Use::NOP)});
57✔
259
        this->entries_.insert({if_else_stmt, this->users_.at(s).get()});
57✔
260

261
        graph::Vertex last = s;
57✔
262

263
        std::unordered_set<std::string> used;
57✔
264
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
140✔
265
            auto condition = if_else_stmt->at(i).second;
83✔
266
            for (auto atom : symbolic::atoms(condition)) {
86✔
267
                if (used.find(atom->get_name()) != used.end()) {
86✔
268
                    continue;
22✔
269
                }
22✔
270
                used.insert(atom->get_name());
64✔
271

272
                auto v = boost::add_vertex(this->graph_);
64✔
273

274
                this->add_user(std::make_unique<User>(v, atom->get_name(), if_else_stmt, Use::READ));
64✔
275

276
                boost::add_edge(last, v, this->graph_);
64✔
277
                last = v;
64✔
278
            }
64✔
279
        }
83✔
280

281
        graph::Vertex t = boost::graph_traits<graph::Graph>::null_vertex();
57✔
282
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
140✔
283
            auto branch = if_else_stmt->at(i);
83✔
284
            auto subgraph = this->traverse(branch.first);
83✔
285
            boost::add_edge(last, subgraph.first, this->graph_);
83✔
286
            if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
83✔
287
                if (t == boost::graph_traits<graph::Graph>::null_vertex()) {
83✔
288
                    t = boost::add_vertex(this->graph_);
57✔
289
                    this->users_.insert({t, std::make_unique<User>(t, "", if_else_stmt, Use::NOP)});
57✔
290
                    this->exits_.insert({if_else_stmt, this->users_.at(t).get()});
57✔
291
                }
57✔
292
                boost::add_edge(subgraph.second, t, this->graph_);
83✔
293
            }
83✔
294
        }
83✔
295

296
        // Forward edge: Potentially missing else case
297
        if (!if_else_stmt->is_complete()) {
57✔
298
            if (t == boost::graph_traits<graph::Graph>::null_vertex()) {
31✔
299
                t = boost::add_vertex(this->graph_);
×
300
                this->users_.insert({t, std::make_unique<User>(t, "", if_else_stmt, Use::NOP)});
×
301
                this->exits_.insert({if_else_stmt, this->users_.at(t).get()});
×
302
            }
×
303
            boost::add_edge(last, t, this->graph_);
31✔
304
        }
31✔
305

306
        return {s, t};
57✔
307
    } else if (auto loop_stmt = dynamic_cast<structured_control_flow::While*>(&node)) {
1,367✔
308
        // NOP
309
        auto s = boost::add_vertex(this->graph_);
21✔
310
        this->users_.insert({s, std::make_unique<User>(s, "", loop_stmt, Use::NOP)});
21✔
311
        this->entries_.insert({loop_stmt, this->users_.at(s).get()});
21✔
312

313
        auto subgraph = this->traverse(loop_stmt->root());
21✔
314

315
        // NOP
316
        auto t = boost::add_vertex(this->graph_);
21✔
317
        this->users_.insert({t, std::make_unique<User>(t, "", loop_stmt, Use::NOP)});
21✔
318
        this->exits_.insert({loop_stmt, this->users_.at(t).get()});
21✔
319

320
        boost::add_edge(s, subgraph.first, this->graph_);
21✔
321
        if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
21✔
322
            boost::add_edge(subgraph.second, t, this->graph_);
21✔
323
        }
21✔
324

325
        // Empty loop
326
        boost::add_edge(s, t, this->graph_);
21✔
327
        // Back edge
328
        boost::add_edge(t, s, this->graph_);
21✔
329

330
        return {s, t};
21✔
331
    } else if (auto for_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(&node)) {
1,346✔
332
        // NOP
333
        auto s = boost::add_vertex(this->graph_);
1,325✔
334
        this->users_.insert({s, std::make_unique<User>(s, "", for_stmt, Use::NOP)});
1,325✔
335
        auto last = s;
1,325✔
336
        this->entries_.insert({for_stmt, this->users_.at(s).get()});
1,325✔
337

338
        // NOP
339
        auto t = boost::add_vertex(this->graph_);
1,325✔
340
        this->users_.insert({t, std::make_unique<User>(t, "", for_stmt, Use::NOP)});
1,325✔
341
        this->exits_.insert({for_stmt, this->users_.at(t).get()});
1,325✔
342

343
        // Init
344
        for (auto atom : symbolic::atoms(for_stmt->init())) {
1,325✔
345
            auto v = boost::add_vertex(this->graph_);
223✔
346
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ, true, false, false));
223✔
347
            boost::add_edge(last, v, this->graph_);
223✔
348
            last = v;
223✔
349
        }
223✔
350
        // Indvar
351
        auto v = boost::add_vertex(this->graph_);
1,325✔
352
        this->add_user(std::make_unique<
1,325✔
353
                       ForUser>(v, for_stmt->indvar()->get_name(), for_stmt, Use::WRITE, true, false, false));
1,325✔
354

355
        boost::add_edge(last, v, this->graph_);
1,325✔
356
        last = v;
1,325✔
357

358
        // Condition
359
        for (auto atom : symbolic::atoms(for_stmt->condition())) {
2,595✔
360
            auto v = boost::add_vertex(this->graph_);
2,595✔
361
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ, false, true, false));
2,595✔
362

363
            boost::add_edge(last, v, this->graph_);
2,595✔
364
            boost::add_edge(v, t, this->graph_);
2,595✔
365
            last = v;
2,595✔
366
        }
2,595✔
367

368
        auto subgraph = this->traverse(for_stmt->root());
1,325✔
369
        boost::add_edge(last, subgraph.first, this->graph_);
1,325✔
370

371
        // Update
372
        auto end = subgraph.second;
1,325✔
373
        for (auto atom : symbolic::atoms(for_stmt->update())) {
1,325✔
374
            auto v = boost::add_vertex(this->graph_);
1,325✔
375
            this->add_user(std::make_unique<ForUser>(v, atom->get_name(), for_stmt, Use::READ, false, false, true));
1,325✔
376
            boost::add_edge(end, v, this->graph_);
1,325✔
377
            end = v;
1,325✔
378
        }
1,325✔
379

380
        auto update_v = boost::add_vertex(this->graph_);
1,325✔
381
        this->add_user(std::make_unique<
1,325✔
382
                       ForUser>(update_v, for_stmt->indvar()->get_name(), for_stmt, Use::WRITE, false, false, true));
1,325✔
383

384
        if (end != boost::graph_traits<graph::Graph>::null_vertex()) {
1,325✔
385
            boost::add_edge(end, update_v, this->graph_);
1,325✔
386
        }
1,325✔
387
        end = update_v;
1,325✔
388

389
        if (end != boost::graph_traits<graph::Graph>::null_vertex()) {
1,325✔
390
            boost::add_edge(end, t, this->graph_);
1,325✔
391
        }
1,325✔
392

393
        // Back edge
394
        boost::add_edge(t, last, this->graph_);
1,325✔
395

396
        return {s, t};
1,325✔
397
    } else if (auto cont_stmt = dynamic_cast<structured_control_flow::Continue*>(&node)) {
1,325✔
398
        // Approximated by general back edge in loop scope
399
        auto v = boost::add_vertex(this->graph_);
4✔
400
        this->users_.insert({v, std::make_unique<User>(v, "", cont_stmt, Use::NOP)});
4✔
401
        this->entries_.insert({cont_stmt, this->users_.at(v).get()});
4✔
402
        this->exits_.insert({cont_stmt, this->users_.at(v).get()});
4✔
403
        return {v, v};
4✔
404
    } else if (auto br_stmt = dynamic_cast<structured_control_flow::Break*>(&node)) {
17✔
405
        // Approximated by general back edge in loop scope
406
        auto v = boost::add_vertex(this->graph_);
5✔
407
        this->users_.insert({v, std::make_unique<User>(v, "", br_stmt, Use::NOP)});
5✔
408
        this->entries_.insert({br_stmt, this->users_.at(v).get()});
5✔
409
        this->exits_.insert({br_stmt, this->users_.at(v).get()});
5✔
410
        return {v, v};
5✔
411
    } else if (auto ret_stmt = dynamic_cast<structured_control_flow::Return*>(&node)) {
12✔
412
        if (!ret_stmt->is_data() || ret_stmt->data().empty()) {
12✔
413
            auto v = boost::add_vertex(this->graph_);
×
414
            this->users_.insert({v, std::make_unique<User>(v, "", ret_stmt, Use::NOP)});
×
415
            this->entries_.insert({ret_stmt, this->users_.at(v).get()});
×
416
            this->exits_.insert({ret_stmt, this->users_.at(v).get()});
×
417
            return {v, boost::graph_traits<graph::Graph>::null_vertex()};
×
418
        } else {
12✔
419
            auto v = boost::add_vertex(this->graph_);
12✔
420
            this->add_user(std::make_unique<User>(v, ret_stmt->data(), ret_stmt, Use::READ));
12✔
421
            this->entries_.insert({ret_stmt, this->users_.at(v).get()});
12✔
422
            this->exits_.insert({ret_stmt, this->users_.at(v).get()});
12✔
423
            return {v, boost::graph_traits<graph::Graph>::null_vertex()};
12✔
424
        }
12✔
425
    }
12✔
426

427
    throw std::invalid_argument("Invalid control flow node type");
×
428
};
5,088✔
429

430
Users::Users(StructuredSDFG& sdfg)
431
    : Analysis(sdfg), node_(sdfg.root()) {
605✔
432

433
      };
605✔
434

435
Users::Users(StructuredSDFG& sdfg, structured_control_flow::ControlFlowNode& node)
436
    : Analysis(sdfg), node_(node) {
×
437

438
      };
×
439

440
void Users::run(analysis::AnalysisManager& analysis_manager) {
605✔
441
    users_.clear();
605✔
442
    graph_.clear();
605✔
443
    source_ = nullptr;
605✔
444
    sink_ = nullptr;
605✔
445
    this->entries_.clear();
605✔
446
    this->exits_.clear();
605✔
447
    this->users_lookup_.clear();
605✔
448

449
    reads_.clear();
605✔
450
    writes_.clear();
605✔
451
    views_.clear();
605✔
452
    moves_.clear();
605✔
453

454
    this->traverse(node_);
605✔
455
    if (this->users_.empty()) {
605✔
456
        return;
×
457
    }
×
458

459
    // Require a single source
460
    for (auto& entry : this->users_) {
23,952✔
461
        if (boost::in_degree(entry.first, this->graph_) == 0) {
23,952✔
462
            if (this->source_ != nullptr) {
605✔
463
                throw InvalidSDFGException("Users Analysis: Non-unique source node");
×
464
            }
×
465
            this->source_ = entry.second.get();
605✔
466
        }
605✔
467
    }
23,952✔
468
    if (this->source_ == nullptr) {
605✔
469
        throw InvalidSDFGException("Users Analysis: No source node");
×
470
    }
×
471

472
    std::list<User*> sinks;
605✔
473
    for (auto& entry : this->users_) {
23,952✔
474
        if (boost::out_degree(entry.first, this->graph_) == 0) {
23,952✔
475
            sinks.push_back(entry.second.get());
617✔
476
        }
617✔
477
    }
23,952✔
478
    if (sinks.size() == 0) {
605✔
479
        throw InvalidSDFGException("Users Analysis: No sink node");
×
480
    }
×
481
    if (sinks.size() > 1) {
605✔
482
        // add artificial sink
483
        auto v = boost::add_vertex(this->graph_);
11✔
484
        auto sink = std::make_unique<User>(v, "", nullptr, Use::NOP);
11✔
485
        this->users_.insert({v, std::move(sink)});
11✔
486
        for (auto sink : sinks) {
23✔
487
            boost::add_edge(sink->vertex_, v, this->graph_);
23✔
488
        }
23✔
489
        sinks.clear();
11✔
490

491
        this->sink_ = this->users_.at(v).get();
11✔
492
    } else {
594✔
493
        this->sink_ = sinks.front();
594✔
494
    }
594✔
495

496
    for (auto& container : sdfg_.containers()) {
3,823✔
497
        this->reads_.insert({container, {}});
3,823✔
498
        this->writes_.insert({container, {}});
3,823✔
499
        this->views_.insert({container, {}});
3,823✔
500
        this->moves_.insert({container, {}});
3,823✔
501
    }
3,823✔
502

503
    // Collect sub structures
504
    for (auto& entry : this->users_) {
23,963✔
505
        auto container = entry.second->container();
23,963✔
506
        if (entry.second->use() == Use::NOP) {
23,963✔
507
            continue;
10,154✔
508
        }
10,154✔
509
        if (container == "") {
13,809✔
510
            continue;
×
511
        }
×
512

513
        switch (entry.second->use()) {
13,809✔
514
            case Use::READ: {
9,546✔
515
                this->reads_[container].push_back(entry.second.get());
9,546✔
516
                break;
9,546✔
517
            }
×
518
            case Use::WRITE: {
4,084✔
519
                this->writes_[container].push_back(entry.second.get());
4,084✔
520
                break;
4,084✔
521
            }
×
522
            case Use::VIEW: {
88✔
523
                this->views_[container].push_back(entry.second.get());
88✔
524
                break;
88✔
525
            }
×
526
            case Use::MOVE: {
91✔
527
                this->moves_[container].push_back(entry.second.get());
91✔
528
                break;
91✔
529
            }
×
530
            default:
×
531
                break;
×
532
        }
13,809✔
533
    }
13,809✔
534
};
605✔
535

536
std::list<User*> Users::uses() const {
×
537
    std::list<User*> us;
×
538
    for (auto& entry : this->users_) {
×
539
        if (entry.second->use() == Use::NOP) {
×
540
            continue;
×
541
        }
×
542
        us.push_back(entry.second.get());
×
543
    }
×
544

545
    return us;
×
546
};
×
547

548
std::list<User*> Users::uses(const std::string& container) const {
2,217✔
549
    std::list<User*> us;
2,217✔
550
    for (auto& entry : this->users_) {
155,962✔
551
        if (entry.second->container() != container) {
155,962✔
552
            continue;
145,385✔
553
        }
145,385✔
554
        if (entry.second->use() == Use::NOP) {
10,577✔
555
            continue;
×
556
        }
×
557
        us.push_back(entry.second.get());
10,577✔
558
    }
10,577✔
559

560
    return us;
2,217✔
561
};
2,217✔
562

563
size_t Users::num_uses(const std::string& container) const { return this->uses(container).size(); };
×
564

UNCOV
565
std::list<User*> Users::writes() const {
×
UNCOV
566
    std::list<User*> us;
×
UNCOV
567
    for (auto& entry : this->users_) {
×
UNCOV
568
        if (entry.second->use() != Use::WRITE) {
×
UNCOV
569
            continue;
×
UNCOV
570
        }
×
UNCOV
571
        us.push_back(entry.second.get());
×
UNCOV
572
    }
×
573

UNCOV
574
    return us;
×
UNCOV
575
};
×
576

577
const std::list<User*>& Users::writes(const std::string& container) const { return this->writes_.at(container); };
96✔
578

579
size_t Users::num_writes(const std::string& container) const { return this->writes(container).size(); };
21✔
580

581
std::list<User*> Users::reads() const {
×
582
    std::list<User*> us;
×
583
    for (auto& entry : this->users_) {
×
584
        if (entry.second->use() != Use::READ) {
×
585
            continue;
×
586
        }
×
587
        us.push_back(entry.second.get());
×
588
    }
×
589

590
    return us;
×
591
};
×
592

593
const std::list<User*>& Users::reads(const std::string& container) const { return this->reads_.at(container); };
1,828✔
594

595
size_t Users::num_reads(const std::string& container) const { return this->reads(container).size(); };
1,809✔
596

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

606
    return us;
×
607
};
×
608

609
const std::list<User*>& Users::views(const std::string& container) const { return this->views_.at(container); };
516✔
610

611
size_t Users::num_views(const std::string& container) const { return this->views(container).size(); };
507✔
612

613
std::list<User*> Users::moves() const {
×
614
    std::list<User*> us;
×
615
    for (auto& entry : this->users_) {
×
616
        if (entry.second->use() != Use::MOVE) {
×
617
            continue;
×
618
        }
×
619
        us.push_back(entry.second.get());
×
620
    }
×
621

622
    return us;
×
623
};
×
624

625
const std::list<User*>& Users::moves(const std::string& container) const { return this->moves_.at(container); };
566✔
626

627
size_t Users::num_moves(const std::string& container) const { return this->moves(container).size(); };
505✔
628

629
structured_control_flow::ControlFlowNode* Users::scope(User* user) {
9,321✔
630
    if (auto data_node = dynamic_cast<data_flow::DataFlowNode*>(user->element())) {
9,321✔
631
        return static_cast<structured_control_flow::Block*>(data_node->get_parent().get_parent());
5,751✔
632
    } else if (auto memlet = dynamic_cast<data_flow::Memlet*>(user->element())) {
5,751✔
633
        return static_cast<structured_control_flow::Block*>(memlet->get_parent().get_parent());
978✔
634
    } else if (auto transition = dynamic_cast<structured_control_flow::Transition*>(user->element())) {
2,592✔
635
        return &transition->parent();
×
636
    } else {
2,592✔
637
        auto user_element = dynamic_cast<structured_control_flow::ControlFlowNode*>(user->element());
2,592✔
638
        assert(user_element != nullptr && "Users::scope: User element is not a ControlFlowNode");
2,592✔
639
        return user_element;
2,592✔
640
    }
2,592✔
641
}
9,321✔
642

643
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
5✔
644
    std::unordered_set<User*> uses;
5✔
645
    std::unordered_set<User*> visited;
5✔
646
    std::list<User*> queue = {&user2};
5✔
647
    while (!queue.empty()) {
28✔
648
        auto current = queue.front();
23✔
649
        queue.pop_front();
23✔
650

651
        // Stop conditions
652
        if (current == &user1) {
23✔
653
            continue;
5✔
654
        }
5✔
655

656
        if (visited.find(current) != visited.end()) {
18✔
657
            continue;
×
658
        }
×
659
        visited.insert(current);
18✔
660

661
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
18✔
662
            uses.insert(current);
1✔
663
        }
1✔
664

665
        // Extend search
666
        // Backward search to utilize domination user1 over user
667
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
18✔
668
        auto edges = std::ranges::subrange(eb, ee);
18✔
669
        for (auto edge : edges) {
18✔
670
            auto v = boost::source(edge, this->graph_);
18✔
671
            queue.push_back(this->users_.at(v).get());
18✔
672
        }
18✔
673
    }
18✔
674

675
    return uses;
5✔
676
};
5✔
677

678
const std::unordered_set<User*> Users::all_uses_after(User& user) {
6✔
679
    std::unordered_set<User*> uses;
6✔
680
    std::unordered_set<User*> visited;
6✔
681
    std::list<User*> queue = {&user};
6✔
682
    while (!queue.empty()) {
28✔
683
        auto current = queue.front();
22✔
684
        queue.pop_front();
22✔
685
        if (visited.find(current) != visited.end()) {
22✔
686
            continue;
×
687
        }
×
688
        visited.insert(current);
22✔
689

690
        if (current != &user && current->use() != Use::NOP) {
22✔
691
            uses.insert(current);
4✔
692
        }
4✔
693

694
        // Extend search
695
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
22✔
696
        auto edges = std::ranges::subrange(eb, ee);
22✔
697
        for (auto edge : edges) {
22✔
698
            auto v = boost::target(edge, this->graph_);
16✔
699
            queue.push_back(this->users_.at(v).get());
16✔
700
        }
16✔
701
    }
22✔
702

703
    return uses;
6✔
704
};
6✔
705

706
const std::vector<std::string> Users::all_containers_in_order() {
×
707
    std::unordered_set<std::string> unique_containers;
×
708
    std::vector<std::string> containers;
×
709

710
    // BFS traversal
711
    std::unordered_set<User*> visited;
×
712
    std::list<User*> queue = {this->source_};
×
713
    while (!queue.empty()) {
×
714
        auto current = queue.front();
×
715
        queue.pop_front();
×
716
        if (visited.find(current) != visited.end()) {
×
717
            continue;
×
718
        }
×
719
        visited.insert(current);
×
720

721
        if ((current->container() != "" || current->use() != Use::NOP) &&
×
722
            unique_containers.find(current->container()) == unique_containers.end()) {
×
723
            unique_containers.insert(current->container());
×
724
            containers.push_back(current->container());
×
725
        }
×
726

727
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
728
        auto edges = std::ranges::subrange(eb, ee);
×
729
        for (auto edge : edges) {
×
730
            auto v = boost::target(edge, this->graph_);
×
731
            queue.push_back(this->users_.at(v).get());
×
732
        }
×
733
    }
×
734
    return containers;
×
735
}
×
736

737
UsersView::UsersView(Users& users, const structured_control_flow::ControlFlowNode& node) : users_(users) {
1,085✔
738
    this->entry_ = users.entries_.at(&node);
1,085✔
739
    this->exit_ = users.exits_.at(&node);
1,085✔
740

741
    // Collect sub users
742
    std::unordered_set<User*> visited;
1,085✔
743
    std::list<User*> queue = {this->exit_};
1,085✔
744
    while (!queue.empty()) {
31,535✔
745
        auto current = queue.front();
30,450✔
746
        queue.pop_front();
30,450✔
747

748
        // Stop conditions
749
        if (current == this->entry_) {
30,450✔
750
            continue;
1,085✔
751
        }
1,085✔
752

753
        if (visited.find(current) != visited.end()) {
29,365✔
754
            continue;
3,918✔
755
        }
3,918✔
756
        visited.insert(current);
25,447✔
757

758
        this->sub_users_.insert(current);
25,447✔
759

760
        // Extend search
761
        // Backward search to utilize domination user1 over user
762
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
25,447✔
763
        auto edges = std::ranges::subrange(eb, ee);
25,447✔
764
        for (auto edge : edges) {
29,365✔
765
            auto v = boost::source(edge, users.graph_);
29,365✔
766
            queue.push_back(users.users_.at(v).get());
29,365✔
767
        }
29,365✔
768
    }
25,447✔
769
};
1,085✔
770

771
std::vector<User*> UsersView::uses() const {
393✔
772
    std::vector<User*> us;
393✔
773
    for (auto& user : this->sub_users_) {
7,617✔
774
        if (user->use() == Use::NOP) {
7,617✔
775
            continue;
2,701✔
776
        }
2,701✔
777
        us.push_back(user);
4,916✔
778
    }
4,916✔
779

780
    return us;
393✔
781
};
393✔
782

783
std::vector<User*> UsersView::uses(const std::string& container) const {
1,366✔
784
    std::vector<User*> us;
1,366✔
785
    for (auto& user : this->sub_users_) {
44,714✔
786
        if (user->container() != container) {
44,714✔
787
            continue;
38,170✔
788
        }
38,170✔
789
        if (user->use() == Use::NOP) {
6,544✔
790
            continue;
×
791
        }
×
792
        us.push_back(user);
6,544✔
793
    }
6,544✔
794

795
    return us;
1,366✔
796
};
1,366✔
797

798
std::vector<User*> UsersView::writes() const {
170✔
799
    std::vector<User*> us;
170✔
800
    for (auto& user : this->sub_users_) {
6,047✔
801
        if (user->use() != Use::WRITE) {
6,047✔
802
            continue;
5,066✔
803
        }
5,066✔
804
        us.push_back(user);
981✔
805
    }
981✔
806

807
    return us;
170✔
808
};
170✔
809

810
std::vector<User*> UsersView::writes(const std::string& container) const {
1,273✔
811
    std::vector<User*> us;
1,273✔
812
    for (auto& user : this->sub_users_) {
26,093✔
813
        if (user->container() != container) {
26,093✔
814
            continue;
20,201✔
815
        }
20,201✔
816
        if (user->use() != Use::WRITE) {
5,892✔
817
            continue;
3,966✔
818
        }
3,966✔
819
        us.push_back(user);
1,926✔
820
    }
1,926✔
821

822
    return us;
1,273✔
823
};
1,273✔
824

825
std::vector<User*> UsersView::reads() const {
92✔
826
    std::vector<User*> us;
92✔
827
    for (auto& user : this->sub_users_) {
3,379✔
828
        if (user->use() != Use::READ) {
3,379✔
829
            continue;
1,777✔
830
        }
1,777✔
831
        us.push_back(user);
1,602✔
832
    }
1,602✔
833

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

837
std::vector<User*> UsersView::reads(const std::string& container) const {
168✔
838
    std::vector<User*> us;
168✔
839
    for (auto& user : this->sub_users_) {
2,557✔
840
        if (user->container() != container) {
2,557✔
841
            continue;
2,268✔
842
        }
2,268✔
843
        if (user->use() != Use::READ) {
289✔
844
            continue;
102✔
845
        }
102✔
846
        us.push_back(user);
187✔
847
    }
187✔
848

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

852
std::vector<User*> UsersView::views() const {
60✔
853
    std::vector<User*> us;
60✔
854
    for (auto& user : this->sub_users_) {
2,459✔
855
        if (user->use() != Use::VIEW) {
2,459✔
856
            continue;
2,459✔
857
        }
2,459✔
858
        us.push_back(user);
×
859
    }
×
860

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

864
std::vector<User*> UsersView::views(const std::string& container) const {
103✔
865
    std::vector<User*> us;
103✔
866
    for (auto& user : this->sub_users_) {
1,584✔
867
        if (user->container() != container) {
1,584✔
868
            continue;
1,399✔
869
        }
1,399✔
870
        if (user->use() != Use::VIEW) {
185✔
871
            continue;
182✔
872
        }
182✔
873
        us.push_back(user);
3✔
874
    }
3✔
875

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

879
std::vector<User*> UsersView::moves() const {
62✔
880
    std::vector<User*> us;
62✔
881
    for (auto& user : this->sub_users_) {
2,477✔
882
        if (user->use() != Use::MOVE) {
2,477✔
883
            continue;
2,475✔
884
        }
2,475✔
885
        us.push_back(user);
2✔
886
    }
2✔
887

888
    return us;
62✔
889
};
62✔
890

891
std::vector<User*> UsersView::moves(const std::string& container) const {
132✔
892
    std::vector<User*> us;
132✔
893
    for (auto& user : this->sub_users_) {
1,966✔
894
        if (user->container() != container) {
1,966✔
895
            continue;
1,714✔
896
        }
1,714✔
897
        if (user->use() != Use::MOVE) {
252✔
898
            continue;
249✔
899
        }
249✔
900
        us.push_back(user);
3✔
901
    }
3✔
902

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

906
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
907
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
908
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
909

910
    std::unordered_set<User*> uses;
×
911
    std::unordered_set<User*> visited;
×
912
    std::list<User*> queue = {&user1};
×
913
    while (!queue.empty()) {
×
914
        auto current = queue.front();
×
915
        queue.pop_front();
×
916
        if (visited.find(current) != visited.end()) {
×
917
            continue;
×
918
        }
×
919
        visited.insert(current);
×
920

921
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
922
            uses.insert(current);
×
923
        }
×
924

925
        // Stop conditions
926
        if (current == exit_) {
×
927
            continue;
×
928
        }
×
929

930
        if (current == &user2) {
×
931
            continue;
×
932
        }
×
933

934
        // Extend search
935
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
936
        auto edges = std::ranges::subrange(eb, ee);
×
937
        for (auto edge : edges) {
×
938
            auto v = boost::target(edge, this->users_.graph_);
×
939
            queue.push_back(this->users_.users_.at(v).get());
×
940
        }
×
941
    }
×
942

943
    return uses;
×
944
};
×
945

946
std::unordered_set<User*> UsersView::all_uses_after(User& user) {
15✔
947
    assert(this->sub_users_.find(&user) != this->sub_users_.end());
15✔
948

949
    std::unordered_set<User*> uses;
15✔
950
    std::unordered_set<User*> visited;
15✔
951
    std::list<User*> queue = {&user};
15✔
952
    while (!queue.empty()) {
59✔
953
        auto current = queue.front();
44✔
954
        queue.pop_front();
44✔
955
        if (visited.find(current) != visited.end()) {
44✔
956
            continue;
×
957
        }
×
958
        visited.insert(current);
44✔
959

960
        if (current != &user && current->use() != Use::NOP) {
44✔
961
            uses.insert(current);
4✔
962
        }
4✔
963

964
        if (current == exit_) {
44✔
965
            continue;
15✔
966
        }
15✔
967

968
        // Extend search
969
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
29✔
970
        auto edges = std::ranges::subrange(eb, ee);
29✔
971
        for (auto edge : edges) {
29✔
972
            auto v = boost::target(edge, this->users_.graph_);
29✔
973
            queue.push_back(this->users_.users_.at(v).get());
29✔
974
        }
29✔
975
    }
29✔
976

977
    return uses;
15✔
978
};
15✔
979

980
bool Users::
981
    has_user(const std::string& container, Element* element, Use use, bool is_init, bool is_condition, bool is_update) {
224✔
982
    UserProps key{container, element, use, is_init, is_condition, is_update};
224✔
983
    if (this->users_lookup_.find(key) == this->users_lookup_.end()) {
224✔
984
        return false;
168✔
985
    }
168✔
986
    return true;
56✔
987
}
224✔
988

989
User* Users::
990
    get_user(const std::string& container, Element* element, Use use, bool is_init, bool is_condition, bool is_update) {
10,432✔
991
    UserProps key{container, element, use, is_init, is_condition, is_update};
10,432✔
992
    return this->users_lookup_.at(key);
10,432✔
993
}
10,432✔
994

995
void Users::add_user(std::unique_ptr<User> user) {
13,809✔
996
    auto vertex = user->vertex_;
13,809✔
997
    this->users_.insert({vertex, std::move(user)});
13,809✔
998

999
    auto user_ptr = this->users_.at(vertex).get();
13,809✔
1000
    bool is_init = false;
13,809✔
1001
    bool is_condition = false;
13,809✔
1002
    bool is_update = false;
13,809✔
1003
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
13,809✔
1004
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
6,793✔
1005
        if (for_loop == nullptr) {
6,793✔
1006
            throw std::invalid_argument("Invalid user type");
×
1007
        }
×
1008
        if (for_user->is_init()) {
6,793✔
1009
            is_init = true;
1,548✔
1010
        } else if (for_user->is_condition()) {
5,245✔
1011
            is_condition = true;
2,595✔
1012
        } else if (for_user->is_update()) {
2,650✔
1013
            is_update = true;
2,650✔
1014
        } else {
2,650✔
1015
            throw std::invalid_argument("Invalid user type");
×
1016
        }
×
1017
    }
6,793✔
1018

1019
    UserProps key{user_ptr->container(), user_ptr->element(), user_ptr->use(), is_init, is_condition, is_update};
13,809✔
1020
    this->users_lookup_.insert({key, user_ptr});
13,809✔
1021
}
13,809✔
1022

1023
std::unordered_set<std::string> Users::locals(structured_control_flow::ControlFlowNode& node) {
88✔
1024
    auto& sdfg = this->sdfg_;
88✔
1025

1026
    // Locals have no uses outside of the node
1027
    // We can check this by comparing the number of uses of the container in the view and the total
1028
    // number of uses of the container in the users map.
1029
    std::unordered_set<std::string> locals;
88✔
1030
    UsersView view(*this, node);
88✔
1031
    for (auto& user : view.uses()) {
1,325✔
1032
        if (!sdfg.is_transient(user->container())) {
1,325✔
1033
            continue;
382✔
1034
        }
382✔
1035
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
943✔
1036
            locals.insert(user->container());
508✔
1037
        }
508✔
1038
    }
943✔
1039

1040
    return locals;
88✔
1041
};
88✔
1042

1043
const std::vector<std::string> UsersView::all_containers_in_order() {
×
1044
    std::unordered_set<std::string> unique_containers;
×
1045
    std::vector<std::string> containers;
×
1046

1047
    // BFS traversal
1048
    std::unordered_set<User*> visited;
×
1049
    std::list<User*> queue = {this->entry_};
×
1050
    while (!queue.empty()) {
×
1051
        auto current = queue.front();
×
1052
        queue.pop_front();
×
1053
        if (visited.find(current) != visited.end()) {
×
1054
            continue;
×
1055
        }
×
1056
        visited.insert(current);
×
1057

1058
        if ((current->container() != "" || current->use() != Use::NOP) &&
×
1059
            unique_containers.find(current->container()) == unique_containers.end()) {
×
1060
            unique_containers.insert(current->container());
×
1061
            containers.push_back(current->container());
×
1062
        }
×
1063

1064
        if (current == this->exit_) {
×
1065
            continue;
×
1066
        }
×
1067

1068
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1069
        auto edges = std::ranges::subrange(eb, ee);
×
1070
        for (auto edge : edges) {
×
1071
            auto v = boost::target(edge, this->users_.graph_);
×
1072
            queue.push_back(this->users_.users_.at(v).get());
×
1073
        }
×
1074
    }
×
1075

1076
    return containers;
×
1077
}
×
1078

1079
} // namespace analysis
1080
} // namespace sdfg
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc