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

daisytuner / sdfglib / 15852980623

24 Jun 2025 02:16PM UTC coverage: 64.412% (+0.3%) from 64.145%
15852980623

push

github

web-flow
Merge pull request #72 from daisytuner/capture-instrumentation

Capture instrumentation

363 of 446 new or added lines in 19 files covered. (81.39%)

100 existing lines in 5 files now uncovered.

8389 of 13024 relevant lines covered (64.41%)

116.79 hits per line

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

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

21
      };
40✔
22

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

27
      };
×
28

29
void DataDependencyAnalysis::run(analysis::AnalysisManager& analysis_manager) {
40✔
30
    results_.clear();
40✔
31

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

36
    auto& assumptions_analysis = analysis_manager.get<AssumptionsAnalysis>();
40✔
37
    auto& users = analysis_manager.get<Users>();
40✔
38
    visit_sequence(users, assumptions_analysis, node_, undefined, open_definitions,
40✔
39
                   closed_definitions);
40

41
    for (auto& entry : open_definitions) {
129✔
42
        closed_definitions.insert(entry);
89✔
43
    }
44

45
    for (auto& entry : closed_definitions) {
134✔
46
        if (results_.find(entry.first->container()) == results_.end()) {
94✔
47
            results_.insert({entry.first->container(), {}});
63✔
48
        }
63✔
49
        results_.at(entry.first->container()).insert(entry);
94✔
50
    }
51
};
40✔
52

53
/****** Visitor API ******/
54

55
void DataDependencyAnalysis::visit_block(
74✔
56
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
57
    structured_control_flow::Block& block, std::unordered_set<User*>& undefined,
58
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
59
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
60
    auto& dataflow = block.dataflow();
74✔
61

62
    for (auto node : dataflow.topological_sort()) {
106✔
63
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
32✔
64
            if (!symbolic::is_pointer(symbolic::symbol(access_node->data()))) {
21✔
65
                if (dataflow.in_degree(*node) > 0) {
21✔
66
                    Use use = Use::WRITE;
11✔
67
                    for (auto& iedge : dataflow.in_edges(*access_node)) {
22✔
68
                        if (iedge.src_conn() == "refs" || iedge.dst_conn() == "refs") {
11✔
69
                            use = Use::MOVE;
×
70
                            break;
×
71
                        }
72
                    }
73

74
                    auto current_user = users.get_user(access_node->data(), access_node, use);
11✔
75

76
                    if (use == Use::WRITE) {
11✔
77
                        std::unordered_map<User*, std::unordered_set<User*>> to_close;
11✔
78
                        for (auto& user : open_definitions) {
13✔
79
                            if (supersedes(*user.first, *current_user, assumptions_analysis)) {
2✔
UNCOV
80
                                to_close.insert(user);
×
UNCOV
81
                            }
×
82
                        }
83
                        for (auto& user : to_close) {
11✔
UNCOV
84
                            open_definitions.erase(user.first);
×
UNCOV
85
                            closed_definitions.insert(user);
×
86
                        }
87
                        open_definitions.insert({current_user, {}});
11✔
88
                    }
11✔
89
                }
11✔
90
                if (dataflow.out_degree(*access_node) > 0) {
21✔
91
                    Use use = Use::READ;
10✔
92
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
20✔
93
                        if (oedge.src_conn() == "refs" || oedge.dst_conn() == "refs") {
10✔
94
                            use = Use::VIEW;
×
95
                            break;
×
96
                        }
97
                    }
98

99
                    auto current_user = users.get_user(access_node->data(), access_node, use);
10✔
100

101
                    if (use == Use::READ) {
10✔
102
                        bool found = false;
10✔
103
                        for (auto& user : open_definitions) {
10✔
UNCOV
104
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
UNCOV
105
                                user.second.insert(current_user);
×
UNCOV
106
                                found = true;
×
UNCOV
107
                            }
×
108
                        }
109
                        if (!found) {
10✔
110
                            undefined.insert(current_user);
10✔
111
                        }
10✔
112
                    }
10✔
113
                }
10✔
114
            }
