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

daisytuner / sdfglib / 19224500338

10 Nov 2025 07:48AM UTC coverage: 61.286% (+0.1%) from 61.165%
19224500338

push

github

web-flow
Merge pull request #332 from daisytuner/constant-elimination

handle previous definitions before constants in elimination pass

14 of 19 new or added lines in 1 file covered. (73.68%)

9 existing lines in 4 files now uncovered.

10274 of 16764 relevant lines covered (61.29%)

101.34 hits per line

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

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

21
      };
100✔
22

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

26
      };
×
27

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

295
    // Merge for with outside
296

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

302
    // Undefined reads are matched or forwarded
303
    for (auto open_read : undefined_for) {
469✔
304
        bool found = false;
403✔
305
        for (auto& entry : open_definitions) {
1,426✔
306
            if (intersects(*entry.first, *open_read, assumptions_analysis)) {
1,023✔
307
                entry.second.insert(open_read);
404✔
308
                found = true;
404✔
309
            }
404✔
310
        }
311
        if (!found) {
403✔
312
            undefined.insert(open_read);
200✔
313
        } else {
200✔
314
            bool subset_all = true;
203✔
315
            for (auto& entry : open_definitions) {
677✔
316
                if (entry.first->container() == open_read->container()) {
474✔
317
                    subset_all &= supersedes_restrictive(*open_read, *entry.first, assumptions_analysis);
404✔
318
                }
404✔
319
            }
320
            if (!subset_all) {
203✔
321
                undefined.insert(open_read);
1✔
322
            }
1✔
323
        }
324
    }
325

326
    // Open definitions may close outside open definitions after loop
327
    std::unordered_set<User*> to_close;
66✔
328
    for (auto& previous : open_definitions) {
216✔
329
        for (auto& user : open_definitions_for) {
436✔
330
            if (this->closes(assumptions_analysis, dominance_analysis, *previous.first, *user.first, false)) {
288✔
331
                to_close.insert(previous.first);
2✔
332
                break;
2✔
333
            }
334
        }
335
    }
336
    for (auto& user : to_close) {
68✔
337
        closed_definitions.insert({user, open_definitions.at(user)});
2✔
338
        open_definitions.erase(user);
2✔
339
    }
340

341
    // Add loop-carried dependencies
342

343
    // Criterion 1: Loop is monotonic -> indvar never takes the same value twice
344
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
66✔
345
    if (is_monotonic) {
66✔
346
        // Case: Can analyze
347
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
65✔
348
        assert(success);
65✔
349
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
65✔
350

351
        // We can focus on written containers
352

353
        // Case 1: Read-Write between iterations
354
        for (auto& read : undefined_for) {
467✔
355
            for (auto& write : open_definitions_for) {
2,069✔
356
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop)) {
1,667✔
357
                    dependencies[read->container()] = LOOP_CARRIED_DEPENDENCY_READ_WRITE;
41✔
358
                    write.second.insert(read);
41✔
359
                }
41✔
360
            }
361
        }
362

363
        // Case 2: Write-Write between iterations
364
        for (auto& write : open_definitions_for) {
189✔
365
            if (dependencies.find(write.first->container()) != dependencies.end()) {
124✔
366
                continue;
40✔
367
            }
368
            for (auto& write_2 : open_definitions_for) {
256✔
369
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis, for_loop)) {
213✔
370
                    dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
41✔
371
                    break;
41✔
372
                }
373
            }
374
        }
375
    } else {
65✔
376
        // Case: Cannot analyze
377
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
1✔
378
        assert(success);
1✔
379
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
1✔
380

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

399
    // Add open definitions from for to outside
400
    for (auto& entry : open_definitions_for) {
191✔
401
        open_definitions.insert(entry);
125✔
402
    }
403
}
66✔
404

405
void DataDependencyAnalysis::visit_if_else(
22✔
406
    analysis::Users& users,
407
    analysis::AssumptionsAnalysis& assumptions_analysis,
408
    analysis::DominanceAnalysis& dominance_analysis,
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
) {
414
    // Read Conditions
415
    for (size_t i = 0; i < if_else.size(); i++) {
60✔
416
        auto child = if_else.at(i).second;
38✔
417
        for (auto atom : symbolic::atoms(child)) {
63✔
418
            auto current_user = users.get_user(atom->get_name(), &if_else, Use::READ);
25✔
419

420
            bool found_user = false;
25✔
421
            bool found_undefined_user = false;
25✔
422
            for (auto& user : open_definitions) {
41✔
423
                if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
16✔
424
                    user.second.insert(current_user);
8✔
425
                    found_user = true;
8✔
426
                    found_undefined_user = this->is_undefined_user(*user.first);
8✔
427
                }
8✔
428
            }
429
            // If no definition found or undefined user found, mark as undefined
430
            if (!found_user || found_undefined_user) {
25✔
431
                undefined.insert(current_user);
17✔
432
            }
17✔
433
        }
25✔
434
    }
