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

daisytuner / sdfglib / 15796425724

21 Jun 2025 02:00PM UTC coverage: 64.059% (-0.5%) from 64.591%
15796425724

push

github

web-flow
Merge pull request #95 from daisytuner/data-dependencies

Extends Data Dependency Analysis

993 of 1197 new or added lines in 19 files covered. (82.96%)

55 existing lines in 5 files now uncovered.

8058 of 12579 relevant lines covered (64.06%)

150.28 hits per line

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

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

21
      };
81✔
22

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

NEW
27
      };
×
28

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

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

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

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

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

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

55
void DataDependencyAnalysis::visit_block(
141✔
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();
141✔
61

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

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

76
                    if (use == Use::WRITE) {
65✔
77
                        std::unordered_map<User*, std::unordered_set<User*>> to_close;
65✔
78
                        for (auto& user : open_definitions) {
89✔
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) {
72✔
84
                            open_definitions.erase(user.first);
7✔
85
                            closed_definitions.insert(user);
7✔
86
                        }
87
                        open_definitions.insert({current_user, {}});
65✔
88
                    }
65✔
89
                }
65✔
90
                if (dataflow.out_degree(*access_node) > 0) {
103✔
91
                    Use use = Use::READ;
41✔
92
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
82✔
93
                        if (oedge.src_conn() == "refs" || oedge.dst_conn() == "refs") {
41✔
NEW
94
                            use = Use::VIEW;
×
NEW
95
                            break;
×
96
                        }
97
                    }
98

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

101
                    if (use == Use::READ) {
41✔
102
                        bool found = false;
41✔
103
                        for (auto& user : open_definitions) {
50✔
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) {
41✔
110
                            undefined.insert(current_user);
36✔
111
                        }
36✔
112
                    }
41✔
113
                }
41✔
114
            }
103✔
115
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
168✔
116
            if (tasklet->is_conditional()) {
65✔
NEW
117
                auto& condition = tasklet->condition();
×
NEW
118
                for (auto& atom : symbolic::atoms(condition)) {
×
NEW
119
                    auto current_user = users.get_user(atom->get_name(), tasklet, Use::READ);
×
120
                    {
NEW
121
                        bool found = false;
×
NEW
122
                        for (auto& user : open_definitions) {
×
NEW
123
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
NEW
124
                                user.second.insert(current_user);
×
NEW
125
                                found = true;
×
NEW
126
                            }
×
127
                        }
NEW
128
                        if (!found) {
×
NEW
129
                            undefined.insert(current_user);
×
NEW
130
                        }
×
131
                    }
132
                }
NEW
133
            }
×
134
        }
65✔
135

136
        for (auto& oedge : dataflow.out_edges(*node)) {
274✔
137
            std::unordered_set<std::string> used;
106✔
138
            for (auto& dim : oedge.subset()) {
200✔
139
                for (auto atom : symbolic::atoms(dim)) {
159✔
140
                    used.insert(atom->get_name());
65✔
141
                }
65✔
142
            }
143
            for (auto& atom : used) {
171✔
144
                auto current_user = users.get_user(atom, &oedge, Use::READ);
65✔
145

146
                {
147
                    bool found = false;
65✔
148
                    for (auto& user : open_definitions) {
75✔
149
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
10✔
150
                            user.second.insert(current_user);
4✔
151
                            found = true;
4✔
152
                        }
4✔
153
                    }
154
                    if (!found) {
65✔
155
                        undefined.insert(current_user);
61✔
156
                    }
61✔
157
                }
158
            }
159
        }
106✔
160
    }
161
}
141✔
162

163
void DataDependencyAnalysis::visit_for(
43✔
164
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
165
    structured_control_flow::For& for_loop, std::unordered_set<User*>& undefined,
166
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
167
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
168
    // Init - Read
169
    for (auto atom : symbolic::atoms(for_loop.init())) {
46✔
170
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, true);
3✔
171

172
        bool found = false;
3✔
173
        for (auto& user : open_definitions) {
4✔
174
            if (user.first->container() == atom->get_name()) {
1✔
175
                user.second.insert(current_user);
1✔
176
                found = true;
1✔
177
            }
1✔
178
        }
179
        if (!found) {
3✔
180
            undefined.insert(current_user);
2✔
181
        }
2✔
182
    }
3✔
183

184
    // Init - Write
185
    {
186
        // Write Induction Variable
187
        auto current_user =
43✔
188
            users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, true);
43✔
189

190
        std::unordered_set<User*> to_close;
43✔
191
        for (auto& user : open_definitions) {
55✔
192
            if (supersedes(*user.first, *current_user, assumptions_analysis)) {
12✔
193
                to_close.insert(user.first);
2✔
194
            }
2✔
195
        }
196
        for (auto& user : to_close) {
45✔
197
            closed_definitions.insert({user, open_definitions.at(user)});
2✔
198
            open_definitions.erase(user);
2✔
199
        }
200
        open_definitions.insert({current_user, {}});
43✔
201

202
        // Improve: If loop is executed at least once, we can close the init's definition
203
        // TODO: Implement this
204
    }
43✔
205

206
    // Update - Write
207
    {
208
        // Exists after loop and inside body
209
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE,
43✔
210
                                           false, false, true);
211
        open_definitions.insert({current_user, {}});
43✔
212

213
        // Improve: If loop is executed at least once, we can close the init's definition
214
        // TODO: Implement this
215
    }
216

217
    // Condition - Read
218
    for (auto atom : symbolic::atoms(for_loop.condition())) {
118✔
219
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, true);
75✔
220

221
        bool found = false;
75✔
222
        for (auto& user : open_definitions) {
236✔
223
            if (user.first->container() == atom->get_name()) {
161✔
224
                user.second.insert(current_user);
87✔
225
                found = true;
87✔
226
            }
87✔
227
        }
228
        if (!found) {
75✔
229
            undefined.insert(current_user);
31✔
230
        }
31✔
231
    }
75✔
232

233
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_for;
43✔
234
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_for;
43✔
235
    std::unordered_set<User*> undefined_for;
43✔
236

237
    // Add assumptions for body
238
    visit_sequence(users, assumptions_analysis, for_loop.root(), undefined_for,
43✔
239
                   open_definitions_for, closed_definitions_for);
240

241
    // Update - Read
242
    for (auto atom : symbolic::atoms(for_loop.update())) {
86✔
243
        auto current_user =
43✔
244
            users.get_user(atom->get_name(), &for_loop, Use::READ, false, false, true);
43✔
245

246
        bool found = false;
43✔
247
        for (auto& user : open_definitions_for) {
99✔
248
            if (user.first->container() == atom->get_name()) {
56✔
249
                user.second.insert(current_user);
1✔
250
                found = true;
1✔
251
            }
1✔
252
        }
253
        if (!found) {
43✔
254
            undefined_for.insert(current_user);
42✔
255
        }
42✔
256
    }
43✔
257

258
    // Merge for with outside
259

260
    // Closed definitions are simply merged
261
    for (auto& entry : closed_definitions_for) {
43✔
NEW
262
        closed_definitions.insert(entry);
×
263
    }
264

265
    // Undefined reads are matched or forwarded
266
    for (auto open_read : undefined_for) {
208✔
267
        bool found = false;
165✔
268
        for (auto& entry : open_definitions) {
511✔
269
            if (intersects(*entry.first, *open_read, assumptions_analysis)) {
346✔
270
                entry.second.insert(open_read);
220✔
271
                found = true;
220✔
272
            }
220✔
273
        }
274
        if (!found) {
165✔
275
            undefined.insert(open_read);
55✔
276
        }
55✔
277
    }
278

279
    // Open definitions may close outside open definitions after loop
280
    std::unordered_set<User*> to_close;
43✔
281
    for (auto& previous : open_definitions) {
139✔
282
        for (auto& user : open_definitions_for) {
215✔
283
            if (supersedes(*previous.first, *user.first, assumptions_analysis)) {
121✔
284
                to_close.insert(previous.first);
2✔
285
                break;
2✔
286
            }
287
        }
288
    }
