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

daisytuner / sdfglib / 21081277365

16 Jan 2026 09:23PM UTC coverage: 64.728% (+1.9%) from 62.838%
21081277365

Pull #458

github

web-flow
Merge 3a0ed0ea6 into 69d31be1e
Pull Request #458: enables ISL tests

5 of 5 new or added lines in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

19023 of 29389 relevant lines covered (64.73%)

392.69 hits per line

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

72.69
/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) {}
105✔
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) {
49✔
41
    domain_ = isl_set_set_tuple_name(domain, name.c_str());
49✔
42
    isl_space *space = isl_set_get_space(domain_);
49✔
43
    schedule_ = isl_map_identity(isl_space_map_from_set(space));
49✔
44
}
49✔
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) {}
41✔
55

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

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

64
    return domain;
40✔
65
}
40✔
66

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

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

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

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

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

97
        isl_union_map *padded_map = isl_union_map_empty(isl_union_map_get_space(sched_map));
37✔
98
        for (int i = 0; i < n_maps; ++i) {
84✔
99
            isl_map *map = isl_map_list_get_map(map_list, i);
47✔
100
            int dim = isl_map_dim(map, isl_dim_out);
47✔
101
            if (dim < max_dim) {
47✔
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);
47✔
108
        }
47✔
109
        isl_map_list_free(map_list);
37✔
110
        isl_union_map_free(sched_map);
37✔
111
        sched_map = padded_map;
37✔
112

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

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

123
    return this->schedule_tree_;
37✔
124
}
41✔
125

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

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

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

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

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

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

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

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

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

191
        auto &transition = sequence.at(i).second;
65✔
192
        size_t j = 0;
65✔
193
        for (const auto &assignment : transition.assignments()) {
65✔
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
    }
65✔
234
}
56✔
235

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

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

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

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

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

287
            this->scop_->insert(statement);
49✔
288
        }
49✔
289
    }
156✔
290
}
50✔
291

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

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

327
            std::string literal_str = symbolic::constraint_to_isl_str(literal);
55✔
328
            if (literal_str.empty()) {
55✔
329
                this->scop_ = nullptr;
×
330
                return;
×
331
            }
×
332
            constraints.push_back(literal_str);
55✔
333
        }
55✔
334
    }
55✔
335
    auto stride = analysis::LoopAnalysis::stride(&loop);
54✔
336
    if (stride.is_null()) {
54✔
337
        this->scop_ = nullptr;
×
338
        return;
×
339
    }
×
340
    int stride_value = stride->as_int();
54✔
341
    if (stride_value != 1) {
54✔
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 ");
54✔
348
    dimension_constraints_[indvar] = condition;
54✔
349

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

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

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

376
    auto stmts = this->scop_->statements();
54✔
377
    for (size_t i = stmt_start; i < stmts.size(); ++i) {
119✔
378
        ScopStatement *stmt = stmts[i];
65✔
379
        isl_set *domain = isl_set_copy(stmt->domain());
65✔
380

381
        // Insert indvar dimension at 0
382
        domain = isl_set_insert_dims(domain, isl_dim_set, 0, 1);
65✔
383
        domain = isl_set_set_dim_name(domain, isl_dim_set, 0, indvar.c_str());
65✔
384

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

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

402
        if (inner_dims > 0) {
65✔
403
            loop_bound = isl_set_add_dims(loop_bound, isl_dim_set, inner_dims);
16✔
404

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

415
        domain = isl_set_intersect(domain, loop_bound);
65✔
416
        stmt->set_domain(domain);
65✔
417

418
        // Update Schedule
419
        isl_map *schedule = isl_map_copy(stmt->schedule());
65✔
420

421
        // Insert dimension in input (domain)
422
        schedule = isl_map_insert_dims(schedule, isl_dim_in, 0, 1);
65✔
423
        schedule = isl_map_set_dim_name(schedule, isl_dim_in, 0, indvar.c_str());
65✔
424

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

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

441
        // Equate input[0] and output[0]
442
        schedule = isl_map_equate(schedule, isl_dim_in, 0, isl_dim_out, 0);
65✔
443

444
        stmt->set_schedule(schedule);
65✔
445

446
        for (auto access : stmt->accesses()) {
137✔
447
            isl_map *relation = isl_map_copy(access->relation());
137✔
448

449
            int dom_n = isl_set_dim(stmt->domain(), isl_dim_set);
137✔
450
            int rel_n_in = isl_map_dim(relation, isl_dim_in);
137✔
451

452
            if (rel_n_in < dom_n) {
137✔
453
                relation = isl_map_insert_dims(relation, isl_dim_in, 0, 1);
2✔
454
                relation = isl_map_set_dim_name(relation, isl_dim_in, 0, indvar.c_str());
2✔
455

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

469
            relation = isl_map_set_tuple_name(relation, isl_dim_in, stmt->name().c_str());
137✔
470
            access->set_relation(relation);
137✔
471
        }
137✔
472
    }
65✔
473
    isl_set_free(loop_bound_base);
54✔
474

475
    this->dimensions_.pop_back();
54✔
476
}
54✔
477

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

502
    return relation;
101✔
503
}
101✔
504

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

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

527
    std::unordered_set<std::string> ReductionArrays;
29✔
528
    for (auto stmt : S.statements())
29✔
529
        for (MemoryAccess *MA : stmt->accesses())
38✔
530
            if (MA->is_reduction_like()) ReductionArrays.insert(MA->data());
80✔
531

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

538
            accdom = isl_map_intersect_domain(accdom, domcp);
80✔
539

540

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

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

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

569
            if (MA->access_type() == AccessType::READ) {
80✔
570
                Read = isl_union_map_add_map(Read, accdom);
42✔
571
            } else {
42✔
572
                MustWrite = isl_union_map_add_map(MustWrite, accdom);
38✔
573
            }
38✔
574
        }
