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

daisytuner / docc / 23049466917

13 Mar 2026 11:48AM UTC coverage: 63.722% (+0.2%) from 63.488%
23049466917

push

github

web-flow
Merge pull request #580 from daisytuner/advanced-loop-tiling

Dependence Deltas, LoopInterchange with Fourier-Motzkin and Novel TileFusion Transformation

613 of 845 new or added lines in 8 files covered. (72.54%)

5 existing lines in 3 files now uncovered.

25274 of 39663 relevant lines covered (63.72%)

400.64 hits per line

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

89.38
/sdfg/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 <isl/ctx.h>
10
#include <isl/map.h>
11
#include <isl/options.h>
12
#include <isl/set.h>
13

14
#include "sdfg/analysis/analysis.h"
15
#include "sdfg/analysis/loop_analysis.h"
16
#include "sdfg/structured_sdfg.h"
17
#include "sdfg/symbolic/maps.h"
18
#include "sdfg/symbolic/sets.h"
19

20
namespace sdfg {
21
namespace analysis {
22

23
DataDependencyAnalysis::DataDependencyAnalysis(StructuredSDFG& sdfg, bool detailed)
24
    : Analysis(sdfg), node_(sdfg.root()), detailed_(detailed) {
272✔
25

26
      };
272✔
27

28
DataDependencyAnalysis::DataDependencyAnalysis(StructuredSDFG& sdfg, structured_control_flow::Sequence& node, bool detailed)
29
    : Analysis(sdfg), node_(node), detailed_(detailed) {
×
30

31
      };
×
32

33
void DataDependencyAnalysis::run(analysis::AnalysisManager& analysis_manager) {
247✔
34
    results_.clear();
247✔
35
    undefined_users_.clear();
247✔
36
    loop_carried_dependencies_.clear();
247✔
37

38
    std::unordered_set<User*> undefined;
247✔
39
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions;
247✔
40
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions;
247✔
41

42
    visit_sequence(analysis_manager, node_, undefined, open_definitions, closed_definitions);
247✔
43

44
    for (auto& entry : open_definitions) {
2,245✔
45
        closed_definitions.insert(entry);
2,245✔
46
    }
2,245✔
47

48
    for (auto& entry : closed_definitions) {
2,322✔
49
        if (results_.find(entry.first->container()) == results_.end()) {
2,322✔
50
            results_.insert({entry.first->container(), {}});
1,305✔
51
        }
1,305✔
52
        results_.at(entry.first->container()).insert(entry);
2,322✔
53
    }
2,322✔
54
};
247✔
55

56
/****** Visitor API ******/
57

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

69
    auto& dataflow = block.dataflow();
850✔
70

71
    for (auto node : dataflow.topological_sort()) {
2,935✔
72
        if (dynamic_cast<data_flow::ConstantNode*>(node) != nullptr) {
2,935✔
73
            continue;
140✔
74
        }
140✔
75

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

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

91
                        // Close open definitions if possible
92
                        std::unordered_map<User*, std::unordered_set<User*>> to_close;
783✔
93
                        for (auto& user : open_definitions) {
783✔
94
                            if (this->closes(analysis_manager, *user.first, *current_user, false)) {
703✔
95
                                to_close.insert(user);
66✔
96
                            }
66✔
97
                        }
703✔
98
                        for (auto& user : to_close) {
783✔
99
                            open_definitions.erase(user.first);
66✔
100
                            closed_definitions.insert(user);
66✔
101
                        }
66✔
102

103
                        // Start new open definition
104
                        open_definitions.insert({current_user, {}});
783✔
105
                    }
783✔
106
                }
784✔
107
                if (dataflow.out_degree(*access_node) > 0) {
2,010✔
108
                    Use use = Use::READ;
1,255✔
109
                    for (auto& oedge : dataflow.out_edges(*access_node)) {
1,327✔
110
                        if (oedge.type() == data_flow::MemletType::Reference ||
1,327✔
111
                            oedge.type() == data_flow::MemletType::Dereference_Dst) {
1,327✔
112
                            use = Use::VIEW;
×
113
                            break;
×
114
                        }
×
115
                    }
1,327✔
116

117
                    if (use == Use::READ) {
1,255✔
118
                        auto current_user = users.get_user(access_node->data(), access_node, use);
1,255✔
119

120
                        // Assign to open definitions
121
                        bool found_user = false;
1,255✔
122
                        bool found_undefined_user = false;
1,255✔
123
                        for (auto& user : open_definitions) {
1,255✔
124
                            if (this->depends(analysis_manager, *user.first, *current_user)) {
1,187✔
125
                                user.second.insert(current_user);
307✔
126
                                found_user = true;
307✔
127
                                found_undefined_user = this->is_undefined_user(*user.first);
307✔
128
                            }
307✔
129
                        }
1,187✔
130
                        // If no definition found or undefined user found, mark as undefined
131
                        if (!found_user || found_undefined_user) {
1,255✔
132
                            undefined.insert(current_user);
992✔
133
                        }
992✔
134
                    }
1,255✔
135
                }
1,255✔
136
            }
