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

daisytuner / sdfglib / 17656823807

11 Sep 2025 08:42PM UTC coverage: 60.447% (+1.1%) from 59.335%
17656823807

Pull #219

github

web-flow
Merge d5416236f into 6c1992b40
Pull Request #219: stdlib Library Nodes and ConstantNodes

460 of 1635 new or added lines in 81 files covered. (28.13%)

93 existing lines in 35 files now uncovered.

9385 of 15526 relevant lines covered (60.45%)

107.21 hits per line

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

84.3
/src/analysis/data_dependency_analysis.cpp
1
#include "sdfg/analysis/data_dependency_analysis.h"
2

3
#include <cassert>
4
#include <string>
5
#include <unordered_map>
6
#include <unordered_set>
7
#include <vector>
8

9
#include "sdfg/analysis/analysis.h"
10
#include "sdfg/analysis/loop_analysis.h"
11
#include "sdfg/structured_sdfg.h"
12
#include "sdfg/symbolic/maps.h"
13
#include "sdfg/symbolic/sets.h"
14

15
namespace sdfg {
16
namespace analysis {
17

18
DataDependencyAnalysis::DataDependencyAnalysis(StructuredSDFG& sdfg)
93✔
19
    : Analysis(sdfg), node_(sdfg.root()) {
93✔
20

21
      };
93✔
22

23
DataDependencyAnalysis::DataDependencyAnalysis(StructuredSDFG& sdfg, structured_control_flow::Sequence& node)
×
24
    : Analysis(sdfg), node_(node) {
×
25

26
      };
×
27

28
void DataDependencyAnalysis::run(analysis::AnalysisManager& analysis_manager) {
69✔
29
    results_.clear();
69✔
30
    loop_carried_dependencies_.clear();
69✔
31

32
    std::unordered_set<User*> undefined;
69✔
33
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions;
69✔
34
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions;
69✔
35

36
    auto& assumptions_analysis = analysis_manager.get<AssumptionsAnalysis>();
69✔
37
    auto& users = analysis_manager.get<Users>();
69✔
38
    visit_sequence(users, assumptions_analysis, node_, undefined, open_definitions, closed_definitions);
69✔
39

40
    for (auto& entry : open_definitions) {
282✔
41
        closed_definitions.insert(entry);
213✔
42
    }
43

44
    for (auto& entry : closed_definitions) {
290✔
45
        if (results_.find(entry.first->container()) == results_.end()) {
221✔
46
            results_.insert({entry.first->container(), {}});
142✔
47
        }
142✔
48
        results_.at(entry.first->container()).insert(entry);
221✔
49
    }
50
};
69✔
51

52
/****** Visitor API ******/
53

54
void DataDependencyAnalysis::visit_block(
159✔
55
    analysis::Users& users,
56
    analysis::AssumptionsAnalysis& assumptions_analysis,
57
    structured_control_flow::Block& block,
58
    std::unordered_set<User*>& undefined,
59
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
60
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
61
) {
62
    auto& dataflow = block.dataflow();
159✔
63

64
    for (auto node : dataflow.topological_sort()) {
394✔
65
        if (dynamic_cast<data_flow::ConstantNode*>(node) != nullptr) {
235✔
66
            continue;
5✔
67
        }
68

69
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
230✔
70
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
147✔
71
                if (dataflow.in_degree(*node) > 0) {
147✔
72
                    Use use = Use::WRITE;
83✔
73
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
166✔
74
                        if (iedge.type() == data_flow::MemletType::Reference ||
166✔
75
                            iedge.type() == data_flow::MemletType::Dereference_Src) {
83✔
76
                            use = Use::MOVE;
×
77
                            break;
×
78
                        }
79
                    }
80

81
                    auto current_user = users.get_user(access_node->data(), access_node, use);
83✔
82

83
                    if (use == Use::WRITE) {
83✔
84
                        // Close all definitions that we supersede
85
                        std::unordered_map<User*, std::unordered_set<User*>> to_close;
83✔
86
                        for (auto& user : open_definitions) {
119✔
87
                            if (supersedes(*user.first, *current_user, assumptions_analysis)) {
36✔
88
                                to_close.insert(user);
8✔
89
                            }
8✔
90
                        }
91
                        for (auto& user : to_close) {
91✔
92
                            open_definitions.erase(user.first);
8✔
93
                            closed_definitions.insert(user);
8✔
94
                        }
95

96
                        // Add new definition
97
                        open_definitions.insert({current_user, {}});
83✔
98
                    }
83✔
99
                }
83✔
100
                if (dataflow.out_degree(*access_node) > 0) {
147✔
101
                    Use use = Use::READ;
67✔
102
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
135✔
103
                        if (oedge.type() == data_flow::MemletType::Reference ||
136✔
104
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
68✔
105
                            use = Use::VIEW;
×
106
                            break;
×
107
                        }
108
                    }
109

110
                    auto current_user = users.get_user(access_node->data(), access_node, use);
67✔
111

112
                    if (use == Use::READ) {
67✔
113
                        // Find all definitions that we read from
114
                        bool found = false;
67✔
115
                        for (auto& user : open_definitions) {
96✔
116
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
29✔
117
                                user.second.insert(current_user);
9✔
118
                                found = true;
9✔
119
                            }
9✔
120
                        }
121
                        if (!found) {
67✔
122
                            undefined.insert(current_user);
59✔
123
                        } else {
59✔
124
                            bool supersedes_all = true;
8✔
125
                            for (auto& user : open_definitions) {
20✔
126
                                if (user.first->container() == current_user->container()) {
12✔
127
                                    supersedes_all &= supersedes(*user.first, *current_user, assumptions_analysis);
9✔
128
                                }
9✔
129
                            }
130
                            if (!supersedes_all) {
8✔
131
                                undefined.insert(current_user);
×
132
                            }
×
133
                        }
134
                    }
67✔
135
                }
67✔
136
            }
147✔
137
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
230✔
UNCOV
138
            for (auto& symbol : library_node->symbols()) {
×
139
                auto current_user = users.get_user(symbol->get_name(), library_node, Use::READ);
×
140
                {
141
                    // Find all definitions that we read from
142
                    bool found = false;
×
143
                    bool superseded_all = false;
×
144
                    for (auto& user : open_definitions) {
×
145
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
146
                            user.second.insert(current_user);
×
147
                            if (!found) {
×
148
                                found = true;
×
149
                                superseded_all = true;
×
150
                            }
×
151
                            superseded_all &= supersedes(*current_user, *user.first, assumptions_analysis);
×
152
                        }
×
153
                    }
154
                    if (!superseded_all) {
×
155
                        undefined.insert(current_user);
×
156
                    }
×
157
                }
158
            }
159
        }
×
160

161
        for (auto& oedge : dataflow.out_edges(*node)) {
381✔
162
            std::unordered_set<std::string> used;
151✔
163
            for (auto& dim : oedge.subset()) {
282✔
164
                for (auto atom : symbolic::atoms(dim)) {
268✔
165
                    used.insert(atom->get_name());
137✔
166
                }
137✔
167
            }
168
            for (auto& atom : used) {
288✔
169
                auto current_user = users.get_user(atom, &oedge, Use::READ);
137✔
170

171
                {
172
                    // Find all definitions that we read from
173
                    bool found = false;
137✔
174
                    bool superseded_all = false;
137✔
175
                    for (auto& user : open_definitions) {
221✔
176
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
84✔
177
                            user.second.insert(current_user);
4✔
178
                            if (!found) {
4✔
179
                                found = true;
4✔
180
                                superseded_all = true;
4✔
181
                            }
4✔
182
                            superseded_all &= supersedes(*current_user, *user.first, assumptions_analysis);
4✔
183
                        }
4✔
184
                    }
185
                    if (!superseded_all) {
137✔
186
                        undefined.insert(current_user);
133✔
187
                    }
133✔
188
                }
189
            }
190
        }
151✔
191
    }
192
}
159✔
193

