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

daisytuner / sdfglib / 15133758385

20 May 2025 09:19AM UTC coverage: 60.543% (-3.0%) from 63.542%
15133758385

push

github

web-flow
Merge pull request #22 from daisytuner/normalization

Removes normalization passes

7922 of 13085 relevant lines covered (60.54%)

104.24 hits per line

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

86.41
/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(
160✔
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;
160✔
13
    for (auto& dim : subset1) {
366✔
14
        auto args = dim->get_args();
206✔
15
        auto new_dim = dim;
206✔
16
        for (auto& arg : args) {
353✔
17
            if (!SymEngine::is_a<SymEngine::Symbol>(*arg) &&
222✔
18
                !SymEngine::is_a<SymEngine::Integer>(*arg)) {
75✔
19
                bool is_moving = false;
73✔
20
                for (auto& atom : symbolic::atoms(arg)) {
186✔
21
                    auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
113✔
22
                    if (moving_symbols.find(sym->get_name()) != moving_symbols.end()) {
113✔
23
                        is_moving = true;
34✔
24
                        break;
34✔
25
                    }
26
                }
113✔
27
                if (!is_moving) {
73✔
28
                    if (replacements.find(arg) != replacements.end()) {
39✔
29
                        new_dim = symbolic::subs(new_dim, arg, replacements.at(arg));
×
30
                    } else {
×
31
                        auto repl = symbolic::symbol("c_" + std::to_string(replacements.size()));
39✔
32
                        substitions.push_back(repl->get_name());
39✔
33
                        replacements.insert({arg, repl});
39✔
34
                        new_dim = symbolic::subs(new_dim, arg, repl);
39✔
35
                    }
39✔
36
                }
39✔
37
            }
73✔
38
        }
39
        subset1_new.push_back(new_dim);
206✔
40
    }
206✔
41

42
    data_flow::Subset subset2_new;
160✔
43
    for (auto& dim : subset2) {
366✔
44
        auto args = dim->get_args();
206✔
45
        auto new_dim = dim;
206✔
46
        for (auto& arg : args) {
351✔
47
            if (!SymEngine::is_a<SymEngine::Symbol>(*arg) &&
218✔
48
                !SymEngine::is_a<SymEngine::Integer>(*arg)) {
73✔
49
                bool is_moving = false;
73✔
50
                for (auto& atom : symbolic::atoms(arg)) {
186✔
51
                    auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
113✔
52
                    if (moving_symbols.find(sym->get_name()) != moving_symbols.end()) {
113✔
53
                        is_moving = true;
34✔
54
                        break;
34✔
55
                    }
56
                }
113✔
57
                if (!is_moving) {
73✔
58
                    if (replacements.find(arg) != replacements.end()) {
39✔
59
                        new_dim = symbolic::subs(new_dim, arg, replacements.at(arg));
39✔
60
                    } else {
39✔
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
                }
39✔
67
            }
73✔
68
        }
69
        subset2_new.push_back(new_dim);
206✔
70
    }
206✔
71

72
    return {subset1_new, subset2_new};
160✔
73
};
160✔
74

75
std::pair<data_flow::Subset, data_flow::Subset> DataParallelismAnalysis::delinearization(
162✔
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;
162✔
86
    for (auto& dim : subset1) {
370✔
87
        if (!SymEngine::is_a<SymEngine::Add>(*dim)) {
208✔
88
            subset1_new.push_back(dim);
135✔
89
            continue;
135✔
90
        }
91
        auto add = SymEngine::rcp_static_cast<const SymEngine::Add>(dim);
73✔
92
        if (add->get_args().size() != 2) {
73✔
93
            subset1_new.push_back(dim);
5✔
94
            continue;
5✔
95
        }
96
        auto offset = add->get_args()[0];
68✔
97
        auto mult = add->get_args()[1];
68✔
98
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
68✔
99
            auto tmp = offset;
37✔
100
            offset = mult;
37✔
101
            mult = tmp;
37✔
102
        }
37✔
103
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
68✔
104
            subset1_new.push_back(dim);
36✔
105
            continue;
36✔
106
        }
107

108
        // Offset must be a symbol and moving
109
        if (!SymEngine::is_a<SymEngine::Symbol>(*offset)) {
32✔
110
            subset1_new.push_back(dim);
×
111
            continue;
×
112
        }
113
        auto off = SymEngine::rcp_static_cast<const SymEngine::Symbol>(offset);
32✔
114
        if (moving_symbols.find(off->get_name()) == moving_symbols.end()) {
32✔
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);
32✔
121
        if (mult_->get_args().size() != 2) {
32✔
122
            subset1_new.push_back(dim);
×
123
            continue;
×
124
        }
125
        auto multiplier = mult_->get_args()[0];
32✔
126
        auto indvar_ = mult_->get_args()[1];
32✔
127
        if (!SymEngine::is_a<SymEngine::Symbol>(*multiplier) ||
64✔
128
            !SymEngine::is_a<SymEngine::Symbol>(*indvar_)) {
32✔
129
            subset1_new.push_back(dim);
×
130
            continue;
×
131
        }
132
        auto mul = SymEngine::rcp_static_cast<const SymEngine::Symbol>(multiplier);
32✔
133
        auto indvar = SymEngine::rcp_static_cast<const SymEngine::Symbol>(indvar_);
32✔
134
        if (moving_symbols.find(mul->get_name()) != moving_symbols.end()) {
32✔
135
            auto tmp = mul;
28✔
136
            mul = indvar;
28✔
137
            indvar = tmp;
28✔
138
        }
28✔
139
        if (moving_symbols.find(mul->get_name()) != moving_symbols.end() ||
64✔
140
            moving_symbols.find(indvar->get_name()) == moving_symbols.end()) {
32✔
141
            subset1_new.push_back(dim);
×
142
            continue;
×
143
        }
144

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

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

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

180
    data_flow::Subset subset2_new;
162✔
181
    for (auto& dim : subset2) {
370✔
182
        if (!SymEngine::is_a<SymEngine::Add>(*dim)) {
208✔
183
            subset2_new.push_back(dim);
136✔
184
            continue;
136✔
185
        }
186
        auto add = SymEngine::rcp_static_cast<const SymEngine::Add>(dim);
72✔
187
        if (add->get_args().size() != 2) {
72✔
188
            subset2_new.push_back(dim);
5✔
189
            continue;
5✔
190
        }
191
        auto offset = add->get_args()[0];
67✔
192
        auto mult = add->get_args()[1];
67✔
193
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
67✔
194
            auto tmp = offset;
36✔
195
            offset = mult;
36✔
196
            mult = tmp;
36✔
197
        }
36✔
198
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
67✔
199
            subset2_new.push_back(dim);
35✔
200
            continue;
35✔
201
        }
202

203
        // Offset must be a symbol and moving
204
        if (!SymEngine::is_a<SymEngine::Symbol>(*offset)) {
32✔
205
            subset2_new.push_back(dim);
×
206
            continue;
×
207
        }
208
        auto off = SymEngine::rcp_static_cast<const SymEngine::Symbol>(offset);
32✔
209
        if (moving_symbols.find(off->get_name()) == moving_symbols.end()) {
32✔
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);
32✔
216
        if (mult_->get_args().size() != 2) {
32✔
217
            subset2_new.push_back(dim);
×
218
            continue;
×
219
        }
220
        auto multiplier = mult_->get_args()[0];
32✔
221
        auto indvar_ = mult_->get_args()[1];
32✔
222
        if (!SymEngine::is_a<SymEngine::Symbol>(*multiplier) ||
64✔
223
            !SymEngine::is_a<SymEngine::Symbol>(*indvar_)) {
32✔
224
            subset2_new.push_back(dim);
×
225
            continue;
×
226
        }
227
        auto mul = SymEngine::rcp_static_cast<const SymEngine::Symbol>(multiplier);
32✔
228
        auto indvar = SymEngine::rcp_static_cast<const SymEngine::Symbol>(indvar_);
32✔
229
        if (moving_symbols.find(mul->get_name()) != moving_symbols.end()) {
32✔
230
            auto tmp = mul;
28✔
231
            mul = indvar;
28✔
232
            indvar = tmp;
28✔
233
        }
28✔
234
        if (moving_symbols.find(mul->get_name()) != moving_symbols.end() ||
64✔
235
            moving_symbols.find(indvar->get_name()) == moving_symbols.end()) {
32✔
236
            subset2_new.push_back(dim);
×
237
            continue;
×
238
        }
