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

daisytuner / docc / 21919989349

11 Feb 2026 07:31PM UTC coverage: 66.315% (-0.08%) from 66.399%
21919989349

Pull #518

github

web-flow
Merge 0553f59bc into 632d0e1c4
Pull Request #518: Revert "Fix DeadDataElimination (#514)"

1 of 1 new or added line in 1 file covered. (100.0%)

30 existing lines in 3 files now uncovered.

23488 of 35419 relevant lines covered (66.31%)

372.33 hits per line

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

87.18
/sdfg/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, bool detailed)
19
    : Analysis(sdfg), node_(sdfg.root()), detailed_(detailed) {
254✔
20

21
      };
254✔
22

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

26
      };
×
27

28
void DataDependencyAnalysis::run(analysis::AnalysisManager& analysis_manager) {
229✔
29
    results_.clear();
229✔
30
    undefined_users_.clear();
229✔
31
    loop_carried_dependencies_.clear();
229✔
32

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

37
    visit_sequence(analysis_manager, node_, undefined, open_definitions, closed_definitions);
229✔
38

39
    for (auto& entry : open_definitions) {
2,056✔
40
        closed_definitions.insert(entry);
2,056✔
41
    }
2,056✔
42

43
    for (auto& entry : closed_definitions) {
2,123✔
44
        if (results_.find(entry.first->container()) == results_.end()) {
2,123✔
45
            results_.insert({entry.first->container(), {}});
1,200✔
46
        }
1,200✔
47
        results_.at(entry.first->container()).insert(entry);
2,123✔
48
    }
2,123✔
49
};
229✔
50

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

53
void DataDependencyAnalysis::visit_block(
54
    analysis::AnalysisManager& analysis_manager,
55
    structured_control_flow::Block& block,
56
    std::unordered_set<User*>& undefined,
57
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
58
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
59
) {
788✔
60
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
788✔
61
    auto& dominance_analysis = analysis_manager.get<analysis::DominanceAnalysis>();
788✔
62
    auto& users = analysis_manager.get<analysis::Users>();
788✔
63

64
    auto& dataflow = block.dataflow();
788✔
65

66
    for (auto node : dataflow.topological_sort()) {
2,714✔
67
        if (dynamic_cast<data_flow::ConstantNode*>(node) != nullptr) {
2,714✔
68
            continue;
120✔
69
        }
120✔
70

71
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
2,594✔
72
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
1,869✔
73
                if (dataflow.in_degree(*node) > 0) {
1,869✔
74
                    Use use = Use::WRITE;
726✔
75
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
726✔
76
                        if (iedge.type() == data_flow::MemletType::Reference ||
726✔
77
                            iedge.type() == data_flow::MemletType::Dereference_Src) {
726✔
78
                            use = Use::MOVE;
1✔
79
                            break;
1✔
80
                        }
1✔
81
                    }
726✔
82

83
                    if (use == Use::WRITE) {
726✔
84
                        auto current_user = users.get_user(access_node->data(), access_node, use);
725✔
85

86
                        // Close open definitions if possible
87
                        std::unordered_map<User*, std::unordered_set<User*>> to_close;
725✔
88
                        for (auto& user : open_definitions) {
725✔
89
                            if (this->closes(analysis_manager, *user.first, *current_user, false)) {
632✔
90
                                to_close.insert(user);
65✔
91
                            }
65✔
92
                        }
632✔
93
                        for (auto& user : to_close) {
725✔
94
                            open_definitions.erase(user.first);
65✔
95
                            closed_definitions.insert(user);
65✔
96
                        }
65✔
97

98
                        // Start new open definition
99
                        open_definitions.insert({current_user, {}});
725✔
100
                    }
725✔
101
                }
726✔
102
                if (dataflow.out_degree(*access_node) > 0) {
1,869✔
103
                    Use use = Use::READ;
1,180✔
104
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
1,252✔
105
                        if (oedge.type() == data_flow::MemletType::Reference ||
1,252✔
106
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
1,252✔
107
                            use = Use::VIEW;
×
108
                            break;
×
109
                        }
×
110
                    }
1,252✔
111

112
                    if (use == Use::READ) {
1,180✔
113
                        auto current_user = users.get_user(access_node->data(), access_node, use);
1,180✔
114

115
                        // Assign to open definitions
116
                        bool found_user = false;
1,180✔
117
                        bool found_undefined_user = false;
1,180✔
118
                        for (auto& user : open_definitions) {
1,180✔
119
                            if (this->depends(analysis_manager, *user.first, *current_user)) {
1,099✔
120
                                user.second.insert(current_user);
294✔
121
                                found_user = true;
294✔
122
                                found_undefined_user = this->is_undefined_user(*user.first);
294✔
123
                            }
294✔
124
                        }
1,099✔
125
                        // If no definition found or undefined user found, mark as undefined
126
                        if (!found_user || found_undefined_user) {
1,180✔
127
                            undefined.insert(current_user);
927✔
128
                        }
927✔
129
                    }
1,180✔
130
                }
