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

daisytuner / sdfglib / 18718471628

22 Oct 2025 01:51PM UTC coverage: 61.746% (-0.6%) from 62.358%
18718471628

push

github

web-flow
Merge pull request #288 from daisytuner/InstrumentationInfo

Instrumentation info

104 of 310 new or added lines in 20 files covered. (33.55%)

25 existing lines in 6 files now uncovered.

9746 of 15784 relevant lines covered (61.75%)

100.97 hits per line

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

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

27
      };
2,959✔
28

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

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

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

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

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

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

61
ForUser::ForUser(
652✔
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) {
652✔
71

72
      };
652✔
73

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

188
        // May be empty
189
        if (subgraph.first == boost::graph_traits<graph::Graph>::null_vertex()) {
250✔
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_);
156✔
195
        boost::add_edge(subgraph.second, t, this->graph_);
156✔
196

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

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

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

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

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

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

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

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

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

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

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

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

262
        std::unordered_set<std::string> used;
26✔
263
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
69✔
264
            auto condition = if_else_stmt->at(i).second;
43✔
265
            for (auto atom : symbolic::atoms(condition)) {
73✔
266
                if (used.find(atom->get_name()) != used.end()) {
30✔
267
                    continue;
11✔
268
                }
269
                used.insert(atom->get_name());
19✔
270

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

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

275
                boost::add_edge(last, v, this->graph_);
19✔
276
                last = v;
19✔
277
            }
30✔
278
        }
43✔
279

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

295
        // Forward edge: Potentially missing else case
296
        if (!if_else_stmt->is_complete()) {
26✔
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};
26✔
306
    } else if (auto loop_stmt = dynamic_cast<structured_control_flow::While*>(&node)) {
175✔
307
        // NOP
308
        auto s = boost::add_vertex(this->graph_);
8✔
309
        this->users_.insert({s, std::make_unique<User>(s, "", loop_stmt, Use::NOP)});
8✔
310
        this->entries_.insert({loop_stmt, this->users_.at(s).get()});
8✔
311

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

395
        return {s, t};
130✔
396
    } else if (auto cont_stmt = dynamic_cast<structured_control_flow::Continue*>(&node)) {
11✔
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)) {
7✔
404
        // Approximated by general back edge in loop scope
405
        auto v = boost::add_vertex(this->graph_);
5✔
406
        this->users_.insert({v, std::make_unique<User>(v, "", br_stmt, Use::NOP)});
5✔
407
        this->entries_.insert({br_stmt, this->users_.at(v).get()});
5✔
408
        this->exits_.insert({br_stmt, this->users_.at(v).get()});
5✔
409
        return {v, v};
5✔
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
};
806✔
428

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

432
      };
195✔
433

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

437
      };
×
438

439
void Users::run(analysis::AnalysisManager& analysis_manager) {
195✔
440
    users_.clear();
195✔
441
    graph_.clear();
195✔
442
    source_ = nullptr;
195✔
443
    sink_ = nullptr;
195✔
444
    this->entries_.clear();
195✔
445
    this->exits_.clear();
195✔
446
    users_by_sdfg_.clear();
195✔
447
    users_by_sdfg_loop_condition_.clear();
195✔
448
    users_by_sdfg_loop_init_.clear();
195✔
449
    users_by_sdfg_loop_update_.clear();
195✔
450

451
    reads_.clear();
195✔
452
    writes_.clear();
195✔
453
    views_.clear();
195✔
454
    moves_.clear();
195✔
455

456
    this->traverse(node_);
195✔
457
    if (this->users_.empty()) {
195✔
458
        return;
×
459
    }
460

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

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

493
        this->sink_ = this->users_.at(v).get();
1✔
494
    } else {
1✔
495
        this->sink_ = sinks.front();
194✔
496
    }
497

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

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

545
    return us;
×
546
};
×
547

548
std::vector<User*> Users::uses(const std::string& container) const {
230✔
549
    std::vector<User*> us;
230✔
550
    for (auto& entry : this->users_) {
5,159✔
551
        if (entry.second->container() != container) {
4,929✔
552
            continue;
4,521✔
553
        }
554
        if (entry.second->use() == Use::NOP) {
408✔
555
            continue;
×
556
        }
557
        us.push_back(entry.second.get());
408✔
558
    }
559

560
    return us;
230✔
561
};
230✔
562

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

