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

daisytuner / sdfglib / 19987946130

06 Dec 2025 11:37AM UTC coverage: 61.725% (-0.09%) from 61.811%
19987946130

push

github

web-flow
Merge pull request #374 from daisytuner/constant-propagation

extends constant propagation to handle pointer definitions

26 of 64 new or added lines in 2 files covered. (40.63%)

1 existing line in 1 file now uncovered.

11250 of 18226 relevant lines covered (61.73%)

110.61 hits per line

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

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

21
      };
101✔
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) {
77✔
29
    results_.clear();
77✔
30
    undefined_users_.clear();
77✔
31
    loop_carried_dependencies_.clear();
77✔
32

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

37
    auto& assumptions_analysis = analysis_manager.get<AssumptionsAnalysis>();
77✔
38
    auto& users = analysis_manager.get<Users>();
77✔
39
    auto& dominance_analysis = analysis_manager.get<DominanceAnalysis>();
77✔
40

41
    visit_sequence(users, assumptions_analysis, dominance_analysis, node_, undefined, open_definitions, closed_definitions);
77✔
42

43
    for (auto& entry : open_definitions) {
316✔
44
        closed_definitions.insert(entry);
239✔
45
    }
46

47
    for (auto& entry : closed_definitions) {
330✔
48
        if (results_.find(entry.first->container()) == results_.end()) {
253✔
49
            results_.insert({entry.first->container(), {}});
159✔
50
        }
159✔
51
        results_.at(entry.first->container()).insert(entry);
253✔
52
    }
53
};
77✔
54

55
/****** Visitor API ******/
56

57
void DataDependencyAnalysis::visit_block(
182✔
58
    analysis::Users& users,
59
    analysis::AssumptionsAnalysis& assumptions_analysis,
60
    analysis::DominanceAnalysis& dominance_analysis,
61
    structured_control_flow::Block& block,
62
    std::unordered_set<User*>& undefined,
63
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
64
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
65
) {
66
    auto& dataflow = block.dataflow();
182✔
67

68
    for (auto node : dataflow.topological_sort()) {
461✔
69
        if (dynamic_cast<data_flow::ConstantNode*>(node) != nullptr) {
279✔
70
            continue;
18✔
71
        }
72

73
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
261✔
74
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
166✔
75
                if (dataflow.in_degree(*node) > 0) {
166✔
76
                    Use use = Use::WRITE;
96✔
77
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
191✔
78
                        if (iedge.type() == data_flow::MemletType::Reference ||
192✔
79
                            iedge.type() == data_flow::MemletType::Dereference_Src) {
96✔
80
                            use = Use::MOVE;
1✔
81
                            break;
1✔
82
                        }
83
                    }
84

85
                    if (use == Use::WRITE) {
96✔
86
                        auto current_user = users.get_user(access_node->data(), access_node, use);
95✔
87

88
                        // Close open definitions if possible
89
                        std::unordered_map<User*, std::unordered_set<User*>> to_close;
95✔
90
                        for (auto& user : open_definitions) {
147✔
91
                            if (this->closes(assumptions_analysis, dominance_analysis, *user.first, *current_user, false)) {
52✔
92
                                to_close.insert(user);
10✔
93
                            }
10✔
94
                        }
95
                        for (auto& user : to_close) {
105✔
96
                            open_definitions.erase(user.first);
10✔
97
                            closed_definitions.insert(user);
10✔
98
                        }
99

100
                        // Start new open definition
101
                        open_definitions.insert({current_user, {}});
95✔
102
                    }
95✔
103
                }
96✔
104
                if (dataflow.out_degree(*access_node) > 0) {
166✔
105
                    Use use = Use::READ;
73✔
106
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
147✔
107
                        if (oedge.type() == data_flow::MemletType::Reference ||
148✔
108
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
74✔
109
                            use = Use::VIEW;
×
110
                            break;
×
111
                        }
112
                    }
113

114
                    if (use == Use::READ) {
73✔
115
                        auto current_user = users.get_user(access_node->data(), access_node, use);
73✔
116

117
                        // Assign to open definitions
118
                        bool found_user = false;
73✔
119
                        bool found_undefined_user = false;
73✔
120
                        for (auto& user : open_definitions) {
118✔
121
                            if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
45✔
122
                                user.second.insert(current_user);
14✔
123
                                found_user = true;
14✔
124
                                found_undefined_user = this->is_undefined_user(*user.first);
14✔
125
                            }
14✔
126
                        }
127
                        // If no definition found or undefined user found, mark as undefined
128
                        if (!found_user || found_undefined_user) {
73✔
129
                            undefined.insert(current_user);
62✔
130
                        }
62✔
131
                    }
73✔
132
                }
73✔
133
            }
166✔
134
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
261✔
135
            for (auto& symbol : library_node->symbols()) {
×
136
                auto current_user = users.get_user(symbol->get_name(), library_node, Use::READ);
×
137

138
                // Assign to open definitions
139
                bool found_user = false;
×
140
                bool found_undefined_user = false;
×
141
                for (auto& user : open_definitions) {
×
142
                    if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
×
143
                        user.second.insert(current_user);
×
144
                        found_user = true;
×
145
                        found_undefined_user = this->is_undefined_user(*current_user);
×
146
                    }
×
147
                }
148
                // If no definition found or undefined user found, mark as undefined
149
                if (!found_user || found_undefined_user) {
×
150
                    undefined.insert(current_user);
×
151
                }
×
152
            }
153
        }
×
154

155
        for (auto& oedge : dataflow.out_edges(*node)) {
430✔
156
            std::unordered_set<std::string> used;
169✔
157
            for (auto& dim : oedge.subset()) {
320✔
158
                for (auto atom : symbolic::atoms(dim)) {
294✔
159
                    used.insert(atom->get_name());
143✔
160
                }
143✔
161
            }
162
            for (auto& atom : used) {
312✔
163
                auto current_user = users.get_user(atom, &oedge, Use::READ);
143✔
164

165
                // Assign to open definitions
166
                bool found_user = false;
143✔
167
                bool found_undefined_user = false;
143✔
168
                for (auto& user : open_definitions) {
238✔
169
                    if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
95✔
170
                        user.second.insert(current_user);
4✔
171
                        found_user = true;
4✔
172
                        found_undefined_user = this->is_undefined_user(*user.first);
4✔
173
                    }
4✔
174
                }
175
                // If no definition found or undefined user found, mark as undefined
176
                if (!found_user || found_undefined_user) {
143✔
177
                    undefined.insert(current_user);
139✔
178
                }
139✔
179
            }
180
        }
169✔
181
    }
182
}
182✔
183

184
void DataDependencyAnalysis::visit_for(
68✔
185
    analysis::Users& users,
186
    analysis::AssumptionsAnalysis& assumptions_analysis,
187
    analysis::DominanceAnalysis& dominance_analysis,
188
    structured_control_flow::StructuredLoop& for_loop,
189
    std::unordered_set<User*>& undefined,
190
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
191
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
192
) {
193
    // Init - Read
194
    for (auto atom : symbolic::atoms(for_loop.init())) {
76✔
195
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, true);
8✔
196

197
        // Assign to open definitions
198
        bool found_user = false;
8✔
199
        bool found_undefined_user = false;
8✔
200
        for (auto& user : open_definitions) {
16✔
201
            if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
8✔
202
                user.second.insert(current_user);
1✔
203
                found_user = true;
1✔
204
                found_undefined_user = this->is_undefined_user(*user.first);
1✔
205
            }
1✔
206
        }
207
        // If no definition found or undefined user found, mark as undefined
208
        if (!found_user || found_undefined_user) {
8✔
209
            undefined.insert(current_user);
7✔
210
        }
7✔
211
    }
8✔
212

213
    // Init - Write
214
    {
215
        // Write Induction Variable
216
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, true);
68✔
217

218
        // Close open definitions if possible
219
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
68✔
220
        for (auto& user : open_definitions) {
90✔
221
            if (this->closes(assumptions_analysis, dominance_analysis, *user.first, *current_user, true)) {
22✔
222
                to_close.insert(user);
2✔
223
            }
2✔
224
        }
225
        for (auto& user : to_close) {
70✔
226
            open_definitions.erase(user.first);
2✔
227
            closed_definitions.insert(user);
2✔
228
        }
229

230
        // Start new open definition
231
        open_definitions.insert({current_user, {}});
68✔
232
    }
68✔
233

234
    // Update - Write
235
    {
236
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, false, false, true);
68✔
237
        open_definitions.insert({current_user, {}});
68✔
238
    }
239

240
    // Condition - Read
241
    for (auto atom : symbolic::atoms(for_loop.condition())) {
193✔
242
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, true);
125✔
243

244
        // Assign to open definitions
245
        bool found_user = false;
125✔
246
        bool found_undefined_user = false;
125✔
247
        for (auto& user : open_definitions) {
399✔
248
            if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
274✔
249
                user.second.insert(current_user);
137✔
250
                found_user = true;
137✔
251
                found_undefined_user = this->is_undefined_user(*user.first);
137✔
252
            }
137✔
253
        }
254
        // If no definition found or undefined user found, mark as undefined
255
        if (!found_user || found_undefined_user) {
125✔
256
            undefined.insert(current_user);
56✔
257
        }
56✔
258
    }
125✔
259

260
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_for;
68✔
261
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_for;
68✔
262
    std::unordered_set<User*> undefined_for;
68✔
263

264
    // Add assumptions for body
265
    visit_sequence(
68✔
266
        users,
68✔
267
        assumptions_analysis,
68✔
268
        dominance_analysis,
68✔
269
        for_loop.root(),
68✔
270
        undefined_for,
271
        open_definitions_for,
272
        closed_definitions_for
273
    );
274

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

279
        // Assign to open definitions
280
        bool found_user = false;
68✔
281
        bool found_undefined_user = false;
68✔
282
        for (auto& user : open_definitions_for) {
201✔
283
            if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
133✔
284
                user.second.insert(current_user);
1✔
285
                found_user = true;
1✔
286
                found_undefined_user = this->is_undefined_user(*user.first);
1✔
287
            }
1✔
288
        }
289
        // If no definition found or undefined user found, mark as undefined
290
        if (!found_user || found_undefined_user) {
68✔
291
            undefined_for.insert(current_user);
67✔
292
        }
67✔
293
    }
68✔
294

295
    // Merge for with outside
296

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

302
    // Undefined reads are matched or forwarded
303
    for (auto open_read : undefined_for) {
484✔
304
        // Simple check: no match or undefined user
305
        std::unordered_set<User*> frontier;
416✔
306
        bool found = false;
416✔
307
        bool found_undefined_user = false;
416✔
308
        for (auto& entry : open_definitions) {
1,479✔
309
            if (intersects(*entry.first, *open_read, assumptions_analysis)) {
1,063✔
310
                entry.second.insert(open_read);
420✔
311
                found = true;
420✔
312
                found_undefined_user = this->is_undefined_user(*entry.first);
420✔
313
                frontier.insert(entry.first);
420✔
314
            }
420✔
315
        }
316
        if (!found || found_undefined_user) {
416✔
317
            undefined.insert(open_read);
204✔
318
            continue;
204✔
319
        }
320

321
        // Users found, check if they fully cover the read
322
        bool covered = false;
212✔
323
        for (auto& entry : frontier) {
221✔
324
            bool covers = supersedes_restrictive(*open_read, *entry, assumptions_analysis);
220✔
325
            if (covers && dominance_analysis.dominates(*entry, *open_read)) {
220✔
326
                covered = true;
211✔
327
                break;
211✔
328
            }
329
        }
330
        if (!covered) {
212✔
331
            undefined.insert(open_read);
1✔
332
        }
1✔
333
    }
416✔
334

335
    // Open definitions may close outside open definitions after loop
336
    std::unordered_set<User*> to_close;
68✔
337
    for (auto& previous : open_definitions) {
224✔
338
        for (auto& user : open_definitions_for) {
459✔
339
            if (this->closes(assumptions_analysis, dominance_analysis, *previous.first, *user.first, false)) {
307✔
340
                to_close.insert(previous.first);
4✔
341
                break;
4✔
342
            }
343
        }
344
    }
345
    for (auto& user : to_close) {
72✔
346
        closed_definitions.insert({user, open_definitions.at(user)});
4✔
347
        open_definitions.erase(user);
4✔
348
    }
349

350
    // Add loop-carried dependencies
351

352
    // Criterion 1: Loop is monotonic -> indvar never takes the same value twice
353
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
68✔
354
    if (is_monotonic) {
68✔
355
        // Case: Can analyze
356
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
67✔
357
        assert(success);
67✔
358
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
67✔
359

360
        // We can focus on written containers
361

362
        // Case 1: Read-Write between iterations
363
        for (auto& read : undefined_for) {
482✔
364
            for (auto& write : open_definitions_for) {
2,132✔
365
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop)) {
1,717✔
366
                    dependencies[read->container()] = LOOP_CARRIED_DEPENDENCY_READ_WRITE;
43✔
367
                    write.second.insert(read);
43✔
368
                }
43✔
369
            }
370
        }
371

372
        // Case 2: Write-Write between iterations
373
        for (auto& write : open_definitions_for) {
199✔
374
            if (dependencies.find(write.first->container()) != dependencies.end()) {
132✔
375
                continue;
44✔
376
            }
377
            for (auto& write_2 : open_definitions_for) {
281✔
378
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis, for_loop)) {
236✔
379
                    dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
43✔
380
                    break;
43✔
381
                }
382
            }
383
        }
384
    } else {
67✔
385
        // Case: Cannot analyze
386
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
1✔
387
        assert(success);
1✔
388
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
1✔
389

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

408
    // Add open definitions from for to outside
409
    for (auto& entry : open_definitions_for) {
201✔
410
        open_definitions.insert(entry);
133✔
411
    }
412
}
68✔
413

414
void DataDependencyAnalysis::visit_if_else(
22✔
415
    analysis::Users& users,
416
    analysis::AssumptionsAnalysis& assumptions_analysis,
417
    analysis::DominanceAnalysis& dominance_analysis,
418
    structured_control_flow::IfElse& if_else,
419
    std::unordered_set<User*>& undefined,
420
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
421
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
422
) {
423
    // Read Conditions
424
    for (size_t i = 0; i < if_else.size(); i++) {
60✔
425
        auto child = if_else.at(i).second;
38✔
426
        for (auto atom : symbolic::atoms(child)) {
63✔
427
            auto current_user = users.get_user(atom->get_name(), &if_else, Use::READ);
25✔
428

429
            bool found_user = false;
25✔
430
            bool found_undefined_user = false;
25✔
431
            for (auto& user : open_definitions) {
41✔
432
                if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
16✔
433
                    user.second.insert(current_user);
8✔
434
                    found_user = true;
8✔
435
                    found_undefined_user = this->is_undefined_user(*user.first);
8✔
436
                }
8✔
437
            }
438
            // If no definition found or undefined user found, mark as undefined
439
            if (!found_user || found_undefined_user) {
25✔
440
                undefined.insert(current_user);
17✔
441
            }
17✔
442
        }
25✔
443
    }
38✔
444

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

461
    // merge partial open reads
462
    for (size_t i = 0; i < if_else.size(); i++) {
60✔
463
        for (auto& entry : undefined_branches.at(i)) {
43✔
464
            bool found_user = false;
5✔
465
            bool found_undefined_user = false;
5✔
466
            for (auto& user : open_definitions) {
11✔
467
                if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *entry)) {
6✔
468
                    user.second.insert(entry);
5✔
469
                    found_user = true;
5✔
470
                    found_undefined_user = this->is_undefined_user(*user.first);
5✔
471
                }
5✔
472
            }
473
            // If no definition found or undefined user found, mark as undefined
474
            if (!found_user || found_undefined_user) {
5✔
475
                undefined.insert(entry);
×
476
            }
×
477
        }
478
    }
38✔
479

480
    // merge closed writes
481
    for (auto& closing : closed_definitionss_branches) {
60✔
482
        for (auto& entry : closing) {
38✔
483
            closed_definitions.insert(entry);
×
484
        }
485
    }
486

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

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

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

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

522
        for (auto& entry : to_close) {
20✔
523
            open_definitions.erase(entry.first);
4✔
524
            closed_definitions.insert(entry);
4✔
525
        }
526
    } else {
16✔
527
        // Incomplete if-else
528

529
        // In order to determine whether a new read is undefined
530
        // we would need to check whether all open definitions
531
        // jointly dominate the read.
532
        // Since this is expensive, we apply a trick:
533
        // For incomplete if-elses and any newly opened definition in
534
        // any branch, we add an artificial undefined user for that container.
535
        // If we encounter this user later, we know that not all branches defined it.
536
        // Hence, we can mark the read as (partially) undefined.
537

538
        for (auto& branch : open_definitions_branches) {
12✔
539
            for (auto& open_definition : branch) {
10✔
540
                auto write = open_definition.first;
4✔
541
                auto artificial_user = std::make_unique<
4✔
542
                    User>(boost::graph_traits<graph::Graph>::null_vertex(), write->container(), nullptr, Use::WRITE);
4✔
543
                this->undefined_users_.push_back(std::move(artificial_user));
4✔
544
                open_definitions.insert({this->undefined_users_.back().get(), {}});
4✔
545
            }
4✔
546
        }
547
    }
548

549
    // Add open definitions from branches to outside
550
    for (auto& branch : open_definitions_branches) {
60✔
551
        for (auto& entry : branch) {
71✔
552
            open_definitions.insert(entry);
33✔
553
        }
554
    }
555
}
22✔
556

557
void DataDependencyAnalysis::visit_while(
8✔
558
    analysis::Users& users,
559
    analysis::AssumptionsAnalysis& assumptions_analysis,
560
    analysis::DominanceAnalysis& dominance_analysis,
561
    structured_control_flow::While& while_loop,
562
    std::unordered_set<User*>& undefined,
563
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
564
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
565
) {
566
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_while;
8✔
567
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_while;
8✔
568
    std::unordered_set<User*> undefined_while;
8✔
569

570
    visit_sequence(
8✔
571
        users,
8✔
572
        assumptions_analysis,
8✔
573
        dominance_analysis,
8✔
574
        while_loop.root(),
8✔
575
        undefined_while,
576
        open_definitions_while,
577
        closed_definitions_while
578
    );
579

580
    // Scope-local closed definitions
581
    for (auto& entry : closed_definitions_while) {
10✔
582
        closed_definitions.insert(entry);
2✔
583
    }
584

585
    for (auto open_read : undefined_while) {
11✔
586
        // Over-Approximation: Add loop-carried dependencies for all open reads
587
        for (auto& entry : open_definitions_while) {
7✔
588
            if (entry.first->container() == open_read->container()) {
4✔
589
                entry.second.insert(open_read);
2✔
590
            }
2✔
591
        }
592

593
        // Connect to outside
594
        bool found = false;
3✔
595
        for (auto& entry : open_definitions) {
6✔
596
            if (entry.first->container() == open_read->container()) {
3✔
597
                entry.second.insert(open_read);
2✔
598
                found = true;
2✔
599
            }
2✔
600
        }
601
        if (!found) {
3✔
602
            undefined.insert(open_read);
1✔
603
        }
1✔
604
    }
605

606
    // Add open definitions from while to outside
607
    for (auto& entry : open_definitions_while) {
21✔
608
        open_definitions.insert(entry);
13✔
609
    }
610
}
8✔
611

612
void DataDependencyAnalysis::visit_return(
×
613
    analysis::Users& users,
614
    analysis::AssumptionsAnalysis& assumptions_analysis,
615
    analysis::DominanceAnalysis& dominance_analysis,
616
    structured_control_flow::Return& return_statement,
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
    if (return_statement.is_data() && !return_statement.data().empty()) {
×
622
        auto current_user = users.get_user(return_statement.data(), &return_statement, Use::READ);
×
623

624
        bool found = false;
×
625
        for (auto& user : open_definitions) {
×
626
            if (user.first->container() == return_statement.data()) {
×
627
                user.second.insert(current_user);
×
628
                found = true;
×
629
            }
×
630
        }
631
        if (!found) {
×
632
            undefined.insert(current_user);
×
633
        }
×
634
    }
×
635

636
    // close all open reads_after_writes
637
    for (auto& entry : open_definitions) {
×
638
        closed_definitions.insert(entry);
×
639
    }
640
    open_definitions.clear();
×
641
}
×
642

643
void DataDependencyAnalysis::visit_sequence(
208✔
644
    analysis::Users& users,
645
    analysis::AssumptionsAnalysis& assumptions_analysis,
646
    analysis::DominanceAnalysis& dominance_analysis,
647
    structured_control_flow::Sequence& sequence,
648
    std::unordered_set<User*>& undefined,
649
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
650
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
651
) {
652
    for (size_t i = 0; i < sequence.size(); i++) {
488✔
653
        auto child = sequence.at(i);
280✔
654
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
280✔
655
            visit_block(
174✔
656
                users, assumptions_analysis, dominance_analysis, *block, undefined, open_definitions, closed_definitions
174✔
657
            );
658
        } else if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
280✔
659
            visit_for(
68✔
660
                users,
68✔
661
                assumptions_analysis,
68✔
662
                dominance_analysis,
68✔
663
                *for_loop,
68✔
664
                undefined,
68✔
665
                open_definitions,
68✔
666
                closed_definitions
68✔
667
            );
668
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
106✔
669
            visit_if_else(
22✔
670
                users, assumptions_analysis, dominance_analysis, *if_else, undefined, open_definitions, closed_definitions
22✔
671
            );
672
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
38✔
673
            visit_while(
8✔
674
                users,
8✔
675
                assumptions_analysis,
8✔
676
                dominance_analysis,
8✔
677
                *while_loop,
8✔
678
                undefined,
8✔
679
                open_definitions,
8✔
680
                closed_definitions
8✔
681
            );
682
        } else if (auto return_statement = dynamic_cast<structured_control_flow::Return*>(&child.first)) {
16✔
683
            visit_return(
×
684
                users,
×
685
                assumptions_analysis,
×
686
                dominance_analysis,
×
687
                *return_statement,
×
688
                undefined,
×
689
                open_definitions,
×
690
                closed_definitions
×
691
            );
692
        } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&child.first)) {
8✔
693
            visit_sequence(
×
694
                users,
×
695
                assumptions_analysis,
×
696
                dominance_analysis,
×
697
                *sequence,
×
698
                undefined,
×
699
                open_definitions,
×
700
                closed_definitions
×
701
            );
702
        }
×
703

704
        // handle transitions read
705
        for (auto& entry : child.second.assignments()) {
360✔
706
            for (auto& atom : symbolic::atoms(entry.second)) {
112✔
707
                if (symbolic::is_pointer(atom)) {
32✔
708
                    continue;
×
709
                }
710
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
32✔
711

712
                bool found = false;
32✔
713
                for (auto& user : open_definitions) {
63✔
714
                    if (user.first->container() == atom->get_name()) {
31✔
715
                        user.second.insert(current_user);
28✔
716
                        found = true;
28✔
717
                    }
28✔
718
                }
719
                if (!found) {
32✔
720
                    undefined.insert(current_user);
14✔
721
                }
14✔
722
            }
723
        }
724

725
        // handle transitions write
726
        for (auto& entry : child.second.assignments()) {
360✔
727
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
80✔
728

729
            std::unordered_set<User*> to_close;
80✔
730
            for (auto& user : open_definitions) {
113✔
731
                if (user.first->container() == entry.first->get_name()) {
33✔
732
                    to_close.insert(user.first);
3✔
733
                }
3✔
734
            }
735
            for (auto& user : to_close) {
83✔
736
                closed_definitions.insert({user, open_definitions.at(user)});
3✔
737
                open_definitions.erase(user);
3✔
738
            }
739
            open_definitions.insert({current_user, {}});
80✔
740
        }
80✔
741
    }
280✔
742
}
208✔
743

744
bool DataDependencyAnalysis::loop_depends(
1,953✔
745
    User& previous,
746
    User& current,
747
    analysis::AssumptionsAnalysis& assumptions_analysis,
748
    structured_control_flow::StructuredLoop& loop
749
) {
750
    if (previous.container() != current.container()) {
1,953✔
751
        return false;
1,789✔
752
    }
753
    // Shortcut for scalars
754
    auto& type = this->sdfg_.type(previous.container());
164✔
755
    if (dynamic_cast<const types::Scalar*>(&type)) {
164✔
756
        return true;
42✔
757
    }
758

759
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
122✔
760
        return true;
×
761
    }
762

763
    auto& previous_subsets = previous.subsets();
122✔
764
    auto& current_subsets = current.subsets();
122✔
765

766
    auto previous_scope = Users::scope(&previous);
122✔
767
    auto previous_assumptions = assumptions_analysis.get(loop.root(), *previous_scope, true);
122✔
768
    auto current_scope = Users::scope(&current);
122✔
769
    auto current_assumptions = assumptions_analysis.get(loop.root(), *current_scope, true);
122✔
770

771
    // Check if previous subset is subset of any current subset
772
    for (auto& previous_subset : previous_subsets) {
200✔
773
        for (auto& current_subset : current_subsets) {
201✔
774
            if (symbolic::maps::intersects(
123✔
775
                    previous_subset, current_subset, loop.indvar(), previous_assumptions, current_assumptions
123✔
776
                )) {
777
                return true;
44✔
778
            }
779
        }
780
    }
781

782
    return false;
78✔
783
}
1,953✔
784

785
bool DataDependencyAnalysis::
786
    supersedes_restrictive(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
220✔
787
    if (previous.container() != current.container()) {
220✔
788
        return false;
×
789
    }
790
    // Shortcut for scalars
791
    auto& type = this->sdfg_.type(previous.container());
220✔
792
    if (dynamic_cast<const types::Scalar*>(&type)) {
220✔
793
        return true;
217✔
794
    }
795

796
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
3✔
797
        return false;
×
798
    }
799

800
    auto& previous_subsets = previous.subsets();
3✔
801
    auto& current_subsets = current.subsets();
3✔
802
    auto previous_scope = Users::scope(&previous);
3✔
803
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
3✔
804
    auto current_scope = Users::scope(&current);
3✔
805
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
3✔
806

807
    // Check if previous subset is subset of any current subset
808
    for (auto& previous_subset : previous_subsets) {
5✔
809
        bool found = false;
3✔
810
        for (auto& current_subset : current_subsets) {
4✔
811
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, previous_assumptions)) {
3✔
812
                found = true;
2✔
813
                break;
2✔
814
            }
815
        }
816
        if (!found) {
3✔
817
            return false;
1✔
818
        }
819
    }
820

821
    return true;
2✔
822
}
220✔
823

824
bool DataDependencyAnalysis::intersects(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
1,063✔
825
    if (previous.container() != current.container()) {
1,063✔
826
        return false;
638✔
827
    }
828
    // Shortcut for scalars
829
    auto& type = this->sdfg_.type(previous.container());
425✔
830
    if (dynamic_cast<const types::Scalar*>(&type)) {
425✔
831
        return true;
417✔
832
    }
833

834
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
8✔
835
        return true;
×
836
    }
837

838
    auto& previous_subsets = previous.subsets();
8✔
839
    auto& current_subsets = current.subsets();
8✔
840

841
    auto previous_scope = Users::scope(&previous);
8✔
842
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
8✔
843
    auto current_scope = Users::scope(&current);
8✔
844
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
8✔
845

846
    // Check if any current subset intersects with any previous subset
847
    bool found = false;
8✔
848
    for (auto& current_subset : current_subsets) {
13✔
849
        for (auto& previous_subset : previous_subsets) {
13✔
850
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
8✔
851
                found = true;
3✔
852
                break;
3✔
853
            }
854
        }
855
        if (found) {
8✔
856
            break;
3✔
857
        }
858
    }
859

860
    return found;
8✔
861
}
1,063✔
862

863
bool DataDependencyAnalysis::closes(
381✔
864
    analysis::AssumptionsAnalysis& assumptions_analysis,
865
    analysis::DominanceAnalysis& dominance_analysis,
866
    User& previous,
867
    User& current,
868
    bool requires_dominance
869
) {
870
    if (previous.container() != current.container()) {
381✔
871
        return false;
355✔
872
    }
873

874
    // Check dominance
875
    if (requires_dominance && !dominance_analysis.post_dominates(current, previous)) {
26✔
876
        return false;
×
877
    }
878

879
    // Previous memlets are subsets of current memlets
880
    auto& type = sdfg_.type(previous.container());
26✔
881
    if (type.type_id() == types::TypeID::Scalar) {
26✔
882
        return true;
5✔
883
    }
884

885
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
21✔
886
        return false;
×
887
    }
888

889
    // Collect memlets and assumptions
890
    auto previous_scope = Users::scope(&previous);
21✔
891
    auto current_scope = Users::scope(&current);
21✔
892
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
21✔
893
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
21✔
894

895
    auto& previous_memlets = previous.subsets();
21✔
896
    auto& current_memlets = current.subsets();
21✔
897

898
    for (auto& subset_ : previous_memlets) {
32✔
899
        bool overwritten = false;
21✔
900
        for (auto& subset : current_memlets) {
31✔
901
            if (symbolic::is_subset(subset_, subset, previous_assumptions, current_assumptions)) {
21✔
902
                overwritten = true;
11✔
903
                break;
11✔
904
            }
905
        }
906
        if (!overwritten) {
21✔
907
            return false;
10✔
908
        }
909
    }
910

911
    return true;
11✔
912
}
381✔
913

914
bool DataDependencyAnalysis::depends(
578✔
915
    analysis::AssumptionsAnalysis& assumptions_analysis,
916
    analysis::DominanceAnalysis& dominance_analysis,
917
    User& previous,
918
    User& current
919
) {
920
    if (previous.container() != current.container()) {
578✔
921
        return false;
403✔
922
    }
923

924
    // Previous memlets are subsets of current memlets
925
    auto& type = sdfg_.type(previous.container());
175✔
926
    if (type.type_id() == types::TypeID::Scalar) {
175✔
927
        return true;
164✔
928
    }
929

930
    if (this->is_undefined_user(previous)) {
11✔
931
        return true;
×
932
    }
933

934
    // Collect memlets and assumptions
935
    auto previous_scope = Users::scope(&previous);
11✔
936
    auto current_scope = Users::scope(&current);
11✔
937
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
11✔
938
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
11✔
939

940
    auto& previous_memlets = previous.subsets();
11✔
941
    auto& current_memlets = current.subsets();
11✔
942

943
    bool intersect_any = false;
11✔
944
    for (auto& current_subset : current_memlets) {
16✔
945
        for (auto& previous_subset : previous_memlets) {
16✔
946
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
11✔
947
                intersect_any = true;
6✔
948
                break;
6✔
949
            }
950
        }
951
        if (intersect_any) {
11✔
952
            break;
6✔
953
        }
954
    }
955

956
    return intersect_any;
11✔
957
}
578✔
958

959
/****** Public API ******/
960

961
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
962
    assert(write.use() == Use::WRITE);
×
963
    if (results_.find(write.container()) == results_.end()) {
×
964
        return {};
×
965
    }
966
    auto& raws = results_.at(write.container());
×
967
    assert(raws.find(&write) != raws.end());
×
968

969
    auto& reads_for_write = raws.at(&write);
×
970

971
    std::unordered_set<User*> reads;
×
972
    for (auto& entry : reads_for_write) {
×
973
        reads.insert(entry);
×
974
    }
975

976
    return reads;
×
977
};
×
978

979
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(const std::string& container) {
33✔
980
    if (results_.find(container) == results_.end()) {
33✔
UNCOV
981
        return {};
×
982
    }
983
    return results_.at(container);
33✔
984
};
33✔
985

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

989
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
990
    for (auto& entry : reads) {
43✔
991
        for (auto& read : entry.second) {
51✔
992
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27✔
993
                read_to_writes_map[read] = {};
20✔
994
            }
20✔
995
            read_to_writes_map[read].insert(entry.first);
27✔
996
        }
997
    }
998
    return read_to_writes_map;
19✔
999
};
19✔
1000

1001
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
1002
    assert(read.use() == Use::READ);
×
1003
    auto definitions = this->definitions(read.container());
×
1004

1005
    std::unordered_set<User*> writes;
×
1006
    for (auto& entry : definitions) {
×
1007
        for (auto& r : entry.second) {
×
1008
            if (&read == r) {
×
1009
                writes.insert(entry.first);
×
1010
            }
×
1011
        }
1012
    }
1013
    return writes;
×
1014
};
×
1015

1016
bool DataDependencyAnalysis::is_undefined_user(User& user) const {
929✔
1017
    return user.vertex_ == boost::graph_traits<graph::Graph>::null_vertex();
929✔
1018
};
1019

1020
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
1021
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
1022
};
1023

1024
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::
1025
    dependencies(structured_control_flow::StructuredLoop& loop) const {
53✔
1026
    return this->loop_carried_dependencies_.at(&loop);
53✔
1027
};
1028

1029
} // namespace analysis
1030
} // 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