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

daisytuner / sdfglib / 20388902294

18 Dec 2025 07:41PM UTC coverage: 39.328% (-0.1%) from 39.454%
20388902294

push

github

web-flow
Merge pull request #400 from daisytuner/backward-symbol-propagation

adds backward symbol propagation

13427 of 44322 branches covered (30.29%)

Branch coverage included in aggregate %.

113 of 250 new or added lines in 4 files covered. (45.2%)

13 existing lines in 3 files now uncovered.

11565 of 19225 relevant lines covered (60.16%)

83.99 hits per line

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

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

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

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

37
    visit_sequence(analysis_manager, node_, undefined, open_definitions, closed_definitions);
73!
38

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

43
    for (auto& entry : closed_definitions) {
306✔
44
        if (results_.find(entry.first->container()) == results_.end()) {
233!
45
            results_.insert({entry.first->container(), {}});
149!
46
        }
149✔
47
        results_.at(entry.first->container()).insert(entry);
233!
48
    }
49
};
73✔
50

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

53
void DataDependencyAnalysis::visit_block(
173✔
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
) {
60
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
173✔
61
    auto& dominance_analysis = analysis_manager.get<analysis::DominanceAnalysis>();
173✔
62
    auto& users = analysis_manager.get<analysis::Users>();
173✔
63

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

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

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

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

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

98
                        // Start new open definition
99
                        open_definitions.insert({current_user, {}});
88!
100
                    }
88✔
101
                }
89✔
102
                if (dataflow.out_degree(*access_node) > 0) {
150!
103
                    Use use = Use::READ;
64✔
104
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
129!
105
                        if (oedge.type() == data_flow::MemletType::Reference ||
130!
106
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
65!
107
                            use = Use::VIEW;
×
108
                            break;
×
109
                        }
110
                    }
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) {
94✔
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
                        }
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
            }
150✔
132
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
238!
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)) {
391!
154
            std::unordered_set<std::string> used;
153✔
155
            for (auto& dim : oedge.subset()) {
290!
156
                for (auto atom : symbolic::atoms(dim)) {
237!
157
                    used.insert(atom->get_name());
100!
158
                }
100✔
159
            }
160
            for (auto& atom : used) {
253✔
161
                auto current_user = users.get_user(atom, &oedge, Use::READ);
100!
162

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

182
void DataDependencyAnalysis::visit_for(
61✔
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
) {
189
    auto& users = analysis_manager.get<analysis::Users>();
61✔
190

191
    // Init - Read
192
    for (auto atom : symbolic::atoms(for_loop.init())) {
66!
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) {
6✔
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
        }
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
    {
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) {
76✔
219
            if (this->closes(analysis_manager, *user.first, *current_user, true)) {
15!
220
                to_close.insert(user);
2!
221
            }
2✔
222
        }
223
        for (auto& user : to_close) {
63✔
224
            open_definitions.erase(user.first);
2!
225
            closed_definitions.insert(user);
2!
226
        }
227

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

232
    // Update - Write
233
    {
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
    }
237

238
    // Condition - Read
239
    for (auto atom : symbolic::atoms(for_loop.condition())) {
174!
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) {
356✔
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
        }
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())) {
122!
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) {
156✔
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
        }
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!
UNCOV
290
        closed_definitions.insert(entry);
×
291
    }
292

293
    // Undefined reads are matched or forwarded
294
    for (auto open_read : undefined_for) {
312✔
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) {
788✔
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
        }
307
        if (!found || found_undefined_user) {
251!
308
            undefined.insert(open_read);
85!
309
            continue;
85✔
310
        }
311

312
        // Users found, check if they fully cover the read
313
        bool covered = false;
166✔
314
        for (auto& entry : frontier) {
168✔
315
            if (!dominance_analysis.dominates(*entry, *open_read)) {
167!
316
                continue;
1✔
317
            }
318
            bool covers = supersedes_restrictive(*open_read, *entry, analysis_manager);
166!
319
            if (covers) {
166✔
320
                covered = true;
165✔
321
                break;
165✔
322
            }
323
        }
324
        if (!covered) {
166✔
325
            undefined.insert(open_read);
1!
326
        }
1✔
327
    }
251!
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) {
196✔
332
        for (auto& user : open_definitions_for) {
339✔
333
            if (this->closes(analysis_manager, *previous.first, *user.first, true)) {
204!
UNCOV
334
                to_close.insert(previous.first);
×
UNCOV
335
                break;
×
336
            }
337
        }
338
    }
339
    for (auto& user : to_close) {
61!
UNCOV
340
        closed_definitions.insert({user, open_definitions.at(user)});
×
UNCOV
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) {
302✔
357
            for (auto& write : open_definitions_for) {
695✔
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
            }
363
        }
364

365
        // Case 2: Write-Write between iterations
366
        for (auto& write : open_definitions_for) {
150✔
367
            if (dependencies.find(write.first->container()) != dependencies.end()) {
94!
368
                continue;
29✔
369
            }
370
            for (auto& write_2 : open_definitions_for) {
170✔
371
                if (loop_depends(*write.first, *write_2.first, analysis_manager, for_loop)) {
128!
372
                    dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
23!
373
                    break;
23✔
374
                }
375
            }
376
        }
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) {
10✔
386
            for (auto& write : open_definitions_for) {
6✔
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
            }
392
        }
393
        // Add loop-carried dependencies for writes
394
        for (auto& write : open_definitions_for) {
6✔
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
        }
399
    }
400

401
    // Add open definitions from for to outside
402
    for (auto& entry : open_definitions_for) {
156✔
403
        open_definitions.insert(entry);
95!
404
    }
405
}
61✔
406

