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

daisytuner / sdfglib / 16828918031

08 Aug 2025 11:08AM UTC coverage: 64.68% (-0.004%) from 64.684%
16828918031

Pull #181

github

web-flow
Merge 283b054a5 into 104fa63dd
Pull Request #181: Data dependency analysis: Supersede open definitions

15 of 23 new or added lines in 2 files covered. (65.22%)

45 existing lines in 1 file now uncovered.

8922 of 13794 relevant lines covered (64.68%)

124.16 hits per line

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

84.31
/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) {
281✔
40
        closed_definitions.insert(entry);
212✔
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) {
120✔
82
                            if (supersedes(*user.first, *current_user, assumptions_analysis)) {
37✔
83
                                to_close.insert(user);
9✔
84
                            }
9✔
85
                        }
86
                        for (auto& user : to_close) {
92✔
87
                            open_definitions.erase(user.first);
9✔
88
                            closed_definitions.insert(user);
9✔
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 superseded = false;
67✔
110
                        for (auto& user : open_definitions) {
97✔
111
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
30✔
112
                                user.second.insert(current_user);
10✔
113
                            }
10✔
114
                            if (supersedes(*current_user, *user.first, assumptions_analysis)) {
30✔
115
                                superseded = true;
8✔
116
                            }
8✔
117
                        }
118
                        if (!superseded) {
67✔
119
                            undefined.insert(current_user);
60✔
120
                        }
60✔
121
                    }
67✔
122
                }
67✔
123
            }
147✔
124
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
230✔
125
            if (tasklet->is_conditional()) {
83✔
UNCOV
126
                auto& condition = tasklet->condition();
×
UNCOV
127
                for (auto& atom : symbolic::atoms(condition)) {
×
UNCOV
128
                    auto current_user = users.get_user(atom->get_name(), tasklet, Use::READ);
×
129
                    {
130
                        // Find all definitions that we read from
131
                        bool superseded = false;
×
UNCOV
132
                        for (auto& user : open_definitions) {
×
NEW
133
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
134
                                user.second.insert(current_user);
×
NEW
135
                            }
×
136
                            if (supersedes(*current_user, *user.first, assumptions_analysis)) {
×
137
                                superseded = true;
×
138
                            }
×
139
                        }
NEW
140
                        if (!superseded) {
×
NEW
141
                            undefined.insert(current_user);
×
NEW
142
                        }
×
143
                    }
144
                }
UNCOV
145
            }
×
146
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
83✔
NEW
147
            for (auto& symbol : library_node->symbols()) {
×
148
                auto current_user = users.get_user(symbol->get_name(), library_node, Use::READ);
×
149
                {
150
                    // Find all definitions that we read from
UNCOV
151
                    bool superseded = false;
×
152
                    for (auto& user : open_definitions) {
×
UNCOV
153
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
154
                            user.second.insert(current_user);
×
155
                        }
×
UNCOV
156
                        if (supersedes(*current_user, *user.first, assumptions_analysis)) {
×
NEW
157
                            superseded = true;
×
158
                        }
×
159
                    }
160
                    if (!superseded) {
×
161
                        undefined.insert(current_user);
×
162
                    }
×
163
                }
164
            }
NEW
165
        }
×
166

167
        for (auto& oedge : dataflow.out_edges(*node)) {
381✔
168
            std::unordered_set<std::string> used;
151✔
169
            for (auto& dim : oedge.subset()) {
282✔
170
                for (auto atom : symbolic::atoms(dim)) {
268✔
171
                    used.insert(atom->get_name());
137✔
172
                }
137✔
173
            }
174
            for (auto& atom : used) {
288✔
175
                auto current_user = users.get_user(atom, &oedge, Use::READ);
137✔
176

177
                {
178
                    // Find all definitions that we read from
179
                    bool superseded = false;
137✔
180
                    for (auto& user : open_definitions) {
224✔
181
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
87✔
182
                            user.second.insert(current_user);
4✔
183
                        }
4✔
184
                        if (supersedes(*current_user, *user.first, assumptions_analysis)) {
87✔
185
                            superseded = true;
4✔
186
                        }
4✔
187
                    }
188
                    if (!superseded) {
137✔
189
                        undefined.insert(current_user);
133✔
190
                    }
133✔
191
                }
192
            }
193
        }
151✔
194
    }
195
}
159✔
196

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

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

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

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

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

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

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

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

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

268
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_for;
65✔
269
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_for;
65✔
270
    std::unordered_set<User*> undefined_for;
65✔
271

272
    // Add assumptions for body
273
    visit_sequence(
65✔
274
        users, assumptions_analysis, for_loop.root(), undefined_for, open_definitions_for, closed_definitions_for
65✔
275
    );
276

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

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

293
    // Merge for with outside
294

295
    // Closed definitions are simply merged
296
    for (auto& entry : closed_definitions_for) {
70✔
297
        closed_definitions.insert(entry);
5✔
298
    }
299

300
    // Undefined reads are matched or forwarded
301
    for (auto open_read : undefined_for) {
474✔
302
        bool found = false;
409✔
303
        for (auto& entry : open_definitions) {
1,446✔
304
            if (intersects(*entry.first, *open_read, assumptions_analysis)) {
1,037✔
305
                entry.second.insert(open_read);
409✔
306
                found = true;
409✔
307
            }
409✔
308
        }
309
        if (!found) {
409✔
310
            undefined.insert(open_read);
204✔
311
        }
204✔
312
    }
313

314
    // Open definitions may close outside open definitions after loop
315
    std::unordered_set<User*> to_close;
65✔
316
    for (auto& previous : open_definitions) {
212✔
317
        for (auto& user : open_definitions_for) {
426✔
318
            if (supersedes(*previous.first, *user.first, assumptions_analysis)) {
281✔
319
                to_close.insert(previous.first);
2✔
320
                break;
2✔
321
            }
322
        }
323
    }
324
    for (auto& user : to_close) {
67✔
325
        closed_definitions.insert({user, open_definitions.at(user)});
2✔
326
        open_definitions.erase(user);
2✔
327
    }
328

329
    // Add loop-carried dependencies
330

331
    // Criterion 1: Loop is monotonic -> indvar never takes the same value twice
332
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
65✔
333
    if (is_monotonic) {
65✔
334
        // Case: Can analyze
335
        this->loop_carried_dependencies_.insert({&for_loop, {}});
64✔
336
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
64✔
337

338
        // We can focus on written containers
339

340
        // Case 1: Read-Write between iterations
341
        for (auto& read : undefined_for) {
472✔
342
            for (auto& write : open_definitions_for) {
2,102✔
343
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop)) {
1,694✔
344
                    if (dependencies.find(read->container()) == dependencies.end()) {
43✔
345
                        dependencies.insert({read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
13✔
346
                    }
13✔
347
                    write.second.insert(read);
43✔
348
                }
43✔
349
            }
350
        }
351

352
        // Case 2: Write-Write between iterations
353
        for (auto& write : open_definitions_for) {
185✔
354
            if (dependencies.find(write.first->container()) != dependencies.end()) {
121✔
355
                continue;
38✔
356
            }
357
            for (auto& write_2 : open_definitions_for) {
263✔
358
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis, for_loop)) {
221✔
359
                    dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
41✔
360
                    break;
41✔
361
                }
362
            }
363
        }
364
    } else {
64✔
365
        // Case: Cannot analyze
366
        this->loop_carried_dependencies_.insert({&for_loop, {}});
1✔
367
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
1✔
368

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

387
    // Add open definitions from for to outside
388
    for (auto& entry : open_definitions_for) {
187✔
389
        open_definitions.insert(entry);
122✔
390
    }
391
}
65✔
392

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

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

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

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

451
    // merge closed writes
452
    for (auto& closing : closed_definitionss_branches) {
58✔
453
        for (auto& entry : closing) {
37✔
UNCOV
454
            closed_definitions.insert(entry);
×
455
        }
456
    }
457

458
    // Close open reads_after_writes for complete branches
459
    if (if_else.is_complete()) {
21✔
460
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
16✔
461
        std::unordered_set<std::string> candidates;
16✔
462
        std::unordered_set<std::string> candidates_tmp;
16✔
463

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

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

487
        for (auto& entry : open_definitions) {
24✔
488
            if (candidates.find(entry.first->container()) != candidates.end()) {
8✔
489
                to_close.insert(entry);
4✔
490
            }
4✔
491
        }
492

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

516
    // merge open reads_after_writes
517
    for (auto& branch : open_definitions_branches) {
58✔
518
        for (auto& entry : branch) {
69✔
519
            open_definitions.insert(entry);
32✔
520
        }
521
    }
522
}
21✔
523

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

536
    visit_sequence(
7✔
537
        users, assumptions_analysis, while_loop.root(), undefined_while, open_definitions_while, closed_definitions_while
7✔
538
    );
539

540
    // Scope-local closed definitions
541
    for (auto& entry : closed_definitions_while) {
9✔
542
        closed_definitions.insert(entry);
2✔
543
    }
544

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

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

566
    // Add open definitions from while to outside
567
    for (auto& entry : open_definitions_while) {
19✔
568
        open_definitions.insert(entry);
12✔
569
    }
570
}
7✔
571

UNCOV
572
void DataDependencyAnalysis::visit_return(
×
573
    analysis::Users& users,
574
    analysis::AssumptionsAnalysis& assumptions_analysis,
575
    structured_control_flow::Return& return_statement,
576
    std::unordered_set<User*>& undefined,
577
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
578
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
579
) {
580
    // close all open reads_after_writes
UNCOV
581
    for (auto& entry : open_definitions) {
×
UNCOV
582
        closed_definitions.insert(entry);
×
583
    }
UNCOV
584
    open_definitions.clear();
×
585
}
×
586

587
void DataDependencyAnalysis::visit_sequence(
195✔
588
    analysis::Users& users,
589
    analysis::AssumptionsAnalysis& assumptions_analysis,
590
    structured_control_flow::Sequence& sequence,
591
    std::unordered_set<User*>& undefined,
592
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
593
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
594
) {
595
    for (size_t i = 0; i < sequence.size(); i++) {
447✔
596
        auto child = sequence.at(i);
252✔
597
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
252✔
598
            visit_block(users, assumptions_analysis, *block, undefined, open_definitions, closed_definitions);
151✔
599
        } else if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
252✔
600
            visit_for(users, assumptions_analysis, *for_loop, undefined, open_definitions, closed_definitions);
65✔
601
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
101✔
602
            visit_if_else(users, assumptions_analysis, *if_else, undefined, open_definitions, closed_definitions);
21✔
603
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
36✔
604
            visit_while(users, assumptions_analysis, *while_loop, undefined, open_definitions, closed_definitions);
7✔
605
        } else if (auto return_statement = dynamic_cast<structured_control_flow::Return*>(&child.first)) {
15✔
UNCOV
606
            visit_return(users, assumptions_analysis, *return_statement, undefined, open_definitions, closed_definitions);
×
607
        } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&child.first)) {
8✔
UNCOV
608
            visit_sequence(users, assumptions_analysis, *sequence, undefined, open_definitions, closed_definitions);
×
UNCOV
609
        }
×
610

611
        // handle transitions read
612
        for (auto& entry : child.second.assignments()) {
321✔
613
            for (auto& atom : symbolic::atoms(entry.second)) {
93✔
614
                if (symbolic::is_pointer(atom)) {
24✔
UNCOV
615
                    continue;
×
616
                }
617
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
24✔
618

619
                bool found = false;
24✔
620
                for (auto& user : open_definitions) {
47✔
621
                    if (user.first->container() == atom->get_name()) {
23✔
622
                        user.second.insert(current_user);
22✔
623
                        found = true;
22✔
624
                    }
22✔
625
                }
626
                if (!found) {
24✔
627
                    undefined.insert(current_user);
10✔
628
                }
10✔
629
            }
630
        }
631

632
        // handle transitions write
633
        for (auto& entry : child.second.assignments()) {
321✔
634
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
69✔
635

636
            std::unordered_set<User*> to_close;
69✔
637
            for (auto& user : open_definitions) {
94✔
638
                if (user.first->container() == entry.first->get_name()) {
25✔
639
                    to_close.insert(user.first);
1✔
640
                }
1✔
641
            }
642
            for (auto& user : to_close) {
70✔
643
                closed_definitions.insert({user, open_definitions.at(user)});
1✔
644
                open_definitions.erase(user);
1✔
645
            }
646
            open_definitions.insert({current_user, {}});
69✔
647
        }
69✔
648
    }
252✔
649
}
195✔
650

651
bool DataDependencyAnalysis::loop_depends(
1,915✔
652
    User& previous,
653
    User& current,
654
    analysis::AssumptionsAnalysis& assumptions_analysis,
655
    structured_control_flow::StructuredLoop& loop
656
) {
657
    if (previous.container() != current.container()) {
1,915✔
658
        return false;
1,757✔
659
    }
660
    // Shortcut for scalars
661
    auto& type = this->sdfg_.type(previous.container());
158✔
662
    if (dynamic_cast<const types::Scalar*>(&type)) {
158✔
663
        return true;
40✔
664
    }
665

666
    auto& previous_subsets = previous.subsets();
118✔
667
    auto& current_subsets = current.subsets();
118✔
668

669
    auto previous_scope = Users::scope(&previous);
118✔
670
    auto previous_assumptions = assumptions_analysis.get(loop.root(), *previous_scope, true);
118✔
671
    auto current_scope = Users::scope(&current);
118✔
672
    auto current_assumptions = assumptions_analysis.get(loop.root(), *current_scope, true);
118✔
673

674
    // Check if previous subset is subset of any current subset
675
    for (auto& previous_subset : previous_subsets) {
192✔
676
        for (auto& current_subset : current_subsets) {
193✔
677
            if (symbolic::maps::intersects(
119✔
678
                    previous_subset, current_subset, loop.indvar(), previous_assumptions, current_assumptions
119✔
679
                )) {
680
                return true;
44✔
681
            }
682
        }
683
    }
684

685
    return false;
74✔
686
}
1,915✔
687

688
bool DataDependencyAnalysis::supersedes(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
454✔
689
    if (previous.container() != current.container()) {
454✔
690
        return false;
419✔
691
    }
692
    // Shortcut for scalars
693
    auto& type = this->sdfg_.type(previous.container());
35✔
694
    if (dynamic_cast<const types::Scalar*>(&type)) {
35✔
695
        return true;
14✔
696
    }
697

698
    auto& previous_subsets = previous.subsets();
21✔
699
    auto& current_subsets = current.subsets();
21✔
700
    auto previous_scope = Users::scope(&previous);
21✔
701
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
21✔
702
    auto current_scope = Users::scope(&current);
21✔
703
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
21✔
704

705
    // Check if previous subset is subset of any current subset
706
    for (auto& previous_subset : previous_subsets) {
32✔
707
        bool found = false;
21✔
708
        for (auto& current_subset : current_subsets) {
31✔
709
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, current_assumptions)) {
21✔
710
                found = true;
11✔
711
                break;
11✔
712
            }
713
        }
714
        if (!found) {
21✔
715
            return false;
10✔
716
        }
717
    }
718

719
    return true;
11✔
720
}
454✔
721

722
bool DataDependencyAnalysis::intersects(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
1,157✔
723
    if (previous.container() != current.container()) {
1,157✔
724
        return false;
726✔
725
    }
726
    // Shortcut for scalars
727
    auto& type = this->sdfg_.type(previous.container());
431✔
728
    if (dynamic_cast<const types::Scalar*>(&type)) {
431✔
729
        return true;
422✔
730
    }
731

732
    auto& previous_subsets = previous.subsets();
9✔
733
    auto& current_subsets = current.subsets();
9✔
734

735
    auto previous_scope = Users::scope(&previous);
9✔
736
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
9✔
737
    auto current_scope = Users::scope(&current);
9✔
738
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
9✔
739

740
    // Check if any current subset intersects with any previous subset
741
    bool found = false;
9✔
742
    for (auto& current_subset : current_subsets) {
15✔
743
        for (auto& previous_subset : previous_subsets) {
15✔
744
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
9✔
745
                found = true;
3✔
746
                break;
3✔
747
            }
748
        }
749
        if (found) {
9✔
750
            break;
3✔
751
        }
752
    }
753

754
    return found;
9✔
755
}
1,157✔
756

757
/****** Public API ******/
758

UNCOV
759
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
UNCOV
760
    assert(write.use() == Use::WRITE);
×
UNCOV
761
    if (results_.find(write.container()) == results_.end()) {
×
UNCOV
762
        return {};
×
763
    }
UNCOV
764
    auto& raws = results_.at(write.container());
×
UNCOV
765
    assert(raws.find(&write) != raws.end());
×
766

UNCOV
767
    auto& reads_for_write = raws.at(&write);
×
768

UNCOV
769
    std::unordered_set<User*> reads;
×
UNCOV
770
    for (auto& entry : reads_for_write) {
×
UNCOV
771
        reads.insert(entry);
×
772
    }
773

774
    return reads;
×
775
};
×
776

777
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(const std::string& container) {
33✔
778
    if (results_.find(container) == results_.end()) {
33✔
UNCOV
779
        return {};
×
780
    }
781
    return results_.at(container);
33✔
782
};
33✔
783

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

787
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
788
    for (auto& entry : reads) {
43✔
789
        for (auto& read : entry.second) {
51✔
790
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27✔
791
                read_to_writes_map[read] = {};
20✔
792
            }
20✔
793
            read_to_writes_map[read].insert(entry.first);
27✔
794
        }
795
    }
796
    return read_to_writes_map;
19✔
797
};
19✔
798

UNCOV
799
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
UNCOV
800
    assert(read.use() == Use::READ);
×
UNCOV
801
    auto definitions = this->definitions(read.container());
×
802

UNCOV
803
    std::unordered_set<User*> writes;
×
UNCOV
804
    for (auto& entry : definitions) {
×
UNCOV
805
        for (auto& r : entry.second) {
×
UNCOV
806
            if (&read == r) {
×
UNCOV
807
                writes.insert(entry.first);
×
UNCOV
808
            }
×
809
        }
810
    }
UNCOV
811
    return writes;
×
812
};
×
813

814
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
UNCOV
815
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
816
};
817

818
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::
819
    dependencies(structured_control_flow::StructuredLoop& loop) const {
50✔
820
    return this->loop_carried_dependencies_.at(&loop);
50✔
821
};
822

823
} // namespace analysis
824
} // 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