38✔
435

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

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

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

478
    // Close open reads_after_writes for complete branches
479
    if (if_else.is_complete()) {
22✔
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)) {
31✔
491
            candidates.insert(entry.first->container());
15✔
492
        }
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)) {
30✔
499
                if (candidates.find(entry.first->container()) != candidates.end()) {
14✔
500
                    candidates_tmp.insert(entry.first->container());
14✔
501
                }
14✔
502
            }
503
            candidates.swap(candidates_tmp);
16✔
504
            candidates_tmp.clear();
16✔
505
        }
16✔
506

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

513
        for (auto& entry : to_close) {
20✔
514
            open_definitions.erase(entry.first);
4✔
515
            closed_definitions.insert(entry);
4✔
516
        }
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) {
12✔
530
            for (auto& open_definition : branch) {
10✔
531
                auto write = open_definition.first;
4✔
532
                auto artificial_user = std::make_unique<
4✔
533
                    User>(boost::graph_traits<graph::Graph>::null_vertex(), write->container(), nullptr, Use::WRITE);
4✔
534
                this->undefined_users_.push_back(std::move(artificial_user));
4✔
535
                open_definitions.insert({this->undefined_users_.back().get(), {}});
4✔
536
            }
4✔
537
        }
538
    }
539

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

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

561
    visit_sequence(
8✔
562
        users,
8✔
563
        assumptions_analysis,
8✔
564
        dominance_analysis,
8✔
565
        while_loop.root(),
8✔
566
        undefined_while,
567
        open_definitions_while,
568
        closed_definitions_while
569
    );
570

571
    // Scope-local closed definitions
572
    for (auto& entry : closed_definitions_while) {
10✔
573
        closed_definitions.insert(entry);
2✔
574
    }
575

576
    for (auto open_read : undefined_while) {
11✔
577
        // Over-Approximation: Add loop-carried dependencies for all open reads
578
        for (auto& entry : open_definitions_while) {
7✔
579
            if (entry.first->container() == open_read->container()) {
4✔
580
                entry.second.insert(open_read);
2✔
581
            }
2✔
582
        }
583

584
        // Connect to outside
585
        bool found = false;
3✔
586
        for (auto& entry : open_definitions) {
6✔
587
            if (entry.first->container() == open_read->container()) {
3✔
588
                entry.second.insert(open_read);
2✔
589
                found = true;
2✔
590
            }
2✔
591
        }
592
        if (!found) {
3✔
593
            undefined.insert(open_read);
1✔
594
        }
1✔
595
    }
596

597
    // Add open definitions from while to outside
598
    for (auto& entry : open_definitions_while) {
21✔
599
        open_definitions.insert(entry);
13✔
600
    }
601
}
8✔
602

603
void DataDependencyAnalysis::visit_return(
×
604
    analysis::Users& users,
605
    analysis::AssumptionsAnalysis& assumptions_analysis,
606
    analysis::DominanceAnalysis& dominance_analysis,
607
    structured_control_flow::Return& return_statement,
608
    std::unordered_set<User*>& undefined,
609
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
610
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
611
) {
612
    if (return_statement.is_data() && !return_statement.data().empty()) {
×
613
        auto current_user = users.get_user(return_statement.data(), &return_statement, Use::READ);
×
614

615
        bool found = false;
×
616
        for (auto& user : open_definitions) {
×
617
            if (user.first->container() == return_statement.data()) {
×
618
                user.second.insert(current_user);
×
619
                found = true;
×
620
            }
×
621
        }
622
        if (!found) {
×
623
            undefined.insert(current_user);
×
624
        }
×
625
    }
×
626

627
    // close all open reads_after_writes
628
    for (auto& entry : open_definitions) {
×
629
        closed_definitions.insert(entry);
×
630
    }
631
    open_definitions.clear();
×
632
}
×
633

