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

daisytuner / sdfglib / 19203085125

08 Nov 2025 07:07PM UTC coverage: 61.165% (-0.1%) from 61.281%
19203085125

push

github

web-flow
Merge pull request #330 from daisytuner/for-2-map-improvs

handles dereferences via local pointers in for2map conversion

7 of 22 new or added lines in 1 file covered. (31.82%)

16 existing lines in 2 files now uncovered.

10247 of 16753 relevant lines covered (61.17%)

100.55 hits per line

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

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

27
      };
3,166✔
28

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

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

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

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

40
    if (auto access_node = dynamic_cast<data_flow::AccessNode*>(this->element_)) {
359✔
41
        auto& graph = access_node->get_parent();
329✔
42
        if (this->use_ == Use::READ || this->use_ == Use::VIEW) {
329✔
43
            std::vector<data_flow::Subset> subsets;
109✔
44
            for (auto& iedge : graph.out_edges(*access_node)) {
223✔
45
                subsets.push_back(iedge.subset());
114✔
46
            }
47
            return subsets;
109✔
48
        } else if (this->use_ == Use::WRITE || this->use_ == Use::MOVE) {
329✔
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 {{}};
30✔
59
};
359✔
60

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

72
      };
664✔
73

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

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

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

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

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

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

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

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

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

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

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

148
        for (auto& oedge : dataflow.out_edges(*node)) {
916✔
149
            std::unordered_set<std::string> used;
369✔
150
            for (auto dim : oedge.subset()) {
663✔
151
                for (auto atom : symbolic::atoms(dim)) {
539✔
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
            }
294✔
167
        }
369✔
168
    }
169

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

253
        return {s, t};
404✔
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_);
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✔
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_);
9✔
303
        }
9✔
304

305
        return {s, t};
26✔
306
    } else if (auto loop_stmt = dynamic_cast<structured_control_flow::While*>(&node)) {
177✔
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)) {
143✔
331
        // NOP
332
        auto s = boost::add_vertex(this->graph_);
132✔
333
        this->users_.insert({s, std::make_unique<User>(s, "", for_stmt, Use::NOP)});
132✔
334
        auto last = s;
132✔
335
        this->entries_.insert({for_stmt, this->users_.at(s).get()});
132✔
336

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

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

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

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

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

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

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

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

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

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

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

395
        return {s, t};
132✔
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
};
860✔
428

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

432
      };
214✔
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) {
214✔
440
    users_.clear();
214✔
441
    graph_.clear();
214✔
442
    source_ = nullptr;
214✔
443
    sink_ = nullptr;
214✔
444
    this->entries_.clear();
214✔
445
    this->exits_.clear();
214✔
446
    users_by_sdfg_.clear();
214✔
447
    users_by_sdfg_loop_condition_.clear();
214✔
448
    users_by_sdfg_loop_init_.clear();
214✔
449
    users_by_sdfg_loop_update_.clear();
214✔
450

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

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

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

474
    std::list<User*> sinks;
214✔
475
    for (auto& entry : this->users_) {
3,376✔
476
        if (boost::out_degree(entry.first, this->graph_) == 0) {
3,162✔
477
            sinks.push_back(entry.second.get());
216✔
478
        }
216✔
479
    }
480
    if (sinks.size() == 0) {
214✔
481
        throw InvalidSDFGException("Users Analysis: No sink node");
×
482
    }
483
    if (sinks.size() > 1) {
214✔
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();
213✔
496
    }
497

498
    // Collect sub structures
499
    for (auto& entry : this->users_) {
3,377✔
500
        auto container = entry.second->container();
3,163✔
501
        switch (entry.second->use()) {
3,163✔
502
            case Use::READ: {
503
                if (this->reads_.find(container) == this->reads_.end()) {
875✔
504
                    this->reads_.insert({container, {}});
457✔
505
                }
457✔
506
                this->reads_[container].push_back(entry.second.get());
875✔
507
                break;
875✔
508
            }
509
            case Use::WRITE: {
510
                if (this->writes_.find(container) == this->writes_.end()) {
541✔
511
                    this->writes_.insert({container, {}});
360✔
512
                }
360✔
513
                this->writes_[container].push_back(entry.second.get());
541✔
514
                break;
541✔
515
            }
516
            case Use::VIEW: {
517
                if (this->views_.find(container) == this->views_.end()) {
20✔
518
                    this->views_.insert({container, {}});
19✔
519
                }
19✔
520
                this->views_[container].push_back(entry.second.get());
20✔
521
                break;
20✔
522
            }
523
            case Use::MOVE: {
524
                if (this->moves_.find(container) == this->moves_.end()) {
19✔
525
                    this->moves_.insert({container, {}});
18✔
526
                }
18✔
527
                this->moves_[container].push_back(entry.second.get());
19✔
528
                break;
19✔
529
            }
530
            default:
531
                break;
1,708✔
532
        }
533
    }
3,163✔
534
};
214✔
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 {
256✔
549
    std::vector<User*> us;
256✔
550
    for (auto& entry : this->users_) {
5,607✔
551
        if (entry.second->container() != container) {
5,351✔
552
            continue;
4,886✔
553
        }
554
        if (entry.second->use() == Use::NOP) {
465✔
555
            continue;
×
556
        }
557
        us.push_back(entry.second.get());
465✔
558
    }
559

560
    return us;
256✔
561
};
256✔
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 {
170✔
596
    if (this->reads_.find(container) == this->reads_.end()) {
170✔
597
        return {};
20✔
598
    } else {
599
        return this->reads_.at(container);
150✔
600
    }
601
};
170✔
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 {
79✔
636
    if (this->moves_.find(container) == this->moves_.end()) {
79✔
637
        return {};
74✔
638
    } else {
639
        return this->moves_.at(container);
5✔
640
    }
641
};
79✔
642

