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

daisytuner / sdfglib / 20141104835

11 Dec 2025 05:03PM UTC coverage: 40.351% (-0.03%) from 40.379%
20141104835

push

github

web-flow
Merge pull request #387 from daisytuner/cleanup

Minor improvements in symbolic analysis

13653 of 43801 branches covered (31.17%)

Branch coverage included in aggregate %.

10 of 17 new or added lines in 4 files covered. (58.82%)

6 existing lines in 4 files now uncovered.

11688 of 19000 relevant lines covered (61.52%)

94.14 hits per line

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

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

21
      };
101✔
22

NEW
23
DataDependencyAnalysis::DataDependencyAnalysis(StructuredSDFG& sdfg, structured_control_flow::Sequence& node, bool detailed)
×
NEW
24
    : Analysis(sdfg), node_(node), detailed_(detailed) {
×
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) {
303✔
44
        closed_definitions.insert(entry);
227!
45
    }
46

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

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

57
void DataDependencyAnalysis::visit_block(
176✔
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();
176✔
67

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

73
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
241!
74
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
152!
75
                if (dataflow.in_degree(*node) > 0) {
152!
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) {
128✔
91
                            if (this->closes(assumptions_analysis, dominance_analysis, *user.first, *current_user, false)) {
39!
92
                                to_close.insert(user);
7!
93
                            }
7✔
94
                        }
95
                        for (auto& user : to_close) {
96✔
96
                            open_definitions.erase(user.first);
7!
97
                            closed_definitions.insert(user);
7!
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) {
152!
105
                    Use use = Use::READ;
65✔
106
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
131!
107
                        if (oedge.type() == data_flow::MemletType::Reference ||
132!
108
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
66!
109
                            use = Use::VIEW;
×
110
                            break;
×
111
                        }
112
                    }
113

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

117
                        // Assign to open definitions
118
                        bool found_user = false;
65✔
119
                        bool found_undefined_user = false;
65✔
120
                        for (auto& user : open_definitions) {
91✔
121
                            if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
26!
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) {
65✔
129
                            undefined.insert(current_user);
56!
130
                        }
56✔
131
                    }
65✔
132
                }
65✔
133
            }
152✔
134
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
241!
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)) {
396!
156
            std::unordered_set<std::string> used;
155✔
157
            for (auto& dim : oedge.subset()) {
294!
158
                for (auto atom : symbolic::atoms(dim)) {
241!
159
                    used.insert(atom->get_name());
102!
160
                }
102✔
161
            }
162
            for (auto& atom : used) {
257✔
163
                auto current_user = users.get_user(atom, &oedge, Use::READ);
102!
164

165
                // Assign to open definitions
166
                bool found_user = false;
102✔
167
                bool found_undefined_user = false;
102✔
168
                for (auto& user : open_definitions) {
123✔
169
                    if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
21!
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) {
102!
177
                    undefined.insert(current_user);
98!
178
                }
98✔
179
            }
180
        }
155✔
181
    }
182
}
176✔
183

184
void DataDependencyAnalysis::visit_for(
63✔
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())) {
69!
195
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, true);
6!
196

197
        // Assign to open definitions
198
        bool found_user = false;
6✔
199
        bool found_undefined_user = false;
6✔
200
        for (auto& user : open_definitions) {
7✔
201
            if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
1!
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) {
6!
209
            undefined.insert(current_user);
5!
210
        }
5✔
211
    }
6✔
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);
63!
217

218
        // Close open definitions if possible
219
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
63✔
220
        for (auto& user : open_definitions) {
78✔
221
            if (this->closes(assumptions_analysis, dominance_analysis, *user.first, *current_user, true)) {
15!
222
                to_close.insert(user);
2!
223
            }
2✔
224
        }
225
        for (auto& user : to_close) {
65✔
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, {}});
63!
232
    }
63✔
233

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

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

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

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

264
    // Add assumptions for body
265
    visit_sequence(
63!
266
        users,
63✔
267
        assumptions_analysis,
63✔
268
        dominance_analysis,
63✔
269
        for_loop.root(),
63!
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())) {
126!
277
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, false, true);
63!
278

279
        // Assign to open definitions
280
        bool found_user = false;
63✔
281
        bool found_undefined_user = false;
63✔
282
        for (auto& user : open_definitions_for) {
160✔
283
            if (this->depends(assumptions_analysis, dominance_analysis, *user.first, *current_user)) {
97!
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) {
63!
291
            undefined_for.insert(current_user);
62!
292
        }
62✔
293
    }
63✔
294

295
    // Merge for with outside
296

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

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

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

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

350
    // Add loop-carried dependencies