634
void DataDependencyAnalysis::visit_sequence(
205✔
635
    analysis::Users& users,
636
    analysis::AssumptionsAnalysis& assumptions_analysis,
637
    analysis::DominanceAnalysis& dominance_analysis,
638
    structured_control_flow::Sequence& sequence,
639
    std::unordered_set<User*>& undefined,
640
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
641
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
642
) {
643
    for (size_t i = 0; i < sequence.size(); i++) {
478✔
644
        auto child = sequence.at(i);
273✔
645
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
273✔
646
            visit_block(
169✔
647
                users, assumptions_analysis, dominance_analysis, *block, undefined, open_definitions, closed_definitions
169✔
648
            );
649
        } else if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
273✔
650
            visit_for(
66✔
651
                users,
66✔
652
                assumptions_analysis,
66✔
653
                dominance_analysis,
66✔
654
                *for_loop,
66✔
655
                undefined,
66✔
656
                open_definitions,
66✔
657
                closed_definitions
66✔
658
            );
659
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
104✔
660
            visit_if_else(
22✔
661
                users, assumptions_analysis, dominance_analysis, *if_else, undefined, open_definitions, closed_definitions
22✔
662
            );
663
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
38✔
664
            visit_while(
8✔
665
                users,
8✔
666
                assumptions_analysis,
8✔
667
                dominance_analysis,
8✔
668
                *while_loop,
8✔
669
                undefined,
8✔
670
                open_definitions,
8✔
671
                closed_definitions
8✔
672
            );
673
        } else if (auto return_statement = dynamic_cast<structured_control_flow::Return*>(&child.first)) {
16✔
674
            visit_return(
×
675
                users,
×
676
                assumptions_analysis,
×
677
                dominance_analysis,
×
678
                *return_statement,
×
679
                undefined,
×
680
                open_definitions,
×
681
                closed_definitions
×
682
            );
683
        } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&child.first)) {
8✔
684
            visit_sequence(
×
685
                users,
×
686
                assumptions_analysis,
×
687
                dominance_analysis,
×
688
                *sequence,
×
689
                undefined,
×
690
                open_definitions,
×
691
                closed_definitions
×
692
            );
693
        }
×
694

695
        // handle transitions read
696
        for (auto& entry : child.second.assignments()) {
353✔
697
            for (auto& atom : symbolic::atoms(entry.second)) {
112✔
698
                if (symbolic::is_pointer(atom)) {
32✔
UNCOV
699
                    continue;
×
700
                }
701
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
32✔
702

703
                bool found = false;
32✔
704
                for (auto& user : open_definitions) {
63✔
705
                    if (user.first->container() == atom->get_name()) {
31✔
706
                        user.second.insert(current_user);
28✔
707
                        found = true;
28✔
708
                    }
28✔
709
                }
710
                if (!found) {
32✔
711
                    undefined.insert(current_user);
14✔
712
                }
14✔
713
            }
714
        }
715

716
        // handle transitions write
717
        for (auto& entry : child.second.assignments()) {
353✔
718
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
80✔
719

720
            std::unordered_set<User*> to_close;
80✔
721
            for (auto& user : open_definitions) {
113✔
722
                if (user.first->container() == entry.first->get_name()) {
33✔
723
                    to_close.insert(user.first);
3✔
724
                }
3✔
725
            }
726
            for (auto& user : to_close) {
83✔
727
                closed_definitions.insert({user, open_definitions.at(user)});
3✔
728
                open_definitions.erase(user);
3✔
729
            }
730
            open_definitions.insert({current_user, {}});
80✔
731
        }
80✔
732
    }
273✔
733
}
205✔
734

735
bool DataDependencyAnalysis::loop_depends(
1,880✔
736
    User& previous,
737
    User& current,
738
    analysis::AssumptionsAnalysis& assumptions_analysis,
739
    structured_control_flow::StructuredLoop& loop
740
) {
741
    if (previous.container() != current.container()) {
1,880✔
742
        return false;
1,724✔
743
    }
744
    // Shortcut for scalars
745
    auto& type = this->sdfg_.type(previous.container());
156✔
746
    if (dynamic_cast<const types::Scalar*>(&type)) {
156✔
747
        return true;
41✔
748
    }
749

750
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
115✔
751
        return true;
×
752
    }
753

754
    auto& previous_subsets = previous.subsets();