643
structured_control_flow::ControlFlowNode* Users::scope(User* user) {
297✔
644
    if (auto data_node = dynamic_cast<data_flow::DataFlowNode*>(user->element())) {
297✔
645
        return static_cast<structured_control_flow::Block*>(data_node->get_parent().get_parent());
291✔
646
    } else if (auto memlet = dynamic_cast<data_flow::Memlet*>(user->element())) {
6✔
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())) {
6✔
649
        return &transition->parent();
6✔
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
}
297✔
656

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

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

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

675
        if (current != &user1 && current != &user2 && current->use() != Use::NOP) {
63✔
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_);
63✔
682
        auto edges = std::ranges::subrange(eb, ee);
126✔
683
        for (auto edge : edges) {
128✔
684
            auto v = boost::source(edge, this->graph_);
65✔
685
            queue.push_back(this->users_.at(v).get());
65✔
686
        }
687
    }
688

689
    return uses;
15✔
690
};
15✔
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) {
83✔
752
    this->entry_ = users.entries_.at(&node);
83✔
753
    this->exit_ = users.exits_.at(&node);
83✔
754

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

762
        // Stop conditions
763
        if (current == this->entry_) {
1,110✔
764
            continue;
83✔
765
        }
766

767
        if (visited.find(current) != visited.end()) {
1,027✔
768
            continue;
115✔
769
        }
770
        visited.insert(current);
912✔
771

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

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

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

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

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

809
    return us;
44✔
810
};
44✔
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_) {
243✔
827
        if (user->container() != container) {
228✔
828
            continue;
211✔
829
        }
830
        if (user->use() != Use::WRITE) {
17✔
831
            continue;
6✔
832
        }
833
        us.push_back(user);
11✔
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_) {
312✔
854
        if (user->container() != container) {
293✔
855
            continue;
251✔
856
        }
857
        if (user->use() != Use::READ) {
42✔
858
            continue;
20✔
859
        }
860
        us.push_back(user);
22✔
861
    }
862

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

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

UNCOV
875
    return us;
×
UNCOV
876
};
×
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_) {
243✔
881
        if (user->container() != container) {
228✔
882
            continue;
211✔
883
        }
884
        if (user->use() != Use::VIEW) {
17✔
885
            continue;
17✔
886
        }
887
        us.push_back(user);
×
888
    }
889

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

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

UNCOV
902
    return us;
×
UNCOV
903
};
×
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_) {
243✔
908
        if (user->container() != container) {
228✔
909
            continue;
211✔
910
        }
911
        if (user->use() != Use::MOVE) {
17✔
912
            continue;
17✔
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) {
160✔
996
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element)) {
160✔
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() ||
320✔
1026
        users_by_sdfg_.at(container).find(element) == users_by_sdfg_.at(container).end() ||
160✔
1027
        users_by_sdfg_.at(container).at(element).find(use) == users_by_sdfg_.at(container).at(element).end()) {
160✔
1028
        return false;
120✔
1029
    }
1030

1031
    return true;
40✔
1032
}
160✔
1033

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

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

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

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

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

1083
std::unordered_set<std::string> Users::locals(structured_control_flow::ControlFlowNode& node) {
20✔
1084
    auto& sdfg = this->sdfg_;
20✔
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;
20✔
1090
    UsersView view(*this, node);
20✔
1091
    for (auto& user : view.uses()) {
73✔
1092
        if (!sdfg.is_transient(user->container())) {
53✔
1093
            continue;
21✔
1094
        }
1095
        if (view.uses(user->container()).size() == this->uses(user->container()).size()) {
32✔
1096
            locals.insert(user->container());
12✔
1097
        }
12✔
1098
    }
1099

1100
    return locals;
20✔
1101
};
20✔
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

© 2025 Coveralls, Inc