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

daisytuner / sdfglib / 16840735675

08 Aug 2025 09:18PM UTC coverage: 64.703% (+0.02%) from 64.684%
16840735675

Pull #181

github

web-flow
Merge 136e8d84d into b221d342f
Pull Request #181: Data dependency analysis: Supersede open definitions

45 of 65 new or added lines in 2 files covered. (69.23%)

2 existing lines in 1 file now uncovered.

8953 of 13837 relevant lines covered (64.7%)

123.63 hits per line

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

83.33
/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

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

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

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

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

51
/****** Visitor API ******/
52

53
void DataDependencyAnalysis::visit_block(
159✔
54
    analysis::Users& users,
55
    analysis::AssumptionsAnalysis& assumptions_analysis,
56
    structured_control_flow::Block& block,
57
    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
) {
61
    auto& dataflow = block.dataflow();
159✔
62

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

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

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

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

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

107
                    if (use == Use::READ) {
67✔
108
                        // Find all definitions that we read from
109
                        bool found = false;
67✔
110
                        for (auto& user : open_definitions) {
96✔
111
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
29✔
112
                                user.second.insert(current_user);
9✔
113
                                found = true;
9✔
114
                            }
9✔
115
                        }
116
                        if (!found) {
67✔
117
                            undefined.insert(current_user);
59✔
118
                        } else {
59✔
119
                            bool supersedes_all = true;
8✔
120
                            for (auto& user : open_definitions) {
20✔
121
                                if (user.first->container() == current_user->container()) {
12✔
122
                                    supersedes_all &= supersedes(*user.first, *current_user, assumptions_analysis);
9✔
123
                                }
9✔
124
                            }
125
                            if (!supersedes_all) {
8✔
NEW
126
                                undefined.insert(current_user);
×
NEW
127
                            }
×
128
                        }
129
                    }
67✔
130
                }
67✔
131
            }
147✔
132
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
230✔
133
            if (tasklet->is_conditional()) {
83✔
134
                auto& condition = tasklet->condition();
×
135
                for (auto& atom : symbolic::atoms(condition)) {
×
136
                    auto current_user = users.get_user(atom->get_name(), tasklet, Use::READ);
×
137
                    {
138
                        // Find all definitions that we read from
139
                        bool found = false;
×
NEW
140
                        bool superseded_all = false;
×
141
                        for (auto& user : open_definitions) {
×
142
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
143
                                user.second.insert(current_user);
×
NEW
144
                                if (!found) {
×
NEW
145
                                    found = true;
×
NEW
146
                                    superseded_all = true;
×
NEW
147
                                }
×
148

NEW
149
                                superseded_all &= supersedes(*current_user, *user.first, assumptions_analysis);
×
UNCOV
150
                            }
×
151
                        }
NEW
152
                        if (!superseded_all) {
×
153
                            undefined.insert(current_user);
×
154
                        }
×
155
                    }
156
                }
157
            }
×
158
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
83✔
159
            for (auto& symbol : library_node->symbols()) {
×
160
                auto current_user = users.get_user(symbol->get_name(), library_node, Use::READ);
×
161
                {
162
                    // Find all definitions that we read from
163
                    bool found = false;
×
NEW
164
                    bool superseded_all = false;
×
165
                    for (auto& user : open_definitions) {
×
166
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
167
                            user.second.insert(current_user);
×
NEW
168
                            if (!found) {
×
NEW
169
                                found = true;
×
NEW
170
                                superseded_all = true;
×
NEW
171
                            }
×
NEW
172
                            superseded_all &= supersedes(*current_user, *user.first, assumptions_analysis);
×
UNCOV
173
                        }
×
174
                    }
NEW
175
                    if (!superseded_all) {
×
176
                        undefined.insert(current_user);
×
177
                    }
×
178
                }
179
            }
180
        }
×
181

182
        for (auto& oedge : dataflow.out_edges(*node)) {
381✔
183
            std::unordered_set<std::string> used;
151✔
184
            for (auto& dim : oedge.subset()) {
282✔
185
                for (auto atom : symbolic::atoms(dim)) {
268✔
186
                    used.insert(atom->get_name());
137✔
187
                }
137✔
188
            }
189
            for (auto& atom : used) {
288✔
190
                auto current_user = users.get_user(atom, &oedge, Use::READ);
137✔
191

192
                {
193
                    // Find all definitions that we read from
194
                    bool found = false;
137✔
195
                    bool superseded_all = false;
137✔
196
                    for (auto& user : open_definitions) {
221✔
197
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
84✔
198
                            user.second.insert(current_user);
4✔
199
                            if (!found) {
4✔
200
                                found = true;
4✔
201
                                superseded_all = true;
4✔
202
                            }
4✔
203
                            superseded_all &= supersedes(*current_user, *user.first, assumptions_analysis);
4✔
204
                        }
4✔
205
                    }
206
                    if (!superseded_all) {
137✔
207
                        undefined.insert(current_user);
133✔
208
                    }
133✔
209
                }
210
            }
211
        }
151✔
212
    }
213
}
159✔
214

215
void DataDependencyAnalysis::visit_for(
65✔
216
    analysis::Users& users,
217
    analysis::AssumptionsAnalysis& assumptions_analysis,
218
    structured_control_flow::StructuredLoop& for_loop,
219
    std::unordered_set<User*>& undefined,
220
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
221
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
222
) {
223
    // Init - Read
224
    for (auto atom : symbolic::atoms(for_loop.init())) {
73✔
225
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, true);
8✔
226

227
        bool found = false;
8✔
228
        for (auto& user : open_definitions) {
16✔
229
            if (user.first->container() == atom->get_name()) {
8✔
230
                user.second.insert(current_user);
1✔
231
                found = true;
1✔
232
            }
1✔
233
        }
234
        if (!found) {
8✔
235
            undefined.insert(current_user);
7✔
236
        }
7✔
237
    }
8✔
238

239
    // Init - Write
240
    {
241
        // Write Induction Variable
242
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, true);
65✔
243

244
        std::unordered_set<User*> to_close;
65✔
245
        for (auto& user : open_definitions) {
85✔
246
            if (supersedes(*user.first, *current_user, assumptions_analysis)) {
20✔
247
                to_close.insert(user.first);
2✔
248
            }
2✔
249
        }
250
        for (auto& user : to_close) {
67✔
251
            closed_definitions.insert({user, open_definitions.at(user)});
2✔
252
            open_definitions.erase(user);
2✔
253
        }
254
        open_definitions.insert({current_user, {}});
65✔
255

256
        // Improve: If loop is executed at least once, we can close the init's definition
257
        // TODO: Implement this
258
    }
65✔
259

260
    // Update - Write
261
    {
262
        // Exists after loop and inside body
263
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, false, false, true);
65✔
264
        open_definitions.insert({current_user, {}});
65✔
265

266
        // Improve: If loop is executed at least once, we can close the init's definition
267
        // TODO: Implement this
268
    }
269

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

274
        bool found = false;
119✔
275
        for (auto& user : open_definitions) {
377✔
276
            if (user.first->container() == atom->get_name()) {
258✔
277
                user.second.insert(current_user);
131✔
278
                found = true;
131✔
279
            }
131✔
280
        }
281
        if (!found) {
119✔
282
            undefined.insert(current_user);
53✔
283
        }
53✔
284
    }
119✔
285

286
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_for;
65✔
287
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_for;
65✔
288
    std::unordered_set<User*> undefined_for;
65✔
289

290
    // Add assumptions for body
291
    visit_sequence(
65✔
292
        users, assumptions_analysis, for_loop.root(), undefined_for, open_definitions_for, closed_definitions_for
65✔
293
    );
294

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

299
        bool found = false;
65✔
300
        for (auto& user : open_definitions_for) {
188✔
301
            if (user.first->container() == atom->get_name()) {
123✔
302
                user.second.insert(current_user);
1✔
303
                found = true;
1✔
304
            }
1✔
305
        }
306
        if (!found) {
65✔
307
            undefined_for.insert(current_user);
64✔
308
        }
64✔
309
    }
65✔
310

311
    // Merge for with outside
312

313
    // Closed definitions are simply merged
314
    for (auto& entry : closed_definitions_for) {
68✔
315
        closed_definitions.insert(entry);
3✔
316
    }
317

318
    // Undefined reads are matched or forwarded
319
    for (auto open_read : undefined_for) {
470✔
320
        bool found = false;
405✔
321
        for (auto& entry : open_definitions) {
1,432✔
322
            if (intersects(*entry.first, *open_read, assumptions_analysis)) {
1,027✔
323
                entry.second.insert(open_read);
410✔
324
                found = true;
410✔
325
            }
410✔
326
        }
327
        if (!found) {
405✔
328
            undefined.insert(open_read);
199✔
329
        } else {
199✔
330
            bool subset_all = true;
206✔
331
            for (auto& entry : open_definitions) {
686✔
332
                if (entry.first->container() == open_read->container()) {
480✔
333
                    subset_all &= supersedes_restrictive(*open_read, *entry.first, assumptions_analysis);
410✔
334
                }
410✔
335
            }
336
            if (!subset_all) {
206✔
337
                undefined.insert(open_read);
1✔
338
            }
1✔
339
        }
340
    }
341

342
    // Open definitions may close outside open definitions after loop
343
    std::unordered_set<User*> to_close;
65✔
344
    for (auto& previous : open_definitions) {
213✔
345
        for (auto& user : open_definitions_for) {
430✔
346
            if (supersedes(*previous.first, *user.first, assumptions_analysis)) {
284✔
347
                to_close.insert(previous.first);
2✔
348
                break;
2✔
349
            }
350
        }
351
    }
352
    for (auto& user : to_close) {
67✔
353
        closed_definitions.insert({user, open_definitions.at(user)});
2✔
354
        open_definitions.erase(user);
2✔
355
    }
356

357
    // Add loop-carried dependencies
358

359
    // Criterion 1: Loop is monotonic -> indvar never takes the same value twice
360
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
65✔
361
    if (is_monotonic) {
65✔
362
        // Case: Can analyze
363
        this->loop_carried_dependencies_.insert({&for_loop, {}});
64✔
364
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
64✔
365

366
        // We can focus on written containers
367

368
        // Case 1: Read-Write between iterations
369
        for (auto& read : undefined_for) {
468✔
370
            for (auto& write : open_definitions_for) {
2,071✔
371
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop)) {
1,667✔
372
                    dependencies[read->container()] = LOOP_CARRIED_DEPENDENCY_READ_WRITE;
40✔
373
                    write.second.insert(read);
40✔
374
                }
40✔
375
            }
376
        }
377

378
        // Case 2: Write-Write between iterations
379
        for (auto& write : open_definitions_for) {
186✔
380
            if (dependencies.find(write.first->container()) != dependencies.end()) {
122✔
381
                continue;
39✔
382
            }
383
            for (auto& write_2 : open_definitions_for) {
255✔
384
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis, for_loop)) {
213✔
385
                    dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
41✔
386
                    break;
41✔
387
                }
388
            }
389
        }
390
    } else {
64✔
391
        // Case: Cannot analyze
392
        this->loop_carried_dependencies_.insert({&for_loop, {}});
1✔
393
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
1✔
394

395
        // Over-Approximation:
396
        // Add loop-carried dependencies for all open reads to all open writes
397
        for (auto& read : undefined_for) {
2✔
398
            for (auto& write : open_definitions_for) {
2✔
399
                if (intersects(*write.first, *read, assumptions_analysis)) {
1✔
400
                    write.second.insert(read);
×
401
                    dependencies.insert({read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
×
402
                }
×
403
            }
404
        }
405
        // Add loop-carried dependencies for writes
406
        for (auto& write : open_definitions_for) {
2✔
407
            if (dependencies.find(write.first->container()) == dependencies.end()) {
1✔
408
                dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
1✔
409
            }
1✔
410
        }
411
    }
412

413
    // Add open definitions from for to outside
414
    for (auto& entry : open_definitions_for) {
188✔
415
        open_definitions.insert(entry);
123✔
416
    }
417
}
65✔
418

419
void DataDependencyAnalysis::visit_if_else(
21✔
420
    analysis::Users& users,
421
    analysis::AssumptionsAnalysis& assumptions_analysis,
422
    structured_control_flow::IfElse& if_else,
423
    std::unordered_set<User*>& undefined,
424
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
425
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
426
) {
427
    // Read Conditions
428
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
429
        auto child = if_else.at(i).second;
37✔
430
        for (auto atom : symbolic::atoms(child)) {
61✔
431
            auto current_user = users.get_user(atom->get_name(), &if_else, Use::READ);
24✔
432

433
            bool found = false;
24✔
434
            for (auto& user : open_definitions) {
38✔
435
                if (user.first->container() == atom->get_name()) {
14✔
436
                    user.second.insert(current_user);
7✔
437
                    found = true;
7✔
438
                }
7✔
439
            }
440
            if (!found) {
24✔
441
                undefined.insert(current_user);
17✔
442
            }
17✔
443
        }
24✔
444
    }
37✔
445

446
    std::vector<std::unordered_set<User*>> undefined_branches(if_else.size());
21✔
447
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> open_definitions_branches(if_else.size());
21✔
448
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> closed_definitionss_branches(if_else.size());
21✔
449
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
450
        auto& child = if_else.at(i).first;
37✔
451
        visit_sequence(
37✔
452
            users,
37✔
453
            assumptions_analysis,
37✔
454
            child,
37✔
455
            undefined_branches.at(i),
37✔
456
            open_definitions_branches.at(i),
37✔
457
            closed_definitionss_branches.at(i)
37✔
458
        );
459
    }
37✔
460

461
    // merge partial open reads
462
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
463
        for (auto& entry : undefined_branches.at(i)) {
41✔
464
            bool found = false;
4✔
465
            for (auto& entry2 : open_definitions) {
8✔
466
                if (entry2.first->container() == entry->container()) {
4✔
467
                    entry2.second.insert(entry);
4✔
468
                    found = true;
4✔
469
                }
4✔
470
            }
471
            if (!found) {
4✔
472
                undefined.insert(entry);
×
473
            }
×
474
        }
475
    }
37✔
476

477
    // merge closed writes
478
    for (auto& closing : closed_definitionss_branches) {
58✔
479
        for (auto& entry : closing) {
37✔
480
            closed_definitions.insert(entry);
×
481
        }
482
    }
483

484
    // Close open reads_after_writes for complete branches
485
    if (if_else.is_complete()) {
21✔
486
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
16✔
487
        std::unordered_set<std::string> candidates;
16✔
488
        std::unordered_set<std::string> candidates_tmp;
16✔
489

490
        /* Complete close open reads_after_writes
491
        1. get candidates from first iteration
492
        2. iterate over all branches and prune candidates
493
        3. find prior writes for remaining candidates
494
        4. close open reads_after_writes for all candidates
495
        */
496
        for (auto& entry : open_definitions_branches.at(0)) {
31✔
497
            candidates.insert(entry.first->container());
15✔
498
        }
499
        for (auto& entry : closed_definitionss_branches.at(0)) {
16✔
500
            candidates.insert(entry.first->container());
×
501
        }
502

503
        for (size_t i = 1; i < if_else.size(); i++) {
32✔
504
            for (auto& entry : open_definitions_branches.at(i)) {
30✔
505
                if (candidates.find(entry.first->container()) != candidates.end()) {
14✔
506
                    candidates_tmp.insert(entry.first->container());
14✔
507
                }
14✔
508
            }
509
            candidates.swap(candidates_tmp);
16✔
510
            candidates_tmp.clear();
16✔
511
        }
16✔
512

513
        for (auto& entry : open_definitions) {
24✔
514
            if (candidates.find(entry.first->container()) != candidates.end()) {
8✔
515
                to_close.insert(entry);
4✔
516
            }
4✔
517
        }
518

519
        for (auto& entry : to_close) {
20✔
520
            open_definitions.erase(entry.first);
4✔
521
            closed_definitions.insert(entry);
4✔
522
        }
523
    } else {
16✔
524
        // Over-Approximation: Add all branch definitions to outer definitions or undefined
525
        // Here we add writes to read sets as "anti-reads"
526
        for (auto& branch : open_definitions_branches) {
10✔
527
            for (auto& def : branch) {
8✔
528
                bool found = false;
3✔
529
                for (auto& entry : open_definitions) {
5✔
530
                    if (intersects(*entry.first, *def.first, assumptions_analysis)) {
2✔
531
                        entry.second.insert(def.first);
2✔
532
                        found = true;
2✔
533
                    }
2✔
534
                }
535
                if (!found) {
3✔
536
                    undefined.insert(def.first);
1✔
537
                }
1✔
538
            }
539
        }
540
    }
541

542
    // merge open reads_after_writes
543
    for (auto& branch : open_definitions_branches) {
58✔
544
        for (auto& entry : branch) {
69✔
545
            open_definitions.insert(entry);
32✔
546
        }
547
    }
548
}
21✔
549

550
void DataDependencyAnalysis::visit_while(
7✔
551
    analysis::Users& users,
552
    analysis::AssumptionsAnalysis& assumptions_analysis,
553
    structured_control_flow::While& while_loop,
554
    std::unordered_set<User*>& undefined,
555
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
556
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
557
) {
558
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_while;
7✔
559
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_while;
7✔
560
    std::unordered_set<User*> undefined_while;
7✔
561

562
    visit_sequence(
7✔
563
        users, assumptions_analysis, while_loop.root(), undefined_while, open_definitions_while, closed_definitions_while
7✔
564
    );
565

566
    // Scope-local closed definitions
567
    for (auto& entry : closed_definitions_while) {
9✔
568
        closed_definitions.insert(entry);
2✔
569
    }
570

571
    for (auto open_read : undefined_while) {
9✔
572
        // Over-Approximation: Add loop-carried dependencies for all open reads
573
        for (auto& entry : open_definitions_while) {
5✔
574
            if (entry.first->container() == open_read->container()) {
3✔
575
                entry.second.insert(open_read);
2✔
576
            }
2✔
577
        }
578

579
        // Connect to outside
580
        bool found = false;
2✔
581
        for (auto& entry : open_definitions) {
3✔
582
            if (entry.first->container() == open_read->container()) {
1✔
583
                entry.second.insert(open_read);
1✔
584
                found = true;
1✔
585
            }
1✔
586
        }
587
        if (!found) {
2✔
588
            undefined.insert(open_read);
1✔
589
        }
1✔
590
    }
591

592
    // Add open definitions from while to outside
593
    for (auto& entry : open_definitions_while) {
19✔
594
        open_definitions.insert(entry);
12✔
595
    }
596
}
7✔
597

598
void DataDependencyAnalysis::visit_return(
×
599
    analysis::Users& users,
600
    analysis::AssumptionsAnalysis& assumptions_analysis,
601
    structured_control_flow::Return& return_statement,
602
    std::unordered_set<User*>& undefined,
603
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
604
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
605
) {
606
    // close all open reads_after_writes
607
    for (auto& entry : open_definitions) {
×
608
        closed_definitions.insert(entry);
×
609
    }
610
    open_definitions.clear();
×
611
}
×
612

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

637
        // handle transitions read
638
        for (auto& entry : child.second.assignments()) {
321✔
639
            for (auto& atom : symbolic::atoms(entry.second)) {
93✔
640
                if (symbolic::is_pointer(atom)) {
24✔
641
                    continue;
×
642
                }
643
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
24✔
644

645
                bool found = false;
24✔
646
                for (auto& user : open_definitions) {
47✔
647
                    if (user.first->container() == atom->get_name()) {
23✔
648
                        user.second.insert(current_user);
22✔
649
                        found = true;
22✔
650
                    }
22✔
651
                }
652
                if (!found) {
24✔
653
                    undefined.insert(current_user);
10✔
654
                }
10✔
655
            }
656
        }
657

658
        // handle transitions write
659
        for (auto& entry : child.second.assignments()) {
321✔
660
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
69✔
661

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

677
bool DataDependencyAnalysis::loop_depends(
1,880✔
678
    User& previous,
679
    User& current,
680
    analysis::AssumptionsAnalysis& assumptions_analysis,
681
    structured_control_flow::StructuredLoop& loop
682
) {
683
    if (previous.container() != current.container()) {
1,880✔
684
        return false;
1,726✔
685
    }
686
    // Shortcut for scalars
687
    auto& type = this->sdfg_.type(previous.container());
154✔
688
    if (dynamic_cast<const types::Scalar*>(&type)) {
154✔
689
        return true;
40✔
690
    }
691

692
    auto& previous_subsets = previous.subsets();
114✔
693
    auto& current_subsets = current.subsets();
114✔
694

695
    auto previous_scope = Users::scope(&previous);
114✔
696
    auto previous_assumptions = assumptions_analysis.get(loop.root(), *previous_scope, true);
114✔
697
    auto current_scope = Users::scope(&current);
114✔
698
    auto current_assumptions = assumptions_analysis.get(loop.root(), *current_scope, true);
114✔
699

700
    // Check if previous subset is subset of any current subset
701
    for (auto& previous_subset : previous_subsets) {
187✔
702
        for (auto& current_subset : current_subsets) {
188✔
703
            if (symbolic::maps::intersects(
115✔
704
                    previous_subset, current_subset, loop.indvar(), previous_assumptions, current_assumptions
115✔
705
                )) {
706
                return true;
41✔
707
            }
708
        }
709
    }
710

711
    return false;
73✔
712
}
1,880✔
713

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

724
    auto& previous_subsets = previous.subsets();
18✔
725
    auto& current_subsets = current.subsets();
18✔
726
    auto previous_scope = Users::scope(&previous);
18✔
727
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
18✔
728
    auto current_scope = Users::scope(&current);
18✔
729
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
18✔
730

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

745
    return true;
11✔
746
}
353✔
747

748
bool DataDependencyAnalysis::
749
    supersedes_restrictive(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
410✔
750
    if (previous.container() != current.container()) {
410✔
NEW
751
        return false;
×
752
    }
753
    // Shortcut for scalars
754
    auto& type = this->sdfg_.type(previous.container());
410✔
755
    if (dynamic_cast<const types::Scalar*>(&type)) {
410✔
756
        return true;
409✔
757
    }
758

759
    auto& previous_subsets = previous.subsets();
1✔
760
    auto& current_subsets = current.subsets();
1✔
761
    auto previous_scope = Users::scope(&previous);
1✔
762
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
1✔
763
    auto current_scope = Users::scope(&current);
1✔
764
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
1✔
765

766
    // Check if previous subset is subset of any current subset
767
    for (auto& previous_subset : previous_subsets) {
1✔
768
        bool found = false;
1✔
769
        for (auto& current_subset : current_subsets) {
2✔
770
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, previous_assumptions)) {
1✔
NEW
771
                found = true;
×
NEW
772
                break;
×
773
            }
774
        }
775
        if (!found) {
1✔
776
            return false;
1✔
777
        }
778
    }
779

NEW
780
    return true;
×
781
}
410✔
782

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

793
    auto& previous_subsets = previous.subsets();
8✔
794
    auto& current_subsets = current.subsets();
8✔
795

796
    auto previous_scope = Users::scope(&previous);
8✔
797
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
8✔
798
    auto current_scope = Users::scope(&current);
8✔
799
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
8✔
800

801
    // Check if any current subset intersects with any previous subset
802
    bool found = false;
8✔
803
    for (auto& current_subset : current_subsets) {
13✔
804
        for (auto& previous_subset : previous_subsets) {
13✔
805
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
8✔
806
                found = true;
3✔
807
                break;
3✔
808
            }
809
        }
810
        if (found) {
8✔
811
            break;
3✔
812
        }
813
    }
814

815
    return found;
8✔
816
}
1,143✔
817

818
/****** Public API ******/
819

820
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
821
    assert(write.use() == Use::WRITE);
×
822
    if (results_.find(write.container()) == results_.end()) {
×
823
        return {};
×
824
    }
825
    auto& raws = results_.at(write.container());
×
826
    assert(raws.find(&write) != raws.end());
×
827

828
    auto& reads_for_write = raws.at(&write);
×
829

830
    std::unordered_set<User*> reads;
×
831
    for (auto& entry : reads_for_write) {
×
832
        reads.insert(entry);
×
833
    }
834

835
    return reads;
×
836
};
×
837

838
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(const std::string& container) {
33✔
839
    if (results_.find(container) == results_.end()) {
33✔
840
        return {};
×
841
    }
842
    return results_.at(container);
33✔
843
};
33✔
844

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

848
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
849
    for (auto& entry : reads) {
43✔
850
        for (auto& read : entry.second) {
51✔
851
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27✔
852
                read_to_writes_map[read] = {};
20✔
853
            }
20✔
854
            read_to_writes_map[read].insert(entry.first);
27✔
855
        }
856
    }
857
    return read_to_writes_map;
19✔
858
};
19✔
859

860
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
861
    assert(read.use() == Use::READ);
×
862
    auto definitions = this->definitions(read.container());
×
863

864
    std::unordered_set<User*> writes;
×
865
    for (auto& entry : definitions) {
×
866
        for (auto& r : entry.second) {
×
867
            if (&read == r) {
×
868
                writes.insert(entry.first);
×
869
            }
×
870
        }
871
    }
872
    return writes;
×
873
};
×
874

875
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
876
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
877
};
878

879
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::
880
    dependencies(structured_control_flow::StructuredLoop& loop) const {
50✔
881
    return this->loop_carried_dependencies_.at(&loop);
50✔
882
};
883

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

© 2025 Coveralls, Inc