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

daisytuner / docc / 26556322966

27 May 2026 03:45PM UTC coverage: 60.869% (-0.02%) from 60.886%
26556322966

push

github

web-flow
Libnode ptr edges (#719)

Migrating SDFGs to treat pointers as inputs to libNodes / Calls as scalars.
A pointer will only appear in an output edge if its actually returned from the function (like malloc).

* Stdlib, Blas and Tensor Matmul nodes were migrated to this new format. Other, currently transitory Tensor Nodes are not yet migrated.
* DOCC version was bumped to incorporate previous docc-llvm versions (up to 0.4.0) that had been counted separately.
! Until all passes consider the use / leak of pointers as uncertainty / hiding potential writes, TensorNodes are declared as general side-effect.
* Lots of utility functions to centralize the creation (and edges) of various libNodes that needed to be changed.
* Fixed & unified docc paths across python and llvm front-ends.
* Skip BlockFusion test that fails to its libNodes currently having side effects
~ Prevent a crash in DotViz when using symbolic offsets into structs
* Removing old ConstProp pass, it is not safe for the new pointer representation and should not be all too critical

961 of 1749 new or added lines in 52 files covered. (54.95%)

87 existing lines in 28 files now uncovered.

35225 of 57870 relevant lines covered (60.87%)

11046.32 hits per line

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

77.5
/sdfg/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)
25
    : vertex_(vertex), container_(container), use_(use), element_(element) {
23,967✔
26

27
      };
23,967✔
28

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

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

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

35
const std::vector<data_flow::Subset>& User::subsets() const {
1,619✔
36
    if (this->subsets_cached_) {
1,619✔
37
        return this->subsets_;
763✔
38
    }
763✔
39

40
    if (this->container_ == "") {
856✔
41
        // No-op user
42
    } else if (auto access_node = dynamic_cast<data_flow::AccessNode*>(this->element_)) {
856✔
43
        auto& graph = access_node->get_parent();
591✔
44
        if (this->use_ == Use::READ || this->use_ == Use::VIEW) {
591✔
45
            for (auto& iedge : graph.out_edges(*access_node)) {
287✔
46
                this->subsets_.push_back(iedge.subset());
287✔
47
            }
287✔
48
        } else if (this->use_ == Use::WRITE || this->use_ == Use::MOVE) {
324✔
49
            for (auto& oedge : graph.in_edges(*access_node)) {
324✔
50
                this->subsets_.push_back(oedge.subset());
324✔
51
            }
324✔
52
        }
324✔
53
    } else {
591✔
54
        // Use of symbol
55
        this->subsets_.push_back({});
265✔
56
    }
265✔
57

58
    this->subsets_cached_ = true;
856✔
59
    return this->subsets_;
856✔
60
};
1,619✔
61

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

73
      };
6,793✔
74

75
bool ForUser::is_init() const { return this->is_init_; };
6,797✔
76

77
bool ForUser::is_condition() const { return this->is_condition_; };
5,248✔
78

79
bool ForUser::is_update() const { return this->is_update_; };
2,655✔
80

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

89
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
4,422✔
90
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
3,105✔
91
                if (dataflow.in_degree(*node) > 0) {
3,105✔
92
                    Use use = Use::WRITE;
1,305✔
93

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

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

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

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

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

128
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
1,853✔
129
                        boost::add_edge(last, v, this->graph_);
782✔
130
                    } else {
1,071✔
131
                        first = v;
1,071✔
132
                    }
1,071✔
133
                    last = v;
1,853✔
134
                }
1,853✔
135
            }
3,105✔
136
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
3,105✔
137
            for (auto& symbol : library_node->symbols()) {
222✔
138
                auto v = boost::add_vertex(this->graph_);
21✔
139
                this->add_user(std::make_unique<User>(v, symbol->get_name(), library_node, Use::READ));
21✔
140
                if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
21✔
141
                    boost::add_edge(last, v, this->graph_);
12✔
142
                } else {
12✔
143
                    first = v;
9✔
144
                }
9✔
145
                last = v;
21✔
146
            }
21✔
147
        }
222✔
148

149
        for (auto& oedge : dataflow.out_edges(*node)) {
4,422✔
150
            std::unordered_set<std::string> used;
3,160✔
151
            for (auto dim : oedge.subset()) {
3,160✔
152
                for (auto atom : symbolic::atoms(dim)) {
3,465✔
153
                    if (used.find(atom->get_name()) != used.end()) {
3,465✔
154
                        continue;
33✔
155
                    }
33✔
156
                    used.insert(atom->get_name());
3,432✔
157

158
                    auto v = boost::add_vertex(this->graph_);
3,432✔
159
                    this->add_user(std::make_unique<User>(v, atom->get_name(), &oedge, Use::READ));
3,432✔
160
                    if (last != boost::graph_traits<graph::Graph>::null_vertex()) {
3,432✔
161
                        boost::add_edge(last, v, this->graph_);
3,307✔
162
                    } else {
3,307✔
163
                        first = v;
125✔
164
                    }
125✔
165
                    last = v;
3,432✔
166
                }
3,432✔
167
            }
2,975✔
168
        }
3,160✔
169
    }
4,422✔
170

171
    return {first, last};
1,624✔
172
};
1,624✔
173

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

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

186
        auto& dataflow = block_stmt->dataflow();
1,624✔
187
        auto subgraph = this->traverse(dataflow);
1,624✔
188

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

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

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

204
        graph::Vertex current = s;
2,040✔
205
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
5,082✔
206
            auto child = sequence_stmt->at(i);
3,054✔
207

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

216
            std::unordered_set<std::string> used;
3,042✔
217
            for (auto& entry : child.second.assignments()) {
3,042✔
218
                for (auto atom : symbolic::atoms(entry.second)) {
220✔
219
                    if (symbolic::is_pointer(atom)) {
117✔
220
                        continue;
×
221
                    }
×
222
                    if (used.find(atom->get_name()) != used.end()) {
117✔
223
                        continue;
8✔
224
                    }
8✔
225
                    used.insert(atom->get_name());
109✔
226

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

230
                    boost::add_edge(current, v, this->graph_);
109✔
231
                    current = v;
109✔
232
                }
109✔
233
            }
220✔
234

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

239
                boost::add_edge(current, v, this->graph_);
220✔
240
                current = v;
220✔
241
            }
220✔
242
        }
3,042✔
243

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

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

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

261
        graph::Vertex last = s;
57✔
262

263
        std::unordered_set<std::string> used;
57✔
264
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
140✔
265
            auto condition = if_else_stmt->at(i).second;
83✔
266
            for (auto atom : symbolic::atoms(condition)) {
86✔
267
                if (used.find(atom->get_name()) != used.end()) {
86✔
268
                    continue;
22✔
269
                }
22✔
270
                used.insert(atom->get_name());
64✔
271

272
                auto v = boost::add_vertex(this->graph_);
64✔
273

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

276
                boost::add_edge(last, v, this->graph_);
64✔
277
                last = v;
64✔
278
            }
64✔
279
        }
83✔
280

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

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

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

313
        auto subgraph = this->traverse(loop_stmt->root());
21✔
314

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

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

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

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

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

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

355
        boost::add_edge(last, v, this->graph_);
1,325✔
356
        last = v;
1,325✔
357

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

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

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

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

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

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

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

393
        // Back edge
394
        boost::add_edge(t, last, this->graph_);
1,325✔
395

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

427
    throw std::invalid_argument("Invalid control flow node type");
×
428
};
5,088✔
429

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

433
      };
605✔
434

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

438
      };
×
439

440
void Users::run(analysis::AnalysisManager& analysis_manager) {
605✔
441
    users_.clear();
605✔
442
    graph_.clear();
605✔
443
    source_ = nullptr;
605✔
444
    sink_ = nullptr;
605✔
445
    this->entries_.clear();
605✔
446
    this->exits_.clear();
605✔
447
    this->users_lookup_.clear();
605✔
448

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

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

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

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

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

496
    for (auto& container : sdfg_.containers()) {
3,823✔
497
        this->reads_.insert({container, {}});
3,823✔
498
        this->writes_.insert({container, {}});
3,823✔
499
        this->views_.insert({container, {}});
3,823✔
500
        this->moves_.insert({container, {}});
3,823✔
501
    }
3,823✔
502

503
    // Collect sub structures
504
    for (auto& entry : this->users_) {
23,963✔
505
        auto container = entry.second->container();
23,963✔
506
        if (entry.second->use() == Use::NOP) {
23,963✔
507
            continue;
10,154✔
508
        }
10,154✔
509
        if (container == "") {
13,809✔
510
            continue;
×
511
        }
×
512

513
        switch (entry.second->use()) {
13,809✔
514
            case Use::READ: {
9,546✔
515
                this->reads_[container].push_back(entry.second.get());
9,546✔
516
                break;
9,546✔
517
            }
×
518
            case Use::WRITE: {
4,084✔
519
                this->writes_[container].push_back(entry.second.get());
4,084✔
520
                break;
4,084✔
521
            }
×
522
            case Use::VIEW: {
88✔
523
                this->views_[container].push_back(entry.second.get());
88✔
524
                break;
88✔
525
            }
×
526
            case Use::MOVE: {
91✔
527
                this->moves_[container].push_back(entry.second.get());
91✔
528
                break;
91✔
529
            }
×
530
            default:
×
531
                break;
×
532
        }
13,809✔
533
    }
13,809✔
534
};
605✔
535

536
std::list<User*> Users::uses() const {
×
537
    std::list<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::list<User*> Users::uses(const std::string& container) const {
2,217✔
549
    std::list<User*> us;
2,217✔
550
    for (auto& entry : this->users_) {
155,962✔
551
        if (entry.second->container() != container) {
155,962✔
552
            continue;
145,385✔
553
        }
145,385✔
554
        if (entry.second->use() == Use::NOP) {
10,577✔
555
            continue;
×
556
        }
×
557
        us.push_back(entry.second.get());
10,577✔
558
    }
10,577✔
559

560
    return us;
2,217✔
561
};
2,217✔
562

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

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

UNCOV
574
    return us;
×
UNCOV
575
};
×
576

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

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

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

590
    return us;
×
591
};
×
592

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

595
size_t Users::num_reads(const std::string& container) const { return this->reads(container).size(); };
1,809✔
596

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

606
    return us;
×
607
};
×
608

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

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

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

622
    return us;
×
623
};
×
624

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

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

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

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

651
        // Stop conditions
652
        if (current == &user1) {
23✔
653
            continue;
5✔
654
        }
5✔
655

656
        if (visited.find(current) != visited.end()) {
18✔
657
            continue;
×
658
        }
×
659
        visited.insert(current);
18✔
660

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

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

675
    return uses;
5✔
676
};
5✔
677

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

690
        if (current != &user && current->use() != Use::NOP) {
39✔
691
            uses.insert(current);
9✔
692
        }
9✔
693

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

703
    return uses;
8✔
704
};
8✔
705

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

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

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

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

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

741
    // Collect sub users
742
    std::unordered_set<User*> visited;
1,085✔
743
    std::list<User*> queue = {this->exit_};
1,085✔
744
    while (!queue.empty()) {
31,535✔
745
        auto current = queue.front();
30,450✔
746
        queue.pop_front();
30,450✔
747

748
        // Stop conditions
749
        if (current == this->entry_) {
30,450✔
750
            continue;
1,085✔
751
        }
1,085✔
752

753
        if (visited.find(current) != visited.end()) {
29,365✔
754
            continue;
3,918✔
755
        }
3,918✔
756
        visited.insert(current);
25,447✔
757

758
        this->sub_users_.insert(current);
25,447✔
759

760
        // Extend search
761
        // Backward search to utilize domination user1 over user
762
        auto [eb, ee] = boost::in_edges(current->vertex_, users.graph_);
25,447✔
763
        auto edges = std::ranges::subrange(eb, ee);
25,447✔
764
        for (auto edge : edges) {
29,365✔
765
            auto v = boost::source(edge, users.graph_);
29,365✔
766
            queue.push_back(users.users_.at(v).get());
29,365✔
767
        }
29,365✔
768
    }
25,447✔
769
};
1,085✔
770

771
std::vector<User*> UsersView::uses() const {
393✔
772
    std::vector<User*> us;
393✔
773
    for (auto& user : this->sub_users_) {
7,617✔
774
        if (user->use() == Use::NOP) {
7,617✔
775
            continue;
2,701✔
776
        }
2,701✔
777
        us.push_back(user);
4,916✔
778
    }
4,916✔
779

780
    return us;
393✔
781
};
393✔
782

783
std::vector<User*> UsersView::uses(const std::string& container) const {
1,366✔
784
    std::vector<User*> us;
1,366✔
785
    for (auto& user : this->sub_users_) {
44,714✔
786
        if (user->container() != container) {
44,714✔
787
            continue;
38,170✔
788
        }
38,170✔
789
        if (user->use() == Use::NOP) {
6,544✔
790
            continue;
×
791
        }
×
792
        us.push_back(user);
6,544✔
793
    }
6,544✔
794

795
    return us;
1,366✔
796
};
1,366✔
797

798
std::vector<User*> UsersView::writes() const {
170✔
799
    std::vector<User*> us;
170✔
800
    for (auto& user : this->sub_users_) {
6,047✔
801
        if (user->use() != Use::WRITE) {
6,047✔
802
            continue;
5,066✔
803
        }
5,066✔
804
        us.push_back(user);
981✔
805
    }
981✔
806

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

810
std::vector<User*> UsersView::writes(const std::string& container) const {
1,273✔
811
    std::vector<User*> us;
1,273✔
812
    for (auto& user : this->sub_users_) {
26,093✔
813
        if (user->container() != container) {
26,093✔
814
            continue;
20,201✔
815
        }
20,201✔
816
        if (user->use() != Use::WRITE) {
5,892✔
817
            continue;
3,966✔
818
        }
3,966✔
819
        us.push_back(user);
1,926✔
820
    }
1,926✔
821

822
    return us;
1,273✔
823
};
1,273✔
824

825
std::vector<User*> UsersView::reads() const {
92✔
826
    std::vector<User*> us;
92✔
827
    for (auto& user : this->sub_users_) {
3,379✔
828
        if (user->use() != Use::READ) {
3,379✔
829
            continue;
1,777✔
830
        }
1,777✔
831
        us.push_back(user);
1,602✔
832
    }
1,602✔
833

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

837
std::vector<User*> UsersView::reads(const std::string& container) const {
168✔
838
    std::vector<User*> us;
168✔
839
    for (auto& user : this->sub_users_) {
2,557✔
840
        if (user->container() != container) {
2,557✔
841
            continue;
2,268✔
842
        }
2,268✔
843
        if (user->use() != Use::READ) {
289✔
844
            continue;
102✔
845
        }
102✔
846
        us.push_back(user);
187✔
847
    }
187✔
848

849
    return us;
168✔
850
};
168✔
851

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

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

864
std::vector<User*> UsersView::views(const std::string& container) const {
103✔
865
    std::vector<User*> us;
103✔
866
    for (auto& user : this->sub_users_) {
1,584✔
867
        if (user->container() != container) {
1,584✔
868
            continue;
1,399✔
869
        }
1,399✔
870
        if (user->use() != Use::VIEW) {
185✔
871
            continue;
182✔
872
        }
182✔
873
        us.push_back(user);
3✔
874
    }
3✔
875

876
    return us;
103✔
877
};
103✔
878

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

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

891
std::vector<User*> UsersView::moves(const std::string& container) const {
132✔
892
    std::vector<User*> us;
132✔
893
    for (auto& user : this->sub_users_) {
1,966✔
894
        if (user->container() != container) {
1,966✔
895
            continue;
1,714✔
896
        }
1,714✔
897
        if (user->use() != Use::MOVE) {
252✔
898
            continue;
249✔
899
        }
249✔
900
        us.push_back(user);
3✔
901
    }
3✔
902

903
    return us;
132✔
904
};
132✔
905

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

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

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

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

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

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

943
    return uses;
×
944
};
×
945

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

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

960
        if (current != &user && current->use() != Use::NOP) {
44✔
961
            uses.insert(current);
4✔
962
        }
4✔
963

964
        if (current == exit_) {
44✔
965
            continue;
15✔
966
        }
15✔
967

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

977
    return uses;
15✔
978
};
15✔
979

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

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

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

999
    auto user_ptr = this->users_.at(vertex).get();
13,809✔
1000
    bool is_init = false;
13,809✔
1001
    bool is_condition = false;
13,809✔
1002
    bool is_update = false;
13,809✔
1003
    if (auto for_user = dynamic_cast<ForUser*>(user_ptr)) {
13,809✔
1004
        auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(user_ptr->element());
6,793✔
1005
        if (for_loop == nullptr) {
6,793✔
1006
            throw std::invalid_argument("Invalid user type");
×
1007
        }
×
1008
        if (for_user->is_init()) {
6,793✔
1009
            is_init = true;
1,548✔
1010
        } else if (for_user->is_condition()) {
5,245✔
1011
            is_condition = true;
2,595✔
1012
        } else if (for_user->is_update()) {
2,650✔
1013
            is_update = true;
2,650✔
1014
        } else {
2,650✔
1015
            throw std::invalid_argument("Invalid user type");
×
1016
        }
×
1017
    }
6,793✔
1018

1019
    UserProps key{user_ptr->container(), user_ptr->element(), user_ptr->use(), is_init, is_condition, is_update};
13,809✔
1020
    this->users_lookup_.insert({key, user_ptr});
13,809✔
1021
}
13,809✔
1022

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

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

1040
    return locals;
88✔
1041
};
88✔
1042

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

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

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

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

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

1076
    return containers;
×
1077
}
×
1078

1079
} // namespace analysis
1080
} // namespace sdfg
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc