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

daisytuner / sdfglib / 20279737409

16 Dec 2025 07:12PM UTC coverage: 40.034% (-0.05%) from 40.086%
20279737409

Pull #393

github

web-flow
Merge 5e13d3008 into b699f8f96
Pull Request #393: refactors assumptions analysis for better performance

13432 of 43539 branches covered (30.85%)

Branch coverage included in aggregate %.

253 of 296 new or added lines in 9 files covered. (85.47%)

87 existing lines in 10 files now uncovered.

11599 of 18986 relevant lines covered (61.09%)

86.79 hits per line

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

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

21
      };
100✔
22

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

26
      };
×
27

28
void DataDependencyAnalysis::run(analysis::AnalysisManager& analysis_manager) {
75✔
29
    results_.clear();
75✔
30
    undefined_users_.clear();
75✔
31
    loop_carried_dependencies_.clear();
75✔
32
    this->loop_analysis_ = nullptr;
75✔
33

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

38
    auto& assumptions_analysis = analysis_manager.get<AssumptionsAnalysis>();
75!
39
    auto& users = analysis_manager.get<Users>();
75!
40
    auto& dominance_analysis = analysis_manager.get<DominanceAnalysis>();
75!
41
    this->loop_analysis_ = &analysis_manager.get<LoopAnalysis>();
75!
42

43
    visit_sequence(users, assumptions_analysis, dominance_analysis, node_, undefined, open_definitions, closed_definitions);
75!
44

45
    for (auto& entry : open_definitions) {
297✔
46
        closed_definitions.insert(entry);
222!
47
    }
48

49
    for (auto& entry : closed_definitions) {
308✔
50
        if (results_.find(entry.first->container()) == results_.end()) {
233!
51
            results_.insert({entry.first->container(), {}});
149!
52
        }
149✔
53
        results_.at(entry.first->container()).insert(entry);
233!
54
    }
55
};
75✔
56

57
/****** Visitor API ******/
58

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

70
    for (auto node : dataflow.topological_sort()) {
430✔
71
        if (dynamic_cast<data_flow::ConstantNode*>(node) != nullptr) {
255!
72
            continue;
17✔
73
        }
74

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

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

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

102
                        // Start new open definition
103
                        open_definitions.insert({current_user, {}});
88!
104
                    }
88✔
105
                }
89✔
106
                if (dataflow.out_degree(*access_node) > 0) {
150!
107
                    Use use = Use::READ;
64✔
108
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
129!
109
                        if (oedge.type() == data_flow::MemletType::Reference ||
130!
110
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
65!
UNCOV
111
                            use = Use::VIEW;
×
UNCOV
112
                            break;
×
113
                        }
114
                    }
115