2,010✔
137
        } else if (auto library_node = dynamic_cast<data_flow::LibraryNode*>(node)) {
2,010✔
138
            for (auto& symbol : library_node->symbols()) {
9✔
139
                auto current_user = users.get_user(symbol->get_name(), library_node, Use::READ);
2✔
140

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

158
        for (auto& oedge : dataflow.out_edges(*node)) {
2,795✔
159
            std::unordered_set<std::string> used;
2,110✔
160
            for (auto& dim : oedge.subset()) {
2,503✔
161
                for (auto atom : symbolic::atoms(dim)) {
2,503✔
162
                    used.insert(atom->get_name());
2,476✔
163
                }
2,476✔
164
            }
2,503✔
165
            for (auto& atom : used) {
2,441✔
166
                auto current_user = users.get_user(atom, &oedge, Use::READ);
2,441✔
167

168
                // Assign to open definitions
169
                bool found_user = false;
2,441✔
170
                bool found_undefined_user = false;
2,441✔
171
                for (auto& user : open_definitions) {
2,441✔
172
                    if (this->depends(analysis_manager, *user.first, *current_user)) {
2,031✔
173
                        user.second.insert(current_user);
6✔
174
                        found_user = true;
6✔
175
                        found_undefined_user = this->is_undefined_user(*user.first);
6✔
176
                    }
6✔
177
                }
2,031✔
178
                // If no definition found or undefined user found, mark as undefined
179
                if (!found_user || found_undefined_user) {
2,441✔
180
                    undefined.insert(current_user);
2,435✔
181
                }
2,435✔
182
            }
2,441✔
183
        }
2,110✔
184
    }
2,795✔
185
}
850✔
186

187
void DataDependencyAnalysis::visit_for(
188
    analysis::AnalysisManager& analysis_manager,
189
    structured_control_flow::StructuredLoop& for_loop,
190
    std::unordered_set<User*>& undefined,
191
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
192
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
193
) {
744✔
194
    auto& users = analysis_manager.get<analysis::Users>();
744✔
195

196
    // Init - Read
197
    for (auto atom : symbolic::atoms(for_loop.init())) {
744✔
198
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, true);
20✔
199

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

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

221
        // Close open definitions if possible
222
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
744✔
223
        for (auto& user : open_definitions) {
1,305✔
224
            if (this->closes(analysis_manager, *user.first, *current_user, true)) {
1,305✔
225
                to_close.insert(user);
2✔
226
            }
2✔
227
        }
1,305✔
228
        for (auto& user : to_close) {
744✔
229
            open_definitions.erase(user.first);
2✔
230
            closed_definitions.insert(user);
2✔
231
        }
2✔
232

233
        // Start new open definition
234
        open_definitions.insert({current_user, {}});
744✔
235
    }
744✔
236

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

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

247
        // Assign to open definitions
248
        bool found_user = false;
1,460✔
249
        bool found_undefined_user = false;
1,460✔
250
        for (auto& user : open_definitions) {
5,517✔
251
            if (this->depends(analysis_manager, *user.first, *current_user)) {
5,517✔
252
                user.second.insert(current_user);
1,489✔
253
                found_user = true;
1,489✔
254
                found_undefined_user = this->is_undefined_user(*user.first);
1,489✔
255
            }
1,489✔
256
        }
5,517✔
257
        // If no definition found or undefined user found, mark as undefined
258
        if (!found_user || found_undefined_user) {
1,460✔
259
            undefined.insert(current_user);
715✔
260
        }
715✔
261
    }
1,460✔
262

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

267
    // Add assumptions for body
268
    visit_sequence(analysis_manager, for_loop.root(), undefined_for, open_definitions_for, closed_definitions_for);
744✔
269

270
    // Update - Read
271
    for (auto atom : symbolic::atoms(for_loop.update())) {
744✔
272
        auto current_user = users.get_user(atom->get_name(), &for_loop, Use::READ, false, false, true);
744✔
273

274
        // Assign to open definitions
275
        bool found_user = false;
744✔
276
        bool found_undefined_user = false;
744✔
277
        for (auto& user : open_definitions_for) {
2,887✔
278
            if (this->depends(analysis_manager, *user.first, *current_user)) {
2,887✔
279
                user.second.insert(current_user);
1✔
280
                found_user = true;
1✔
281
                found_undefined_user = this->is_undefined_user(*user.first);
1✔
282
            }
1✔
283
        }
2,887✔
284
        // If no definition found or undefined user found, mark as undefined
285
        if (!found_user || found_undefined_user) {
744✔
286
            undefined_for.insert(current_user);
743✔
287
        }
743✔
288
    }
744✔
289

290
    // Merge for with outside
291
    auto& dominance_analysis = analysis_manager.get<analysis::DominanceAnalysis>();
744✔
292

293
    // Closed definitions are simply merged
294
    for (auto& entry : closed_definitions_for) {
744✔
295
        closed_definitions.insert(entry);
101✔
296
    }
101✔
297

298
    // Undefined reads are matched or forwarded
299
    for (auto open_read : undefined_for) {
7,670✔
300
        // Simple check: no match or undefined user
301
        std::unordered_set<User*> frontier;
7,670✔
302
        bool found = false;
7,670✔
303
        bool found_undefined_user = false;
7,670✔
304
        for (auto& entry : open_definitions) {
27,058✔
305
            if (intersects(*entry.first, *open_read, analysis_manager)) {
27,058✔
306
                entry.second.insert(open_read);
6,856✔
307
                found = true;
6,856✔
308
                found_undefined_user = this->is_undefined_user(*entry.first);
6,856✔
309
                frontier.insert(entry.first);
6,856✔
310
            }
6,856✔
311
        }
27,058✔
312
        if (!found || found_undefined_user) {
7,670✔
313
            undefined.insert(open_read);
4,138✔
314
            continue;
4,138✔
315
        }
4,138✔
316

317
        // Users found, check if they fully cover the read
318
        bool covered = false;
3,532✔
319
        for (auto& entry : frontier) {
3,850✔
320
            if (!dominance_analysis.dominates(*entry, *open_read)) {
3,850✔
321
                continue;
501✔
322
            }
501✔
323
            bool covers = supersedes_restrictive(*open_read, *entry, analysis_manager);
3,349✔
324
            if (covers) {
3,349✔
325
                covered = true;
3,344✔
326
                break;
3,344✔
327
            }
3,344✔
328
        }
3,349✔
329
        if (!covered) {
3,532✔
330
            undefined.insert(open_read);
188✔
331
        }
188✔
332
    }
3,532✔
333

334
    // Open definitions may close outside open definitions after loop
335
    std::unordered_set<User*> to_close;
744✔
336
    for (auto& previous : open_definitions) {
2,791✔
337
        for (auto& user : open_definitions_for) {
9,592✔
338
            if (this->closes(analysis_manager, *previous.first, *user.first, true)) {
9,592✔
339
                to_close.insert(previous.first);
×
340
                break;
×
341
            }
×
342
        }
9,592✔
343
    }
2,791✔
344
    for (auto& user : to_close) {
744✔
345
        closed_definitions.insert({user, open_definitions.at(user)});
×
346
        open_definitions.erase(user);
×
347
    }
×
348

349
    // Add loop-carried dependencies
350
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
744✔
351
    bool is_monotonic = LoopAnalysis::is_monotonic(&for_loop, assumptions_analysis);
744✔
352
    if (this->detailed_ && is_monotonic) {
744✔
353
        // Case: Can analyze
354
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
607✔
355
        assert(success);
607✔
356
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
607✔
357

358
        // Helper to merge deltas into an existing entry for a container
359
        auto merge_deltas = [](LoopCarriedDependencyInfo& info, const symbolic::maps::DependenceDeltas& new_deltas) {
607✔
360
            if (new_deltas.empty) {
153✔
NEW
361
                return;
×
NEW
362
            }
×
363
            if (info.deltas.empty) {
153✔
NEW
364
                info.deltas = new_deltas;
×
NEW
365
                return;
×
NEW
366
            }
×
367
            if (new_deltas.deltas_str.empty() || info.deltas.deltas_str.empty()) {
153✔
368
                // One side has no isl representation — keep non-empty or stay empty-string
369
                info.deltas.empty = false;
59✔
370
                return;
59✔
371
            }
59✔
372
            // Union the two delta sets
373
            isl_ctx* ctx = isl_ctx_alloc();
94✔
374
            isl_options_set_on_error(ctx, ISL_ON_ERROR_CONTINUE);
94✔
375
            isl_set* s1 = isl_set_read_from_str(ctx, info.deltas.deltas_str.c_str());
94✔
376
            isl_set* s2 = isl_set_read_from_str(ctx, new_deltas.deltas_str.c_str());
94✔
377
            if (s1 && s2) {
94✔
378
                isl_set* u = isl_set_union(s1, s2);
94✔
379
                char* str = u ? isl_set_to_str(u) : nullptr;
94✔
380
                if (str) {
94✔
381
                    info.deltas.deltas_str = std::string(str);
42✔
382
                    free(str);
42✔
383
                } else {
52✔
384
                    // Union failed (e.g. mismatched dimensionalities) — degrade gracefully
385
                    info.deltas.deltas_str = "";
52✔
386
                    info.deltas.dimensions.clear();
52✔
387
                }
52✔
388
                if (u) isl_set_free(u);
94✔
389
            } else {
94✔
NEW
390
                if (s1) isl_set_free(s1);
×
NEW
391
                if (s2) isl_set_free(s2);
×
392
                // Parse failed — degrade gracefully
NEW
393
                info.deltas.deltas_str = "";
×
NEW
394
                info.deltas.dimensions.clear();
×
NEW
395
            }
×
396
            isl_ctx_free(ctx);
94✔
397
        };
94✔
398

399
        // Case 1: Read-Write between iterations
400
        for (auto& read : undefined_for) {
6,183✔
401
            for (auto& write : open_definitions_for) {
29,416✔
402
                auto deltas = loop_deltas(*write.first, *read, analysis_manager, for_loop);
29,416✔
403
                if (!deltas.empty) {
29,416✔
404
                    if (dependencies.find(read->container()) == dependencies.end()) {
433✔
405
                        dependencies[read->container()] =
280✔
406
                            LoopCarriedDependencyInfo{LOOP_CARRIED_DEPENDENCY_READ_WRITE, deltas};
280✔
407
                    } else {
280✔
408
                        merge_deltas(dependencies[read->container()], deltas);
153✔
409
                    }
153✔
410
                    write.second.insert(read);
433✔
411
                }
433✔
412
            }
29,416✔
413
        }
6,183✔
414

415
        // Case 2: Write-Write between iterations
416
        for (auto& write : open_definitions_for) {
2,348✔
417
            if (dependencies.find(write.first->container()) != dependencies.end()) {
2,348✔
418
                continue;
926✔
419
            }
926✔
420
            for (auto& write_2 : open_definitions_for) {
5,744✔
421
                auto deltas = loop_deltas(*write.first, *write_2.first, analysis_manager, for_loop);
5,744✔
422
                if (!deltas.empty) {
5,744✔
423
                    dependencies.insert(
917✔
424
                        {write.first->container(),
917✔
425
                         LoopCarriedDependencyInfo{LOOP_CARRIED_DEPENDENCY_WRITE_WRITE, deltas}}
917✔
426
                    );
917✔
427
                    break;
917✔
428
                }
917✔
429
            }
5,744✔
430
        }
1,422✔
431
    } else {
607✔
432
        // Case: Cannot analyze
433
        bool success = this->loop_carried_dependencies_.insert({&for_loop, {}}).second;
137✔
434
        assert(success);
137✔
435
        auto& dependencies = this->loop_carried_dependencies_.at(&for_loop);
137✔
436

437
        // Over-Approximation:
438
        // Add loop-carried dependencies for all open reads to all open writes
439
        for (auto& read : undefined_for) {
1,487✔
440
            for (auto& write : open_definitions_for) {
7,103✔
441
                if (this->depends(analysis_manager, *write.first, *read)) {
7,103✔
442
                    write.second.insert(read);
232✔
443
                    dependencies.insert(
232✔
444
                        {read->container(),
232✔
445
                         LoopCarriedDependencyInfo{
232✔
446
                             LOOP_CARRIED_DEPENDENCY_READ_WRITE, symbolic::maps::DependenceDeltas{false, "", {}}
232✔
447
                         }}
232✔
448
                    );
232✔
449
                }
232✔
450
            }
7,103✔
451
        }
1,487✔
452
        // Add loop-carried dependencies for writes
453
        for (auto& write : open_definitions_for) {
539✔
454
            if (dependencies.find(write.first->container()) == dependencies.end()) {
539✔
455
                dependencies.insert(
245✔
456
                    {write.first->container(),
245✔
457
                     LoopCarriedDependencyInfo{
245✔
458
                         LOOP_CARRIED_DEPENDENCY_WRITE_WRITE, symbolic::maps::DependenceDeltas{false, "", {}}
245✔
459
                     }}
245✔
460
                );
245✔
461
            }
245✔
462
        }
539✔
463
    }
137✔
464

465
    // Add open definitions from for to outside
466
    for (auto& entry : open_definitions_for) {
2,887✔
467
        open_definitions.insert(entry);
2,887✔
468
    }
2,887✔
469
}
744✔
470

471
void DataDependencyAnalysis::visit_if_else(
472
    analysis::AnalysisManager& analysis_manager,
473
    structured_control_flow::IfElse& if_else,
474
    std::unordered_set<User*>& undefined,
475
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
476
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
477
) {
29✔
478
    auto& users = analysis_manager.get<analysis::Users>();
29✔
479

480
    // Read Conditions
481
    for (size_t i = 0; i < if_else.size(); i++) {
80✔
482
        auto child = if_else.at(i).second;
51✔
483
        for (auto atom : symbolic::atoms(child)) {
51✔
484
            auto current_user = users.get_user(atom->get_name(), &if_else, Use::READ);
40✔
485

486
            bool found_user = false;
40✔
487
            bool found_undefined_user = false;
40✔
488
            for (auto& user : open_definitions) {
40✔
489
                if (this->depends(analysis_manager, *user.first, *current_user)) {
20✔
490
                    user.second.insert(current_user);
9✔
491
                    found_user = true;
9✔
492
                    found_undefined_user = this->is_undefined_user(*user.first);
9✔
493
                }
9✔
494
            }
20✔
495
            // If no definition found or undefined user found, mark as undefined
496
            if (!found_user || found_undefined_user) {
40✔
497
                undefined.insert(current_user);
31✔
498
            }
31✔
499
        }
40✔
500
    }
51✔
501

502
    std::vector<std::unordered_set<User*>> undefined_branches(if_else.size());
29✔
503
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> open_definitions_branches(if_else.size());
29✔
504
    std::vector<std::unordered_map<User*, std::unordered_set<User*>>> closed_definitionss_branches(if_else.size());
29✔
505
    for (size_t i = 0; i < if_else.size(); i++) {
80✔
506
        auto& child = if_else.at(i).first;
51✔
507
        visit_sequence(
51✔
508
            analysis_manager,
51✔
509
            child,
51✔
510
            undefined_branches.at(i),
51✔
511
            open_definitions_branches.at(i),
51✔
512
            closed_definitionss_branches.at(i)
51✔
513
        );
51✔
514
    }
51✔
515

516
    // merge partial open reads
517
    for (size_t i = 0; i < if_else.size(); i++) {
80✔
518
        for (auto& entry : undefined_branches.at(i)) {
51✔
519
            bool found_user = false;
30✔
520
            bool found_undefined_user = false;
30✔
521
            for (auto& user : open_definitions) {
30✔
522
                if (this->depends(analysis_manager, *user.first, *entry)) {
12✔
523
                    user.second.insert(entry);
6✔
524
                    found_user = true;
6✔
525
                    found_undefined_user = this->is_undefined_user(*user.first);
6✔
526
                }
6✔
527
            }
12✔
528
            // If no definition found or undefined user found, mark as undefined
529
            if (!found_user || found_undefined_user) {
30✔
530
                undefined.insert(entry);
24✔
531
            }
24✔
532
        }
30✔
533
    }
51✔
534

535
    // merge closed writes
536
    for (auto& closing : closed_definitionss_branches) {
51✔
537
        for (auto& entry : closing) {
51✔
538
            closed_definitions.insert(entry);
×
539
        }
×
540
    }
51✔
541

542
    // Close open reads_after_writes for complete branches
543
    if (if_else.is_complete()) {
29✔
544
        std::unordered_map<User*, std::unordered_set<User*>> to_close;
22✔
545
        std::unordered_set<std::string> candidates;
22✔
546
        std::unordered_set<std::string> candidates_tmp;
22✔
547

548
        /* Complete close open reads_after_writes
549
        1. get candidates from first iteration
550
        2. iterate over all branches and prune candidates
551
        3. find prior writes for remaining candidates
552
        4. close open reads_after_writes for all candidates
553
        */
554
        for (auto& entry : open_definitions_branches.at(0)) {
23✔
555
            candidates.insert(entry.first->container());
23✔
556
        }
23✔
557
        for (auto& entry : closed_definitionss_branches.at(0)) {
22✔
558
            candidates.insert(entry.first->container());
×
559
        }
×
560

561
        for (size_t i = 1; i < if_else.size(); i++) {
44✔
562
            for (auto& entry : open_definitions_branches.at(i)) {
22✔
563
                if (candidates.find(entry.first->container()) != candidates.end()) {
22✔
564
                    candidates_tmp.insert(entry.first->container());
21✔
565
                }
21✔
566
            }
22✔
567
            candidates.swap(candidates_tmp);
22✔
568
            candidates_tmp.clear();
22✔
569
        }
22✔
570

571
        for (auto& entry : open_definitions) {
22✔
572
            if (candidates.find(entry.first->container()) != candidates.end()) {
9✔
573
                to_close.insert(entry);
4✔
574
            }
4✔
575
        }
9✔
576

577
        for (auto& entry : to_close) {
22✔
578
            open_definitions.erase(entry.first);
4✔
579
            closed_definitions.insert(entry);
4✔
580
        }
4✔
581
    } else {
22✔
582
        // Incomplete if-else
583

584
        // In order to determine whether a new read is undefined
585
        // we would need to check whether all open definitions
586
        // jointly dominate the read.
587
        // Since this is expensive, we apply a trick:
588
        // For incomplete if-elses and any newly opened definition in
589
        // any branch, we add an artificial undefined user for that container.
590
        // If we encounter this user later, we know that not all branches defined it.
591
        // Hence, we can mark the read as (partially) undefined.
592

593
        for (auto& branch : open_definitions_branches) {
7✔
594
            for (auto& open_definition : branch) {
7✔
595
                auto write = open_definition.first;
5✔
596
                auto artificial_user = std::make_unique<
5✔
597
                    User>(boost::graph_traits<graph::Graph>::null_vertex(), write->container(), nullptr, Use::WRITE);
5✔
598
                this->undefined_users_.push_back(std::move(artificial_user));
5✔
599
                open_definitions.insert({this->undefined_users_.back().get(), {}});
5✔
600
            }
5✔
601
        }
7✔
602
    }
7✔
603

604
    // Add open definitions from branches to outside
605
    for (auto& branch : open_definitions_branches) {
51✔
606
        for (auto& entry : branch) {
51✔
607
            open_definitions.insert(entry);
50✔
608
        }
50✔
609
    }
51✔
610
}
29✔
611

612
void DataDependencyAnalysis::visit_while(
613
    analysis::AnalysisManager& analysis_manager,
614
    structured_control_flow::While& while_loop,
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
) {
8✔
619
    std::unordered_map<User*, std::unordered_set<User*>> open_definitions_while;
8✔
620
    std::unordered_map<User*, std::unordered_set<User*>> closed_definitions_while;
8✔
621
    std::unordered_set<User*> undefined_while;
8✔
622

623
    visit_sequence(analysis_manager, while_loop.root(), undefined_while, open_definitions_while, closed_definitions_while);
8✔
624

625
    // Scope-local closed definitions
626
    for (auto& entry : closed_definitions_while) {
8✔
627
        closed_definitions.insert(entry);
2✔
628
    }
2✔
629

630
    for (auto open_read : undefined_while) {
8✔
631
        // Over-Approximation: Add loop-carried dependencies for all open reads
632
        for (auto& entry : open_definitions_while) {
4✔
633
            if (entry.first->container() == open_read->container()) {
4✔
634
                entry.second.insert(open_read);
2✔
635
            }
2✔
636
        }
4✔
637

638
        // Connect to outside
639
        bool found = false;
3✔
640
        for (auto& entry : open_definitions) {
3✔
641
            if (entry.first->container() == open_read->container()) {
3✔
642
                entry.second.insert(open_read);
2✔
643
                found = true;
2✔
644
            }
2✔
645
        }
3✔
646
        if (!found) {
3✔
647
            undefined.insert(open_read);
1✔
648
        }
1✔
649
    }
3✔
650

651
    // Add open definitions from while to outside
652
    for (auto& entry : open_definitions_while) {
13✔
653
        open_definitions.insert(entry);
13✔
654
    }
13✔
655
}
8✔
656

657
void DataDependencyAnalysis::visit_return(
658
    analysis::AnalysisManager& analysis_manager,
659
    structured_control_flow::Return& return_statement,
660
    std::unordered_set<User*>& undefined,
661
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
662
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
663
) {
2✔
664
    auto& users = analysis_manager.get<analysis::Users>();
2✔
665

666
    if (return_statement.is_data() && !return_statement.data().empty()) {
2✔
667
        auto current_user = users.get_user(return_statement.data(), &return_statement, Use::READ);
2✔
668

669
        bool found = false;
2✔
670
        for (auto& user : open_definitions) {
9✔
671
            if (user.first->container() == return_statement.data()) {
9✔
672
                user.second.insert(current_user);
8✔
673
                found = true;
8✔
674
            }
8✔
675
        }
9✔
676
        if (!found) {
2✔
677
            undefined.insert(current_user);
×
678
        }
×
679
    }
2✔
680

681
    // close all open reads_after_writes
682
    for (auto& entry : open_definitions) {
9✔
683
        closed_definitions.insert(entry);
9✔
684
    }
9✔
685
    open_definitions.clear();
2✔
686
}
2✔
687

688
void DataDependencyAnalysis::visit_sequence(
689
    analysis::AnalysisManager& analysis_manager,
690
    structured_control_flow::Sequence& sequence,
691
    std::unordered_set<User*>& undefined,
692
    std::unordered_map<User*, std::unordered_set<User*>>& open_definitions,
693
    std::unordered_map<User*, std::unordered_set<User*>>& closed_definitions
694
) {
1,067✔
695
    auto& users = analysis_manager.get<analysis::Users>();
1,067✔
696

697
    for (size_t i = 0; i < sequence.size(); i++) {
2,700✔
698
        auto child = sequence.at(i);
1,633✔
699
        if (auto block = dynamic_cast<structured_control_flow::Block*>(&child.first)) {
1,633✔
700
            visit_block(analysis_manager, *block, undefined, open_definitions, closed_definitions);
842✔
701
        } else if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(&child.first)) {
842✔
702
            visit_for(analysis_manager, *for_loop, undefined, open_definitions, closed_definitions);
744✔
703
        } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
744✔
704
            visit_if_else(analysis_manager, *if_else, undefined, open_definitions, closed_definitions);
29✔
705
        } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&child.first)) {
29✔
706
            visit_while(analysis_manager, *while_loop, undefined, open_definitions, closed_definitions);
8✔
707
        } else if (auto return_statement = dynamic_cast<structured_control_flow::Return*>(&child.first)) {
10✔
708
            visit_return(analysis_manager, *return_statement, undefined, open_definitions, closed_definitions);
2✔
709
        } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&child.first)) {
8✔
710
            visit_sequence(analysis_manager, *sequence, undefined, open_definitions, closed_definitions);
×
711
        }
×
712

713
        // handle transitions read
714
        for (auto& entry : child.second.assignments()) {
1,633✔
715
            for (auto& atom : symbolic::atoms(entry.second)) {
108✔
716
                if (symbolic::is_pointer(atom)) {
47✔
717
                    continue;
×
718
                }
×
719
                auto current_user = users.get_user(atom->get_name(), &child.second, Use::READ);
47✔
720

721
                bool found = false;
47✔
722
                for (auto& user : open_definitions) {
47✔
723
                    if (user.first->container() == atom->get_name()) {
45✔
724
                        user.second.insert(current_user);
39✔
725
                        found = true;
39✔
726
                    }
39✔
727
                }
45✔
728
                if (!found) {
47✔
729
                    undefined.insert(current_user);
18✔
730
                }
18✔
731
            }
47✔
732
        }
108✔
733

734
        // handle transitions write
735
        for (auto& entry : child.second.assignments()) {
1,633✔
736
            auto current_user = users.get_user(entry.first->get_name(), &child.second, Use::WRITE);
108✔
737

738
            std::unordered_set<User*> to_close;
108✔
739
            for (auto& user : open_definitions) {
108✔
740
                if (this->closes(analysis_manager, *user.first, *current_user, true)) {
52✔
741
                    to_close.insert(user.first);
3✔
742
                }
3✔
743
            }
52✔
744
            for (auto& user : to_close) {
108✔
745
                closed_definitions.insert({user, open_definitions.at(user)});
3✔
746
                open_definitions.erase(user);
3✔
747
            }
3✔
748
            open_definitions.insert({current_user, {}});
108✔
749
        }
108✔
750
    }
1,633✔
751
}
1,067✔
752

753
symbolic::maps::DependenceDeltas DataDependencyAnalysis::loop_deltas(
754
    User& previous,
755
    User& current,
756
    analysis::AnalysisManager& analysis_manager,
757
    structured_control_flow::StructuredLoop& loop
758
) {
35,160✔
759
    symbolic::maps::DependenceDeltas empty_result{true, "", {}};
35,160✔
760

761
    if (previous.container() != current.container()) {
35,160✔
762
        return empty_result;
32,694✔
763
    }
32,694✔
764
    // Shortcut for scalars — always dependent, but distance is unknown
765
    auto& type = this->sdfg_.type(previous.container());
2,466✔
766
    if (dynamic_cast<const types::Scalar*>(&type)) {
2,466✔
767
        return symbolic::maps::DependenceDeltas{false, "", {}};
926✔
768
    }
926✔
769

770
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
1,540✔
771
        return empty_result;
4✔
772
    }
4✔
773

774
    auto& previous_subsets = previous.subsets();
1,536✔
775
    auto& current_subsets = current.subsets();
1,536✔
776

777
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
1,536✔
778
    auto previous_scope = Users::scope(&previous);
1,536✔
779
    auto previous_assumptions = assumptions_analysis.get(*previous_scope, true);
1,536✔
780
    auto current_scope = Users::scope(&current);
1,536✔
781
    auto current_assumptions = assumptions_analysis.get(*current_scope, true);
1,536✔
782

783
    // We're using the assumptions from the blocks, where the memory accesses occur
784
    // However, we need to revert constantness assumptions from the perspective of the loop
785
    // for which we're checking loop-carried dependencies
786
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
1,536✔
787
    if (previous_assumptions.find(loop.indvar()) != previous_assumptions.end()) {
1,536✔
788
        previous_assumptions.at(loop.indvar()).constant(false);
1,536✔
789
    }
1,536✔
790
    if (current_assumptions.find(loop.indvar()) != current_assumptions.end()) {
1,536✔
791
        current_assumptions.at(loop.indvar()).constant(false);
1,536✔
792
    }
1,536✔
793
    for (auto& inner_loop : loop_analysis.descendants(&loop)) {
2,770✔
794
        if (auto structured_loop = dynamic_cast<const structured_control_flow::StructuredLoop*>(inner_loop)) {
2,770✔
795
            auto indvar = structured_loop->indvar();
2,770✔
796
            if (previous_assumptions.find(indvar) != previous_assumptions.end()) {
2,770✔
797
                previous_assumptions.at(indvar).constant(false);
2,770✔
798
            }
2,770✔
799
            if (current_assumptions.find(indvar) != current_assumptions.end()) {
2,770✔
800
                current_assumptions.at(indvar).constant(false);
2,770✔
801
            }
2,770✔
802
        }
2,770✔
803
    }
2,770✔
804

805
    // Collect deltas from all subset pairs and union them
806
    isl_ctx* union_ctx = nullptr;
1,536✔
807
    isl_set* accumulated = nullptr;
1,536✔
808
    std::vector<std::string> result_dimensions;
1,536✔
809

810
    for (auto& previous_subset : previous_subsets) {
1,536✔
811
        for (auto& current_subset : current_subsets) {
1,614✔
812
            auto deltas = symbolic::maps::dependence_deltas(
1,614✔
813
                previous_subset, current_subset, loop.indvar(), previous_assumptions, current_assumptions
1,614✔
814
            );
1,614✔
815
            if (deltas.empty) {
1,614✔
816
                continue;
1,158✔
817
            }
1,158✔
818
            if (deltas.deltas_str.empty()) {
456✔
819
                // Dependence exists but no isl representation (scalar case)
820
                if (accumulated) {
17✔
NEW
821
                    isl_set_free(accumulated);
×
NEW
822
                    isl_ctx_free(union_ctx);
×
NEW
823
                }
×
824
                return symbolic::maps::DependenceDeltas{false, "", {}};
17✔
825
            }
17✔
826
            if (!union_ctx) {
439✔
827
                union_ctx = isl_ctx_alloc();
407✔
828
                isl_options_set_on_error(union_ctx, ISL_ON_ERROR_CONTINUE);
407✔
829
                accumulated = isl_set_read_from_str(union_ctx, deltas.deltas_str.c_str());
407✔
830
                result_dimensions = deltas.dimensions;
407✔
831
            } else {
407✔
832
                isl_set* new_set = isl_set_read_from_str(union_ctx, deltas.deltas_str.c_str());
32✔
833
                if (new_set && accumulated) {
32✔
834
                    accumulated = isl_set_union(accumulated, new_set);
32✔
835
                } else if (new_set) {
32✔
NEW
836
                    isl_set_free(new_set);
×
NEW
837
                }
×
838
            }
32✔
839
        }
439✔
840
    }
1,536✔
841

842
    if (!accumulated) {
1,519✔
843
        if (union_ctx) {
1,112✔
NEW
844
            isl_ctx_free(union_ctx);
×
NEW
845
        }
×
846
        return empty_result;
1,112✔
847
    }
1,112✔
848

849
    // Serialize the union
850
    char* str = isl_set_to_str(accumulated);
407✔
851
    if (!str) {
407✔
NEW
852
        isl_set_free(accumulated);
×
NEW
853
        isl_ctx_free(union_ctx);
×
NEW
854
        return symbolic::maps::DependenceDeltas{false, "", {}};
×
NEW
855
    }
×
856
    std::string union_str(str);
407✔
857
    free(str);
407✔
858

859
    isl_set_free(accumulated);
407✔
860
    isl_ctx_free(union_ctx);
407✔
861

862
    return symbolic::maps::DependenceDeltas{false, union_str, result_dimensions};
407✔
863
}
407✔
864

865
bool DataDependencyAnalysis::
866
    supersedes_restrictive(User& previous, User& current, analysis::AnalysisManager& analysis_manager) {
3,349✔
867
    if (previous.container() != current.container()) {
3,349✔
868
        return false;
×
869
    }
×
870
    // Shortcut for scalars
871
    auto& type = this->sdfg_.type(previous.container());
3,349✔
872
    if (dynamic_cast<const types::Scalar*>(&type)) {
3,349✔
873
        return true;
3,278✔
874
    }
3,278✔
875

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

880
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
71✔
881
    auto& previous_subsets = previous.subsets();
71✔
882
    auto& current_subsets = current.subsets();
71✔
883
    auto previous_scope = Users::scope(&previous);
71✔
884
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
71✔
885
    auto current_scope = Users::scope(&current);
71✔
886
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
71✔
887

888
    // Check if previous subset is subset of any current subset
889
    for (auto& previous_subset : previous_subsets) {
71✔
890
        bool found = false;
71✔
891
        for (auto& current_subset : current_subsets) {
71✔
892
            if (symbolic::is_subset(previous_subset, current_subset, previous_assumptions, previous_assumptions)) {
71✔
893
                found = true;
66✔
894
                break;
66✔
895
            }
66✔
896
        }
71✔
897
        if (!found) {
71✔
898
            return false;
5✔
899
        }
5✔
900
    }
71✔
901

902
    return true;
66✔
903
}
71✔
904

905
bool DataDependencyAnalysis::intersects(User& previous, User& current, analysis::AnalysisManager& analysis_manager) {
27,058✔
906
    if (previous.container() != current.container()) {
27,058✔
907
        return false;
20,186✔
908
    }
20,186✔
909
    // Shortcut for scalars
910
    auto& type = this->sdfg_.type(previous.container());
6,872✔
911
    if (dynamic_cast<const types::Scalar*>(&type)) {
6,872✔
912
        return true;
6,544✔
913
    }
6,544✔
914

915
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
328✔
916
        return true;
×
917
    }
×
918

919
    if (!this->detailed_) {
328✔
920
        return true;
66✔
921
    }
66✔
922

923
    auto& previous_subsets = previous.subsets();
262✔
924
    auto& current_subsets = current.subsets();
262✔
925

926
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
262✔
927
    auto previous_scope = Users::scope(&previous);
262✔
928
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
262✔
929
    auto current_scope = Users::scope(&current);
262✔
930
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
262✔
931

932
    // Check if any current subset intersects with any previous subset
933
    bool found = false;
262✔
934
    for (auto& current_subset : current_subsets) {
270✔
935
        for (auto& previous_subset : previous_subsets) {
270✔
936
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
270✔
937
                found = true;
246✔
938
                break;
246✔
939
            }
246✔
940
        }
270✔
941
        if (found) {
270✔
942
            break;
246✔
943
        }
246✔
944
    }
270✔
945

946
    return found;
262✔
947
}
328✔
948

949
bool DataDependencyAnalysis::
950
    closes(analysis::AnalysisManager& analysis_manager, User& previous, User& current, bool requires_dominance) {
11,652✔
951
    if (previous.container() != current.container()) {
11,652✔
952
        return false;
11,356✔
953
    }
11,356✔
954

955
    if (this->is_undefined_user(previous) || this->is_undefined_user(current)) {
296✔
956
        return false;
×
957
    }
×
958

959
    // Check dominance
960
    if (requires_dominance) {
296✔
961
        auto& dominance_analysis = analysis_manager.get<analysis::DominanceAnalysis>();
200✔
962
        if (!dominance_analysis.post_dominates(current, previous)) {
200✔
963
            return false;
195✔
964
        }
195✔
965
    }
200✔
966

967
    // Previous memlets are subsets of current memlets
968
    auto& type = sdfg_.type(previous.container());
101✔
969
    if (type.type_id() == types::TypeID::Scalar) {
101✔
970
        return true;
18✔
971
    }
18✔
972

973
    if (!this->detailed_) {
83✔
974
        return false;
16✔
975
    }
16✔
976

977
    // Collect memlets and assumptions
978
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
67✔
979
    auto previous_scope = Users::scope(&previous);
67✔
980
    auto current_scope = Users::scope(&current);
67✔
981
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
67✔
982
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
67✔
983

984
    auto& previous_memlets = previous.subsets();
67✔
985
    auto& current_memlets = current.subsets();
67✔
986

987
    for (auto& subset_ : previous_memlets) {
67✔
988
        bool overwritten = false;
67✔
989
        for (auto& subset : current_memlets) {
67✔
990
            if (symbolic::is_subset(subset_, subset, previous_assumptions, current_assumptions)) {
67✔
991
                overwritten = true;
53✔
992
                break;
53✔
993
            }
53✔
994
        }
67✔
995
        if (!overwritten) {
67✔
996
            return false;
14✔
997
        }
14✔
998
    }
67✔
999

1000
    return true;
53✔
1001
}
67✔
1002

1003
bool DataDependencyAnalysis::depends(analysis::AnalysisManager& analysis_manager, User& previous, User& current) {
18,758✔
1004
    if (previous.container() != current.container()) {
18,758✔
1005
        return false;
16,696✔
1006
    }
16,696✔
1007

1008
    // Previous memlets are subsets of current memlets
1009
    auto& type = sdfg_.type(previous.container());
2,062✔
1010
    if (type.type_id() == types::TypeID::Scalar) {
2,062✔
1011
        return true;
1,721✔
1012
    }
1,721✔
1013

1014
    if (this->is_undefined_user(previous)) {
341✔
1015
        return true;
×
1016
    }
×
1017

1018
    if (!this->detailed_) {
341✔
1019
        return true;
252✔
1020
    }
252✔
1021

1022
    // Collect memlets and assumptions
1023
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
89✔
1024
    auto previous_scope = Users::scope(&previous);
89✔
1025
    auto current_scope = Users::scope(&current);
89✔
1026
    auto& previous_assumptions = assumptions_analysis.get(*previous_scope, true);
89✔
1027
    auto& current_assumptions = assumptions_analysis.get(*current_scope, true);
89✔
1028

1029
    auto& previous_memlets = previous.subsets();
89✔
1030
    auto& current_memlets = current.subsets();
89✔
1031

1032
    bool intersect_any = false;
89✔
1033
    for (auto& current_subset : current_memlets) {
89✔
1034
        for (auto& previous_subset : previous_memlets) {
89✔
1035
            if (!symbolic::is_disjoint(current_subset, previous_subset, current_assumptions, previous_assumptions)) {
89✔
1036
                intersect_any = true;
78✔
1037
                break;
78✔
1038
            }
78✔
1039
        }
89✔
1040
        if (intersect_any) {
89✔
1041
            break;
78✔
1042
        }
78✔
1043
    }
89✔
1044

1045
    return intersect_any;
89✔
1046
}
341✔
1047

1048
/****** Public API ******/
1049

1050
std::unordered_set<User*> DataDependencyAnalysis::defines(User& write) {
×
1051
    assert(write.use() == Use::WRITE);
×
1052
    if (results_.find(write.container()) == results_.end()) {
×
1053
        return {};
×
1054
    }
×
1055
    auto& raws = results_.at(write.container());
×
1056
    assert(raws.find(&write) != raws.end());
×
1057

1058
    auto& reads_for_write = raws.at(&write);
×
1059

1060
    std::unordered_set<User*> reads;
×
1061
    for (auto& entry : reads_for_write) {
×
1062
        reads.insert(entry);
×
1063
    }
×
1064

1065
    return reads;
×
1066
};
×
1067

1068
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::definitions(const std::string& container) {
583✔
1069
    if (results_.find(container) == results_.end()) {
583✔
1070
        return {};
3✔
1071
    }
3✔
1072
    return results_.at(container);
580✔
1073
};
583✔
1074

1075
std::unordered_map<User*, std::unordered_set<User*>> DataDependencyAnalysis::defined_by(const std::string& container) {
259✔
1076
    auto reads = this->definitions(container);
259✔
1077

1078
    std::unordered_map<User*, std::unordered_set<User*>> read_to_writes_map;
259✔
1079
    for (auto& entry : reads) {
474✔
1080
        for (auto& read : entry.second) {
2,471✔
1081
            if (read_to_writes_map.find(read) == read_to_writes_map.end()) {
2,471✔
1082
                read_to_writes_map[read] = {};
1,248✔
1083
            }
1,248✔
1084
            read_to_writes_map[read].insert(entry.first);
2,471✔
1085
        }
2,471✔
1086
    }
474✔
1087
    return read_to_writes_map;
259✔
1088
};
259✔
1089

1090
std::unordered_set<User*> DataDependencyAnalysis::defined_by(User& read) {
×
1091
    assert(read.use() == Use::READ);
×
1092
    auto definitions = this->definitions(read.container());
×
1093

1094
    std::unordered_set<User*> writes;
×
1095
    for (auto& entry : definitions) {
×
1096
        for (auto& r : entry.second) {
×
1097
            if (&read == r) {
×
1098
                writes.insert(entry.first);
×
1099
            }
×
1100
        }
×
1101
    }
×
1102
    return writes;
×
1103
};
×
1104

1105
bool DataDependencyAnalysis::is_undefined_user(User& user) const {
16,542✔
1106
    return user.vertex_ == boost::graph_traits<graph::Graph>::null_vertex();
16,542✔
1107
};
16,542✔
1108

1109
bool DataDependencyAnalysis::available(structured_control_flow::StructuredLoop& loop) const {
×
1110
    return this->loop_carried_dependencies_.find(&loop) != this->loop_carried_dependencies_.end();
×
1111
};
×
1112

1113
const std::unordered_map<std::string, LoopCarriedDependencyInfo>& DataDependencyAnalysis::
1114
    dependencies(structured_control_flow::StructuredLoop& loop) const {
308✔
1115
    return this->loop_carried_dependencies_.at(&loop);
308✔
1116
};
308✔
1117

1118
} // namespace analysis
1119
} // 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