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

daisytuner / sdfglib / 15827874660

23 Jun 2025 03:03PM UTC coverage: 64.193% (+0.4%) from 63.824%
15827874660

push

github

web-flow
Merge pull request #100 from daisytuner/transformations

new definition of map and adapts transformations

148 of 194 new or added lines in 13 files covered. (76.29%)

45 existing lines in 4 files now uncovered.

8193 of 12763 relevant lines covered (64.19%)

136.04 hits per line

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

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

3
#include <regex>
4

5
#include "sdfg/analysis/assumptions_analysis.h"
6
#include "sdfg/analysis/loop_analysis.h"
7
#include "sdfg/analysis/users.h"
8
#include "sdfg/codegen/language_extensions/cpp_language_extension.h"
9
#include "sdfg/structured_control_flow/structured_loop.h"
10

11
namespace sdfg {
12
namespace analysis {
13

14
std::pair<data_flow::Subset, data_flow::Subset> DataParallelismAnalysis::substitution(
78✔
15
    const data_flow::Subset& subset1, const data_flow::Subset& subset2, const std::string& indvar,
16
    const std::unordered_set<std::string>& moving_symbols, symbolic::ExpressionMap& replacements,
17
    std::vector<std::string>& substitions) {
18
    data_flow::Subset subset1_new;
78✔
19
    for (auto& dim : subset1) {
163✔
20
        auto args = dim->get_args();
85✔
21
        auto new_dim = dim;
85✔
22
        for (auto& arg : args) {
168✔
23
            if (!SymEngine::is_a<SymEngine::Symbol>(*arg) &&
124✔
24
                !SymEngine::is_a<SymEngine::Integer>(*arg)) {
41✔
25
                bool is_moving = false;
39✔
26
                for (auto& atom : symbolic::atoms(arg)) {
112✔
27
                    auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
73✔
28
                    if (moving_symbols.find(sym->get_name()) != moving_symbols.end()) {
73✔
29
                        is_moving = true;
6✔
30
                        break;
6✔
31
                    }
32
                }
73✔
33
                if (!is_moving) {
39✔
34
                    if (replacements.find(arg) != replacements.end()) {
33✔
35
                        new_dim = symbolic::subs(new_dim, arg, replacements.at(arg));
×
36
                    } else {
×
37
                        auto repl = symbolic::symbol("c_" + std::to_string(replacements.size()));
33✔
38
                        substitions.push_back(repl->get_name());
33✔
39
                        replacements.insert({arg, repl});
33✔
40
                        new_dim = symbolic::subs(new_dim, arg, repl);
33✔
41
                    }
33✔
42
                }
33✔
43
            }
39✔
44
        }
45
        subset1_new.push_back(new_dim);
85✔
46
    }
85✔
47

48
    data_flow::Subset subset2_new;
78✔
49
    for (auto& dim : subset2) {
163✔
50
        auto args = dim->get_args();
85✔
51
        auto new_dim = dim;
85✔
52
        for (auto& arg : args) {
166✔
53
            if (!SymEngine::is_a<SymEngine::Symbol>(*arg) &&
120✔
54
                !SymEngine::is_a<SymEngine::Integer>(*arg)) {
39✔
55
                bool is_moving = false;
39✔
56
                for (auto& atom : symbolic::atoms(arg)) {
112✔
57
                    auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
73✔
58
                    if (moving_symbols.find(sym->get_name()) != moving_symbols.end()) {
73✔
59
                        is_moving = true;
6✔
60
                        break;
6✔
61
                    }
62
                }
73✔
63
                if (!is_moving) {
39✔
64
                    if (replacements.find(arg) != replacements.end()) {
33✔
65
                        new_dim = symbolic::subs(new_dim, arg, replacements.at(arg));
33✔
66
                    } else {
33✔
67
                        auto repl = symbolic::symbol("c_" + std::to_string(replacements.size()));
×
68
                        substitions.push_back(repl->get_name());
×
69
                        replacements.insert({arg, repl});
×
70
                        new_dim = symbolic::subs(new_dim, arg, repl);
×
71
                    }
×
72
                }
33✔
73
            }
39✔
74
        }
75
        subset2_new.push_back(new_dim);
85✔
76
    }
85✔
77

78
    return {subset1_new, subset2_new};
78✔
79
};
78✔
80

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

114
        // Offset must be a symbol and moving
115
        if (!SymEngine::is_a<SymEngine::Symbol>(*offset)) {
8✔
116
            subset1_new.push_back(dim);
×
117
            continue;
×
118
        }
119
        auto off = SymEngine::rcp_static_cast<const SymEngine::Symbol>(offset);
8✔
120
        if (moving_symbols.find(off->get_name()) == moving_symbols.end()) {
8✔
121
            subset1_new.push_back(dim);
×
122
            continue;
×
123
        }
124

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

151
        bool is_nonnegative = false;
8✔
152
        symbolic::ExpressionSet lbs_off;
8✔
153
        symbolic::lower_bounds(off, assumptions, lbs_off);
8✔
154
        for (auto& lb : lbs_off) {
14✔
155
            if (SymEngine::is_a<SymEngine::Integer>(*lb)) {
8✔
156
                auto lb_int = SymEngine::rcp_static_cast<const SymEngine::Integer>(lb);
8✔
157
                if (lb_int->as_int() >= 0) {
8✔
158
                    is_nonnegative = true;
2✔
159
                    break;
2✔
160
                }
161
            }
8✔
162
        }
163
        if (!is_nonnegative) {
8✔
164
            subset1_new.push_back(dim);
6✔
165
            continue;
6✔
166
        }
167

168
        bool success = false;
2✔
169
        symbolic::ExpressionSet ubs_off;
2✔
170
        symbolic::upper_bounds(off, assumptions, ubs_off);
2✔
171
        for (auto& ub_off : ubs_off) {
4✔
172
            if (symbolic::eq(mul, symbolic::add(ub_off, symbolic::one()))) {
3✔
173
                subset1_new.push_back(indvar);
1✔
174
                subset1_new.push_back(off);
1✔
175

176
                success = true;
1✔
177
                break;
1✔
178
            }
179
        }
180
        if (success) {
2✔
181
            continue;
1✔
182
        }
183
        subset1_new.push_back(dim);
1✔
184
    }
43✔
185

186
    data_flow::Subset subset2_new;
80✔
187
    for (auto& dim : subset2) {
167✔
188
        if (!SymEngine::is_a<SymEngine::Add>(*dim)) {
87✔
189
            subset2_new.push_back(dim);
45✔
190
            continue;
45✔
191
        }
192
        auto add = SymEngine::rcp_static_cast<const SymEngine::Add>(dim);
42✔
193
        if (add->get_args().size() != 2) {
42✔
194
            subset2_new.push_back(dim);
1✔
195
            continue;
1✔
196
        }
197
        auto offset = add->get_args()[0];
41✔
198
        auto mult = add->get_args()[1];
41✔
199
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
41✔
200
            auto tmp = offset;
33✔
201
            offset = mult;
33✔
202
            mult = tmp;
33✔
203
        }
33✔
204
        if (!SymEngine::is_a<SymEngine::Mul>(*mult)) {
41✔
205
            subset2_new.push_back(dim);
33✔
206
            continue;
33✔
207
        }
208

209
        // Offset must be a symbol and moving
210
        if (!SymEngine::is_a<SymEngine::Symbol>(*offset)) {
8✔
211
            subset2_new.push_back(dim);
×
212
            continue;
×
213
        }
214
        auto off = SymEngine::rcp_static_cast<const SymEngine::Symbol>(offset);
8✔
215
        if (moving_symbols.find(off->get_name()) == moving_symbols.end()) {
8✔
216
            subset2_new.push_back(dim);
×
217
            continue;
×
218
        }
219

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

246
        bool is_nonnegative = false;
8✔
247
        symbolic::ExpressionSet lbs_off;
8✔
248
        symbolic::lower_bounds(off, assumptions, lbs_off);
8✔
249
        for (auto& lb : lbs_off) {
14✔
250
            if (SymEngine::is_a<SymEngine::Integer>(*lb)) {
8✔
251
                auto lb_int = SymEngine::rcp_static_cast<const SymEngine::Integer>(lb);
8✔
252
                if (lb_int->as_int() >= 0) {
8✔
253
                    is_nonnegative = true;
2✔
254
                    break;
2✔
255
                }
256
            }
8✔
257
        }
258
        if (!is_nonnegative) {
8✔
259
            subset2_new.push_back(dim);
6✔
260
            continue;
6✔
261
        }
262

263
        bool success = false;
2✔
264
        symbolic::ExpressionSet ubs_off;
2✔
265
        symbolic::upper_bounds(off, assumptions, ubs_off);
2✔
266
        for (auto& ub_off : ubs_off) {
4✔
267
            if (symbolic::eq(mul, symbolic::add(ub_off, symbolic::one()))) {
3✔
268
                subset2_new.push_back(indvar);
1✔
269
                subset2_new.push_back(off);
1✔
270

271
                success = true;
1✔
272
                break;
1✔
273
            }
274
        }
275
        if (success) {
2✔
276
            continue;
1✔
277
        }
278
        subset2_new.push_back(dim);
1✔
279
    }
42✔
280

281
    return {subset1_new, subset2_new};
80✔
282
};
80✔
283

284
bool DataParallelismAnalysis::disjoint(const data_flow::Subset& subset1,
77✔
285
                                       const data_flow::Subset& subset2, const std::string& indvar,
286
                                       const std::unordered_set<std::string>& moving_symbols,
287
                                       const symbolic::Assumptions& assumptions) {
288
    if (subset1.size() != subset2.size()) {
77✔
289
        return false;
×
290
    }
291

292
    codegen::CPPLanguageExtension language_extension;
77✔
293

294
    // Attempt to substitute complex constant expressions by parameters
295
    symbolic::ExpressionMap replacements;
77✔
296
    std::vector<std::string> substitions;
77✔
297
    auto [subset1_, subset2_] = DataParallelismAnalysis::substitution(
77✔
298
        subset1, subset2, indvar, moving_symbols, replacements, substitions);
77✔
299

300
    // Attempt to delinearize subsets
301
    auto [subset1_2, subset2_2] =
231✔
302
        DataParallelismAnalysis::delinearization(subset1_, subset2_, moving_symbols, assumptions);
77✔
303

304
    // Overapproximate multiplications with parameters
305
    data_flow::Subset subset1_new;
77✔
306
    for (auto& dim : subset1_2) {
161✔
307
        auto dim_ = dim;
84✔
308
        for (auto mul : symbolic::muls(dim)) {
90✔
309
            auto mul_ = SymEngine::rcp_static_cast<const SymEngine::Mul>(mul);
6✔
310
            auto arg1 = mul_->get_args()[0];
6✔
311
            if (SymEngine::is_a<SymEngine::Symbol>(*arg1) &&
11✔
312
                moving_symbols.find(
10✔
313
                    SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg1)->get_name()) ==
10✔
314
                    moving_symbols.end()) {
5✔
315
                dim_ = symbolic::subs(dim_, mul_, symbolic::one());
×
316
            } else {
×
317
                auto arg2 = mul_->get_args()[1];
6✔
318
                if (SymEngine::is_a<SymEngine::Symbol>(*arg2) &&
12✔
319
                    moving_symbols.find(
12✔
320
                        SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg2)->get_name()) ==
12✔
321
                        moving_symbols.end()) {
6✔
322
                    dim_ = symbolic::subs(dim_, mul_, symbolic::one());
5✔
323
                }
5✔
324
            }
6✔
325
        }
6✔
326
        subset1_new.push_back(dim_);
84✔
327
    }
84✔
328
    data_flow::Subset subset2_new;
77✔
329
    for (auto& dim : subset2_2) {
161✔
330
        auto dim_ = dim;
84✔
331
        for (auto mul : symbolic::muls(dim)) {
90✔
332
            auto mul_ = SymEngine::rcp_static_cast<const SymEngine::Mul>(mul);
6✔
333
            auto arg1 = mul_->get_args()[0];
6✔
334
            if (SymEngine::is_a<SymEngine::Symbol>(*arg1) &&
11✔
335
                moving_symbols.find(
10✔
336
                    SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg1)->get_name()) ==
10✔
337
                    moving_symbols.end()) {
5✔
338
                dim_ = symbolic::subs(dim_, mul_, symbolic::one());
×
339
            } else {
×
340
                auto arg2 = mul_->get_args()[1];
6✔
341
                if (SymEngine::is_a<SymEngine::Symbol>(*arg2) &&
12✔
342
                    moving_symbols.find(
12✔
343
                        SymEngine::rcp_static_cast<const SymEngine::Symbol>(arg2)->get_name()) ==
12✔
344
                        moving_symbols.end()) {
6✔
345
                    dim_ = symbolic::subs(dim_, mul_, symbolic::one());
5✔
346
                }
5✔
347
            }
6✔
348
        }
6✔
349
        subset2_new.push_back(dim_);
84✔
350
    }
84✔
351

352
    // Collect parameters and dimensions
353
    std::unordered_set<std::string> dimensions_;
77✔
354
    std::unordered_set<std::string> parameters_;
77✔
355
    for (auto& dim : subset1_new) {
161✔
356
        for (auto& atom : symbolic::atoms(dim)) {
200✔
357
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
116✔
358
            if (sym->get_name() == indvar) {
116✔
359
                continue;
63✔
360
            }
361

362
            if (std::find(substitions.begin(), substitions.end(), sym->get_name()) !=
53✔
363
                substitions.end()) {
53✔
364
                continue;
32✔
365
            }
366
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
21✔
367
                parameters_.insert(sym->get_name());
8✔
368
            } else {
8✔
369
                dimensions_.insert(sym->get_name());
13✔
370
            }
371
        }
116✔
372
    }
373
    for (auto& dim : subset2_new) {
161✔
374
        for (auto& atom : symbolic::atoms(dim)) {
201✔
375
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
117✔
376
            if (sym->get_name() == indvar) {
117✔
377
                continue;
63✔
378
            }
379

380
            if (std::find(substitions.begin(), substitions.end(), sym->get_name()) !=
54✔
381
                substitions.end()) {
54✔
382
                continue;
32✔
383
            }
384
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
22✔
385
                parameters_.insert(sym->get_name());
9✔
386
            } else {
9✔
387
                dimensions_.insert(sym->get_name());
13✔
388
            }
389
        }
117✔
390
    }
391
    dimensions_.insert(indvar);
77✔
392
    std::vector<std::string> dimensions;
77✔
393
    for (auto& dim : dimensions_) {
167✔
394
        dimensions.push_back(dim);
90✔
395
    }
396
    for (auto mv : moving_symbols) {
372✔
397
        if (dimensions_.find(mv) == dimensions_.end()) {
295✔
398
            dimensions.push_back(mv);
205✔
399
            dimensions_.insert(mv);
205✔
400
        }
205✔
401
    }
295✔
402
    std::vector<std::string> parameters;
77✔
403
    for (auto& dim : parameters_) {
86✔
404
        parameters.push_back(dim);
9✔
405
    }
406

407
    // Double dimension space and constraints
408
    size_t k = 0;
77✔
409
    std::vector<std::string> doubled_dimensions;
77✔
410
    std::vector<std::string> constraints;
77✔
411
    for (auto& dim : dimensions) {
372✔
412
        doubled_dimensions.push_back(dim + "_1");
295✔
413
        doubled_dimensions.push_back(dim + "_2");
295✔
414

415
        // Proof: dim_1 != dim_2
416
        if (dim == indvar) {
295✔
417
            constraints.push_back(dim + "_1 != " + dim + "_2");
77✔
418
        }
77✔
419

420
        // Collect lb and ub
421
        auto lb1 = assumptions.at(symbolic::symbol(dim)).lower_bound();
295✔
422
        for (auto atom : symbolic::atoms(lb1)) {
295✔
UNCOV
423
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
×
UNCOV
424
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
×
UNCOV
425
                if (parameters_.find(sym->get_name()) == parameters_.end()) {
×
UNCOV
426
                    parameters_.insert(sym->get_name());
×
UNCOV
427
                    parameters.push_back(sym->get_name());
×
UNCOV
428
                }
×
UNCOV
429
            }
×
UNCOV
430
        }
×
431
        auto ub1 = assumptions.at(symbolic::symbol(dim)).upper_bound();
295✔
432
        for (auto atom : symbolic::atoms(ub1)) {
372✔
433
            auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
77✔
434
            if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
77✔
435
                if (parameters_.find(sym->get_name()) == parameters_.end()) {
77✔
436
                    parameters_.insert(sym->get_name());
75✔
437
                    parameters.push_back(sym->get_name());
75✔
438
                }
75✔
439
            }
77✔
440
        }
77✔
441

442
        // Add constraints
443
        auto lb2 = lb1;
295✔
444
        auto ub2 = ub1;
295✔
445
        for (auto& dim : dimensions) {
2,080✔
446
            lb1 = symbolic::subs(lb1, symbolic::symbol(dim), symbolic::symbol(dim + "_1"));
1,785✔
447
            ub1 = symbolic::subs(ub1, symbolic::symbol(dim), symbolic::symbol(dim + "_1"));
1,785✔
448
            lb2 = symbolic::subs(lb2, symbolic::symbol(dim), symbolic::symbol(dim + "_2"));
1,785✔
449
            ub2 = symbolic::subs(ub2, symbolic::symbol(dim), symbolic::symbol(dim + "_2"));
1,785✔
450
        }
451

452
        if (!SymEngine::eq(*lb1, *symbolic::infty(-1))) {
295✔
453
            auto mins = SymEngine::atoms<const SymEngine::Min>(*lb1);
295✔
454
            if (mins.size() > 0) {
295✔
455
                continue;
×
456
            }
457

458
            if (SymEngine::is_a<SymEngine::Max>(*lb1)) {
295✔
459
                auto max = SymEngine::rcp_static_cast<const SymEngine::Max>(lb1);
×
460
                auto args1 = max->get_args();
×
461
                constraints.push_back(language_extension.expression(args1[0]) + " <= " + dim +
×
462
                                      "_1");
463
                constraints.push_back(language_extension.expression(args1[1]) + " <= " + dim +
×
464
                                      "_1");
465

466
                auto max_ = SymEngine::rcp_static_cast<const SymEngine::Max>(lb2);
×
467
                auto args2 = max_->get_args();
×
468
                constraints.push_back(language_extension.expression(args2[0]) + " <= " + dim +
×
469
                                      "_2");
470
                constraints.push_back(language_extension.expression(args2[1]) + " <= " + dim +
×
471
                                      "_2");
472
            } else {
×
473
                constraints.push_back(language_extension.expression(lb1) + " <= " + dim + "_1");
295✔
474
                constraints.push_back(language_extension.expression(lb2) + " <= " + dim + "_2");
295✔
475
            }
476
        }
295✔
477
        if (!SymEngine::eq(*ub1, *symbolic::infty(1))) {
295✔
478
            auto maxs = SymEngine::atoms<const SymEngine::Max>(*ub1);
280✔
479
            if (maxs.size() > 0) {
280✔
480
                continue;
×
481
            }
482

483
            if (SymEngine::is_a<SymEngine::Min>(*ub1)) {
280✔
UNCOV
484
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub1);
×
UNCOV
485
                auto args1 = min->get_args();
×
UNCOV
486
                constraints.push_back(dim + "_1 <= " + language_extension.expression(args1[0]));
×
UNCOV
487
                constraints.push_back(dim + "_1 <= " + language_extension.expression(args1[1]));
×
488

UNCOV
489
                auto min_ = SymEngine::rcp_static_cast<const SymEngine::Min>(ub2);
×
UNCOV
490
                auto args2 = min_->get_args();
×
UNCOV
491
                constraints.push_back(dim + "_2 <= " + language_extension.expression(args2[0]));
×
UNCOV
492
                constraints.push_back(dim + "_2 <= " + language_extension.expression(args2[1]));
×
UNCOV
493
            } else {
×
494
                constraints.push_back(dim + "_1 <= " + language_extension.expression(ub1));
280✔
495
                constraints.push_back(dim + "_2 <= " + language_extension.expression(ub2));
280✔
496
            }
497
        }
280✔
498

499
        // Add map constraints
500
        symbolic::Expression map = SymEngine::null;
295✔
501
        if (map == SymEngine::null) {
295✔
502
            continue;
295✔
503
        }
504
        if (!SymEngine::is_a<SymEngine::Add>(*map)) {
×
505
            continue;
×
506
        }
507
        auto args = SymEngine::rcp_static_cast<const SymEngine::Add>(map)->get_args();
×
508
        if (args.size() != 2) {
×
509
            continue;
×
510
        }
511
        auto arg0 = args[0];
×
512
        auto arg1 = args[1];
×
513
        if (!symbolic::eq(arg0, symbolic::symbol(dim))) {
×
514
            arg0 = args[1];
×
515
            arg1 = args[0];
×
516
        }
×
517
        if (!symbolic::eq(arg0, symbolic::symbol(dim))) {
×
518
            continue;
×
519
        }
520
        if (!SymEngine::is_a<SymEngine::Integer>(*arg1)) {
×
521
            continue;
×
522
        }
523
        if (!SymEngine::is_a<SymEngine::Integer>(*lb1)) {
×
524
            continue;
×
525
        }
526

527
        std::string new_k = "__daisy_iterator" + std::to_string(k++);
×
528

529
        std::string k_1 = new_k + "_1";
×
530
        constraints.push_back("exists " + k_1 + " : " + dim +
×
531
                              "_1 = " + language_extension.expression(lb1) + " + " + k_1 + " * " +
×
532
                              language_extension.expression(arg1));
×
533

534
        std::string k_2 = new_k + "_2";
×
535
        constraints.push_back("exists " + k_2 + " : " + dim +
×
536
                              "_2 = " + language_extension.expression(lb1) + " + " + k_2 + " * " +
×
537
                              language_extension.expression(arg1));
×
538
    }
295✔
539

540
    // Extend parameters by dependening parameters
541
    size_t num_params = parameters.size();
77✔
542
    size_t num_params_old = 0;
77✔
543
    do {
77✔
544
        for (size_t i = 0; i < num_params; i++) {
161✔
545
            auto param = parameters[i];
84✔
546
            auto lb = assumptions.at(symbolic::symbol(param)).lower_bound();
84✔
547
            for (auto& atom : symbolic::atoms(lb)) {
84✔
UNCOV
548
                auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
×
UNCOV
549
                if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
×
UNCOV
550
                    if (parameters_.find(sym->get_name()) == parameters_.end()) {
×
UNCOV
551
                        parameters_.insert(sym->get_name());
×
UNCOV
552
                        parameters.push_back(sym->get_name());
×
UNCOV
553
                    }
×
UNCOV
554
                }
×
UNCOV
555
            }
×
556
            auto ub = assumptions.at(symbolic::symbol(param)).upper_bound();
84✔
557
            for (auto& atom : symbolic::atoms(ub)) {
89✔
558
                auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
5✔
559
                if (moving_symbols.find(sym->get_name()) == moving_symbols.end()) {
5✔
560
                    if (parameters_.find(sym->get_name()) == parameters_.end()) {
5✔
UNCOV
561
                        parameters_.insert(sym->get_name());
×
UNCOV
562
                        parameters.push_back(sym->get_name());
×
UNCOV
563
                    }
×
564
                }
5✔
565
            }
5✔
566
        }
84✔
567
        num_params_old = num_params;
77✔
568
        num_params = parameters.size();
77✔
569
    } while (num_params != num_params_old);
77✔
570

571
    // Collect constraints for parameters
572
    for (size_t i = 0; i < parameters.size(); i++) {
161✔
573
        auto lb = assumptions.at(symbolic::symbol(parameters[i])).lower_bound();
84✔
574
        auto ub = assumptions.at(symbolic::symbol(parameters[i])).upper_bound();
84✔
575

576
        std::string constraint = "";
84✔
577
        if (!SymEngine::eq(*lb, *symbolic::infty(-1))) {
84✔
578
            if (SymEngine::is_a<SymEngine::Max>(*lb)) {
84✔
579
                auto max = SymEngine::rcp_static_cast<const SymEngine::Max>(lb);
×
580
                auto args = max->get_args();
×
581
                constraints.push_back(language_extension.expression(args[0]) +
×
582
                                      " <= " + parameters[i]);
×
583
                constraints.push_back(language_extension.expression(args[1]) +
×
584
                                      " <= " + parameters[i]);
×
585
            } else if (SymEngine::atoms<const SymEngine::Min>(*lb).size() > 0) {
84✔
586
            } else {
×
587
                constraints.push_back(language_extension.expression(lb) + " <= " + parameters[i]);
84✔
588
            }
589
        }
84✔
590
        if (!SymEngine::eq(*ub, *symbolic::infty(1))) {
84✔
591
            if (SymEngine::is_a<SymEngine::Min>(*ub)) {
7✔
UNCOV
592
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub);
×
UNCOV
593
                auto args = min->get_args();
×
UNCOV
594
                constraints.push_back(parameters[i] +
×
UNCOV
595
                                      " <= " + language_extension.expression(args[0]));
×
UNCOV
596
                constraints.push_back(parameters[i] +
×
UNCOV
597
                                      " <= " + language_extension.expression(args[1]));
×
598
            } else if (SymEngine::atoms<const SymEngine::Max>(*ub).size() > 0) {
7✔
599
            } else {
×
600
                constraints.push_back(parameters[i] + " <= " + language_extension.expression(ub));
7✔
601
            }
602
        }
7✔
603
    }
84✔
604

605
    // Allocate context
606
    isl_ctx* ctx = isl_ctx_alloc();
77✔
607

608
    // Define maps
609
    std::string map_1;
77✔
610
    if (!parameters.empty()) {
77✔
611
        map_1 += "[";
77✔
612
        map_1 += helpers::join(parameters, ", ");
77✔
613
    }
77✔
614
    if (!substitions.empty()) {
77✔
615
        if (!parameters.empty()) {
32✔
616
            map_1 += ", ";
32✔
617
        } else {
32✔
618
            map_1 += "[";
×
619
        }
620
        map_1 += helpers::join(substitions, ", ");
32✔
621
    }
32✔
622
    if (!map_1.empty()) {
77✔
623
        map_1 += "] -> ";
77✔
624
    }
77✔
625
    map_1 += "{ [" + helpers::join(doubled_dimensions, ", ") + "] -> [";
77✔
626
    for (size_t i = 0; i < subset1_new.size(); i++) {
161✔
627
        auto dim = subset1_new[i];
84✔
628
        for (auto& iter : dimensions) {
390✔
629
            dim = symbolic::subs(dim, symbolic::symbol(iter), symbolic::symbol(iter + "_1"));
306✔
630
        }
631
        map_1 += language_extension.expression(dim);
84✔
632
        if (i < subset1_new.size() - 1) {
84✔
633
            map_1 += ", ";
8✔
634
        }
8✔
635
    }
84✔
636
    map_1 += "] : " + helpers::join(constraints, " and ") + " }";
77✔
637

638
    std::string map_2;
77✔
639
    if (!parameters.empty()) {
77✔
640
        map_2 += "[";
77✔
641
        map_2 += helpers::join(parameters, ", ");
77✔
642
    }
77✔
643
    if (!substitions.empty()) {
77✔
644
        if (!parameters.empty()) {
32✔
645
            map_2 += ", ";
32✔
646
        } else {
32✔
647
            map_2 += "[";
×
648
        }
649
        map_2 += helpers::join(substitions, ", ");
32✔
650
    }
32✔
651
    if (!map_2.empty()) {
77✔
652
        map_2 += "] -> ";
77✔
653
    }
77✔
654
    map_2 += "{ [" + helpers::join(doubled_dimensions, ", ") + "] -> [";
77✔
655
    for (size_t i = 0; i < subset2_new.size(); i++) {
161✔
656
        auto dim = subset2_new[i];
84✔
657
        for (auto& iter : dimensions) {
390✔
658
            dim = symbolic::subs(dim, symbolic::symbol(iter), symbolic::symbol(iter + "_2"));
306✔
659
        }
660
        map_2 += language_extension.expression(dim);
84✔
661
        if (i < subset2_new.size() - 1) {
84✔
662
            map_2 += ", ";
8✔
663
        }
8✔
664
    }
84✔
665
    map_2 += "] : " + helpers::join(constraints, " and ") + " }";
77✔
666

667
    // Replace NV symbols with names without .
668
    map_1 = std::regex_replace(map_1, std::regex("\\."), "_");
77✔
669
    map_2 = std::regex_replace(map_2, std::regex("\\."), "_");
77✔
670

671
    isl_map* index_map_1 = isl_map_read_from_str(ctx, map_1.c_str());
77✔
672
    if (!index_map_1) {
77✔
673
        isl_ctx_free(ctx);
×
674
        return false;
×
675
    }
676

677
    isl_map* index_map_2 = isl_map_read_from_str(ctx, map_2.c_str());
77✔
678
    if (!index_map_2) {
77✔
679
        isl_map_free(index_map_1);
×
680
        isl_ctx_free(ctx);
×
681
        return false;
×
682
    }
683

684
    isl_map* intersection = isl_map_intersect(index_map_1, index_map_2);
77✔
685
    if (!intersection) {
77✔
686
        isl_map_free(index_map_1);
×
687
        isl_map_free(index_map_2);
×
688
        isl_ctx_free(ctx);
×
689
        return false;
×
690
    }
691

692
    bool disjoint = isl_map_is_empty(intersection);
77✔
693

694
    isl_map_free(intersection);
77✔
695
    isl_ctx_free(ctx);
77✔
696

697
    return disjoint;
77✔
698
};
77✔
699

700
void DataParallelismAnalysis::classify(analysis::AnalysisManager& analysis_manager,
35✔
701
                                       structured_control_flow::StructuredLoop* loop) {
702
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
35✔
703

704
    // Strictly monotonic update
705
    auto& indvar = loop->indvar();
35✔
706
    if (!LoopAnalysis::is_monotonic(loop, assumptions_analysis)) {
35✔
707
        this->results_.insert({loop, DataParallelismAnalysisResult()});
×
708
        return;
×
709
    }
710

711
    // Users analysis
712
    auto& body = loop->root();
35✔
713
    auto& users = analysis_manager.get<analysis::Users>();
35✔
714
    analysis::UsersView body_users(users, body);
35✔
715
    if (!body_users.views().empty() || !body_users.moves().empty()) {
35✔
716
        this->results_.insert({loop, DataParallelismAnalysisResult()});
×
717
        return;
×
718
    }
719

720
    this->results_.insert({loop, DataParallelismAnalysisResult()});
35✔
721
    auto& result = this->results_.at(loop);
35✔
722

723
    // Assumptions analysis
724
    auto assumptions = assumptions_analysis.get(body, true);
35✔
725

726
    // For each container, we now classify the access pattern
727

728
    // 1. Identify private containers
729
    auto locals = users.locals(body);
35✔
730
    for (auto& local : locals) {
73✔
731
        result.insert({local, Parallelism::PRIVATE});
38✔
732
    }
733

734
    // 2. Filter our read-only containers
735
    std::unordered_set<std::string> writeset;
35✔
736
    std::unordered_set<std::string> moving_symbols = {indvar->get_name()};
35✔
737
    for (auto& entry : body_users.writes()) {
135✔
738
        writeset.insert(entry->container());
100✔
739

740
        auto& type = sdfg_.type(entry->container());
100✔
741
        if (!dynamic_cast<const types::Scalar*>(&type)) {
100✔
742
            continue;
47✔
743
        }
744
        if (!types::is_integer(type.primitive_type())) {
53✔
745
            continue;
21✔
746
        }
747
        moving_symbols.insert(entry->container());
32✔
748
    }
749
    for (auto& entry : body_users.reads()) {
445✔
750
        if (writeset.find(entry->container()) != writeset.end()) {
410✔
751
            // Loop-carried dependencies in tasklets's conditions
752
            if (dynamic_cast<data_flow::Tasklet*>(entry->element())) {
141✔
753
                // Locals cannot be loop-carried
754
                if (locals.find(entry->container()) != locals.end()) {
6✔
755
                    continue;
5✔
756
                } else {
757
                    result.clear();
1✔
758
                    return;
1✔
759
                }
760
            }
761

762
            continue;
135✔
763
        }
764
        if (entry->container() == indvar->get_name()) {
269✔
765
            continue;
108✔
766
        }
767
        result.insert({entry->container(), Parallelism::READONLY});
161✔
768
    }
769

770
    // 3. Prove parallelism
771
    /** For each container of the writeset, we check the following properties:
772
     *  1. For all i, writes are disjoint (false -> empty parallelism)
773
     *  2. For all i, reads and writes are disjoint (false -> empty parallelism)
774
     */
775
    for (auto& container : writeset) {
106✔
776
        // Skip if already classified
777
        if (result.find(container) != result.end()) {
72✔
778
            continue;
29✔
779
        }
780

781
        // For each i1, i2, subset(i1) and subset(i2) are disjoint
782
        bool ww_conflict = false;
43✔
783
        auto writes = body_users.writes(container);
43✔
784
        for (auto& write : writes) {
74✔
785
            for (auto& write_ : writes) {
83✔
786
                if (write == write_ && writes.size() > 1) {
52✔
787
                    continue;
4✔
788
                }
789

790
                // Determine moving symbols locally
791
                std::unordered_set<std::string> moving_symbols_local = moving_symbols;
48✔
792

793
                auto subsets = write->subsets();
48✔
794
                auto subsets_ = write_->subsets();
48✔
795
                for (auto& subset : subsets) {
82✔
796
                    for (auto& subset_ : subsets_) {
82✔
797
                        if (!this->disjoint(subset, subset_, indvar->get_name(),
48✔
798
                                            moving_symbols_local, assumptions)) {
799
                            ww_conflict = true;
14✔
800
                            break;
14✔
801
                        }
802
                    }
803
                    if (ww_conflict) {
48✔
804
                        break;
14✔
805
                    }
806
                }
807
                if (ww_conflict) {
48✔
808
                    break;
14✔
809
                }
810
            }
48✔
811
            if (ww_conflict) {
45✔
812
                break;
14✔
813
            }
814
        }
815
        if (ww_conflict) {
43✔
816
            result.insert({container, Parallelism::DEPENDENT});
14✔
817
            continue;
14✔
818
        }
819

820
        bool rw_conflict = false;
29✔
821
        auto reads = body_users.reads(container);
29✔
822
        for (auto& read : reads) {
47✔
823
            for (auto& write_ : writes) {
43✔
824
                // Determine moving symbols locally
825
                std::unordered_set<std::string> moving_symbols_local = moving_symbols;
25✔
826

827
                auto subsets = read->subsets();
25✔
828
                auto subsets_ = write_->subsets();
25✔
829
                for (auto& subset : subsets) {
51✔
830
                    for (auto& subset_ : subsets_) {
55✔
831
                        if (!this->disjoint(subset, subset_, indvar->get_name(),
29✔
832
                                            moving_symbols_local, assumptions)) {
833
                            rw_conflict = true;
3✔
834
                            break;
3✔
835
                        }
836
                    }
837
                    if (rw_conflict) {
29✔
838
                        break;
3✔
839
                    }
840
                }
841
                if (rw_conflict) {
25✔
842
                    break;
3✔
843
                }
844
            }
25✔
845
            if (rw_conflict) {
21✔
846
                break;
3✔
847
            }
848
        }
849
        if (rw_conflict) {
29✔
850
            result.insert({container, Parallelism::DEPENDENT});
3✔
851
            continue;
3✔
852
        }
853

854
        result.insert({container, Parallelism::PARALLEL});
26✔
855
    }
43✔
856

857
    // 4. Reductions
858
    for (auto& entry : result) {
166✔
859
        auto& container = entry.first;
132✔
860
        auto& dep_type = entry.second;
132✔
861
        if (dep_type != Parallelism::DEPENDENT) {
132✔
862
            continue;
115✔
863
        }
864

865
        // Check if it is a reduction
866
        auto reads = body_users.reads(container);
17✔
867
        auto writes = body_users.writes(container);
17✔
868

869
        // Criterion: Must write to constant location
870
        bool is_reduction = true;
17✔
871
        auto first_write = writes.at(0);
17✔
872
        auto first_subset = first_write->subsets().at(0);
17✔
873
        for (auto& dim : first_subset) {
21✔
874
            for (auto& sym : moving_symbols) {
26✔
875
                if (symbolic::uses(dim, sym)) {
22✔
876
                    is_reduction = false;
12✔
877
                    break;
12✔
878
                }
879
            }
880
            if (!is_reduction) {
16✔
881
                break;
12✔
882
            }
883
        }
884
        if (!is_reduction) {
17✔
885
            continue;
12✔
886
        }
887

888
        // Criterion: All writes must have the same subset
889
        for (auto& write : writes) {
10✔
890
            for (auto& subset : write->subsets()) {
10✔
891
                if (subset.size() != first_subset.size()) {
5✔
892
                    is_reduction = false;
×
893
                    break;
×
894
                }
895

896
                for (size_t i = 0; i < subset.size(); i++) {
9✔
897
                    if (!symbolic::eq(subset[i], first_subset[i])) {
4✔
898
                        is_reduction = false;
×
899
                        break;
×
900
                    }
901
                }
4✔
902
            }
903
        }
904
        if (!is_reduction) {
5✔
905
            continue;
×
906
        }
907

908
        // Criterion: All reads must have the same subset
909
        for (auto& read : reads) {
8✔
910
            for (auto& subset : read->subsets()) {
6✔
911
                if (subset.size() != first_subset.size()) {
3✔
912
                    is_reduction = false;
×
913
                    break;
×
914
                }
915

916
                for (size_t i = 0; i < subset.size(); i++) {
6✔
917
                    if (!symbolic::eq(subset[i], first_subset[i])) {
3✔
918
                        is_reduction = false;
×
919
                        break;
×
920
                    }
921
                }
3✔
922
            }
923
        }
924

925
        if (is_reduction) {
5✔
926
            result[container] = Parallelism::REDUCTION;
5✔
927
        }
5✔
928
    }
17✔
929
};
35✔
930

931
void DataParallelismAnalysis::run(analysis::AnalysisManager& analysis_manager) {
27✔
932
    this->results_.clear();
27✔
933

934
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
27✔
935
    for (auto& loop : loop_analysis.loops()) {
62✔
936
        if (auto sloop = dynamic_cast<structured_control_flow::StructuredLoop*>(loop)) {
35✔
937
            this->classify(analysis_manager, sloop);
35✔
938
        }
35✔
939
    }
940
};
27✔
941

942
DataParallelismAnalysis::DataParallelismAnalysis(StructuredSDFG& sdfg)
54✔
943
    : Analysis(sdfg) {
27✔
944

945
      };
27✔
946

947
const DataParallelismAnalysisResult& DataParallelismAnalysis::get(
33✔
948
    const structured_control_flow::StructuredLoop& loop) const {
949
    return this->results_.at(&loop);
33✔
950
};
951

952
symbolic::Expression DataParallelismAnalysis::bound(
8✔
953
    const structured_control_flow::StructuredLoop& loop) {
954
    auto& indvar = loop.indvar();
8✔
955
    auto& condition = loop.condition();
8✔
956
    auto args = condition->get_args();
8✔
957
    if (args.size() != 2) {
8✔
958
        return SymEngine::null;
×
959
    }
960
    auto& arg0 = args[0];
8✔
961
    auto& arg1 = args[1];
8✔
962
    if (SymEngine::eq(*arg0, *indvar)) {
8✔
963
        return arg1;
8✔
964
    } else if (SymEngine::eq(*arg1, *indvar)) {
×
965
        return arg0;
×
966
    }
967
    return SymEngine::null;
×
968
};
8✔
969

970
}  // namespace analysis
971
}  // 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