194
void DataDependencyAnalysis::visit_for(
65✔
195
    analysis::Users& users,
196
    analysis::AssumptionsAnalysis& assumptions_analysis,
197
    structured_control_flow::StructuredLoop& for_loop,
198
    std::unordered_set<User*>& undefined,
199
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
200
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
201
) {
202
    // Init - Read
203
    for (auto atom : symbolic::atoms(for_loop.init())) {
73✔
204
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, true);
8✔
205

206
        bool found = false;
8✔
207
        for (auto& user : open_definitions) {
16✔
208
            if (user.first->container() == atom->get_name()) {
8✔
209
                user.second.insert(current_user);
1✔
210
                found = true;
1✔
211
            }
1✔
212
        }
213
        if (!found) {
8✔
214
            undefined.insert(current_user);
7✔
215
        }
7✔
216
    }
8✔
217

218
    // Init - Write
219
    {
220
        // Write Induction Variable
221
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, true);
65✔
222

223
        std::unordered_set<User*> to_close;
65✔
224
        for (auto& user : open_definitions) {
85✔
225
            if (supersedes(*user.first, *current_user, assumptions_analysis)) {
20✔
226
                to_close.insert(user.first);
2✔
227
            }
2✔
228
        }
229
        for (auto& user : to_close) {
67✔
230
            closed_definitions.insert({user, open_definitions.at(user)});
2✔
231
            open_definitions.erase(user);
2✔
232
        }
233
        open_definitions.insert({current_user, {}});
65✔
234

235
        // Improve: If loop is executed at least once, we can close the init's definition
236
        // TODO: Implement this
237
    }
65✔
238

239
    // Update - Write
240
    {
241
        // Exists after loop and inside body
242
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, false, false, true);
65✔
243
        open_definitions.insert({current_user, {}});
65✔
244

245
        // Improve: If loop is executed at least once, we can close the init's definition
246
        // TODO: Implement this
247
    }
248

249
    // Condition - Read
250
    for (auto atom : symbolic::atoms(for_loop.condition())) {
184✔
251
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, true);
119✔
252

253
        bool found = false;
119✔
254
        for (auto& user : open_definitions) {
377✔
255
            if (user.first->container() == atom->get_name()) {
258✔
256
                user.second.insert(current_user);
131✔
257
                found = true;
131✔
258
            }
131✔
259
        }
260
        if (!found) {
119✔
261
            undefined.insert(current_user);
53✔
262
        }
53✔
263
    }
119✔
264

265
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_for;
65✔
266
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_for;
65✔
267
    std::unordered_set<User*> undefined_for;
65✔
268

269
    // Add assumptions for body
270
    visit_sequence(
65✔
271
        users, assumptions_analysis, for_loop.root(), undefined_for, open_definitions_for, closed_definitions_for
65✔
272
    );
273

274
    // Update - Read
275
    for (auto atom : symbolic::atoms(for_loop.update())) {
130✔
276
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, false, true);
65✔
277

278
        bool found = false;
65✔
279
        for (auto& user : open_definitions_for) {
188✔
280
            if (user.first->container() == atom->get_name()) {
123✔
281
                user.second.insert(current_user);
1✔
282
                found = true;
1✔
283
            }
1✔
284
        }
285
        if (!found) {
65✔
286
            undefined_for.insert(current_user);
64✔
287
        }
64✔
288
    }
65✔
289

290
    // Merge for with outside
291

292
    // Closed definitions are simply merged
293
    for (auto& entry : closed_definitions_for) {
68✔
294
        closed_definitions.insert(entry);
3✔
295
    }
296

297
    // Undefined reads are matched or forwarded
298
    for (auto open_read : undefined_for) {
470✔
299
        bool found = false;
405✔
300
        for (auto& entry : open_definitions) {
1,432✔
301
            if (intersects(*entry.first, *open_read, assumptions_analysis)) {
1,027✔
302
                entry.second.insert(open_read);
410✔
303
                found = true;
410✔
304
            }
410✔
305
        }
306
        if (!found) {
405✔
307
            undefined.insert(open_read);
199✔
308
        } else {
199✔
309
            bool subset_all = true;
206✔
310
            for (auto& entry : open_definitions) {
686✔
311
                if (entry.first->container() == open_read->container()) {
480✔
312
                    subset_all &= supersedes_restrictive(*open_read, *entry.first, assumptions_analysis);
410✔
313
                }
410✔
314
            }
315
            if (!subset_all) {
206✔
316
                undefined.insert(open_read);
1✔
317
            }
1✔
318
        }
319
    }