21✔
115
        } else if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node)) {
32✔
116
            if (tasklet->is_conditional()) {
11✔
117
                auto& condition = tasklet->condition();
×
118
                for (auto& atom : symbolic::atoms(condition)) {
×
119
                    auto current_user = users.get_user(atom->get_name(), tasklet, Use::READ);
×
120
                    {
121
                        bool found = false;
×
122
                        for (auto& user : open_definitions) {
×
123
                            if (intersects(*user.first, *current_user, assumptions_analysis)) {
×
124
                                user.second.insert(current_user);
×
125
                                found = true;
×
126
                            }
×
127
                        }
128
                        if (!found) {
×
129
                            undefined.insert(current_user);
×
130
                        }
×
131
                    }
132
                }
133
            }
×
134
        }
11✔
135

136
        for (auto& oedge : dataflow.out_edges(*node)) {
53✔
137
            std::unordered_set<std::string> used;
21✔
138
            for (auto& dim : oedge.subset()) {
39✔
139
                for (auto atom : symbolic::atoms(dim)) {
36✔
140
                    used.insert(atom->get_name());
18✔
141
                }
18✔
142
            }
143
            for (auto& atom : used) {
39✔
144
                auto current_user = users.get_user(atom, &oedge, Use::READ);
18✔
145

146
                {
147
                    bool found = false;
18✔
148
                    for (auto& user : open_definitions) {
20✔
149
                        if (intersects(*user.first, *current_user, assumptions_analysis)) {
2✔
150
                            user.second.insert(current_user);
2✔
151
                            found = true;
2✔
152
                        }
2✔
153
                    }
154
                    if (!found) {
18✔
155
                        undefined.insert(current_user);
16✔
156
                    }
16✔
157
                }
158
            }
159
        }
21✔
160
    }
161
}
74✔
162

163
void DataDependencyAnalysis::visit_for(
15✔
164
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
165
    structured_control_flow::StructuredLoop& for_loop, std::unordered_set<User*>& undefined,
166
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
167
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
168
    // Init - Read
169
    for (auto atom : symbolic::atoms(for_loop.init())) {
18✔
170
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, true);
3✔
171

172
        bool found = false;
3✔
173
        for (auto& user : open_definitions) {
4✔
174
            if (user.first->container() == atom->get_name()) {
1✔
175
                user.second.insert(current_user);
1✔
176
                found = true;
1✔
177
            }
1✔
178
        }
179
        if (!found) {
3✔
180
            undefined.insert(current_user);
2✔
181
        }
2✔
182
    }
3✔
183

184
    // Init - Write
185
    {
186
        // Write Induction Variable
187
        auto current_user =
15✔
188
            users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE, true);
15✔
189

190
        std::unordered_set<User*> to_close;
15✔
191
        for (auto& user : open_definitions) {
18✔
192
            if (supersedes(*user.first, *current_user, assumptions_analysis)) {
3✔
193
                to_close.insert(user.first);
2✔
194
            }
2✔
195
        }
196
        for (auto& user : to_close) {
17✔
197
            closed_definitions.insert({user, open_definitions.at(user)});
2✔
198
            open_definitions.erase(user);
2✔
199
        }
200
        open_definitions.insert({current_user, {}});
15✔
201

202
        // Improve: If loop is executed at least once, we can close the init's definition
203
        // TODO: Implement this
204
    }
15✔
205

206
    // Update - Write
207
    {
208
        // Exists after loop and inside body
209
        auto current_user = users.get_user(for_loop.indvar()->get_name(), &for_loop, Use::WRITE,
15✔
210
                                           false, false, true);
211
        open_definitions.insert({current_user, {}});
15✔
212

213
        // Improve: If loop is executed at least once, we can close the init's definition
214
        // TODO: Implement this
215
    }
216

217
    // Condition - Read
218
    for (auto atom : symbolic::atoms(for_loop.condition())) {
44✔
219
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, true);
29✔
220

221
        bool found = false;
29✔
222
        for (auto& user : open_definitions) {
89✔
223
            if (user.first->container() == atom->get_name()) {
60✔
224
                user.second.insert(current_user);
31✔
225
                found = true;
31✔
226
            }
31✔
227
        }
228
        if (!found) {
29✔
229
            undefined.insert(current_user);
13✔
230
        }
13✔
231
    }
29✔
232

233
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_for;
15✔
234
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_for;
15✔
235
    std::unordered_set<User*> undefined_for;
15✔
236

237
    // Add assumptions for body
238
    visit_sequence(users, assumptions_analysis, for_loop.root(), undefined_for,
15✔
239
                   open_definitions_for, closed_definitions_for);
