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

daisytuner / sdfglib / 18651924023

20 Oct 2025 12:26PM UTC coverage: 60.977% (-0.6%) from 61.539%
18651924023

push

github

web-flow
Merge pull request #286 from daisytuner/reserved-names

removes restricted globals filtering in codegen

8 of 20 new or added lines in 2 files covered. (40.0%)

342 existing lines in 17 files now uncovered.

9174 of 15045 relevant lines covered (60.98%)

92.1 hits per line

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

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

27
      };
2,271✔
28

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

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

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

35
const std::vector<data_flow::Subset> User::subsets() const {
358✔
36
    if (this->container_ == "") {
358✔
UNCOV
37
        return {};
×
38
    }
39

40
    if (auto access_node = dynamic_cast<data_flow::AccessNode*>(this->element_)) {
358✔
41
        auto& graph = access_node->get_parent();
334✔
42
        if (this->use_ == Use::READ || this->use_ == Use::VIEW) {
334✔
43
            std::vector<data_flow::Subset> subsets;
112✔
44
            for (auto& iedge : graph.out_edges(*access_node)) {
229✔
45
                subsets.push_back(iedge.subset());
117✔
46
            }
47
            return subsets;
112✔
48
        } else if (this->use_ == Use::WRITE || this->use_ == Use::MOVE) {
334✔
49
            std::vector<data_flow::Subset> subsets;
222✔
50
            for (auto& oedge : graph.in_edges(*access_node)) {
444✔
51
                subsets.push_back(oedge.subset());
222✔
52
            }
53
            return subsets;
222✔
54
        }
222✔
UNCOV
55
    }
×
56

57
    // Use of symbol
58
    return {{}};
24✔
59
};
358✔
60

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

72
      };
417✔
73

74
bool ForUser::is_init() const { return this->is_init_; };
421✔
75

76
bool ForUser::is_condition() const { return this->is_condition_; };
325✔
77

78
bool ForUser::is_update() const { return this->is_update_; };
177✔
79

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

88
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
362✔
89
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
243✔
90
                if (dataflow.in_degree(*node) > 0) {
243✔
91
                    Use use = Use::WRITE;
136✔
92

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

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

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

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

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

127
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
112✔
128
                        boost::add_edge(last, v, this->graph_);
20✔
129
                    } else {
20✔
130
                        first = v;
92✔
131
                    }
132
                    last = v;
112✔
133
                }
112✔
134
            }
243✔
135
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
362✔
UNCOV
136
            for (auto& symbol : library_node->symbols()) {
×
UNCOV
137
                auto v = boost::add_vertex(this->graph_);
×
UNCOV
138
                this->add_user(std::make_unique<User>(v, symbol->get_name(), library_node, Use::READ));
×
UNCOV
139
                if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
×
UNCOV
140
                    boost::add_edge(last, v, this->graph_);
×
UNCOV
141
                } else {
×
UNCOV
142
                    first = v;
×
143
                }
UNCOV
144
                last = v;
×
145
            }
UNCOV
146
        }
×
147

148
        for (auto& oedge : dataflow.out_edges(*node)) {
594✔
149
            std::unordered_set<std::string> used;
232✔
150
            for (auto dim : oedge.subset()) {
474✔
151
                for (auto atom : symbolic::atoms(dim)) {
440✔
152
                    if (used.find(atom->get_name()) != used.end()) {
198✔
153
                        continue;
×
154
                    }
155
                    used.insert(atom->get_name());
198✔
156

157
                    auto v = boost::add_vertex(this->graph_);
198✔
158
                    this->add_user(std::make_unique<User>(v, atom->get_name(), &oedge, Use::READ));
198✔
159
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
198✔
160
                        boost::add_edge(last, v, this->graph_);
185✔
161
                    } else {
185✔
162
                        first = v;
13✔
163
                    }
164
                    last = v;
198✔
165
                }
198✔
166
            }
242✔
167
        }