115✔
755
    auto& current_subsets = current.subsets();
115✔
756

757
    auto previous_scope = Users::scope(&previous);
115✔
758
    auto previous_assumptions = assumptions_analysis.get(loop.root(), *previous_scope, true);
115✔
759
    auto current_scope = Users::scope(&current);
115✔
760
    auto current_assumptions = assumptions_analysis.get(loop.root(), *current_scope, true);
115✔
761

762
    // Check if previous subset is subset of any current subset
763
    for (auto& previous_subset : previous_subsets) {
189✔
764
        for (auto& current_subset : current_subsets) {
190✔
765
            if (symbolic::maps::intersects(
116✔
766
                    previous_subset, current_subset, loop.indvar(), previous_assumptions, current_assumptions
116✔
767
                )) {
768
                return true;
41✔
769
            }
770
        }
771
    }
772

773
    return false;
74✔
774
}
1,880✔
775

776
bool DataDependencyAnalysis::
777
    supersedes_restrictive(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
404✔
778
    if (previous.container() != current.container()) {
404✔
779
        return false;
×
780
    }
781
    // Shortcut for scalars
782
    auto& type = this->sdfg_.type(previous.container());
404✔
783
    if (dynamic_cast<const types::Scalar*>(&type)) {
404✔
784
        return true;
403✔
785
    }
786

787
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
1✔
788
        return false;
×
789
    }
790

791
    auto& previous_subsets = previous.subsets();
1✔
792
    auto& current_subsets = current.subsets();
1✔
793
    auto previous_scope = Users::scope(&previous);
1✔
794
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
1✔
795
    auto current_scope = Users::scope(&current);
1✔
796
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
1✔
797

798
    // Check if previous subset is subset of any current subset
799
    for (auto& previous_subset : previous_subsets) {
1✔
800
        bool found = false;
1✔
801
        for (auto& current_subset : current_subsets) {
2✔
802
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, previous_assumptions)) {
1✔
803
                found = true;
×
804
                break;
×
805
            }
806
        }
807
        if (!found) {
1✔
808
            return false;
1✔
809
        }
810
    }
811

812
    return true;
×
813
}
404✔
814

815
bool DataDependencyAnalysis::intersects(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
1,023✔
816
    if (previous.container() != current.container()) {
1,023✔
817
        return false;
616✔
818
    }
819
    // Shortcut for scalars
820
    auto& type = this->sdfg_.type(previous.container());
407✔
821
    if (dynamic_cast<const types::Scalar*>(&type)) {
407✔
822
        return true;
403✔
823
    }
824

825
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
4✔
826
        return true;
×
827
    }
828

829
    auto& previous_subsets = previous.subsets();
4✔
830
    auto& current_subsets = current.subsets();
4✔
831

832
    auto previous_scope = Users::scope(&previous);
4✔
833
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
4✔
834
    auto current_scope = Users::scope(&current);
4✔
835
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
4✔
836

837
    // Check if any current subset intersects with any previous subset
838
    bool found = false;
4✔
839
    for (auto& current_subset : current_subsets) {
7✔
840
        for (auto& previous_subset : previous_subsets) {
7✔
841
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
4✔
842
                found = true;
1✔
843
                break;
1✔
844
            }
845
        }
846
        if (found) {
4✔
847
            break;
1✔
848
        }
849
    }
850

851
    return found;
4✔
852
}
1,023✔
853

854
bool DataDependencyAnalysis::closes(
349✔
855
    analysis::AssumptionsAnalysis& assumptions_analysis,
856
    analysis::DominanceAnalysis& dominance_analysis,
857
    User& previous,
858
    User& current,
859
    bool requires_dominance
860
) {
861
    if (previous.container() != current.container()) {
349✔
862
        return false;
328✔
863
    }
864

865
    // Check dominance
866
    if (requires_dominance && !dominance_analysis.post_dominates(current, previous)) {
21✔
867
        return false;
×
868
    }
869

870
    // Previous memlets are subsets of current memlets
871
    auto& type = sdfg_.type(previous.container());
21✔
872
    if (type.type_id() == types::TypeID::Scalar) {
21✔
873
        return true;
5✔
874
    }
875

876
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
16✔
877
        return false;
×
878
    }
879

880
    // Collect memlets and assumptions
881
    auto previous_scope = Users::scope(&previous);