116
                    if (use == Use::READ) {
64!
117
                        auto current_user = users.get_user(access_node->data(), access_node, use);
64!
118

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

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

157
        for (auto& oedge : dataflow.out_edges(*node)) {
391!
158
            std::unordered_set<std::string> used;
153✔
159
            for (auto& dim : oedge.subset()) {
290!
160
                for (auto atom : symbolic::atoms(dim)) {
237!
161
                    used.insert(atom->get_name());
100!
162
                }
100✔
163
            }
164
            for (auto& atom : used) {
253✔
165
                auto current_user = users.get_user(atom, &oedge, Use::READ);
100!
166

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

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

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

215
    // Init - Write
216
    {
217
        // Write Induction Variable
218
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, true);
61!
219

220
        // Close open definitions if possible
221
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
61✔
222
        for (auto& user : open_definitions) {
76✔
223
            if (this->closes(assumptions_analysis, dominance_analysis, *user.first, *current_user, true)) {
15!
224
                to_close.insert(user);
2!
225
            }
2✔
226
        }
227
        for (auto& user : to_close) {
63✔
228
            open_definitions.erase(user.first);
2!
229
            closed_definitions.insert(user);
2!
230
        }
231

232
        // Start new open definition
233
        open_definitions.insert({current_user, {}});
61!
234
    }
61✔
235

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

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

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

262
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_for;
61✔
263
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_for;
61✔
264
    std::unordered_set<User*> undefined_for;
61✔
265

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

277
    // Update - Read
278
    for (auto atom : symbolic::atoms(for_loop.update())) {
122!
279
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, false, true);
61!
280

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

297
    // Merge for with outside
298

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

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

323
        // Users found, check if they fully cover the read
324
        bool covered = false;
166✔
325
        for (auto& entry : frontier) {
167✔
326
            if (!dominance_analysis.dominates(*entry, *open_read)) {
166!
UNCOV
327
                continue;
×
328
            }
329
            bool covers = supersedes_restrictive(*open_read, *entry, assumptions_analysis);
166!
330
            if (covers) {
166✔
331
                covered = true;
165✔
332
                break;
165✔
333
            }
334
        }
335
        if (!covered) {
166✔
336
            undefined.insert(open_read);
1!
337
        }
1✔
338
    }
251!
339

340
    // Open definitions may close outside open definitions after loop
341
    std::unordered_set<User*> to_close;
61✔
342
    for (auto& previous : open_definitions) {
196✔
343
        for (auto& user : open_definitions_for) {
330✔
344
            if (this->closes(assumptions_analysis, dominance_analysis, *previous.first, *user.first, false)) {
199!
345
                to_close.insert(previous.first);
4!
346
                break;
4✔
347
            }
348
        }
349
    }
350
    for (auto& user : to_close) {
65✔
351
        closed_definitions.insert({user, open_definitions.at(user)});
4!
352
        open_definitions.erase(user);
4!
353
    }
354

355
    // Add loop-carried dependencies
356
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
61!
357
    if (this->detailed_ && is_monotonic) {
61!
358
        // Case: Can analyze
359
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
56!
360
        assert(success);
56!
361
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
56!
362

363
        // We can focus on written containers
364

365
        // Case 1: Read-Write between iterations
366
        for (auto& read : undefined_for) {
302✔
367
            for (auto& write : open_definitions_for) {
683✔
368
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop)) {
437!
369
                    dependencies[read->container()] = LOOP_CARRIED_DEPENDENCY_READ_WRITE;
14!
370
                    write.second.insert(read);
14!
371
                }
14✔
372
            }
373
        }
374

375
        // Case 2: Write-Write between iterations
376
        for (auto& write : open_definitions_for) {
148✔
377
            if (dependencies.find(write.first->container()) != dependencies.end()) {
92!
378
                continue;
27✔
379
            }
380
            for (auto& write_2 : open_definitions_for) {
165✔
381
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis, for_loop)) {
123!
382
                    dependencies.insert({write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
23!
383
                    break;
23✔
384
                }
385
            }
386
        }
387
    } else {
56✔
388
        // Case: Cannot analyze
389
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
5!
390
        assert(success);
5!
391
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
5!
392

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

411
    // Add open definitions from for to outside
412
    for (auto& entry : open_definitions_for) {
154✔
413
        open_definitions.insert(entry);
93!
414
    }
415
}
61✔
416

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

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

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

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

483
    // merge closed writes
484
    for (auto& closing : closed_definitionss_branches) {
59✔
485
        for (auto& entry : closing) {
37!
UNCOV
486
            closed_definitions.insert(entry);
×
487
        }
488
    }
489

490
    // Close open reads_after_writes for complete branches
491
    if (if_else.is_complete()) {
22!
492
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
15✔
493
        std::unordered_set<std::string> candidates;
15✔
494
        std::unordered_set<std::string> candidates_tmp;
15✔
495

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

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

519
        for (auto& entry : open_definitions) {
23✔
520
            if (candidates.find(entry.first->container()) != candidates.end()) {
8!
521
                to_close.insert(entry);
4!
522
            }
4✔
523
        }
524

525
        for (auto& entry : to_close) {
19✔
526
            open_definitions.erase(entry.first);
4!
527
            closed_definitions.insert(entry);
4!
528
        }
529
    } else {
15✔
530
        // Incomplete if-else
531

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

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

552
    // Add open definitions from branches to outside
553
    for (auto& branch : open_definitions_branches) {
59✔
554
        for (auto& entry : branch) {
69✔
555
            open_definitions.insert(entry);
32!
556
        }
557
    }
558
}
22✔
559

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

573
    visit_sequence(
8!
574
        users,
8✔
575
        assumptions_analysis,
8✔
576
        dominance_analysis,
8✔
577
        while_loop.root(),
8!
578
        undefined_while,
579
        open_definitions_while,
580
        closed_definitions_while
581
    );
582

583
    // Scope-local closed definitions
584
    for (auto& entry : closed_definitions_while) {
10✔
585
        closed_definitions.insert(entry);
2!
586
    }
587

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

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

609
    // Add open definitions from while to outside
610
    for (auto& entry : open_definitions_while) {
21✔
611
        open_definitions.insert(entry);
13!
612
    }
613
}
8✔
614

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

627
        bool found = false;
×
628
        for (auto& user : open_definitions) {
×
629
            if (user.first->container() == return_statement.data()) {
×
630
                user.second.insert(current_user);
×
UNCOV
631
                found = true;
×
632
            }
×
633
        }
634
        if (!found) {
×
635
            undefined.insert(current_user);
×
UNCOV
636
        }
×
UNCOV
637
    }
×
638

639
    // close all open reads_after_writes
UNCOV
640
    for (auto& entry : open_definitions) {
×
641
        closed_definitions.insert(entry);
×
642
    }
UNCOV
643
    open_definitions.clear();
×
UNCOV
644
}
×
645

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

707
        // handle transitions read
708
        for (auto& entry : child.second.assignments()) {
346✔
709
            for (auto& atom : symbolic::atoms(entry.second)) {
112!
710
                if (symbolic::is_pointer(atom)) {
32!
UNCOV
711
                    continue;
×
712
                }
713
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
32!
714

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

728
        // handle transitions write
729
        for (auto& entry : child.second.assignments()) {
346✔
730
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
80✔
731

732
            std::unordered_set<User*> to_close;
80✔
733
            for (auto& user : open_definitions) {
113✔
734
                if (user.first->container() == entry.first->get_name()) {
33!
735
                    to_close.insert(user.first);
3!
736
                }
3✔
737
            }
738
            for (auto& user : to_close) {
83✔
739
                closed_definitions.insert({user, open_definitions.at(user)});
3!
740
                open_definitions.erase(user);
3!
741
            }
742
            open_definitions.insert({current_user, {}});
80!
743
        }
80✔
744
    }
266✔
745
}
198✔
746

747
bool DataDependencyAnalysis::loop_depends(
560✔
748
    User& previous,
749
    User& current,
750
    analysis::AssumptionsAnalysis& assumptions_analysis,
751
    structured_control_flow::StructuredLoop& loop
752
) {
753
    if (previous.container() != current.container()) {
560✔
754
        return false;
457✔
755
    }
756
    // Shortcut for scalars
757
    auto& type = this->sdfg_.type(previous.container());
103✔
758
    if (dynamic_cast<const types::Scalar*>(&type)) {
103!
759
        return true;
22✔
760
    }
761

762
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
81✔
763
        return false;
4✔
764
    }
765

766
    auto& previous_subsets = previous.subsets();
77✔
767
    auto& current_subsets = current.subsets();
77✔
768

769
    auto previous_scope = Users::scope(&previous);
77✔
770
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
77✔
771
    auto current_scope = Users::scope(&current);
77!
772
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
77!
773

774
    // This part shall go into a backward propagation of assumptions analysis
775
    auto tmp_loop_analysis = loop_analysis_;
77✔
776
    if (tmp_loop_analysis == nullptr) {
77✔
777
        tmp_loop_analysis = new analysis::LoopAnalysis(this->sdfg_);
6!
778

779
        analysis::AnalysisManager am(this->sdfg_);
6!
780
        tmp_loop_analysis->run(am);
6!
781
    }
6✔
782
    for (auto& child : tmp_loop_analysis->descendants(&loop)) {
97!
783
        if (auto sub_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(child)) {
20!
784
            auto& child_indvar = sub_loop->indvar();
20!
785
            if (previous_assumptions.find(child_indvar) != previous_assumptions.end()) {
20!
786
                previous_assumptions.at(child_indvar).constant(false);
20!
787
            }
20✔
788
            if (current_assumptions.find(child_indvar) != current_assumptions.end()) {
20!
789
                current_assumptions.at(child_indvar).constant(false);
20!
790
            }
20✔
791
        }
20✔
792
    }
793

794
    // Check if previous subset is subset of any current subset
795
    for (auto& previous_subset : previous_subsets) {
139✔
796
        for (auto& current_subset : current_subsets) {
140✔
797
            if (symbolic::maps::intersects(
78!
798
                    previous_subset, current_subset, loop.indvar(), previous_assumptions, current_assumptions
78!
799
                )) {
800
                return true;
15✔
801
            }
802
        }
803
    }
