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

daisytuner / sdfglib / 16416595321

21 Jul 2025 12:11PM UTC coverage: 64.85% (-0.3%) from 65.106%
16416595321

Pull #153

github

web-flow
Merge 8bc3b1830 into d1eec4e04
Pull Request #153: Restricts memlets to contiguous memory

193 of 240 new or added lines in 24 files covered. (80.42%)

65 existing lines in 6 files now uncovered.

8295 of 12791 relevant lines covered (64.85%)

126.37 hits per line

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

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

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

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

21
namespace sdfg {
22
namespace analysis {
23

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

27
      };
1,651✔
28

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

32
      };
381✔
33

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

36
};
3,689✔
37

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

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

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

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

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

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

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

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

83
      };
375✔
84

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

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

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

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

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

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

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

127
                    // Check if the pointer itself is moved (overwritten)
128
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
218✔
129
                        if (iedge.dst_conn() == "ref" || iedge.dst_conn() == "deref") {
110✔
130
                            use = Use::MOVE;
2✔
131
                            break;
2✔
132
                        }
133
                    }
134

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

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

148
                    // Check if the pointer itself is viewed (aliased)
149
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
184✔
150
                        if (oedge.dst_conn() == "ref" || oedge.src_conn() == "deref") {
93✔
151
                            use = Use::VIEW;
2✔
152
                            break;
2✔
153
                        }
154
                    }
155

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

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

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

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

216
    return {first, last};
194✔
217
};
×
218

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

302
        graph::Vertex last = s;
27✔
303

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

313
                auto v = boost::add_vertex(this->graph_);
25✔
314

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

317
                boost::add_edge(last, v, this->graph_);
25✔
318
                last = v;
25✔
319
            }
39✔
320
        }
46✔
321

322
        // NOP
323
        auto t = boost::add_vertex(this->graph_);
27✔
324
        this->users_.insert({t, std::make_unique<User>(t, "", if_else_stmt, Use::NOP)});
27✔
325
        this->exits_.insert({if_else_stmt, this->users_.at(t).get()});
27✔
326

327
        // Forward edge: Potentially missing else case
328
        if (!if_else_stmt->is_complete()) {
27✔
329
            boost::add_edge(last, t, this->graph_);
8✔
330
        }
8✔
331

332
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
73✔
333
            auto branch = if_else_stmt->at(i);
46✔
334
            auto subgraph = this->traverse(branch.first);
46✔
335
            boost::add_edge(last, subgraph.first, this->graph_);
46✔
336
            if (subgraph.second != boost::graph_traits<graph::Graph>::null_vertex()) {
46✔
337
                boost::add_edge(subgraph.second, t, this->graph_);
46✔
338
            }
46✔
339
        }
46✔
340

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

348
        auto subgraph = this->traverse(loop_stmt->root());
7✔
349

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

460
      };
130✔
461

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

465
      };
×
466

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

479
    reads_.clear();
130✔
480
    writes_.clear();
130✔
481
    views_.clear();
130✔
482
    moves_.clear();
130✔
483

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

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

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

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

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

563
    this->init_dom_tree();
130✔
564
};
130✔
565

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

575
    return us;
×
576
};
×
577

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

590
    return us;
36✔
591
};
36✔
592

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

602
    return us;
×
603
};
×
604

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

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

622
    return us;
×
623
};
×
624

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

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

642
    return us;
×
643
};
×
644

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

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

662
    return us;
×
663
};
×
664

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

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

687
/****** Domination Analysis ******/
688

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

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

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

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

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

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

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

743
    return uses;
15✔
744
};
15✔
745

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

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

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

771
    return uses;
×
772
};
×
773

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

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

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

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

795
        this->sub_users_.insert(current);
456✔
796

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

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

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

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

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

832
    return us;
20✔
833
};
20✔
834

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

847
    return us;
42✔
848
};
42✔
849

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

859
    return us;
6✔
860
};
6✔
861

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

874
    return us;
46✔
875
};
46✔
876

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

UNCOV
886
    return us;
×
UNCOV
887
};
×
888

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

901
    return us;
7✔
902
};
7✔
903

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

UNCOV
913
    return us;
×
UNCOV
914
};
×
915

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

928
    return us;
7✔
929
};
7✔
930

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

UNCOV
940
    return us;
×
UNCOV
941
};
×
942

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

955
    return us;
7✔
956
};
7✔
957

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

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

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

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

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

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

1004
        if (current == &user2) {
×
1005
            continue;
×
1006
        }
1007

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

1017
    return uses;
×
1018
};
×
1019

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

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

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

1038
        if (current == exit_) {
×
1039
            continue;
×
1040
        }
1041

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

1051
    return uses;
×
1052
};
×
1053

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

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

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

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

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

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

1119
    return locals;
20✔
1120
};
20✔
1121

1122
} // namespace analysis
1123
} // 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