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

daisytuner / sdfglib / 15898736035

26 Jun 2025 09:54AM UTC coverage: 65.01% (-0.1%) from 65.152%
15898736035

push

github

web-flow
Merge pull request #108 from daisytuner/library-node-symbols

Library node symbols

3 of 32 new or added lines in 4 files covered. (9.38%)

4 existing lines in 3 files now uncovered.

8487 of 13055 relevant lines covered (65.01%)

139.12 hits per line

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

84.41
/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)
88✔
19
    : Analysis(sdfg), node_(sdfg.root()) {
88✔
20

21
      };
88✔
22

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

27
      };
×
28

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

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

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

41
    for (auto& entry : open_definitions) {
245✔
42
        closed_definitions.insert(entry);
181✔
43
    }
44

45
    for (auto& entry : closed_definitions) {
252✔
46
        if (results_.find(entry.first->container()) == results_.end()) {
188✔
47
            results_.insert({entry.first->container(), {}});
123✔
48
        }
123✔
49
        results_.at(entry.first->container()).insert(entry);
188✔
50
    }
51
};
64✔
52

53
/****** Visitor API ******/
54

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

62
    for (auto node : dataflow.topological_sort()) {
340✔
63
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
192✔
64
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
120✔
65
                if (dataflow.in_degree(*node) > 0) {
120✔
66
                    Use use = Use::WRITE;
72✔
67
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
144✔
68
                        if (iedge.src_conn() == "refs" || iedge.dst_conn() == "refs") {
72✔
69
                            use = Use::MOVE;
×
70
                            break;
×
71
                        }
72
                    }
73

74
                    auto current_user = users.get_user(access_node->data(), access_node, use);
72✔
75

76
                    if (use == Use::WRITE) {
72✔
77
                        std::unordered_map<User*, std::unordered_set<User*>> to_close;
72✔
78
                        for (auto& user : open_definitions) {
96✔
79
                            if (supersedes(*user.first, *current_user, assumptions_analysis)) {
24✔
80
                                to_close.insert(user);
7✔
81
                            }
7✔
82
                        }
83
                        for (auto& user : to_close) {
79✔
84
                            open_definitions.erase(user.first);
7✔
85
                            closed_definitions.insert(user);
7✔
86
                        }
87
                        open_definitions.insert({current_user, {}});
72✔
88
                    }
72✔
89
                }
72✔
90
                if (dataflow.out_degree(*access_node) > 0) {
120✔
91
                    Use use = Use::READ;
51✔
92
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
102✔
93
                        if (oedge.src_conn() == "refs" || oedge.dst_conn() == "refs") {
51✔
94
                            use = Use::VIEW;
×
95
                            break;
×
96
                        }
97
                    }
98

99
                    auto current_user = users.get_user(access_node->data(), access_node, use);
51✔
100

101
                    if (use == Use::READ) {
51✔
102
                        bool found = false;
51✔
103
                        for (auto& user : open_definitions) {
60✔
104
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
9✔
105
                                user.second.insert(current_user);
6✔
106
                                found = true;
6✔
107
                            }
6✔
108
                        }
109
                        if (!found) {
51✔
110
                            undefined.insert(current_user);
46✔
111
                        }
46✔
112
                    }
51✔
113
                }
51✔
114
            }
120✔
115
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
192✔
116
            if (tasklet->is_conditional()) {
72✔
117
                auto& condition = tasklet->condition();
×
118
                for (auto& atom : symbolic::atoms(condition)) {
×
119
                    auto current_user = users.get_user(atom->get_name(), tasklet, Use::READ);
×
120
                    {
121
                        bool found = false;
×
122
                        for (auto& user : open_definitions) {
×
123
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
124
                                user.second.insert(current_user);
×
125
                                found = true;
×
126
                            }
×
127
                        }
128
                        if (!found) {
×
129
                            undefined.insert(current_user);
×
130
                        }
×
131
                    }
132
                }
133
            }
×
134
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
72✔
NEW
135
            for (auto& symbol : library_node->symbols()) {
×
NEW
136
                auto current_user = users.get_user(symbol->get_name(), library_node, Use::READ);
×
137
                {
NEW
138
                    bool found = false;
×
NEW
139
                    for (auto& user : open_definitions) {
×
NEW
140
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
NEW
141
                            user.second.insert(current_user);
×
NEW
142
                            found = true;
×
NEW
143
                        }
×
144
                    }
NEW
145
                    if (!found) {
×
NEW
146
                        undefined.insert(current_user);
×
NEW
147
                    }
×
148
                }
149
            }
UNCOV
150
        }
×
151

152
        for (auto& oedge : dataflow.out_edges(*node)) {
315✔
153
            std::unordered_set<std::string> used;
123✔
154
            for (auto& dim : oedge.subset()) {
233✔
155
                for (auto atom : symbolic::atoms(dim)) {
195✔
156
                    used.insert(atom->get_name());
85✔
157
                }
85✔
158
            }
159
            for (auto& atom : used) {
208✔
160
                auto current_user = users.get_user(atom, &oedge, Use::READ);
85✔
161

162
                {
163
                    bool found = false;
85✔
164
                    for (auto& user : open_definitions) {
95✔
165
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
10✔
166
                            user.second.insert(current_user);
4✔
167
                            found = true;
4✔
168
                        }
4✔
169
                    }
170
                    if (!found) {
85✔
171
                        undefined.insert(current_user);
81✔
172
                    }
81✔
173
                }
174
            }
175
        }
123✔
176
    }
177
}
148✔
178

179
void DataDependencyAnalysis::visit_for(
54✔
180
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
181
    structured_control_flow::StructuredLoop& for_loop, std::unordered_set<User*>& undefined,
182
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
183
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
184
    // Init - Read
185
    for (auto atom : symbolic::atoms(for_loop.init())) {
60✔
186
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, true);
6✔
187

188
        bool found = false;
6✔
189
        for (auto& user : open_definitions) {
7✔
190
            if (user.first->container() == atom->get_name()) {
1✔
191
                user.second.insert(current_user);
1✔
192
                found = true;
1✔
193
            }
1✔
194
        }
195
        if (!found) {
6✔
196
            undefined.insert(current_user);
5✔
197
        }
5✔
198
    }
6✔
199

200
    // Init - Write
201
    {
202
        // Write Induction Variable
203
        auto current_user =
54✔
204
            users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, true);
54✔
205

206
        std::unordered_set<User*> to_close;
54✔
207
        for (auto& user : open_definitions) {
66✔
208
            if (supersedes(*user.first, *current_user, assumptions_analysis)) {
12✔
209
                to_close.insert(user.first);
2✔
210
            }
2✔
211
        }
212
        for (auto& user : to_close) {
56✔
213
            closed_definitions.insert({user, open_definitions.at(user)});
2✔
214
            open_definitions.erase(user);
2✔
215
        }
216
        open_definitions.insert({current_user, {}});
54✔
217

218
        // Improve: If loop is executed at least once, we can close the init's definition
219
        // TODO: Implement this
220
    }
54✔
221

222
    // Update - Write
223
    {
224
        // Exists after loop and inside body
225
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE,
54✔
226
                                           false, false, true);
227
        open_definitions.insert({current_user, {}});
54✔
228

229
        // Improve: If loop is executed at least once, we can close the init's definition
230
        // TODO: Implement this
231
    }
232

233
    // Condition - Read
234
    for (auto atom : symbolic::atoms(for_loop.condition())) {
154✔
235
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, true);
100✔
236

237
        bool found = false;
100✔
238
        for (auto& user : open_definitions) {
311✔
239
            if (user.first->container() == atom->get_name()) {
211✔
240
                user.second.insert(current_user);
109✔
241
                found = true;
109✔
242
            }
109✔
243
        }
244
        if (!found) {
100✔
245
            undefined.insert(current_user);
45✔
246
        }
45✔
247
    }
100✔
248

249
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_for;
54✔
250
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_for;
54✔
251
    std::unordered_set<User*> undefined_for;
54✔
252

253
    // Add assumptions for body
254
    visit_sequence(users, assumptions_analysis, for_loop.root(), undefined_for,
54✔
255
                   open_definitions_for, closed_definitions_for);
256

257
    // Update - Read
258
    for (auto atom : symbolic::atoms(for_loop.update())) {
108✔
259
        auto current_user =
54✔
260
            users.get_user(atom->get_name(), &for_loop, Use::READ, false, false, true);
54✔
261

262
        bool found = false;
54✔
263
        for (auto& user : open_definitions_for) {
129✔
264
            if (user.first->container() == atom->get_name()) {
75✔
265
                user.second.insert(current_user);
1✔
266
                found = true;
1✔
267
            }
1✔
268
        }
269
        if (!found) {
54✔
270
            undefined_for.insert(current_user);
53✔
271
        }
53✔
272
    }
54✔
273

274
    // Merge for with outside
275

276
    // Closed definitions are simply merged
277
    for (auto& entry : closed_definitions_for) {
54✔
278
        closed_definitions.insert(entry);
×
279
    }
280

281
    // Undefined reads are matched or forwarded
282
    for (auto open_read : undefined_for) {
276✔
283
        bool found = false;
222✔
284
        for (auto& entry : open_definitions) {
682✔
285
            if (intersects(*entry.first, *open_read, assumptions_analysis)) {
460✔
286
                entry.second.insert(open_read);
292✔
287
                found = true;
292✔
288
            }
292✔
289
        }
290
        if (!found) {
222✔
291
            undefined.insert(open_read);
76✔
292
        }
76✔
293
    }
294

295
    // Open definitions may close outside open definitions after loop
296
    std::unordered_set<User*> to_close;
54✔
297
    for (auto& previous : open_definitions) {
172✔
298
        for (auto& user : open_definitions_for) {
275✔
299
            if (supersedes(*previous.first, *user.first, assumptions_analysis)) {
159✔
300
                to_close.insert(previous.first);
2✔
301
                break;
2✔
302
            }
303
        }
304
    }
305
    for (auto& user : to_close) {
56✔
306
        closed_definitions.insert({user, open_definitions.at(user)});
2✔
307
        open_definitions.erase(user);
2✔
308
    }
309

310
    // Add loop-carried dependencies
311

312
    // Criterion 1: Loop is monotonic -> indvar never takes the same value twice
313
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
54✔
314
    if (is_monotonic) {
54✔
315
        // Case: Can analyze
316
        this->loop_carried_dependencies_.insert({&for_loop, {}});
53✔
317
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
53✔
318

319
        // We can focus on written containers
320

321
        // Case 1: Read-Write between iterations
322
        for (auto& read : undefined_for) {
274✔
323
            for (auto& write : open_definitions_for) {
570✔
324
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop)) {
349✔
325
                    if (dependencies.find(read->container()) == dependencies.end()) {
7✔
326
                        dependencies.insert(
14✔
327
                            {read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
7✔
328
                    }
7✔
329
                    write.second.insert(read);
7✔
330
                }
7✔
331
            }
332
        }
333

334
        // Case 2: Write-Write between iterations
335
        for (auto& write : open_definitions_for) {
127✔
336
            if (dependencies.find(write.first->container()) != dependencies.end()) {
74✔
337
                continue;
18✔
338
            }
339
            for (auto& write_2 : open_definitions_for) {
128✔
340
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis, for_loop)) {
91✔
341
                    dependencies.insert(
38✔
342
                        {write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
19✔
343
                    break;
19✔
344
                }
345
            }
346
        }
347
    } else {
53✔
348
        // Case: Cannot analyze
349
        this->loop_carried_dependencies_.insert({&for_loop, {}});
1✔
350
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
1✔
351

352
        // Over-Approximation:
353
        // Add loop-carried dependencies for all open reads to all open writes
354
        for (auto& read : undefined_for) {
2✔
355
            for (auto& write : open_definitions_for) {
2✔
356
                if (intersects(*write.first, *read, assumptions_analysis)) {
1✔
357
                    write.second.insert(read);
×
358
                    dependencies.insert({read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
×
359
                }
×
360
            }
361
        }
362
        // Add loop-carried dependencies for writes
363
        for (auto& write : open_definitions_for) {
2✔
364
            if (dependencies.find(write.first->container()) == dependencies.end()) {
1✔
365
                dependencies.insert(
2✔
366
                    {write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
1✔
367
            }
1✔
368
        }
369
    }
370

371
    // Add open definitions from for to outside
372
    for (auto& entry : open_definitions_for) {
129✔
373
        open_definitions.insert(entry);
75✔
374
    }
375
}
54✔
376

377
void DataDependencyAnalysis::visit_if_else(
21✔
378
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
379
    structured_control_flow::IfElse& if_else, std::unordered_set<User*>& undefined,
380
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
381
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
382
    // Read Conditions
383
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
384
        auto child = if_else.at(i).second;
37✔
385
        for (auto atom : symbolic::atoms(child)) {
61✔
386
            auto current_user = users.get_user(atom->get_name(), &if_else, Use::READ);
24✔
387

388
            bool found = false;
24✔
389
            for (auto& user : open_definitions) {
38✔
390
                if (user.first->container() == atom->get_name()) {
14✔
391
                    user.second.insert(current_user);
7✔
392
                    found = true;
7✔
393
                }
7✔
394
            }
395
            if (!found) {
24✔
396
                undefined.insert(current_user);
17✔
397
            }
17✔
398
        }
24✔
399
    }
37✔
400

401
    std::vector<std::unordered_set<User*>> undefined_branches(if_else.size());
21✔
402
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> open_definitions_branches(
21✔
403
        if_else.size());
21✔
404
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> closed_definitionss_branches(
21✔
405
        if_else.size());
21✔
406
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
407
        auto& child = if_else.at(i).first;
37✔
408
        visit_sequence(users, assumptions_analysis, child, undefined_branches.at(i),
74✔
409
                       open_definitions_branches.at(i), closed_definitionss_branches.at(i));
37✔
410
    }
37✔
411

412
    // merge partial open reads
413
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
414
        for (auto& entry : undefined_branches.at(i)) {
41✔
415
            bool found = false;
4✔
416
            for (auto& entry2 : open_definitions) {
8✔
417
                if (entry2.first->container() == entry->container()) {
4✔
418
                    entry2.second.insert(entry);
4✔
419
                    found = true;
4✔
420
                }
4✔
421
            }
422
            if (!found) {
4✔
423
                undefined.insert(entry);
×
424
            }
×
425
        }
426
    }
37✔
427

428
    // merge closed writes
429
    for (auto& closing : closed_definitionss_branches) {
58✔
430
        for (auto& entry : closing) {
37✔
431
            closed_definitions.insert(entry);
×
432
        }
433
    }
434

435
    // Close open reads_after_writes for complete branches
436
    if (if_else.is_complete()) {
21✔
437
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
16✔
438
        std::unordered_set<std::string> candidates;
16✔
439
        std::unordered_set<std::string> candidates_tmp;
16✔
440

441
        /* Complete close open reads_after_writes
442
        1. get candidates from first iteration
443
        2. iterate over all branches and prune candidates
444
        3. find prior writes for remaining candidates
445
        4. close open reads_after_writes for all candidates
446
        */
447
        for (auto& entry : open_definitions_branches.at(0)) {
31✔
448
            candidates.insert(entry.first->container());
15✔
449
        }
450
        for (auto& entry : closed_definitionss_branches.at(0)) {
16✔
451
            candidates.insert(entry.first->container());
×
452
        }
453

454
        for (size_t i = 1; i < if_else.size(); i++) {
32✔
455
            for (auto& entry : open_definitions_branches.at(i)) {
30✔
456
                if (candidates.find(entry.first->container()) != candidates.end()) {
14✔
457
                    candidates_tmp.insert(entry.first->container());
14✔
458
                }
14✔
459
            }
460
            candidates.swap(candidates_tmp);
16✔
461
            candidates_tmp.clear();
16✔
462
        }
16✔
463

464
        for (auto& entry : open_definitions) {
24✔
465
            if (candidates.find(entry.first->container()) != candidates.end()) {
8✔
466
                to_close.insert(entry);
4✔
467
            }
4✔
468
        }
469

470
        for (auto& entry : to_close) {
20✔
471
            open_definitions.erase(entry.first);
4✔
472
            closed_definitions.insert(entry);
4✔
473
        }
474
    } else {
16✔
475
        // Over-Approximation: Add all branch definitions to outer definitions or undefined
476
        // Here we add writes to read sets as "anti-reads"
477
        for (auto& branch : open_definitions_branches) {
10✔
478
            for (auto& def : branch) {
8✔
479
                bool found = false;
3✔
480
                for (auto& entry : open_definitions) {
5✔
481
                    if (intersects(*entry.first, *def.first, assumptions_analysis)) {
2✔
482
                        entry.second.insert(def.first);
2✔
483
                        found = true;
2✔
484
                    }
2✔
485
                }
486
                if (!found) {
3✔
487
                    undefined.insert(def.first);
1✔
488
                }
1✔
489
            }
490
        }
491
    }
492

493
    // merge open reads_after_writes
494
    for (auto& branch : open_definitions_branches) {
58✔
495
        for (auto& entry : branch) {
69✔
496
            open_definitions.insert(entry);
32✔
497
        }
498
    }
499
}
21✔
500

501
void DataDependencyAnalysis::visit_while(
7✔
502
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
503
    structured_control_flow::While& while_loop, std::unordered_set<User*>& undefined,
504
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
505
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
506
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_while;
7✔
507
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_while;
7✔
508
    std::unordered_set<User*> undefined_while;
7✔
509

510
    visit_sequence(users, assumptions_analysis, while_loop.root(), undefined_while,
7✔
511
                   open_definitions_while, closed_definitions_while);
512

513
    // Scope-local closed definitions
514
    for (auto& entry : closed_definitions_while) {
9✔
515
        closed_definitions.insert(entry);
2✔
516
    }
517

518
    for (auto open_read : undefined_while) {
9✔
519
        // Over-Approximation: Add loop-carried dependencies for all open reads
520
        for (auto& entry : open_definitions_while) {
5✔
521
            if (entry.first->container() == open_read->container()) {
3✔
522
                entry.second.insert(open_read);
2✔
523
            }
2✔
524
        }
525

526
        // Connect to outside
527
        bool found = false;
2✔
528
        for (auto& entry : open_definitions) {
3✔
529
            if (entry.first->container() == open_read->container()) {
1✔
530
                entry.second.insert(open_read);
1✔
531
                found = true;
1✔
532
            }
1✔
533
        }
534
        if (!found) {
2✔
535
            undefined.insert(open_read);
1✔
536
        }
1✔
537
    }
538

539
    // Add open definitions from while to outside
540
    for (auto& entry : open_definitions_while) {
19✔
541
        open_definitions.insert(entry);
12✔
542
    }
543
}
7✔
544

545
void DataDependencyAnalysis::visit_return(
×
546
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
547
    structured_control_flow::Return& return_statement, std::unordered_set<User*>& undefined,
548
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
549
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
550
    // close all open reads_after_writes
551
    for (auto& entry : open_definitions) {
×
552
        closed_definitions.insert(entry);
×
553
    }
554
    open_definitions.clear();
×
555
}
×
556

557
void DataDependencyAnalysis::visit_sequence(
179✔
558
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
559
    structured_control_flow::Sequence& sequence, std::unordered_set<User*>& undefined,
560
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
561
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
562
    for (size_t i = 0; i < sequence.size(); i++) {
409✔
563
        auto child = sequence.at(i);
230✔
564
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
230✔
565
            visit_block(users, assumptions_analysis, *block, undefined, open_definitions,
280✔
566
                        closed_definitions);
140✔
567
        } else if (auto for_loop =
230✔
568
                       dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
90✔
569
            visit_for(users, assumptions_analysis, *for_loop, undefined, open_definitions,
108✔
570
                      closed_definitions);
54✔
571
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
90✔
572
            visit_if_else(users, assumptions_analysis, *if_else, undefined, open_definitions,
42✔
573
                          closed_definitions);
21✔
574
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
36✔
575
            visit_while(users, assumptions_analysis, *while_loop, undefined, open_definitions,
14✔
576
                        closed_definitions);
7✔
577
        } else if (auto return_statement =
15✔
578
                       dynamic_cast<structured_control_flow::Return*>(&child.first)) {
8✔
579
            visit_return(users, assumptions_analysis, *return_statement, undefined,
×
580
                         open_definitions, closed_definitions);
×
581
        } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&child.first)) {
8✔
582
            visit_sequence(users, assumptions_analysis, *sequence, undefined, open_definitions,
×
583
                           closed_definitions);
×
584
        }
×
585

586
        // handle transitions read
587
        for (auto& entry : child.second.assignments()) {
299✔
588
            for (auto& atom : symbolic::atoms(entry.second)) {
93✔
589
                if (symbolic::is_pointer(atom)) {
24✔
590
                    continue;
×
591
                }
592
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
24✔
593

594
                bool found = false;
24✔
595
                for (auto& user : open_definitions) {
47✔
596
                    if (user.first->container() == atom->get_name()) {
23✔
597
                        user.second.insert(current_user);
22✔
598
                        found = true;
22✔
599
                    }
22✔
600
                }
601
                if (!found) {
24✔
602
                    undefined.insert(current_user);
10✔
603
                }
10✔
604
            }
605
        }
606

607
        // handle transitions write
608
        for (auto& entry : child.second.assignments()) {
299✔
609
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
69✔
610

611
            std::unordered_set<User*> to_close;
69✔
612
            for (auto& user : open_definitions) {
94✔
613
                if (user.first->container() == entry.first->get_name()) {
25✔
614
                    to_close.insert(user.first);
1✔
615
                }
1✔
616
            }
617
            for (auto& user : to_close) {
70✔
618
                closed_definitions.insert({user, open_definitions.at(user)});
1✔
619
                open_definitions.erase(user);
1✔
620
            }
621
            open_definitions.insert({current_user, {}});
69✔
622
        }
69✔
623
    }
230✔
624
}
179✔
625

626
bool DataDependencyAnalysis::loop_depends(User& previous, User& current,
440✔
627
                                          analysis::AssumptionsAnalysis& assumptions_analysis,
628
                                          structured_control_flow::StructuredLoop& loop) {
629
    if (previous.container() != current.container()) {
440✔
630
        return false;
359✔
631
    }
632
    // Shortcut for scalars
633
    auto& type = this->sdfg_.type(previous.container());
81✔
634
    if (dynamic_cast<const types::Scalar*>(&type)) {
81✔
635
        return true;
18✔
636
    }
637

638
    auto& previous_subsets = previous.subsets();
63✔
639
    auto& current_subsets = current.subsets();
63✔
640

641
    auto previous_scope = Users::scope(&previous);
63✔
642
    auto previous_assumptions = assumptions_analysis.get(loop.root(), *previous_scope, true);
63✔
643
    auto current_scope = Users::scope(&current);
63✔
644
    auto current_assumptions = assumptions_analysis.get(loop.root(), *current_scope, true);
63✔
645

646
    // Check if previous subset is subset of any current subset
647
    for (auto& previous_subset : previous_subsets) {
118✔
648
        for (auto& current_subset : current_subsets) {
118✔
649
            if (symbolic::maps::intersects(previous_subset, current_subset, loop.indvar(),
63✔
650
                                           previous_assumptions, current_assumptions)) {
651
                return true;
8✔
652
            }
653
        }
654
    }
655

656
    return false;
55✔
657
}
440✔
658

659
bool DataDependencyAnalysis::supersedes(User& previous, User& current,
195✔
660
                                        analysis::AssumptionsAnalysis& assumptions_analysis) {
661
    if (previous.container() != current.container()) {
195✔
662
        return false;
179✔
663
    }
664
    // Shortcut for scalars
665
    auto& type = this->sdfg_.type(previous.container());
16✔
666
    if (dynamic_cast<const types::Scalar*>(&type)) {
16✔
667
        return true;
3✔
668
    }
669

670
    auto& previous_subsets = previous.subsets();
13✔
671
    auto& current_subsets = current.subsets();
13✔
672
    auto previous_scope = Users::scope(&previous);
13✔
673
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
13✔
674
    auto current_scope = Users::scope(&current);
13✔
675
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
13✔
676

677
    // Check if previous subset is subset of any current subset
678
    for (auto& previous_subset : previous_subsets) {
21✔
679
        bool found = false;
13✔
680
        for (auto& current_subset : current_subsets) {
18✔
681
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions,
13✔
682
                                    current_assumptions)) {
683
                found = true;
8✔
684
                break;
8✔
685
            }
686
        }
687
        if (!found) {
13✔
688
            return false;
5✔
689
        }
690
    }
691

692
    return true;
8✔
693
}
195✔
694

695
bool DataDependencyAnalysis::intersects(User& previous, User& current,
482✔
696
                                        analysis::AssumptionsAnalysis& assumptions_analysis) {
697
    if (previous.container() != current.container()) {
482✔
698
        return false;
177✔
699
    }
700
    // Shortcut for scalars
701
    auto& type = this->sdfg_.type(previous.container());
305✔
702
    if (dynamic_cast<const types::Scalar*>(&type)) {
305✔
703
        return true;
303✔
704
    }
705

706
    auto& previous_subsets = previous.subsets();
2✔
707
    auto& current_subsets = current.subsets();
2✔
708

709
    auto previous_scope = Users::scope(&previous);
2✔
710
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
2✔
711
    auto current_scope = Users::scope(&current);
2✔
712
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
2✔
713

714
    // Check if any current subset intersects with any previous subset
715
    bool found = false;
2✔
716
    for (auto& current_subset : current_subsets) {
3✔
717
        for (auto& previous_subset : previous_subsets) {
3✔
718
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions,
2✔
719
                                       previous_assumptions)) {
720
                found = true;
1✔
721
                break;
1✔
722
            }
723
        }
724
        if (found) {
2✔
725
            break;
1✔
726
        }
727
    }
728

729
    return found;
2✔
730
}
482✔
731

732
/****** Public API ******/
733

734
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
735
    assert(write.use() == Use::WRITE);
×
736
    if (results_.find(write.container()) == results_.end()) {
×
737
        return {};
×
738
    }
739
    auto& raws = results_.at(write.container());
×
740
    assert(raws.find(&write) != raws.end());
×
741

742
    auto& reads_for_write = raws.at(&write);
×
743

744
    std::unordered_set<User*> reads;
×
745
    for (auto& entry : reads_for_write) {
×
746
        reads.insert(entry);
×
747
    }
748

749
    return reads;
×
750
};
×
751

752
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(
33✔
753
    const std::string& container) {
754
    if (results_.find(container) == results_.end()) {
33✔
755
        return {};
×
756
    }
757
    return results_.at(container);
33✔
758
};
33✔
759

760
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::defined_by(
19✔
761
    const std::string& container) {
762
    auto reads = this->definitions(container);
19✔
763

764
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
765
    for (auto& entry : reads) {
43✔
766
        for (auto& read : entry.second) {
51✔
767
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27✔
768
                read_to_writes_map[read] = {};
20✔
769
            }
20✔
770
            read_to_writes_map[read].insert(entry.first);
27✔
771
        }
772
    }
773
    return read_to_writes_map;
19✔
774
};
19✔
775

776
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
777
    assert(read.use() == Use::READ);
×
778
    auto definitions = this->definitions(read.container());
×
779

780
    std::unordered_set<User*> writes;
×
781
    for (auto& entry : definitions) {
×
782
        for (auto& r : entry.second) {
×
783
            if (&read == r) {
×
784
                writes.insert(entry.first);
×
785
            }
×
786
        }
787
    }
788
    return writes;
×
789
};
×
790

791
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
792
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
793
};
794

795
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::dependencies(
39✔
796
    structured_control_flow::StructuredLoop& loop) const {
797
    return this->loop_carried_dependencies_.at(&loop);
39✔
798
};
799

800
}  // namespace analysis
801
}  // 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