320

321
    // Open definitions may close outside open definitions after loop
322
    std::unordered_set<User*> to_close;
65✔
323
    for (auto& previous : open_definitions) {
213✔
324
        for (auto& user : open_definitions_for) {
430✔
325
            if (supersedes(*previous.first, *user.first, assumptions_analysis)) {
284✔
326
                to_close.insert(previous.first);
2✔
327
                break;
2✔
328
            }
329
        }
330
    }
331
    for (auto& user : to_close) {
67✔
332
        closed_definitions.insert({user, open_definitions.at(user)});
2✔
333
        open_definitions.erase(user);
2✔
334
    }
335

336
    // Add loop-carried dependencies
337

338
    // Criterion 1: Loop is monotonic -> indvar never takes the same value twice
339
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
65✔
340
    if (is_monotonic) {
65✔
341
        // Case: Can analyze
342
        assert(this->loop_carried_dependencies_.insert({&for_loop, {}}).second);
64✔
343
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
64✔
344

345
        // We can focus on written containers
346

347
        // Case 1: Read-Write between iterations
348
        for (auto& read : undefined_for) {
468✔
349
            for (auto& write : open_definitions_for) {
2,071✔
350
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop)) {
1,667✔
351
                    dependencies[read->container()] = LOOP_CARRIED_DEPENDENCY_READ_WRITE;
40✔
352
                    write.second.insert(read);
40✔
353
                }
40✔
354
            }
355
        }
356

357
        // Case 2: Write-Write between iterations
358
        for (auto& write : open_definitions_for) {
186✔
359
            if (dependencies.find(write.first->container()) != dependencies.end()) {
122✔
360
                continue;
39✔
361
            }
362
            for (auto& write_2 : open_definitions_for) {
257✔
363
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis, for_loop)) {
215✔
364
                    dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
41✔
365
                    break;
41✔
366
                }
367
            }
368
        }
369
    } else {
64✔
370
        // Case: Cannot analyze
371
        assert(this->loop_carried_dependencies_.insert({&for_loop, {}}).second);
1✔
372
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
1✔
373

374
        // Over-Approximation:
375
        // Add loop-carried dependencies for all open reads to all open writes
376
        for (auto& read : undefined_for) {
2✔
377
            for (auto& write : open_definitions_for) {
2✔
378
                if (intersects(*write.first, *read, assumptions_analysis)) {
1✔
379
                    write.second.insert(read);
×
380
                    dependencies.insert({read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
×
381
                }
×
382
            }
383
        }
384
        // Add loop-carried dependencies for writes
385
        for (auto& write : open_definitions_for) {
2✔
386
            if (dependencies.find(write.first->container()) == dependencies.end()) {
1✔
387
                dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
1✔
388
            }
1✔
389
        }
390
    }
391

392
    // Add open definitions from for to outside
393
    for (auto& entry : open_definitions_for) {
188✔
394
        open_definitions.insert(entry);
123✔
395
    }
396
}
65✔
397

398
void DataDependencyAnalysis::visit_if_else(
21✔
399
    analysis::Users& users,
400
    analysis::AssumptionsAnalysis& assumptions_analysis,
401
    structured_control_flow::IfElse& if_else,
402
    std::unordered_set<User*>& undefined,
403
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
404
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
405
) {
406
    // Read Conditions
407
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
408
        auto child = if_else.at(i).second;
37✔
409
        for (auto atom : symbolic::atoms(child)) {
61✔
410
            auto current_user = users.get_user(atom->get_name(), &if_else, Use::READ);
24✔
411

412
            bool found = false;
24✔
413
            for (auto& user : open_definitions) {
38✔
414
                if (user.first->container() == atom->get_name()) {
14✔
415
                    user.second.insert(current_user);
7✔
416
                    found = true;
7✔
417
                }
7✔
418
            }
419
            if (!found) {
24✔
420
                undefined.insert(current_user);
17✔
421
            }
17✔
422
        }
24✔
423
    }
37✔
424

425
    std::vector<std::unordered_set<User*>> undefined_branches(if_else.size());
21✔
426
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> open_definitions_branches(if_else.size());
21✔
427
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> closed_definitionss_branches(if_else.size());
21✔
428
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
429
        auto& child = if_else.at(i).first;
37✔
430
        visit_sequence(
37✔
431
            users,
37✔
432
            assumptions_analysis,
37✔
433
            child,
37✔
434
            undefined_branches.at(i),
37✔
435
            open_definitions_branches.at(i),
37✔
436
            closed_definitionss_branches.at(i)
37✔
437
        );
438
    }
37✔
439

440
    // merge partial open reads
441
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
442
        for (auto& entry : undefined_branches.at(i)) {
41✔
443
            bool found = false;
4✔
444
            for (auto& entry2 : open_definitions) {
8✔
445
                if (entry2.first->container() == entry->container()) {
4✔
446
                    entry2.second.insert(entry);
4✔
447
                    found = true;
4✔
448
                }
4✔
449
            }
450
            if (!found) {
4✔
451
                undefined.insert(entry);
×
452
            }
×
453
        }
454
    }
37✔
455

456
    // merge closed writes
457
    for (auto& closing : closed_definitionss_branches) {
58✔
458
        for (auto& entry : closing) {
37✔
459
            closed_definitions.insert(entry);
×
460
        }
461
    }
462

463
    // Close open reads_after_writes for complete branches
464
    if (if_else.is_complete()) {
21✔
465
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
16✔
466
        std::unordered_set<std::string> candidates;
16✔
467
        std::unordered_set<std::string> candidates_tmp;
16✔
468

469
        /* Complete close open reads_after_writes
470
        1. get candidates from first iteration
471
        2. iterate over all branches and prune candidates
472
        3. find prior writes for remaining candidates
473
        4. close open reads_after_writes for all candidates
474
        */
475
        for (auto& entry : open_definitions_branches.at(0)) {
31✔
476
            candidates.insert(entry.first->container());
15✔
477
        }
478
        for (auto& entry : closed_definitionss_branches.at(0)) {
16✔
479
            candidates.insert(entry.first->container());
×
480
        }
481

482
        for (size_t i = 1; i < if_else.size(); i++) {
32✔
483
            for (auto& entry : open_definitions_branches.at(i)) {
30✔
484
                if (candidates.find(entry.first->container()) != candidates.end()) {
14✔
485
                    candidates_tmp.insert(entry.first->container());
14✔
486
                }
14✔
487
            }
488
            candidates.swap(candidates_tmp);
16✔
489
            candidates_tmp.clear();
16✔
490
        }
16✔
491

492
        for (auto& entry : open_definitions) {
24✔
493
            if (candidates.find(entry.first->container()) != candidates.end()) {
8✔
494
                to_close.insert(entry);
4✔
495
            }
4✔
496
        }
497

498
        for (auto& entry : to_close) {
20✔
499
            open_definitions.erase(entry.first);
4✔
500
            closed_definitions.insert(entry);
4✔
501
        }
502
    } else {
16✔
503
        // Over-Approximation: Add all branch definitions to outer definitions or undefined
504
        // Here we add writes to read sets as "anti-reads"
505
        for (auto& branch : open_definitions_branches) {
10✔
506
            for (auto& def : branch) {
8✔
507
                bool found = false;
3✔
508
                for (auto& entry : open_definitions) {
5✔
509
                    if (intersects(*entry.first, *def.first, assumptions_analysis)) {
2✔
510
                        entry.second.insert(def.first);
2✔
511
                        found = true;
2✔
512
                    }
2✔
513
                }
514
                if (!found) {
3✔
515
                    undefined.insert(def.first);
1✔
516
                }
1✔
517
            }
518
        }
519
    }
520

521
    // merge open reads_after_writes
522
    for (auto& branch : open_definitions_branches) {
58✔
523
        for (auto& entry : branch) {
69✔
524
            open_definitions.insert(entry);
32✔
525
        }
526
    }
527
}
21✔
528