239

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

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

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

275
    return {subset1_new, subset2_new};
162✔
276
};
162✔
277

278
bool DataParallelismAnalysis::disjoint(const data_flow::Subset& subset1,
159✔
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()) {
159✔
283
        return false;
×
284
    }
285

286
    codegen::CPPLanguageExtension language_extension;
159✔
287

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

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

298
    // Overapproximate multiplications with parameters
299
    data_flow::Subset subset1_new;
159✔
300
    for (auto& dim : subset1_2) {
393✔
301
        auto dim_ = dim;
234✔
302
        for (auto mul : symbolic::muls(dim)) {
239✔
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(
×
307
                    SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg1)->get_name()) ==
×
308
                    moving_symbols.end()) {
×
309
                dim_ = symbolic::subs(dim_, mul_, symbolic::one());
×
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());
×
317
                }
×
318
            }
5✔
319
        }
5✔
320
        subset1_new.push_back(dim_);
234✔
321
    }
234✔
322
    data_flow::Subset subset2_new;
159✔
323
    for (auto& dim : subset2_2) {
393✔
324
        auto dim_ = dim;
234✔
325
        for (auto mul : symbolic::muls(dim)) {
239✔
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(
×
330
                    SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg1)->get_name()) ==
×
331
                    moving_symbols.end()) {
×
332
                dim_ = symbolic::subs(dim_, mul_, symbolic::one());
×
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());
×
340
                }
×
341
            }
5✔
342
        }
5✔
343
        subset2_new.push_back(dim_);
234✔
344
    }
234✔
345

346
    // Collect parameters and dimensions
347
    std::unordered_set<std::string> dimensions_;
159✔
348
    std::unordered_set<std::string> parameters_;
159✔
349
    for (auto& dim : subset1_new) {
393✔
350
        for (auto& atom : symbolic::atoms(dim)) {
507✔
351
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
273✔
352
            if (sym->get_name() == indvar) {
273✔
353
                continue;
122✔
354
            }
355

356
            if (std::find(substitions.begin(), substitions.end(), sym->get_name()) !=
151✔
357
                substitions.end()) {
151✔
358
                continue;
38✔
359
            }
360
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
113✔
361
                parameters_.insert(sym->get_name());
40✔
362
            } else {
40✔
363
                dimensions_.insert(sym->get_name());
73✔
364
            }
365
        }
273✔
366
    }
367
    for (auto& dim : subset2_new) {
393✔
368
        for (auto& atom : symbolic::atoms(dim)) {
508✔
369
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
274✔
370
            if (sym->get_name() == indvar) {
274✔
371
                continue;
122✔
372
            }
373

374
            if (std::find(substitions.begin(), substitions.end(), sym->get_name()) !=
152✔
375
                substitions.end()) {
152✔
376
                continue;
38✔
377
            }
378
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
114✔
379
                parameters_.insert(sym->get_name());
41✔
380
            } else {
41✔
381
                dimensions_.insert(sym->get_name());
73✔
382
            }
383
        }
274✔
384
    }
385
    dimensions_.insert(indvar);
159✔
386
    std::vector<std::string> dimensions;
159✔
387
    for (auto& dim : dimensions_) {
391✔
388
        dimensions.push_back(dim);
232✔
389
    }
390
    for (auto mv : moving_symbols) {
790✔
391
        if (dimensions_.find(mv) == dimensions_.end()) {
631✔
392
            dimensions.push_back(mv);
399✔
393
            dimensions_.insert(mv);
399✔
394
        }
399✔
395
    }
631✔
396
    std::vector<std::string> parameters;
159✔
397
    for (auto& dim : parameters_) {
200✔
398
        parameters.push_back(dim);
41✔
399
    }
400

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

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

414
        // Collect lb and ub
415
        auto lb1 = assumptions.at(symbolic::symbol(dim)).lower_bound();
631✔
416
        for (auto atom : symbolic::atoms(lb1)) {
718✔
417
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
87✔
418
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
87✔
419
                if (parameters_.find(sym->get_name()) == parameters_.end()) {
30✔
420
                    parameters_.insert(sym->get_name());
25✔
421
                    parameters.push_back(sym->get_name());
25✔
422
                }
25✔
423
            }
30✔
424
        }
87✔
425
        auto ub1 = assumptions.at(symbolic::symbol(dim)).upper_bound();
631✔
426
        for (auto atom : symbolic::atoms(ub1)) {
995✔
427
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
364✔
428
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
364✔
429
                if (parameters_.find(sym->get_name()) == parameters_.end()) {
313✔
430
                    parameters_.insert(sym->get_name());
221✔
431
                    parameters.push_back(sym->get_name());
221✔
432
                }
221✔
433
            }
313✔
434
        }
364✔
435

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

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

452
            if (SymEngine::is_a<SymEngine::Max>(*lb1)) {
631✔
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");
631✔
468
                constraints.push_back(language_extension.expression(lb2) + " <= " + dim + "_2");
631✔
469
            }
470
        }
631✔
471
        if (!SymEngine::eq(*ub1, *symbolic::infty(1))) {
631✔
472
            auto maxs = SymEngine::atoms<const SymEngine::Max>(*ub1);
625✔
473
            if (maxs.size() > 0) {
625✔
474
                continue;
8✔
475
            }
476

477
            if (SymEngine::is_a<SymEngine::Min>(*ub1)) {
617✔
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));
546✔
489
                constraints.push_back(dim + "_2 <= " + language_extension.expression(ub2));
546✔
490
            }
491
        }
625✔
492

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

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

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

528
        std::string k_2 = new_k + "_2";
202✔
529
        constraints.push_back("exists " + k_2 + " : " + dim +
404✔
530
                              "_2 = " + language_extension.expression(lb1) + " + " + k_2 + " * " +
404✔
531
                              language_extension.expression(arg1));
202✔
532
    }
631✔
533

534
    // Extend parameters by dependening parameters
535
    size_t num_params = parameters.size();
159✔
536
    size_t num_params_old = 0;
159✔
537
    do {
159✔
538
        for (size_t i = 0; i < num_params; i++) {
602✔
539
            auto param = parameters[i];
417✔
540
            auto lb = assumptions.at(symbolic::symbol(param)).lower_bound();
417✔
541
            for (auto& atom : symbolic::atoms(lb)) {
459✔
542
                auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
42✔
543
                if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
42✔
544
                    if (parameters_.find(sym->get_name()) == parameters_.end()) {
42✔
545
                        parameters_.insert(sym->get_name());
21✔
546
                        parameters.push_back(sym->get_name());
21✔
547
                    }
21✔
548
                }
42✔
549
            }
42✔
550
            auto ub = assumptions.at(symbolic::symbol(param)).upper_bound();
417✔
551
            for (auto& atom : symbolic::atoms(ub)) {
589✔
552
                auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
172✔
553
                if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
172✔
554
                    if (parameters_.find(sym->get_name()) == parameters_.end()) {
172✔
555
                        parameters_.insert(sym->get_name());
29✔
556
                        parameters.push_back(sym->get_name());
29✔
557
                    }
29✔
558
                }
172✔
559
            }
172✔
560
        }
417✔
561
        num_params_old = num_params;
185✔
562
        num_params = parameters.size();
185✔
563
    } while (num_params != num_params_old);
185✔
564

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

570
        std::string constraint = "";
337✔
571
        if (!SymEngine::eq(*lb, *symbolic::infty(-1))) {
337✔
572
            if (SymEngine::is_a<SymEngine::Max>(*lb)) {
332✔
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) {
332✔
580
            } else {
×
581
                constraints.push_back(language_extension.expression(lb) + " <= " + parameters[i]);
332✔
582
            }
583
        }
332✔
584
        if (!SymEngine::eq(*ub, *symbolic::infty(1))) {
337✔
585
            if (SymEngine::is_a<SymEngine::Min>(*ub)) {
184✔
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) {
184✔
593
            } else {
×
594
                constraints.push_back(parameters[i] + " <= " + language_extension.expression(ub));
163✔
595
            }
596
        }
184✔
597
    }
337✔
598

599
    // Allocate context
600
    isl_ctx* ctx = isl_ctx_alloc();
159✔
601

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

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

661
    isl_map* index_map_1 = isl_map_read_from_str(ctx, map_1.c_str());
159✔
662
    if (!index_map_1) {
159✔
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());
159✔
668
    if (!index_map_2) {
159✔
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);
159✔
675
    if (!intersection) {
159✔
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);
159✔
683

684
    isl_map_free(intersection);
159✔
685
    isl_ctx_free(ctx);
159✔
686

687
    return disjoint;
159✔
688
};
159✔
689

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

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

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

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

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

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

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

730
        auto& type = sdfg_.type(entry->container());
243✔
731
        if (!dynamic_cast<const types::Scalar*>(&type)) {
243✔
732
            continue;
87✔
733
        }
734
        if (!types::is_integer(type.primitive_type())) {
156✔
735
            continue;
22✔
736
        }
737
        moving_symbols.insert(entry->container());
134✔
738
    }
739
    for (auto& entry : body_users.reads()) {
901✔
740
        if (writeset.find(entry->container()) != writeset.end()) {
827✔
741
            // Loop-carried dependencies in tasklets's conditions
742
            if (dynamic_cast<data_flow::Tasklet*>(entry->element())) {
345✔
743
                // Locals cannot be loop-carried
744
                if (locals.find(entry->container()) != locals.end()) {
6✔
745
                    continue;
5✔
746
                } else {
747
                    result.clear();
1✔
748
                    return;
1✔
749
                }
750
            }
751

752
            continue;
339✔
753
        }
754
        if (entry->container() == indvar->get_name()) {
482✔
755
            continue;
183✔
756
        }
757
        result.insert({entry->container(), Parallelism::READONLY});
299✔
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) {
238✔
766
        // Skip if already classified
767
        if (result.find(container) != result.end()) {
165✔
768
            continue;
83✔
769
        }
770

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

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

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

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

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

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

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

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

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

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

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

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

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

915
        if (is_reduction) {
13✔
916
            result[container] = Parallelism::REDUCTION;
13✔
917
        }
13✔
918
    }
24✔
919
};
74✔
920

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

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

931
        if (auto sequence_stmt = dynamic_cast<const structured_control_flow::Sequence*>(current)) {
274✔
932
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
274✔
933
                queue.push_back(&sequence_stmt->at(i).first);
153✔
934
            }
153✔
935
        } else if (auto if_else_stmt =
274✔
936
                       dynamic_cast<const structured_control_flow::IfElse*>(current)) {
153✔
937
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
938
                queue.push_back(&if_else_stmt->at(i).first);
×
939
            }
×
940
        } else if (auto while_stmt = dynamic_cast<const structured_control_flow::While*>(current)) {
153✔
941
            queue.push_back(&while_stmt->root());
×
942
        } else if (auto for_stmt = dynamic_cast<const structured_control_flow::For*>(current)) {
153✔
943
            this->loops_.insert(for_stmt);
74✔
944
            queue.push_back(&for_stmt->root());
74✔
945
        } else if (auto kern_stmt = dynamic_cast<const structured_control_flow::Kernel*>(current)) {
153✔
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_) {
116✔
953
        this->classify(analysis_manager, entry);
74✔
954
    }
955
};
42✔
956

957
DataParallelismAnalysis::DataParallelismAnalysis(StructuredSDFG& sdfg)
84✔
958
    : Analysis(sdfg) {
42✔
959

960
      };
42✔
961

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

967
bool DataParallelismAnalysis::is_contiguous(const structured_control_flow::For& loop) {
9✔
968
    return symbolic::contiguity(loop.update(), loop.indvar());
9✔
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) {
2✔
976
    auto& indvar = loop.indvar();
2✔
977
    auto& condition = loop.condition();
2✔
978
    auto args = condition->get_args();
2✔
979
    if (args.size() != 2) {
2✔
980
        return SymEngine::null;
×
981
    }
982
    auto& arg0 = args[0];
2✔
983
    auto& arg1 = args[1];
2✔
984
    if (SymEngine::eq(*arg0, *indvar)) {
2✔
985
        return arg1;
2✔
986
    } else if (SymEngine::eq(*arg1, *indvar)) {
×
987
        return arg0;
×
988
    }
989
    return SymEngine::null;
×
990
};
2✔
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