572
    return us;
×
573
};
×
574

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

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

592
    return us;
×
593
};
×
594

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

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

612
    return us;
×
613
};
×
614

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

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

632
    return us;
×
633
};
×
634

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

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

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

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

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

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

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

689
    return uses;
13✔
690
};
13✔
691

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

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

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

717
    return uses;
×
718
};
×
719

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

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

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

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

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

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

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

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

772
        this->sub_users_.insert(current);
503✔
773

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

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

794
    return us;
19✔
795
};
19✔
796

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

809
    return us;
36✔
810
};
36✔
811

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

821
    return us;
2✔
822
};
2✔
823

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

836
    return us;
15✔
837
};
15✔
838

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

848
    return us;
×
849
};
×
850

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

863
    return us;
19✔
864
};
19✔
865

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

875
    return us;
8✔
876
};
8✔
877

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

890
    return us;
15✔
891
};
15✔
892

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

902
    return us;
9✔
903
};
9✔
904

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

917
    return us;
15✔
918
};
15✔
919

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

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

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

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

944
        if (current == &user2) {
×
945
            continue;
×
946
        }
947

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

957
    return uses;
×
958
};
×
959

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

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

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

978
        if (current == exit_) {
×
979
            continue;
×
980
        }
981

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

991
    return uses;
×
992
};
×
993

994
bool Users::
995
    has_user(const std::string& container, Element* element, Use use, bool is_init, bool is_condition, bool is_update) {
112✔
996
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element)) {
112✔
997
        if (is_init) {
×
998
            if (users_by_sdfg_loop_init_.find(container) == users_by_sdfg_loop_init_.end() ||
×
999
                users_by_sdfg_loop_init_.at(container).find(for_loop) == users_by_sdfg_loop_init_.at(container).end() ||
×
1000
                users_by_sdfg_loop_init_.at(container).at(for_loop).find(use) ==
×
1001
                    users_by_sdfg_loop_init_.at(container).at(for_loop).end()) {
×
1002
                return false;
×
1003
            }
1004
            return true;
×
1005
        } else if (is_condition) {
×
1006
            if (users_by_sdfg_loop_condition_.find(container) == users_by_sdfg_loop_condition_.end() ||
×
1007
                users_by_sdfg_loop_condition_.at(container).find(for_loop) ==
×
1008
                    users_by_sdfg_loop_condition_.at(container).end() ||
×
1009
                users_by_sdfg_loop_condition_.at(container).at(for_loop).find(use) ==
×
1010
                    users_by_sdfg_loop_condition_.at(container).at(for_loop).end()) {
×
1011
                return false;
×
1012
            }
1013
            return true;
×
1014
        } else if (is_update) {
×
1015
            if (users_by_sdfg_loop_update_.find(container) == users_by_sdfg_loop_update_.end() ||
×
1016
                users_by_sdfg_loop_update_.at(container).find(for_loop) ==
×
1017
                    users_by_sdfg_loop_update_.at(container).end() ||
×
1018
                users_by_sdfg_loop_update_.at(container).at(for_loop).find(use) ==
×
1019
                    users_by_sdfg_loop_update_.at(container).at(for_loop).end()) {
×
1020
                return false;
×
1021
            }
1022
            return true;
×
1023
        }
1024
    }
×
1025
    if (users_by_sdfg_.find(container) == users_by_sdfg_.end() ||
224✔
1026
        users_by_sdfg_.at(container).find(element) == users_by_sdfg_.at(container).end() ||
112✔
1027
        users_by_sdfg_.at(container).at(element).find(use) == users_by_sdfg_.at(container).at(element).end()) {
112✔
1028
        return false;
84✔
1029
    }
1030

1031
    return true;
28✔
1032
}
112✔
1033

1034
User* Users::
1035
    get_user(const std::string& container, Element* element, Use use, bool is_init, bool is_condition, bool is_update) {
811✔
1036
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element)) {
811✔
1037
        if (is_init) {
326✔
1038
            auto tmp = users_by_sdfg_loop_init_.at(container).at(for_loop).at(use);
74✔
1039
            return tmp;
74✔
1040
        } else if (is_condition) {
252✔
1041
            return users_by_sdfg_loop_condition_.at(container).at(for_loop).at(use);
120✔
1042
        } else if (is_update) {
132✔
1043
            return users_by_sdfg_loop_update_.at(container).at(for_loop).at(use);
132✔
1044
        }
1045
    }
×
1046

1047
    auto tmp = users_by_sdfg_.at(container).at(element).at(use);
485✔
1048
    return tmp;
485✔
1049
}
811✔
1050

1051
void Users::add_user(std::unique_ptr<User> user) {
1,356✔
1052
    auto vertex = user->vertex_;
1,356✔
1053
    this->users_.insert({vertex, std::move(user)});
1,356✔
1054

1055
    auto user_ptr = this->users_.at(vertex).get();
1,356✔
1056
    auto* target_structure = &users_by_sdfg_;
1,356✔
1057
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,356✔
1058
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
652✔
1059
        if (for_loop == nullptr) {
652✔
1060
            throw std::invalid_argument("Invalid user type");
×
1061
        }
1062
        if (for_user->is_init()) {
652✔
1063
            target_structure = &users_by_sdfg_loop_init_;
146✔
1064
        } else if (for_user->is_condition()) {
652✔
1065
            target_structure = &users_by_sdfg_loop_condition_;
246✔
1066
        } else if (for_user->is_update()) {
506✔
1067
            target_structure = &users_by_sdfg_loop_update_;
260✔
1068
        } else {
260✔
1069
            throw std::invalid_argument("Invalid user type");
×
1070
        }
1071
    }
652✔
1072

1073
    if (target_structure->find(user_ptr->container()) == target_structure->end()) {
1,356✔
1074
        target_structure->insert({user_ptr->container(), {}});
906✔
1075
    }
906✔
1076
    if ((*target_structure)[user_ptr->container()].find(user_ptr->element()) ==
2,712✔
1077
        (*target_structure)[user_ptr->container()].end()) {
1,356✔
1078
        target_structure->at(user_ptr->container()).insert({user_ptr->element(), {}});
1,219✔
1079
    }
1,219✔
1080
    target_structure->at(user_ptr->container()).at(user_ptr->element()).insert({user_ptr->use(), user_ptr});
1,356✔
1081
}
1,356✔
1082

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

1086
    // Locals have no uses outside of the node
1087
    // We can check this by comparing the number of uses of the container in the view and the total
1088
    // number of uses of the container in the users map.
1089
    std::unordered_set<std::string> locals;
19✔
1090
    UsersView view(*this, node);
19✔
1091
    for (auto& user : view.uses()) {
66✔
1092
        if (!sdfg.is_transient(user->container())) {
47✔
1093
            continue;
19✔
1094
        }
1095
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
28✔
1096
            locals.insert(user->container());
10✔
1097
        }
10✔
1098
    }
1099

1100
    return locals;
19✔
1101
};
19✔
1102

1103
const std::vector<std::string> UsersView::all_containers_in_order() {
×
1104
    std::unordered_set<std::string> unique_containers;
×
1105
    std::vector<std::string> containers;
×
1106

1107
    // BFS traversal
1108
    std::unordered_set<User*> visited;
×
1109
    std::list<User*> queue = {this->entry_};
×
1110
    while (!queue.empty()) {
×
1111
        auto current = queue.front();
×
1112
        queue.pop_front();
×
1113
        if (visited.find(current) != visited.end()) {
×
1114
            continue;
×
1115
        }
1116
        visited.insert(current);
×
1117

1118
        if ((current->container() != "" || current->use() != Use::NOP) &&
×
1119
            unique_containers.find(current->container()) == unique_containers.end()) {
×
1120
            unique_containers.insert(current->container());
×
1121
            containers.push_back(current->container());
×
1122
        }
×
1123

1124
        if (current == this->exit_) {
×
1125
            continue;
×
1126
        }
1127

1128
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1129
        auto edges = std::ranges::subrange(eb, ee);
×
1130
        for (auto edge : edges) {
×
1131
            auto v = boost::target(edge, this->users_.graph_);
×
1132
            queue.push_back(this->users_.users_.at(v).get());
×
1133
        }
1134
    }
1135

1136
    return containers;
×
1137
}
×
1138

1139
} // namespace analysis
1140
} // 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