240

241
    // Update - Read
242
    for (auto atom : symbolic::atoms(for_loop.update())) {
30✔
243
        auto current_user =
15✔
244
            users.get_user(atom->get_name(), &for_loop, Use::READ, false, false, true);
15✔
245

246
        bool found = false;
15✔
247
        for (auto& user : open_definitions_for) {
30✔
248
            if (user.first->container() == atom->get_name()) {
15✔
249
                user.second.insert(current_user);
1✔
250
                found = true;
1✔
251
            }
1✔
252
        }
253
        if (!found) {
15✔
254
            undefined_for.insert(current_user);
14✔
255
        }
14✔
256
    }
15✔
257

258
    // Merge for with outside
259

260
    // Closed definitions are simply merged
261
    for (auto& entry : closed_definitions_for) {
15✔
262
        closed_definitions.insert(entry);
×
263
    }
264

265
    // Undefined reads are matched or forwarded
266
    for (auto open_read : undefined_for) {
64✔
267
        bool found = false;
49✔
268
        for (auto& entry : open_definitions) {
148✔
269
            if (intersects(*entry.first, *open_read, assumptions_analysis)) {
99✔
270
                entry.second.insert(open_read);
74✔
271
                found = true;
74✔
272
            }
74✔
273
        }
274
        if (!found) {
49✔
275
            undefined.insert(open_read);
12✔
276
        }
12✔
277
    }
278

279
    // Open definitions may close outside open definitions after loop
280
    std::unordered_set<User*> to_close;
15✔
281
    for (auto& previous : open_definitions) {
46✔
282
        for (auto& user : open_definitions_for) {
61✔
283
            if (supersedes(*previous.first, *user.first, assumptions_analysis)) {
30✔
UNCOV
284
                to_close.insert(previous.first);
×
UNCOV
285
                break;
×
286
            }
287
        }
288
    }
289
    for (auto& user : to_close) {
15✔
UNCOV
290
        closed_definitions.insert({user, open_definitions.at(user)});
×
UNCOV
291
        open_definitions.erase(user);
×
292
    }
293

294
    // Add loop-carried dependencies
295

296
    // Criterion 1: Loop is monotonic -> indvar never takes the same value twice
297
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
15✔
298
    if (is_monotonic) {
15✔
299
        // Case: Can analyze
300
        this->loop_carried_dependencies_.insert({&for_loop, {}});
14✔
301
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
14✔
302

303
        // We can focus on written containers
304

305
        // Case 1: Read-Write between iterations
306
        for (auto& read : undefined_for) {
62✔
307
            for (auto& write : open_definitions_for) {
112✔
308
                if (loop_depends(*write.first, *read, assumptions_analysis, for_loop)) {
64✔
UNCOV
309
                    if (dependencies.find(read->container()) == dependencies.end()) {
×
UNCOV
310
                        dependencies.insert(
×
UNCOV
311
                            {read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
×
UNCOV
312
                    }
×
UNCOV
313
                    write.second.insert(read);
×
UNCOV
314
                }
×
315
            }
316
        }
317

318
        // Case 2: Write-Write between iterations
319
        for (auto& write : open_definitions_for) {
28✔
320
            if (dependencies.find(write.first->container()) != dependencies.end()) {
14✔
321
                continue;
2✔
322
            }
323
            for (auto& write_2 : open_definitions_for) {
28✔
324
                if (loop_depends(*write.first, *write_2.first, assumptions_analysis, for_loop)) {
18✔
325
                    dependencies.insert(
4✔
326
                        {write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
2✔
327
                    break;
2✔
328
                }
329
            }
330
        }
331
    } else {
14✔
332
        // Case: Cannot analyze
333
        this->loop_carried_dependencies_.insert({&for_loop, {}});
1✔
334
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
1✔
335

336
        // Over-Approximation:
337
        // Add loop-carried dependencies for all open reads to all open writes
338
        for (auto& read : undefined_for) {
2✔
339
            for (auto& write : open_definitions_for) {
2✔
340
                if (intersects(*write.first, *read, assumptions_analysis)) {
1✔
341
                    write.second.insert(read);
×
342
                    dependencies.insert({read->container(), LOOP_CARRIED_DEPENDENCY_READ_WRITE});
×
343
                }
×
344
            }
345
        }
346
        // Add loop-carried dependencies for writes
347
        for (auto& write : open_definitions_for) {
2✔
348
            if (dependencies.find(write.first->container()) == dependencies.end()) {
1✔
349
                dependencies.insert(
2✔
350
                    {write.first->container(), LOOP_CARRIED_DEPENDENCY_WRITE_WRITE});
1✔
351
            }
1✔
352
        }
353
    }
354

355
    // Add open definitions from for to outside
356
    for (auto& entry : open_definitions_for) {
30✔
357
        open_definitions.insert(entry);
15✔
358
    }
359
}
15✔
360

361
void DataDependencyAnalysis::visit_if_else(
11✔
362
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
363
    structured_control_flow::IfElse& if_else, std::unordered_set<User*>& undefined,
364
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
365
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
366
    // Read Conditions
367
    for (size_t i = 0; i < if_else.size(); i++) {
32✔
368
        auto child = if_else.at(i).second;
21✔
369
        for (auto atom : symbolic::atoms(child)) {
40✔
370
            auto current_user = users.get_user(atom->get_name(), &if_else, Use::READ);
19✔
371

372
            bool found = false;
19✔
373
            for (auto& user : open_definitions) {
32✔
374
                if (user.first->container() == atom->get_name()) {
13✔
375
                    user.second.insert(current_user);
6✔
376
                    found = true;
6✔
377
                }
6✔
378
            }
379
            if (!found) {
19✔
380
                undefined.insert(current_user);
13✔
381
            }
13✔
382
        }
19✔
383
    }
21✔
384

385
    std::vector<std::unordered_set<User*>> undefined_branches(if_else.size());
11✔
386
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> open_definitions_branches(
11✔
387
        if_else.size());
11✔
388
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> closed_definitionss_branches(
11✔
389
        if_else.size());
11✔
390
    for (size_t i = 0; i < if_else.size(); i++) {
32✔
391
        auto& child = if_else.at(i).first;
21✔
392
        visit_sequence(users, assumptions_analysis, child, undefined_branches.at(i),
42✔
393
                       open_definitions_branches.at(i), closed_definitionss_branches.at(i));
21✔
394
    }
21✔
395

396
    // merge partial open reads
397
    for (size_t i = 0; i < if_else.size(); i++) {
32✔
398
        for (auto& entry : undefined_branches.at(i)) {
25✔
399
            bool found = false;
4✔
400
            for (auto& entry2 : open_definitions) {
8✔
401
                if (entry2.first->container() == entry->container()) {
4✔
402
                    entry2.second.insert(entry);
4✔
403
                    found = true;
4✔
404
                }
4✔
405
            }
406
            if (!found) {
4✔
407
                undefined.insert(entry);
×
408
            }
×
409
        }
410
    }
21✔
411

412
    // merge closed writes
413
    for (auto& closing : closed_definitionss_branches) {
32✔
414
        for (auto& entry : closing) {
21✔
415
            closed_definitions.insert(entry);
×
416
        }
417
    }
418

419
    // Close open reads_after_writes for complete branches
420
    if (if_else.is_complete()) {
11✔
421
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
10✔
422
        std::unordered_set<std::string> candidates;
10✔
423
        std::unordered_set<std::string> candidates_tmp;
10✔
424

425
        /* Complete close open reads_after_writes
426
        1. get candidates from first iteration
427
        2. iterate over all branches and prune candidates
428
        3. find prior writes for remaining candidates
429
        4. close open reads_after_writes for all candidates
430
        */
431
        for (auto& entry : open_definitions_branches.at(0)) {
19✔
432
            candidates.insert(entry.first->container());
9✔
433
        }
434
        for (auto& entry : closed_definitionss_branches.at(0)) {
10✔
435
            candidates.insert(entry.first->container());
×
436
        }
437

438
        for (size_t i = 1; i < if_else.size(); i++) {
20✔
439
            for (auto& entry : open_definitions_branches.at(i)) {
18✔
440
                if (candidates.find(entry.first->container()) != candidates.end()) {
8✔
441
                    candidates_tmp.insert(entry.first->container());
8✔
442
                }
8✔
443
            }
444
            candidates.swap(candidates_tmp);
10✔
445
            candidates_tmp.clear();
10✔
446
        }
10✔
447

448
        for (auto& entry : open_definitions) {
17✔
449
            if (candidates.find(entry.first->container()) != candidates.end()) {
7✔
450
                to_close.insert(entry);
3✔
451
            }
3✔
452
        }
453

454
        for (auto& entry : to_close) {
13✔
455
            open_definitions.erase(entry.first);
3✔
456
            closed_definitions.insert(entry);
3✔
457
        }
458
    } else {
10✔
459
        // Over-Approximation: Add all branch definitions to outer definitions or undefined
460
        // Here we add writes to read sets as "anti-reads"
461
        for (auto& branch : open_definitions_branches) {
2✔
462
            for (auto& def : branch) {
2✔
463
                bool found = false;
1✔
464
                for (auto& entry : open_definitions) {
2✔
465
                    if (intersects(*entry.first, *def.first, assumptions_analysis)) {
1✔
466
                        entry.second.insert(def.first);
1✔
467
                        found = true;
1✔
468
                    }
1✔
469
                }
470
                if (!found) {
1✔
UNCOV
471
                    undefined.insert(def.first);
×
UNCOV
472
                }
×
473
            }
474
        }
475
    }
476

477
    // merge open reads_after_writes
478
    for (auto& branch : open_definitions_branches) {
32✔
479
        for (auto& entry : branch) {
39✔
480
            open_definitions.insert(entry);
18✔
481
        }
482
    }
483
}
11✔
484

485
void DataDependencyAnalysis::visit_while(
7✔
486
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
487
    structured_control_flow::While& while_loop, std::unordered_set<User*>& undefined,
488
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
489
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
490
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_while;
7✔
491
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_while;
7✔
492
    std::unordered_set<User*> undefined_while;
7✔
493

494
    visit_sequence(users, assumptions_analysis, while_loop.root(), undefined_while,
7✔
495
                   open_definitions_while, closed_definitions_while);
496

497
    // Scope-local closed definitions
498
    for (auto& entry : closed_definitions_while) {
9✔
499
        closed_definitions.insert(entry);
2✔
500
    }
501

502
    for (auto open_read : undefined_while) {
9✔
503
        // Over-Approximation: Add loop-carried dependencies for all open reads
504
        for (auto& entry : open_definitions_while) {
5✔
505
            if (entry.first->container() == open_read->container()) {
3✔
506
                entry.second.insert(open_read);
2✔
507
            }
2✔
508
        }
509

510
        // Connect to outside
511
        bool found = false;
2✔
512
        for (auto& entry : open_definitions) {
3✔
513
            if (entry.first->container() == open_read->container()) {
1✔
514
                entry.second.insert(open_read);
1✔
515
                found = true;
1✔
516
            }
1✔
517
        }
518
        if (!found) {
2✔
519
            undefined.insert(open_read);
1✔
520
        }
1✔
521
    }
522

523
    // Add open definitions from while to outside
524
    for (auto& entry : open_definitions_while) {
19✔
525
        open_definitions.insert(entry);
12✔
526
    }
527
}
7✔
528

529
void DataDependencyAnalysis::visit_return(
×
530
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
531
    structured_control_flow::Return& return_statement, std::unordered_set<User*>& undefined,
532
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
533
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
534
    // close all open reads_after_writes
535
    for (auto& entry : open_definitions) {
×
536
        closed_definitions.insert(entry);
×
537
    }
538
    open_definitions.clear();
×
539
}
×
540

541
void DataDependencyAnalysis::visit_sequence(
83✔
542
    analysis::Users& users, analysis::AssumptionsAnalysis& assumptions_analysis,
543
    structured_control_flow::Sequence& sequence, std::unordered_set<User*>& undefined,
544
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
545
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions) {
546
    for (size_t i = 0; i < sequence.size(); i++) {
198✔
547
        auto child = sequence.at(i);
115✔
548
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
115✔
549
            visit_block(users, assumptions_analysis, *block, undefined, open_definitions,
148✔
550
                        closed_definitions);
74✔
551
        } else if (auto for_loop =
115✔
552
                       dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
41✔
553
            visit_for(users, assumptions_analysis, *for_loop, undefined, open_definitions,
30✔
554
                      closed_definitions);
15✔
555
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
41✔
556
            visit_if_else(users, assumptions_analysis, *if_else, undefined, open_definitions,
22✔
557
                          closed_definitions);
11✔
558
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
26✔
559
            visit_while(users, assumptions_analysis, *while_loop, undefined, open_definitions,
14✔
560
                        closed_definitions);
7✔
561
        } else if (auto return_statement =
15✔
562
                       dynamic_cast<structured_control_flow::Return*>(&child.first)) {
8✔
563
            visit_return(users, assumptions_analysis, *return_statement, undefined,
×
564
                         open_definitions, closed_definitions);
×
565
        } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&child.first)) {
8✔
566
            visit_sequence(users, assumptions_analysis, *sequence, undefined, open_definitions,
×
567
                           closed_definitions);
×
568
        }
×
569

570
        // handle transitions read
571
        for (auto& entry : child.second.assignments()) {
168✔
572
            for (auto& atom : symbolic::atoms(entry.second)) {
74✔
573
                if (symbolic::is_pointer(atom)) {
21✔
574
                    continue;
×
575
                }
576
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
21✔
577

578
                bool found = false;
21✔
579
                for (auto& user : open_definitions) {
39✔
580
                    if (user.first->container() == atom->get_name()) {
18✔
581
                        user.second.insert(current_user);
17✔
582
                        found = true;
17✔
583
                    }
17✔
584
                }
585
                if (!found) {
21✔
586
                    undefined.insert(current_user);
10✔
587
                }
10✔
588
            }
589
        }
590

591
        // handle transitions write
592
        for (auto& entry : child.second.assignments()) {
168✔
593
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
53✔
594

595
            std::unordered_set<User*> to_close;
53✔
596
            for (auto& user : open_definitions) {
72✔
597
                if (user.first->container() == entry.first->get_name()) {
19✔
UNCOV
598
                    to_close.insert(user.first);
×
UNCOV
599
                }
×
600
            }
601
            for (auto& user : to_close) {
53✔
UNCOV
602
                closed_definitions.insert({user, open_definitions.at(user)});
×
UNCOV
603
                open_definitions.erase(user);
×
604
            }
605
            open_definitions.insert({current_user, {}});
53✔
606
        }
53✔
607
    }
115✔
608
}
83✔
609

610
bool DataDependencyAnalysis::loop_depends(User& previous, User& current,
82✔
611
                                          analysis::AssumptionsAnalysis& assumptions_analysis,
612
                                          structured_control_flow::StructuredLoop& loop) {
613
    if (previous.container() != current.container()) {
82✔
614
        return false;
66✔
615
    }
616
    // Shortcut for scalars
617
    auto& type = this->sdfg_.type(previous.container());
16✔
618
    if (dynamic_cast<const types::Scalar*>(&type)) {
16✔
619
        return true;
2✔
620
    }
621

622
    auto& previous_subsets = previous.subsets();
14✔
623
    auto& current_subsets = current.subsets();
14✔
624

625
    auto previous_scope = Users::scope(&previous);
14✔
626
    auto previous_assumptions = assumptions_analysis.get(loop.root(), *previous_scope, true);
14✔
627
    auto current_scope = Users::scope(&current);
14✔
628
    auto current_assumptions = assumptions_analysis.get(loop.root(), *current_scope, true);
14✔
629

630
    // Check if previous subset is subset of any current subset
631
    for (auto& previous_subset : previous_subsets) {
28✔
632
        for (auto& current_subset : current_subsets) {
28✔
633
            if (symbolic::maps::intersects(previous_subset, current_subset, loop.indvar(),
14✔
634
                                           previous_assumptions, current_assumptions)) {
UNCOV
635
                return true;
×
636
            }
637
        }
638
    }
639

640
    return false;
14✔
641
}
82✔
642

643
bool DataDependencyAnalysis::supersedes(User& previous, User& current,
35✔
644
                                        analysis::AssumptionsAnalysis& assumptions_analysis) {
645
    if (previous.container() != current.container()) {
35✔
646
        return false;
33✔
647
    }
648
    // Shortcut for scalars
649
    auto& type = this->sdfg_.type(previous.container());
2✔
650
    if (dynamic_cast<const types::Scalar*>(&type)) {
2✔
651
        return true;
2✔
652
    }
653

UNCOV
654
    auto& previous_subsets = previous.subsets();
×
UNCOV
655
    auto& current_subsets = current.subsets();
×
UNCOV
656
    auto previous_scope = Users::scope(&previous);
×
UNCOV
657
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
×
UNCOV
658
    auto current_scope = Users::scope(&current);
×
UNCOV
659
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
×
660

661
    // Check if previous subset is subset of any current subset
UNCOV
662
    for (auto& previous_subset : previous_subsets) {
×
UNCOV
663
        bool found = false;
×
UNCOV
664
        for (auto& current_subset : current_subsets) {
×
UNCOV
665
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions,
×
666
                                    current_assumptions)) {
UNCOV
667
                found = true;
×
UNCOV
668
                break;
×
669
            }
670
        }
UNCOV
671
        if (!found) {
×
UNCOV
672
            return false;
×
673
        }