529
void DataDependencyAnalysis::visit_while(
7✔
530
    analysis::Users& users,
531
    analysis::AssumptionsAnalysis& assumptions_analysis,
532
    structured_control_flow::While& while_loop,
533
    std::unordered_set<User*>& undefined,
534
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
535
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
536
) {
537
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_while;
7✔
538
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_while;
7✔
539
    std::unordered_set<User*> undefined_while;
7✔
540

541
    visit_sequence(
7✔
542
        users, assumptions_analysis, while_loop.root(), undefined_while, open_definitions_while, closed_definitions_while
7✔
543
    );
544

545
    // Scope-local closed definitions
546
    for (auto& entry : closed_definitions_while) {
9✔
547
        closed_definitions.insert(entry);
2✔
548
    }
549

550
    for (auto open_read : undefined_while) {
9✔
551
        // Over-Approximation: Add loop-carried dependencies for all open reads
552
        for (auto& entry : open_definitions_while) {
5✔
553
            if (entry.first->container() == open_read->container()) {
3✔
554
                entry.second.insert(open_read);
2✔
555
            }
2✔
556
        }
557

558
        // Connect to outside
559
        bool found = false;
2✔
560
        for (auto& entry : open_definitions) {
3✔
561
            if (entry.first->container() == open_read->container()) {
1✔
562
                entry.second.insert(open_read);
1✔
563
                found = true;
1✔
564
            }
1✔
565
        }
566
        if (!found) {
2✔
567
            undefined.insert(open_read);
1✔
568
        }
1✔
569
    }
570

571
    // Add open definitions from while to outside
572
    for (auto& entry : open_definitions_while) {
19✔
573
        open_definitions.insert(entry);
12✔
574
    }
575
}
7✔
576

577
void DataDependencyAnalysis::visit_return(
×
578
    analysis::Users& users,
579
    analysis::AssumptionsAnalysis& assumptions_analysis,
580
    structured_control_flow::Return& return_statement,
581
    std::unordered_set<User*>& undefined,
582
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
583
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
584
) {
NEW
585
    if (return_statement.has_data()) {
×
NEW
586
        auto current_user = users.get_user(return_statement.data(), &return_statement, Use::READ);
×
587

NEW
588
        bool found = false;
×
NEW
589
        for (auto& user : open_definitions) {
×
NEW
590
            if (user.first->container() == return_statement.data()) {
×
NEW
591
                user.second.insert(current_user);
×
NEW
592
                found = true;
×
NEW
593
            }
×
594
        }
NEW
595
        if (!found) {
×
NEW
596
            undefined.insert(current_user);
×
NEW
597
        }
×
NEW
598
    }
×
599

600
    // close all open reads_after_writes
601
    for (auto& entry : open_definitions) {
×
602
        closed_definitions.insert(entry);
×
603
    }
604
    open_definitions.clear();
×
605
}
×
606

607
void DataDependencyAnalysis::visit_sequence(
195✔
608
    analysis::Users& users,
609
    analysis::AssumptionsAnalysis& assumptions_analysis,
610
    structured_control_flow::Sequence& sequence,
611
    std::unordered_set<User*>& undefined,
612
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
613
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
614
) {
615
    for (size_t i = 0; i < sequence.size(); i++) {
447✔
616
        auto child = sequence.at(i);
252✔
617
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
252✔
618
            visit_block(users, assumptions_analysis, *block, undefined, open_definitions, closed_definitions);
151✔
619
        } else if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
252✔
620
            visit_for(users, assumptions_analysis, *for_loop, undefined, open_definitions, closed_definitions);
65✔
621
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
101✔
622
            visit_if_else(users, assumptions_analysis, *if_else, undefined, open_definitions, closed_definitions);
21✔
623
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
36✔
624
            visit_while(users, assumptions_analysis, *while_loop, undefined, open_definitions, closed_definitions);
7✔
625
        } else if (auto return_statement = dynamic_cast<structured_control_flow::Return*>(&child.first)) {
15✔
626
            visit_return(users, assumptions_analysis, *return_statement, undefined, open_definitions, closed_definitions);
×
627
        } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&child.first)) {
8✔
628
            visit_sequence(users, assumptions_analysis, *sequence, undefined, open_definitions, closed_definitions);
×
629
        }
×
630

631
        // handle transitions read
632
        for (auto& entry : child.second.assignments()) {
321✔
633
            for (auto& atom : symbolic::atoms(entry.second)) {
93✔
634
                if (symbolic::is_pointer(atom)) {
24✔
635
                    continue;
×
636
                }
637
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
24✔
638

639
                bool found = false;
24✔
640
                for (auto& user : open_definitions) {
47✔
641
                    if (user.first->container() == atom->get_name()) {
23✔
642
                        user.second.insert(current_user);
22✔
643
                        found = true;
22✔
644
                    }
22✔
645
                }
646
                if (!found) {
24✔
647
                    undefined.insert(current_user);
10✔
648
                }
10✔
649
            }
650
        }
651

652
        // handle transitions write
653
        for (auto& entry : child.second.assignments()) {
321✔
654
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
69✔
655

656
            std::unordered_set<User*> to_close;
69✔
657
            for (auto& user : open_definitions) {
94✔
658
                if (user.first->container() == entry.first->get_name()) {
25✔
659
                    to_close.insert(user.first);
1✔
660
                }
1✔
661
            }
662
            for (auto& user : to_close) {
70✔
663
                closed_definitions.insert({user, open_definitions.at(user)});
1✔
664
                open_definitions.erase(user);
1✔
665
            }
666
            open_definitions.insert({current_user, {}});
69✔
667
        }
69✔
668
    }
252✔
669
}
195✔
670

671
bool DataDependencyAnalysis::loop_depends(
1,882✔
672
    User& previous,
673
    User& current,
674
    analysis::AssumptionsAnalysis& assumptions_analysis,
675
    structured_control_flow::StructuredLoop& loop
676
) {
677
    if (previous.container() != current.container()) {
1,882✔
678
        return false;
1,728✔
679
    }
680
    // Shortcut for scalars
681
    auto& type = this->sdfg_.type(previous.container());
154✔
682
    if (dynamic_cast<const types::Scalar*>(&type)) {
154✔
683
        return true;
40✔
684
    }
685

686
    auto& previous_subsets = previous.subsets();
114✔
687
    auto& current_subsets = current.subsets();
114✔
688

689
    auto previous_scope = Users::scope(&previous);
114✔
690
    auto previous_assumptions = assumptions_analysis.get(loop.root(), *previous_scope, true);
114✔
691
    auto current_scope = Users::scope(&current);
114✔
692
    auto current_assumptions = assumptions_analysis.get(loop.root(), *current_scope, true);
114✔
693

694
    // Check if previous subset is subset of any current subset
695
    for (auto& previous_subset : previous_subsets) {
187✔
696
        for (auto& current_subset : current_subsets) {
188✔
697
            if (symbolic::maps::intersects(
115✔
698
                    previous_subset, current_subset, loop.indvar(), previous_assumptions, current_assumptions
115✔
699
                )) {
700
                return true;
41✔
701
            }
702
        }
703
    }
704

705
    return false;
73✔
706
}
1,882✔
707

708
bool DataDependencyAnalysis::supersedes(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
353✔
709
    if (previous.container() != current.container()) {
353✔
710
        return false;
321✔
711
    }
712
    // Shortcut for scalars
713
    auto& type = this->sdfg_.type(previous.container());
32✔
714
    if (dynamic_cast<const types::Scalar*>(&type)) {
32✔
715
        return true;
14✔
716
    }
717

718
    auto& previous_subsets = previous.subsets();
18✔
719
    auto& current_subsets = current.subsets();
18✔
720
    auto previous_scope = Users::scope(&previous);
18✔
721
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
18✔
722
    auto current_scope = Users::scope(&current);
18✔
723
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
18✔
724

725
    // Check if previous subset is subset of any current subset
726
    for (auto& previous_subset : previous_subsets) {
29✔
727
        bool found = false;
18✔
728
        for (auto& current_subset : current_subsets) {
25✔
729
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, current_assumptions)) {
18✔
730
                found = true;
11✔
731
                break;
11✔
732
            }
733
        }
734
        if (!found) {
18✔
735
            return false;
7✔
736
        }
737
    }
738

739
    return true;
11✔
740
}
353✔
741