232✔
168
    }
169

170
    return {first, last};
223✔
UNCOV
171
};
×
172

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

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

185
        auto& dataflow = block_stmt->dataflow();
223✔
186
        auto subgraph = this->traverse(dataflow);
223✔
187

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

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

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

203
        graph::Vertex current = s;
290✔
204
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
643✔
205
            auto child = sequence_stmt->at(i);
355✔
206

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

215
            std::unordered_set<std::string> used;
353✔
216
            for (auto& entry : child.second.assignments()) {
439✔
217
                for (auto atom : symbolic::atoms(entry.second)) {
119✔
218
                    if (symbolic::is_pointer(atom)) {
33✔
219
                        continue;
2✔
220
                    }
221
                    if (used.find(atom->get_name()) != used.end()) {
31✔
UNCOV
222
                        continue;
×
223
                    }
224
                    used.insert(atom->get_name());
31✔
225

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

229
                    boost::add_edge(current, v, this->graph_);
31✔
230
                    current = v;
31✔
231
                }
33✔
232
            }
233

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

238
                boost::add_edge(current, v, this->graph_);
86✔
239
                current = v;
86✔
240
            }
241
        }
353✔
242

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

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

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

260
        graph::Vertex last = s;
25✔
261

262
        std::unordered_set<std::string> used;
25✔
263
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
66✔
264
            auto condition = if_else_stmt->at(i).second;
41✔
265
            for (auto atom : symbolic::atoms(condition)) {
69✔
266
                if (used.find(atom->get_name()) != used.end()) {
28✔
267
                    continue;
10✔
268
                }
269
                used.insert(atom->get_name());
18✔
270

271
                auto v = boost::add_vertex(this->graph_);
18✔
272

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

275
                boost::add_edge(last, v, this->graph_);
18✔
276
                last = v;
18✔
277
            }
28✔
278
        }
41✔
279

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

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

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

312
        auto subgraph = this->traverse(loop_stmt->root());
7✔
313

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

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

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

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

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

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

354
        boost::add_edge(last, v, this->graph_);
86✔
355
        last = v;
86✔
356

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

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

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

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

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

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

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

392
        // Back edge
393
        boost::add_edge(t, last, this->graph_);
86✔
394

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

426
    throw std::invalid_argument("Invalid control flow node type");
×
427
};
641✔
428

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

432
      };
152✔
433

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

437
      };
×
438

439
void Users::run(analysis::AnalysisManager& analysis_manager) {
152✔
440
    users_.clear();
152✔
441
    graph_.clear();
152✔
442
    source_ = nullptr;
152✔
443
    sink_ = nullptr;
152✔
444
    users_by_sdfg_.clear();
152✔
445
    users_by_sdfg_loop_condition_.clear();
152✔
446
    users_by_sdfg_loop_init_.clear();
152✔
447
    users_by_sdfg_loop_update_.clear();
152✔
448

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

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

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

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

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

496
    // Collect sub structures
497
    for (auto& entry : this->users_) {
2,423✔
498
        auto container = entry.second->container();
2,271✔
499
        switch (entry.second->use()) {
2,271✔
500
            case Use::READ: {
501
                if (this->reads_.find(container) == this->reads_.end()) {
592✔
502
                    this->reads_.insert({container, {}});
287✔
503
                }
287✔
504
                this->reads_[container].push_back(entry.second.get());
592✔
505
                break;
592✔
506
            }
507
            case Use::WRITE: {
508
                if (this->writes_.find(container) == this->writes_.end()) {
380✔
509
                    this->writes_.insert({container, {}});
251✔
510
                }
251✔
511
                this->writes_[container].push_back(entry.second.get());
380✔
512
                break;
380✔
513
            }
514
            case Use::VIEW: {
515
                if (this->views_.find(container) == this->views_.end()) {
14✔
516
                    this->views_.insert({container, {}});
14✔
517
                }
14✔
518
                this->views_[container].push_back(entry.second.get());
14✔
519
                break;
14✔
520
            }
521
            case Use::MOVE: {
522
                if (this->moves_.find(container) == this->moves_.end()) {
14✔
523
                    this->moves_.insert({container, {}});
13✔
524
                }
13✔
525
                this->moves_[container].push_back(entry.second.get());
14✔
526
                break;
14✔
527
            }
528
            default:
529
                break;
1,271✔
530
        }
531
    }
2,271✔
532
};
152✔
533

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