289
    for (auto& user : to_close) {
45✔
290
        closed_definitions.insert({user, open_definitions.at(user)});
2✔
291
        open_definitions.erase(user);
2✔
292
    }
293

294
    // Add loop-carried dependencies
295

296
    // Criterion 1: Loop is monotonic -> indvar never takes the same value twice
297
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
43✔
298
    if (is_monotonic) {
43✔
299
        // Case: Can analyze
300
        this->loop_carried_dependencies_.insert({&for_loop, {}});
42✔
301
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
42✔
302

303
        // We can focus on written containers
304

305
        // Case 1: Read-Write between iterations
306
        for (auto& read : undefined_for) {
206✔
307
            for (auto& write : open_definitions_for) {
416✔
308
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop.indvar())) {
252✔
309
                    if (dependencies.find(read->container()) == dependencies.end()) {
6✔
310
                        dependencies.insert(
12✔
311
                            {read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
6✔
312
                    }
6✔
313
                    write.second.insert(read);
6✔
314
                }
6✔
315
            }
316
        }
317

318
        // Case 2: Write-Write between iterations
319
        for (auto& write : open_definitions_for) {
97✔
320
            if (dependencies.find(write.first->container()) != dependencies.end()) {
55✔
321
                continue;
13✔
322
            }
323
            for (auto& write_2 : open_definitions_for) {
93✔
324
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis,
66✔
325
                                 for_loop.indvar())) {
66✔
326
                    dependencies.insert(
30✔
327
                        {write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
15✔
328
                    break;
15✔
329
                }
330
            }
331
        }
332
    } else {
42✔
333
        // Case: Cannot analyze
334
        this->loop_carried_dependencies_.insert({&for_loop, {}});
1✔
335
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
1✔
336

337
        // Over-Approximation:
338
        // Add loop-carried dependencies for all open reads to all open writes
339
        for (auto& read : undefined_for) {
2✔
340
            for (auto& write : open_definitions_for) {
2✔
341
                if (intersects(*write.first, *read, assumptions_analysis)) {
1✔
NEW
342
                    write.second.insert(read);
×
NEW
343
                    dependencies.insert({read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
×
NEW
344
                }
×
345
            }
346
        }
347
        // Add loop-carried dependencies for writes
348
        for (auto& write : open_definitions_for) {
2✔
349
            if (dependencies.find(write.first->container()) == dependencies.end()) {
1✔
350
                dependencies.insert(
2✔
351
                    {write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
1✔
352
            }
1✔
353
        }
354
    }
355

356
    // Add open definitions from for to outside
357
    for (auto& entry : open_definitions_for) {
99✔
358
        open_definitions.insert(entry);
56✔
359
    }
360
}
43✔
361

362
void DataDependencyAnalysis::visit_if_else(
21✔
363
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
364
    structured_control_flow::IfElse& if_else, std::unordered_set<User*>& undefined,
365
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
366
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
367
    // Read Conditions
368
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
369
        auto child = if_else.at(i).second;
37✔
370
        for (auto atom : symbolic::atoms(child)) {
61✔
371
            auto current_user = users.get_user(atom->get_name(), &if_else, Use::READ);
24✔
372

373
            bool found = false;
24✔
374
            for (auto& user : open_definitions) {
38✔
375
                if (user.first->container() == atom->get_name()) {
14✔
376
                    user.second.insert(current_user);
7✔
377
                    found = true;
7✔
378
                }
7✔
379
            }
380
            if (!found) {
24✔
381
                undefined.insert(current_user);
17✔
382
            }
17✔
383
        }
24✔
384
    }
37✔
385

386
    std::vector<std::unordered_set<User*>> undefined_branches(if_else.size());
21✔
387
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> open_definitions_branches(
21✔
388
        if_else.size());
21✔
389
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> closed_definitionss_branches(
21✔
390
        if_else.size());
21✔
391
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
392
        auto& child = if_else.at(i).first;
37✔
393
        visit_sequence(users, assumptions_analysis, child, undefined_branches.at(i),
74✔
394
                       open_definitions_branches.at(i), closed_definitionss_branches.at(i));
37✔
395
    }
37✔
396

397
    // merge partial open reads
398
    for (size_t i = 0; i < if_else.size(); i++) {
58✔
399
        for (auto& entry : undefined_branches.at(i)) {
41✔
400
            bool found = false;
4✔
401
            for (auto& entry2 : open_definitions) {
8✔
402
                if (entry2.first->container() == entry->container()) {
4✔
403
                    entry2.second.insert(entry);
4✔
404
                    found = true;
4✔
405
                }
4✔
406
            }
407
            if (!found) {
4✔
NEW
408
                undefined.insert(entry);
×
NEW
409
            }
×
410
        }
411
    }
37✔
412

413
    // merge closed writes
414
    for (auto& closing : closed_definitionss_branches) {
58✔
415
        for (auto& entry : closing) {
37✔
NEW
416
            closed_definitions.insert(entry);
×
417
        }
418
    }
419

420
    // Close open reads_after_writes for complete branches
421
    if (if_else.is_complete()) {
21✔
422
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
16✔
423
        std::unordered_set<std::string> candidates;
16✔
424
        std::unordered_set<std::string> candidates_tmp;
16✔
425

426
        /* Complete close open reads_after_writes
427
        1. get candidates from first iteration
428
        2. iterate over all branches and prune candidates
429
        3. find prior writes for remaining candidates
430
        4. close open reads_after_writes for all candidates
431
        */
432
        for (auto& entry : open_definitions_branches.at(0)) {
31✔
433
            candidates.insert(entry.first->container());
15✔
434
        }
435
        for (auto& entry : closed_definitionss_branches.at(0)) {
16✔
NEW
436
            candidates.insert(entry.first->container());
×
437
        }
438

439
        for (size_t i = 1; i < if_else.size(); i++) {
32✔
440
            for (auto& entry : open_definitions_branches.at(i)) {
30✔
441
                if (candidates.find(entry.first->container()) != candidates.end()) {
14✔
442
                    candidates_tmp.insert(entry.first->container());
14✔
443
                }
14✔
444
            }
445
            candidates.swap(candidates_tmp);
16✔
446
            candidates_tmp.clear();
16✔
447
        }
16✔
448

449
        for (auto& entry : open_definitions) {
24✔
450
            if (candidates.find(entry.first->container()) != candidates.end()) {
8✔
451
                to_close.insert(entry);
4✔
452
            }
4✔
453
        }
454

455
        for (auto& entry : to_close) {
20✔
456
            open_definitions.erase(entry.first);
4✔
457
            closed_definitions.insert(entry);
4✔
458
        }
459
    } else {
16✔
460
        // Over-Approximation: Add all branch definitions to outer definitions or undefined
461
        // Here we add writes to read sets as "anti-reads"
462
        for (auto& branch : open_definitions_branches) {
10✔
463
            for (auto& def : branch) {
8✔
464
                bool found = false;
3✔
465
                for (auto& entry : open_definitions) {
5✔
466
                    if (intersects(*entry.first, *def.first, assumptions_analysis)) {
2✔
467
                        entry.second.insert(def.first);
2✔
468
                        found = true;
2✔
469
                    }
2✔
470
                }
471
                if (!found) {
3✔
472
                    undefined.insert(def.first);
1✔
473
                }
1✔
474
            }
475
        }
476
    }
477

478
    // merge open reads_after_writes
479
    for (auto& branch : open_definitions_branches) {
58✔
480
        for (auto& entry : branch) {
69✔
481
            open_definitions.insert(entry);
32✔
482
        }
483
    }
484
}
21✔
485

486
void DataDependencyAnalysis::visit_while(
7✔
487
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
488
    structured_control_flow::While& while_loop, std::unordered_set<User*>& undefined,
489
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
490
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
491
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_while;
7✔
492
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_while;
7✔
493
    std::unordered_set<User*> undefined_while;
7✔
494

495
    visit_sequence(users, assumptions_analysis, while_loop.root(), undefined_while,
7✔
496
                   open_definitions_while, closed_definitions_while);
497

498
    // Scope-local closed definitions
499
    for (auto& entry : closed_definitions_while) {
9✔
500
        closed_definitions.insert(entry);
2✔
501
    }
502

503
    for (auto open_read : undefined_while) {
9✔
504
        // Over-Approximation: Add loop-carried dependencies for all open reads
505
        for (auto& entry : open_definitions_while) {
5✔
506
            if (entry.first->container() == open_read->container()) {
3✔
507
                entry.second.insert(open_read);
2✔
508
            }
2✔
509
        }
510

511
        // Connect to outside
512
        bool found = false;
2✔
513
        for (auto& entry : open_definitions) {
3✔
514
            if (entry.first->container() == open_read->container()) {
1✔
515
                entry.second.insert(open_read);
1✔
516
                found = true;
1✔
517
            }
1✔
518
        }
519
        if (!found) {
2✔
520
            undefined.insert(open_read);
1✔
521
        }
1✔
522
    }
523

524
    // Add open definitions from while to outside
525
    for (auto& entry : open_definitions_while) {
19✔
526
        open_definitions.insert(entry);
12✔
527
    }
528
}
7✔
529

NEW
530
void DataDependencyAnalysis::visit_return(
×
531
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
532
    structured_control_flow::Return& return_statement, std::unordered_set<User*>& undefined,
533
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
534
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
535
    // close all open reads_after_writes
NEW
536
    for (auto& entry : open_definitions) {
×
NEW
537
        closed_definitions.insert(entry);
×
538
    }
NEW
539
    open_definitions.clear();
×
NEW
540
}
×
541

NEW
542
void DataDependencyAnalysis::visit_map(
×
543
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
544
    structured_control_flow::Map& map, std::unordered_set<User*>& undefined,
545
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
546
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
547
    // write Init
NEW
548
    auto current_user = users.get_user(map.indvar()->get_name(), &map, Use::WRITE);
×
549

NEW
550
    open_definitions.insert({current_user, {}});
×
551

NEW
552
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_map;
×
NEW
553
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitionss_map;
×
NEW
554
    std::unordered_set<User*> undefined_map;
×
555

556
    // Add assumptions for body
NEW
557
    visit_sequence(users, assumptions_analysis, map.root(), undefined_map, open_definitions_map,
×
558
                   closed_definitionss_map);
559

NEW
560
    for (auto& entry : closed_definitionss_map) {
×
NEW
561
        closed_definitions.insert(entry);
×
562
    }
563

564
    // Handle open reads of for
NEW
565
    for (auto open_read : undefined_map) {
×
566
        // Add recursive
NEW
567
        for (auto& user : open_definitions_map) {
×
NEW
568
            if (user.first->container() == open_read->container()) {
×
NEW
569
                user.second.insert(open_read);
×
NEW
570
            }
×
571
        }
572

NEW
573
        bool found = false;
×
NEW
574
        for (auto& entry : open_definitions) {
×
NEW
575
            if (entry.first->container() == open_read->container()) {
×
NEW
576
                entry.second.insert(open_read);
×
NEW
577
                found = true;
×
NEW
578
            }
×
579
        }
NEW
580
        if (!found) {
×
NEW
581
            undefined.insert(open_read);
×
NEW
582
        }
×
583
    }
584

585
    // Merge open reads_after_writes
NEW
586
    for (auto& entry : open_definitions_map) {
×
NEW
587
        open_definitions.insert(entry);
×
588
    }
NEW
589
}
×
590

591
void DataDependencyAnalysis::visit_sequence(
161✔
592
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
593
    structured_control_flow::Sequence& sequence, std::unordered_set<User*>& undefined,
594
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
595
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
596
    for (size_t i = 0; i < sequence.size(); i++) {
373✔
597
        auto child = sequence.at(i);
212✔
598
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
212✔
599
            visit_block(users, assumptions_analysis, *block, undefined, open_definitions,
266✔
600
                        closed_definitions);
133✔
601
        } else if (auto for_loop = dynamic_cast<structured_control_flow::For*>(&child.first)) {
212✔
602
            visit_for(users, assumptions_analysis, *for_loop, undefined, open_definitions,
86✔
603
                      closed_definitions);
43✔
604
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
79✔
605
            visit_if_else(users, assumptions_analysis, *if_else, undefined, open_definitions,
42✔
606
                          closed_definitions);
21✔
607
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
36✔
608
            visit_while(users, assumptions_analysis, *while_loop, undefined, open_definitions,
14✔
609
                        closed_definitions);
7✔
610
        } else if (auto return_statement =
15✔
611
                       dynamic_cast<structured_control_flow::Return*>(&child.first)) {
8✔
NEW
612
            visit_return(users, assumptions_analysis, *return_statement, undefined,
×
NEW
613
                         open_definitions, closed_definitions);
×
614
        } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&child.first)) {
8✔
NEW
615
            visit_sequence(users, assumptions_analysis, *sequence, undefined, open_definitions,
×
NEW
616
                           closed_definitions);
×
617
        } else if (auto map = dynamic_cast<structured_control_flow::Map*>(&child.first)) {
8✔
NEW
618
            visit_map(users, assumptions_analysis, *map, undefined, open_definitions,
×
NEW
619
                      closed_definitions);
×
NEW
620
        }