80✔
575
    }
38✔
576

577
    StmtSchedule = isl_union_map_intersect_params(StmtSchedule, isl_set_universe(isl_space_copy(Space)));
29✔
578
    TaggedStmtDomain = isl_union_map_domain(StmtSchedule);
29✔
579

580
    ReductionTagMap = isl_union_map_coalesce(ReductionTagMap);
29✔
581
    Read = isl_union_map_coalesce(Read);
29✔
582
    MustWrite = isl_union_map_coalesce(MustWrite);
29✔
583

584
    isl_space_free(Space);
29✔
585
}
29✔
586

587
static isl_union_flow *
588
buildFlow(isl_union_map *Snk, isl_union_map *Src, isl_union_map *MaySrc, isl_union_map *Kill, isl_schedule *Schedule) {
116✔
589
    isl_union_access_info *AI;
116✔
590

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

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

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

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

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

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

628
    if (Kinds & TYPE_RAW) Deps = isl_union_map_union(Deps, isl_union_map_copy(RAW));
11✔
629

630
    if (Kinds & TYPE_WAR) Deps = isl_union_map_union(Deps, isl_union_map_copy(WAR));
11✔
631

632
    if (Kinds & TYPE_WAW) Deps = isl_union_map_union(Deps, isl_union_map_copy(WAW));
11✔
633

634
    if (Kinds & TYPE_RED) Deps = isl_union_map_union(Deps, isl_union_map_copy(RED));
11✔
635

636
    if (Kinds & TYPE_TC_RED) Deps = isl_union_map_union(Deps, isl_union_map_copy(TC_RED));
11✔
637

638
    Deps = isl_union_map_coalesce(Deps);
11✔
639
    Deps = isl_union_map_detect_equalities(Deps);
11✔
640
    return Deps;
11✔
641
}
11✔
642

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

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

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

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

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

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

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

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

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

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

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

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

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

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

722
    isl_space *space = isl_map_get_space(bmap);
62✔
723
    isl_space *domain_space = isl_space_domain(space);
62✔
724

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

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

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

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

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

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

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

774
    return deps;
30✔
775
}
30✔
776

777
bool Dependences::has_valid_dependences() const { return (RAW != nullptr) && (WAR != nullptr) && (WAW != nullptr); }
14✔
778

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

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

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

790
    isl_schedule *Schedule = isl_schedule_copy(S.schedule_tree());
29✔
791

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

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

811
        isl_union_pw_multi_aff *Tags = isl_union_pw_multi_aff_union_add(ReductionTags, IdentityTags);
×
812

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

819
    isl_union_map *StrictWAW = nullptr;
29✔
820
    {
29✔
821
        RAW = WAW = WAR = RED = nullptr;
29✔
822
        isl_union_map *Write = isl_union_map_copy(MustWrite);
29✔
823

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

883
        isl_union_flow *Flow = buildFlow(Write, Write, Read, nullptr, Schedule);
29✔
884
        StrictWAW = isl_union_flow_get_must_dependence(Flow);
29✔
885
        isl_union_flow_free(Flow);
29✔
886

887
        Flow = buildFlow(Read, nullptr, Write, nullptr, Schedule);
29✔
888
        RAW = isl_union_flow_get_may_dependence(Flow);
29✔
889
        isl_union_flow_free(Flow);
29✔
890

891
        Flow = buildFlow(Write, nullptr, Read, nullptr, Schedule);
29✔
892
        WAR = isl_union_flow_get_may_dependence(Flow);
29✔
893
        isl_union_flow_free(Flow);
29✔
894

895
        Flow = buildFlow(Write, nullptr, Write, nullptr, Schedule);
29✔
896
        WAW = isl_union_flow_get_may_dependence(Flow);
29✔
897
        isl_union_flow_free(Flow);
29✔
898

899
        isl_union_map_free(Write);
29✔
900
        isl_union_map_free(MustWrite);
29✔
901
        isl_union_map_free(Read);
29✔
902
        isl_schedule_free(Schedule);
29✔
903

904
        RAW = isl_union_map_coalesce(RAW);
29✔
905
        WAW = isl_union_map_coalesce(WAW);
29✔
906
        WAR = isl_union_map_coalesce(WAR);
29✔
907

908
        // End of max_operations scope.
909
    }
29✔
910

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

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

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

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

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

960
        // Step 4)
961
        add_privatization_dependences();
×
962
    } else {
29✔
963
        TC_RED = isl_union_map_empty(isl_union_map_get_space(RED));
29✔
964
    }
29✔
965

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

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

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

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

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

1007
    RAW = isl_union_map_zip(RAW);
29✔
1008
    WAW = isl_union_map_zip(WAW);
29✔
1009
    WAR = isl_union_map_zip(WAR);
29✔
1010
    RED = isl_union_map_zip(RED);
29✔
1011
    TC_RED = isl_union_map_zip(TC_RED);
29✔
1012

1013
    RAW = isl_union_set_unwrap(isl_union_map_domain(RAW));
29✔
1014
    WAW = isl_union_set_unwrap(isl_union_map_domain(WAW));
29✔
1015
    WAR = isl_union_set_unwrap(isl_union_map_domain(WAR));
29✔
1016
    RED = isl_union_set_unwrap(isl_union_map_domain(RED));
29✔
1017
    TC_RED = isl_union_set_unwrap(isl_union_map_domain(TC_RED));
29✔
1018

1019
    RAW = isl_union_map_union(RAW, STMT_RAW);
29✔
1020
    WAW = isl_union_map_union(WAW, STMT_WAW);
29✔
1021
    WAR = isl_union_map_union(WAR, STMT_WAR);
29✔
1022

1023
    RAW = isl_union_map_coalesce(RAW);
29✔
1024
    WAW = isl_union_map_coalesce(WAW);
29✔
1025
    WAR = isl_union_map_coalesce(WAR);
29✔
1026
    RED = isl_union_map_coalesce(RED);
29✔
1027
    TC_RED = isl_union_map_coalesce(TC_RED);
29✔
1028
}
29✔
1029

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

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

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

1048
    isl_union_set_foreach_set(Universe, fix_set_to_zero, &Zero);
×
1049
    isl_union_set_free(Universe);
×
1050

1051
    isl_union_map *NonPositive = isl_union_set_lex_le_union_set(UDeltas, Zero);
×
1052

1053
    TC_RED = isl_union_map_subtract(TC_RED, NonPositive);
×
1054

1055
    TC_RED = isl_union_map_union(TC_RED, isl_union_map_reverse(isl_union_map_copy(TC_RED)));
×
1056
    TC_RED = isl_union_map_coalesce(TC_RED);
×
1057

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

1063
        *PrivMap = isl_union_map_apply_range(isl_union_map_copy(*Map), isl_union_map_copy(TC_RED));
×
1064
        *PrivMap =
×
1065
            isl_union_map_union(*PrivMap, isl_union_map_apply_range(isl_union_map_copy(TC_RED), isl_union_map_copy(*Map)));
×
1066

1067
        *Map = isl_union_map_union(*Map, *PrivMap);
×
1068
    }
×
1069
}
×
1070

1071
bool Dependences::is_parallel(isl_union_map *schedule, isl_pw_aff **min_distance_ptr) const {
×
1072
    isl_set *Deltas, *Distance;
×
1073
    isl_union_map *deps = this->dependences(TYPE_RAW | TYPE_WAR | TYPE_WAW);
×
1074
    isl_map *schedule_deps;
×
1075
    unsigned Dimension;
×
1076
    bool IsParallel;
×
1077

1078
    deps = isl_union_map_apply_range(deps, isl_union_map_copy(schedule));
×
1079
    deps = isl_union_map_apply_domain(deps, isl_union_map_copy(schedule));
×
1080

1081
    if (isl_union_map_is_empty(deps)) {
×
1082
        isl_union_map_free(deps);
×
1083
        return true;
×
1084
    }
×
1085

1086
    schedule_deps = isl_map_from_union_map(deps);
×
1087
    Dimension = isl_map_dim(schedule_deps, isl_dim_out) - 1;
×
1088

1089
    for (unsigned i = 0; i < Dimension; i++)
×
1090
        schedule_deps = isl_map_equate(schedule_deps, isl_dim_out, i, isl_dim_in, i);
×
1091

1092
    Deltas = isl_map_deltas(schedule_deps);
×
1093
    Distance = isl_set_universe(isl_set_get_space(Deltas));
×
1094

1095
    // [0, ..., 0, +] - All zeros and last dimension larger than zero
1096
    for (unsigned i = 0; i < Dimension; i++) Distance = isl_set_fix_si(Distance, isl_dim_set, i, 0);
×
1097

1098
    Distance = isl_set_lower_bound_si(Distance, isl_dim_set, Dimension, 1);
×
1099
    Distance = isl_set_intersect(Distance, Deltas);
×
1100

1101
    IsParallel = isl_set_is_empty(Distance);
×
1102
    if (IsParallel || !min_distance_ptr) {
×
1103
        isl_set_free(Distance);
×
1104
        return IsParallel;
×
1105
    }
×
1106

1107
    Distance = isl_set_project_out(Distance, isl_dim_set, 0, Dimension);
×
1108
    Distance = isl_set_coalesce(Distance);
×
1109

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

1115
    return false;
×
1116
}
×
1117

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

1121
    Deps = isl_union_map_apply_domain(Deps, isl_union_map_copy(Schedule));
2✔
1122
    Deps = isl_union_map_apply_range(Deps, isl_union_map_copy(Schedule));
2✔
1123

1124
    isl_space *ScheduleSpace = NULL;
2✔
1125
    isl_union_map_foreach_map(Schedule, get_map_range_space, &ScheduleSpace);
2✔
1126
    isl_union_map_free(Schedule);
2✔
1127

1128
    if (!ScheduleSpace) {
2✔
1129
        isl_union_map_free(Deps);
×
1130
        return true; /* Empty schedule considered valid/trivial */
×
1131
    }
×
1132

1133
    isl_set *Zero = isl_set_universe(isl_space_copy(ScheduleSpace));
2✔
1134
    int dims = isl_set_dim(Zero, isl_dim_set);
2✔
1135
    for (int i = 0; i < dims; ++i) Zero = isl_set_fix_si(Zero, isl_dim_set, i, 0);
4✔
1136

1137
    isl_union_set *UDeltas = isl_union_map_deltas(Deps);
2✔
1138
    isl_set *Deltas = isl_union_set_extract_set(UDeltas, ScheduleSpace);
2✔
1139
    isl_union_set_free(UDeltas);
2✔
1140

1141
    isl_space *Space = isl_set_get_space(Deltas);
2✔
1142
    isl_space *MapSpace = isl_space_map_from_set(isl_space_copy(Space));
2✔
1143
    isl_map *NonPositive = isl_map_universe(MapSpace);
2✔
1144

1145
    isl_multi_pw_aff *Identity = isl_multi_pw_aff_identity_on_domain_space(Space);
2✔
1146
    NonPositive = isl_map_lex_le_at_multi_pw_aff(NonPositive, Identity);
2✔
1147

1148
    NonPositive = isl_map_intersect_domain(NonPositive, Deltas);
2✔
1149
    NonPositive = isl_map_intersect_range(NonPositive, Zero);
2✔
1150

1151
    bool IsEmpty = isl_map_is_empty(NonPositive);
2✔
1152

1153
    isl_map_free(NonPositive);
2✔
1154
    return IsEmpty;
2✔
1155
}
2✔
1156

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

1161
    for (ScopStatement *Stmt : scop.statements()) {
1✔
1162
        isl_map *StmtScat;
1✔
1163

1164
        auto Lookup = new_schedule.find(Stmt);
1✔
1165
        if (Lookup == new_schedule.end()) {
1✔
1166
            StmtScat = isl_map_copy(Stmt->schedule());
×
1167
        } else {
1✔
1168
            StmtScat = isl_map_copy(Lookup->second);
1✔
1169
        }
1✔
1170

1171
        Schedule = isl_union_map_union(Schedule, isl_union_map_from_map(StmtScat));
1✔
1172
    }
1✔
1173

1174
    return check_validity(this, scop, Schedule);
1✔
1175
}
1✔
1176

1177
bool Dependences::is_valid(Scop &scop, isl_schedule *schedule) const {
1✔
1178
    return check_validity(this, scop, isl_schedule_get_map(schedule));
1✔
1179
}
1✔
1180

1181
ScopToSDFG::ScopToSDFG(Scop &scop, builder::StructuredSDFGBuilder &builder) : scop_(scop), builder_(builder) {
5✔
1182
    for (auto *stmt : scop_.statements()) {
5✔
1183
        stmt_map_[stmt->name()] = stmt;
5✔
1184
    }
5✔
1185
}
5✔
1186

1187
struct NameConnectInfo {
1188
    std::vector<std::string> names;
1189
};
1190

1191
static isl_stat collect_dim_names(isl_map *map, void *user) {
5✔
1192
    auto *info = static_cast<NameConnectInfo *>(user);
5✔
1193
    int dim = isl_map_dim(map, isl_dim_out);
5✔
1194
    if (dim > info->names.size()) {
5✔
1195
        info->names.resize(dim);
5✔
1196
    }
5✔
1197

1198
    isl_space *space = isl_map_get_space(map);
5✔
1199
    isl_local_space *ls = isl_local_space_from_space(space);
5✔
1200

1201
    for (int i = 0; i < dim; ++i) {
13✔
1202
        if (!info->names[i].empty()) {
8✔
1203
            continue;
×
1204
        }
×
1205

1206
        const char *name = isl_map_get_dim_name(map, isl_dim_out, i);
8✔
1207
        if (name) {
8✔
1208
            info->names[i] = name;
×
1209
            continue;
×
1210
        }
×
1211

1212
        // Try to recover from input dimensions
1213
        int in_dim = isl_map_dim(map, isl_dim_in);
8✔
1214
        for (int j = 0; j < in_dim; ++j) {
11✔
1215
            isl_constraint *c = isl_constraint_alloc_equality(isl_local_space_copy(ls));
11✔
1216
            c = isl_constraint_set_coefficient_si(c, isl_dim_out, i, 1);
11✔
1217
            c = isl_constraint_set_coefficient_si(c, isl_dim_in, j, -1);
11✔
1218

1219
            isl_basic_map *eq_bmap = isl_basic_map_from_constraint(c);
11✔
1220
            isl_map *eq_map = isl_map_from_basic_map(eq_bmap);
11✔
1221

1222
            bool is_subset = isl_map_is_subset(map, eq_map);
11✔
1223
            isl_map_free(eq_map);
11✔
1224

1225
            if (is_subset) {
11✔
1226
                const char *in_name = isl_map_get_dim_name(map, isl_dim_in, j);
8✔
1227
                if (in_name) {
8✔
1228
                    info->names[i] = in_name;
8✔
1229
                    break;
8✔
1230
                }
8✔
1231
            }
8✔
1232
        }
11✔
1233
    }
8✔
1234
    isl_local_space_free(ls);
5✔
1235
    isl_map_free(map);
5✔
1236
    return isl_stat_ok;
5✔
1237
}
5✔
1238

1239
void ScopToSDFG::build(analysis::AnalysisManager &analysis_manager) {
5✔
1240
    isl_ctx *ctx = scop_.ctx();
5✔
1241

1242
    // 1. Create AST Builder
1243
    isl_ast_build *ast_builder = isl_ast_build_alloc(ctx);
5✔
1244

1245
    // 2. Generate AST from Schedule
1246
    isl_schedule *schedule = isl_schedule_copy(scop_.schedule_tree());
5✔
1247
    isl_union_map *schedule_map = isl_union_map_copy(scop_.schedule());
5✔
1248

1249
    // Apply dimension names to schedule
1250
    NameConnectInfo info;
5✔
1251
    isl_union_map_foreach_map(schedule_map, collect_dim_names, &info);
5✔
1252
    isl_union_map_free(schedule_map);
5✔
1253

1254
    if (!info.names.empty()) {
5✔
1255
        isl_id_list *iterators = isl_id_list_alloc(ctx, info.names.size());
5✔
1256
        for (int i = 0; i < info.names.size(); ++i) {
13✔
1257
            std::string id_name = info.names[i].empty() ? "c" + std::to_string(i) : info.names[i];
8✔
1258
            iterators = isl_id_list_add(iterators, isl_id_alloc(ctx, id_name.c_str(), nullptr));
8✔
1259
        }
8✔
1260
        ast_builder = isl_ast_build_set_iterators(ast_builder, iterators);
5✔
1261
    }
5✔
1262

1263
    isl_ast_node *root_node = isl_ast_build_node_from_schedule(ast_builder, schedule);
5✔
1264

1265
    // 3. Start Traversal
1266
    auto &scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
5✔
1267
    auto parent_scope = scope_analysis.parent_scope(&scop_.node());
5✔
1268
    if (!parent_scope) {
5✔
1269
        return; // Cannot build SDFG without a parent scope
×
1270
    }
×
1271
    auto parent_sequence = static_cast<structured_control_flow::Sequence *>(parent_scope);
5✔
1272
    int index = parent_sequence->index(scop_.node());
5✔
1273
    auto &target_sequence = builder_.add_sequence_before(*parent_sequence, scop_.node(), {}, scop_.node().debug_info());
5✔
1274

1275
    visit_node(root_node, target_sequence);
5✔
1276
    builder_.remove_child(*parent_sequence, index + 1);
5✔
1277

1278
    isl_ast_node_free(root_node);
5✔
1279
    isl_ast_build_free(ast_builder);
5✔
1280
}
5✔
1281

1282
void ScopToSDFG::visit_node(isl_ast_node *node, structured_control_flow::Sequence &scope) {
13✔
1283
    if (!node) return;
13✔
1284

1285
    switch (isl_ast_node_get_type(node)) {
13✔
1286
        case isl_ast_node_for:
8✔
1287
            visit_for(node, scope);
8✔
1288
            break;
8✔
1289
        case isl_ast_node_if:
×
1290
            visit_if(node, scope);
×
1291
            break;
×
1292
        case isl_ast_node_block:
×
1293
            visit_block(node, scope);
×
1294
            break;
×
1295
        case isl_ast_node_user:
5✔
1296
            visit_user(node, scope);
5✔
1297
            break;
5✔
1298
        case isl_ast_node_mark: {
×
1299
            // Handle markers (e.g., parallelism annotations) here if needed
1300
            isl_ast_node *child = isl_ast_node_mark_get_node(node);
×
1301
            visit_node(child, scope);
×
1302
            isl_ast_node_free(child);
×
1303
            break;
×
1304
        }
×
1305
        default:
×
1306
            break;
×
1307
    }
13✔
1308
}
13✔
1309

1310
void ScopToSDFG::visit_for(isl_ast_node *node, structured_control_flow::Sequence &scope) {
8✔
1311
    // Extract Loop Components from ISL
1312
    isl_ast_expr *iterator = isl_ast_node_for_get_iterator(node);
8✔
1313
    isl_ast_expr *init = isl_ast_node_for_get_init(node);
8✔
1314
    isl_ast_expr *cond = isl_ast_node_for_get_cond(node);
8✔
1315
    isl_ast_expr *inc = isl_ast_node_for_get_inc(node);
8✔
1316
    isl_ast_node *body = isl_ast_node_for_get_body(node);
8✔
1317

1318
    // Convert to Symbolic
1319
    auto id = isl_ast_expr_get_id(iterator);
8✔
1320
    symbolic::Symbol sym_iter = symbolic::symbol(isl_id_get_name(id));
8✔
1321
    symbolic::Expression sym_init = convert_expr(init);
8✔
1322
    symbolic::Condition sym_cond = convert_cond(cond);
8✔
1323
    symbolic::Expression step = convert_expr(inc);
8✔
1324
    symbolic::Expression update_expr = symbolic::add(sym_iter, step);
8✔
1325

1326
    auto &loop = builder_.add_for(scope, sym_iter, sym_cond, sym_init, update_expr);
8✔
1327

1328
    // Recurse into body
1329
    visit_node(body, loop.root());
8✔
1330

1331
    isl_ast_expr_free(iterator);
8✔
1332
    isl_ast_expr_free(init);
8✔
1333
    isl_ast_expr_free(cond);
8✔
1334
    isl_ast_expr_free(inc);
8✔
1335
    isl_ast_node_free(body);
8✔
1336
    isl_id_free(id);
8✔
1337
}
8✔
1338

1339
void ScopToSDFG::visit_if(isl_ast_node *node, structured_control_flow::Sequence &scope) {
×
1340
    isl_ast_expr *cond = isl_ast_node_if_get_cond(node);
×
1341
    isl_ast_node *then_node = isl_ast_node_if_get_then(node);
×
1342
    isl_ast_node *else_node = isl_ast_node_if_get_else(node);
×
1343

1344
    auto &if_stmt = builder_.add_if_else(scope);
×
1345

1346
    // Then Block
1347
    symbolic::Condition sym_cond = convert_cond(cond);
×
1348
    auto &then_seq = builder_.add_case(if_stmt, sym_cond);
×
1349
    visit_node(then_node, then_seq);
×
1350

1351
    // Else Block (if exists)
1352
    if (else_node) {
×
1353
        auto &else_seq = builder_.add_case(if_stmt, symbolic::Not(sym_cond));
×
1354
        visit_node(else_node, else_seq);
×
1355
    }
×
1356

1357
    isl_ast_expr_free(cond);
×
1358
    isl_ast_node_free(then_node);
×
1359
    isl_ast_node_free(else_node);
×
1360
}
×
1361

1362
void ScopToSDFG::visit_block(isl_ast_node *node, structured_control_flow::Sequence &scope) {
×
1363
    isl_ast_node_list *list = isl_ast_node_block_get_children(node);
×
1364
    int n = isl_ast_node_list_n_ast_node(list);
×
1365

1366
    // Just iterate and append to current sequence
1367
    for (int i = 0; i < n; ++i) {
×
1368
        isl_ast_node *child = isl_ast_node_list_get_ast_node(list, i);
×
1369
        visit_node(child, scope);
×
1370
        isl_ast_node_free(child);
×
1371
    }
×
1372
    isl_ast_node_list_free(list);
×
1373
}
×
1374

1375
void ScopToSDFG::visit_user(isl_ast_node *node, structured_control_flow::Sequence &scope) {
5✔
1376
    isl_ast_expr *expr = isl_ast_node_user_get_expr(node);
5✔
1377
    // Usually 'expr' is a call operation: "StatementName(i, j)"
1378
    // The identifier of the operation is the statement name.
1379

1380
    // A more robust way to get tuple name directly from expr if it is an ID or Call:
1381
    isl_id *id = isl_ast_expr_get_id(expr); // If it's just an ID
5✔
1382
    if (!id && isl_ast_expr_get_type(expr) == isl_ast_expr_op) {
5✔
1383
        // It's likely a call: S(i, j)
1384
        // The first "arg" or the operation type might hold ID.
1385
        // Actually, isl_ast_expr_get_op_type(expr) == isl_ast_expr_op_call
1386
        isl_ast_expr *func = isl_ast_expr_get_op_arg(expr, 0);
5✔
1387
        id = isl_ast_expr_get_id(func);
5✔
1388
        isl_ast_expr_free(func);
5✔
1389
    }
5✔
1390

1391
    if (id) {
5✔
1392
        std::string name = isl_id_get_name(id);
5✔
1393
        if (stmt_map_.count(name)) {
5✔
1394
            ScopStatement *stmt = stmt_map_[name];
5✔
1395

1396
            if (!stmt->expression().is_null()) {
5✔
1397
                std::string rhs = (*stmt->writes().begin())->data();
1✔
1398
                builder_.add_block(scope, {{symbolic::symbol(rhs), stmt->expression()}});
1✔
1399
            } else {
4✔
1400
                auto &new_block = builder_.add_block(scope);
4✔
1401
                auto &new_code_node =
4✔
1402
                    static_cast<data_flow::CodeNode &>(builder_.copy_node(new_block, *stmt->code_node()));
4✔
1403

1404
                // Create Access Node Mapping
1405
                auto &old_graph = stmt->code_node()->get_parent();
4✔
1406
                auto &new_graph = new_code_node.get_parent();
4✔
1407
                std::unordered_map<const data_flow::DataFlowNode *, data_flow::DataFlowNode *> in_node_map;
4✔
1408
                for (auto &iedge : old_graph.in_edges(*stmt->code_node())) {
4✔
1409
                    if (in_node_map.find(&iedge.src()) == in_node_map.end()) {
4✔
1410
                        in_node_map[&iedge.src()] = &builder_.copy_node(new_block, iedge.src());
4✔
1411
                    }
4✔
1412
                    builder_.add_memlet(
4✔
1413
                        new_block,
4✔
1414
                        *in_node_map[&iedge.src()],
4✔
1415
                        iedge.src_conn(),
4✔
1416
                        new_code_node,
4✔
1417
                        iedge.dst_conn(),
4✔
1418
                        iedge.subset(),
4✔
1419
                        iedge.base_type(),
4✔
1420
                        iedge.debug_info()
4✔
1421
                    );
4✔
1422
                }
4✔
1423

1424
                std::unordered_map<const data_flow::DataFlowNode *, data_flow::DataFlowNode *> out_node_map;
4✔
1425
                for (auto &oedge : old_graph.out_edges(*stmt->code_node())) {
4✔
1426
                    if (out_node_map.find(&oedge.dst()) == out_node_map.end()) {
4✔
1427
                        out_node_map[&oedge.dst()] = &builder_.copy_node(new_block, oedge.dst());
4✔
1428
                    }
4✔
1429
                    builder_.add_memlet(
4✔
1430
                        new_block,
4✔
1431
                        new_code_node,
4✔
1432
                        oedge.src_conn(),
4✔
1433
                        *out_node_map[&oedge.dst()],
4✔
1434
                        oedge.dst_conn(),
4✔
1435
                        oedge.subset(),
4✔
1436
                        oedge.base_type(),
4✔
1437
                        oedge.debug_info()
4✔
1438
                    );
4✔
1439
                }
4✔
1440
            }
4✔
1441
        }
5✔
1442
        isl_id_free(id);
5✔
1443
    }
5✔
1444
    isl_ast_expr_free(expr);
5✔
1445
}
5✔
1446

1447
symbolic::Expression ScopToSDFG::convert_expr(isl_ast_expr *expr) {
32✔
1448
    if (!expr) return SymEngine::null;
32✔
1449

1450
    isl_ast_expr_type type = isl_ast_expr_get_type(expr);
32✔
1451
    if (type == isl_ast_expr_int) {
32✔
1452
        isl_val *v = isl_ast_expr_get_val(expr);
18✔
1453
        long val = isl_val_get_num_si(v);
18✔
1454
        isl_val_free(v);
18✔
1455
        return symbolic::integer(val);
18✔
1456
    } else if (type == isl_ast_expr_id) {
18✔
1457
        isl_id *id = isl_ast_expr_get_id(expr);
14✔
1458
        std::string name = isl_id_get_name(id);
14✔
1459
        isl_id_free(id);
14✔
1460
        return symbolic::symbol(name);
14✔
1461
    } else if (type == isl_ast_expr_op) {
14✔
1462
        isl_ast_op_type op = isl_ast_expr_get_op_type(expr);
×
1463
        int n_args = isl_ast_expr_get_op_n_arg(expr);
×
1464

1465
        if (n_args == 1) {
×
1466
            isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
×
1467
            symbolic::Expression res;
×
1468
            if (op == isl_ast_op_minus) {
×
1469
                res = symbolic::mul(symbolic::integer(-1), convert_expr(arg0));
×
1470
            }
×
1471
            isl_ast_expr_free(arg0);
×
1472
            return res;
×
1473
        }
×
1474

1475
        if (n_args == 2) {
×
1476
            isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
×
1477
            isl_ast_expr *arg1 = isl_ast_expr_get_op_arg(expr, 1);
×
1478
            symbolic::Expression s0 = convert_expr(arg0);
×
1479
            symbolic::Expression s1 = convert_expr(arg1);
×
1480
            isl_ast_expr_free(arg0);
×
1481
            isl_ast_expr_free(arg1);
×
1482

1483
            switch (op) {
×
1484
                case isl_ast_op_add:
×
1485
                    return symbolic::add(s0, s1);
×
1486
                case isl_ast_op_sub:
×
1487
                    return symbolic::sub(s0, s1);
×
1488
                case isl_ast_op_mul:
×
1489
                    return symbolic::mul(s0, s1);
×
1490
                case isl_ast_op_div:
×
1491
                case isl_ast_op_pdiv_q:
×
1492
                case isl_ast_op_fdiv_q:
×
1493
                    return symbolic::div(s0, s1);
×
1494
                case isl_ast_op_pdiv_r:
×
1495
                case isl_ast_op_zdiv_r:
×
1496
                    return symbolic::mod(s0, s1);
×
1497
                case isl_ast_op_min:
×
1498
                    return symbolic::min(s0, s1);
×
1499
                case isl_ast_op_max:
×
1500
                    return symbolic::max(s0, s1);
×
1501
                default:
×
1502
                    break;
×
1503
            }
×
1504
        }
×
1505
    }
×
1506
    return SymEngine::null;
×
1507
}
32✔
1508

1509
symbolic::Condition ScopToSDFG::convert_cond(isl_ast_expr *expr) {
8✔
1510
    if (!expr) return SymEngine::null;
8✔
1511

1512
    if (isl_ast_expr_get_type(expr) != isl_ast_expr_op) {
8✔
1513
        return SymEngine::null;
×
1514
    }
×
1515

1516
    isl_ast_op_type op = isl_ast_expr_get_op_type(expr);
8✔
1517
    int n_args = isl_ast_expr_get_op_n_arg(expr);
8✔
1518

1519
    if (n_args == 2) {
8✔
1520
        isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
8✔
1521
        isl_ast_expr *arg1 = isl_ast_expr_get_op_arg(expr, 1);
8✔
1522

1523
        symbolic::Condition res;
8✔
1524

1525
        switch (op) {
8✔
1526
            case isl_ast_op_eq:
×
1527
                res = symbolic::Eq(convert_expr(arg0), convert_expr(arg1));
×
1528
                break;
×
1529
            case isl_ast_op_lt:
6✔
1530
                res = symbolic::Lt(convert_expr(arg0), convert_expr(arg1));
6✔
1531
                break;
6✔
1532
            case isl_ast_op_le:
2✔
1533
                res = symbolic::Le(convert_expr(arg0), convert_expr(arg1));
2✔
1534
                break;
2✔
1535
            case isl_ast_op_gt:
×
1536
                res = symbolic::Gt(convert_expr(arg0), convert_expr(arg1));
×
1537
                break;
×
1538
            case isl_ast_op_ge:
×
1539
                res = symbolic::Ge(convert_expr(arg0), convert_expr(arg1));
×
1540
                break;
×
1541
            case isl_ast_op_and:
×
1542
            case isl_ast_op_and_then:
×
1543
                res = symbolic::And(convert_cond(arg0), convert_cond(arg1));
×
1544
                break;
×
1545
            case isl_ast_op_or:
×
1546
            case isl_ast_op_or_else:
×
1547
                res = symbolic::Or(convert_cond(arg0), convert_cond(arg1));
×
1548
                break;
×
1549
            default:
×
1550
                break;
×
1551
        }
8✔
1552

1553
        isl_ast_expr_free(arg0);
8✔
1554
        isl_ast_expr_free(arg1);
8✔
1555
        return res;
8✔
1556
    }
8✔
1557

1558
    return SymEngine::null;
×
1559
}
8✔
1560

1561
ScopAnalysis::ScopAnalysis(StructuredSDFG &sdfg) : Analysis(sdfg) {}
×
1562

1563
void ScopAnalysis::run(analysis::AnalysisManager &analysis_manager) {
×
1564
    this->scops_.clear();
×
1565
    this->dependences_.clear();
×
1566

1567
    auto &loop_analysis = analysis_manager.get<LoopAnalysis>();
×
1568
    for (auto &loop : loop_analysis.loops()) {
×
1569
        if (!dynamic_cast<structured_control_flow::StructuredLoop *>(loop)) {
×
1570
            continue;
×
1571
        }
×
1572
        auto sloop = static_cast<structured_control_flow::StructuredLoop *>(loop);
×
1573

1574
        ScopBuilder builder(this->sdfg_, *sloop);
×
1575
        auto scop = builder.build(analysis_manager);
×
1576
        if (!scop) {
×
1577
            continue;
×
1578
        }
×
1579
        auto dependences = std::make_unique<Dependences>(*scop);
×
1580
        if (!dependences->has_valid_dependences()) {
×
1581
            continue;
×
1582
        }
×
1583

1584
        this->scops_[loop] = std::move(scop);
×
1585
        this->dependences_[loop] = std::move(dependences);
×
1586
    }
×
1587
}
×
1588

1589
bool ScopAnalysis::has(const structured_control_flow::ControlFlowNode *node) const {
×
1590
    return this->scops_.find(node) != this->scops_.end();
×
1591
}
×
1592

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

1595
const Dependences &ScopAnalysis::dependences(const structured_control_flow::ControlFlowNode *node) const {
×
1596
    return *this->dependences_.at(node);
×
1597
}
×
1598

1599
} // namespace analysis
1600
} // 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