UNCOV
543
    return us;
×
UNCOV
544
};
×
545

546
std::vector<User*> Users::uses(const std::string& container) const {
30✔
547
    std::vector<User*> us;
30✔
548
    for (auto& entry : this->users_) {
608✔
549
        if (entry.second->container() != container) {
578✔
550
            continue;
434✔
551
        }
552
        if (entry.second->use() == Use::NOP) {
144✔
UNCOV
553
            continue;
×
554
        }
555
        us.push_back(entry.second.get());
144✔
556
    }
557

558
    return us;
30✔
559
};
30✔
560

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

UNCOV
570
    return us;
×
UNCOV
571
};
×
572

573
std::vector<User*> Users::writes(const std::string& container) const {
60✔
574
    if (this->writes_.find(container) == this->writes_.end()) {
60✔
575
        return {};
35✔
576
    } else {
577
        return this->writes_.at(container);
25✔
578
    }
579
};
60✔
580

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

UNCOV
590
    return us;
×
UNCOV
591
};
×
592

593
std::vector<User*> Users::reads(const std::string& container) const {
49✔
594
    if (this->reads_.find(container) == this->reads_.end()) {
49✔
595
        return {};
20✔
596
    } else {
597
        return this->reads_.at(container);
29✔
598
    }
599
};
49✔
600

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

UNCOV
610
    return us;
×
UNCOV
611
};
×
612

613
std::vector<User*> Users::views(const std::string& container) const {
68✔
614
    if (this->views_.find(container) == this->views_.end()) {
68✔
615
        return {};
65✔
616
    } else {
617
        return this->views_.at(container);
3✔
618
    }
619
};
68✔
620

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

UNCOV
630
    return us;
×
UNCOV
631
};
×
632

633
std::vector<User*> Users::moves(const std::string& container) const {
76✔
634
    if (this->moves_.find(container) == this->moves_.end()) {
76✔
635
        return {};
73✔
636
    } else {
637
        return this->moves_.at(container);
3✔
638
    }
639
};
76✔
640

641
structured_control_flow::ControlFlowNode* Users::scope(User* user) {
296✔
642
    if (auto data_node = dynamic_cast<data_flow::DataFlowNode*>(user->element())) {
296✔
643
        return static_cast<structured_control_flow::Block*>(data_node->get_parent().get_parent());
296✔
UNCOV
644
    } else if (auto memlet = dynamic_cast<data_flow::Memlet*>(user->element())) {
×
UNCOV
645
        return static_cast<structured_control_flow::Block*>(memlet->get_parent().get_parent());
×
UNCOV
646
    } else if (auto transition = dynamic_cast<structured_control_flow::Transition*>(user->element())) {
×
UNCOV
647
        return &transition->parent();
×
648
    } else {
UNCOV
649
        auto user_element = dynamic_cast<structured_control_flow::ControlFlowNode*>(user->element());
×
UNCOV
650
        assert(user_element != nullptr && "Users::scope: User element is not a ControlFlowNode");
×
UNCOV
651
        return user_element;
×
652
    }
653
}
296✔
654

655
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
13✔
656
    std::unordered_set<User*> uses;
13✔
657
    std::unordered_set<User*> visited;
13✔
658
    std::list<User*> queue = {&user2};
13✔
659
    while (!queue.empty()) {
85✔
660
        auto current = queue.front();
72✔
661
        queue.pop_front();
72✔
662

663
        // Stop conditions
664
        if (current == &user1) {
72✔
665
            continue;
13✔
666
        }
667

668
        if (visited.find(current) != visited.end()) {
59✔
669
            continue;
2✔
670
        }
671
        visited.insert(current);
57✔
672

673
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
57✔
674
            uses.insert(current);
9✔
675
        }
9✔
676

677
        // Extend search
678
        // Backward search to utilize domination user1 over user
679
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
57✔
680
        auto edges = std::ranges::subrange(eb, ee);
114✔
681
        for (auto edge : edges) {
116✔
682
            auto v = boost::source(edge, this->graph_);
59✔
683
            queue.push_back(this->users_.at(v).get());
59✔
684
        }
685
    }
686

687
    return uses;
13✔
688
};
13✔
689

UNCOV
690
const std::unordered_set<User*> Users::all_uses_after(User& user) {
×
UNCOV
691
    std::unordered_set<User*> uses;
×
UNCOV
692
    std::unordered_set<User*> visited;
×
UNCOV
693
    std::list<User*> queue = {&user};
×
UNCOV
694
    while (!queue.empty()) {
×
UNCOV
695
        auto current = queue.front();
×
UNCOV
696
        queue.pop_front();
×
UNCOV
697
        if (visited.find(current) != visited.end()) {
×
UNCOV
698
            continue;
×
699
        }
UNCOV
700
        visited.insert(current);
×
701

702
        if (current != &user && current->use() != Use::NOP) {
×
703
            uses.insert(current);
×
704
        }
×
705

706
        // Extend search
707
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
708
        auto edges = std::ranges::subrange(eb, ee);
×
709
        for (auto edge : edges) {
×
UNCOV
710
            auto v = boost::target(edge, this->graph_);
×
711
            queue.push_back(this->users_.at(v).get());
×
712
        }
713
    }
714

715
    return uses;
×
UNCOV
716
};
×
717

718
const std::vector<std::string> Users::all_containers_in_order() {
×
719
    std::unordered_set<std::string> unique_containers;
×
720
    std::vector<std::string> containers;
×
721

722
    // BFS traversal
UNCOV
723
    std::unordered_set<User*> visited;
×
UNCOV
724
    std::list<User*> queue = {this->source_};
×
UNCOV
725
    while (!queue.empty()) {
×
726
        auto current = queue.front();
×
727
        queue.pop_front();
×
UNCOV
728
        if (visited.find(current) != visited.end()) {
×
729
            continue;
×
730
        }
731
        visited.insert(current);
×
732

UNCOV
733
        if ((current->container() != "" || current->use() != Use::NOP) &&
×
734
            unique_containers.find(current->container()) == unique_containers.end()) {
×
735
            unique_containers.insert(current->container());
×
736
            containers.push_back(current->container());
×
737
        }
×
738

739
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
740
        auto edges = std::ranges::subrange(eb, ee);
×
UNCOV
741
        for (auto edge : edges) {
×
742
            auto v = boost::target(edge, this->graph_);
×
UNCOV
743
            queue.push_back(this->users_.at(v).get());
×
744
        }
745
    }
746
    return containers;
×
747
}
×
748

749
UsersView::UsersView(Users& users, const structured_control_flow::ControlFlowNode& node) : users_(users) {
49✔
750
    this->entry_ = users.entries_.at(&node);
49✔
751
    this->exit_ = users.exits_.at(&node);
49✔
752

753
    // Collect sub users
754
    std::unordered_set<User*> visited;
49✔
755
    std::list<User*> queue = {this->exit_};
49✔
756
    while (!queue.empty()) {
671✔
757
        auto current = queue.front();
622✔
758
        queue.pop_front();
622✔
759

760
        // Stop conditions
761
        if (current == this->entry_) {
622✔
762
            continue;
49✔
763
        }
764

765
        if (visited.find(current) != visited.end()) {
573✔
766
            continue;
70✔
767
        }
768
        visited.insert(current);
503✔
769

770
        this->sub_users_.insert(current);
503✔
771

772
        // Extend search
773
        // Backward search to utilize domination user1 over user
774
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
503✔
775
        auto edges = std::ranges::subrange(eb, ee);
1,006✔
776
        for (auto edge : edges) {
1,076✔
777
            auto v = boost::source(edge, users.graph_);
573✔
778
            queue.push_back(users.users_.at(v).get());
573✔
779
        }
780
    }
781
};
49✔
782

783
std::vector<User*> UsersView::uses() const {
19✔
784
    std::vector<User*> us;
19✔
785
    for (auto& user : this->sub_users_) {
133✔
786
        if (user->use() == Use::NOP) {
114✔
787
            continue;
67✔
788
        }
789
        us.push_back(user);
47✔
790
    }
791

792
    return us;
19✔
793
};
19✔
794

795
std::vector<User*> UsersView::uses(const std::string& container) const {
36✔
796
    std::vector<User*> us;
36✔
797
    for (auto& user : this->sub_users_) {
384✔
798
        if (user->container() != container) {
348✔
799
            continue;
258✔
800
        }
801
        if (user->use() == Use::NOP) {
90✔
UNCOV
802
            continue;
×
803
        }
804
        us.push_back(user);
90✔
805
    }
806

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

810
std::vector<User*> UsersView::writes() const {
2✔
811
    std::vector<User*> us;
2✔
812
    for (auto& user : this->sub_users_) {
18✔
813
        if (user->use() != Use::WRITE) {
16✔
814
            continue;
14✔
815
        }
816
        us.push_back(user);
2✔
817
    }
818

819
    return us;
2✔
820
};
2✔
821

822
std::vector<User*> UsersView::writes(const std::string& container) const {
15✔
823
    std::vector<User*> us;
15✔
824
    for (auto& user : this->sub_users_) {
273✔
825
        if (user->container() != container) {
258✔
826
            continue;
244✔
827
        }
828
        if (user->use() != Use::WRITE) {
14✔
829
            continue;
5✔
830
        }
831
        us.push_back(user);
9✔
832
    }
833

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

UNCOV
837
std::vector<User*> UsersView::reads() const {
×
UNCOV
838
    std::vector<User*> us;
×
UNCOV
839
    for (auto& user : this->sub_users_) {
×
UNCOV
840
        if (user->use() != Use::READ) {
×
UNCOV
841
            continue;
×
842
        }
UNCOV
843
        us.push_back(user);
×
844
    }
845

UNCOV
846
    return us;
×
UNCOV
847
};
×
848

849
std::vector<User*> UsersView::reads(const std::string& container) const {
19✔
850
    std::vector<User*> us;
19✔
851
    for (auto& user : this->sub_users_) {
342✔
852
        if (user->container() != container) {
323✔
853
            continue;
284✔
854
        }
855
        if (user->use() != Use::READ) {
39✔
856
            continue;
18✔
857
        }
858
        us.push_back(user);
21✔
859
    }
860

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

864
std::vector<User*> UsersView::views() const {
8✔
865
    std::vector<User*> us;
8✔
866
    for (auto& user : this->sub_users_) {
134✔
867
        if (user->use() != Use::VIEW) {
126✔
868
            continue;
126✔
869
        }
UNCOV
870
        us.push_back(user);
×
871
    }
872

873
    return us;
8✔
874
};
8✔
875

876
std::vector<User*> UsersView::views(const std::string& container) const {
15✔
877
    std::vector<User*> us;
15✔
878
    for (auto& user : this->sub_users_) {
273✔
879
        if (user->container() != container) {
258✔
880
            continue;
244✔
881
        }
882
        if (user->use() != Use::VIEW) {
14✔
883
            continue;
14✔
884
        }
UNCOV
885
        us.push_back(user);
×
886
    }
887

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

891
std::vector<User*> UsersView::moves() const {
9✔
892
    std::vector<User*> us;
9✔
893
    for (auto& user : this->sub_users_) {
153✔
894
        if (user->use() != Use::MOVE) {
144✔
895
            continue;
143✔
896
        }
897
        us.push_back(user);
1✔
898
    }
899

900
    return us;
9✔
901
};
9✔
902

903
std::vector<User*> UsersView::moves(const std::string& container) const {
15✔
904
    std::vector<User*> us;
15✔
905
    for (auto& user : this->sub_users_) {
273✔
906
        if (user->container() != container) {
258✔
907
            continue;
244✔
908
        }
909
        if (user->use() != Use::MOVE) {
14✔
910
            continue;
14✔
911
        }
UNCOV
912
        us.push_back(user);
×
913
    }
914

915
    return us;
15✔
916
};
15✔
917

UNCOV
918
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
UNCOV
919
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
UNCOV
920
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
921

UNCOV
922
    std::unordered_set<User*> uses;
×
923
    std::unordered_set<User*> visited;
×
UNCOV
924
    std::list<User*> queue = {&user1};
×
UNCOV
925
    while (!queue.empty()) {
×
UNCOV
926
        auto current = queue.front();
×
UNCOV
927
        queue.pop_front();
×
UNCOV
928
        if (visited.find(current) != visited.end()) {
×
929
            continue;
×
930
        }
931
        visited.insert(current);
×
932

933
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
934
            uses.insert(current);
×
935
        }
×
936

937
        // Stop conditions
938
        if (current == exit_) {
×
939
            continue;
×
940
        }
941

942
        if (current == &user2) {
×
UNCOV
943
            continue;
×
944
        }
945

946
        // Extend search
UNCOV
947
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
UNCOV
948
        auto edges = std::ranges::subrange(eb, ee);
×
949
        for (auto edge : edges) {
×
950
            auto v = boost::target(edge, this->users_.graph_);
×
UNCOV
951
            queue.push_back(this->users_.users_.at(v).get());
×
952
        }
953
    }
954

UNCOV
955
    return uses;
×
UNCOV
956
};
×
957

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

961
    std::unordered_set<User*> uses;
×
962
    std::unordered_set<User*> visited;
×
UNCOV
963
    std::list<User*> queue = {&user};
×
UNCOV
964
    while (!queue.empty()) {
×
UNCOV
965
        auto current = queue.front();
×
966
        queue.pop_front();
×
967
        if (visited.find(current) != visited.end()) {
×
UNCOV
968
            continue;
×
969
        }
970
        visited.insert(current);
×
971

972
        if (current != &user && current->use() != Use::NOP) {
×
973
            uses.insert(current);
×
974
        }
×
975

976
        if (current == exit_) {
×
977
            continue;
×
978
        }
979

980
        // Extend search
981
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
UNCOV
982
        auto edges = std::ranges::subrange(eb, ee);
×
983
        for (auto edge : edges) {
×
984
            auto v = boost::target(edge, this->users_.graph_);
×
985
            queue.push_back(this->users_.users_.at(v).get());
×
986
        }
987
    }
988

UNCOV
989
    return uses;
×
UNCOV
990
};
×
991

992
User* Users::
993
    get_user(const std::string& container, Element* element, Use use, bool is_init, bool is_condition, bool is_update) {
892✔
994
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element)) {
892✔
995
        if (is_init) {
326✔
996
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
74✔
997
            return tmp;
74✔
998
        } else if (is_condition) {
252✔
999
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
120✔
1000
        } else if (is_update) {
132✔
1001
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
132✔
1002
        }
UNCOV
1003
    }
×
1004
    if (users_by_sdfg_.find(container) == users_by_sdfg_.end() ||
1,132✔
1005
        users_by_sdfg_.at(container).find(element) == users_by_sdfg_.at(container).end() ||
566✔
1006
        users_by_sdfg_.at(container).at(element).find(use) == users_by_sdfg_.at(container).at(element).end()) {
566✔
1007
        return nullptr;
84✔
1008
    }
1009

1010
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
482✔
1011
    return tmp;
482✔
1012
}
892✔
1013

1014
void Users::add_user(std::unique_ptr<User> user) {
1,000✔
1015
    auto vertex = user->vertex_;
1,000✔
1016
    this->users_.insert({vertex, std::move(user)});
1,000✔
1017

1018
    auto user_ptr = this->users_.at(vertex).get();
1,000✔
1019
    auto* target_structure = &users_by_sdfg_;
1,000✔
1020
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,000✔
1021
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
417✔
1022
        if (for_loop == nullptr) {
417✔
UNCOV
1023
            throw std::invalid_argument("Invalid user type");
×
1024
        }
1025
        if (for_user->is_init()) {
417✔
1026
            target_structure = &users_by_sdfg_loop_init_;
95✔
1027
        } else if (for_user->is_condition()) {
417✔
1028
            target_structure = &users_by_sdfg_loop_condition_;
150✔
1029
        } else if (for_user->is_update()) {
322✔
1030
            target_structure = &users_by_sdfg_loop_update_;
172✔
1031
        } else {
172✔
UNCOV
1032
            throw std::invalid_argument("Invalid user type");
×
1033
        }
1034
    }
