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

daisytuner / sdfglib / 21098544716

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

push

github

web-flow
Merge pull request #459 from daisytuner/isl-tiling

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%)

788.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) {}
218✔
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) {
102✔
41
    domain_ = isl_set_set_tuple_name(domain, name.c_str());
102✔
42
    isl_space *space = isl_set_get_space(domain_);
102✔
43
    schedule_ = isl_map_identity(isl_space_map_from_set(space));
102✔
44
}
102✔
45

46
ScopStatement::ScopStatement(const std::string &name, isl_set *domain, symbolic::Expression expression)
47
    : name_(name), code_node_(nullptr), expression_(expression) {
4✔
48
    domain_ = isl_set_set_tuple_name(domain, name.c_str());
4✔
49
    isl_space *space = isl_set_get_space(domain_);
4✔
50
    schedule_ = isl_map_identity(isl_space_map_from_set(space));
4✔
51
}
4✔
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) {}
86✔
55

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

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

64
    return domain;
88✔
65
}
88✔
66

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

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

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

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

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

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

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

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

123
    return this->schedule_tree_;
78✔
124
}
104✔
125

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

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

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

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

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

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

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

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

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

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

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

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

231
            this->scop_->insert(statement);
4✔
232
        }
4✔
233
    }
138✔
234
}
120✔
235

236
void ScopBuilder::visit_block(analysis::AnalysisManager &analysis_manager, structured_control_flow::Block &block) {
104✔
237
    auto &graph = block.dataflow();
104✔
238
    for (auto node : graph.topological_sort()) {
324✔
239
        if (auto code_node = dynamic_cast<data_flow::CodeNode *>(node)) {
324✔
240
            if (dynamic_cast<data_flow::LibraryNode *>(code_node)) {
102✔
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));
102✔
248
            auto statement =
102✔
249
                std::make_unique<ScopStatement>("S_" + std::to_string(code_node->element_id()), domain, code_node);
102✔
250

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

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

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

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

287
            this->scop_->insert(statement);
102✔
288
        }
102✔
289
    }
324✔
290
}
104✔
291

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

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

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

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

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

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

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

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

383
        // Insert indvar dimension at 0
384
        domain = isl_set_insert_dims(domain, isl_dim_set, 0, 1);
138✔
385
        domain = isl_set_set_dim_name(domain, isl_dim_set, 0, indvar.c_str());
138✔
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());
138✔
389
        if (param_idx >= 0) {
138✔
390
            isl_space *space = isl_set_get_space(domain);
36✔
391
            isl_local_space *ls = isl_local_space_from_space(space);
36✔
392
            isl_constraint *c = isl_constraint_alloc_equality(ls);
36✔
393
            c = isl_constraint_set_coefficient_si(c, isl_dim_set, 0, 1);
36✔
394
            c = isl_constraint_set_coefficient_si(c, isl_dim_param, param_idx, -1);
36✔
395
            domain = isl_set_add_constraint(domain, c);
36✔
396
            domain = isl_set_project_out(domain, isl_dim_param, param_idx, 1);
36✔
397
        }
36✔
398

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

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

407
            // Match names for inner dimensions
408
            for (int j = 0; j < inner_dims; ++j) {
72✔
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);
36✔
411
                if (name) {
36✔
412
                    loop_bound = isl_set_set_dim_name(loop_bound, isl_dim_set, 1 + j, name);
36✔
413
                }
36✔
414
            }
36✔
415
        }
36✔
416

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

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

423
        // Insert dimension in input (domain)
424
        schedule = isl_map_insert_dims(schedule, isl_dim_in, 0, 1);
138✔
425
        schedule = isl_map_set_dim_name(schedule, isl_dim_in, 0, indvar.c_str());
138✔
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());
138✔
429
        if (param_idx >= 0) {
138✔
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);
138✔
441
        schedule = isl_map_set_dim_name(schedule, isl_dim_out, 0, indvar.c_str());
138✔
442

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

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

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

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