674
    }
675

UNCOV
676
    return true;
×
677
}
35✔
678

679
bool DataDependencyAnalysis::intersects(User& previous, User& current,
103✔
680
                                        analysis::AssumptionsAnalysis& assumptions_analysis) {
681
    if (previous.container() != current.container()) {
103✔
682
        return false;
26✔
683
    }
684
    // Shortcut for scalars
685
    auto& type = this->sdfg_.type(previous.container());
77✔
686
    if (dynamic_cast<const types::Scalar*>(&type)) {
77✔
687
        return true;
77✔
688
    }
689

UNCOV
690
    auto& previous_subsets = previous.subsets();
×
UNCOV
691
    auto& current_subsets = current.subsets();
×
692

UNCOV
693
    auto previous_scope = Users::scope(&previous);
×
UNCOV
694
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
×
UNCOV
695
    auto current_scope = Users::scope(&current);
×
UNCOV
696
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
×
697

698
    // Check if any current subset intersects with any previous subset
UNCOV
699
    bool found = false;
×
UNCOV
700
    for (auto& current_subset : current_subsets) {
×
UNCOV
701
        for (auto& previous_subset : previous_subsets) {
×
UNCOV
702
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions,
×
703
                                       previous_assumptions)) {
UNCOV
704
                found = true;
×
UNCOV
705
                break;
×
706
            }
707
        }
UNCOV
708
        if (found) {
×
UNCOV
709
            break;
×
710
        }
711
    }
712

UNCOV
713
    return found;
×
714
}
103✔
715

716
/****** Public API ******/
717

718
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
719
    assert(write.use() == Use::WRITE);
×
720
    if (results_.find(write.container()) == results_.end()) {
×
721
        return {};
×
722
    }
723
    auto& raws = results_.at(write.container());
×
724
    assert(raws.find(&write) != raws.end());
×
725

726
    auto& reads_for_write = raws.at(&write);
×
727

728
    std::unordered_set<User*> reads;
×
729
    for (auto& entry : reads_for_write) {
×
730
        reads.insert(entry);
×
731
    }
732

733
    return reads;
×
734
};
×
735

736
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(
33✔
737
    const std::string& container) {
738
    if (results_.find(container) == results_.end()) {
33✔
739
        return {};
×
740
    }
741
    return results_.at(container);
33✔
742
};
33✔
743

744
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::defined_by(
19✔
745
    const std::string& container) {
746
    auto reads = this->definitions(container);
19✔
747

748
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
19✔
749
    for (auto& entry : reads) {
43✔
750
        for (auto& read : entry.second) {
51✔
751
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
27✔
752
                read_to_writes_map[read] = {};
20✔
753
            }
20✔
754
            read_to_writes_map[read].insert(entry.first);
27✔
755
        }
756
    }
757
    return read_to_writes_map;
19✔
758
};
19✔
759

760
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
761
    assert(read.use() == Use::READ);
×
762
    auto definitions = this->definitions(read.container());
×
763

764
    std::unordered_set<User*> writes;
×
765
    for (auto& entry : definitions) {
×
766
        for (auto& r : entry.second) {
×
767
            if (&read == r) {
×
768
                writes.insert(entry.first);
×
769
            }
×
770
        }
771
    }
772
    return writes;
×
773
};
×
774

775
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
776
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
777
};
778

779
const std::unordered_map<std::string, LoopCarriedDependency>& DataDependencyAnalysis::dependencies(
8✔
780
    structured_control_flow::StructuredLoop& loop) const {
781
    return this->loop_carried_dependencies_.at(&loop);
8✔
782
};
783

784
}  // namespace analysis
785
}  // 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