417✔
1035

1036
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,000✔
1037
        target_structure->insert({user_ptr->container(), {}});
651✔
1038
    }
651✔
1039
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
2,000✔
1040
        (*target_structure)[user_ptr->container()].end()) {
1,000✔
1041
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
908✔
1042
    }
908✔
1043
    target_structure->at(user_ptr->container()).at(user_ptr->element()).insert({user_ptr->use(), user_ptr});
1,000✔
1044
}
1,000✔
1045

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

1049
    // Locals have no uses outside of the node
1050
    // We can check this by comparing the number of uses of the container in the view and the total
1051
    // number of uses of the container in the users map.
1052
    std::unordered_set<std::string> locals;
19✔
1053
    UsersView view(*this, node);
19✔
1054
    for (auto& user : view.uses()) {
66✔
1055
        if (!sdfg.is_transient(user->container())) {
47✔
1056
            continue;
19✔
1057
        }
1058
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
28✔
1059
            locals.insert(user->container());
10✔
1060
        }
10✔
1061
    }
1062

1063
    return locals;
19✔
1064
};
19✔
1065

UNCOV
1066
const std::vector<std::string> UsersView::all_containers_in_order() {
×
UNCOV
1067
    std::unordered_set<std::string> unique_containers;
×
UNCOV
1068
    std::vector<std::string> containers;
×
1069

1070
    // BFS traversal
1071
    std::unordered_set<User*> visited;
×
1072
    std::list<User*> queue = {this->entry_};
×
1073
    while (!queue.empty()) {
×
UNCOV
1074
        auto current = queue.front();
×
UNCOV
1075
        queue.pop_front();
×
1076
        if (visited.find(current) != visited.end()) {
×
1077
            continue;
×
1078
        }
1079
        visited.insert(current);
×
1080

1081
        if ((current->container() != "" || current->use() != Use::NOP) &&
×
1082
            unique_containers.find(current->container()) == unique_containers.end()) {
×
UNCOV
1083
            unique_containers.insert(current->container());
×
1084
            containers.push_back(current->container());
×
UNCOV
1085
        }
×
1086

1087
        if (current == this->exit_) {
×
1088
            continue;
×
1089
        }
1090

UNCOV
1091
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1092
        auto edges = std::ranges::subrange(eb, ee);
×
1093
        for (auto edge : edges) {
×
UNCOV
1094
            auto v = boost::target(edge, this->users_.graph_);
×
UNCOV
1095
            queue.push_back(this->users_.users_.at(v).get());
×
1096
        }
1097
    }
1098

1099
    return containers;
×
1100
}
×
1101

1102
} // namespace analysis
1103
} // 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