454
            if (rel_n_in < dom_n) {
290✔
455
                relation = isl_map_insert_dims(relation, isl_dim_in, 0, 1);
4✔
456
                relation = isl_map_set_dim_name(relation, isl_dim_in, 0, indvar.c_str());
4✔
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());
4✔
460
                if (param_idx >= 0) {
4✔
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
            }
4✔
470

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

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

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

504
    return relation;
210✔
505
}
210✔
506

507
static isl_map *tag(isl_map *Relation, isl_id *TagId) {
352✔
508
    isl_space *Space = isl_map_get_space(Relation);
352✔
509
    Space = isl_space_drop_dims(Space, isl_dim_out, 0, isl_map_dim(Relation, isl_dim_out));
352✔
510
    Space = isl_space_set_tuple_id(Space, isl_dim_out, TagId);
352✔
511
    isl_multi_aff *Tag = isl_multi_aff_domain_map(Space);
352✔
512
    Relation = isl_map_preimage_domain_multi_aff(Relation, Tag);
352✔
513
    return Relation;
352✔
514
}
352✔
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
) {
66✔
523
    isl_space *Space = isl_space_copy(S.param_space());
66✔
524
    Read = isl_union_map_empty(isl_space_copy(Space));
66✔
525
    MustWrite = isl_union_map_empty(isl_space_copy(Space));
66✔
526
    ReductionTagMap = isl_union_map_empty(isl_space_copy(Space));
66✔
527
    isl_union_map *StmtSchedule = isl_union_map_empty(isl_space_copy(Space));
66✔
528

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

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

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

542

543
            if (ReductionArrays.count(MA->data())) {
176✔
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 {
176✔
558
                isl_map *StmtScheduleMap = isl_map_copy(stmt->schedule());
176✔
559
                assert(
176✔
560
                    StmtScheduleMap &&
176✔
561
                    "Schedules that contain extension nodes require special "
176✔
562
                    "handling."
176✔
563
                );
176✔
564

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

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

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

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

586
    isl_space_free(Space);
66✔
587
}
66✔
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) {
264✔
591
    isl_union_access_info *AI;
264✔
592

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

602
static isl_stat get_map_range_space(isl_map *Map, void *User) {
4✔
603
    isl_space **Space = (isl_space **) User;
4✔
604
    if (*Space) {
4✔
605
        isl_map_free(Map);
×
606
        return isl_stat_ok;
×
607
    }
×
608
    *Space = isl_space_range(isl_map_get_space(Map));
4✔
609
    isl_map_free(Map);
4✔
610
    return isl_stat_ok;
4✔
611
}
4✔
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 {
54✔
626
    assert(has_valid_dependences() && "No valid dependences available");
54✔
627
    isl_space *Space = isl_union_map_get_space(RAW);
54✔
628
    isl_union_map *Deps = isl_union_map_empty(Space);
54✔
629

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

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

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

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

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

640
    Deps = isl_union_map_coalesce(Deps);
54✔
641
    Deps = isl_union_map_detect_equalities(Deps);
54✔
642
    return Deps;
54✔
643
}
54✔
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) {
312✔
652
    auto *info = static_cast<DepCallbackInfo *>(user);
312✔
653

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

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

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

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

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

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

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

699
        if (dim_idx != -1) {
156✔
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) {
198✔
705
                deltas = isl_set_fix_si(deltas, isl_dim_set, i, 0);
50✔
706
            }
50✔
707

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

711
            if (!isl_set_is_subset(deltas, zero_at_dim)) {
148✔
712
                exists_for_dim = true;
124✔
713
            }
124✔
714
            isl_set_free(zero_at_dim);
148✔
715
        }
148✔
716
        isl_set_free(deltas);
156✔
717

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

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

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

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

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

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

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

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

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

776
    return deps;
60✔
777
}
60✔
778

779
bool Dependences::has_valid_dependences() const { return (RAW != nullptr) && (WAR != nullptr) && (WAW != nullptr); }
64✔
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) {
66✔
788
    isl_union_map *Read, *MustWrite, *ReductionTagMap;
66✔
789
    isl_union_set *TaggedStmtDomain;
66✔
790
    collectInfo(S, Read, MustWrite, ReductionTagMap, TaggedStmtDomain);
66✔
791

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

794
    bool has_reductions = !isl_union_map_is_empty(ReductionTagMap);
66✔
795
    if (!has_reductions) {
66✔
796
        isl_union_map_free(ReductionTagMap);
66✔
797
        // Tag the schedule tree if we want fine-grain dependence info
798
        auto TaggedMap = isl_union_set_unwrap(isl_union_set_copy(TaggedStmtDomain));
66✔
799
        auto Tags = isl_union_map_domain_map_union_pw_multi_aff(TaggedMap);
66✔
800
        Schedule = isl_schedule_pullback_union_pw_multi_aff(Schedule, Tags);
66✔
801
    } else {
66✔
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;
66✔
822
    {
66✔
823
        RAW = WAW = WAR = RED = nullptr;
66✔
824
        isl_union_map *Write = isl_union_map_copy(MustWrite);
66✔
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);
66✔
886
        StrictWAW = isl_union_flow_get_must_dependence(Flow);
66✔
887
        isl_union_flow_free(Flow);
66✔
888

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

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

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

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

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

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

913
    isl_union_map *STMT_RAW =
66✔
914
        isl_union_map_intersect_domain(isl_union_map_copy(RAW), isl_union_set_copy(TaggedStmtDomain));
66✔
915
    isl_union_map *STMT_WAW =
66✔
916
        isl_union_map_intersect_domain(isl_union_map_copy(WAW), isl_union_set_copy(TaggedStmtDomain));
66✔
917
    isl_union_map *STMT_WAR =
66✔
918
        isl_union_map_intersect_domain(isl_union_map_copy(WAR), isl_union_set_copy(TaggedStmtDomain));
66✔
919
    isl_union_set_free(TaggedStmtDomain);
66✔
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));
66✔
937
    for (auto stmt : S.statements()) {
84✔
938
        for (MemoryAccess *MA : stmt->accesses()) {
176✔
939
            if (!MA->is_reduction_like()) {
176✔
940
                continue;
176✔
941
            }
176✔
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
    }
84✔
947

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

956
    if (!isl_union_map_is_empty(RED)) {
66✔
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 {
66✔
965
        TC_RED = isl_union_map_empty(isl_union_map_get_space(RED));
66✔
966
    }
66✔
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));
66✔
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()) {
84✔
981
        for (MemoryAccess *MA : stmt->accesses()) {
176✔
982
            if (!MA->is_reduction_like()) {
176✔
983
                continue;
176✔
984
            }
176✔
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
    }
84✔
1001

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

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

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

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

1025
    RAW = isl_union_map_coalesce(RAW);
66✔
1026
    WAW = isl_union_map_coalesce(WAW);
66✔
1027
    WAR = isl_union_map_coalesce(WAR);
66✔
1028
    RED = isl_union_map_coalesce(RED);
66✔
1029
    TC_RED = isl_union_map_coalesce(TC_RED);
66✔
1030
}
66✔
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 {
24✔
1074
    isl_union_map *deps = this->dependences(TYPE_RAW | TYPE_WAR | TYPE_WAW);
24✔
1075
    deps = isl_union_map_apply_range(deps, isl_union_map_copy(schedule));
24✔
1076
    deps = isl_union_map_apply_domain(deps, isl_union_map_copy(schedule));
24✔
1077
    if (isl_union_map_is_empty(deps)) {
24✔
1078
        isl_union_map_free(deps);
18✔
1079
        return true;
18✔
1080
    }
18✔
1081

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

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

1088
    isl_set *Deltas = isl_map_deltas(schedule_deps);
6✔
1089
    isl_set *Distance = isl_set_universe(isl_set_get_space(Deltas));
6✔
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);
8✔
1093

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

1097
    bool IsParallel = isl_set_is_empty(Distance);
6✔
1098
    if (IsParallel || !min_distance_ptr) {
6✔
1099
        isl_set_free(Distance);
6✔
1100
        return IsParallel;
6✔
1101
    }
6✔
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
}
6✔
1113

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

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

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

1124
    if (!ScheduleSpace) {
4✔
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));
4✔
1130
    int dims = isl_set_dim(Zero, isl_dim_set);
4✔
1131
    for (int i = 0; i < dims; ++i) Zero = isl_set_fix_si(Zero, isl_dim_set, i, 0);
8✔
1132

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

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

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

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

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

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

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

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

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

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

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

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

1177
ScopToSDFG::ScopToSDFG(Scop &scop, const Dependences &dependences, builder::StructuredSDFGBuilder &builder)
1178
    : scop_(scop), dependences_(dependences), builder_(builder) {
14✔
1179
    for (auto *stmt : scop_.statements()) {
14✔
1180
        stmt_map_[stmt->name()] = stmt;
14✔
1181
    }
14✔
1182
}
14✔
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) {
24✔
1237
    auto *deps = static_cast<const Dependences *>(user);
24✔
1238
    isl_union_map *schedule = isl_ast_build_get_schedule(build);
24✔
1239
    bool is_parallel = deps->is_parallel(schedule);
24✔
1240
    isl_union_map_free(schedule);
24✔
1241

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

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

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

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

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

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

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

1276
    return target_sequence;
14✔
1277
}
14✔
1278

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

1282
    switch (isl_ast_node_get_type(node)) {
38✔
1283
        case isl_ast_node_for:
24✔
1284
            visit_for(node, scope);
24✔
1285
            break;
24✔
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:
14✔
1293
            visit_user(node, scope);
14✔
1294
            break;
14✔
NEW
1295
        default: {
×
NEW
1296
            throw std::runtime_error("Unsupported AST node type encountered during SCoP to SDFG conversion.");
×
1297
            break;
×
1298
        }
×
1299
    }
38✔
1300
}
38✔
1301

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

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

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

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

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

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

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

1346
    isl_ast_expr_free(iterator);
24✔
1347
    isl_ast_expr_free(init);
24✔
1348
    isl_ast_expr_free(cond);
24✔
1349
    isl_ast_expr_free(inc);
24✔
1350
    isl_ast_node_free(body);
24✔
1351
    isl_id_free(id);
24✔
1352
}
24✔
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) {
14✔
1391
    isl_ast_expr *expr = isl_ast_node_user_get_expr(node);
14✔
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
14✔
1397
    if (!id && isl_ast_expr_get_type(expr) == isl_ast_expr_op) {
14✔
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);
14✔
1402
        id = isl_ast_expr_get_id(func);
14✔
1403
        isl_ast_expr_free(func);
14✔
1404
    }
14✔
1405

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

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

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

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

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

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

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

1485
    isl_ast_expr_type type = isl_ast_expr_get_type(expr);
120✔
1486
    if (type == isl_ast_expr_int) {
120✔
1487
        isl_val *v = isl_ast_expr_get_val(expr);
52✔
1488
        long val = isl_val_get_num_si(v);
52✔
1489
        isl_val_free(v);
52✔
1490
        return symbolic::integer(val);
52✔
1491
    } else if (type == isl_ast_expr_id) {
68✔
1492
        isl_id *id = isl_ast_expr_get_id(expr);
68✔
1493
        std::string name = isl_id_get_name(id);
68✔
1494
        isl_id_free(id);
68✔
1495
        return symbolic::symbol(name);
68✔
1496
    } else if (type == isl_ast_expr_op) {
68✔
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
}
120✔
1543

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

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

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

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

1558
        symbolic::Condition res;
24✔
1559

1560
        switch (op) {
24✔
1561
            case isl_ast_op_eq:
×
1562
                res = symbolic::Eq(convert_expr(arg0), convert_expr(arg1));
×
1563
                break;
×
1564
            case isl_ast_op_lt:
20✔
1565
                res = symbolic::Lt(convert_expr(arg0), convert_expr(arg1));
20✔
1566
                break;
20✔
1567
            case isl_ast_op_le:
4✔
1568
                res = symbolic::Le(convert_expr(arg0), convert_expr(arg1));
4✔
1569
                break;
4✔
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
        }
24✔
1587

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

1593
    return SymEngine::null;
×
1594
}
24✔
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