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

daisytuner / sdfglib / 16069945621

04 Jul 2025 08:56AM UTC coverage: 64.375% (-0.2%) from 64.606%
16069945621

push

github

web-flow
Merge pull request #137 from daisytuner/clang-format

runs clang-format on codebase

609 of 827 new or added lines in 63 files covered. (73.64%)

46 existing lines in 30 files now uncovered.

8578 of 13325 relevant lines covered (64.38%)

177.24 hits per line

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

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

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

80
    return {subset1_new, subset2_new};
78✔
81
};
78✔
82

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

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

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

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

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

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

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

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

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

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

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

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

283
    return {subset1_new, subset2_new};
80✔
284
};
80✔
285

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

297
    codegen::CPPLanguageExtension language_extension;
77✔
298

299
    // Attempt to substitute complex constant expressions by parameters
300
    symbolic::ExpressionMap replacements;
77✔
301
    std::vector<std::string> substitions;
77✔
302
    auto [subset1_, subset2_] =
77✔
303
        DataParallelismAnalysis::substitution(subset1, subset2, indvar, moving_symbols, replacements, substitions);
77✔
304

305
    // Attempt to delinearize subsets
306
    auto [subset1_2, subset2_2] =
231✔
307
        DataParallelismAnalysis::delinearization(subset1_, subset2_, moving_symbols, assumptions);
77✔
308

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

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

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

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

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

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

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

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

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

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

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

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

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

522
        std::string new_k = "__daisy_iterator" + std::to_string(k++);
×
523

524
        std::string k_1 = new_k + "_1";
×
NEW
525
        constraints.push_back(
×
NEW
526
            "exists " + k_1 + " : " + dim + "_1 = " + language_extension.expression(lb1) + " + " + k_1 + " * " +
×
NEW
527
            language_extension.expression(arg1)
×
528
        );
529

530
        std::string k_2 = new_k + "_2";
×
NEW
531
        constraints.push_back(
×
NEW
532
            "exists " + k_2 + " : " + dim + "_2 = " + language_extension.expression(lb1) + " + " + k_2 + " * " +
×
NEW
533
            language_extension.expression(arg1)
×
534
        );
535
    }
295✔
536

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

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

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

598
    // Allocate context
599
    isl_ctx* ctx = isl_ctx_alloc();
77✔
600

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

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

660
    // Replace NV symbols with names without .
661
    map_1 = std::regex_replace(map_1, std::regex("\\."), "_");
77✔
662
    map_2 = std::regex_replace(map_2, std::regex("\\."), "_");
77✔
663

664
    isl_map* index_map_1 = isl_map_read_from_str(ctx, map_1.c_str());
77✔
665
    if (!index_map_1) {
77✔
666
        isl_ctx_free(ctx);
×
667
        return false;
×
668
    }
669

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

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

685
    bool disjoint = isl_map_is_empty(intersection);
77✔
686

687
    isl_map_free(intersection);
77✔
688
    isl_ctx_free(ctx);
77✔
689

690
    return disjoint;
77✔
691
};
77✔
692

693
void DataParallelismAnalysis::
694
    classify(analysis::AnalysisManager& analysis_manager, structured_control_flow::StructuredLoop* loop) {
35✔
695
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
35✔
696

697
    // Strictly monotonic update
698
    auto& indvar = loop->indvar();
35✔
699
    if (!LoopAnalysis::is_monotonic(loop, assumptions_analysis)) {
35✔
700
        this->results_.insert({loop, DataParallelismAnalysisResult()});
×
701
        return;
×
702
    }
703

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

713
    this->results_.insert({loop, DataParallelismAnalysisResult()});
35✔
714
    auto& result = this->results_.at(loop);
35✔
715

716
    // Assumptions analysis
717
    auto assumptions = assumptions_analysis.get(body, true);
35✔
718

719
    // For each container, we now classify the access pattern
720

721
    // 1. Identify private containers
722
    auto locals = users.locals(body);
35✔
723
    for (auto& local : locals) {
73✔
724
        result.insert({local, Parallelism::PRIVATE});
38✔
725
    }
726

727
    // 2. Filter our read-only containers
728
    std::unordered_set<std::string> writeset;
35✔
729
    std::unordered_set<std::string> moving_symbols = {indvar->get_name()};
35✔
730
    for (auto& entry : body_users.writes()) {
135✔
731
        writeset.insert(entry->container());
100✔
732

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

755
            continue;
135✔
756
        }
757
        if (entry->container() == indvar->get_name()) {
268✔
758
            continue;
107✔
759
        }
760
        result.insert({entry->container(), Parallelism::READONLY});
161✔
761
    }
762

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

774
        // For each i1, i2, subset(i1) and subset(i2) are disjoint
775
        bool ww_conflict = false;
43✔
776
        auto writes = body_users.writes(container);
43✔
777
        for (auto& write : writes) {
74✔
778
            for (auto& write_ : writes) {
83✔
779
                if (write == write_ && writes.size() > 1) {
52✔
780
                    continue;
4✔
781
                }
782

783
                // Determine moving symbols locally
784
                std::unordered_set<std::string> moving_symbols_local = moving_symbols;
48✔
785

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

812
        bool rw_conflict = false;
29✔
813
        auto reads = body_users.reads(container);
29✔
814
        for (auto& read : reads) {
47✔
815
            for (auto& write_ : writes) {
43✔
816
                // Determine moving symbols locally
817
                std::unordered_set<std::string> moving_symbols_local = moving_symbols;
25✔
818

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

845
        result.insert({container, Parallelism::PARALLEL});
26✔
846
    }
43✔
847

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

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

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

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

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

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

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

916
        if (is_reduction) {
5✔
917
            result[container] = Parallelism::REDUCTION;
5✔
918
        }
5✔
919
    }
17✔
920
};
35✔
921

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

925
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
27✔
926
    for (auto& loop : loop_analysis.loops()) {
62✔
927
        if (auto sloop = dynamic_cast<structured_control_flow::StructuredLoop*>(loop)) {
35✔
928
            this->classify(analysis_manager, sloop);
35✔
929
        }
35✔
930
    }
931
};
27✔
932

933
DataParallelismAnalysis::DataParallelismAnalysis(StructuredSDFG& sdfg)
54✔
934
    : Analysis(sdfg) {
27✔
935

936
      };
27✔
937

938
const DataParallelismAnalysisResult& DataParallelismAnalysis::get(const structured_control_flow::StructuredLoop& loop
33✔
939
) const {
940
    return this->results_.at(&loop);
33✔
941
};
942

943
symbolic::Expression DataParallelismAnalysis::bound(const structured_control_flow::StructuredLoop& loop) {
8✔
944
    auto& indvar = loop.indvar();
8✔
945
    auto& condition = loop.condition();
8✔
946
    auto args = condition->get_args();
8✔
947
    if (args.size() != 2) {
8✔
948
        return SymEngine::null;
×
949
    }
950
    auto& arg0 = args[0];
8✔
951
    auto& arg1 = args[1];
8✔
952
    if (SymEngine::eq(*arg0, *indvar)) {
8✔
953
        return arg1;
8✔
954
    } else if (SymEngine::eq(*arg1, *indvar)) {
×
955
        return arg0;
×
956
    }
957
    return SymEngine::null;
×
958
};
8✔
959

960
} // namespace analysis
961
} // 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

© 2025 Coveralls, Inc