804

805
    if (loop_analysis_ == nullptr) {
62✔
806
        delete tmp_loop_analysis;
4!
807
    }
4✔
808

809
    return false;
62✔
810
}
560✔
811

812
bool DataDependencyAnalysis::
813
    supersedes_restrictive(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
166✔
814
    if (previous.container() != current.container()) {
166!
UNCOV
815
        return false;
×
816
    }
817
    // Shortcut for scalars
818
    auto& type = this->sdfg_.type(previous.container());
166✔
819
    if (dynamic_cast<const types::Scalar*>(&type)) {
166!
820
        return true;
163✔
821
    }
822

823
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
3!
UNCOV
824
        return false;
×
825
    }
826

827
    auto& previous_subsets = previous.subsets();
3✔
828
    auto& current_subsets = current.subsets();
3✔
829
    auto previous_scope = Users::scope(&previous);
3✔
830
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
3✔
831
    auto current_scope = Users::scope(&current);
3✔
832
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
3✔
833

834
    // Check if previous subset is subset of any current subset
835
    for (auto& previous_subset : previous_subsets) {
5✔
836
        bool found = false;
3✔
837
        for (auto& current_subset : current_subsets) {
4✔
838
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, previous_assumptions)) {
3✔
839
                found = true;
2✔
840
                break;
2✔
841
            }
842
        }
843
        if (!found) {
3✔
844
            return false;
1✔
845
        }
846
    }
847

848
    return true;
2✔
849
}
166✔
850

851
bool DataDependencyAnalysis::intersects(User& previous, User& current, analysis::AssumptionsAnalysis& assumptions_analysis) {
537✔
852
    if (previous.container() != current.container()) {
537✔
853
        return false;
206✔
854
    }
855
    // Shortcut for scalars
856
    auto& type = this->sdfg_.type(previous.container());
331✔
857
    if (dynamic_cast<const types::Scalar*>(&type)) {
331!
858
        return true;
326✔
859
    }
860

861
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
5!
UNCOV
862
        return true;
×
863
    }
864

865
    if (!this->detailed_) {
5!
UNCOV
866
        return true;
×
867
    }
868

869
    auto& previous_subsets = previous.subsets();
5✔
870
    auto& current_subsets = current.subsets();
5✔
871

872
    auto previous_scope = Users::scope(&previous);
5✔
873
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
5✔
874
    auto current_scope = Users::scope(&current);
5✔
875
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
5✔
876

877
    // Check if any current subset intersects with any previous subset
878
    bool found = false;
5✔
879
    for (auto& current_subset : current_subsets) {
7✔
880
        for (auto& previous_subset : previous_subsets) {
7✔
881
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
5✔
882
                found = true;
3✔
883
                break;
3✔
884
            }
885
        }
886
        if (found) {
5✔
887
            break;
3✔
888
        }
889
    }
890

891
    return found;
5✔
892
}
537✔
893

894
bool DataDependencyAnalysis::closes(
253✔
895
    analysis::AssumptionsAnalysis& assumptions_analysis,
896
    analysis::DominanceAnalysis& dominance_analysis,
897
    User& previous,
898
    User& current,
899
    bool requires_dominance
900
) {
901
    if (previous.container() != current.container()) {
253✔
902
        return false;
231✔
903
    }
904

905
    // Check dominance
906
    if (requires_dominance && !dominance_analysis.post_dominates(current, previous)) {
22!
UNCOV
907
        return false;
×
908
    }
909

910
    // Previous memlets are subsets of current memlets
911
    auto& type = sdfg_.type(previous.container());
22✔
912
    if (type.type_id() == types::TypeID::Scalar) {
22✔
913
        return true;
5✔
914
    }
915

916
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
17!
UNCOV
917
        return false;
×
918
    }
919

920
    if (!this->detailed_) {
17!
UNCOV
921
        return false;
×
922
    }
923

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

930
    auto& previous_memlets = previous.subsets();
17✔
931
    auto& current_memlets = current.subsets();
17✔
932

933
    for (auto& subset_ : previous_memlets) {
25✔
934
        bool overwritten = false;
17✔
935
        for (auto& subset : current_memlets) {
26✔
936
            if (symbolic::is_subset(subset_, subset, previous_assumptions, current_assumptions)) {
17✔
937
                overwritten = true;
8✔
938
                break;
8✔
939
            }
940
        }
941
        if (!overwritten) {
17✔
942
            return false;
9✔
943
        }
944
    }
945

946
    return true;
8✔
947
}
253✔
948

949
bool DataDependencyAnalysis::depends(
413✔
950
    analysis::AssumptionsAnalysis& assumptions_analysis,
951
    analysis::DominanceAnalysis& dominance_analysis,
952
    User& previous,
953
    User& current
954
) {
955
    if (previous.container() != current.container()) {
413✔
956
        return false;
253✔
957
    }
958

959
    // Previous memlets are subsets of current memlets
960
    auto& type = sdfg_.type(previous.container());
160✔
961
    if (type.type_id() == types::TypeID::Scalar) {
160✔
962
        return true;
151✔
963
    }
964

965
    if (this->is_undefined_user(previous)) {
9!
UNCOV
966
        return true;
×
967
    }
968

969
    if (!this->detailed_) {
9✔
970
        return true;
2✔
971
    }
972

973
    // Collect memlets and assumptions
974
    auto previous_scope = Users::scope(&previous);
7✔
975
    auto current_scope = Users::scope(&current);
7✔
976
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
7✔
977
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
7✔
978

979
    auto& previous_memlets = previous.subsets();
7✔
980
    auto& current_memlets = current.subsets();
7✔
981

982
    bool intersect_any = false;
7✔
983
    for (auto& current_subset : current_memlets) {
11✔
984
        for (auto& previous_subset : previous_memlets) {
11✔
985
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
7✔
986
                intersect_any = true;
3✔
987
                break;
3✔
988
            }
989
        }
990
        if (intersect_any) {
7✔
991
            break;
3✔
992
        }
993
    }
994

995
    return intersect_any;
7✔
996
}
413✔
997

998
/****** Public API ******/
999

UNCOV
1000
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
UNCOV
1001
    assert(write.use() == Use::WRITE);
×
UNCOV
1002
    if (results_.find(write.container()) == results_.end()) {
×
UNCOV
1003
        return {};
×
1004
    }
UNCOV
1005
    auto& raws = results_.at(write.container());
×
UNCOV
1006
    assert(raws.find(&write) != raws.end());
×
1007

UNCOV
1008
    auto& reads_for_write = raws.at(&write);
×
1009

UNCOV
1010
    std::unordered_set<User*> reads;
×
UNCOV
1011
    for (auto& entry : reads_for_write) {
×
UNCOV
1012
        reads.insert(entry);
×
1013
    }
1014

1015
    return reads;
×
1016
};
×
1017

1018
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(const std::string& container) {
33✔
1019
    if (results_.find(container) == results_.end()) {
33!
1020
        return {};
×
1021
    }
1022
    return results_.at(container);
33✔
1023
};
33✔
1024

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

1028
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
1029
    for (auto& entry : reads) {
43✔
1030
        for (auto& read : entry.second) {
51✔
1031
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27!
1032
                read_to_writes_map[read] = {};
20!
1033
            }
20✔
1034
            read_to_writes_map[read].insert(entry.first);
27!
1035
        }
1036
    }
1037
    return read_to_writes_map;
19✔
1038
};
19!
1039

UNCOV
1040
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
UNCOV
1041
    assert(read.use() == Use::READ);
×
UNCOV
1042
    auto definitions = this->definitions(read.container());
×
1043

UNCOV
1044
    std::unordered_set<User*> writes;
×
UNCOV
1045
    for (auto& entry : definitions) {
×
UNCOV
1046
        for (auto& r : entry.second) {
×
UNCOV
1047
            if (&read == r) {
×
UNCOV
1048
                writes.insert(entry.first);
×
UNCOV
1049
            }
×
1050
        }
1051
    }
UNCOV
1052
    return writes;
×
UNCOV
1053
};
×
1054

1055
bool DataDependencyAnalysis::is_undefined_user(User& user) const {
723✔
1056
    return user.vertex_ == boost::graph_traits<graph::Graph>::null_vertex();
723✔
1057
};
1058

UNCOV
1059
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
UNCOV
1060
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
1061
};
1062

1063
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::
1064
    dependencies(structured_control_flow::StructuredLoop& loop) const {
48✔
1065
    return this->loop_carried_dependencies_.at(&loop);
48✔
1066
};
1067

1068
} // namespace analysis
1069
} // 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