351
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
63!
352
    if (this->detailed_ && is_monotonic) {
63!
353
        // Case: Can analyze
354
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
58!
355
        assert(success);
58!
356
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
58!
357

358
        // We can focus on written containers
359

360
        // Case 1: Read-Write between iterations
361
        for (auto& read : undefined_for) {
313✔
362
            for (auto& write : open_definitions_for) {
711✔
363
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop)) {
456!
364
                    dependencies[read->container()] = LOOP_CARRIED_DEPENDENCY_READ_WRITE;
14!
365
                    write.second.insert(read);
14!
366
                }
14✔
367
            }
368
        }
369

370
        // Case 2: Write-Write between iterations
371
        for (auto& write : open_definitions_for) {
154✔
372
            if (dependencies.find(write.first->container()) != dependencies.end()) {
96!
373
                continue;
28✔
374
            }
375
            for (auto& write_2 : open_definitions_for) {
175✔
376
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis, for_loop)) {
131!
377
                    dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
24!
378
                    break;
24✔
379
                }
380
            }
381
        }
382
    } else {
58✔
383
        // Case: Cannot analyze
384
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
5!
385
        assert(success);
5!
386
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
5!
387

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

406
    // Add open definitions from for to outside
407
    for (auto& entry : open_definitions_for) {
160✔
408
        open_definitions.insert(entry);
97!
409
    }
410
}
63✔
411

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

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

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

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

478
    // merge closed writes
479
    for (auto& closing : closed_definitionss_branches) {
59✔
480
        for (auto& entry : closing) {
37!
481
            closed_definitions.insert(entry);
×
482
        }
483
    }
484

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

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

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

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

520
        for (auto& entry : to_close) {
19✔
521
            open_definitions.erase(entry.first);
4!
522
            closed_definitions.insert(entry);
4!
523
        }
524
    } else {
15✔
525
        // Incomplete if-else
526

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

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

547
    // Add open definitions from branches to outside
548
    for (auto& branch : open_definitions_branches) {
59✔
549
        for (auto& entry : branch) {
69✔
550
            open_definitions.insert(entry);
32!
551
        }
552
    }
553
}
22✔
554

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

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

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

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

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

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

610
void DataDependencyAnalysis::visit_return(
×
611
    analysis::Users& users,
612
    analysis::AssumptionsAnalysis& assumptions_analysis,
613
    analysis::DominanceAnalysis& dominance_analysis,
614
    structured_control_flow::Return& return_statement,
615
    std::unordered_set<User*>& undefined,
616
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
617
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
618
) {
619
    if (return_statement.is_data() && !return_statement.data().empty()) {
×
620
        auto current_user = users.get_user(return_statement.data(), &return_statement, Use::READ);
×
621

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

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

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

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

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

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

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

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

757
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
85✔
758
        return false;
4✔
759
    }
760

761
    auto& previous_subsets = previous.subsets();
81✔
762
    auto& current_subsets = current.subsets();
81!
763

764
    auto previous_scope = Users::scope(&previous);
81!
765
    auto previous_assumptions = assumptions_analysis.get(loop.root(), *previous_scope, true);
81!
766
    auto current_scope = Users::scope(&current);
81!
767
    auto current_assumptions = assumptions_analysis.get(loop.root(), *current_scope, true);
81!
768

769
    // Check if previous subset is subset of any current subset
770
    for (auto& previous_subset : previous_subsets) {
147✔
771
        for (auto& current_subset : current_subsets) {
148✔
772
            if (symbolic::maps::intersects(
82!
773
                    previous_subset, current_subset, loop.indvar(), previous_assumptions, current_assumptions
82!
774
                )) {
775
                return true;
15✔
776
            }
777
        }
778
    }
779

780
    return false;
66✔
781
}
587✔
782

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

794
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
3!
795
        return false;
×
796
    }
797

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

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

819
    return true;
2✔
820
}
174✔
821

822
bool DataDependencyAnalysis::intersects(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
555✔
823
    if (previous.container() != current.container()) {
555✔
824
        return false;
212✔
825
    }
826
    // Shortcut for scalars
827
    auto& type = this->sdfg_.type(previous.container());
343✔
828
    if (dynamic_cast<const types::Scalar*>(&type)) {
343!
829
        return true;
338✔
830
    }
831

832
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
5!
833
        return true;
×
834
    }
835

836
    if (!this->detailed_) {
5!
NEW
837
        return true;
×
838
    }
839

840
    auto& previous_subsets = previous.subsets();
5✔
841
    auto& current_subsets = current.subsets();
5!
842

843
    auto previous_scope = Users::scope(&previous);
5!
844
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
5!
845
    auto current_scope = Users::scope(&current);
5!
846
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
5!
847

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

862
    return found;
5✔
863
}
555✔
864

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

876
    // Check dominance
877
    if (requires_dominance && !dominance_analysis.post_dominates(current, previous)) {
22!
878
        return false;
×
879
    }