407
void DataDependencyAnalysis::visit_if_else(
22✔
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
) {
414
    auto& users = analysis_manager.get<analysis::Users>();
22✔
415

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

422
            bool found_user = false;
26✔
423
            bool found_undefined_user = false;
26✔
424
            for (auto& user : open_definitions) {
44✔
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
            }
431
            // If no definition found or undefined user found, mark as undefined
432
            if (!found_user || found_undefined_user) {
26!
433
                undefined.insert(current_user);
17!
434
            }
17✔
435
        }
26✔
436
    }
37✔
437

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

452
    // merge partial open reads
453
    for (size_t i = 0; i < if_else.size(); i++) {
59!
454
        for (auto& entry : undefined_branches.at(i)) {
44!
455
            bool found_user = false;
7✔
456
            bool found_undefined_user = false;
7✔
457
            for (auto& user : open_definitions) {
17✔
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
            }
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
        }
469
    }
37✔
470

471
    // merge closed writes
472
    for (auto& closing : closed_definitionss_branches) {
59✔
473
        for (auto& entry : closing) {
37!
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;
15✔
481
        std::unordered_set<std::string> candidates;
15✔
482
        std::unordered_set<std::string> candidates_tmp;
15✔
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)) {
29!
491
            candidates.insert(entry.first->container());
14!
492
        }
493
        for (auto& entry : closed_definitionss_branches.at(0)) {
15!
494
            candidates.insert(entry.first->container());
×
495
        }
496

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

507
        for (auto& entry : open_definitions) {
23✔
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) {
19✔
514
            open_definitions.erase(entry.first);
4!
515
            closed_definitions.insert(entry);
4!
516
        }
517
    } else {
15✔
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) {
14✔
530
            for (auto& open_definition : branch) {
12✔
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
        }
538
    }
539

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

548
void DataDependencyAnalysis::visit_while(
8✔
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
) {
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) {
10✔
563
        closed_definitions.insert(entry);
2!
564
    }
565

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

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

587
    // Add open definitions from while to outside
588
    for (auto& entry : open_definitions_while) {
21✔
589
        open_definitions.insert(entry);
13!
590
    }
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(
196✔
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
) {
631
    auto& users = analysis_manager.get<analysis::Users>();
196✔
632

633
    for (size_t i = 0; i < sequence.size(); i++) {
460✔
634
        auto child = sequence.at(i);
264✔
635
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
264!
636
            visit_block(analysis_manager, *block, undefined, open_definitions, closed_definitions);
165✔
637
        } else if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
264!
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)) {
99!
640
            visit_if_else(analysis_manager, *if_else, undefined, open_definitions, closed_definitions);
22✔
641
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
38!
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)) {
16!
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()) {
344✔
651
            for (auto& atom : symbolic::atoms(entry.second)) {
112!
652
                if (symbolic::is_pointer(atom)) {
32!
653
                    continue;
×
654
                }
655
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
32!
656

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

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

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

689
bool DataDependencyAnalysis::loop_depends(
577✔
690
    User& previous,
691
    User& current,
692
    analysis::AnalysisManager& analysis_manager,
693
    structured_control_flow::StructuredLoop& loop
694
) {
695
    if (previous.container() != current.container()) {
577✔
696
        return false;
474✔
697
    }
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
    }
703

704
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
81✔
705
        return false;
4✔
706
    }
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)) {
97!
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
    }
738

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

750
    return false;
62✔
751
}
577✔
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
    }
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) {
5✔
778
        bool found = false;
3✔
779
        for (auto& current_subset : current_subsets) {
4✔
780
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, previous_assumptions)) {
3✔
781
                found = true;
2✔
782
                break;
2✔
783
            }
784
        }
785
        if (!found) {
3✔
786
            return false;
1✔
787
        }
788
    }
789

790
    return true;
2✔
791
}
166✔
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
    }
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
    }
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) {
7✔
823
        for (auto& previous_subset : previous_subsets) {
7✔
824
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
5✔
825
                found = true;
3✔
826
                break;
3✔
827
            }
828
        }
829
        if (found) {
5✔
830
            break;
3✔
831
        }
832
    }
833

834
    return found;
5✔
835
}
537✔
836

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

843
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
26!
NEW
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
        }
853
    }
5✔
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
    }
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) {
14✔
876
        bool overwritten = false;
10✔
877
        for (auto& subset : current_memlets) {
16✔
878
            if (symbolic::is_subset(subset_, subset, previous_assumptions, current_assumptions)) {
10✔
879
                overwritten = true;
4✔
880
                break;
4✔
881
            }
882
        }
883
        if (!overwritten) {
10✔
884
            return false;
6✔
885
        }
886
    }
887

888
    return true;
4✔
889
}
295✔
890

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

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

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

906
    if (!this->detailed_) {
13✔
907
        return true;
2✔
908
    }
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) {
17✔
922
        for (auto& previous_subset : previous_memlets) {
17✔
923
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
11✔
924
                intersect_any = true;
5✔
925
                break;
5✔
926
            }
927
        }
928
        if (intersect_any) {
11✔
929
            break;
5✔
930
        }
931
    }
932

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

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

966
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
967
    for (auto& entry : reads) {
43✔
968
        for (auto& read : entry.second) {
51✔
969
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27!
970
                read_to_writes_map[read] = {};
20!
971
            }
20✔
972
            read_to_writes_map[read].insert(entry.first);
27!
973
        }
974
    }
975
    return read_to_writes_map;
19✔
976
};
19!
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 {
761✔
994
    return user.vertex_ == boost::graph_traits<graph::Graph>::null_vertex();
761✔
995
};
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
};
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

© 2025 Coveralls, Inc