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

daisytuner / sdfglib / 21098439361

17 Jan 2026 05:47PM UTC coverage: 64.664% (-0.06%) from 64.728%
21098439361

Pull #459

github

web-flow
Merge 2723168ea into b09ddf4fd
Pull Request #459: Adds OMPScheduler and PollyScheduler

220 of 375 new or added lines in 13 files covered. (58.67%)

44 existing lines in 2 files now uncovered.

19129 of 29582 relevant lines covered (64.66%)

394.2 hits per line

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

72.59
/opt/src/analysis/scop_analysis.cpp
1
//===- ScopBuilder.cpp --------------------------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===--- DependenceInfo.cpp - Polyhedral dependency analysis *- C++ -*-===//
8
//
9
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
10
// See https://llvm.org/LICENSE.txt for license information.
11
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
12
//
13
//===----------------------------------------------------------------------===//
14
//
15
// This file is based on the original source files which were modified for sdfglib.
16
//
17
//===----------------------------------------------------------------------===//
18
#include "sdfg/analysis/scop_analysis.h"
19

20
#include <isl/constraint.h>
21
#include <isl/flow.h>
22
#include <isl/id.h>
23
#include <isl/local_space.h>
24
#include <isl/options.h>
25
#include <isl/val.h>
26

27
#include <sdfg/analysis/loop_analysis.h>
28
#include <sdfg/data_flow/library_nodes/math/math.h>
29
#include <sdfg/symbolic/utils.h>
30

31
namespace sdfg {
32
namespace analysis {
33

34

35
MemoryAccess::
36
    MemoryAccess(AccessType access_type, isl_map *relation, const std::string &data, const data_flow::Memlet *memlet)
37
    : access_type_(access_type), relation_(relation), data_(data), memlet_(memlet) {}
109✔
38

39
ScopStatement::ScopStatement(const std::string &name, isl_set *domain, data_flow::CodeNode *code_node)
40
    : name_(name), code_node_(code_node), expression_(SymEngine::null) {
51✔
41
    domain_ = isl_set_set_tuple_name(domain, name.c_str());
51✔
42
    isl_space *space = isl_set_get_space(domain_);
51✔
43
    schedule_ = isl_map_identity(isl_space_map_from_set(space));
51✔
44
}
51✔
45

46
ScopStatement::ScopStatement(const std::string &name, isl_set *domain, symbolic::Expression expression)
47
    : name_(name), code_node_(nullptr), expression_(expression) {
2✔
48
    domain_ = isl_set_set_tuple_name(domain, name.c_str());
2✔
49
    isl_space *space = isl_set_get_space(domain_);
2✔
50
    schedule_ = isl_map_identity(isl_space_map_from_set(space));
2✔
51
}
2✔
52

53
Scop::Scop(structured_control_flow::ControlFlowNode &node, isl_ctx *ctx, isl_space *param_space)
54
    : node_(node), ctx_(ctx), param_space_(param_space), schedule_tree_(nullptr), schedule_(nullptr) {}
43✔
55

56
isl_union_set *Scop::domains() {
44✔
57
    isl_space *empty_space = isl_space_params_alloc(this->ctx_, 0);
44✔
58
    isl_union_set *domain = isl_union_set_empty(empty_space);
44✔
59

60
    for (auto &stmt : this->statements_) {
54✔
61
        domain = isl_union_set_add_set(domain, isl_set_copy(stmt->domain()));
54✔
62
    }
54✔
63

64
    return domain;
44✔
65
}
44✔
66

67
isl_union_map *Scop::schedule() { return this->schedule_; }
4✔
68

69
isl_schedule *Scop::schedule_tree() {
52✔
70
    if (this->schedule_tree_) {
52✔
71
        return this->schedule_tree_;
13✔
72
    }
13✔
73

74
    // Construct default schedule from statements
75
    isl_space *empty_space = isl_space_params_alloc(this->ctx_, 0);
39✔
76
    isl_union_map *sched_map = isl_union_map_empty(empty_space);
39✔
77
    for (auto &stmt : this->statements_) {
49✔
78
        sched_map = isl_union_map_add_map(sched_map, isl_map_copy(stmt->schedule()));
49✔
79
    }
49✔
80

81
    isl_schedule *sched = isl_schedule_from_domain(domains());
39✔
82
    if (!isl_union_map_is_empty(sched_map)) {
39✔
83
        // Ensure consistent dimensionality
84
        isl_map_list *map_list = isl_union_map_get_map_list(sched_map);
39✔
85
        int n_maps = isl_map_list_n_map(map_list);
39✔
86
        int max_dim = 0;
39✔
87

88
        for (int i = 0; i < n_maps; ++i) {
88✔
89
            isl_map *map = isl_map_list_get_map(map_list, i);
49✔
90
            int dim = isl_map_dim(map, isl_dim_out);
49✔
91
            if (dim > max_dim) {
49✔
92
                max_dim = dim;
41✔
93
            }
41✔
94
            isl_map_free(map);
49✔
95
        }
49✔
96

97
        isl_union_map *padded_map = isl_union_map_empty(isl_union_map_get_space(sched_map));
39✔
98
        for (int i = 0; i < n_maps; ++i) {
88✔
99
            isl_map *map = isl_map_list_get_map(map_list, i);
49✔
100
            int dim = isl_map_dim(map, isl_dim_out);
49✔
101
            if (dim < max_dim) {
49✔
102
                map = isl_map_add_dims(map, isl_dim_out, max_dim - dim);
6✔
103
                for (int j = dim; j < max_dim; ++j) {
12✔
104
                    map = isl_map_fix_si(map, isl_dim_out, j, 0);
6✔
105
                }
6✔
106
            }
6✔
107
            padded_map = isl_union_map_add_map(padded_map, map);
49✔
108
        }
49✔
109
        isl_map_list_free(map_list);
39✔
110
        isl_union_map_free(sched_map);
39✔
111
        sched_map = padded_map;
39✔
112

113
        isl_union_pw_multi_aff *upma = isl_union_pw_multi_aff_from_union_map(sched_map);
39✔
114
        isl_multi_union_pw_aff *mupa = isl_multi_union_pw_aff_from_union_pw_multi_aff(upma);
39✔
115
        sched = isl_schedule_insert_partial_schedule(sched, mupa);
39✔
116
    } else {
39✔
117
        isl_union_map_free(sched_map);
×
118
    }
×
119

120
    this->schedule_tree_ = sched;
39✔
121
    this->schedule_ = isl_schedule_get_map(this->schedule_tree_);
39✔
122

123
    return this->schedule_tree_;
39✔
124
}
52✔
125

126
void Scop::set_schedule_tree(isl_schedule *schedule) {
5✔
127
    if (this->schedule_tree_) {
5✔
128
        isl_schedule_free(this->schedule_tree_);
5✔
129
        isl_union_map_free(this->schedule_);
5✔
130
    }
5✔
131
    this->schedule_tree_ = schedule;
5✔
132
    this->schedule_ = isl_schedule_get_map(schedule);
5✔
133
}
5✔
134

135
std::string Scop::ast() {
11✔
136
    isl_schedule *schedule = isl_schedule_copy(this->schedule_tree());
11✔
137
    isl_ast_build *build = isl_ast_build_alloc(this->ctx_);
11✔
138
    isl_ast_node *tree = isl_ast_build_node_from_schedule(build, schedule);
11✔
139
    char *str = isl_ast_node_to_C_str(tree);
11✔
140
    std::string result = "";
11✔
141
    if (str) {
11✔
142
        result = std::string(str);
11✔
143
        free(str);
11✔
144
    }
11✔
145
    isl_ast_node_free(tree);
11✔
146
    isl_ast_build_free(build);
11✔
147
    return result;
11✔
148
}
11✔
149

150
ScopBuilder::ScopBuilder(StructuredSDFG &sdfg, structured_control_flow::ControlFlowNode &node)
151
    : sdfg_(sdfg), node_(node), scop_(nullptr) {};
43✔
152

153
std::unique_ptr<Scop> ScopBuilder::build(analysis::AnalysisManager &analysis_manager) {
43✔
154
    isl_ctx *ctx = isl_ctx_alloc();
43✔
155
    isl_options_set_on_error(ctx, ISL_ON_ERROR_CONTINUE);
43✔
156
    isl_space *param_space = isl_space_set_alloc(ctx, 0, 0);
43✔
157
    this->scop_ = std::make_unique<Scop>(this->node_, ctx, param_space);
43✔
158

159
    this->visit(analysis_manager, node_);
43✔
160
    if (this->scop_ == nullptr) {
43✔
161
        return nullptr;
×
162
    }
×
163

164
    return std::move(scop_);
43✔
165
}
43✔
166

167
void ScopBuilder::visit(analysis::AnalysisManager &analysis_manager, structured_control_flow::ControlFlowNode &node) {
112✔
168
    if (this->scop_ == nullptr) {
112✔
169
        return;
×
170
    }
×
171

172
    if (auto block = dynamic_cast<structured_control_flow::Block *>(&node)) {
112✔
173
        this->visit_block(analysis_manager, *block);
52✔
174
    } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence *>(&node)) {
60✔
175
        this->visit_sequence(analysis_manager, *sequence);
2✔
176
    } else if (auto loop = dynamic_cast<structured_control_flow::StructuredLoop *>(&node)) {
58✔
177
        this->visit_structured_loop(analysis_manager, *loop);
58✔
178
    } else {
58✔
179
        this->scop_ = nullptr;
×
180
        return;
×
181
    }
×
182
}
112✔
183

184
void ScopBuilder::visit_sequence(analysis::AnalysisManager &analysis_manager, structured_control_flow::Sequence &sequence) {
60✔
185
    for (size_t i = 0; i < sequence.size(); ++i) {
129✔
186
        this->visit(analysis_manager, sequence.at(i).first);
69✔
187
        if (this->scop_ == nullptr) {
69✔
188
            return;
×
189
        }
×
190

191
        auto &transition = sequence.at(i).second;
69✔
192
        size_t j = 0;
69✔
193
        for (const auto &assignment : transition.assignments()) {
69✔
194
            isl_set *domain = isl_set_universe(isl_space_set_alloc(scop_->ctx(), 0, 0));
2✔
195
            auto statement = std::make_unique<ScopStatement>(
2✔
196
                "S_" + std::to_string(transition.element_id()) + "_" + std::to_string(j), domain, assignment.second
2✔
197
            );
2✔
198
            j++;
2✔
199

200
            AccessType access_type = AccessType::WRITE;
2✔
201
            std::string data = assignment.first->get_name();
2✔
202
            std::string relation;
2✔
203
            if (!this->parameters_.empty()) {
2✔
204
                relation += "[";
×
205
                relation += helpers::join(this->parameters_, ", ");
×
206
                relation += "] -> ";
×
207
            }
×
208
            relation += "{ [] -> [0] }";
2✔
209
            isl_map *isl_relation = isl_map_read_from_str(scop_->ctx(), relation.c_str());
2✔
210
            if (!isl_relation) {
2✔
211
                this->scop_ = nullptr;
×
212
                return;
×
213
            }
×
214
            isl_relation = isl_map_set_tuple_name(isl_relation, isl_dim_in, statement->name().c_str());
2✔
215
            auto memory_access = std::make_unique<MemoryAccess>(access_type, isl_relation, data, nullptr);
2✔
216
            statement->insert(memory_access);
2✔
217

218
            for (auto &sym : symbolic::atoms(assignment.second)) {
2✔
219
                AccessType access_type = AccessType::READ;
2✔
220
                std::string data = sym->get_name();
2✔
221
                isl_map *isl_relation = isl_map_read_from_str(scop_->ctx(), relation.c_str());
2✔
222
                if (!isl_relation) {
2✔
223
                    this->scop_ = nullptr;
×
224
                    return;
×
225
                }
×
226
                isl_relation = isl_map_set_tuple_name(isl_relation, isl_dim_in, statement->name().c_str());
2✔
227
                auto memory_access = std::make_unique<MemoryAccess>(access_type, isl_relation, data, nullptr);
2✔
228
                statement->insert(memory_access);
2✔
229
            }
2✔
230

231
            this->scop_->insert(statement);
2✔
232
        }
2✔
233
    }
69✔
234
}
60✔
235

236
void ScopBuilder::visit_block(analysis::AnalysisManager &analysis_manager, structured_control_flow::Block &block) {
52✔
237
    auto &graph = block.dataflow();
52✔
238
    for (auto node : graph.topological_sort()) {
162✔
239
        if (auto code_node = dynamic_cast<data_flow::CodeNode *>(node)) {
162✔
240
            if (dynamic_cast<data_flow::LibraryNode *>(code_node)) {
51✔
241
                if (!dynamic_cast<math::cmath::CMathNode *>(code_node)) {
×
242
                    this->scop_ = nullptr;
×
243
                    return;
×
244
                }
×
245
            }
×
246

247
            isl_set *domain = isl_set_universe(isl_space_set_alloc(scop_->ctx(), 0, 0));
51✔
248
            auto statement =
51✔
249
                std::make_unique<ScopStatement>("S_" + std::to_string(code_node->element_id()), domain, code_node);
51✔
250

251
            // Add reads
252
            for (auto &iedge : graph.in_edges(*node)) {
61✔
253
                if (dynamic_cast<data_flow::ConstantNode *>(&iedge.src())) {
61✔
254
                    continue;
7✔
255
                }
7✔
256

257
                AccessType access_type = AccessType::READ;
54✔
258
                std::string relation = generate_subset(analysis_manager, block, iedge.subset());
54✔
259
                std::string data = static_cast<data_flow::AccessNode &>(iedge.src()).data();
54✔
260
                isl_map *isl_relation = isl_map_read_from_str(scop_->ctx(), relation.c_str());
54✔
261
                if (!isl_relation) {
54✔
262
                    this->scop_ = nullptr;
×
263
                    return;
×
264
                }
×
265
                isl_relation = isl_map_set_tuple_name(isl_relation, isl_dim_in, statement->name().c_str());
54✔
266
                auto memory_access = std::make_unique<MemoryAccess>(access_type, isl_relation, data, &iedge);
54✔
267
                statement->insert(memory_access);
54✔
268
            }
54✔
269

270
            // Add writes
271
            for (auto &oedge : graph.out_edges(*node)) {
51✔
272
                AccessType access_type = AccessType::WRITE;
51✔
273

274
                std::set<std::string> symbols;
51✔
275
                std::string relation = generate_subset(analysis_manager, block, oedge.subset());
51✔
276
                std::string data = static_cast<data_flow::AccessNode &>(oedge.dst()).data();
51✔
277
                isl_map *isl_relation = isl_map_read_from_str(scop_->ctx(), relation.c_str());
51✔
278
                if (!isl_relation) {
51✔
279
                    this->scop_ = nullptr;
×
280
                    return;
×
281
                }
×
282
                isl_relation = isl_map_set_tuple_name(isl_relation, isl_dim_in, statement->name().c_str());
51✔
283
                auto memory_access = std::make_unique<MemoryAccess>(access_type, isl_relation, data, &oedge);
51✔
284
                statement->insert(memory_access);
51✔
285
            }
51✔
286

287
            this->scop_->insert(statement);
51✔
288
        }
51✔
289
    }
162✔
290
}
52✔
291

292
void ScopBuilder::
293
    visit_structured_loop(analysis::AnalysisManager &analysis_manager, structured_control_flow::StructuredLoop &loop) {
58✔
294
    // Add domain for the statements inside the loop
295
    std::string indvar = loop.indvar()->__str__();
58✔
296
    this->dimensions_.push_back(indvar);
58✔
297

298
    std::string init = loop.init()->__str__();
58✔
299
    symbolic::CNF condition_cnf;
58✔
300
    try {
58✔
301
        condition_cnf = symbolic::conjunctive_normal_form(loop.condition());
58✔
302
    } catch (symbolic::CNFException &e) {
58✔
303
        this->scop_ = nullptr;
×
304
        return;
×
305
    }
×
306
    std::vector<std::string> constraints;
58✔
307
    constraints.push_back(indvar + " >= " + init);
58✔
308
    for (auto &clause : condition_cnf) {
59✔
309
        if (clause.size() != 1) {
59✔
310
            this->scop_ = nullptr;
×
311
            return;
×
312
        }
×
313
        for (auto &literal : clause) {
59✔
314
            for (auto &sym : symbolic::atoms(literal)) {
106✔
315
                if (sym->get_name() == indvar) {
106✔
316
                    continue;
59✔
317
                }
59✔
318
                if (this->dimension_constraints_.find(sym->get_name()) != this->dimension_constraints_.end()) {
47✔
319
                    continue;
5✔
320
                }
5✔
321
                if (std::find(this->parameters_.begin(), this->parameters_.end(), sym->get_name()) ==
42✔
322
                    this->parameters_.end()) {
42✔
323
                    this->parameters_.push_back(sym->get_name());
37✔
324
                }
37✔
325
            }
42✔
326

327
            std::string literal_str = symbolic::constraint_to_isl_str(literal);
59✔
328
            if (literal_str.empty()) {
59✔
329
                this->scop_ = nullptr;
×
330
                return;
×
331
            }
×
332
            constraints.push_back(literal_str);
59✔
333
        }
59✔
334
    }
59✔
335
    auto stride = analysis::LoopAnalysis::stride(&loop);
58✔
336
    if (stride.is_null()) {
58✔
337
        this->scop_ = nullptr;
×
338
        return;
×
339
    }
×
340
    int stride_value = stride->as_int();
58✔
341
    if (stride_value != 1) {
58✔
342
        std::string iter = "__daisy_iterator_" + indvar;
4✔
343
        std::string update_constraint = "exists " + iter + " : " + indvar + " = " + init + " + " + iter + " * " +
4✔
344
                                        std::to_string(stride_value);
4✔
345
        constraints.push_back(update_constraint);
4✔
346
    }
4✔
347
    std::string condition = helpers::join(constraints, " and ");
58✔
348
    dimension_constraints_[indvar] = condition;
58✔
349

350
    // Construct constraints string.
351
    std::string set_str;
58✔
352
    if (this->dimensions_.size() > 1 || !this->parameters_.empty()) {
58✔
353
        std::vector<std::string> params = this->parameters_;
47✔
354
        for (int i = 0; i < this->dimensions_.size() - 1; ++i) {
64✔
355
            params.push_back(this->dimensions_[i]);
17✔
356
        }
17✔
357
        set_str += "[ " + helpers::join(params, ", ") + " ] -> ";
47✔
358
    }
47✔
359
    set_str += "{ [" + indvar + "] : " + condition + " }";
58✔
360

361
    isl_ctx *ctx = scop_->ctx();
58✔
362
    isl_set *loop_bound_base = isl_set_read_from_str(ctx, set_str.c_str());
58✔
363
    if (!loop_bound_base) {
58✔
364
        this->scop_ = nullptr;
×
365
        return;
×
366
    }
×
367
    char *loop_bound_str = isl_set_to_str(loop_bound_base);
58✔
368
    free(loop_bound_str);
58✔
369

370
    size_t stmt_start = this->scop_->statements().size();
58✔
371
    this->visit_sequence(analysis_manager, loop.root());
58✔
372
    if (this->scop_ == nullptr) {
58✔
373
        return;
×
374
    }
×
375

376
    auto stmts = this->scop_->statements();
58✔
377
    for (size_t i = stmt_start; i < stmts.size(); ++i) {
127✔
378
        ScopStatement *stmt = stmts[i];
69✔
379
        stmt->push_front(loop.indvar());
69✔
380

381
        isl_set *domain = isl_set_copy(stmt->domain());
69✔
382

383
        // Insert indvar dimension at 0
384
        domain = isl_set_insert_dims(domain, isl_dim_set, 0, 1);
69✔
385
        domain = isl_set_set_dim_name(domain, isl_dim_set, 0, indvar.c_str());
69✔
386

387
        // Equate parameter with loop dimension if it exists and project it out
388
        int param_idx = isl_set_find_dim_by_name(domain, isl_dim_param, indvar.c_str());
69✔
389
        if (param_idx >= 0) {
69✔
390
            isl_space *space = isl_set_get_space(domain);
18✔
391
            isl_local_space *ls = isl_local_space_from_space(space);
18✔
392
            isl_constraint *c = isl_constraint_alloc_equality(ls);
18✔
393
            c = isl_constraint_set_coefficient_si(c, isl_dim_set, 0, 1);
18✔
394
            c = isl_constraint_set_coefficient_si(c, isl_dim_param, param_idx, -1);
18✔
395
            domain = isl_set_add_constraint(domain, c);
18✔
396
            domain = isl_set_project_out(domain, isl_dim_param, param_idx, 1);
18✔
397
        }
18✔
398

399
        // Prepare loop bound set with compatible dimensions
400
        isl_set *loop_bound = isl_set_copy(loop_bound_base);
69✔
401
        int n_dims = isl_set_dim(domain, isl_dim_set);
69✔
402
        int inner_dims = n_dims - 1;
69✔
403

404
        if (inner_dims > 0) {
69✔
405
            loop_bound = isl_set_add_dims(loop_bound, isl_dim_set, inner_dims);
18✔
406

407
            // Match names for inner dimensions
408
            for (int j = 0; j < inner_dims; ++j) {
36✔
409
                // The inner dims in domain are at indices 1 + j
410
                const char *name = isl_set_get_dim_name(domain, isl_dim_set, 1 + j);
18✔
411
                if (name) {
18✔
412
                    loop_bound = isl_set_set_dim_name(loop_bound, isl_dim_set, 1 + j, name);
18✔
413
                }
18✔
414
            }
18✔
415
        }
18✔
416

417
        domain = isl_set_intersect(domain, loop_bound);
69✔
418
        stmt->set_domain(domain);
69✔
419

420
        // Update Schedule
421
        isl_map *schedule = isl_map_copy(stmt->schedule());
69✔
422

423
        // Insert dimension in input (domain)
424
        schedule = isl_map_insert_dims(schedule, isl_dim_in, 0, 1);
69✔
425
        schedule = isl_map_set_dim_name(schedule, isl_dim_in, 0, indvar.c_str());
69✔
426

427
        // Equate parameter with loop dimension if it exists and project it out
428
        param_idx = isl_map_find_dim_by_name(schedule, isl_dim_param, indvar.c_str());
69✔
429
        if (param_idx >= 0) {
69✔
430
            isl_space *space = isl_map_get_space(schedule);
×
431
            isl_local_space *ls = isl_local_space_from_space(space);
×
432
            isl_constraint *c = isl_constraint_alloc_equality(ls);
×
433
            c = isl_constraint_set_coefficient_si(c, isl_dim_in, 0, 1);
×
434
            c = isl_constraint_set_coefficient_si(c, isl_dim_param, param_idx, -1);
×
435
            schedule = isl_map_add_constraint(schedule, c);
×
436
            schedule = isl_map_project_out(schedule, isl_dim_param, param_idx, 1);
×
437
        }
×
438

439
        // Insert dimension in output (range/time)
440
        schedule = isl_map_insert_dims(schedule, isl_dim_out, 0, 1);
69✔
441
        schedule = isl_map_set_dim_name(schedule, isl_dim_out, 0, indvar.c_str());
69✔
442

443
        // Equate input[0] and output[0]
444
        schedule = isl_map_equate(schedule, isl_dim_in, 0, isl_dim_out, 0);
69✔
445

446
        stmt->set_schedule(schedule);
69✔
447

448
        for (auto access : stmt->accesses()) {
145✔
449
            isl_map *relation = isl_map_copy(access->relation());
145✔
450

451
            int dom_n = isl_set_dim(stmt->domain(), isl_dim_set);
145✔
452
            int rel_n_in = isl_map_dim(relation, isl_dim_in);
145✔
453

454
            if (rel_n_in < dom_n) {
145✔
455
                relation = isl_map_insert_dims(relation, isl_dim_in, 0, 1);
2✔
456
                relation = isl_map_set_dim_name(relation, isl_dim_in, 0, indvar.c_str());
2✔
457

458
                // Equate parameter with loop dimension if it exists and project it out
459
                param_idx = isl_map_find_dim_by_name(relation, isl_dim_param, indvar.c_str());
2✔
460
                if (param_idx >= 0) {
2✔
461
                    isl_space *space = isl_map_get_space(relation);
×
462
                    isl_local_space *ls = isl_local_space_from_space(space);
×
463
                    isl_constraint *c = isl_constraint_alloc_equality(ls);
×
464
                    c = isl_constraint_set_coefficient_si(c, isl_dim_in, 0, 1);
×
465
                    c = isl_constraint_set_coefficient_si(c, isl_dim_param, param_idx, -1);
×
466
                    relation = isl_map_add_constraint(relation, c);
×
467
                    relation = isl_map_project_out(relation, isl_dim_param, param_idx, 1);
×
468
                }
×
469
            }
2✔
470

471
            relation = isl_map_set_tuple_name(relation, isl_dim_in, stmt->name().c_str());
145✔
472
            access->set_relation(relation);
145✔
473
        }
145✔
474
    }
69✔
475
    isl_set_free(loop_bound_base);
58✔
476

477
    this->dimensions_.pop_back();
58✔
478
}
58✔
479

480
std::string ScopBuilder::generate_subset(
481
    analysis::AnalysisManager &analysis_manager,
482
    structured_control_flow::ControlFlowNode &node,
483
    const symbolic::MultiExpression &subset
484
) {
105✔
485
    std::string relation = "";
105✔
486
    if (!this->parameters_.empty()) {
105✔
487
        relation += "[ " + helpers::join(this->parameters_, ", ") + " ] -> ";
82✔
488
    }
82✔
489
    relation += "{ [";
105✔
490
    relation += helpers::join(this->dimensions_, ", ");
105✔
491
    relation += "] -> [";
105✔
492
    if (subset.empty()) {
105✔
493
        relation += "0";
13✔
494
    } else {
92✔
495
        for (size_t i = 0; i < subset.size(); ++i) {
208✔
496
            if (i > 0) {
116✔
497
                relation += ", ";
24✔
498
            }
24✔
499
            relation += subset.at(i)->__str__();
116✔
500
        }
116✔
501
    }
92✔
502
    relation += "] }";
105✔
503

504
    return relation;
105✔
505
}
105✔
506

507
static isl_map *tag(isl_map *Relation, isl_id *TagId) {
176✔
508
    isl_space *Space = isl_map_get_space(Relation);
176✔
509
    Space = isl_space_drop_dims(Space, isl_dim_out, 0, isl_map_dim(Relation, isl_dim_out));
176✔
510
    Space = isl_space_set_tuple_id(Space, isl_dim_out, TagId);
176✔
511
    isl_multi_aff *Tag = isl_multi_aff_domain_map(Space);
176✔
512
    Relation = isl_map_preimage_domain_multi_aff(Relation, Tag);
176✔
513
    return Relation;
176✔
514
}
176✔
515

516
static void collectInfo(
517
    Scop &S,
518
    isl_union_map *&Read,
519
    isl_union_map *&MustWrite,
520
    isl_union_map *&ReductionTagMap,
521
    isl_union_set *&TaggedStmtDomain
522
) {
33✔
523
    isl_space *Space = isl_space_copy(S.param_space());
33✔
524
    Read = isl_union_map_empty(isl_space_copy(Space));
33✔
525
    MustWrite = isl_union_map_empty(isl_space_copy(Space));
33✔
526
    ReductionTagMap = isl_union_map_empty(isl_space_copy(Space));
33✔
527
    isl_union_map *StmtSchedule = isl_union_map_empty(isl_space_copy(Space));
33✔
528

529
    std::unordered_set<std::string> ReductionArrays;
33✔
530
    for (auto stmt : S.statements())
33✔
531
        for (MemoryAccess *MA : stmt->accesses())
42✔
532
            if (MA->is_reduction_like()) ReductionArrays.insert(MA->data());
88✔
533

534
    for (auto stmt : S.statements()) {
42✔
535
        for (MemoryAccess *MA : stmt->accesses()) {
88✔
536
            isl_set *domcp = isl_set_copy(stmt->domain());
88✔
537
            isl_map *accdom = isl_map_copy(MA->relation());
88✔
538
            accdom = isl_map_set_tuple_name(accdom, isl_dim_out, MA->data().c_str());
88✔
539

540
            accdom = isl_map_intersect_domain(accdom, domcp);
88✔
541

542

543
            if (ReductionArrays.count(MA->data())) {
88✔
544
                // Wrap the access domain and adjust the schedule accordingly.
545
                //
546
                // An access domain like
547
                //   Stmt[i0, i1] -> MemAcc_A[i0 + i1]
548
                // will be transformed into
549
                //   [Stmt[i0, i1] -> MemAcc_A[i0 + i1]] -> MemAcc_A[i0 + i1]
550
                //
551
                // We collect all the access domains in the ReductionTagMap.
552
                // This is used in Dependences::calculateDependences to create
553
                // a tagged Schedule tree.
554

555
                ReductionTagMap = isl_union_map_add_map(ReductionTagMap, isl_map_copy(accdom));
×
556
                accdom = isl_map_range_map(accdom);
×
557
            } else {
88✔
558
                isl_map *StmtScheduleMap = isl_map_copy(stmt->schedule());
88✔
559
                assert(
88✔
560
                    StmtScheduleMap &&
88✔
561
                    "Schedules that contain extension nodes require special "
88✔
562
                    "handling."
88✔
563
                );
88✔
564

565
                isl_id *id = isl_id_alloc(S.ctx(), MA->data().c_str(), MA);
88✔
566
                accdom = tag(accdom, isl_id_copy(id));
88✔
567
                isl_map *Schedule = tag(StmtScheduleMap, id);
88✔
568
                StmtSchedule = isl_union_map_add_map(StmtSchedule, Schedule);
88✔
569
            }
88✔
570

571
            if (MA->access_type() == AccessType::READ) {
88✔
572
                Read = isl_union_map_add_map(Read, accdom);
46✔
573
            } else {
46✔
574
                MustWrite = isl_union_map_add_map(MustWrite, accdom);
42✔
575
            }
42✔
576
        }
88✔
577
    }
42✔
578

579
    StmtSchedule = isl_union_map_intersect_params(StmtSchedule, isl_set_universe(isl_space_copy(Space)));
33✔
580
    TaggedStmtDomain = isl_union_map_domain(StmtSchedule);
33✔
581

582
    ReductionTagMap = isl_union_map_coalesce(ReductionTagMap);
33✔
583
    Read = isl_union_map_coalesce(Read);
33✔
584
    MustWrite = isl_union_map_coalesce(MustWrite);
33✔
585

586
    isl_space_free(Space);
33✔
587
}
33✔
588

589
static isl_union_flow *
590
buildFlow(isl_union_map *Snk, isl_union_map *Src, isl_union_map *MaySrc, isl_union_map *Kill, isl_schedule *Schedule) {
132✔
591
    isl_union_access_info *AI;
132✔
592

593
    AI = isl_union_access_info_from_sink(isl_union_map_copy(Snk));
132✔
594
    if (MaySrc) AI = isl_union_access_info_set_may_source(AI, isl_union_map_copy(MaySrc));
132✔
595
    if (Src) AI = isl_union_access_info_set_must_source(AI, isl_union_map_copy(Src));
132✔
596
    if (Kill) AI = isl_union_access_info_set_kill(AI, isl_union_map_copy(Kill));
132✔
597
    AI = isl_union_access_info_set_schedule(AI, isl_schedule_copy(Schedule));
132✔
598
    auto Flow = isl_union_access_info_compute_flow(AI);
132✔
599
    return Flow;
132✔
600
}
132✔
601

602
static isl_stat get_map_range_space(isl_map *Map, void *User) {
2✔
603
    isl_space **Space = (isl_space **) User;
2✔
604
    if (*Space) {
2✔
605
        isl_map_free(Map);
×
606
        return isl_stat_ok;
×
607
    }
×
608
    *Space = isl_space_range(isl_map_get_space(Map));
2✔
609
    isl_map_free(Map);
2✔
610
    return isl_stat_ok;
2✔
611
}
2✔
612

613
static isl_stat fix_set_to_zero(isl_set *Set, void *User) {
×
614
    isl_union_set **UserUS = (isl_union_set **) User;
×
615

616
    int dims = isl_set_dim(Set, isl_dim_set);
×
617
    for (int i = 0; i < dims; ++i) {
×
618
        Set = isl_set_fix_si(Set, isl_dim_set, i, 0);
×
619
    }
×
620

621
    *UserUS = isl_union_set_union(*UserUS, isl_union_set_from_set(Set));
×
622
    return isl_stat_ok;
×
623
}
×
624

625
isl_union_map *Dependences::dependences(int Kinds) const {
27✔
626
    assert(has_valid_dependences() && "No valid dependences available");
27✔
627
    isl_space *Space = isl_union_map_get_space(RAW);
27✔
628
    isl_union_map *Deps = isl_union_map_empty(Space);
27✔
629

630
    if (Kinds & TYPE_RAW) Deps = isl_union_map_union(Deps, isl_union_map_copy(RAW));
27✔
631

632
    if (Kinds & TYPE_WAR) Deps = isl_union_map_union(Deps, isl_union_map_copy(WAR));
27✔
633

634
    if (Kinds & TYPE_WAW) Deps = isl_union_map_union(Deps, isl_union_map_copy(WAW));
27✔
635

636
    if (Kinds & TYPE_RED) Deps = isl_union_map_union(Deps, isl_union_map_copy(RED));
27✔
637

638
    if (Kinds & TYPE_TC_RED) Deps = isl_union_map_union(Deps, isl_union_map_copy(TC_RED));
27✔
639

640
    Deps = isl_union_map_coalesce(Deps);
27✔
641
    Deps = isl_union_map_detect_equalities(Deps);
27✔
642
    return Deps;
27✔
643
}
27✔
644

645
struct DepCallbackInfo {
646
    std::unordered_map<std::string, analysis::LoopCarriedDependency> *deps;
647
    analysis::LoopCarriedDependency type;
648
    const sdfg::structured_control_flow::StructuredLoop *loop;
649
};
650

651
static isl_stat collect_deps(isl_map *bmap, void *user) {
156✔
652
    auto *info = static_cast<DepCallbackInfo *>(user);
156✔
653

654
    if (info->loop) {
156✔
655
        bool exists_for_dim = false;
156✔
656
        std::string indvar_name = info->loop->indvar()->get_name();
156✔
657

658
        isl_map *map_for_deltas = isl_map_copy(bmap);
156✔
659
        isl_space *map_space = isl_map_get_space(map_for_deltas);
156✔
660
        isl_space *dom_space = isl_space_domain(isl_space_copy(map_space));
156✔
661
        if (isl_space_is_wrapping(dom_space)) {
156✔
662
            map_for_deltas = isl_map_domain_factor_domain(map_for_deltas);
80✔
663
        }
80✔
664
        isl_space_free(dom_space);
156✔
665

666
        isl_space *ran_space = isl_space_range(map_space);
156✔
667
        if (isl_space_is_wrapping(ran_space)) {
156✔
668
            map_for_deltas = isl_map_range_factor_domain(map_for_deltas);
80✔
669
        }
80✔
670
        isl_space_free(ran_space);
156✔
671

672
        isl_space *map_space_final = isl_map_get_space(map_for_deltas);
156✔
673
        isl_space *domain_space = isl_space_domain(isl_space_copy(map_space_final));
156✔
674
        isl_space *range_space = isl_space_range(isl_space_copy(map_space_final));
156✔
675
        isl_space_free(map_space_final);
156✔
676

677
        if (!isl_space_is_equal(domain_space, range_space)) {
156✔
678
            isl_space_free(domain_space);
78✔
679
            isl_space_free(range_space);
78✔
680
            isl_map_free(map_for_deltas);
78✔
681
            isl_map_free(bmap);
78✔
682
            return isl_stat_ok;
78✔
683
        }
78✔
684
        isl_space_free(domain_space);
78✔
685
        isl_space_free(range_space);
78✔
686

687
        isl_set *deltas = isl_map_deltas(map_for_deltas);
78✔
688
        int dims = isl_set_dim(deltas, isl_dim_set);
78✔
689
        int dim_idx = -1;
78✔
690

691
        for (int i = 0; i < dims; ++i) {
107✔
692
            const char *name = isl_set_get_dim_name(deltas, isl_dim_set, i);
103✔
693
            if (name && indvar_name == name) {
103✔
694
                dim_idx = i;
74✔
695
                break;
74✔
696
            }
74✔
697
        }
103✔
698

699
        if (dim_idx != -1) {
78✔
700
            // Check if there are any dependencies carried by this dimension.
701
            // This means we check if there exists a dependence vector d such that:
702
            // d[0...dim_idx-1] = 0 AND d[dim_idx] != 0
703

704
            for (int i = 0; i < dim_idx; ++i) {
99✔
705
                deltas = isl_set_fix_si(deltas, isl_dim_set, i, 0);
25✔
706
            }
25✔
707

708
            isl_set *zero_at_dim = isl_set_universe(isl_set_get_space(deltas));
74✔
709
            zero_at_dim = isl_set_fix_si(zero_at_dim, isl_dim_set, dim_idx, 0);
74✔
710

711
            if (!isl_set_is_subset(deltas, zero_at_dim)) {
74✔
712
                exists_for_dim = true;
62✔
713
            }
62✔
714
            isl_set_free(zero_at_dim);
74✔
715
        }
74✔
716
        isl_set_free(deltas);
78✔
717

718
        if (!exists_for_dim) {
78✔
719
            isl_map_free(bmap);
16✔
720
            return isl_stat_ok;
16✔
721
        }
16✔
722
    }
78✔
723

724
    isl_space *space = isl_map_get_space(bmap);
62✔
725
    isl_space *domain_space = isl_space_domain(space);
62✔
726

727
    if (isl_space_is_wrapping(domain_space)) {
62✔
728
        isl_space *domain_space_copy = isl_space_copy(domain_space);
31✔
729
        isl_space *unwrapped = isl_space_unwrap(domain_space_copy);
31✔
730
        isl_space *range = isl_space_range(unwrapped);
31✔
731

732
        if (isl_space_has_tuple_name(range, isl_dim_set)) {
31✔
733
            const char *name = isl_space_get_tuple_name(range, isl_dim_set);
31✔
734
            if (name) {
31✔
735
                std::string sname(name);
31✔
736
                auto &map = *info->deps;
31✔
737
                if (map.find(sname) == map.end()) {
31✔
738
                    map[sname] = info->type;
13✔
739
                } else {
18✔
740
                    if (info->type == analysis::LoopCarriedDependency::LOOP_CARRIED_DEPENDENCY_READ_WRITE) {
18✔
741
                        map[sname] = info->type;
14✔
742
                    }
14✔
743
                }
18✔
744
            }
31✔
745
        }
31✔
746
        isl_space_free(range);
31✔
747
    }
31✔
748

749
    isl_space_free(domain_space);
62✔
750
    isl_map_free(bmap);
62✔
751
    return isl_stat_ok;
62✔
752
}
156✔
753

754
std::unordered_map<std::string, analysis::LoopCarriedDependency> Dependences::
755
    dependencies(const sdfg::structured_control_flow::StructuredLoop &loop) const {
30✔
756
    std::unordered_map<std::string, analysis::LoopCarriedDependency> deps;
30✔
757
    DepCallbackInfo info;
30✔
758
    info.deps = &deps;
30✔
759
    info.loop = &loop;
30✔
760

761
    if (WAW) {
30✔
762
        info.type = analysis::LoopCarriedDependency::LOOP_CARRIED_DEPENDENCY_WRITE_WRITE;
30✔
763
        isl_union_map_foreach_map(WAW, collect_deps, &info);
30✔
764
    }
30✔
765

766
    if (RAW) {
30✔
767
        info.type = analysis::LoopCarriedDependency::LOOP_CARRIED_DEPENDENCY_READ_WRITE;
30✔
768
        isl_union_map_foreach_map(RAW, collect_deps, &info);
30✔
769
    }
30✔
770

771
    if (WAR) {
30✔
772
        info.type = analysis::LoopCarriedDependency::LOOP_CARRIED_DEPENDENCY_READ_WRITE;
30✔
773
        isl_union_map_foreach_map(WAR, collect_deps, &info);
30✔
774
    }
30✔
775

776
    return deps;
30✔
777
}
30✔
778

779
bool Dependences::has_valid_dependences() const { return (RAW != nullptr) && (WAR != nullptr) && (WAW != nullptr); }
32✔
780

781
isl_map *Dependences::reduction_dependences(MemoryAccess *MA) const { return reduction_dependences_.at(MA); }
×
782

783
void Dependences::set_reduction_dependences(MemoryAccess *memory_access, isl_map *deps) {
×
784
    reduction_dependences_[memory_access] = deps;
×
785
}
×
786

787
void Dependences::calculate_dependences(Scop &S) {
33✔
788
    isl_union_map *Read, *MustWrite, *ReductionTagMap;
33✔
789
    isl_union_set *TaggedStmtDomain;
33✔
790
    collectInfo(S, Read, MustWrite, ReductionTagMap, TaggedStmtDomain);
33✔
791

792
    isl_schedule *Schedule = isl_schedule_copy(S.schedule_tree());
33✔
793

794
    bool has_reductions = !isl_union_map_is_empty(ReductionTagMap);
33✔
795
    if (!has_reductions) {
33✔
796
        isl_union_map_free(ReductionTagMap);
33✔
797
        // Tag the schedule tree if we want fine-grain dependence info
798
        auto TaggedMap = isl_union_set_unwrap(isl_union_set_copy(TaggedStmtDomain));
33✔
799
        auto Tags = isl_union_map_domain_map_union_pw_multi_aff(TaggedMap);
33✔
800
        Schedule = isl_schedule_pullback_union_pw_multi_aff(Schedule, Tags);
33✔
801
    } else {
33✔
802
        // Extract Reduction tags from the combined access domains in the given
803
        // SCoP. The result is a map that maps each tagged element in the domain to
804
        // the memory location it accesses. ReductionTags = {[Stmt[i] ->
805
        // Array[f(i)]] -> Stmt[i] }
806
        isl_union_pw_multi_aff *ReductionTags = isl_union_map_domain_map_union_pw_multi_aff(ReductionTagMap);
×
807

808
        // Compute an identity map from each statement in domain to itself.
809
        // IdentityTags = { [Stmt[i] -> Stmt[i] }
810
        isl_union_map *IdentityMap = isl_union_set_identity(isl_union_set_copy(TaggedStmtDomain));
×
811
        isl_union_pw_multi_aff *IdentityTags = isl_union_pw_multi_aff_from_union_map(IdentityMap);
×
812

813
        isl_union_pw_multi_aff *Tags = isl_union_pw_multi_aff_union_add(ReductionTags, IdentityTags);
×
814

815
        // By pulling back Tags from Schedule, we have a schedule tree that can
816
        // be used to compute normal dependences, as well as 'tagged' reduction
817
        // dependences.
818
        Schedule = isl_schedule_pullback_union_pw_multi_aff(Schedule, Tags);
×
819
    }
×
820

821
    isl_union_map *StrictWAW = nullptr;
33✔
822
    {
33✔
823
        RAW = WAW = WAR = RED = nullptr;
33✔
824
        isl_union_map *Write = isl_union_map_copy(MustWrite);
33✔
825

826
        // We are interested in detecting reductions that do not have intermediate
827
        // computations that are captured by other statements.
828
        //
829
        // Example:
830
        // void f(int *A, int *B) {
831
        //     for(int i = 0; i <= 100; i++) {
832
        //
833
        //            *-WAR (S0[i] -> S0[i + 1] 0 <= i <= 100)------------*
834
        //            |                                                   |
835
        //            *-WAW (S0[i] -> S0[i + 1] 0 <= i <= 100)------------*
836
        //            |                                                   |
837
        //            v                                                   |
838
        //     S0:    *A += i; >------------------*-----------------------*
839
        //                                        |
840
        //         if (i >= 98) {          WAR (S0[i] -> S1[i]) 98 <= i <= 100
841
        //                                        |
842
        //     S1:        *B = *A; <--------------*
843
        //         }
844
        //     }
845
        // }
846
        //
847
        // S0[0 <= i <= 100] has a reduction. However, the values in
848
        // S0[98 <= i <= 100] is captured in S1[98 <= i <= 100].
849
        // Since we allow free reordering on our reduction dependences, we need to
850
        // remove all instances of a reduction statement that have data dependences
851
        // originating from them.
852
        // In the case of the example, we need to remove S0[98 <= i <= 100] from
853
        // our reduction dependences.
854
        //
855
        // When we build up the WAW dependences that are used to detect reductions,
856
        // we consider only **Writes that have no intermediate Reads**.
857
        //
858
        // `isl_union_flow_get_must_dependence` gives us dependences of the form:
859
        // (sink <- must_source).
860
        //
861
        // It *will not give* dependences of the form:
862
        // 1. (sink <- ... <- may_source <- ... <- must_source)
863
        // 2. (sink <- ... <- must_source <- ... <- must_source)
864
        //
865
        // For a detailed reference on ISL's flow analysis, see:
866
        // "Presburger Formulas and Polyhedral Compilation" - Approximate Dataflow
867
        //  Analysis.
868
        //
869
        // Since we set "Write" as a must-source, "Read" as a may-source, and ask
870
        // for must dependences, we get all Writes to Writes that **do not flow
871
        // through a Read**.
872
        //
873
        // ScopInfo::checkForReductions makes sure that if something captures
874
        // the reduction variable in the same basic block, then it is rejected
875
        // before it is even handed here. This makes sure that there is exactly
876
        // one read and one write to a reduction variable in a Statement.
877
        // Example:
878
        //     void f(int *sum, int A[N], int B[N]) {
879
        //       for (int i = 0; i < N; i++) {
880
        //         *sum += A[i]; < the store and the load is not tagged as a
881
        //         B[i] = *sum;  < reduction-like access due to the overlap.
882
        //       }
883
        //     }
884

885
        isl_union_flow *Flow = buildFlow(Write, Write, Read, nullptr, Schedule);
33✔
886
        StrictWAW = isl_union_flow_get_must_dependence(Flow);
33✔
887
        isl_union_flow_free(Flow);
33✔
888

889
        Flow = buildFlow(Read, nullptr, Write, nullptr, Schedule);
33✔
890
        RAW = isl_union_flow_get_may_dependence(Flow);
33✔
891
        isl_union_flow_free(Flow);
33✔
892

893
        Flow = buildFlow(Write, nullptr, Read, nullptr, Schedule);
33✔
894
        WAR = isl_union_flow_get_may_dependence(Flow);
33✔
895
        isl_union_flow_free(Flow);
33✔
896

897
        Flow = buildFlow(Write, nullptr, Write, nullptr, Schedule);
33✔
898
        WAW = isl_union_flow_get_may_dependence(Flow);
33✔
899
        isl_union_flow_free(Flow);
33✔
900

901
        isl_union_map_free(Write);
33✔
902
        isl_union_map_free(MustWrite);
33✔
903
        isl_union_map_free(Read);
33✔
904
        isl_schedule_free(Schedule);
33✔
905

906
        RAW = isl_union_map_coalesce(RAW);
33✔
907
        WAW = isl_union_map_coalesce(WAW);
33✔
908
        WAR = isl_union_map_coalesce(WAR);
33✔
909

910
        // End of max_operations scope.
911
    }
33✔
912

913
    isl_union_map *STMT_RAW =
33✔
914
        isl_union_map_intersect_domain(isl_union_map_copy(RAW), isl_union_set_copy(TaggedStmtDomain));
33✔
915
    isl_union_map *STMT_WAW =
33✔
916
        isl_union_map_intersect_domain(isl_union_map_copy(WAW), isl_union_set_copy(TaggedStmtDomain));
33✔
917
    isl_union_map *STMT_WAR =
33✔
918
        isl_union_map_intersect_domain(isl_union_map_copy(WAR), isl_union_set_copy(TaggedStmtDomain));
33✔
919
    isl_union_set_free(TaggedStmtDomain);
33✔
920

921
    // To handle reduction dependences we proceed as follows:
922
    // 1) Aggregate all possible reduction dependences, namely all self
923
    //    dependences on reduction like statements.
924
    // 2) Intersect them with the actual RAW & WAW dependences to the get the
925
    //    actual reduction dependences. This will ensure the load/store memory
926
    //    addresses were __identical__ in the two iterations of the statement.
927
    // 3) Relax the original RAW, WAW and WAR dependences by subtracting the
928
    //    actual reduction dependences. Binary reductions (sum += A[i]) cause
929
    //    the same, RAW, WAW and WAR dependences.
930
    // 4) Add the privatization dependences which are widened versions of
931
    //    already present dependences. They model the effect of manual
932
    //    privatization at the outermost possible place (namely after the last
933
    //    write and before the first access to a reduction location).
934

935
    // Step 1)
936
    RED = isl_union_map_empty(isl_union_map_get_space(RAW));
33✔
937
    for (auto stmt : S.statements()) {
42✔
938
        for (MemoryAccess *MA : stmt->accesses()) {
88✔
939
            if (!MA->is_reduction_like()) {
88✔
940
                continue;
88✔
941
            }
88✔
942
            isl_set *AccDomW = isl_map_wrap(isl_map_copy(MA->relation()));
×
943
            isl_map *Identity = isl_map_from_domain_and_range(isl_set_copy(AccDomW), AccDomW);
×
944
            RED = isl_union_map_add_map(RED, Identity);
×
945
        }
×
946
    }
42✔
947

948
    // Step 2)
949
    RED = isl_union_map_intersect(RED, isl_union_map_copy(RAW));
33✔
950
    if (isl_union_map_is_empty(RED)) {
33✔
951
        isl_union_map_free(StrictWAW);
33✔
952
    } else {
33✔
953
        RED = isl_union_map_intersect(RED, StrictWAW);
×
954
    }
×
955

956
    if (!isl_union_map_is_empty(RED)) {
33✔
957
        // Step 3)
958
        RAW = isl_union_map_subtract(RAW, isl_union_map_copy(RED));
×
959
        WAW = isl_union_map_subtract(WAW, isl_union_map_copy(RED));
×
960
        WAR = isl_union_map_subtract(WAR, isl_union_map_copy(RED));
×
961

962
        // Step 4)
963
        add_privatization_dependences();
×
964
    } else {
33✔
965
        TC_RED = isl_union_map_empty(isl_union_map_get_space(RED));
33✔
966
    }
33✔
967

968
    // RED_SIN is used to collect all reduction dependences again after we
969
    // split them according to the causing memory accesses. The current assumption
970
    // is that our method of splitting will not have any leftovers. In the end
971
    // we validate this assumption until we have more confidence in this method.
972
    isl_union_map *RED_SIN = isl_union_map_empty(isl_union_map_get_space(RAW));
33✔
973

974
    // For each reduction like memory access, check if there are reduction
975
    // dependences with the access relation of the memory access as a domain
976
    // (wrapped space!). If so these dependences are caused by this memory access.
977
    // We then move this portion of reduction dependences back to the statement ->
978
    // statement space and add a mapping from the memory access to these
979
    // dependences.
980
    for (auto stmt : S.statements()) {
42✔
981
        for (MemoryAccess *MA : stmt->accesses()) {
88✔
982
            if (!MA->is_reduction_like()) {
88✔
983
                continue;
88✔
984
            }
88✔
985

986
            isl_set *AccDomW = isl_map_wrap(isl_map_copy(MA->relation()));
×
987
            isl_union_map *AccRedDepU =
×
988
                isl_union_map_intersect_domain(isl_union_map_copy(TC_RED), isl_union_set_from_set(AccDomW));
×
989
            if (isl_union_map_is_empty(AccRedDepU)) {
×
990
                isl_union_map_free(AccRedDepU);
×
991
                continue;
×
992
            }
×
993

994
            isl_map *AccRedDep = isl_map_from_union_map(AccRedDepU);
×
995
            RED_SIN = isl_union_map_add_map(RED_SIN, isl_map_copy(AccRedDep));
×
996
            AccRedDep = isl_map_zip(AccRedDep);
×
997
            AccRedDep = isl_set_unwrap(isl_map_domain(AccRedDep));
×
998
            set_reduction_dependences(MA, AccRedDep);
×
999
        }
×
1000
    }
42✔
1001

1002
    assert(
33✔
1003
        isl_union_map_is_equal(RED_SIN, TC_RED) &&
33✔
1004
        "Intersecting the reduction dependence domain with the wrapped access "
33✔
1005
        "relation is not enough, we need to loosen the access relation also"
33✔
1006
    );
33✔
1007
    isl_union_map_free(RED_SIN);
33✔
1008

1009
    RAW = isl_union_map_zip(RAW);
33✔
1010
    WAW = isl_union_map_zip(WAW);
33✔
1011
    WAR = isl_union_map_zip(WAR);
33✔
1012
    RED = isl_union_map_zip(RED);
33✔
1013
    TC_RED = isl_union_map_zip(TC_RED);
33✔
1014

1015
    RAW = isl_union_set_unwrap(isl_union_map_domain(RAW));
33✔
1016
    WAW = isl_union_set_unwrap(isl_union_map_domain(WAW));
33✔
1017
    WAR = isl_union_set_unwrap(isl_union_map_domain(WAR));
33✔
1018
    RED = isl_union_set_unwrap(isl_union_map_domain(RED));
33✔
1019
    TC_RED = isl_union_set_unwrap(isl_union_map_domain(TC_RED));
33✔
1020

1021
    RAW = isl_union_map_union(RAW, STMT_RAW);
33✔
1022
    WAW = isl_union_map_union(WAW, STMT_WAW);
33✔
1023
    WAR = isl_union_map_union(WAR, STMT_WAR);
33✔
1024

1025
    RAW = isl_union_map_coalesce(RAW);
33✔
1026
    WAW = isl_union_map_coalesce(WAW);
33✔
1027
    WAR = isl_union_map_coalesce(WAR);
33✔
1028
    RED = isl_union_map_coalesce(RED);
33✔
1029
    TC_RED = isl_union_map_coalesce(TC_RED);
33✔
1030
}
33✔
1031

1032
void Dependences::add_privatization_dependences() {
×
1033
    isl_union_map *PrivRAW, *PrivWAW, *PrivWAR;
×
1034

1035
    // The transitive closure might be over approximated, thus could lead to
1036
    // dependency cycles in the privatization dependences. To make sure this
1037
    // will not happen we remove all negative dependences after we computed
1038
    // the transitive closure.
1039
    TC_RED = isl_union_map_transitive_closure(isl_union_map_copy(RED), nullptr);
×
1040

1041
    // FIXME: Apply the current schedule instead of assuming the identity schedule
1042
    //        here. The current approach is only valid as long as we compute the
1043
    //        dependences only with the initial (identity schedule). Any other
1044
    //        schedule could change "the direction of the backward dependences" we
1045
    //        want to eliminate here.
1046
    isl_union_set *UDeltas = isl_union_map_deltas(isl_union_map_copy(TC_RED));
×
1047
    isl_union_set *Universe = isl_union_set_universe(isl_union_set_copy(UDeltas));
×
1048
    isl_union_set *Zero = isl_union_set_empty(isl_union_set_get_space(Universe));
×
1049

1050
    isl_union_set_foreach_set(Universe, fix_set_to_zero, &Zero);
×
1051
    isl_union_set_free(Universe);
×
1052

1053
    isl_union_map *NonPositive = isl_union_set_lex_le_union_set(UDeltas, Zero);
×
1054

1055
    TC_RED = isl_union_map_subtract(TC_RED, NonPositive);
×
1056

1057
    TC_RED = isl_union_map_union(TC_RED, isl_union_map_reverse(isl_union_map_copy(TC_RED)));
×
1058
    TC_RED = isl_union_map_coalesce(TC_RED);
×
1059

1060
    isl_union_map **Maps[] = {&RAW, &WAW, &WAR};
×
1061
    isl_union_map **PrivMaps[] = {&PrivRAW, &PrivWAW, &PrivWAR};
×
1062
    for (unsigned u = 0; u < 3; u++) {
×
1063
        isl_union_map **Map = Maps[u], **PrivMap = PrivMaps[u];
×
1064

1065
        *PrivMap = isl_union_map_apply_range(isl_union_map_copy(*Map), isl_union_map_copy(TC_RED));
×
1066
        *PrivMap =
×
1067
            isl_union_map_union(*PrivMap, isl_union_map_apply_range(isl_union_map_copy(TC_RED), isl_union_map_copy(*Map)));
×
1068

1069
        *Map = isl_union_map_union(*Map, *PrivMap);
×
1070
    }
×
1071
}
×
1072

1073
bool Dependences::is_parallel(isl_union_map *schedule, isl_pw_aff **min_distance_ptr) const {
12✔
1074
    isl_union_map *deps = this->dependences(TYPE_RAW | TYPE_WAR | TYPE_WAW);
12✔
1075
    deps = isl_union_map_apply_range(deps, isl_union_map_copy(schedule));
12✔
1076
    deps = isl_union_map_apply_domain(deps, isl_union_map_copy(schedule));
12✔
1077
    if (isl_union_map_is_empty(deps)) {
12✔
1078
        isl_union_map_free(deps);
9✔
1079
        return true;
9✔
1080
    }
9✔
1081

1082
    isl_map *schedule_deps = isl_map_from_union_map(deps);
3✔
1083
    unsigned Dimension = isl_map_dim(schedule_deps, isl_dim_out) - 1;
3✔
1084

1085
    for (unsigned i = 0; i < Dimension; i++)
4✔
1086
        schedule_deps = isl_map_equate(schedule_deps, isl_dim_out, i, isl_dim_in, i);
1✔
1087

1088
    isl_set *Deltas = isl_map_deltas(schedule_deps);
3✔
1089
    isl_set *Distance = isl_set_universe(isl_set_get_space(Deltas));
3✔
1090

1091
    // [0, ..., 0, +] - All zeros and last dimension larger than zero
1092
    for (unsigned i = 0; i < Dimension; i++) Distance = isl_set_fix_si(Distance, isl_dim_set, i, 0);
4✔
1093

1094
    Distance = isl_set_lower_bound_si(Distance, isl_dim_set, Dimension, 1);
3✔
1095
    Distance = isl_set_intersect(Distance, Deltas);
3✔
1096

1097
    bool IsParallel = isl_set_is_empty(Distance);
3✔
1098
    if (IsParallel || !min_distance_ptr) {
3✔
1099
        isl_set_free(Distance);
3✔
1100
        return IsParallel;
3✔
1101
    }
3✔
1102

1103
    Distance = isl_set_project_out(Distance, isl_dim_set, 0, Dimension);
×
1104
    Distance = isl_set_coalesce(Distance);
×
1105

1106
    // This last step will compute a expression for the minimal value in the
1107
    // distance polyhedron Distance with regards to the first (outer most)
1108
    // dimension.
1109
    *min_distance_ptr = isl_pw_aff_coalesce(isl_set_dim_min(Distance, 0));
×
1110

1111
    return false;
×
1112
}
3✔
1113

1114
static bool check_validity(const Dependences *DepsObj, Scop &scop, isl_union_map *Schedule) {
2✔
1115
    isl_union_map *Deps = DepsObj->dependences(Dependences::TYPE_RAW | Dependences::TYPE_WAW | Dependences::TYPE_WAR);
2✔
1116

1117
    Deps = isl_union_map_apply_domain(Deps, isl_union_map_copy(Schedule));
2✔
1118
    Deps = isl_union_map_apply_range(Deps, isl_union_map_copy(Schedule));
2✔
1119

1120
    isl_space *ScheduleSpace = NULL;
2✔
1121
    isl_union_map_foreach_map(Schedule, get_map_range_space, &ScheduleSpace);
2✔
1122
    isl_union_map_free(Schedule);
2✔
1123

1124
    if (!ScheduleSpace) {
2✔
1125
        isl_union_map_free(Deps);
×
1126
        return true; /* Empty schedule considered valid/trivial */
×
1127
    }
×
1128

1129
    isl_set *Zero = isl_set_universe(isl_space_copy(ScheduleSpace));
2✔
1130
    int dims = isl_set_dim(Zero, isl_dim_set);
2✔
1131
    for (int i = 0; i < dims; ++i) Zero = isl_set_fix_si(Zero, isl_dim_set, i, 0);
4✔
1132

1133
    isl_union_set *UDeltas = isl_union_map_deltas(Deps);
2✔
1134
    isl_set *Deltas = isl_union_set_extract_set(UDeltas, ScheduleSpace);
2✔
1135
    isl_union_set_free(UDeltas);
2✔
1136

1137
    isl_space *Space = isl_set_get_space(Deltas);
2✔
1138
    isl_space *MapSpace = isl_space_map_from_set(isl_space_copy(Space));
2✔
1139
    isl_map *NonPositive = isl_map_universe(MapSpace);
2✔
1140

1141
    isl_multi_pw_aff *Identity = isl_multi_pw_aff_identity_on_domain_space(Space);
2✔
1142
    NonPositive = isl_map_lex_le_at_multi_pw_aff(NonPositive, Identity);
2✔
1143

1144
    NonPositive = isl_map_intersect_domain(NonPositive, Deltas);
2✔
1145
    NonPositive = isl_map_intersect_range(NonPositive, Zero);
2✔
1146

1147
    bool IsEmpty = isl_map_is_empty(NonPositive);
2✔
1148

1149
    isl_map_free(NonPositive);
2✔
1150
    return IsEmpty;
2✔
1151
}
2✔
1152

1153
bool Dependences::is_valid(Scop &scop, const std::unordered_map<ScopStatement *, isl_map *> &new_schedule) const {
1✔
1154
    isl_space *SpaceParams = isl_space_params_alloc(scop.ctx(), 0);
1✔
1155
    isl_union_map *Schedule = isl_union_map_empty(SpaceParams);
1✔
1156

1157
    for (ScopStatement *Stmt : scop.statements()) {
1✔
1158
        isl_map *StmtScat;
1✔
1159

1160
        auto Lookup = new_schedule.find(Stmt);
1✔
1161
        if (Lookup == new_schedule.end()) {
1✔
1162
            StmtScat = isl_map_copy(Stmt->schedule());
×
1163
        } else {
1✔
1164
            StmtScat = isl_map_copy(Lookup->second);
1✔
1165
        }
1✔
1166

1167
        Schedule = isl_union_map_union(Schedule, isl_union_map_from_map(StmtScat));
1✔
1168
    }
1✔
1169

1170
    return check_validity(this, scop, Schedule);
1✔
1171
}
1✔
1172

1173
bool Dependences::is_valid(Scop &scop, isl_schedule *schedule) const {
1✔
1174
    return check_validity(this, scop, isl_schedule_get_map(schedule));
1✔
1175
}
1✔
1176

1177
ScopToSDFG::ScopToSDFG(Scop &scop, const Dependences &dependences, builder::StructuredSDFGBuilder &builder)
1178
    : scop_(scop), dependences_(dependences), builder_(builder) {
7✔
1179
    for (auto *stmt : scop_.statements()) {
7✔
1180
        stmt_map_[stmt->name()] = stmt;
7✔
1181
    }
7✔
1182
}
7✔
1183

1184
struct NameConnectInfo {
1185
    std::vector<std::string> names;
1186
};
1187

UNCOV
1188
static isl_stat collect_dim_names(isl_map *map, void *user) {
×
UNCOV
1189
    auto *info = static_cast<NameConnectInfo *>(user);
×
UNCOV
1190
    int dim = isl_map_dim(map, isl_dim_out);
×
UNCOV
1191
    if (dim > info->names.size()) {
×
UNCOV
1192
        info->names.resize(dim);
×
UNCOV
1193
    }
×
UNCOV
1194

×
UNCOV
1195
    isl_space *space = isl_map_get_space(map);
×
UNCOV
1196
    isl_local_space *ls = isl_local_space_from_space(space);
×
UNCOV
1197

×
UNCOV
1198
    for (int i = 0; i < dim; ++i) {
×
UNCOV
1199
        if (!info->names[i].empty()) {
×
1200
            continue;
×
1201
        }
×
UNCOV
1202

×
UNCOV
1203
        const char *name = isl_map_get_dim_name(map, isl_dim_out, i);
×
UNCOV
1204
        if (name) {
×
1205
            info->names[i] = name;
×
1206
            continue;
×
1207
        }
×
UNCOV
1208

×
UNCOV
1209
        // Try to recover from input dimensions
×
UNCOV
1210
        int in_dim = isl_map_dim(map, isl_dim_in);
×
UNCOV
1211
        for (int j = 0; j < in_dim; ++j) {
×
UNCOV
1212
            isl_constraint *c = isl_constraint_alloc_equality(isl_local_space_copy(ls));
×
UNCOV
1213
            c = isl_constraint_set_coefficient_si(c, isl_dim_out, i, 1);
×
UNCOV
1214
            c = isl_constraint_set_coefficient_si(c, isl_dim_in, j, -1);
×
UNCOV
1215

×
UNCOV
1216
            isl_basic_map *eq_bmap = isl_basic_map_from_constraint(c);
×
UNCOV
1217
            isl_map *eq_map = isl_map_from_basic_map(eq_bmap);
×
UNCOV
1218

×
UNCOV
1219
            bool is_subset = isl_map_is_subset(map, eq_map);
×
UNCOV
1220
            isl_map_free(eq_map);
×
UNCOV
1221

×
UNCOV
1222
            if (is_subset) {
×
UNCOV
1223
                const char *in_name = isl_map_get_dim_name(map, isl_dim_in, j);
×
UNCOV
1224
                if (in_name) {
×
UNCOV
1225
                    info->names[i] = in_name;
×
UNCOV
1226
                    break;
×
UNCOV
1227
                }
×
UNCOV
1228
            }
×
UNCOV
1229
        }
×
UNCOV
1230
    }
×
UNCOV
1231
    isl_local_space_free(ls);
×
UNCOV
1232
    isl_map_free(map);
×
UNCOV
1233
    return isl_stat_ok;
×
UNCOV
1234
}
×
1235

1236
static __isl_give isl_ast_node *after_each_for(__isl_take isl_ast_node *node, __isl_keep isl_ast_build *build, void *user) {
12✔
1237
    auto *deps = static_cast<const Dependences *>(user);
12✔
1238
    isl_union_map *schedule = isl_ast_build_get_schedule(build);
12✔
1239
    bool is_parallel = deps->is_parallel(schedule);
12✔
1240
    isl_union_map_free(schedule);
12✔
1241

1242
    if (is_parallel) {
12✔
1243
        isl_id *id = isl_id_alloc(isl_ast_node_get_ctx(node), "parallel", nullptr);
10✔
1244
        node = isl_ast_node_set_annotation(node, id);
10✔
1245
    }
10✔
1246
    return node;
12✔
1247
}
12✔
1248

1249
structured_control_flow::ControlFlowNode &ScopToSDFG::build(analysis::AnalysisManager &analysis_manager) {
7✔
1250
    isl_ctx *ctx = scop_.ctx();
7✔
1251

1252
    // 1. Create AST Builder
1253
    isl_ast_build *ast_builder = isl_ast_build_alloc(ctx);
7✔
1254
    ast_builder = isl_ast_build_set_after_each_for(ast_builder, &after_each_for, (void *) &dependences_);
7✔
1255

1256
    // 2. Generate AST from Schedule
1257
    isl_schedule *schedule = isl_schedule_copy(scop_.schedule_tree());
7✔
1258
    isl_ast_node *root_node = isl_ast_build_node_from_schedule(ast_builder, schedule);
7✔
1259

1260
    // 3. Start Traversal
1261
    auto &scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
7✔
1262
    auto parent_scope = scope_analysis.parent_scope(&scop_.node());
7✔
1263
    if (!parent_scope) {
7✔
NEW
1264
        parent_scope = &builder_.hoist_root();
×
1265
    }
×
1266
    auto parent_sequence = static_cast<structured_control_flow::Sequence *>(parent_scope);
7✔
1267
    int index = parent_sequence->index(scop_.node());
7✔
1268
    auto &target_sequence = builder_.add_sequence_before(*parent_sequence, scop_.node(), {}, scop_.node().debug_info());
7✔
1269

1270
    visit_node(root_node, target_sequence);
7✔
1271
    builder_.remove_child(*parent_sequence, index + 1);
7✔
1272

1273
    isl_ast_node_free(root_node);
7✔
1274
    isl_ast_build_free(ast_builder);
7✔
1275

1276
    return target_sequence;
7✔
1277
}
7✔
1278

1279
void ScopToSDFG::visit_node(isl_ast_node *node, structured_control_flow::Sequence &scope) {
19✔
1280
    if (!node) return;
19✔
1281

1282
    switch (isl_ast_node_get_type(node)) {
19✔
1283
        case isl_ast_node_for:
12✔
1284
            visit_for(node, scope);
12✔
1285
            break;
12✔
1286
        case isl_ast_node_if:
×
1287
            visit_if(node, scope);
×
1288
            break;
×
1289
        case isl_ast_node_block:
×
1290
            visit_block(node, scope);
×
1291
            break;
×
1292
        case isl_ast_node_user:
7✔
1293
            visit_user(node, scope);
7✔
1294
            break;
7✔
NEW
1295
        default: {
×
NEW
1296
            throw std::runtime_error("Unsupported AST node type encountered during SCoP to SDFG conversion.");
×
1297
            break;
×
1298
        }
×
1299
    }
19✔
1300
}
19✔
1301

1302
void ScopToSDFG::visit_for(isl_ast_node *node, structured_control_flow::Sequence &scope) {
12✔
1303
    // Extract Loop Components from ISL
1304
    isl_ast_expr *iterator = isl_ast_node_for_get_iterator(node);
12✔
1305
    isl_ast_expr *init = isl_ast_node_for_get_init(node);
12✔
1306
    isl_ast_expr *cond = isl_ast_node_for_get_cond(node);
12✔
1307
    isl_ast_expr *inc = isl_ast_node_for_get_inc(node);
12✔
1308
    isl_ast_node *body = isl_ast_node_for_get_body(node);
12✔
1309

1310
    // Convert to Symbolic
1311
    auto id = isl_ast_expr_get_id(iterator);
12✔
1312
    symbolic::Symbol sym_iter = symbolic::symbol(isl_id_get_name(id));
12✔
1313
    if (!builder_.subject().exists(sym_iter->get_name())) {
12✔
1314
        builder_.add_container(sym_iter->get_name(), types::Scalar(types::PrimitiveType::Int64));
12✔
1315
    }
12✔
1316
    symbolic::Expression sym_init = convert_expr(init);
12✔
1317
    symbolic::Condition sym_cond = convert_cond(cond);
12✔
1318
    symbolic::Expression step = convert_expr(inc);
12✔
1319
    symbolic::Expression update_expr = symbolic::add(sym_iter, step);
12✔
1320

1321
    // Create map for parallel loops
1322
    bool is_parallel = false;
12✔
1323

1324
    isl_id *annotation = isl_ast_node_get_annotation(node);
12✔
1325
    if (annotation) {
12✔
1326
        if (std::string(isl_id_get_name(annotation)) == "parallel") {
10✔
1327
            is_parallel = true;
10✔
1328
        }
10✔
1329
        isl_id_free(annotation);
10✔
1330
    }
10✔
1331

1332
    if (is_parallel) {
12✔
1333
        auto &loop = builder_.add_map(
10✔
1334
            scope, sym_iter, sym_cond, sym_init, update_expr, structured_control_flow::ScheduleType_Sequential::create()
10✔
1335
        );
10✔
1336

1337
        // Recurse into body
1338
        visit_node(body, loop.root());
10✔
1339
    } else {
10✔
1340
        auto &loop = builder_.add_for(scope, sym_iter, sym_cond, sym_init, update_expr);
2✔
1341

1342
        // Recurse into body
1343
        visit_node(body, loop.root());
2✔
1344
    }
2✔
1345

1346
    isl_ast_expr_free(iterator);
12✔
1347
    isl_ast_expr_free(init);
12✔
1348
    isl_ast_expr_free(cond);
12✔
1349
    isl_ast_expr_free(inc);
12✔
1350
    isl_ast_node_free(body);
12✔
1351
    isl_id_free(id);
12✔
1352
}
12✔
1353

1354
void ScopToSDFG::visit_if(isl_ast_node *node, structured_control_flow::Sequence &scope) {
×
1355
    isl_ast_expr *cond = isl_ast_node_if_get_cond(node);
×
1356
    isl_ast_node *then_node = isl_ast_node_if_get_then(node);
×
1357
    isl_ast_node *else_node = isl_ast_node_if_get_else(node);
×
1358

1359
    auto &if_stmt = builder_.add_if_else(scope);
×
1360

1361
    // Then Block
1362
    symbolic::Condition sym_cond = convert_cond(cond);
×
1363
    auto &then_seq = builder_.add_case(if_stmt, sym_cond);
×
1364
    visit_node(then_node, then_seq);
×
1365

1366
    // Else Block (if exists)
1367
    if (else_node) {
×
1368
        auto &else_seq = builder_.add_case(if_stmt, symbolic::Not(sym_cond));
×
1369
        visit_node(else_node, else_seq);
×
1370
    }
×
1371

1372
    isl_ast_expr_free(cond);
×
1373
    isl_ast_node_free(then_node);
×
1374
    isl_ast_node_free(else_node);
×
1375
}
×
1376

1377
void ScopToSDFG::visit_block(isl_ast_node *node, structured_control_flow::Sequence &scope) {
×
1378
    isl_ast_node_list *list = isl_ast_node_block_get_children(node);
×
1379
    int n = isl_ast_node_list_n_ast_node(list);
×
1380

1381
    // Just iterate and append to current sequence
1382
    for (int i = 0; i < n; ++i) {
×
1383
        isl_ast_node *child = isl_ast_node_list_get_ast_node(list, i);
×
1384
        visit_node(child, scope);
×
1385
        isl_ast_node_free(child);
×
1386
    }
×
1387
    isl_ast_node_list_free(list);
×
1388
}
×
1389

1390
void ScopToSDFG::visit_user(isl_ast_node *node, structured_control_flow::Sequence &scope) {
7✔
1391
    isl_ast_expr *expr = isl_ast_node_user_get_expr(node);
7✔
1392
    // Usually 'expr' is a call operation: "StatementName(i, j)"
1393
    // The identifier of the operation is the statement name.
1394

1395
    // A more robust way to get tuple name directly from expr if it is an ID or Call:
1396
    isl_id *id = isl_ast_expr_get_id(expr); // If it's just an ID
7✔
1397
    if (!id && isl_ast_expr_get_type(expr) == isl_ast_expr_op) {
7✔
1398
        // It's likely a call: S(i, j)
1399
        // The first "arg" or the operation type might hold ID.
1400
        // Actually, isl_ast_expr_get_op_type(expr) == isl_ast_expr_op_call
1401
        isl_ast_expr *func = isl_ast_expr_get_op_arg(expr, 0);
7✔
1402
        id = isl_ast_expr_get_id(func);
7✔
1403
        isl_ast_expr_free(func);
7✔
1404
    }
7✔
1405

1406
    if (id) {
7✔
1407
        std::string name = isl_id_get_name(id);
7✔
1408
        if (stmt_map_.count(name)) {
7✔
1409
            ScopStatement *stmt = stmt_map_[name];
7✔
1410

1411
            // Get args of expr
1412
            int n_args = isl_ast_expr_get_op_n_arg(expr);
7✔
1413
            std::vector<symbolic::Expression> args;
7✔
1414
            for (int i = 1; i < n_args; ++i) {
19✔
1415
                isl_ast_expr *arg_expr = isl_ast_expr_get_op_arg(expr, i);
12✔
1416
                symbolic::Expression arg_sym = convert_expr(arg_expr);
12✔
1417
                args.push_back(arg_sym);
12✔
1418
                isl_ast_expr_free(arg_expr);
12✔
1419
            }
12✔
1420

1421
            // Add transition mapping stmt->iterator_i = arg_i
1422
            assert(args.size() == stmt->iterators().size());
7✔
1423
            Assignments assignments;
7✔
1424
            for (size_t i = 0; i < stmt->iterators().size(); ++i) {
19✔
1425
                assignments[stmt->iterators().at(i)] = args[i];
12✔
1426
            }
12✔
1427
            if (!assignments.empty()) {
7✔
1428
                builder_.add_block(scope, assignments);
7✔
1429
            }
7✔
1430

1431
            if (!stmt->expression().is_null()) {
7✔
1432
                std::string rhs = (*stmt->writes().begin())->data();
1✔
1433
                builder_.add_block(scope, {{symbolic::symbol(rhs), stmt->expression()}});
1✔
1434
            } else {
6✔
1435
                auto &new_block = builder_.add_block(scope);
6✔
1436
                auto &new_code_node =
6✔
1437
                    static_cast<data_flow::CodeNode &>(builder_.copy_node(new_block, *stmt->code_node()));
6✔
1438

1439
                // Create Access Node Mapping
1440
                auto &old_graph = stmt->code_node()->get_parent();
6✔
1441
                auto &new_graph = new_code_node.get_parent();
6✔
1442
                std::unordered_map<const data_flow::DataFlowNode *, data_flow::DataFlowNode *> in_node_map;
6✔
1443
                for (auto &iedge : old_graph.in_edges(*stmt->code_node())) {
6✔
1444
                    if (in_node_map.find(&iedge.src()) == in_node_map.end()) {
6✔
1445
                        in_node_map[&iedge.src()] = &builder_.copy_node(new_block, iedge.src());
6✔
1446
                    }
6✔
1447
                    builder_.add_memlet(
6✔
1448
                        new_block,
6✔
1449
                        *in_node_map[&iedge.src()],
6✔
1450
                        iedge.src_conn(),
6✔
1451
                        new_code_node,
6✔
1452
                        iedge.dst_conn(),
6✔
1453
                        iedge.subset(),
6✔
1454
                        iedge.base_type(),
6✔
1455
                        iedge.debug_info()
6✔
1456
                    );
6✔
1457
                }
6✔
1458

1459
                std::unordered_map<const data_flow::DataFlowNode *, data_flow::DataFlowNode *> out_node_map;
6✔
1460
                for (auto &oedge : old_graph.out_edges(*stmt->code_node())) {
6✔
1461
                    if (out_node_map.find(&oedge.dst()) == out_node_map.end()) {
6✔
1462
                        out_node_map[&oedge.dst()] = &builder_.copy_node(new_block, oedge.dst());
6✔
1463
                    }
6✔
1464
                    builder_.add_memlet(
6✔
1465
                        new_block,
6✔
1466
                        new_code_node,
6✔
1467
                        oedge.src_conn(),
6✔
1468
                        *out_node_map[&oedge.dst()],
6✔
1469
                        oedge.dst_conn(),
6✔
1470
                        oedge.subset(),
6✔
1471
                        oedge.base_type(),
6✔
1472
                        oedge.debug_info()
6✔
1473
                    );
6✔
1474
                }
6✔
1475
            }
6✔
1476
        }
7✔
1477
        isl_id_free(id);
7✔
1478
    }
7✔
1479
    isl_ast_expr_free(expr);
7✔
1480
}
7✔
1481

1482
symbolic::Expression ScopToSDFG::convert_expr(isl_ast_expr *expr) {
60✔
1483
    if (!expr) return SymEngine::null;
60✔
1484

1485
    isl_ast_expr_type type = isl_ast_expr_get_type(expr);
60✔
1486
    if (type == isl_ast_expr_int) {
60✔
1487
        isl_val *v = isl_ast_expr_get_val(expr);
26✔
1488
        long val = isl_val_get_num_si(v);
26✔
1489
        isl_val_free(v);
26✔
1490
        return symbolic::integer(val);
26✔
1491
    } else if (type == isl_ast_expr_id) {
34✔
1492
        isl_id *id = isl_ast_expr_get_id(expr);
34✔
1493
        std::string name = isl_id_get_name(id);
34✔
1494
        isl_id_free(id);
34✔
1495
        return symbolic::symbol(name);
34✔
1496
    } else if (type == isl_ast_expr_op) {
34✔
1497
        isl_ast_op_type op = isl_ast_expr_get_op_type(expr);
×
1498
        int n_args = isl_ast_expr_get_op_n_arg(expr);
×
1499

1500
        if (n_args == 1) {
×
1501
            isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
×
1502
            symbolic::Expression res;
×
1503
            if (op == isl_ast_op_minus) {
×
1504
                res = symbolic::mul(symbolic::integer(-1), convert_expr(arg0));
×
1505
            }
×
1506
            isl_ast_expr_free(arg0);
×
1507
            return res;
×
1508
        }
×
1509

1510
        if (n_args == 2) {
×
1511
            isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
×
1512
            isl_ast_expr *arg1 = isl_ast_expr_get_op_arg(expr, 1);
×
1513
            symbolic::Expression s0 = convert_expr(arg0);
×
1514
            symbolic::Expression s1 = convert_expr(arg1);
×
1515
            isl_ast_expr_free(arg0);
×
1516
            isl_ast_expr_free(arg1);
×
1517

1518
            switch (op) {
×
1519
                case isl_ast_op_add:
×
1520
                    return symbolic::add(s0, s1);
×
1521
                case isl_ast_op_sub:
×
1522
                    return symbolic::sub(s0, s1);
×
1523
                case isl_ast_op_mul:
×
1524
                    return symbolic::mul(s0, s1);
×
1525
                case isl_ast_op_div:
×
1526
                case isl_ast_op_pdiv_q:
×
1527
                case isl_ast_op_fdiv_q:
×
1528
                    return symbolic::div(s0, s1);
×
1529
                case isl_ast_op_pdiv_r:
×
1530
                case isl_ast_op_zdiv_r:
×
1531
                    return symbolic::mod(s0, s1);
×
1532
                case isl_ast_op_min:
×
1533
                    return symbolic::min(s0, s1);
×
1534
                case isl_ast_op_max:
×
1535
                    return symbolic::max(s0, s1);
×
1536
                default:
×
1537
                    break;
×
1538
            }
×
1539
        }
×
1540
    }
×
1541
    return SymEngine::null;
×
1542
}
60✔
1543

1544
symbolic::Condition ScopToSDFG::convert_cond(isl_ast_expr *expr) {
12✔
1545
    if (!expr) return SymEngine::null;
12✔
1546

1547
    if (isl_ast_expr_get_type(expr) != isl_ast_expr_op) {
12✔
1548
        return SymEngine::null;
×
1549
    }
×
1550

1551
    isl_ast_op_type op = isl_ast_expr_get_op_type(expr);
12✔
1552
    int n_args = isl_ast_expr_get_op_n_arg(expr);
12✔
1553

1554
    if (n_args == 2) {
12✔
1555
        isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
12✔
1556
        isl_ast_expr *arg1 = isl_ast_expr_get_op_arg(expr, 1);
12✔
1557

1558
        symbolic::Condition res;
12✔
1559

1560
        switch (op) {
12✔
1561
            case isl_ast_op_eq:
×
1562
                res = symbolic::Eq(convert_expr(arg0), convert_expr(arg1));
×
1563
                break;
×
1564
            case isl_ast_op_lt:
10✔
1565
                res = symbolic::Lt(convert_expr(arg0), convert_expr(arg1));
10✔
1566
                break;
10✔
1567
            case isl_ast_op_le:
2✔
1568
                res = symbolic::Le(convert_expr(arg0), convert_expr(arg1));
2✔
1569
                break;
2✔
1570
            case isl_ast_op_gt:
×
1571
                res = symbolic::Gt(convert_expr(arg0), convert_expr(arg1));
×
1572
                break;
×
1573
            case isl_ast_op_ge:
×
1574
                res = symbolic::Ge(convert_expr(arg0), convert_expr(arg1));
×
1575
                break;
×
1576
            case isl_ast_op_and:
×
1577
            case isl_ast_op_and_then:
×
1578
                res = symbolic::And(convert_cond(arg0), convert_cond(arg1));
×
1579
                break;
×
1580
            case isl_ast_op_or:
×
1581
            case isl_ast_op_or_else:
×
1582
                res = symbolic::Or(convert_cond(arg0), convert_cond(arg1));
×
1583
                break;
×
1584
            default:
×
1585
                break;
×
1586
        }
12✔
1587

1588
        isl_ast_expr_free(arg0);
12✔
1589
        isl_ast_expr_free(arg1);
12✔
1590
        return res;
12✔
1591
    }
12✔
1592

1593
    return SymEngine::null;
×
1594
}
12✔
1595

1596
ScopAnalysis::ScopAnalysis(StructuredSDFG &sdfg) : Analysis(sdfg) {}
×
1597

1598
void ScopAnalysis::run(analysis::AnalysisManager &analysis_manager) {
×
1599
    this->scops_.clear();
×
1600
    this->dependences_.clear();
×
1601

1602
    auto &loop_analysis = analysis_manager.get<LoopAnalysis>();
×
1603
    for (auto &loop : loop_analysis.loops()) {
×
1604
        if (!dynamic_cast<structured_control_flow::StructuredLoop *>(loop)) {
×
1605
            continue;
×
1606
        }
×
1607
        auto sloop = static_cast<structured_control_flow::StructuredLoop *>(loop);
×
1608

1609
        ScopBuilder builder(this->sdfg_, *sloop);
×
1610
        auto scop = builder.build(analysis_manager);
×
1611
        if (!scop) {
×
1612
            continue;
×
1613
        }
×
1614
        auto dependences = std::make_unique<Dependences>(*scop);
×
1615
        if (!dependences->has_valid_dependences()) {
×
1616
            continue;
×
1617
        }
×
1618

1619
        this->scops_[loop] = std::move(scop);
×
1620
        this->dependences_[loop] = std::move(dependences);
×
1621
    }
×
1622
}
×
1623

1624
bool ScopAnalysis::has(const structured_control_flow::ControlFlowNode *node) const {
×
1625
    return this->scops_.find(node) != this->scops_.end();
×
1626
}
×
1627

1628
Scop &ScopAnalysis::scop(const structured_control_flow::ControlFlowNode *node) const { return *this->scops_.at(node); }
×
1629

1630
const Dependences &ScopAnalysis::dependences(const structured_control_flow::ControlFlowNode *node) const {
×
1631
    return *this->dependences_.at(node);
×
1632
}
×
1633

1634
} // namespace analysis
1635
} // 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