1,180✔
131
            }
1,869✔
132
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
1,869✔
133
            for (auto& symbol : library_node->symbols()) {
7✔
UNCOV
134
                auto current_user = users.get_user(symbol->get_name(), library_node, Use::READ);
×
135

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

153
        for (auto& oedge : dataflow.out_edges(*node)) {
2,594✔
154
            std::unordered_set<std::string> used;
1,977✔
155
            for (auto& dim : oedge.subset()) {
2,350✔
156
                for (auto atom : symbolic::atoms(dim)) {
2,350✔
157
                    used.insert(atom->get_name());
2,304✔
158
                }
2,304✔
159
            }
2,350✔
160
            for (auto& atom : used) {
2,269✔
161
                auto current_user = users.get_user(atom, &oedge, Use::READ);
2,269✔
162

163
                // Assign to open definitions
164
                bool found_user = false;
2,269✔
165
                bool found_undefined_user = false;
2,269✔
166
                for (auto& user : open_definitions) {
2,269✔
167
                    if (this->depends(analysis_manager, *user.first, *current_user)) {
1,861✔
168
                        user.second.insert(current_user);
6✔
169
                        found_user = true;
6✔
170
                        found_undefined_user = this->is_undefined_user(*user.first);
6✔
171
                    }
6✔
172
                }
1,861✔
173
                // If no definition found or undefined user found, mark as undefined
174
                if (!found_user || found_undefined_user) {
2,269✔
175
                    undefined.insert(current_user);
2,263✔
176
                }
2,263✔
177
            }
2,269✔
178
        }
1,977✔
179
    }
2,594✔
180
}
788✔
181

182
void DataDependencyAnalysis::visit_for(
183
    analysis::AnalysisManager& analysis_manager,
184
    structured_control_flow::StructuredLoop& for_loop,
185
    std::unordered_set<User*>& undefined,
186
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
187
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
188
) {
674✔
189
    auto& users = analysis_manager.get<analysis::Users>();
674✔
190

191
    // Init - Read
192
    for (auto atom : symbolic::atoms(for_loop.init())) {
674✔
193
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, true);
19✔
194

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

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

216
        // Close open definitions if possible
217
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
674✔
218
        for (auto& user : open_definitions) {
1,187✔
219
            if (this->closes(analysis_manager, *user.first, *current_user, true)) {
1,187✔
220
                to_close.insert(user);
2✔
221
            }
2✔
222
        }
1,187✔
223
        for (auto& user : to_close) {
674✔
224
            open_definitions.erase(user.first);
2✔
225
            closed_definitions.insert(user);
2✔
226
        }
2✔
227

228
        // Start new open definition
229
        open_definitions.insert({current_user, {}});
674✔
230
    }
674✔
231

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

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

242
        // Assign to open definitions
243
        bool found_user = false;
1,319✔
244
        bool found_undefined_user = false;
1,319✔
245
        for (auto& user : open_definitions) {
4,999✔
246
            if (this->depends(analysis_manager, *user.first, *current_user)) {
4,999✔
247
                user.second.insert(current_user);
1,349✔
248
                found_user = true;
1,349✔
249
                found_undefined_user = this->is_undefined_user(*user.first);
1,349✔
250
            }
1,349✔
251
        }
4,999✔
252
        // If no definition found or undefined user found, mark as undefined
253
        if (!found_user || found_undefined_user) {
1,319✔
254
            undefined.insert(current_user);
644✔
255
        }
644✔
256
    }
1,319✔
257

258
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_for;
674✔
259
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_for;
674✔
260
    std::unordered_set<User*> undefined_for;
674✔
261

262
    // Add assumptions for body
263
    visit_sequence(analysis_manager, for_loop.root(), undefined_for, open_definitions_for, closed_definitions_for);
674✔
264

265
    // Update - Read
266
    for (auto atom : symbolic::atoms(for_loop.update())) {
674✔
267
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, false, true);
674✔
268

269
        // Assign to open definitions
270
        bool found_user = false;
674✔
271
        bool found_undefined_user = false;
674✔
272
        for (auto& user : open_definitions_for) {
2,589✔
273
            if (this->depends(analysis_manager, *user.first, *current_user)) {
2,589✔
274
                user.second.insert(current_user);
1✔
275
                found_user = true;
1✔
276
                found_undefined_user = this->is_undefined_user(*user.first);
1✔
277
            }
1✔
278
        }
2,589✔
279
        // If no definition found or undefined user found, mark as undefined
280
        if (!found_user || found_undefined_user) {
674✔
281
            undefined_for.insert(current_user);
673✔
282
        }
673✔
283
    }
674✔
284

285
    // Merge for with outside
286
    auto& dominance_analysis = analysis_manager.get<analysis::DominanceAnalysis>();
674✔
287

288
    // Closed definitions are simply merged
289
    for (auto& entry : closed_definitions_for) {
674✔
290
        closed_definitions.insert(entry);
99✔
291
    }
99✔
292

293
    // Undefined reads are matched or forwarded
294
    for (auto open_read : undefined_for) {
7,048✔
295
        // Simple check: no match or undefined user
296
        std::unordered_set<User*> frontier;
7,048✔
297
        bool found = false;
7,048✔
298
        bool found_undefined_user = false;
7,048✔
299
        for (auto& entry : open_definitions) {
24,854✔
300
            if (intersects(*entry.first, *open_read, analysis_manager)) {
24,854✔
301
                entry.second.insert(open_read);
6,352✔
302
                found = true;
6,352✔
303
                found_undefined_user = this->is_undefined_user(*entry.first);
6,352✔
304
                frontier.insert(entry.first);
6,352✔
305
            }
6,352✔
306
        }
24,854✔
307
        if (!found || found_undefined_user) {
7,048✔
308
            undefined.insert(open_read);
3,778✔
309
            continue;
3,778✔
310
        }
3,778✔
311

312
        // Users found, check if they fully cover the read
313
        bool covered = false;
3,270✔
314
        for (auto& entry : frontier) {
3,571✔
315
            if (!dominance_analysis.dominates(*entry, *open_read)) {
3,571✔
316
                continue;
462✔
317
            }
462✔
318
            bool covers = supersedes_restrictive(*open_read, *entry, analysis_manager);
3,109✔
319
            if (covers) {
3,109✔
320
                covered = true;
3,108✔
321
                break;
3,108✔
322
            }
3,108✔
323
        }
3,109✔
324
        if (!covered) {
3,270✔
325
            undefined.insert(open_read);
162✔
326
        }
162✔
327
    }
3,270✔
328

329
    // Open definitions may close outside open definitions after loop
330
    std::unordered_set<User*> to_close;
674✔
331
    for (auto& previous : open_definitions) {
2,533✔
332
        for (auto& user : open_definitions_for) {
8,752✔
333
            if (this->closes(analysis_manager, *previous.first, *user.first, true)) {
8,752✔
334
                to_close.insert(previous.first);
×
335
                break;
×
336
            }
×
337
        }
8,752✔
338
    }
2,533✔
339
    for (auto& user : to_close) {
674✔
340
        closed_definitions.insert({user, open_definitions.at(user)});
×
341
        open_definitions.erase(user);
×
342
    }
×
343

344
    // Add loop-carried dependencies
