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

daisytuner / sdfglib / 20263852258

16 Dec 2025 09:58AM UTC coverage: 40.086% (-0.2%) from 40.252%
20263852258

push

github

web-flow
Merge pull request #392 from daisytuner/for2map-performance

refactors passes to improve performance

13621 of 43972 branches covered (30.98%)

Branch coverage included in aggregate %.

126 of 173 new or added lines in 14 files covered. (72.83%)

55 existing lines in 8 files now uncovered.

11633 of 19027 relevant lines covered (61.14%)

90.79 hits per line

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

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

27
      };
3,331✔
28

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

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

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

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

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

57
    // Use of symbol
58
    return {{}};
42!
59
};
321✔
60

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

72
      };
647✔
73

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

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

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

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

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

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

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

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

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

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

127
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
225!
128
                        boost::add_edge(last, v, this->graph_);
52!
129
                    } else {
52✔
130
                        first = v;
173✔
131
                    }
132
                    last = v;
225✔
133
                }
225✔
134
            }
446✔
135
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
644!
136
            for (auto& symbol : library_node->symbols()) {
12!
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
        }
12✔
147

148
        for (auto& oedge : dataflow.out_edges(*node)) {
1,070!
149
            std::unordered_set<std::string> used;
426✔
150
            for (auto dim : oedge.subset()) {
762!
151
                for (auto atom : symbolic::atoms(dim)) {
546!
152
                    if (used.find(atom->get_name()) != used.end()) {
210!
153
                        continue;
×
154
                    }
155
                    used.insert(atom->get_name());
210!
156

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

243
        if (current == boost::graph_traits<graph::Graph>::null_vertex()) {
424!
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_);
424✔
249
        this->users_.insert({t, std::make_unique<User>(t, "", sequence_stmt, Use::NOP)});
424!
250
        boost::add_edge(current, t, this->graph_);
424✔
251
        this->exits_.insert({sequence_stmt, this->users_.at(t).get()});
424✔
252

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

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

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

271
                auto v = boost::add_vertex(this->graph_);
21!
272

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

275
                boost::add_edge(last, v, this->graph_);
21!
276
                last = v;
21✔
277
            }
32✔
278
        }
45✔
279

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

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

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

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

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

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

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

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

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

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

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

357
        // Condition
358
        for (auto atom : symbolic::atoms(for_stmt->condition())) {
375!
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());
129✔
368
        boost::add_edge(last, subgraph.first, this->graph_);
129✔
369

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

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

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

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

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

395
        return {s, t};
129✔
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!
412
            auto v = boost::add_vertex(this->graph_);
×
413
            this->users_.insert({v, std::make_unique<User>(v, "", ret_stmt, Use::NOP)});
×
414
            this->entries_.insert({ret_stmt, this->users_.at(v).get()});
×
415
            this->exits_.insert({ret_stmt, this->users_.at(v).get()});
×
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
};
926✔
428

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

432
      };
235✔
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) {
235✔
440
    users_.clear();
235✔
441
    graph_.clear();
235✔
442
    source_ = nullptr;
235✔
443
    sink_ = nullptr;
235✔
444
    this->entries_.clear();
235✔
445
    this->exits_.clear();
235✔
446
    this->users_lookup_.clear();
235✔
447

448
    reads_.clear();
235✔
449
    writes_.clear();
235✔
450
    views_.clear();
235✔
451
    moves_.clear();
235✔
452

453
    this->traverse(node_);
235✔
454
    if (this->users_.empty()) {
235!
455
        return;
×
456
    }
457

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

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

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

495
    for (auto& container : sdfg_.containers()) {
904!
496
        this->reads_.insert({container, {}});
669!
497
        this->writes_.insert({container, {}});
669!
498
        this->views_.insert({container, {}});
669!
499
        this->moves_.insert({container, {}});
669!
500
    }
501

502
    // Collect sub structures
503
    for (auto& entry : this->users_) {
3,561✔
504
        auto container = entry.second->container();
3,326!
505
        if (entry.second->use() == Use::NOP) {
3,326✔
506
            continue;
1,840✔
507
        }
508
        if (container == "") {
1,486!
NEW
509
            continue;
×
510
        }
511

512
        switch (entry.second->use()) {
1,486!
513
            case Use::READ: {
514
                this->reads_[container].push_back(entry.second.get());
854!
515
                break;
854✔
516
            }
517
            case Use::WRITE: {
518
                this->writes_[container].push_back(entry.second.get());
566!
519
                break;
566✔
520
            }
521
            case Use::VIEW: {
522
                this->views_[container].push_back(entry.second.get());
32!
523
                break;
32✔
524
            }
525
            case Use::MOVE: {
526
                this->moves_[container].push_back(entry.second.get());
34!
527
                break;
34✔
528
            }
529
            default:
UNCOV
530
                break;
×
531
        }
532
    }
3,326✔
533
};
235✔
534

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

544
    return us;
×
545
};
×
546

547
std::list<User*> Users::uses(const std::string& container) const {
262✔
548
    std::list<User*> us;
262✔
549
    for (auto& entry : this->users_) {
5,579✔
550
        if (entry.second->container() != container) {
5,317✔
551
            continue;
4,860✔
552
        }
553
        if (entry.second->use() == Use::NOP) {
457!
554
            continue;
×
555
        }
556
        us.push_back(entry.second.get());
457!
557
    }
558

559
    return us;
262✔
560
};
262!
561

NEW
562
size_t Users::num_uses(const std::string& container) const { return this->uses(container).size(); };
×
563

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

573
    return us;
×
574
};
×
575

576
const std::list<User*>& Users::writes(const std::string& container) const { return this->writes_.at(container); };
72✔
577

578
size_t Users::num_writes(const std::string& container) const { return this->writes(container).size(); };
28✔
579

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

589
    return us;
×
590
};
×
591

592
const std::list<User*>& Users::reads(const std::string& container) const { return this->reads_.at(container); };
156✔
593

594
size_t Users::num_reads(const std::string& container) const { return this->reads(container).size(); };
21✔
595

NEW
596
std::list<User*> Users::views() const {
×
NEW
597
    std::list<User*> us;
×
598
    for (auto& entry : this->users_) {
×
599
        if (entry.second->use() != Use::VIEW) {
×
600
            continue;
×
601
        }
602
        us.push_back(entry.second.get());
×
603
    }
604

605
    return us;
×
606
};
×
607

608
const std::list<User*>& Users::views(const std::string& container) const { return this->views_.at(container); };
58✔
609

610
size_t Users::num_views(const std::string& container) const { return this->views(container).size(); };
52✔
611

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

621
    return us;
×
622
};
×
623

624
const std::list<User*>& Users::moves(const std::string& container) const { return this->moves_.at(container); };
100✔
625

626
size_t Users::num_moves(const std::string& container) const { return this->moves(container).size(); };
52✔
627

628
structured_control_flow::ControlFlowNode* Users::scope(User* user) {
235✔
629
    if (auto data_node = dynamic_cast<data_flow::DataFlowNode*>(user->element())) {
235!
630
        return static_cast<structured_control_flow::Block*>(data_node->get_parent().get_parent());
229✔
631
    } else if (auto memlet = dynamic_cast<data_flow::Memlet*>(user->element())) {
6!
632
        return static_cast<structured_control_flow::Block*>(memlet->get_parent().get_parent());
×
633
    } else if (auto transition = dynamic_cast<structured_control_flow::Transition*>(user->element())) {
6!
634
        return &transition->parent();
6✔
635
    } else {
636
        auto user_element = dynamic_cast<structured_control_flow::ControlFlowNode*>(user->element());
×
637
        assert(user_element != nullptr && "Users::scope: User element is not a ControlFlowNode");
×
638
        return user_element;
×
639
    }
640
}
235✔
641

642
const std::unordered_set<User*> Users::all_uses_between(User& user1, User& user2) {
19✔
643
    std::unordered_set<User*> uses;
19✔
644
    std::unordered_set<User*> visited;
19✔
645
    std::list<User*> queue = {&user2};
19!
646
    while (!queue.empty()) {
117✔
647
        auto current = queue.front();
98✔
648
        queue.pop_front();
98✔
649

650
        // Stop conditions
651
        if (current == &user1) {
98✔
652
            continue;
19✔
653
        }
654

655
        if (visited.find(current) != visited.end()) {
79!
656
            continue;
2✔
657
        }
658
        visited.insert(current);
77!
659

660
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
77!
661
            uses.insert(current);
11!
662
        }
11✔
663

664
        // Extend search
665
        // Backward search to utilize domination user1 over user
666
        auto [eb, ee] = boost::in_edges(current->vertex_, this->graph_);
77!
667
        auto edges = std::ranges::subrange(eb, ee);
154✔
668
        for (auto edge : edges) {
156!
669
            auto v = boost::source(edge, this->graph_);
79!
670
            queue.push_back(this->users_.at(v).get());
79!
671
        }
672
    }
673

674
    return uses;
19✔
675
};
19!
676

677
const std::unordered_set<User*> Users::all_uses_after(User& user) {
×
678
    std::unordered_set<User*> uses;
×
679
    std::unordered_set<User*> visited;
×
680
    std::list<User*> queue = {&user};
×
681
    while (!queue.empty()) {
×
682
        auto current = queue.front();
×
683
        queue.pop_front();
×
684
        if (visited.find(current) != visited.end()) {
×
685
            continue;
×
686
        }
687
        visited.insert(current);
×
688

689
        if (current != &user && current->use() != Use::NOP) {
×
690
            uses.insert(current);
×
691
        }
×
692

693
        // Extend search
694
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
695
        auto edges = std::ranges::subrange(eb, ee);
×
696
        for (auto edge : edges) {
×
697
            auto v = boost::target(edge, this->graph_);
×
698
            queue.push_back(this->users_.at(v).get());
×
699
        }
700
    }
701

702
    return uses;
×
703
};
×
704

705
const std::vector<std::string> Users::all_containers_in_order() {
×
706
    std::unordered_set<std::string> unique_containers;
×
707
    std::vector<std::string> containers;
×
708

709
    // BFS traversal
710
    std::unordered_set<User*> visited;
×
711
    std::list<User*> queue = {this->source_};
×
712
    while (!queue.empty()) {
×
713
        auto current = queue.front();
×
714
        queue.pop_front();
×
715
        if (visited.find(current) != visited.end()) {
×
716
            continue;
×
717
        }
718
        visited.insert(current);
×
719

720
        if ((current->container() != "" || current->use() != Use::NOP) &&
×
721
            unique_containers.find(current->container()) == unique_containers.end()) {
×
722
            unique_containers.insert(current->container());
×
723
            containers.push_back(current->container());
×
724
        }
×
725

726
        auto [eb, ee] = boost::out_edges(current->vertex_, this->graph_);
×
727
        auto edges = std::ranges::subrange(eb, ee);
×
728
        for (auto edge : edges) {
×
729
            auto v = boost::target(edge, this->graph_);
×
730
            queue.push_back(this->users_.at(v).get());
×
731
        }
732
    }
733
    return containers;
×
734
}
×
735

736
UsersView::UsersView(Users& users, const structured_control_flow::ControlFlowNode& node) : users_(users) {
250✔
737
    this->entry_ = users.entries_.at(&node);
250!
738
    this->exit_ = users.exits_.at(&node);
250!
739

740
    // Collect sub users
741
    std::unordered_set<User*> visited;
250✔
742
    std::list<User*> queue = {this->exit_};
250!
743
    while (!queue.empty()) {
3,251✔
744
        auto current = queue.front();
3,001✔
745
        queue.pop_front();
3,001✔
746

747
        // Stop conditions
748
        if (current == this->entry_) {
3,001✔
749
            continue;
250✔
750
        }
751

752
        if (visited.find(current) != visited.end()) {
2,751!
753
            continue;
254✔
754
        }
755
        visited.insert(current);
2,497!
756

757
        this->sub_users_.insert(current);
2,497!
758

759
        // Extend search
760
        // Backward search to utilize domination user1 over user
761
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
2,497!
762
        auto edges = std::ranges::subrange(eb, ee);
4,994✔
763
        for (auto edge : edges) {
5,248!
764
            auto v = boost::source(edge, users.graph_);
2,751!
765
            queue.push_back(users.users_.at(v).get());
2,751!
766
        }
767
    }
768
};
250✔
769

770
std::vector<User*> UsersView::uses() const {
81✔
771
    std::vector<User*> us;
81✔
772
    for (auto& user : this->sub_users_) {
990✔
773
        if (user->use() == Use::NOP) {
909✔
774
            continue;
379✔
775
        }
776
        us.push_back(user);
530!
777
    }
778

779
    return us;
81✔
780
};
81!
781

782
std::vector<User*> UsersView::uses(const std::string& container) const {
246✔
783
    std::vector<User*> us;
246✔
784
    for (auto& user : this->sub_users_) {
3,486✔
785
        if (user->container() != container) {
3,240✔
786
            continue;
2,685✔
787
        }
788
        if (user->use() == Use::NOP) {
555!
789
            continue;
×
790
        }
791
        us.push_back(user);
555!
792
    }
793

794
    return us;
246✔
795
};
246!
796

797
std::vector<User*> UsersView::writes() const {
2✔
798
    std::vector<User*> us;
2✔
799
    for (auto& user : this->sub_users_) {
18✔
800
        if (user->use() != Use::WRITE) {
16✔
801
            continue;
14✔
802
        }
803
        us.push_back(user);
2!
804
    }
805

806
    return us;
2✔
807
};
2!
808

809
std::vector<User*> UsersView::writes(const std::string& container) const {
137✔
810
    std::vector<User*> us;
137✔
811
    for (auto& user : this->sub_users_) {
2,881✔
812
        if (user->container() != container) {
2,744✔
813
            continue;
2,280✔
814
        }
815
        if (user->use() != Use::WRITE) {
464✔
816
            continue;
275✔
817
        }
818
        us.push_back(user);
189!
819
    }
820

821
    return us;
137✔
822
};
137!
823

824
std::vector<User*> UsersView::reads() const {
117✔
825
    std::vector<User*> us;
117✔
826
    for (auto& user : this->sub_users_) {
1,272✔
827
        if (user->use() != Use::READ) {
1,155✔
828
            continue;
670✔
829
        }
830
        us.push_back(user);
485!
831
    }
832

833
    return us;
117✔
834
};
117!
835

836
std::vector<User*> UsersView::reads(const std::string& container) const {
215✔
837
    std::vector<User*> us;
215✔
838
    for (auto& user : this->sub_users_) {
3,339✔
839
        if (user->container() != container) {
3,124✔
840
            continue;
2,623✔
841
        }
842
        if (user->use() != Use::READ) {
501✔
843
            continue;
106✔
844
        }
845
        us.push_back(user);
395!
846
    }
847

848
    return us;
215✔
849
};
215!
850

851
std::vector<User*> UsersView::views() const {
×
852
    std::vector<User*> us;
×
853
    for (auto& user : this->sub_users_) {
×
854
        if (user->use() != Use::VIEW) {
×
855
            continue;
×
856
        }
857
        us.push_back(user);
×
858
    }
859

860
    return us;
×
861
};
×
862

863
std::vector<User*> UsersView::views(const std::string& container) const {
27✔
864
    std::vector<User*> us;
27✔
865
    for (auto& user : this->sub_users_) {
329✔
866
        if (user->container() != container) {
302✔
867
            continue;
270✔
868
        }
869
        if (user->use() != Use::VIEW) {
32✔
870
            continue;
29✔
871
        }
872
        us.push_back(user);
3!
873
    }
874

875
    return us;
27✔
876
};
27!
877

878
std::vector<User*> UsersView::moves() const {
2✔
879
    std::vector<User*> us;
2✔
880
    for (auto& user : this->sub_users_) {
20✔
881
        if (user->use() != Use::MOVE) {
18✔
882
            continue;
16✔
883
        }
884
        us.push_back(user);
2!
885
    }
886

887
    return us;
2✔
888
};
2!
889

890
std::vector<User*> UsersView::moves(const std::string& container) const {
27✔
891
    std::vector<User*> us;
27✔
892
    for (auto& user : this->sub_users_) {
329✔
893
        if (user->container() != container) {
302✔
894
            continue;
270✔
895
        }
896
        if (user->use() != Use::MOVE) {
32✔
897
            continue;
29✔
898
        }
899
        us.push_back(user);
3!
900
    }
901

902
    return us;
27✔
903
};
27!
904

905
std::unordered_set<User*> UsersView::all_uses_between(User& user1, User& user2) {
×
906
    assert(this->sub_users_.find(&user1) != this->sub_users_.end());
×
907
    assert(this->sub_users_.find(&user2) != this->sub_users_.end());
×
908

909
    std::unordered_set<User*> uses;
×
910
    std::unordered_set<User*> visited;
×
911
    std::list<User*> queue = {&user1};
×
912
    while (!queue.empty()) {
×
913
        auto current = queue.front();
×
914
        queue.pop_front();
×
915
        if (visited.find(current) != visited.end()) {
×
916
            continue;
×
917
        }
918
        visited.insert(current);
×
919

920
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
×
921
            uses.insert(current);
×
922
        }
×
923

924
        // Stop conditions
925
        if (current == exit_) {
×
926
            continue;
×
927
        }
928

929
        if (current == &user2) {
×
930
            continue;
×
931
        }
932

933
        // Extend search
934
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
935
        auto edges = std::ranges::subrange(eb, ee);
×
936
        for (auto edge : edges) {
×
937
            auto v = boost::target(edge, this->users_.graph_);
×
938
            queue.push_back(this->users_.users_.at(v).get());
×
939
        }
940
    }
941

942
    return uses;
×
943
};
×
944

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

948
    std::unordered_set<User*> uses;
×
949
    std::unordered_set<User*> visited;
×
950
    std::list<User*> queue = {&user};
×
951
    while (!queue.empty()) {
×
952
        auto current = queue.front();
×
953
        queue.pop_front();
×
954
        if (visited.find(current) != visited.end()) {
×
955
            continue;
×
956
        }
957
        visited.insert(current);
×
958

959
        if (current != &user && current->use() != Use::NOP) {
×
960
            uses.insert(current);
×
961
        }
×
962

963
        if (current == exit_) {
×
964
            continue;
×
965
        }
966

967
        // Extend search
968
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
969
        auto edges = std::ranges::subrange(eb, ee);
×
970
        for (auto edge : edges) {
×
971
            auto v = boost::target(edge, this->users_.graph_);
×
972
            queue.push_back(this->users_.users_.at(v).get());
×
973
        }
974
    }
975

976
    return uses;
×
977
};
×
978

979
bool Users::
980
    has_user(const std::string& container, Element* element, Use use, bool is_init, bool is_condition, bool is_update) {
208✔
981
    UserProps key{container, element, use, is_init, is_condition, is_update};
208✔
982
    if (this->users_lookup_.find(key) == this->users_lookup_.end()) {
208!
983
        return false;
156✔
984
    }
985
    return true;
52✔
986
}
208✔
987

988
User* Users::
989
    get_user(const std::string& container, Element* element, Use use, bool is_init, bool is_condition, bool is_update) {
808✔
990
    UserProps key{container, element, use, is_init, is_condition, is_update};
808✔
991
    return this->users_lookup_.at(key);
808!
992
}
808✔
993

994
void Users::add_user(std::unique_ptr<User> user) {
1,486✔
995
    auto vertex = user->vertex_;
1,486✔
996
    this->users_.insert({vertex, std::move(user)});
1,486!
997

998
    auto user_ptr = this->users_.at(vertex).get();
1,486✔
999
    bool is_init = false;
1,486✔
1000
    bool is_condition = false;
1,486✔
1001
    bool is_update = false;
1,486✔
1002
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
1,486!
1003
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
647!
1004
        if (for_loop == nullptr) {
647!
1005
            throw std::invalid_argument("Invalid user type");
×
1006
        }
1007
        if (for_user->is_init()) {
647✔
1008
            is_init = true;
143✔
1009
        } else if (for_user->is_condition()) {
647✔
1010
            is_condition = true;
246✔
1011
        } else if (for_user->is_update()) {
504!
1012
            is_update = true;
258✔
1013
        } else {
258✔
1014
            throw std::invalid_argument("Invalid user type");
×
1015
        }
1016
    }
647✔
1017

1018
    UserProps key{user_ptr->container(), user_ptr->element(), user_ptr->use(), is_init, is_condition, is_update};
1,486✔
1019
    this->users_lookup_.insert({key, user_ptr});
1,486!
1020
}
1,486✔
1021

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

1025
    // Locals have no uses outside of the node
1026
    // We can check this by comparing the number of uses of the container in the view and the total
1027
    // number of uses of the container in the users map.
1028
    std::unordered_set<std::string> locals;
20✔
1029
    UsersView view(*this, node);
20!
1030
    for (auto& user : view.uses()) {
73!
1031
        if (!sdfg.is_transient(user->container())) {
53!
1032
            continue;
21✔
1033
        }
1034
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
32!
1035
            locals.insert(user->container());
12!
1036
        }
12✔
1037
    }
1038

1039
    return locals;
20✔
1040
};
20!
1041

1042
const std::vector<std::string> UsersView::all_containers_in_order() {
×
1043
    std::unordered_set<std::string> unique_containers;
×
1044
    std::vector<std::string> containers;
×
1045

1046
    // BFS traversal
1047
    std::unordered_set<User*> visited;
×
1048
    std::list<User*> queue = {this->entry_};
×
1049
    while (!queue.empty()) {
×
1050
        auto current = queue.front();
×
1051
        queue.pop_front();
×
1052
        if (visited.find(current) != visited.end()) {
×
1053
            continue;
×
1054
        }
1055
        visited.insert(current);
×
1056

1057
        if ((current->container() != "" || current->use() != Use::NOP) &&
×
1058
            unique_containers.find(current->container()) == unique_containers.end()) {
×
1059
            unique_containers.insert(current->container());
×
1060
            containers.push_back(current->container());
×
1061
        }
×
1062

1063
        if (current == this->exit_) {
×
1064
            continue;
×
1065
        }
1066

1067
        auto [eb, ee] = boost::out_edges(current->vertex_, this->users_.graph_);
×
1068
        auto edges = std::ranges::subrange(eb, ee);
×
1069
        for (auto edge : edges) {
×
1070
            auto v = boost::target(edge, this->users_.graph_);
×
1071
            queue.push_back(this->users_.users_.at(v).get());
×
1072
        }
1073
    }
1074

1075
    return containers;
×
1076
}
×
1077

1078
} // namespace analysis
1079
} // 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