×
621

622
        // handle transitions read
623
        for (auto& entry : child.second.assignments()) {
281✔
624
            for (auto& atom : symbolic::atoms(entry.second)) {
93✔
625
                if (symbolic::is_pointer(atom)) {
24✔
NEW
626
                    continue;
×
627
                }
628
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
24✔
629

630
                bool found = false;
24✔
631
                for (auto& user : open_definitions) {
47✔
632
                    if (user.first->container() == atom->get_name()) {
23✔
633
                        user.second.insert(current_user);
22✔
634
                        found = true;
22✔
635
                    }
22✔
636
                }
637
                if (!found) {
24✔
638
                    undefined.insert(current_user);
10✔
639
                }
10✔
640
            }
641
        }
642

643
        // handle transitions write
644
        for (auto& entry : child.second.assignments()) {
281✔
645
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
69✔
646

647
            std::unordered_set<User*> to_close;
69✔
648
            for (auto& user : open_definitions) {
94✔
649
                if (user.first->container() == entry.first->get_name()) {
25✔
650
                    to_close.insert(user.first);
1✔
651
                }
1✔
652
            }
653
            for (auto& user : to_close) {
70✔
654
                closed_definitions.insert({user, open_definitions.at(user)});
1✔
655
                open_definitions.erase(user);
1✔
656
            }
657
            open_definitions.insert({current_user, {}});
69✔
658
        }
69✔
659
    }
212✔
660
}
161✔
661

662
bool DataDependencyAnalysis::loop_depends(User& previous, User& current,
318✔
663
                                          analysis::AssumptionsAnalysis& assumptions_analysis,
664
                                          symbolic::Symbol& indvar) {
665
    if (previous.container() != current.container()) {
318✔
666
        return false;
260✔
667
    }
668
    // Shortcut for scalars
669
    auto& type = this->sdfg_.type(previous.container());
58✔
670
    if (dynamic_cast<const types::Scalar*>(&type)) {
58✔
671
        return true;
14✔
672
    }
673

674
    auto& previous_subsets = previous.subsets();
44✔
675
    auto& current_subsets = current.subsets();
44✔
676

677
    auto previous_scope = Users::scope(&previous);
44✔
678
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
44✔
679
    auto current_scope = Users::scope(&current);
44✔
680
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
44✔
681

682
    // Check if previous subset is subset of any current subset
683
    for (auto& previous_subset : previous_subsets) {
81✔
684
        for (auto& current_subset : current_subsets) {
81✔
685
            if (symbolic::intersects(previous_subset, current_subset, indvar, previous_assumptions,
44✔
686
                                     current_assumptions)) {
687
                return true;
7✔
688
            }
689
        }
690
    }
691

692
    return false;
37✔
693
}
318✔
694

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

706
    auto& previous_subsets = previous.subsets();
13✔
707
    auto& current_subsets = current.subsets();
13✔
708
    auto previous_scope = Users::scope(&previous);
13✔
709
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
13✔
710
    auto current_scope = Users::scope(&current);
13✔
711
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
13✔
712

713
    // Check if previous subset is subset of any current subset
714
    for (auto& previous_subset : previous_subsets) {
21✔
715
        bool found = false;
13✔
716
        for (auto& current_subset : current_subsets) {
18✔
717
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions,
13✔
718
                                    current_assumptions)) {
719
                found = true;
8✔
720
                break;
8✔
721
            }
722
        }
723
        if (!found) {
13✔
724
            return false;
5✔
725
        }
726
    }
727

728
    return true;
8✔
729
}
157✔
730

731
bool DataDependencyAnalysis::intersects(User& previous, User& current,
368✔
732
                                        analysis::AssumptionsAnalysis& assumptions_analysis) {
733
    if (previous.container() != current.container()) {
368✔
734
        return false;
135✔
735
    }
736
    // Shortcut for scalars
737
    auto& type = this->sdfg_.type(previous.container());
233✔
738
    if (dynamic_cast<const types::Scalar*>(&type)) {
233✔
739
        return true;
231✔
740
    }
741

742
    auto& previous_subsets = previous.subsets();
2✔
743
    auto& current_subsets = current.subsets();
2✔
744

745
    auto previous_scope = Users::scope(&previous);
2✔
746
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
2✔
747
    auto current_scope = Users::scope(&current);
2✔
748
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
2✔
749

750
    // Check if any current subset intersects with any previous subset
751
    bool found = false;
2✔
752
    for (auto& current_subset : current_subsets) {
3✔
753
        for (auto& previous_subset : previous_subsets) {
3✔
754
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions,
2✔
755
                                       previous_assumptions)) {
756
                found = true;
1✔
757
                break;
1✔
758
            }
759
        }
760
        if (found) {
2✔
761
            break;
1✔
762
        }
763
    }
764

765
    return found;
2✔
766
}
368✔
767

768
/****** Public API ******/
769

NEW
770
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
NEW
771
    assert(write.use() == Use::WRITE);
×
NEW
772
    if (results_.find(write.container()) == results_.end()) {
×
NEW
773
        return {};
×
774
    }
NEW
775
    auto& raws = results_.at(write.container());
×
NEW
776
    assert(raws.find(&write) != raws.end());
×
777

NEW
778
    auto& reads_for_write = raws.at(&write);
×
779

NEW
780
    std::unordered_set<User*> reads;
×
NEW
781
    for (auto& entry : reads_for_write) {
×
NEW
782
        reads.insert(entry);
×
783
    }
784

NEW
785
    return reads;
×
NEW
786
};
×
787

788
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(
33✔
789
    const std::string& container) {
790
    if (results_.find(container) == results_.end()) {
33✔
NEW
791
        return {};
×
792
    }
793
    return results_.at(container);
33✔
794
};
33✔
795

796
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::defined_by(
19✔
797
    const std::string& container) {
798
    auto reads = this->definitions(container);
19✔
799

800
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
801
    for (auto& entry : reads) {
43✔
802
        for (auto& read : entry.second) {
51✔
803
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27✔
804
                read_to_writes_map[read] = {};
20✔
805
            }
20✔
806
            read_to_writes_map[read].insert(entry.first);
27✔
807
        }
808
    }
809
    return read_to_writes_map;
19✔
810
};
19✔
811

NEW
812
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
NEW
813
    assert(read.use() == Use::READ);
×
NEW
814
    auto definitions = this->definitions(read.container());
×
815

NEW
816
    std::unordered_set<User*> writes;
×
NEW
817
    for (auto& entry : definitions) {
×
NEW
818
        for (auto& r : entry.second) {
×
NEW
819
            if (&read == r) {
×
NEW
820
                writes.insert(entry.first);
×
NEW
821
            }
×
822
        }
823
    }
NEW
824
    return writes;
×
NEW
825
};
×
826

NEW
827
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
NEW
828
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
829
};
830

831
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::dependencies(
30✔
832
    structured_control_flow::StructuredLoop& loop) const {
833
    return this->loop_carried_dependencies_.at(&loop);
30✔
834
};
835

836
}  // namespace analysis
837
}  // 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