16✔
882
    auto current_scope = Users::scope(&current);
16✔
883
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
16✔
884
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
16✔
885

886
    auto& previous_memlets = previous.subsets();
16✔
887
    auto& current_memlets = current.subsets();
16✔
888

889
    for (auto& subset_ : previous_memlets) {
25✔
890
        bool overwritten = false;
16✔
891
        for (auto& subset : current_memlets) {
23✔
892
            if (symbolic::is_subset(subset_, subset, previous_assumptions, current_assumptions)) {
16✔
893
                overwritten = true;
9✔
894
                break;
9✔
895
            }
896
        }
897
        if (!overwritten) {
16✔
898
            return false;
7✔
899
        }
900
    }
901

902
    return true;
9✔
903
}
349✔
904

905
bool DataDependencyAnalysis::depends(
537✔
906
    analysis::AssumptionsAnalysis& assumptions_analysis,
907
    analysis::DominanceAnalysis& dominance_analysis,
908
    User& previous,
909
    User& current
910
) {
911
    if (previous.container() != current.container()) {
537✔
912
        return false;
371✔
913
    }
914

915
    // Previous memlets are subsets of current memlets
916
    auto& type = sdfg_.type(previous.container());
166✔
917
    if (type.type_id() == types::TypeID::Scalar) {
166✔
918
        return true;
160✔
919
    }
920

921
    if (this->is_undefined_user(previous)) {
6✔
922
        return true;
×
923
    }
924

925
    // Collect memlets and assumptions
926
    auto previous_scope = Users::scope(&previous);
6✔
927
    auto current_scope = Users::scope(&current);
6✔
928
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
6✔
929
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
6✔
930

931
    auto& previous_memlets = previous.subsets();
6✔
932
    auto& current_memlets = current.subsets();
6✔
933

934
    bool intersect_any = false;
6✔
935
    for (auto& current_subset : current_memlets) {
8✔
936
        for (auto& previous_subset : previous_memlets) {
8✔
937
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
6✔
938
                intersect_any = true;
4✔
939
                break;
4✔
940
            }
941
        }
942
        if (intersect_any) {
6✔
943
            break;
4✔
944
        }
945
    }
946

947
    return intersect_any;
6✔
948
}
537✔
949

950
/****** Public API ******/
951

952
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
953
    assert(write.use() == Use::WRITE);
×
954
    if (results_.find(write.container()) == results_.end()) {
×
955
        return {};
×
956
    }
957
    auto& raws = results_.at(write.container());
×
958
    assert(raws.find(&write) != raws.end());
×
959

960
    auto& reads_for_write = raws.at(&write);
×
961

962
    std::unordered_set<User*> reads;
×
963
    for (auto& entry : reads_for_write) {
×
964
        reads.insert(entry);
×
965
    }
966

967
    return reads;
×
968
};
×
969

970
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(const std::string& container) {
45✔
971
    if (results_.find(container) == results_.end()) {
45✔
972
        return {};
2✔
973
    }
974
    return results_.at(container);
43✔
975
};
45✔
976

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

980
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
981
    for (auto& entry : reads) {
43✔
982
        for (auto& read : entry.second) {
51✔
983
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27✔
984
                read_to_writes_map[read] = {};
20✔
985
            }
20✔
986
            read_to_writes_map[read].insert(entry.first);
27✔
987
        }
988
    }
989
    return read_to_writes_map;
19✔
990
};
19✔
991

992
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
993
    assert(read.use() == Use::READ);
×
994
    auto definitions = this->definitions(read.container());
×
995

996
    std::unordered_set<User*> writes;
×
997
    for (auto& entry : definitions) {
×
998
        for (auto& r : entry.second) {
×
999
            if (&read == r) {
×
1000
                writes.insert(entry.first);
×
1001
            }
×
1002
        }
1003
    }
1004
    return writes;
×
1005
};
×
1006

1007
bool DataDependencyAnalysis::is_undefined_user(User& user) const {
475✔
1008
    return user.vertex_ == boost::graph_traits<graph::Graph>::null_vertex();
475✔
1009
};
1010

1011
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
1012
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
1013
};
1014

1015
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::
1016
    dependencies(structured_control_flow::StructuredLoop& loop) const {
51✔
1017
    return this->loop_carried_dependencies_.at(&loop);
51✔
1018
};
1019

1020
} // namespace analysis
1021
} // namespace sdfg
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc