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

daisytuner / sdfglib / 17656823807

11 Sep 2025 08:42PM UTC coverage: 60.447% (+1.1%) from 59.335%
17656823807

Pull #219

github

web-flow
Merge d5416236f into 6c1992b40
Pull Request #219: stdlib Library Nodes and ConstantNodes

460 of 1635 new or added lines in 81 files covered. (28.13%)

93 existing lines in 35 files now uncovered.

9385 of 15526 relevant lines covered (60.45%)

107.21 hits per line

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

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

27
      };
1,733✔
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,928✔
35

36
};
3,928✔
37

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

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

42
Element* User::element() { return this->element_; };
3,575✔
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(
390✔
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) {
390✔
82

83
      };
390✔
84

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

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

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

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

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

106
    // Compute post-dominator-tree
107
    auto pdom_tree = graph::post_dominator_tree(this->graph_);
142✔
108
    for (auto& entry : pdom_tree) {
2,301✔
109
        User* first = this->users_.at(entry.first).get();
2,159✔
110
        User* second = nullptr;
2,159✔
111
        if (entry.second != boost::graph_traits<graph::Graph>::null_vertex()) {
2,159✔
112
            second = this->users_.at(entry.second).get();
2,017✔
113
        }
2,017✔
114
        this->pdom_tree_.insert({first, second});
2,159✔
115
    }
116
};
142✔
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) {
611✔
212
    if (auto block_stmt = dynamic_cast<structured_control_flow::Block*>(&node)) {
611✔
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)) {
402✔
237
        auto s = boost::add_vertex(this->graph_);
277✔
238
        this->users_.insert({s, std::make_unique<User>(s, "", sequence_stmt, Use::NOP)});
277✔
239
        this->entries_.insert({sequence_stmt, this->users_.at(s).get()});
277✔
240

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

245
            auto subgraph = this->traverse(child.first);
338✔
246
            boost::add_edge(current, subgraph.first, this->graph_);
338✔
247
            current = subgraph.second;
338✔
248

249
            std::unordered_set<std::string> used;
338✔
250
            for (auto& entry : child.second.assignments()) {
420✔
251
                for (auto atom : symbolic::atoms(entry.second)) {
109✔
252
                    if (symbolic::is_pointer(atom)) {
27✔
253
                        continue;
×
254
                    }
255
                    if (used.find(atom->get_name()) != used.end()) {
27✔
256
                        continue;
×
257
                    }
258
                    used.insert(atom->get_name());
27✔
259

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

263
                    boost::add_edge(current, v, this->graph_);
27✔
264
                    current = v;
27✔
265
                }
27✔
266
            }
267

268
            for (auto& entry : child.second.assignments()) {
420✔
269
                auto v = boost::add_vertex(this->graph_);
82✔
270
                this->add_user(std::make_unique<User>(v, entry.first->get_name(), &child.second, Use::WRITE));
82✔
271

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

277
        if (current == boost::graph_traits<graph::Graph>::null_vertex()) {
277✔
278
            this->exits_.insert({sequence_stmt, this->users_.at(s).get()});
3✔
279
            return {s, current};
3✔
280
        }
281

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

287
        return {s, t};
274✔
288
    } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(&node)) {
125✔
289
        // NOP
290
        auto s = boost::add_vertex(this->graph_);
28✔
291
        this->users_.insert({s, std::make_unique<User>(s, "", if_else_stmt, Use::NOP)});
28✔
292
        this->entries_.insert({if_else_stmt, this->users_.at(s).get()});
28✔
293

294
        graph::Vertex last = s;
28✔
295

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

305
                auto v = boost::add_vertex(this->graph_);
25✔
306

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

309
                boost::add_edge(last, v, this->graph_);
25✔
310
                last = v;
25✔
311
            }
35✔
312
        }
44✔
313

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

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

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

346
        auto subgraph = this->traverse(loop_stmt->root());
7✔
347

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

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

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

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

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

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

388
        boost::add_edge(last, v, this->graph_);
80✔
389
        last = v;
80✔
390

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

396
            boost::add_edge(last, v, this->graph_);
141✔
397
            boost::add_edge(v, t, this->graph_);
141✔
398
            last = v;
141✔
399
        }
141✔
400

401
        auto subgraph = this->traverse(for_stmt->root());
80✔
402
        boost::add_edge(last, subgraph.first, this->graph_);
80✔
403

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

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

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

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

426
        // Back edge
427
        boost::add_edge(t, last, this->graph_);
80✔
428

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

460
    throw std::invalid_argument("Invalid control flow node type");
×
461
};
611✔
462

463
Users::Users(StructuredSDFG& sdfg)
142✔
464
    : Analysis(sdfg), node_(sdfg.root()) {
142✔
465

466
      };
142✔
467

468
Users::Users(StructuredSDFG& sdfg, structured_control_flow::ControlFlowNode& node)
×
469
    : Analysis(sdfg), node_(node) {
×
470

471
      };
×
472

473
void Users::run(analysis::AnalysisManager& analysis_manager) {
142✔
474
    users_.clear();
142✔
475
    graph_.clear();
142✔
476
    source_ = nullptr;
142✔
477
    sink_ = nullptr;
142✔
478
    dom_tree_.clear();
142✔
479
    pdom_tree_.clear();
142✔
480
    users_by_sdfg_.clear();
142✔
481
    users_by_sdfg_loop_condition_.clear();
142✔
482
    users_by_sdfg_loop_init_.clear();
142✔
483
    users_by_sdfg_loop_update_.clear();
142✔
484

485
    reads_.clear();
142✔
486
    writes_.clear();
142✔
487
    views_.clear();
142✔
488
    moves_.clear();
142✔
489

490
    this->traverse(node_);
142✔
491
    if (this->users_.empty()) {
142✔
492
        return;
×
493
    }
494

495
    // Require a single source
496
    for (auto& entry : this->users_) {
2,300✔
497
        if (boost::in_degree(entry.first, this->graph_) == 0) {
2,158✔
498
            if (this->source_ != nullptr) {
142✔
499
                throw InvalidSDFGException("Users Analysis: Non-unique source node");
×
500
            }
501
            this->source_ = entry.second.get();
142✔
502
        }
142✔
503
    }
504
    if (this->source_ == nullptr) {
142✔
505
        throw InvalidSDFGException("Users Analysis: No source node");
×
506
    }
507

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

527
        this->sink_ = this->users_.at(v).get();
1✔
528
    } else {
1✔
529
        this->sink_ = sinks.front();
141✔
530
    }
531

532
    // Collect sub structures
533
    for (auto& entry : this->users_) {
2,301✔
534
        auto container = entry.second->container();
2,159✔
535
        switch (entry.second->use()) {
2,159✔
536
            case Use::READ: {
537
                if (this->reads_.find(container) == this->reads_.end()) {
578✔
538
                    this->reads_.insert({container, {}});
280✔
539
                }
280✔
540
                this->reads_[container].push_back(entry.second.get());
578✔
541
                break;
578✔
542
            }
543
            case Use::WRITE: {
544
                if (this->writes_.find(container) == this->writes_.end()) {
361✔
545
                    this->writes_.insert({container, {}});
240✔
546
                }
240✔
547
                this->writes_[container].push_back(entry.second.get());
361✔
548
                break;
361✔
549
            }
550
            case Use::VIEW: {
551
                if (this->views_.find(container) == this->views_.end()) {
6✔
552
                    this->views_.insert({container, {}});
6✔
553
                }
6✔
554
                this->views_[container].push_back(entry.second.get());
6✔
555
                break;
6✔
556
            }
557
            case Use::MOVE: {
558
                if (this->moves_.find(container) == this->moves_.end()) {
7✔
559
                    this->moves_.insert({container, {}});
7✔
560
                }
7✔
561
                this->moves_[container].push_back(entry.second.get());
7✔
562
                break;
7✔
563
            }
564
            default:
565
                break;
1,207✔
566
        }
567
    }
2,159✔
568

569
    this->init_dom_tree();
142✔
570
};
142✔
571

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

581
    return us;
×
582
};
×
583

584
std::vector<User*> Users::uses(const std::string& container) const {
35✔
585
    std::vector<User*> us;
35✔
586
    for (auto& entry : this->users_) {
702✔
587
        if (entry.second->container() != container) {
667✔
588
            continue;
493✔
589
        }
590
        if (entry.second->use() == Use::NOP) {
174✔
591
            continue;
×
592
        }
593
        us.push_back(entry.second.get());
174✔
594
    }
595

596
    return us;
35✔
597
};
35✔
598

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

608
    return us;
×
609
};
×
610

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

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

628
    return us;
×
629
};
×
630

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

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

648
    return us;
×
649
};
×
650

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

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

668
    return us;
×
669
};
×
670

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

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

693
/****** Domination Analysis ******/
694

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

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

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

725
        // Stop conditions
726
        if (current == &user1) {
90✔
727
            continue;
17✔
728
        }
729

730
        if (visited.find(current) != visited.end()) {
73✔
731
            continue;
2✔
732
        }
733
        visited.insert(current);
71✔
734

735
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
71✔
736
            uses.insert(current);
11✔
737
        }
11✔
738

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

749
    return uses;
17✔
750
};
17✔
751

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

764
        if (current != &user && current->use() != Use::NOP) {
×
765
            uses.insert(current);
×
766
        }
×
767

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

777
    return uses;
×
778
};
×
779

780
UsersView::UsersView(Users& users, const structured_control_flow::ControlFlowNode& node) : users_(users) {
76✔
781
    this->entry_ = users.entries_.at(&node);
76✔
782
    this->exit_ = users.exits_.at(&node);
76✔
783

784
    // Collect sub users
785
    std::unordered_set<User*> visited;
76✔
786
    std::list<User*> queue = {this->exit_};
76✔
787
    while (!queue.empty()) {
808✔
788
        auto current = queue.front();
732✔
789
        queue.pop_front();
732✔
790

791
        // Stop conditions
792
        if (current == this->entry_) {
732✔
793
            continue;
76✔
794
        }
795

796
        if (visited.find(current) != visited.end()) {
656✔
797
            continue;
61✔
798
        }
799
        visited.insert(current);
595✔
800

801
        this->sub_users_.insert(current);
595✔
802

803
        // Extend search
804
        // Backward search to utilize domination user1 over user
805
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
595✔
806
        auto edges = std::ranges::subrange(eb, ee);
1,190✔
807
        for (auto edge : edges) {
1,251✔
808
            auto v = boost::source(edge, users.graph_);
656✔
809
            queue.push_back(users.users_.at(v).get());
656✔
810
        }
811
    }
812

813
    // Collect sub dom tree
814
    auto& dom_tree = this->users_.dom_tree_;
76✔
815

816
    for (auto& user : this->sub_users_) {
671✔
817
        this->sub_dom_tree_[user] = dom_tree[user];
595✔
818
    }
819
    this->sub_dom_tree_[this->entry_] = nullptr;
76✔
820

821
    // Collect sub post dom tree
822
    auto& pdom_tree = this->users_.pdom_tree_;
76✔
823
    for (auto& user : this->sub_users_) {
671✔
824
        this->sub_pdom_tree_[user] = pdom_tree[user];
595✔
825
    }
826
    this->sub_pdom_tree_[this->exit_] = nullptr;
76✔
827
};
76✔
828

829
std::vector<User*> UsersView::uses() const {
16✔
830
    std::vector<User*> us;
16✔
831
    for (auto& user : this->sub_users_) {
129✔
832
        if (user->use() == Use::NOP) {
113✔
833
            continue;
64✔
834
        }
835
        us.push_back(user);
49✔
836
    }
837

838
    return us;
16✔
839
};
16✔
840

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

853
    return us;
38✔
854
};
38✔
855

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

865
    return us;
2✔
866
};
2✔
867

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

880
    return us;
38✔
881
};
38✔
882

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

892
    return us;
×
893
};
×
894

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

907
    return us;
11✔
908
};
11✔
909

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

919
    return us;
8✔
920
};
8✔
921

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

934
    return us;
7✔
935
};
7✔
936

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

946
    return us;
9✔
947
};
9✔
948

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

961
    return us;
7✔
962
};
7✔
963

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

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

986
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
987
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
988
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
989

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

1001
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
1002
            uses.insert(current);
×
1003
        }
×
1004

1005
        // Stop conditions
1006
        if (current == exit_) {
×
1007
            continue;
×
1008
        }
1009

1010
        if (current == &user2) {
×
1011
            continue;
×
1012
        }
1013

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

1023
    return uses;
×
1024
};
×
1025

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

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

1040
        if (current != &user && current->use() != Use::NOP) {
×
1041
            uses.insert(current);
×
1042
        }
×
1043

1044
        if (current == exit_) {
×
1045
            continue;
×
1046
        }
1047

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

1057
    return uses;
×
1058
};
×
1059

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

1076
void Users::add_user(std::unique_ptr<User> user) {
952✔
1077
    auto vertex = user->vertex_;
952✔
1078
    this->users_.insert({vertex, std::move(user)});
952✔
1079

1080
    auto user_ptr = this->users_.at(vertex).get();
952✔
1081
    auto* target_structure = &users_by_sdfg_;
952✔
1082
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
952✔
1083
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
390✔
1084
        if (for_loop == nullptr) {
390✔
1085
            throw std::invalid_argument("Invalid user type");
×
1086
        }
1087
        if (for_user->is_init()) {
390✔
1088
            target_structure = &users_by_sdfg_loop_init_;
89✔
1089
        } else if (for_user->is_condition()) {
390✔
1090
            target_structure = &users_by_sdfg_loop_condition_;
141✔
1091
        } else if (for_user->is_update()) {
301✔
1092
            target_structure = &users_by_sdfg_loop_update_;
160✔
1093
        } else {
160✔
1094
            throw std::invalid_argument("Invalid user type");
×
1095
        }
1096
    }
390✔
1097

1098
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
952✔
1099
        target_structure->insert({user_ptr->container(), {}});
612✔
1100
    }
612✔
1101
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
1,904✔
1102
        (*target_structure)[user_ptr->container()].end()) {
952✔
1103
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
866✔
1104
    }
866✔
1105
    target_structure->at(user_ptr->container()).at(user_ptr->element()).insert({user_ptr->use(), user_ptr});
952✔
1106
}
952✔
1107

1108
std::unordered_set<std::string> Users::locals(structured_control_flow::ControlFlowNode& node) {
16✔
1109
    auto& sdfg = this->sdfg_;
16✔
1110

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

1125
    return locals;
16✔
1126
};
16✔
1127

1128
} // namespace analysis
1129
} // 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