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

daisytuner / sdfglib / 20770413849

06 Jan 2026 10:50PM UTC coverage: 62.168% (+21.4%) from 40.764%
20770413849

push

github

web-flow
Merge pull request #433 from daisytuner/clang-coverage

updates clang coverage flags

14988 of 24109 relevant lines covered (62.17%)

88.57 hits per line

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

85.13
/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) {
110✔
20

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

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

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

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

43
    for (auto& entry : closed_definitions) {
261✔
44
        if (results_.find(entry.first->container()) == results_.end()) {
261✔
45
            results_.insert({entry.first->container(), {}});
177✔
46
        }
177✔
47
        results_.at(entry.first->container()).insert(entry);
261✔
48
    }
261✔
49
};
85✔
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
) {
202✔
60
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
202✔
61
    auto& dominance_analysis = analysis_manager.get<analysis::DominanceAnalysis>();
202✔
62
    auto& users = analysis_manager.get<analysis::Users>();
202✔
63

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

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

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

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

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

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

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

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

136
                // Assign to open definitions
137
                bool found_user = false;
×
138
                bool found_undefined_user = false;
×
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
147
                if (!found_user || found_undefined_user) {
×
148
                    undefined.insert(current_user);
×
149
                }
×
150
            }
×
151
        }
×
152

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

163
                // Assign to open definitions
164
                bool found_user = false;
102✔
165
                bool found_undefined_user = false;
102✔
166
                for (auto& user : open_definitions) {
102✔
167
                    if (this->depends(analysis_manager, *user.first, *current_user)) {
29✔
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
                }
29✔
173
                // If no definition found or undefined user found, mark as undefined
174
                if (!found_user || found_undefined_user) {
102✔
175
                    undefined.insert(current_user);
96✔
176
                }
96✔
177
            }
102✔
178
        }
154✔
179
    }
240✔
180
}
202✔
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
) {
61✔
189
    auto& users = analysis_manager.get<analysis::Users>();
61✔
190

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

195
        // Assign to open definitions
196
        bool found_user = false;
5✔
197
        bool found_undefined_user = false;
5✔
198
        for (auto& user : open_definitions) {
5✔
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) {
5✔
207
            undefined.insert(current_user);
4✔
208
        }
4✔
209
    }
5✔
210

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

216
        // Close open definitions if possible
217
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
61✔
218
        for (auto& user : open_definitions) {
61✔
219
            if (this->closes(analysis_manager, *user.first, *current_user, true)) {
15✔
220
                to_close.insert(user);
2✔
221
            }
2✔
222
        }
15✔
223
        for (auto& user : to_close) {
61✔
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, {}});
61✔
230
    }
61✔
231

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

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

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

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

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

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

269
        // Assign to open definitions
270
        bool found_user = false;
61✔
271
        bool found_undefined_user = false;
61✔
272
        for (auto& user : open_definitions_for) {
95✔
273
            if (this->depends(analysis_manager, *user.first, *current_user)) {
95✔
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
        }
95✔
279
        // If no definition found or undefined user found, mark as undefined
280
        if (!found_user || found_undefined_user) {
61✔
281
            undefined_for.insert(current_user);
60✔
282
        }
60✔
283
    }
61✔
284

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

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

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

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

329
    // Open definitions may close outside open definitions after loop
330
    std::unordered_set<User*> to_close;
61✔
331
    for (auto& previous : open_definitions) {
135✔
332
        for (auto& user : open_definitions_for) {
204✔
333
            if (this->closes(analysis_manager, *previous.first, *user.first, true)) {
204✔
334
                to_close.insert(previous.first);
×
335
                break;
×
336
            }
×
337
        }
204✔
338
    }
135✔
339
    for (auto& user : to_close) {
61✔
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>();
61✔
346
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
61✔
347
    if (this->detailed_ && is_monotonic) {
61✔
348
        // Case: Can analyze
349
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
56✔
350
        assert(success);
56✔
351
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
56✔
352

353
        // We can focus on written containers
354

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

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

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

401
    // Add open definitions from for to outside
402
    for (auto& entry : open_definitions_for) {
95✔
403
        open_definitions.insert(entry);
95✔
404
    }
95✔
405
}
61✔
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
599
) {
×
600
    auto& users = analysis_manager.get<analysis::Users>();
×
601

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

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

617
    // close all open reads_after_writes
618
    for (auto& entry : open_definitions) {
×
619
        closed_definitions.insert(entry);
×
620
    }
×
621
    open_definitions.clear();
×
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
) {
210✔
631
    auto& users = analysis_manager.get<analysis::Users>();
210✔
632

633
    for (size_t i = 0; i < sequence.size(); i++) {
504✔
634
        auto child = sequence.at(i);
294✔
635
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
294✔
636
            visit_block(analysis_manager, *block, undefined, open_definitions, closed_definitions);
194✔
637
        } else if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
194✔
638
            visit_for(analysis_manager, *for_loop, undefined, open_definitions, closed_definitions);
61✔
639
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
61✔
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✔
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()) {
294✔
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()) {
294✔
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
    }
294✔
687
}
210✔
688

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

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

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

711
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
77✔
712
    auto previous_scope = Users::scope(&previous);
77✔
713
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
77✔
714
    auto current_scope = Users::scope(&current);
77✔
715
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
77✔
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>();
77✔
721
    if (previous_assumptions.find(loop.indvar()) != previous_assumptions.end()) {
77✔
722
        previous_assumptions.at(loop.indvar()).constant(false);
77✔
723
    }
77✔
724
    if (current_assumptions.find(loop.indvar()) != current_assumptions.end()) {
77✔
725
        current_assumptions.at(loop.indvar()).constant(false);
77✔
726
    }
77✔
727
    for (auto& inner_loop : loop_analysis.descendants(&loop)) {
77✔
728
        if (auto structured_loop = dynamic_cast<const structured_control_flow::StructuredLoop*>(inner_loop)) {
20✔
729
            auto indvar = structured_loop->indvar();
20✔
730
            if (previous_assumptions.find(indvar) != previous_assumptions.end()) {
20✔
731
                previous_assumptions.at(indvar).constant(false);
20✔
732
            }
20✔
733
            if (current_assumptions.find(indvar) != current_assumptions.end()) {
20✔
734
                current_assumptions.at(indvar).constant(false);
20✔
735
            }
20✔
736
        }
20✔
737
    }
20✔
738

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

750
    return false;
62✔
751
}
77✔
752

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

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

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

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

790
    return true;
2✔
791
}
3✔
792

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

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

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

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

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

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

834
    return found;
5✔
835
}
5✔
836

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

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

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

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

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

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

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

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

888
    return true;
4✔
889
}
10✔
890

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

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

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

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

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

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

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

933
    return intersect_any;
11✔
934
}
13✔
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) {
59✔
957
    if (results_.find(container) == results_.end()) {
59✔
958
        return {};
×
959
    }
×
960
    return results_.at(container);
59✔
961
};
59✔
962

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

966
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
45✔
967
    for (auto& entry : reads) {
50✔
968
        for (auto& read : entry.second) {
50✔
969
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
39✔
970
                read_to_writes_map[read] = {};
32✔
971
            }
32✔
972
            read_to_writes_map[read].insert(entry.first);
39✔
973
        }
39✔
974
    }
50✔
975
    return read_to_writes_map;
45✔
976
};
45✔
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 {
775✔
994
    return user.vertex_ == boost::graph_traits<graph::Graph>::null_vertex();
775✔
995
};
775✔
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 {
48✔
1003
    return this->loop_carried_dependencies_.at(&loop);
48✔
1004
};
48✔
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