345
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
674✔
346
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
674✔
347
    if (this->detailed_ && is_monotonic) {
674✔
348
        // Case: Can analyze
349
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
537✔
350
        assert(success);
537✔
351
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
537✔
352

353
        // We can focus on written containers
354

355
        // Case 1: Read-Write between iterations
356
        for (auto& read : undefined_for) {
5,561✔
357
            for (auto& write : open_definitions_for) {
25,752✔
358
                if (loop_depends(*write.first, *read, analysis_manager, for_loop)) {
25,752✔
359
                    dependencies[read->container()] = LOOP_CARRIED_DEPENDENCY_READ_WRITE;
336✔
360
                    write.second.insert(read);
336✔
361
                }
336✔
362
            }
25,752✔
363
        }
5,561✔
364

365
        // Case 2: Write-Write between iterations
366
        for (auto& write : open_definitions_for) {
2,050✔
367
            if (dependencies.find(write.first->container()) != dependencies.end()) {
2,050✔
368
                continue;
779✔
369
            }
779✔
370
            for (auto& write_2 : open_definitions_for) {
5,078✔
371
                if (loop_depends(*write.first, *write_2.first, analysis_manager, for_loop)) {
5,078✔
372
                    dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
821✔
373
                    break;
821✔
374
                }
821✔
375
            }
5,078✔
376
        }
1,271✔
377
    } else {
537✔
378
        // Case: Cannot analyze
379
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
137✔
380
        assert(success);
137✔
381
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
137✔
382

383
        // Over-Approximation:
384
        // Add loop-carried dependencies for all open reads to all open writes
385
        for (auto& read : undefined_for) {
1,487✔
386
            for (auto& write : open_definitions_for) {
7,103✔
387
                if (this->depends(analysis_manager, *write.first, *read)) {
7,103✔
388
                    write.second.insert(read);
232✔
389
                    dependencies.insert({read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
232✔
390
                }
232✔
391
            }
7,103✔
392
        }
1,487✔
393
        // Add loop-carried dependencies for writes
394
        for (auto& write : open_definitions_for) {
539✔
395
            if (dependencies.find(write.first->container()) == dependencies.end()) {
539✔
396
                dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
245✔
397
            }
245✔
398
        }
539✔
399
    }
137✔
400

401
    // Add open definitions from for to outside
402
    for (auto& entry : open_definitions_for) {
2,589✔
403
        open_definitions.insert(entry);
2,589✔
404
    }
2,589✔
405
}
674✔
406

407
void DataDependencyAnalysis::visit_if_else(
408
    analysis::AnalysisManager& analysis_manager,
409
    structured_control_flow::IfElse& if_else,
410
    std::unordered_set<User*>& undefined,
411
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
412
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
413
) {
23✔
414
    auto& users = analysis_manager.get<analysis::Users>();
23✔
415

416
    // Read Conditions
417
    for (size_t i = 0; i < if_else.size(); i++) {
62✔
418
        auto child = if_else.at(i).second;
39✔
419
        for (auto atom : symbolic::atoms(child)) {
39✔
420
            auto current_user = users.get_user(atom->get_name(), &if_else, Use::READ);
28✔
421

422
            bool found_user = false;
28✔
423
            bool found_undefined_user = false;
28✔
424
            for (auto& user : open_definitions) {
28✔
425
                if (this->depends(analysis_manager, *user.first, *current_user)) {
18✔
426
                    user.second.insert(current_user);
9✔
427
                    found_user = true;
9✔
428
                    found_undefined_user = this->is_undefined_user(*user.first);
9✔
429
                }
9✔
430
            }
18✔
431
            // If no definition found or undefined user found, mark as undefined
432
            if (!found_user || found_undefined_user) {
28✔
433
                undefined.insert(current_user);
19✔
434
            }
19✔
435
        }
28✔
436
    }
39✔
437

438
    std::vector<std::unordered_set<User*>> undefined_branches(if_else.size());
23✔
439
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> open_definitions_branches(if_else.size());
23✔
440
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> closed_definitionss_branches(if_else.size());
23✔
441
    for (size_t i = 0; i < if_else.size(); i++) {
62✔
442
        auto& child = if_else.at(i).first;
39✔
443
        visit_sequence(
39✔
444
            analysis_manager,
39✔
445
            child,
39✔
446
            undefined_branches.at(i),
39✔
447
            open_definitions_branches.at(i),
39✔
448
            closed_definitionss_branches.at(i)
39✔
449
        );
39✔
450
    }
39✔
451

452
    // merge partial open reads
453
    for (size_t i = 0; i < if_else.size(); i++) {
62✔
454
        for (auto& entry : undefined_branches.at(i)) {
39✔
455
            bool found_user = false;
7✔
456
            bool found_undefined_user = false;
7✔
457
            for (auto& user : open_definitions) {
10✔
458
                if (this->depends(analysis_manager, *user.first, *entry)) {
10✔
459
                    user.second.insert(entry);
6✔
460
                    found_user = true;
6✔
461
                    found_undefined_user = this->is_undefined_user(*user.first);
6✔
462
                }
6✔
463
            }
10✔
464
            // If no definition found or undefined user found, mark as undefined
465
            if (!found_user || found_undefined_user) {
7✔
466
                undefined.insert(entry);
1✔
467
            }
1✔
468
        }
7✔
469
    }
39✔
470

471
    // merge closed writes
472
    for (auto& closing : closed_definitionss_branches) {
39✔
473
        for (auto& entry : closing) {
39✔
474
            closed_definitions.insert(entry);
×
475
        }
×
476
    }
39✔
477

478
    // Close open reads_after_writes for complete branches
479
    if (if_else.is_complete()) {
23✔
480
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
16✔
481
        std::unordered_set<std::string> candidates;
16✔
482
        std::unordered_set<std::string> candidates_tmp;
16✔
483

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

497
        for (size_t i = 1; i < if_else.size(); i++) {
32✔
498
            for (auto& entry : open_definitions_branches.at(i)) {
16✔
499
                if (candidates.find(entry.first->container()) != candidates.end()) {
13✔
500
                    candidates_tmp.insert(entry.first->container());
13✔
501
                }
13✔
502
            }
13✔
503
            candidates.swap(candidates_tmp);
16✔
504
            candidates_tmp.clear();
16✔
505
        }
16✔
506

507
        for (auto& entry : open_definitions) {
16✔
508
            if (candidates.find(entry.first->container()) != candidates.end()) {
8✔
509
                to_close.insert(entry);
4✔
510
            }
4✔
511
        }
8✔
512

513
        for (auto& entry : to_close) {
16✔
514
            open_definitions.erase(entry.first);
4✔
515
            closed_definitions.insert(entry);
4✔
516
        }
4✔
517
    } else {
16✔
518
        // Incomplete if-else
519

520
        // In order to determine whether a new read is undefined
521
        // we would need to check whether all open definitions
522
        // jointly dominate the read.
523
        // Since this is expensive, we apply a trick:
524
        // For incomplete if-elses and any newly opened definition in
525
        // any branch, we add an artificial undefined user for that container.
526
        // If we encounter this user later, we know that not all branches defined it.
527
        // Hence, we can mark the read as (partially) undefined.
528

529
        for (auto& branch : open_definitions_branches) {
7✔
530
            for (auto& open_definition : branch) {
7✔
531
                auto write = open_definition.first;
5✔
532
                auto artificial_user = std::make_unique<
5✔
533
                    User>(boost::graph_traits<graph::Graph>::null_vertex(), write->container(), nullptr, Use::WRITE);
5✔
534
                this->undefined_users_.push_back(std::move(artificial_user));
5✔
535
                open_definitions.insert({this->undefined_users_.back().get(), {}});
5✔
536
            }
5✔
537
        }
7✔
538
    }
7✔
539

540
    // Add open definitions from branches to outside
541
    for (auto& branch : open_definitions_branches) {
39✔
542
        for (auto& entry : branch) {
39✔
543
            open_definitions.insert(entry);
33✔
544
        }
33✔
545
    }
39✔
546
}
23✔
547

548
void DataDependencyAnalysis::visit_while(
549
    analysis::AnalysisManager& analysis_manager,
550
    structured_control_flow::While& while_loop,
551
    std::unordered_set<User*>& undefined,
552
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
553
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
554
) {
8✔
555
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_while;
8✔
556
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_while;
8✔
557
    std::unordered_set<User*> undefined_while;
8✔
558

559
    visit_sequence(analysis_manager, while_loop.root(), undefined_while, open_definitions_while, closed_definitions_while);
8✔
560

561
    // Scope-local closed definitions
562
    for (auto& entry : closed_definitions_while) {
8✔
563
        closed_definitions.insert(entry);
2✔
564
    }
2✔
565

566
    for (auto open_read : undefined_while) {
8✔
567
        // Over-Approximation: Add loop-carried dependencies for all open reads
568
        for (auto& entry : open_definitions_while) {
4✔
569
            if (entry.first->container() == open_read->container()) {
4✔
570
                entry.second.insert(open_read);
2✔
571
            }
2✔
572
        }
4✔
573

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

587
    // Add open definitions from while to outside
588
    for (auto& entry : open_definitions_while) {
13✔
589
        open_definitions.insert(entry);
13✔
590
    }
13✔
591
}
8✔
592

593
void DataDependencyAnalysis::visit_return(
594
    analysis::AnalysisManager& analysis_manager,
595
    structured_control_flow::Return& return_statement,
596
    std::unordered_set<User*>& undefined,
597
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
598
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
UNCOV
599
) {
×
UNCOV
600
    auto& users = analysis_manager.get<analysis::Users>();
×
601

UNCOV
602
    if (return_statement.is_data() && !return_statement.data().empty()) {
×
UNCOV
603
        auto current_user = users.get_user(return_statement.data(), &return_statement, Use::READ);
×
604

UNCOV
605
        bool found = false;
×
UNCOV
606
        for (auto& user : open_definitions) {
×
UNCOV
607
            if (user.first->container() == return_statement.data()) {
×
UNCOV
608
                user.second.insert(current_user);
×
UNCOV
609
                found = true;
×
UNCOV
610
            }
×
UNCOV
611
        }
×
UNCOV
612
        if (!found) {
×
613
            undefined.insert(current_user);
×
614
        }
×
UNCOV
615
    }
×
616

617
    // close all open reads_after_writes
UNCOV
618
    for (auto& entry : open_definitions) {
×
UNCOV
619
        closed_definitions.insert(entry);
×
UNCOV
620
    }
×
UNCOV
621
    open_definitions.clear();
×
UNCOV
622
}
×
623

624
void DataDependencyAnalysis::visit_sequence(
625
    analysis::AnalysisManager& analysis_manager,
626
    structured_control_flow::Sequence& sequence,
627
    std::unordered_set<User*>& undefined,
628
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
629
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
630
) {
967✔
631
    auto& users = analysis_manager.get<analysis::Users>();
967✔
632

633
    for (size_t i = 0; i < sequence.size(); i++) {
2,460✔
634
        auto child = sequence.at(i);
1,493✔
635
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
1,493✔
636
            visit_block(analysis_manager, *block, undefined, open_definitions, closed_definitions);
780✔
637
        } else if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
780✔
638
            visit_for(analysis_manager, *for_loop, undefined, open_definitions, closed_definitions);
674✔
639
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
674✔
640
            visit_if_else(analysis_manager, *if_else, undefined, open_definitions, closed_definitions);
23✔
641
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
23✔
642
            visit_while(analysis_manager, *while_loop, undefined, open_definitions, closed_definitions);
8✔
643
        } else if (auto return_statement = dynamic_cast<structured_control_flow::Return*>(&child.first)) {
8✔
UNCOV
644
            visit_return(analysis_manager, *return_statement, undefined, open_definitions, closed_definitions);
×
645
        } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&child.first)) {
8✔
646
            visit_sequence(analysis_manager, *sequence, undefined, open_definitions, closed_definitions);
×
647
        }
×
648

649
        // handle transitions read
650
        for (auto& entry : child.second.assignments()) {
1,493✔
651
            for (auto& atom : symbolic::atoms(entry.second)) {
107✔
652
                if (symbolic::is_pointer(atom)) {
46✔
653
                    continue;
×
654
                }
×
655
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
46✔
656

657
                bool found = false;
46✔
658
                for (auto& user : open_definitions) {
46✔
659
                    if (user.first->container() == atom->get_name()) {
45✔
660
                        user.second.insert(current_user);
39✔
661
                        found = true;
39✔
662
                    }
39✔
663
                }
45✔
664
                if (!found) {
46✔
665
                    undefined.insert(current_user);
17✔
666
                }
17✔
667
            }
46✔
668
        }
107✔
669

670
        // handle transitions write
671
        for (auto& entry : child.second.assignments()) {
1,493✔
672
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
107✔
673

674
            std::unordered_set<User*> to_close;
107✔
675
            for (auto& user : open_definitions) {
107✔
676
                if (this->closes(analysis_manager, *user.first, *current_user, true)) {
52✔
677
                    to_close.insert(user.first);
3✔
678
                }
3✔
679
            }
52✔
680
            for (auto& user : to_close) {
107✔
681
                closed_definitions.insert({user, open_definitions.at(user)});
3✔
682
                open_definitions.erase(user);
3✔
683
            }
3✔
684
            open_definitions.insert({current_user, {}});
107✔
685
        }
107✔
686
    }
1,493✔
687
}
967✔
688

689
bool DataDependencyAnalysis::loop_depends(
690
    User& previous,
691
    User& current,
692
    analysis::AnalysisManager& analysis_manager,
693
    structured_control_flow::StructuredLoop& loop
694
) {
30,830✔
695
    if (previous.container() != current.container()) {
30,830✔
696
        return false;
28,649✔
697
    }
28,649✔
698
    // Shortcut for scalars
699
    auto& type = this->sdfg_.type(previous.container());
2,181✔
700
    if (dynamic_cast<const types::Scalar*>(&type)) {
2,181✔
701
        return true;
828✔
702
    }
828✔
703

704
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
1,353✔
705
        return false;
4✔
706
    }
4✔
707

708
    auto& previous_subsets = previous.subsets();
1,349✔
709
    auto& current_subsets = current.subsets();
1,349✔
710

711
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
1,349✔
712
    auto previous_scope = Users::scope(&previous);
1,349✔
713
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
1,349✔
714
    auto current_scope = Users::scope(&current);
1,349✔
715
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
1,349✔
716

717
    // We're using the assumptions from the blocks, where the memory accesses occur
718
    // However, we need to revert constantness assumptions from the perspective of the loop
719
    // for which we're checking loop-carried dependencies
720
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
1,349✔
721
    if (previous_assumptions.find(loop.indvar()) != previous_assumptions.end()) {
1,349✔
722
        previous_assumptions.at(loop.indvar()).constant(false);
1,349✔
723
    }
1,349✔
724
    if (current_assumptions.find(loop.indvar()) != current_assumptions.end()) {
1,349✔
725
        current_assumptions.at(loop.indvar()).constant(false);
1,349✔
726
    }
1,349✔
727
    for (auto& inner_loop : loop_analysis.descendants(&loop)) {
2,266✔
728
        if (auto structured_loop = dynamic_cast<const structured_control_flow::StructuredLoop*>(inner_loop)) {
2,266✔
729
            auto indvar = structured_loop->indvar();
2,266✔
730
            if (previous_assumptions.find(indvar) != previous_assumptions.end()) {
2,266✔
731
                previous_assumptions.at(indvar).constant(false);
2,266✔
732
            }
2,266✔
733
            if (current_assumptions.find(indvar) != current_assumptions.end()) {
2,266✔
734
                current_assumptions.at(indvar).constant(false);
2,266✔
735
            }
2,266✔
736
        }
2,266✔
737
    }
2,266✔
738

739
    // Check if previous subset is subset of any current subset
740
    for (auto& previous_subset : previous_subsets) {
1,349✔
741
        for (auto& current_subset : current_subsets) {
1,388✔
742
            if (symbolic::maps::intersects(
1,388✔
743
                    previous_subset, current_subset, loop.indvar(), previous_assumptions, current_assumptions
1,388✔
744
                )) {
1,388✔
745
                return true;
329✔
746
            }
329✔
747
        }
1,388✔
748
    }
1,349✔
749

750
    return false;
1,020✔
751
}
1,349✔
752

753
bool DataDependencyAnalysis::
754
    supersedes_restrictive(User& previous, User& current, analysis::AnalysisManager& analysis_manager) {
3,109✔
755
    if (previous.container() != current.container()) {
3,109✔
756
        return false;
×
757
    }
×
758
    // Shortcut for scalars
759
    auto& type = this->sdfg_.type(previous.container());
3,109✔
760
    if (dynamic_cast<const types::Scalar*>(&type)) {
3,109✔
761
        return true;
3,042✔
762
    }
3,042✔
763

764
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
67✔
765
        return false;
×
766
    }
×
767

768
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
67✔
769
    auto& previous_subsets = previous.subsets();
67✔
770
    auto& current_subsets = current.subsets();
67✔
771
    auto previous_scope = Users::scope(&previous);
67✔
772
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
67✔
773
    auto current_scope = Users::scope(&current);
67✔
774
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
67✔
775

776
    // Check if previous subset is subset of any current subset
777
    for (auto& previous_subset : previous_subsets) {
67✔
778
        bool found = false;
67✔
779
        for (auto& current_subset : current_subsets) {
67✔
780
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, previous_assumptions)) {
67✔
781
                found = true;
66✔
782
                break;
66✔
783
            }
66✔
784
        }
67✔
785
        if (!found) {
67✔
786
            return false;
1✔
787
        }
1✔
788
    }
67✔
789

790
    return true;
66✔
791
}
67✔
792

793
bool DataDependencyAnalysis::intersects(User& previous, User& current, analysis::AnalysisManager& analysis_manager) {
24,854✔
794
    if (previous.container() != current.container()) {
24,854✔
795
        return false;
18,487✔
796
    }
18,487✔
797
    // Shortcut for scalars
798
    auto& type = this->sdfg_.type(previous.container());
6,367✔
799
    if (dynamic_cast<const types::Scalar*>(&type)) {
6,367✔
800
        return true;
6,074✔
801
    }
6,074✔
802

803
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
293✔
804
        return true;
×
805
    }
×
806

807
    if (!this->detailed_) {
293✔
808
        return true;
66✔
809
    }
66✔
810

811
    auto& previous_subsets = previous.subsets();
227✔
812
    auto& current_subsets = current.subsets();
227✔
813

814
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
227✔
815
    auto previous_scope = Users::scope(&previous);
227✔
816
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
227✔
817
    auto current_scope = Users::scope(&current);
227✔
818
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
227✔
819

820
    // Check if any current subset intersects with any previous subset
821
    bool found = false;
227✔
822
    for (auto& current_subset : current_subsets) {
235✔
823
        for (auto& previous_subset : previous_subsets) {
235✔
824
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
235✔
825
                found = true;
212✔
826
                break;
212✔
827
            }
212✔
828
        }
235✔
829
        if (found) {
235✔
830
            break;
212✔
831
        }
212✔
832
    }
235✔
833

834
    return found;
227✔
835
}
293✔
836

837
bool DataDependencyAnalysis::
838
    closes(analysis::AnalysisManager& analysis_manager, User& previous, User& current, bool requires_dominance) {
10,623✔
839
    if (previous.container() != current.container()) {
10,623✔
840
        return false;
10,348✔
841
    }
10,348✔
842

843
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
275✔
844
        return false;
×
845
    }
×
846

847
    // Check dominance
848
    if (requires_dominance) {
275✔
849
        auto& dominance_analysis = analysis_manager.get<analysis::DominanceAnalysis>();
183✔
850
        if (!dominance_analysis.post_dominates(current, previous)) {
183✔
851
            return false;
178✔
852
        }
178✔
853
    }
183✔
854

855
    // Previous memlets are subsets of current memlets
856
    auto& type = sdfg_.type(previous.container());
97✔
857
    if (type.type_id() == types::TypeID::Scalar) {
97✔
858
        return true;
18✔
859
    }
18✔
860

861
    if (!this->detailed_) {
79✔
862
        return false;
16✔
863
    }
16✔
864

865
    // Collect memlets and assumptions
866
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
63✔
867
    auto previous_scope = Users::scope(&previous);
63✔
868
    auto current_scope = Users::scope(&current);
63✔
869
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
63✔
870
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
63✔
871

872
    auto& previous_memlets = previous.subsets();
63✔
873
    auto& current_memlets = current.subsets();
63✔
874

875
    for (auto& subset_ : previous_memlets) {
63✔
876
        bool overwritten = false;
63✔
877
        for (auto& subset : current_memlets) {
63✔
878
            if (symbolic::is_subset(subset_, subset, previous_assumptions, current_assumptions)) {
63✔
879
                overwritten = true;
52✔
880
                break;
52✔
881
            }
52✔
882
        }
63✔
883
        if (!overwritten) {
63✔
884
            return false;
11✔
885
        }
11✔
886
    }
63✔
887

888
    return true;
52✔
889
}
63✔
890

891
bool DataDependencyAnalysis::depends(analysis::AnalysisManager& analysis_manager, User& previous, User& current) {
17,680✔
892
    if (previous.container() != current.container()) {
17,680✔
893
        return false;
15,771✔
894
    }
15,771✔
895

896
    // Previous memlets are subsets of current memlets
897
    auto& type = sdfg_.type(previous.container());
1,909✔
898
    if (type.type_id() == types::TypeID::Scalar) {
1,909✔
899
        return true;
1,573✔
900
    }
1,573✔
901

902
    if (this->is_undefined_user(previous)) {
336✔
903
        return true;
×
904
    }
×
905

906
    if (!this->detailed_) {
336✔
907
        return true;
252✔
908
    }
252✔
909

910
    // Collect memlets and assumptions
911
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
84✔
912
    auto previous_scope = Users::scope(&previous);
84✔
913
    auto current_scope = Users::scope(&current);
84✔
914
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
84✔
915
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
84✔
916

917
    auto& previous_memlets = previous.subsets();
84✔
918
    auto& current_memlets = current.subsets();
84✔
919

920
    bool intersect_any = false;
84✔
921
    for (auto& current_subset : current_memlets) {
84✔
922
        for (auto& previous_subset : previous_memlets) {
84✔
923
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
84✔
924
                intersect_any = true;
73✔
925
                break;
73✔
926
            }
73✔
927
        }
84✔
928
        if (intersect_any) {
84✔
929
            break;
73✔
930
        }
73✔
931
    }
84✔
932

933
    return intersect_any;
84✔
934
}
336✔
935

936
/****** Public API ******/
937

938
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
939
    assert(write.use() == Use::WRITE);
×
940
    if (results_.find(write.container()) == results_.end()) {
×
941
        return {};
×
942
    }
×
943
    auto& raws = results_.at(write.container());
×
944
    assert(raws.find(&write) != raws.end());
×
945

946
    auto& reads_for_write = raws.at(&write);
×
947

948
    std::unordered_set<User*> reads;
×
949
    for (auto& entry : reads_for_write) {
×
950
        reads.insert(entry);
×
951
    }
×
952

953
    return reads;
×
954
};
×
955

956
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(const std::string& container) {
579✔
957
    if (results_.find(container) == results_.end()) {
579✔
958
        return {};
2✔
959
    }
2✔
960
    return results_.at(container);
577✔
961
};
579✔
962

963
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::defined_by(const std::string& container) {
259✔
964
    auto reads = this->definitions(container);
259✔
965

966
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
259✔
967
    for (auto& entry : reads) {
474✔
968
        for (auto& read : entry.second) {
2,471✔
969
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
2,471✔
970
                read_to_writes_map[read] = {};
1,248✔
971
            }
1,248✔
972
            read_to_writes_map[read].insert(entry.first);
2,471✔
973
        }
2,471✔
974
    }
474✔
975
    return read_to_writes_map;
259✔
976
};
259✔
977

978
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
979
    assert(read.use() == Use::READ);
×
980
    auto definitions = this->definitions(read.container());
×
981

982
    std::unordered_set<User*> writes;
×
983
    for (auto& entry : definitions) {
×
984
        for (auto& r : entry.second) {
×
985
            if (&read == r) {
×
986
                writes.insert(entry.first);
×
987
            }
×
988
        }
×
989
    }
×
990
    return writes;
×
991
};
×
992

993
bool DataDependencyAnalysis::is_undefined_user(User& user) const {
14,805✔
994
    return user.vertex_ == boost::graph_traits<graph::Graph>::null_vertex();
14,805✔
995
};
14,805✔
996

997
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
998
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
999
};
×
1000

1001
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::
1002
    dependencies(structured_control_flow::StructuredLoop& loop) const {
278✔
1003
    return this->loop_carried_dependencies_.at(&loop);
278✔
1004
};
278✔
1005

1006
} // namespace analysis
1007
} // 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