880

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

887
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
17!
888
        return false;
×
889
    }
890

891
    if (!this->detailed_) {
17!
NEW
892
        return false;
×
893
    }
894

895
    // Collect memlets and assumptions
896
    auto previous_scope = Users::scope(&previous);
17✔
897
    auto current_scope = Users::scope(&current);
17✔
898
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
17✔
899
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
17!
900

901
    auto& previous_memlets = previous.subsets();
17!
902
    auto& current_memlets = current.subsets();
17!
903

904
    for (auto& subset_ : previous_memlets) {
25✔
905
        bool overwritten = false;
17✔
906
        for (auto& subset : current_memlets) {
26✔
907
            if (symbolic::is_subset(subset_, subset, previous_assumptions, current_assumptions)) {
17!
908
                overwritten = true;
8✔
909
                break;
8✔
910
            }
911
        }
912
        if (!overwritten) {
17✔
913
            return false;
9✔
914
        }
915
    }
916

917
    return true;
8✔
918
}
261✔
919

920
bool DataDependencyAnalysis::depends(
427✔
921
    analysis::AssumptionsAnalysis& assumptions_analysis,
922
    analysis::DominanceAnalysis& dominance_analysis,
923
    User& previous,
924
    User& current
925
) {
926
    if (previous.container() != current.container()) {
427✔
927
        return false;
263✔
928
    }
929

930
    // Previous memlets are subsets of current memlets
931
    auto& type = sdfg_.type(previous.container());
164✔
932
    if (type.type_id() == types::TypeID::Scalar) {
164✔
933
        return true;
155✔
934
    }
935

936
    if (this->is_undefined_user(previous)) {
9!
937
        return true;
×
938
    }
939

940
    if (!this->detailed_) {
9✔
941
        return true;
2✔
942
    }
943

944
    // Collect memlets and assumptions
945
    auto previous_scope = Users::scope(&previous);
7✔
946
    auto current_scope = Users::scope(&current);
7✔
947
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
7✔
948
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
7!
949

950
    auto& previous_memlets = previous.subsets();
7!
951
    auto& current_memlets = current.subsets();
7!
952

953
    bool intersect_any = false;
7✔
954
    for (auto& current_subset : current_memlets) {
11✔
955
        for (auto& previous_subset : previous_memlets) {
11✔
956
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
7!
957
                intersect_any = true;
3✔
958
                break;
3✔
959
            }
960
        }
961
        if (intersect_any) {
7✔
962
            break;
3✔
963
        }
964
    }
965

966
    return intersect_any;
7✔
967
}
427✔
968

969
/****** Public API ******/
970

971
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
972
    assert(write.use() == Use::WRITE);
×
973
    if (results_.find(write.container()) == results_.end()) {
×
974
        return {};
×
975
    }
976
    auto& raws = results_.at(write.container());
×
977
    assert(raws.find(&write) != raws.end());
×
978

979
    auto& reads_for_write = raws.at(&write);
×
980

981
    std::unordered_set<User*> reads;
×
982
    for (auto& entry : reads_for_write) {
×
983
        reads.insert(entry);
×
984
    }
985

986
    return reads;
×
987
};
×
988

989
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(const std::string& container) {
33✔
990
    if (results_.find(container) == results_.end()) {
33!
991
        return {};
×
992
    }
993
    return results_.at(container);
33✔
994
};
33✔
995

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

999
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
1000
    for (auto& entry : reads) {
43✔
1001
        for (auto& read : entry.second) {
51✔
1002
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27!
1003
                read_to_writes_map[read] = {};
20!
1004
            }
20✔
1005
            read_to_writes_map[read].insert(entry.first);
27!
1006
        }
1007
    }
1008
    return read_to_writes_map;
19✔
1009
};
19!
1010

1011
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
1012
    assert(read.use() == Use::READ);
×
1013
    auto definitions = this->definitions(read.container());
×
1014

1015
    std::unordered_set<User*> writes;
×
1016
    for (auto& entry : definitions) {
×
1017
        for (auto& r : entry.second) {
×
1018
            if (&read == r) {
×
1019
                writes.insert(entry.first);
×
1020
            }
×
1021
        }
1022
    }
1023
    return writes;
×
1024
};
×
1025

1026
bool DataDependencyAnalysis::is_undefined_user(User& user) const {
747✔
1027
    return user.vertex_ == boost::graph_traits<graph::Graph>::null_vertex();
747✔
1028
};
1029

1030
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
1031
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
1032
};
1033

1034
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::
1035
    dependencies(structured_control_flow::StructuredLoop& loop) const {
48✔
1036
    return this->loop_carried_dependencies_.at(&loop);
48✔
1037
};
1038

1039
} // namespace analysis
1040
} // 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