742
bool DataDependencyAnalysis::
743
    supersedes_restrictive(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
410✔
744
    if (previous.container() != current.container()) {
410✔
745
        return false;
×
746
    }
747
    // Shortcut for scalars
748
    auto& type = this->sdfg_.type(previous.container());
410✔
749
    if (dynamic_cast<const types::Scalar*>(&type)) {
410✔
750
        return true;
409✔
751
    }
752

753
    auto& previous_subsets = previous.subsets();
1✔
754
    auto& current_subsets = current.subsets();
1✔
755
    auto previous_scope = Users::scope(&previous);
1✔
756
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
1✔
757
    auto current_scope = Users::scope(&current);
1✔
758
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
1✔
759

760
    // Check if previous subset is subset of any current subset
761
    for (auto& previous_subset : previous_subsets) {
1✔
762
        bool found = false;
1✔
763
        for (auto& current_subset : current_subsets) {
2✔
764
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, previous_assumptions)) {
1✔
765
                found = true;
×
766
                break;
×
767
            }
768
        }
769
        if (!found) {
1✔
770
            return false;
1✔
771
        }
772
    }
773

774
    return true;
×
775
}
410✔
776

777
bool DataDependencyAnalysis::intersects(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
1,143✔
778
    if (previous.container() != current.container()) {
1,143✔
779
        return false;
713✔
780
    }
781
    // Shortcut for scalars
782
    auto& type = this->sdfg_.type(previous.container());
430✔
783
    if (dynamic_cast<const types::Scalar*>(&type)) {
430✔
784
        return true;
422✔
785
    }
786

787
    auto& previous_subsets = previous.subsets();
8✔
788
    auto& current_subsets = current.subsets();
8✔
789

790
    auto previous_scope = Users::scope(&previous);
8✔
791
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
8✔
792
    auto current_scope = Users::scope(&current);
8✔
793
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
8✔
794

795
    // Check if any current subset intersects with any previous subset
796
    bool found = false;
8✔
797
    for (auto& current_subset : current_subsets) {
13✔
798
        for (auto& previous_subset : previous_subsets) {
13✔
799
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
8✔
800
                found = true;
3✔
801
                break;
3✔
802
            }
803
        }
804
        if (found) {
8✔
805
            break;
3✔
806
        }
807
    }
808

809
    return found;
8✔
810
}
1,143✔
811

812
/****** Public API ******/
813

814
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
815
    assert(write.use() == Use::WRITE);
×
816
    if (results_.find(write.container()) == results_.end()) {
×
817
        return {};
×
818
    }
819
    auto& raws = results_.at(write.container());
×
820
    assert(raws.find(&write) != raws.end());
×
821

822
    auto& reads_for_write = raws.at(&write);
×
823

824
    std::unordered_set<User*> reads;
×
825
    for (auto& entry : reads_for_write) {
×
826
        reads.insert(entry);
×
827
    }
828

829
    return reads;
×
830
};
×
831

832
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(const std::string& container) {
33✔
833
    if (results_.find(container) == results_.end()) {
33✔
834
        return {};
×
835
    }
836
    return results_.at(container);
33✔
837
};
33✔
838

839
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::defined_by(const std::string& container) {
19✔
840
    auto reads = this->definitions(container);
19✔
841

842
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
843
    for (auto& entry : reads) {
43✔
844
        for (auto& read : entry.second) {
51✔
845
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27✔
846
                read_to_writes_map[read] = {};
20✔
847
            }
20✔
848
            read_to_writes_map[read].insert(entry.first);
27✔
849
        }
850
    }
851
    return read_to_writes_map;
19✔
852
};
19✔
853

854
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
855
    assert(read.use() == Use::READ);
×
856
    auto definitions = this->definitions(read.container());
×
857

858
    std::unordered_set<User*> writes;
×
859
    for (auto& entry : definitions) {
×
860
        for (auto& r : entry.second) {
×
861
            if (&read == r) {
×
862
                writes.insert(entry.first);
×
863
            }
×
864
        }
865
    }
866
    return writes;
×
867
};
×
868

869
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
870
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
871
};
872

873
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::
874
    dependencies(structured_control_flow::StructuredLoop& loop) const {
50✔
875
    return this->loop_carried_dependencies_.at(&loop);
50✔
876
};
877

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

© 2026 Coveralls, Inc