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

daisytuner / sdfglib / 17911352749

22 Sep 2025 09:46AM UTC coverage: 60.234% (+0.04%) from 60.192%
17911352749

push

github

web-flow
Merge pull request #234 from daisytuner/dead-cfg-returns

adds dead cfg elimination for return nodes

16 of 17 new or added lines in 3 files covered. (94.12%)

2 existing lines in 1 file now uncovered.

9526 of 15815 relevant lines covered (60.23%)

105.78 hits per line

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

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

27
      };
1,767✔
28

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

32
      };
426✔
33

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

36
};
3,984✔
37

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

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

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

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

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

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

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

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

83
      };
402✔
84

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

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

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

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

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

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

118
std::pair<graph::Vertex, graph::Vertex> Users::traverse(data_flow::DataFlowGraph& dataflow) {
209✔
119
    graph::Vertex first = boost::graph_traits<graph::Graph>::null_vertex();
209✔
120
    graph::Vertex last = boost::graph_traits<graph::Graph>::null_vertex();
209✔
121
    for (auto node : dataflow.topological_sort()) {
568✔
122
        if (dynamic_cast<data_flow::ConstantNode*>(node) != nullptr) {
359✔
123
            continue;
13✔
124
        }
125

126
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
346✔
127
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
229✔
128
                if (dataflow.in_degree(*node) > 0) {
229✔
129
                    Use use = Use::WRITE;
126✔
130

131
                    // Check if the pointer itself is moved (overwritten)
132
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
245✔
133
                        if (iedge.type() == data_flow::MemletType::Reference ||
249✔
134
                            iedge.type() == data_flow::MemletType::Dereference_Src) {
123✔
135
                            use = Use::MOVE;
7✔
136
                            break;
7✔
137
                        }
138
                    }
139

140
                    auto v = boost::add_vertex(this->graph_);
126✔
141
                    this->add_user(std::make_unique<User>(v, access_node->data(), access_node, &dataflow, use));
126✔
142

143
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
126✔
144
                        boost::add_edge(last, v, this->graph_);
100✔
145
                    } else {
100✔
146
                        first = v;
26✔
147
                    }
148
                    last = v;
126✔
149
                }
126✔
150
                if (dataflow.out_degree(*access_node) > 0) {
229✔
151
                    Use use = Use::READ;
108✔
152

153
                    // Check if the pointer itself is viewed (aliased)
154
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
211✔
155
                        if (oedge.type() == data_flow::MemletType::Reference ||
215✔
156
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
106✔
157
                            use = Use::VIEW;
6✔
158
                            break;
6✔
159
                        }
160
                    }
161

162
                    auto v = boost::add_vertex(this->graph_);
108✔
163
                    this->add_user(std::make_unique<User>(v, access_node->data(), access_node, &dataflow, use));
108✔
164

165
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
108✔
166
                        boost::add_edge(last, v, this->graph_);
26✔
167
                    } else {
26✔
168
                        first = v;
82✔
169
                    }
170
                    last = v;
108✔
171
                }
108✔
172
            }
229✔
173
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
346✔
174
            for (auto& symbol : library_node->symbols()) {
×
175
                auto v = boost::add_vertex(this->graph_);
×
176
                this->add_user(std::make_unique<User>(v, symbol->get_name(), library_node, &dataflow, Use::READ));
×
177
                if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
×
178
                    boost::add_edge(last, v, this->graph_);
×
179
                } else {
×
180
                    first = v;
×
181
                }
182
                last = v;
×
183
            }
184
        }
×
185

186
        for (auto& oedge : dataflow.out_edges(*node)) {
572✔
187
            std::unordered_set<std::string> used;
226✔
188
            for (auto dim : oedge.subset()) {
456✔
189
                for (auto atom : symbolic::atoms(dim)) {
422✔
190
                    if (used.find(atom->get_name()) != used.end()) {
192✔
191
                        continue;
×
192
                    }
193
                    used.insert(atom->get_name());
192✔
194

195
                    auto v = boost::add_vertex(this->graph_);
192✔
196
                    this->add_user(std::make_unique<User>(v, atom->get_name(), &oedge, &dataflow, Use::READ));
192✔
197
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
192✔
198
                        boost::add_edge(last, v, this->graph_);
181✔
199
                    } else {
181✔
200
                        first = v;
11✔
201
                    }
202
                    last = v;
192✔
203
                }
192✔
204
            }
230✔
205
        }
226✔
206
    }
207

208
    return {first, last};
209✔
209
};
×
210

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

218
        // NOP
219
        auto t = boost::add_vertex(this->graph_);
209✔
220
        this->users_.insert({t, std::make_unique<User>(t, "", block_stmt, Use::NOP)});
209✔
221
        this->exits_.insert({block_stmt, this->users_.at(t).get()});
209✔
222

223
        auto& dataflow = block_stmt->dataflow();
209✔
224
        auto subgraph = this->traverse(dataflow);
209✔
225

226
        // May be empty
227
        if (subgraph.first == boost::graph_traits<graph::Graph>::null_vertex()) {
209✔
228
            boost::add_edge(s, t, this->graph_);
90✔
229
            return {s, t};
90✔
230
        }
231

232
        boost::add_edge(s, subgraph.first, this->graph_);
119✔
233
        boost::add_edge(subgraph.second, t, this->graph_);
119✔
234

235
        return {s, t};
119✔
236
    } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(&node)) {
411✔
237
        auto s = boost::add_vertex(this->graph_);
283✔
238
        this->users_.insert({s, std::make_unique<User>(s, "", sequence_stmt, Use::NOP)});
283✔
239
        this->entries_.insert({sequence_stmt, this->users_.at(s).get()});
283✔
240

241
        graph::Vertex current = s;
283✔
242
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
622✔
243
            auto child = sequence_stmt->at(i);
341✔
244

245
            auto subgraph = this->traverse(child.first);
341✔
246
            boost::add_edge(current, subgraph.first, this->graph_);
341✔
247
            // Return node
248
            if (subgraph.second == boost::graph_traits<graph::Graph>::null_vertex()) {
341✔
249
                break;
2✔
250
            }
251
            current = subgraph.second;
339✔
252

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

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

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

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

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

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

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

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

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

300
        std::unordered_set<std::string> used;
28✔
301
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
72✔
302
            auto condition = if_else_stmt->at(i).second;
44✔
303
            for (auto atom : symbolic::atoms(condition)) {
79✔
304
                if (used.find(atom->get_name()) != used.end()) {
35✔
305
                    continue;
10✔
306
                }
307
                used.insert(atom->get_name());
25✔
308

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

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

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

318
        graph::Vertex t = boost::graph_traits<graph::Graph>::null_vertex();
28✔
319
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
72✔
320
            auto branch = if_else_stmt->at(i);
44✔
321
            auto subgraph = this->traverse(branch.first);
44✔
322
            boost::add_edge(last, subgraph.first, this->graph_);
44✔
323
            if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
44✔
324
                if (t == boost::graph_traits<graph::Graph>::null_vertex()) {
44✔
325
                    t = boost::add_vertex(this->graph_);
28✔
326
                    this->users_.insert({t, std::make_unique<User>(t, "", if_else_stmt, Use::NOP)});
28✔
327
                    this->exits_.insert({if_else_stmt, this->users_.at(t).get()});
28✔
328
                }
28✔
329
                boost::add_edge(subgraph.second, t, this->graph_);
44✔
330
            }
44✔
331
        }
44✔
332

333
        // Forward edge: Potentially missing else case
334
        if (!if_else_stmt->is_complete()) {
28✔
335
            if (t == boost::graph_traits<graph::Graph>::null_vertex()) {
12✔
336
                t = boost::add_vertex(this->graph_);
×
337
                this->users_.insert({t, std::make_unique<User>(t, "", if_else_stmt, Use::NOP)});
×
338
                this->exits_.insert({if_else_stmt, this->users_.at(t).get()});
×
339
            }
×
340
            boost::add_edge(last, t, this->graph_);
12✔
341
        }
12✔
342

343
        return {s, t};
28✔
344
    } else if (auto loop_stmt = dynamic_cast<structured_control_flow::While*>(&node)) {
128✔
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)) {
93✔
369
        // NOP
370
        auto s = boost::add_vertex(this->graph_);
83✔
371
        this->users_.insert({s, std::make_unique<User>(s, "", for_stmt, Use::NOP)});
83✔
372
        auto last = s;
83✔
373
        this->entries_.insert({for_stmt, this->users_.at(s).get()});
83✔
374

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

380
        // Init
381
        for (auto atom : symbolic::atoms(for_stmt->init())) {
92✔
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_);
83✔
389
        this->add_user(std::make_unique<
83✔
390
                       ForUser>(v, for_stmt->indvar()->get_name(), for_stmt, Use::WRITE, true, false, false));
83✔
391

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

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

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

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

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

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

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

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

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

433
        return {s, t};
83✔
434
    } else if (auto cont_stmt = dynamic_cast<structured_control_flow::Continue*>(&node)) {
10✔
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)) {
6✔
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)) {
2✔
449
        if (!ret_stmt->is_data() || ret_stmt->data().empty()) {
2✔
450
            auto v = boost::add_vertex(this->graph_);
×
451
            this->users_.insert({v, std::make_unique<User>(v, "", ret_stmt, Use::NOP)});
×
452
            this->entries_.insert({ret_stmt, this->users_.at(v).get()});
×
453
            this->exits_.insert({ret_stmt, this->users_.at(v).get()});
×
454
            return {v, boost::graph_traits<graph::Graph>::null_vertex()};
×
455
        } else {
456
            auto v = boost::add_vertex(this->graph_);
2✔
457
            this->add_user(std::make_unique<User>(v, ret_stmt->data(), ret_stmt, Use::READ));
2✔
458
            this->entries_.insert({ret_stmt, this->users_.at(v).get()});
2✔
459
            this->exits_.insert({ret_stmt, this->users_.at(v).get()});
2✔
460
            return {v, boost::graph_traits<graph::Graph>::null_vertex()};
2✔
461
        }
462
    }
463

464
    throw std::invalid_argument("Invalid control flow node type");
×
465
};
620✔
466

467
Users::Users(StructuredSDFG& sdfg)
145✔
468
    : Analysis(sdfg), node_(sdfg.root()) {
145✔
469

470
      };
145✔
471

472
Users::Users(StructuredSDFG& sdfg, structured_control_flow::ControlFlowNode& node)
×
473
    : Analysis(sdfg), node_(node) {
×
474

475
      };
×
476

477
void Users::run(analysis::AnalysisManager& analysis_manager) {
145✔
478
    users_.clear();
145✔
479
    graph_.clear();
145✔
480
    source_ = nullptr;
145✔
481
    sink_ = nullptr;
145✔
482
    dom_tree_.clear();
145✔
483
    pdom_tree_.clear();
145✔
484
    users_by_sdfg_.clear();
145✔
485
    users_by_sdfg_loop_condition_.clear();
145✔
486
    users_by_sdfg_loop_init_.clear();
145✔
487
    users_by_sdfg_loop_update_.clear();
145✔
488

489
    reads_.clear();
145✔
490
    writes_.clear();
145✔
491
    views_.clear();
145✔
492
    moves_.clear();
145✔
493

494
    this->traverse(node_);
145✔
495
    if (this->users_.empty()) {
145✔
496
        return;
×
497
    }
498

499
    // Require a single source
500
    for (auto& entry : this->users_) {
2,337✔
501
        if (boost::in_degree(entry.first, this->graph_) == 0) {
2,192✔
502
            if (this->source_ != nullptr) {
145✔
503
                throw InvalidSDFGException("Users Analysis: Non-unique source node");
×
504
            }
505
            this->source_ = entry.second.get();
145✔
506
        }
145✔
507
    }
508
    if (this->source_ == nullptr) {
145✔
509
        throw InvalidSDFGException("Users Analysis: No source node");
×
510
    }
511

512
    std::list<User*> sinks;
145✔
513
    for (auto& entry : this->users_) {
2,337✔
514
        if (boost::out_degree(entry.first, this->graph_) == 0) {
2,192✔
515
            sinks.push_back(entry.second.get());
147✔
516
        }
147✔
517
    }
518
    if (sinks.size() == 0) {
145✔
519
        throw InvalidSDFGException("Users Analysis: No sink node");
×
520
    }
521
    if (sinks.size() > 1) {
145✔
522
        // add artificial sink
523
        auto v = boost::add_vertex(this->graph_);
1✔
524
        auto sink = std::make_unique<User>(v, "", nullptr, Use::NOP);
1✔
525
        this->users_.insert({v, std::move(sink)});
1✔
526
        for (auto sink : sinks) {
4✔
527
            boost::add_edge(sink->vertex_, v, this->graph_);
3✔
528
        }
529
        sinks.clear();
1✔
530

531
        this->sink_ = this->users_.at(v).get();
1✔
532
    } else {
1✔
533
        this->sink_ = sinks.front();
144✔
534
    }
535

536
    // Collect sub structures
537
    for (auto& entry : this->users_) {
2,338✔
538
        auto container = entry.second->container();
2,193✔
539
        switch (entry.second->use()) {
2,193✔
540
            case Use::READ: {
541
                if (this->reads_.find(container) == this->reads_.end()) {
584✔
542
                    this->reads_.insert({container, {}});
283✔
543
                }
283✔
544
                this->reads_[container].push_back(entry.second.get());
584✔
545
                break;
584✔
546
            }
547
            case Use::WRITE: {
548
                if (this->writes_.find(container) == this->writes_.end()) {
367✔
549
                    this->writes_.insert({container, {}});
243✔
550
                }
243✔
551
                this->writes_[container].push_back(entry.second.get());
367✔
552
                break;
367✔
553
            }
554
            case Use::VIEW: {
555
                if (this->views_.find(container) == this->views_.end()) {
6✔
556
                    this->views_.insert({container, {}});
6✔
557
                }
6✔
558
                this->views_[container].push_back(entry.second.get());
6✔
559
                break;
6✔
560
            }
561
            case Use::MOVE: {
562
                if (this->moves_.find(container) == this->moves_.end()) {
7✔
563
                    this->moves_.insert({container, {}});
7✔
564
                }
7✔
565
                this->moves_[container].push_back(entry.second.get());
7✔
566
                break;
7✔
567
            }
568
            default:
569
                break;
1,229✔
570
        }
571
    }
2,193✔
572

573
    this->init_dom_tree();
145✔
574
};
145✔
575

576
std::vector<User*> Users::uses() const {
×
577
    std::vector<User*> us;
×
578
    for (auto& entry : this->users_) {
×
579
        if (entry.second->use() == Use::NOP) {
×
580
            continue;
×
581
        }
582
        us.push_back(entry.second.get());
×
583
    }
584

585
    return us;
×
586
};
×
587

588
std::vector<User*> Users::uses(const std::string& container) const {
35✔
589
    std::vector<User*> us;
35✔
590
    for (auto& entry : this->users_) {
710✔
591
        if (entry.second->container() != container) {
675✔
592
            continue;
501✔
593
        }
594
        if (entry.second->use() == Use::NOP) {
174✔
595
            continue;
×
596
        }
597
        us.push_back(entry.second.get());
174✔
598
    }
599

600
    return us;
35✔
601
};
35✔
602

603
std::vector<User*> Users::writes() const {
×
604
    std::vector<User*> us;
×
605
    for (auto& entry : this->users_) {
×
606
        if (entry.second->use() != Use::WRITE) {
×
607
            continue;
×
608
        }
609
        us.push_back(entry.second.get());
×
610
    }
611

612
    return us;
×
613
};
×
614

615
std::vector<User*> Users::writes(const std::string& container) const {
58✔
616
    if (this->writes_.find(container) == this->writes_.end()) {
58✔
617
        return {};
33✔
618
    } else {
619
        return this->writes_.at(container);
25✔
620
    }
621
};
58✔
622

623
std::vector<User*> Users::reads() const {
×
624
    std::vector<User*> us;
×
625
    for (auto& entry : this->users_) {
×
626
        if (entry.second->use() != Use::READ) {
×
627
            continue;
×
628
        }
629
        us.push_back(entry.second.get());
×
630
    }
631

632
    return us;
×
633
};
×
634

635
std::vector<User*> Users::reads(const std::string& container) const {
49✔
636
    if (this->reads_.find(container) == this->reads_.end()) {
49✔
637
        return {};
20✔
638
    } else {
639
        return this->reads_.at(container);
29✔
640
    }
641
};
49✔
642

643
std::vector<User*> Users::views() const {
×
644
    std::vector<User*> us;
×
645
    for (auto& entry : this->users_) {
×
646
        if (entry.second->use() != Use::VIEW) {
×
647
            continue;
×
648
        }
649
        us.push_back(entry.second.get());
×
650
    }
651

652
    return us;
×
653
};
×
654

655
std::vector<User*> Users::views(const std::string& container) const {
65✔
656
    if (this->views_.find(container) == this->views_.end()) {
65✔
657
        return {};
62✔
658
    } else {
659
        return this->views_.at(container);
3✔
660
    }
661
};
65✔
662

663
std::vector<User*> Users::moves() const {
×
664
    std::vector<User*> us;
×
665
    for (auto& entry : this->users_) {
×
666
        if (entry.second->use() != Use::MOVE) {
×
667
            continue;
×
668
        }
669
        us.push_back(entry.second.get());
×
670
    }
671

672
    return us;
×
673
};
×
674

675
std::vector<User*> Users::moves(const std::string& container) const {
66✔
676
    if (this->moves_.find(container) == this->moves_.end()) {
66✔
677
        return {};
60✔
678
    } else {
679
        return this->moves_.at(container);
6✔
680
    }
681
};
66✔
682

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

697
/****** Domination Analysis ******/
698

699
bool Users::dominates(User& user1, User& user) {
39✔
700
    auto dominator = this->dom_tree_.at(&user);
39✔
701
    while (dominator != nullptr) {
136✔
702
        if (dominator == &user1) {
126✔
703
            return true;
29✔
704
        }
705
        dominator = this->dom_tree_.at(dominator);
97✔
706
    }
707
    return false;
10✔
708
};
39✔
709

710
bool Users::post_dominates(User& user1, User& user) {
14✔
711
    auto dominator = this->pdom_tree_.at(&user);
14✔
712
    while (dominator != nullptr) {
33✔
713
        if (dominator == &user1) {
25✔
714
            return true;
6✔
715
        }
716
        dominator = this->pdom_tree_.at(dominator);
19✔
717
    }
718
    return false;
8✔
719
};
14✔
720

721
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
17✔
722
    std::unordered_set<User*> uses;
17✔
723
    std::unordered_set<User*> visited;
17✔
724
    std::list<User*> queue = {&user2};
17✔
725
    while (!queue.empty()) {
107✔
726
        auto current = queue.front();
90✔
727
        queue.pop_front();
90✔
728

729
        // Stop conditions
730
        if (current == &user1) {
90✔
731
            continue;
17✔
732
        }
733

734
        if (visited.find(current) != visited.end()) {
73✔
735
            continue;
2✔
736
        }
737
        visited.insert(current);
71✔
738

739
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
71✔
740
            uses.insert(current);
11✔
741
        }
11✔
742

743
        // Extend search
744
        // Backward search to utilize domination user1 over user
745
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
71✔
746
        auto edges = std::ranges::subrange(eb, ee);
142✔
747
        for (auto edge : edges) {
144✔
748
            auto v = boost::source(edge, this->graph_);
73✔
749
            queue.push_back(this->users_.at(v).get());
73✔
750
        }
751
    }
752

753
    return uses;
17✔
754
};
17✔
755

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

768
        if (current != &user && current->use() != Use::NOP) {
×
769
            uses.insert(current);
×
770
        }
×
771

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

781
    return uses;
×
782
};
×
783

784
UsersView::UsersView(Users& users, const structured_control_flow::ControlFlowNode& node) : users_(users) {
82✔
785
    this->entry_ = users.entries_.at(&node);
82✔
786
    this->exit_ = users.exits_.at(&node);
82✔
787

788
    // Collect sub users
789
    std::unordered_set<User*> visited;
82✔
790
    std::list<User*> queue = {this->exit_};
82✔
791
    while (!queue.empty()) {
826✔
792
        auto current = queue.front();
744✔
793
        queue.pop_front();
744✔
794

795
        // Stop conditions
796
        if (current == this->entry_) {
744✔
797
            continue;
82✔
798
        }
799

800
        if (visited.find(current) != visited.end()) {
662✔
801
            continue;
61✔
802
        }
803
        visited.insert(current);
601✔
804

805
        this->sub_users_.insert(current);
601✔
806

807
        // Extend search
808
        // Backward search to utilize domination user1 over user
809
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
601✔
810
        auto edges = std::ranges::subrange(eb, ee);
1,202✔
811
        for (auto edge : edges) {
1,263✔
812
            auto v = boost::source(edge, users.graph_);
662✔
813
            queue.push_back(users.users_.at(v).get());
662✔
814
        }
815
    }
816

817
    // Collect sub dom tree
818
    auto& dom_tree = this->users_.dom_tree_;
82✔
819

820
    for (auto& user : this->sub_users_) {
683✔
821
        this->sub_dom_tree_[user] = dom_tree[user];
601✔
822
    }
823
    this->sub_dom_tree_[this->entry_] = nullptr;
82✔
824

825
    // Collect sub post dom tree
826
    auto& pdom_tree = this->users_.pdom_tree_;
82✔
827
    for (auto& user : this->sub_users_) {
683✔
828
        this->sub_pdom_tree_[user] = pdom_tree[user];
601✔
829
    }
830
    this->sub_pdom_tree_[this->exit_] = nullptr;
82✔
831
};
82✔
832

833
std::vector<User*> UsersView::uses() const {
19✔
834
    std::vector<User*> us;
19✔
835
    for (auto& user : this->sub_users_) {
135✔
836
        if (user->use() == Use::NOP) {
116✔
837
            continue;
67✔
838
        }
839
        us.push_back(user);
49✔
840
    }
841

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

845
std::vector<User*> UsersView::uses(const std::string& container) const {
38✔
846
    std::vector<User*> us;
38✔
847
    for (auto& user : this->sub_users_) {
423✔
848
        if (user->container() != container) {
385✔
849
            continue;
277✔
850
        }
851
        if (user->use() == Use::NOP) {
108✔
852
            continue;
×
853
        }
854
        us.push_back(user);
108✔
855
    }
856

857
    return us;
38✔
858
};
38✔
859

860
std::vector<User*> UsersView::writes() const {
2✔
861
    std::vector<User*> us;
2✔
862
    for (auto& user : this->sub_users_) {
18✔
863
        if (user->use() != Use::WRITE) {
16✔
864
            continue;
14✔
865
        }
866
        us.push_back(user);
2✔
867
    }
868

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

872
std::vector<User*> UsersView::writes(const std::string& container) const {
38✔
873
    std::vector<User*> us;
38✔
874
    for (auto& user : this->sub_users_) {
329✔
875
        if (user->container() != container) {
291✔
876
            continue;
271✔
877
        }
878
        if (user->use() != Use::WRITE) {
20✔
879
            continue;
3✔
880
        }
881
        us.push_back(user);
17✔
882
    }
883

884
    return us;
38✔
885
};
38✔
886

887
std::vector<User*> UsersView::reads() const {
×
888
    std::vector<User*> us;
×
889
    for (auto& user : this->sub_users_) {
×
890
        if (user->use() != Use::READ) {
×
891
            continue;
×
892
        }
893
        us.push_back(user);
×
894
    }
895

896
    return us;
×
897
};
×
898

899
std::vector<User*> UsersView::reads(const std::string& container) const {
11✔
900
    std::vector<User*> us;
11✔
901
    for (auto& user : this->sub_users_) {
258✔
902
        if (user->container() != container) {
247✔
903
            continue;
214✔
904
        }
905
        if (user->use() != Use::READ) {
33✔
906
            continue;
14✔
907
        }
908
        us.push_back(user);
19✔
909
    }
910

911
    return us;
11✔
912
};
11✔
913

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

923
    return us;
8✔
924
};
8✔
925

926
std::vector<User*> UsersView::views(const std::string& container) const {
7✔
927
    std::vector<User*> us;
7✔
928
    for (auto& user : this->sub_users_) {
189✔
929
        if (user->container() != container) {
182✔
930
            continue;
174✔
931
        }
932
        if (user->use() != Use::VIEW) {
8✔
933
            continue;
8✔
934
        }
935
        us.push_back(user);
×
936
    }
937

938
    return us;
7✔
939
};
7✔
940

941
std::vector<User*> UsersView::moves() const {
9✔
942
    std::vector<User*> us;
9✔
943
    for (auto& user : this->sub_users_) {
155✔
944
        if (user->use() != Use::MOVE) {
146✔
945
            continue;
145✔
946
        }
947
        us.push_back(user);
1✔
948
    }
949

950
    return us;
9✔
951
};
9✔
952

953
std::vector<User*> UsersView::moves(const std::string& container) const {
7✔
954
    std::vector<User*> us;
7✔
955
    for (auto& user : this->sub_users_) {
189✔
956
        if (user->container() != container) {
182✔
957
            continue;
174✔
958
        }
959
        if (user->use() != Use::MOVE) {
8✔
960
            continue;
8✔
961
        }
962
        us.push_back(user);
×
963
    }
964

965
    return us;
7✔
966
};
7✔
967

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

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

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

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

1005
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
1006
            uses.insert(current);
×
1007
        }
