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

daisytuner / sdfglib / 15044057891

15 May 2025 11:42AM UTC coverage: 59.37% (+1.8%) from 57.525%
15044057891

push

github

web-flow
Merge pull request #14 from daisytuner/sanitizers

enables sanitizer on unit tests

63 of 67 new or added lines in 47 files covered. (94.03%)

570 existing lines in 62 files now uncovered.

7356 of 12390 relevant lines covered (59.37%)

505.93 hits per line

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

86.69
/src/analysis/data_parallelism_analysis.cpp
1
#include "sdfg/analysis/data_parallelism_analysis.h"
2

3
#include "sdfg/codegen/language_extensions/cpp_language_extension.h"
4

5
namespace sdfg {
6
namespace analysis {
7

8
std::pair<data_flow::Subset, data_flow::Subset> DataParallelismAnalysis::substitution(
1,295✔
9
    const data_flow::Subset& subset1, const data_flow::Subset& subset2, const std::string& indvar,
10
    const std::unordered_set<std::string>& moving_symbols, symbolic::SymbolicMap& replacements,
11
    std::vector<std::string>& substitions) {
12
    data_flow::Subset subset1_new;
1,295✔
13
    for (auto& dim : subset1) {
3,471✔
14
        auto args = dim->get_args();
2,176✔
15
        auto new_dim = dim;
2,176✔
16
        for (auto& arg : args) {
2,437✔
17
            if (!SymEngine::is_a<SymEngine::Symbol>(*arg) &&
393✔
18
                !SymEngine::is_a<SymEngine::Integer>(*arg)) {
132✔
19
                bool is_moving = false;
129✔
20
                for (auto& atom : symbolic::atoms(arg)) {
326✔
21
                    auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
197✔
22
                    if (moving_symbols.find(sym->get_name()) != moving_symbols.end()) {
197✔
23
                        is_moving = true;
62✔
24
                        break;
62✔
25
                    }
26
                }
197✔
27
                if (!is_moving) {
129✔
28
                    if (replacements.find(arg) != replacements.end()) {
67✔
29
                        new_dim = symbolic::subs(new_dim, arg, replacements.at(arg));
×
UNCOV
30
                    } else {
×
31
                        auto repl = symbolic::symbol("c_" + std::to_string(replacements.size()));
67✔
32
                        substitions.push_back(repl->get_name());
67✔
33
                        replacements.insert({arg, repl});
67✔
34
                        new_dim = symbolic::subs(new_dim, arg, repl);
67✔
35
                    }
67✔
36
                }
67✔
37
            }
129✔
38
        }
39
        subset1_new.push_back(new_dim);
2,176✔
40
    }
2,176✔
41

42
    data_flow::Subset subset2_new;
1,295✔
43
    for (auto& dim : subset2) {
3,471✔
44
        auto args = dim->get_args();
2,176✔
45
        auto new_dim = dim;
2,176✔
46
        for (auto& arg : args) {
2,433✔
47
            if (!SymEngine::is_a<SymEngine::Symbol>(*arg) &&
386✔
48
                !SymEngine::is_a<SymEngine::Integer>(*arg)) {
129✔
49
                bool is_moving = false;
129✔
50
                for (auto& atom : symbolic::atoms(arg)) {
326✔
51
                    auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
197✔
52
                    if (moving_symbols.find(sym->get_name()) != moving_symbols.end()) {
197✔
53
                        is_moving = true;
62✔
54
                        break;
62✔
55
                    }
56
                }
197✔
57
                if (!is_moving) {
129✔
58
                    if (replacements.find(arg) != replacements.end()) {
67✔
59
                        new_dim = symbolic::subs(new_dim, arg, replacements.at(arg));
67✔
60
                    } else {
67✔
61
                        auto repl = symbolic::symbol("c_" + std::to_string(replacements.size()));
×
62
                        substitions.push_back(repl->get_name());
×
63
                        replacements.insert({arg, repl});
×
64
                        new_dim = symbolic::subs(new_dim, arg, repl);
×
65
                    }
×
66
                }
67✔
67
            }
129✔
68
        }
69
        subset2_new.push_back(new_dim);
2,176✔
70
    }
2,176✔
71

72
    return {subset1_new, subset2_new};
1,295✔
73
};
1,295✔
74

75
std::pair<data_flow::Subset, data_flow::Subset> DataParallelismAnalysis::delinearization(
1,297✔
76
    const data_flow::Subset& subset1, const data_flow::Subset& subset2,
77
    const std::unordered_set<std::string>& moving_symbols,
78
    const symbolic::Assumptions& assumptions) {
79
    // Attempt to prove:
80
    // dim = i + j * M
81
    // We can delinearize iff:
82
    // 1. M is a constant
83
    // 2. i and j are not symbols
84
    // 3. M >= ub(i)
85
    data_flow::Subset subset1_new;
1,297✔
86
    for (auto& dim : subset1) {
3,475✔
87
        if (!SymEngine::is_a<SymEngine::Add>(*dim)) {
2,178✔
88
            subset1_new.push_back(dim);
2,048✔
89
            continue;
2,048✔
90
        }
91
        auto add = SymEngine::rcp_static_cast<const SymEngine::Add>(dim);
130✔
92
        if (add->get_args().size() != 2) {
130✔
93
            subset1_new.push_back(dim);
5✔
94
            continue;
5✔
95
        }
96
        auto offset = add->get_args()[0];
125✔
97
        auto mult = add->get_args()[1];
125✔
98
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
125✔
99
            auto tmp = offset;
66✔
100
            offset = mult;
66✔
101
            mult = tmp;
66✔
102
        }
66✔
103
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
125✔
104
            subset1_new.push_back(dim);
65✔
105
            continue;
65✔
106
        }
107

108
        // Offset must be a symbol and moving
109
        if (!SymEngine::is_a<SymEngine::Symbol>(*offset)) {
60✔
110
            subset1_new.push_back(dim);
×
111
            continue;
×
112
        }
113
        auto off = SymEngine::rcp_static_cast<const SymEngine::Symbol>(offset);
60✔
114
        if (moving_symbols.find(off->get_name()) == moving_symbols.end()) {
60✔
115
            subset1_new.push_back(dim);
×
116
            continue;
×
117
        }
118

119
        // Multiplier must be two symbols and one moving
120
        auto mult_ = SymEngine::rcp_static_cast<const SymEngine::Mul>(mult);
60✔
121
        if (mult_->get_args().size() != 2) {
60✔
122
            subset1_new.push_back(dim);
×
123
            continue;
×
124
        }
125
        auto multiplier = mult_->get_args()[0];
60✔
126
        auto indvar_ = mult_->get_args()[1];
60✔
127
        if (!SymEngine::is_a<SymEngine::Symbol>(*multiplier) ||
120✔
128
            !SymEngine::is_a<SymEngine::Symbol>(*indvar_)) {
60✔
129
            subset1_new.push_back(dim);
×
130
            continue;
×
131
        }
132
        auto mul = SymEngine::rcp_static_cast<const SymEngine::Symbol>(multiplier);
60✔
133
        auto indvar = SymEngine::rcp_static_cast<const SymEngine::Symbol>(indvar_);
60✔
134
        if (moving_symbols.find(mul->get_name()) != moving_symbols.end()) {
60✔
135
            auto tmp = mul;
56✔
136
            mul = indvar;
56✔
137
            indvar = tmp;
56✔
138
        }
56✔
139
        if (moving_symbols.find(mul->get_name()) != moving_symbols.end() ||
120✔
140
            moving_symbols.find(indvar->get_name()) == moving_symbols.end()) {
60✔
141
            subset1_new.push_back(dim);
×
142
            continue;
×
143
        }
144

145
        bool is_nonnegative = false;
60✔
146
        symbolic::SymbolicSet lbs_off;
60✔
147
        symbolic::lower_bounds(off, assumptions, lbs_off);
60✔
148
        for (auto& lb : lbs_off) {
61✔
149
            if (SymEngine::is_a<SymEngine::Integer>(*lb)) {
60✔
150
                auto lb_int = SymEngine::rcp_static_cast<const SymEngine::Integer>(lb);
60✔
151
                if (lb_int->as_int() >= 0) {
60✔
152
                    is_nonnegative = true;
59✔
153
                    break;
59✔
154
                }
155
            }
60✔
156
        }
157
        if (!is_nonnegative) {
60✔
158
            subset1_new.push_back(dim);
1✔
159
            continue;
1✔
160
        }
161

162
        bool success = false;
59✔
163
        symbolic::SymbolicSet ubs_off;
59✔
164
        symbolic::upper_bounds(off, assumptions, ubs_off);
59✔
165
        for (auto& ub_off : ubs_off) {
61✔
166
            if (symbolic::eq(mul, symbolic::add(ub_off, symbolic::one()))) {
60✔
167
                subset1_new.push_back(indvar);
58✔
168
                subset1_new.push_back(off);
58✔
169

170
                success = true;
58✔
171
                break;
58✔
172
            }
173
        }
174
        if (success) {
59✔
175
            continue;
58✔
176
        }
177
        subset1_new.push_back(dim);
1✔
178
    }
130✔
179

180
    data_flow::Subset subset2_new;
1,297✔
181
    for (auto& dim : subset2) {
3,475✔
182
        if (!SymEngine::is_a<SymEngine::Add>(*dim)) {
2,178✔
183
            subset2_new.push_back(dim);
2,050✔
184
            continue;
2,050✔
185
        }
186
        auto add = SymEngine::rcp_static_cast<const SymEngine::Add>(dim);
128✔
187
        if (add->get_args().size() != 2) {
128✔
188
            subset2_new.push_back(dim);
5✔
189
            continue;
5✔
190
        }
191
        auto offset = add->get_args()[0];
123✔
192
        auto mult = add->get_args()[1];
123✔
193
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
123✔
194
            auto tmp = offset;
64✔
195
            offset = mult;
64✔
196
            mult = tmp;
64✔
197
        }
64✔
198
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
123✔
199
            subset2_new.push_back(dim);
63✔
200
            continue;
63✔
201
        }
202

203
        // Offset must be a symbol and moving
204
        if (!SymEngine::is_a<SymEngine::Symbol>(*offset)) {
60✔
205
            subset2_new.push_back(dim);
×
206
            continue;
×
207
        }
208
        auto off = SymEngine::rcp_static_cast<const SymEngine::Symbol>(offset);
60✔
209
        if (moving_symbols.find(off->get_name()) == moving_symbols.end()) {
60✔
210
            subset2_new.push_back(dim);
×
211
            continue;
×
212
        }
213

214
        // Multiplier must be two symbols and one moving
215
        auto mult_ = SymEngine::rcp_static_cast<const SymEngine::Mul>(mult);
60✔
216
        if (mult_->get_args().size() != 2) {
60✔
217
            subset2_new.push_back(dim);
×
218
            continue;
×
219
        }
220
        auto multiplier = mult_->get_args()[0];
60✔
221
        auto indvar_ = mult_->get_args()[1];
60✔
222
        if (!SymEngine::is_a<SymEngine::Symbol>(*multiplier) ||
120✔
223
            !SymEngine::is_a<SymEngine::Symbol>(*indvar_)) {
60✔
224
            subset2_new.push_back(dim);
×
225
            continue;
×
226
        }
227
        auto mul = SymEngine::rcp_static_cast<const SymEngine::Symbol>(multiplier);
60✔
228
        auto indvar = SymEngine::rcp_static_cast<const SymEngine::Symbol>(indvar_);
60✔
229
        if (moving_symbols.find(mul->get_name()) != moving_symbols.end()) {
60✔
230
            auto tmp = mul;
56✔
231
            mul = indvar;
56✔
232
            indvar = tmp;
56✔
233
        }
56✔
234
        if (moving_symbols.find(mul->get_name()) != moving_symbols.end() ||
120✔
235
            moving_symbols.find(indvar->get_name()) == moving_symbols.end()) {
60✔
236
            subset2_new.push_back(dim);
×
237
            continue;
×
238
        }
239

240
        bool is_nonnegative = false;
60✔
241
        symbolic::SymbolicSet lbs_off;
60✔
242
        symbolic::lower_bounds(off, assumptions, lbs_off);
60✔
243
        for (auto& lb : lbs_off) {
61✔
244
            if (SymEngine::is_a<SymEngine::Integer>(*lb)) {
60✔
245
                auto lb_int = SymEngine::rcp_static_cast<const SymEngine::Integer>(lb);
60✔
246
                if (lb_int->as_int() >= 0) {
60✔
247
                    is_nonnegative = true;
59✔
248
                    break;
59✔
249
                }
250
            }
60✔
251
        }
252
        if (!is_nonnegative) {
60✔
253
            subset2_new.push_back(dim);
1✔
254
            continue;
1✔
255
        }
256

257
        bool success = false;
59✔
258
        symbolic::SymbolicSet ubs_off;
59✔
259
        symbolic::upper_bounds(off, assumptions, ubs_off);
59✔
260
        for (auto& ub_off : ubs_off) {
61✔
261
            if (symbolic::eq(mul, symbolic::add(ub_off, symbolic::one()))) {
60✔
262
                subset2_new.push_back(indvar);
58✔
263
                subset2_new.push_back(off);
58✔
264

265
                success = true;
58✔
266
                break;
58✔
267
            }
268
        }
269
        if (success) {
59✔
270
            continue;
58✔
271
        }
272
        subset2_new.push_back(dim);
1✔
273
    }
128✔
274

275
    return {subset1_new, subset2_new};
1,297✔
276
};
1,297✔
277

278
bool DataParallelismAnalysis::disjoint(const data_flow::Subset& subset1,
1,294✔
279
                                       const data_flow::Subset& subset2, const std::string& indvar,
280
                                       const std::unordered_set<std::string>& moving_symbols,
281
                                       const symbolic::Assumptions& assumptions) {
282
    if (subset1.size() != subset2.size()) {
1,294✔
283
        return false;
×
284
    }
285

286
    codegen::CPPLanguageExtension language_extension;
1,294✔
287

288
    // Attempt to substitute complex constant expressions by parameters
289
    symbolic::SymbolicMap replacements;
1,294✔
290
    std::vector<std::string> substitions;
1,294✔
291
    auto [subset1_, subset2_] = DataParallelismAnalysis::substitution(
1,294✔
292
        subset1, subset2, indvar, moving_symbols, replacements, substitions);
1,294✔
293

294
    // Attempt to delinearize subsets
295
    auto [subset1_2, subset2_2] =
3,882✔
296
        DataParallelismAnalysis::delinearization(subset1_, subset2_, moving_symbols, assumptions);
1,294✔
297

298
    // Overapproximate multiplications with parameters
299
    data_flow::Subset subset1_new;
1,294✔
300
    for (auto& dim : subset1_2) {
3,526✔
301
        auto dim_ = dim;
2,232✔
302
        for (auto mul : symbolic::muls(dim)) {
2,237✔
303
            auto mul_ = SymEngine::rcp_static_cast<const SymEngine::Mul>(mul);
5✔
304
            auto arg1 = mul_->get_args()[0];
5✔
305
            if (SymEngine::is_a<SymEngine::Symbol>(*arg1) &&
5✔
306
                moving_symbols.find(
×
UNCOV
307
                    SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg1)->get_name()) ==
×
UNCOV
308
                    moving_symbols.end()) {
×
309
                dim_ = symbolic::subs(dim_, mul_, symbolic::one());
×
UNCOV
310
            } else {
×
311
                auto arg2 = mul_->get_args()[1];
5✔
312
                if (SymEngine::is_a<SymEngine::Symbol>(*arg2) &&
10✔
313
                    moving_symbols.find(
10✔
314
                        SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg2)->get_name()) ==
10✔
315
                        moving_symbols.end()) {
5✔
316
                    dim_ = symbolic::subs(dim_, mul_, symbolic::one());
×
UNCOV
317
                }
×
318
            }
5✔
319
        }
5✔
320
        subset1_new.push_back(dim_);
2,232✔
321
    }
2,232✔
322
    data_flow::Subset subset2_new;
1,294✔
323
    for (auto& dim : subset2_2) {
3,526✔
324
        auto dim_ = dim;
2,232✔
325
        for (auto mul : symbolic::muls(dim)) {
2,237✔
326
            auto mul_ = SymEngine::rcp_static_cast<const SymEngine::Mul>(mul);
5✔
327
            auto arg1 = mul_->get_args()[0];
5✔
328
            if (SymEngine::is_a<SymEngine::Symbol>(*arg1) &&
5✔
329
                moving_symbols.find(
×
UNCOV
330
                    SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg1)->get_name()) ==
×
UNCOV
331
                    moving_symbols.end()) {
×
332
                dim_ = symbolic::subs(dim_, mul_, symbolic::one());
×
UNCOV
333
            } else {
×
334
                auto arg2 = mul_->get_args()[1];
5✔
335
                if (SymEngine::is_a<SymEngine::Symbol>(*arg2) &&
10✔
336
                    moving_symbols.find(
10✔
337
                        SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg2)->get_name()) ==
10✔
338
                        moving_symbols.end()) {
5✔
339
                    dim_ = symbolic::subs(dim_, mul_, symbolic::one());
×
UNCOV
340
                }
×
341
            }
5✔
342
        }
5✔
343
        subset2_new.push_back(dim_);
2,232✔
344
    }
2,232✔
345

346
    // Collect parameters and dimensions
347
    std::unordered_set<std::string> dimensions_;
1,294✔
348
    std::unordered_set<std::string> parameters_;
1,294✔
349
    for (auto& dim : subset1_new) {
3,526✔
350
        for (auto& atom : symbolic::atoms(dim)) {
4,521✔
351
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
2,289✔
352
            if (sym->get_name() == indvar) {
2,289✔
353
                continue;
1,164✔
354
            }
355

356
            if (std::find(substitions.begin(), substitions.end(), sym->get_name()) !=
1,125✔
357
                substitions.end()) {
1,125✔
358
                continue;
66✔
359
            }
360
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
1,059✔
361
                parameters_.insert(sym->get_name());
483✔
362
            } else {
483✔
363
                dimensions_.insert(sym->get_name());
576✔
364
            }
365
        }
2,289✔
366
    }
367
    for (auto& dim : subset2_new) {
3,526✔
368
        for (auto& atom : symbolic::atoms(dim)) {
4,519✔
369
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
2,287✔
370
            if (sym->get_name() == indvar) {
2,287✔
371
                continue;
1,175✔
372
            }
373

374
            if (std::find(substitions.begin(), substitions.end(), sym->get_name()) !=
1,112✔
375
                substitions.end()) {
1,112✔
376
                continue;
66✔
377
            }
378
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
1,046✔
379
                parameters_.insert(sym->get_name());
494✔
380
            } else {
494✔
381
                dimensions_.insert(sym->get_name());
552✔
382
            }
383
        }
2,287✔
384
    }
385
    dimensions_.insert(indvar);
1,294✔
386
    std::vector<std::string> dimensions;
1,294✔
387
    for (auto& dim : dimensions_) {
3,298✔
388
        dimensions.push_back(dim);
2,004✔
389
    }
390
    for (auto mv : moving_symbols) {
4,884✔
391
        if (dimensions_.find(mv) == dimensions_.end()) {
3,590✔
392
            dimensions.push_back(mv);
1,586✔
393
            dimensions_.insert(mv);
1,586✔
394
        }
1,586✔
395
    }
3,590✔
396
    std::vector<std::string> parameters;
1,294✔
397
    for (auto& dim : parameters_) {
1,799✔
398
        parameters.push_back(dim);
505✔
399
    }
400

401
    // Double dimension space and constraints
402
    size_t k = 0;
1,294✔
403
    std::vector<std::string> doubled_dimensions;
1,294✔
404
    std::vector<std::string> constraints;
1,294✔
405
    for (auto& dim : dimensions) {
4,884✔
406
        doubled_dimensions.push_back(dim + "_1");
3,590✔
407
        doubled_dimensions.push_back(dim + "_2");
3,590✔
408

409
        // Proof: dim_1 != dim_2
410
        if (dim == indvar) {
3,590✔
411
            constraints.push_back(dim + "_1 != " + dim + "_2");
1,294✔
412
        }
1,294✔
413

414
        // Collect lb and ub
415
        auto lb1 = assumptions.at(symbolic::symbol(dim)).lower_bound();
3,590✔
416
        for (auto atom : symbolic::atoms(lb1)) {
4,183✔
417
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
593✔
418
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
593✔
419
                if (parameters_.find(sym->get_name()) == parameters_.end()) {
227✔
420
                    parameters_.insert(sym->get_name());
25✔
421
                    parameters.push_back(sym->get_name());
25✔
422
                }
25✔
423
            }
227✔
424
        }
593✔
425
        auto ub1 = assumptions.at(symbolic::symbol(dim)).upper_bound();
3,590✔
426
        for (auto atom : symbolic::atoms(ub1)) {
6,475✔
427
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
2,885✔
428
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
2,885✔
429
                if (parameters_.find(sym->get_name()) == parameters_.end()) {
2,627✔
430
                    parameters_.insert(sym->get_name());
1,830✔
431
                    parameters.push_back(sym->get_name());
1,830✔
432
                }
1,830✔
433
            }
2,627✔
434
        }
2,885✔
435

436
        // Add constraints
437
        auto lb2 = lb1;
3,590✔
438
        auto ub2 = ub1;
3,590✔
439
        for (auto& dim : dimensions) {
18,226✔
440
            lb1 = symbolic::subs(lb1, symbolic::symbol(dim), symbolic::symbol(dim + "_1"));
14,636✔
441
            ub1 = symbolic::subs(ub1, symbolic::symbol(dim), symbolic::symbol(dim + "_1"));
14,636✔
442
            lb2 = symbolic::subs(lb2, symbolic::symbol(dim), symbolic::symbol(dim + "_2"));
14,636✔
443
            ub2 = symbolic::subs(ub2, symbolic::symbol(dim), symbolic::symbol(dim + "_2"));
14,636✔
444
        }
445

446
        if (!SymEngine::eq(*lb1, *symbolic::infty(-1))) {
3,590✔
447
            auto mins = SymEngine::atoms<const SymEngine::Min>(*lb1);
3,590✔
448
            if (mins.size() > 0) {
3,590✔
449
                continue;
×
450
            }
451

452
            if (SymEngine::is_a<SymEngine::Max>(*lb1)) {
3,590✔
453
                auto max = SymEngine::rcp_static_cast<const SymEngine::Max>(lb1);
×
454
                auto args1 = max->get_args();
×
455
                constraints.push_back(language_extension.expression(args1[0]) + " <= " + dim +
×
456
                                      "_1");
457
                constraints.push_back(language_extension.expression(args1[1]) + " <= " + dim +
×
458
                                      "_1");
459

460
                auto max_ = SymEngine::rcp_static_cast<const SymEngine::Max>(lb2);
×
461
                auto args2 = max_->get_args();
×
462
                constraints.push_back(language_extension.expression(args2[0]) + " <= " + dim +
×
463
                                      "_2");
464
                constraints.push_back(language_extension.expression(args2[1]) + " <= " + dim +
×
465
                                      "_2");
466
            } else {
×
467
                constraints.push_back(language_extension.expression(lb1) + " <= " + dim + "_1");
3,590✔
468
                constraints.push_back(language_extension.expression(lb2) + " <= " + dim + "_2");
3,590✔
469
            }
470
        }
3,590✔
471
        if (!SymEngine::eq(*ub1, *symbolic::infty(1))) {
3,590✔
472
            auto maxs = SymEngine::atoms<const SymEngine::Max>(*ub1);
3,584✔
473
            if (maxs.size() > 0) {
3,584✔
474
                continue;
8✔
475
            }
476

477
            if (SymEngine::is_a<SymEngine::Min>(*ub1)) {
3,576✔
478
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub1);
71✔
479
                auto args1 = min->get_args();
71✔
480
                constraints.push_back(dim + "_1 <= " + language_extension.expression(args1[0]));
71✔
481
                constraints.push_back(dim + "_1 <= " + language_extension.expression(args1[1]));
71✔
482

483
                auto min_ = SymEngine::rcp_static_cast<const SymEngine::Min>(ub2);
71✔
484
                auto args2 = min_->get_args();
71✔
485
                constraints.push_back(dim + "_2 <= " + language_extension.expression(args2[0]));
71✔
486
                constraints.push_back(dim + "_2 <= " + language_extension.expression(args2[1]));
71✔
487
            } else {
71✔
488
                constraints.push_back(dim + "_1 <= " + language_extension.expression(ub1));
3,505✔
489
                constraints.push_back(dim + "_2 <= " + language_extension.expression(ub2));
3,505✔
490
            }
491
        }
3,584✔
492

493
        // Add map constraints
494
        auto map = assumptions.at(symbolic::symbol(dim)).map();
3,582✔
495
        if (map == SymEngine::null) {
3,582✔
496
            continue;
678✔
497
        }
498
        if (!SymEngine::is_a<SymEngine::Add>(*map)) {
2,904✔
499
            continue;
×
500
        }
501
        auto args = SymEngine::rcp_static_cast<const SymEngine::Add>(map)->get_args();
2,904✔
502
        if (args.size() != 2) {
2,904✔
503
            continue;
×
504
        }
505
        auto arg0 = args[0];
2,904✔
506
        auto arg1 = args[1];
2,904✔
507
        if (!symbolic::eq(arg0, symbolic::symbol(dim))) {
2,904✔
508
            arg0 = args[1];
2,904✔
509
            arg1 = args[0];
2,904✔
510
        }
2,904✔
511
        if (!symbolic::eq(arg0, symbolic::symbol(dim))) {
2,904✔
512
            continue;
×
513
        }
514
        if (!SymEngine::is_a<SymEngine::Integer>(*arg1)) {
2,904✔
515
            continue;
×
516
        }
517
        if (!SymEngine::is_a<SymEngine::Integer>(*lb1)) {
2,904✔
518
            continue;
585✔
519
        }
520

521
        std::string new_k = "__daisy_iterator" + std::to_string(k++);
2,319✔
522

523
        std::string k_1 = new_k + "_1";
2,319✔
524
        constraints.push_back("exists " + k_1 + " : " + dim +
4,638✔
525
                              "_1 = " + language_extension.expression(lb1) + " + " + k_1 + " * " +
4,638✔
526
                              language_extension.expression(arg1));
2,319✔
527

528
        std::string k_2 = new_k + "_2";
2,319✔
529
        constraints.push_back("exists " + k_2 + " : " + dim +
4,638✔
530
                              "_2 = " + language_extension.expression(lb1) + " + " + k_2 + " * " +
4,638✔
531
                              language_extension.expression(arg1));
2,319✔
532
    }
3,590✔
533

534
    // Extend parameters by dependening parameters
535
    size_t num_params = parameters.size();
1,294✔
536
    size_t num_params_old = 0;
1,294✔
537
    do {
1,294✔
538
        for (size_t i = 0; i < num_params; i++) {
4,418✔
539
            auto param = parameters[i];
2,945✔
540
            auto lb = assumptions.at(symbolic::symbol(param)).lower_bound();
2,945✔
541
            for (auto& atom : symbolic::atoms(lb)) {
3,029✔
542
                auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
84✔
543
                if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
84✔
544
                    if (parameters_.find(sym->get_name()) == parameters_.end()) {
84✔
545
                        parameters_.insert(sym->get_name());
21✔
546
                        parameters.push_back(sym->get_name());
21✔
547
                    }
21✔
548
                }
84✔
549
            }
84✔
550
            auto ub = assumptions.at(symbolic::symbol(param)).upper_bound();
2,945✔
551
            for (auto& atom : symbolic::atoms(ub)) {
3,718✔
552
                auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
773✔
553
                if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
773✔
554
                    if (parameters_.find(sym->get_name()) == parameters_.end()) {
773✔
555
                        parameters_.insert(sym->get_name());
187✔
556
                        parameters.push_back(sym->get_name());
187✔
557
                    }
187✔
558
                }
773✔
559
            }
773✔
560
        }
2,945✔
561
        num_params_old = num_params;
1,473✔
562
        num_params = parameters.size();
1,473✔
563
    } while (num_params != num_params_old);
1,473✔
564

565
    // Collect constraints for parameters
566
    for (size_t i = 0; i < parameters.size(); i++) {
3,862✔
567
        auto lb = assumptions.at(symbolic::symbol(parameters[i])).lower_bound();
2,568✔
568
        auto ub = assumptions.at(symbolic::symbol(parameters[i])).upper_bound();
2,568✔
569

570
        std::string constraint = "";
2,568✔
571
        if (!SymEngine::eq(*lb, *symbolic::infty(-1))) {
2,568✔
572
            if (SymEngine::is_a<SymEngine::Max>(*lb)) {
2,563✔
573
                auto max = SymEngine::rcp_static_cast<const SymEngine::Max>(lb);
×
574
                auto args = max->get_args();
×
575
                constraints.push_back(language_extension.expression(args[0]) +
×
576
                                      " <= " + parameters[i]);
×
577
                constraints.push_back(language_extension.expression(args[1]) +
×
578
                                      " <= " + parameters[i]);
×
579
            } else if (SymEngine::atoms<const SymEngine::Min>(*lb).size() > 0) {
2,563✔
UNCOV
580
            } else {
×
581
                constraints.push_back(language_extension.expression(lb) + " <= " + parameters[i]);
2,563✔
582
            }
583
        }
2,563✔
584
        if (!SymEngine::eq(*ub, *symbolic::infty(1))) {
2,568✔
585
            if (SymEngine::is_a<SymEngine::Min>(*ub)) {
774✔
586
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub);
21✔
587
                auto args = min->get_args();
21✔
588
                constraints.push_back(parameters[i] +
42✔
589
                                      " <= " + language_extension.expression(args[0]));
21✔
590
                constraints.push_back(parameters[i] +
42✔
591
                                      " <= " + language_extension.expression(args[1]));
21✔
592
            } else if (SymEngine::atoms<const SymEngine::Max>(*ub).size() > 0) {
774✔
UNCOV
593
            } else {
×
594
                constraints.push_back(parameters[i] + " <= " + language_extension.expression(ub));
753✔
595
            }
596
        }
774✔
597
    }
2,568✔
598

599
    // Allocate context
600
    isl_ctx* ctx = isl_ctx_alloc();
1,294✔
601

602
    // Define maps
603
    std::string map_1;
1,294✔
604
    if (!parameters.empty()) {
1,294✔
605
        map_1 += "[";
1,276✔
606
        map_1 += helpers::join(parameters, ", ");
1,276✔
607
    }
1,276✔
608
    if (!substitions.empty()) {
1,294✔
609
        if (!parameters.empty()) {
66✔
610
            map_1 += ", ";
66✔
611
        } else {
66✔
612
            map_1 += "[";
×
613
        }
614
        map_1 += helpers::join(substitions, ", ");
66✔
615
    }
66✔
616
    if (!map_1.empty()) {
1,294✔
617
        map_1 += "] -> ";
1,276✔
618
    }
1,276✔
619
    map_1 += "{ [" + helpers::join(doubled_dimensions, ", ") + "] -> [";
1,294✔
620
    for (size_t i = 0; i < subset1_new.size(); i++) {
3,526✔
621
        auto dim = subset1_new[i];
2,232✔
622
        for (auto& iter : dimensions) {
8,548✔
623
            dim = symbolic::subs(dim, symbolic::symbol(iter), symbolic::symbol(iter + "_1"));
6,316✔
624
        }
625
        map_1 += language_extension.expression(dim);
2,232✔
626
        if (i < subset1_new.size() - 1) {
2,232✔
627
            map_1 += ", ";
938✔
628
        }
938✔
629
    }
2,232✔
630
    map_1 += "] : " + helpers::join(constraints, " and ") + " }";
1,294✔
631

632
    std::string map_2;
1,294✔
633
    if (!parameters.empty()) {
1,294✔
634
        map_2 += "[";
1,276✔
635
        map_2 += helpers::join(parameters, ", ");
1,276✔
636
    }
1,276✔
637
    if (!substitions.empty()) {
1,294✔
638
        if (!parameters.empty()) {
66✔
639
            map_2 += ", ";
66✔
640
        } else {
66✔
641
            map_2 += "[";
×
642
        }
643
        map_2 += helpers::join(substitions, ", ");
66✔
644
    }
66✔
645
    if (!map_2.empty()) {
1,294✔
646
        map_2 += "] -> ";
1,276✔
647
    }
1,276✔
648
    map_2 += "{ [" + helpers::join(doubled_dimensions, ", ") + "] -> [";
1,294✔
649
    for (size_t i = 0; i < subset2_new.size(); i++) {
3,526✔
650
        auto dim = subset2_new[i];
2,232✔
651
        for (auto& iter : dimensions) {
8,548✔
652
            dim = symbolic::subs(dim, symbolic::symbol(iter), symbolic::symbol(iter + "_2"));
6,316✔
653
        }
654
        map_2 += language_extension.expression(dim);
2,232✔
655
        if (i < subset2_new.size() - 1) {
2,232✔
656
            map_2 += ", ";
938✔
657
        }
938✔
658
    }
2,232✔
659
    map_2 += "] : " + helpers::join(constraints, " and ") + " }";
1,294✔
660

661
    isl_map* index_map_1 = isl_map_read_from_str(ctx, map_1.c_str());
1,294✔
662
    if (!index_map_1) {
1,294✔
663
        isl_ctx_free(ctx);
×
664
        return false;
×
665
    }
666

667
    isl_map* index_map_2 = isl_map_read_from_str(ctx, map_2.c_str());
1,294✔
668
    if (!index_map_2) {
1,294✔
669
        isl_map_free(index_map_1);
×
670
        isl_ctx_free(ctx);
×
671
        return false;
×
672
    }
673

674
    isl_map* intersection = isl_map_intersect(index_map_1, index_map_2);
1,294✔
675
    if (!intersection) {
1,294✔
676
        isl_map_free(index_map_1);
×
677
        isl_map_free(index_map_2);
×
678
        isl_ctx_free(ctx);
×
679
        return false;
×
680
    }
681

682
    bool disjoint = isl_map_is_empty(intersection);
1,294✔
683

684
    isl_map_free(intersection);
1,294✔
685
    isl_ctx_free(ctx);
1,294✔
686

687
    return disjoint;
1,294✔
688
};
1,294✔
689

690
void DataParallelismAnalysis::classify(analysis::AnalysisManager& analysis_manager,
449✔
691
                                       const structured_control_flow::For* loop) {
692
    // Strictly monotonic update
693
    auto& indvar = loop->indvar();
449✔
694
    auto& update = loop->update();
449✔
695
    if (symbolic::strict_monotonicity(update, indvar) != symbolic::Sign::POSITIVE) {
449✔
696
        this->results_.insert({loop, DataParallelismAnalysisResult()});
×
UNCOV
697
        return;
×
698
    }
699

700
    // Users analysis
701
    auto& body = loop->root();
449✔
702
    auto& users = analysis_manager.get<analysis::Users>();
449✔
703
    analysis::UsersView body_users(users, body);
449✔
704
    if (!body_users.views().empty() || !body_users.moves().empty()) {
449✔
705
        this->results_.insert({loop, DataParallelismAnalysisResult()});
×
706
        return;
×
707
    }
708

709
    this->results_.insert({loop, DataParallelismAnalysisResult()});
449✔
710
    auto& result = this->results_.at(loop);
449✔
711

712
    // Assumptions analysis
713
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
449✔
714
    auto assumptions = assumptions_analysis.get(body);
449✔
715

716
    // For each container, we now classify the access pattern
717

718
    // 1. Identify private containers
719
    auto locals = users.locals(sdfg_, body);
449✔
720
    for (auto& local : locals) {
1,113✔
721
        result.insert({local, Parallelism::PRIVATE});
664✔
722
    }
723

724
    // 2. Filter our read-only containers
725
    std::unordered_set<std::string> writeset;
449✔
726
    std::unordered_set<std::string> moving_symbols = {indvar->get_name()};
449✔
727
    for (auto& entry : body_users.writes()) {
2,127✔
728
        writeset.insert(entry->container());
1,678✔
729

730
        auto& type = sdfg_.type(entry->container());
1,678✔
731
        if (!dynamic_cast<const types::Scalar*>(&type)) {
1,678✔
732
            continue;
631✔
733
        }
734
        if (!types::is_integer(type.primitive_type())) {
1,047✔
735
            continue;
293✔
736
        }
737
        moving_symbols.insert(entry->container());
754✔
738
    }
739
    for (auto& entry : body_users.reads()) {
6,697✔
740
        if (writeset.find(entry->container()) != writeset.end()) {
6,248✔
741
            // Loop-carried dependencies in tasklets's conditions
742
            if (dynamic_cast<data_flow::Tasklet*>(entry->element())) {
2,654✔
743
                // Locals cannot be loop-carried
744
                if (locals.find(entry->container()) != locals.end()) {
10✔
745
                    continue;
9✔
746
                } else {
747
                    result.clear();
1✔
748
                    return;
1✔
749
                }
750
            }
751

752
            continue;
2,644✔
753
        }
754
        if (entry->container() == indvar->get_name()) {
3,594✔
755
            continue;
1,505✔
756
        }
757
        result.insert({entry->container(), Parallelism::READONLY});
2,089✔
758
    }
759

760
    // 3. Prove parallelism
761
    /** For each container of the writeset, we check the following properties:
762
     *  1. For all i, writes are disjoint (false -> empty parallelism)
763
     *  2. For all i, reads and writes are disjoint (false -> empty parallelism)
764
     */
765
    for (auto& container : writeset) {
1,598✔
766
        // Skip if already classified
767
        if (result.find(container) != result.end()) {
1,150✔
768
            continue;
641✔
769
        }
770

771
        // For each i1, i2, subset(i1) and subset(i2) are disjoint
772
        bool ww_conflict = false;
509✔
773
        auto writes = body_users.writes(container);
509✔
774
        for (auto& write : writes) {
983✔
775
            for (auto& write_ : writes) {
1,386✔
776
                if (write == write_ && writes.size() > 1) {
912✔
777
                    continue;
200✔
778
                }
779

780
                // Determine moving symbols locally
781
                std::unordered_set<std::string> moving_symbols_local = moving_symbols;
712✔
782

783
                auto subsets = write->subsets();
712✔
784
                auto subsets_ = write_->subsets();
712✔
785
                for (auto& subset : subsets) {
1,284✔
786
                    for (auto& subset_ : subsets_) {
1,284✔
787
                        if (!this->disjoint(subset, subset_, indvar->get_name(),
712✔
788
                                            moving_symbols_local, assumptions)) {
789
                            ww_conflict = true;
140✔
790
                            break;
140✔
791
                        }
792
                    }
793
                    if (ww_conflict) {
712✔
794
                        break;
140✔
795
                    }
796
                }
797
                if (ww_conflict) {
712✔
798
                    break;
140✔
799
                }
800
            }
712✔
801
            if (ww_conflict) {
614✔
802
                break;
140✔
803
            }
804
        }
805
        if (ww_conflict) {
509✔
806
            result.insert({container, Parallelism::DEPENDENT});
140✔
807
            continue;
140✔
808
        }
809

810
        bool rw_conflict = false;
369✔
811
        auto reads = body_users.reads(container);
369✔
812
        for (auto& read : reads) {
720✔
813
            for (auto& write_ : writes) {
911✔
814
                // Determine moving symbols locally
815
                std::unordered_set<std::string> moving_symbols_local = moving_symbols;
560✔
816

817
                auto subsets = read->subsets();
560✔
818
                auto subsets_ = write_->subsets();
560✔
819
                for (auto& subset : subsets) {
1,128✔
820
                    for (auto& subset_ : subsets_) {
1,150✔
821
                        if (!this->disjoint(subset, subset_, indvar->get_name(),
582✔
822
                                            moving_symbols_local, assumptions)) {
823
                            rw_conflict = true;
14✔
824
                            break;
14✔
825
                        }
826
                    }
827
                    if (rw_conflict) {
582✔
828
                        break;
14✔
829
                    }
830
                }
831
                if (rw_conflict) {
560✔
832
                    break;
14✔
833
                }
834
            }
560✔
835
            if (rw_conflict) {
365✔
836
                break;
14✔
837
            }
838
        }
839
        if (rw_conflict) {
369✔
840
            result.insert({container, Parallelism::DEPENDENT});
14✔
841
            continue;
14✔
842
        }
843

844
        result.insert({container, Parallelism::PARALLEL});
355✔
845
    }
509✔
846

847
    // 4. Reductions
848
    for (auto& entry : result) {
2,706✔
849
        auto& container = entry.first;
2,258✔
850
        auto& dep_type = entry.second;
2,258✔
851
        if (dep_type != Parallelism::DEPENDENT) {
2,258✔
852
            continue;
2,104✔
853
        }
854

855
        // Check if it is a reduction
856
        auto reads = body_users.reads(container);
154✔
857
        auto writes = body_users.writes(container);
154✔
858

859
        // Criterion: Must write to constant location
860
        bool is_reduction = true;
154✔
861
        auto first_write = writes.at(0);
154✔
862
        auto first_subset = first_write->subsets().at(0);
154✔
863
        for (auto& dim : first_subset) {
288✔
864
            for (auto& sym : moving_symbols) {
450✔
865
                if (symbolic::uses(dim, sym)) {
316✔
866
                    is_reduction = false;
71✔
867
                    break;
71✔
868
                }
869
            }
870
            if (!is_reduction) {
205✔
871
                break;
71✔
872
            }
873
        }
874
        if (!is_reduction) {
154✔
875
            continue;
71✔
876
        }
877

878
        // Criterion: All writes must have the same subset
879
        for (auto& write : writes) {
166✔
880
            for (auto& subset : write->subsets()) {
166✔
881
                if (subset.size() != first_subset.size()) {
83✔
882
                    is_reduction = false;
×
883
                    break;
×
884
                }
885

886
                for (size_t i = 0; i < subset.size(); i++) {
200✔
887
                    if (!symbolic::eq(subset[i], first_subset[i])) {
117✔
888
                        is_reduction = false;
×
889
                        break;
×
890
                    }
891
                }
117✔
892
            }
893
        }
894
        if (!is_reduction) {
83✔
895
            continue;
×
896
        }
897

898
        // Criterion: All reads must have the same subset
899
        for (auto& read : reads) {
171✔
900
            for (auto& subset : read->subsets()) {
180✔
901
                if (subset.size() != first_subset.size()) {
92✔
902
                    is_reduction = false;
×
903
                    break;
×
904
                }
905

906
                for (size_t i = 0; i < subset.size(); i++) {
211✔
907
                    if (!symbolic::eq(subset[i], first_subset[i])) {
131✔
908
                        is_reduction = false;
12✔
909
                        break;
12✔
910
                    }
911
                }
119✔
912
            }
913
        }
914

915
        if (is_reduction) {
83✔
916
            result[container] = Parallelism::REDUCTION;
75✔
917
        }
75✔
918
    }
154✔
919
};
449✔
920

921
void DataParallelismAnalysis::run(analysis::AnalysisManager& analysis_manager) {
121✔
922
    this->loops_.clear();
121✔
923
    this->results_.clear();
121✔
924

925
    // Collect all for loops
926
    std::list<const structured_control_flow::ControlFlowNode*> queue = {&sdfg_.root()};
121✔
927
    while (!queue.empty()) {
1,548✔
928
        auto current = queue.front();
1,427✔
929
        queue.pop_front();
1,427✔
930

931
        if (auto sequence_stmt = dynamic_cast<const structured_control_flow::Sequence*>(current)) {
1,427✔
932
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
1,427✔
933
                queue.push_back(&sequence_stmt->at(i).first);
852✔
934
            }
852✔
935
        } else if (auto if_else_stmt =
1,427✔
936
                       dynamic_cast<const structured_control_flow::IfElse*>(current)) {
852✔
937
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
938
                queue.push_back(&if_else_stmt->at(i).first);
×
UNCOV
939
            }
×
940
        } else if (auto while_stmt = dynamic_cast<const structured_control_flow::While*>(current)) {
852✔
941
            queue.push_back(&while_stmt->root());
×
942
        } else if (auto for_stmt = dynamic_cast<const structured_control_flow::For*>(current)) {
852✔
943
            this->loops_.insert(for_stmt);
449✔
944
            queue.push_back(&for_stmt->root());
449✔
945
        } else if (auto kern_stmt = dynamic_cast<const structured_control_flow::Kernel*>(current)) {
852✔
946
            // this->loops_.insert(kern_stmt);
947
            queue.push_back(&kern_stmt->root());
5✔
948
        }
5✔
949
    }
950

951
    // Classify each loop
952
    for (auto& entry : this->loops_) {
570✔
953
        this->classify(analysis_manager, entry);
449✔
954
    }
955
};
121✔
956

957
DataParallelismAnalysis::DataParallelismAnalysis(StructuredSDFG& sdfg)
242✔
958
    : Analysis(sdfg) {
121✔
959

960
      };
121✔
961

962
const DataParallelismAnalysisResult& DataParallelismAnalysis::get(
231✔
963
    const structured_control_flow::For& loop) const {
964
    return this->results_.at(&loop);
231✔
965
};
966

967
bool DataParallelismAnalysis::is_contiguous(const structured_control_flow::For& loop) {
10✔
968
    return symbolic::contiguity(loop.update(), loop.indvar());
10✔
969
};
970

971
bool DataParallelismAnalysis::is_strictly_monotonic(const structured_control_flow::For& loop) {
×
972
    return symbolic::strict_monotonicity(loop.update(), loop.indvar()) == symbolic::Sign::POSITIVE;
×
973
};
974

975
symbolic::Expression DataParallelismAnalysis::bound(const structured_control_flow::For& loop) {
13✔
976
    auto& indvar = loop.indvar();
13✔
977
    auto& condition = loop.condition();
13✔
978
    auto args = condition->get_args();
13✔
979
    if (args.size() != 2) {
13✔
980
        return SymEngine::null;
×
981
    }
982
    auto& arg0 = args[0];
13✔
983
    auto& arg1 = args[1];
13✔
984
    if (SymEngine::eq(*arg0, *indvar)) {
13✔
985
        return arg1;
13✔
986
    } else if (SymEngine::eq(*arg1, *indvar)) {
×
987
        return arg0;
×
988
    }
989
    return SymEngine::null;
×
990
};
13✔
991

992
}  // namespace analysis
993
}  // 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