×
1008

1009
        // Stop conditions
1010
        if (current == exit_) {
×
1011
            continue;
×
1012
        }
1013

1014
        if (current == &user2) {
×
1015
            continue;
×
1016
        }
1017

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

1027
    return uses;
×
1028
};
×
1029

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

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

1044
        if (current != &user && current->use() != Use::NOP) {
×
1045
            uses.insert(current);
×
1046
        }
×
1047

1048
        if (current == exit_) {
×
1049
            continue;
×
1050
        }
1051

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

1061
    return uses;
×
1062
};
×
1063

1064
User* Users::
1065
    get_user(const std::string& container, Element* element, Use use, bool is_init, bool is_condition, bool is_update) {
774✔
1066
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element)) {
774✔
1067
        if (is_init) {
326✔
1068
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
74✔
1069
            return tmp;
74✔
1070
        } else if (is_condition) {
252✔
1071
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
120✔
1072
        } else if (is_update) {
132✔
1073
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
132✔
1074
        }
1075
    }
×
1076
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
448✔
1077
    return tmp;
448✔
1078
}
774✔
1079

1080
void Users::add_user(std::unique_ptr<User> user) {
964✔
1081
    auto vertex = user->vertex_;
964✔
1082
    this->users_.insert({vertex, std::move(user)});
964✔
1083

1084
    auto user_ptr = this->users_.at(vertex).get();
964✔
1085
    auto* target_structure = &users_by_sdfg_;
964✔
1086
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
964✔
1087
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
402✔
1088
        if (for_loop == nullptr) {
402✔
1089
            throw std::invalid_argument("Invalid user type");
×
1090
        }
1091
        if (for_user->is_init()) {
402✔
1092
            target_structure = &users_by_sdfg_loop_init_;
92✔
1093
        } else if (for_user->is_condition()) {
402✔
1094
            target_structure = &users_by_sdfg_loop_condition_;
144✔
1095
        } else if (for_user->is_update()) {
310✔
1096
            target_structure = &users_by_sdfg_loop_update_;
166✔
1097
        } else {
166✔
1098
            throw std::invalid_argument("Invalid user type");
×
1099
        }
1100
    }
402✔
1101

1102
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
964✔
1103
        target_structure->insert({user_ptr->container(), {}});
621✔
1104
    }
621✔
1105
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
1,928✔
1106
        (*target_structure)[user_ptr->container()].end()) {
964✔
1107
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
875✔
1108
    }
875✔
1109
    target_structure->at(user_ptr->container()).at(user_ptr->element()).insert({user_ptr->use(), user_ptr});
964✔
1110
}
964✔
1111

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

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

1129
    return locals;
19✔
1130
};
19✔
1131

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