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

daisytuner / sdfglib / 21120766626

18 Jan 2026 11:50PM UTC coverage: 64.513% (-0.01%) from 64.525%
21120766626

Pull #464

github

web-flow
Merge c6a1f9399 into e349c5a52
Pull Request #464: adds validation checks

9 of 17 new or added lines in 3 files covered. (52.94%)

3 existing lines in 1 file now uncovered.

19797 of 30687 relevant lines covered (64.51%)

390.36 hits per line

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

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

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

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

64
    return domain;
45✔
65
}
45✔
66

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

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

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

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

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

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

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

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

123
    return this->schedule_tree_;
40✔
124
}
53✔
125

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

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

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

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

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

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

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

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

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

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

236
void ScopBuilder::visit_block(analysis::AnalysisManager &analysis_manager, structured_control_flow::Block &block) {
53✔
237
    auto &assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
53✔
238
    auto &assumptions = assumptions_analysis.get(block, true);
53✔
239

240
    auto &graph = block.dataflow();
53✔
241
    for (auto node : graph.topological_sort()) {
165✔
242
        if (auto code_node = dynamic_cast<data_flow::CodeNode *>(node)) {
165✔
243
            if (dynamic_cast<data_flow::LibraryNode *>(code_node)) {
52✔
244
                if (!dynamic_cast<math::cmath::CMathNode *>(code_node)) {
×
245
                    this->scop_ = nullptr;
×
246
                    return;
×
247
                }
×
248
            }
×
249

250
            isl_set *domain = isl_set_universe(isl_space_set_alloc(scop_->ctx(), 0, 0));
52✔
251
            auto statement =
52✔
252
                std::make_unique<ScopStatement>("S_" + std::to_string(code_node->element_id()), domain, code_node);
52✔
253

254
            // Add reads
255
            for (auto &iedge : graph.in_edges(*node)) {
62✔
256
                if (dynamic_cast<data_flow::ConstantNode *>(&iedge.src())) {
62✔
257
                    continue;
7✔
258
                }
7✔
259

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

274
            // Add writes
275
            for (auto &oedge : graph.out_edges(*node)) {
52✔
276
                AccessType access_type = AccessType::WRITE;
52✔
277

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

292
            this->scop_->insert(statement);
52✔
293
        }
52✔
294
    }
165✔
295
}
53✔
296

297
void ScopBuilder::
298
    visit_structured_loop(analysis::AnalysisManager &analysis_manager, structured_control_flow::StructuredLoop &loop) {
60✔
299
    // Add domain for the statements inside the loop
300
    std::string indvar = loop.indvar()->__str__();
60✔
301
    this->dimensions_.push_back(indvar);
60✔
302

303
    std::string init = loop.init()->__str__();
60✔
304
    symbolic::CNF condition_cnf;
60✔
305
    try {
60✔
306
        condition_cnf = symbolic::conjunctive_normal_form(loop.condition());
60✔
307
    } catch (symbolic::CNFException &e) {
60✔
308
        this->scop_ = nullptr;
×
309
        return;
×
310
    }
×
311
    std::vector<std::string> constraints;
60✔
312
    constraints.push_back(indvar + " >= " + init);
60✔
313
    for (auto &clause : condition_cnf) {
61✔
314
        if (clause.size() != 1) {
61✔
315
            this->scop_ = nullptr;
×
316
            return;
×
317
        }
×
318
        for (auto &literal : clause) {
61✔
319
            for (auto &sym : symbolic::atoms(literal)) {
110✔
320
                if (sym->get_name() == indvar) {
110✔
321
                    continue;
61✔
322
                }
61✔
323
                if (this->dimension_constraints_.find(sym->get_name()) != this->dimension_constraints_.end()) {
49✔
324
                    continue;
5✔
325
                }
5✔
326
                if (std::find(this->parameters_.begin(), this->parameters_.end(), sym->get_name()) ==
44✔
327
                    this->parameters_.end()) {
44✔
328
                    this->parameters_.push_back(sym->get_name());
39✔
329
                }
39✔
330
            }
44✔
331

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

355
    // Construct constraints string.
356
    std::string set_str;
60✔
357
    if (this->dimensions_.size() > 1 || !this->parameters_.empty()) {
60✔
358
        std::vector<std::string> params = this->parameters_;
49✔
359
        for (int i = 0; i < this->dimensions_.size() - 1; ++i) {
67✔
360
            params.push_back(this->dimensions_[i]);
18✔
361
        }
18✔
362
        set_str += "[ " + helpers::join(params, ", ") + " ] -> ";
49✔
363
    }
49✔
364
    set_str += "{ [" + indvar + "] : " + condition + " }";
60✔
365

366
    isl_ctx *ctx = scop_->ctx();
60✔
367
    isl_set *loop_bound_base = isl_set_read_from_str(ctx, set_str.c_str());
60✔
368
    if (!loop_bound_base) {
60✔
369
        this->scop_ = nullptr;
×
370
        return;
×
371
    }
×
372
    char *loop_bound_str = isl_set_to_str(loop_bound_base);
60✔
373
    free(loop_bound_str);
60✔
374

375
    size_t stmt_start = this->scop_->statements().size();
60✔
376
    this->visit_sequence(analysis_manager, loop.root());
60✔
377
    if (this->scop_ == nullptr) {
60✔
378
        return;
×
379
    }
×
380

381
    auto stmts = this->scop_->statements();
60✔
382
    for (size_t i = stmt_start; i < stmts.size(); ++i) {
131✔
383
        ScopStatement *stmt = stmts[i];
71✔
384
        stmt->push_front(loop.indvar());
71✔
385

386
        isl_set *domain = isl_set_copy(stmt->domain());
71✔
387

388
        // Insert indvar dimension at 0
389
        domain = isl_set_insert_dims(domain, isl_dim_set, 0, 1);
71✔
390
        domain = isl_set_set_dim_name(domain, isl_dim_set, 0, indvar.c_str());
71✔
391

392
        // Equate parameter with loop dimension if it exists and project it out
393
        int param_idx = isl_set_find_dim_by_name(domain, isl_dim_param, indvar.c_str());
71✔
394
        if (param_idx >= 0) {
71✔
395
            isl_space *space = isl_set_get_space(domain);
19✔
396
            isl_local_space *ls = isl_local_space_from_space(space);
19✔
397
            isl_constraint *c = isl_constraint_alloc_equality(ls);
19✔
398
            c = isl_constraint_set_coefficient_si(c, isl_dim_set, 0, 1);
19✔
399
            c = isl_constraint_set_coefficient_si(c, isl_dim_param, param_idx, -1);
19✔
400
            domain = isl_set_add_constraint(domain, c);
19✔
401
            domain = isl_set_project_out(domain, isl_dim_param, param_idx, 1);
19✔
402
        }
19✔
403

404
        // Prepare loop bound set with compatible dimensions
405
        isl_set *loop_bound = isl_set_copy(loop_bound_base);
71✔
406
        int n_dims = isl_set_dim(domain, isl_dim_set);
71✔
407
        int inner_dims = n_dims - 1;
71✔
408

409
        if (inner_dims > 0) {
71✔
410
            loop_bound = isl_set_add_dims(loop_bound, isl_dim_set, inner_dims);
19✔
411

412
            // Match names for inner dimensions
413
            for (int j = 0; j < inner_dims; ++j) {
38✔
414
                // The inner dims in domain are at indices 1 + j
415
                const char *name = isl_set_get_dim_name(domain, isl_dim_set, 1 + j);
19✔
416
                if (name) {
19✔
417
                    loop_bound = isl_set_set_dim_name(loop_bound, isl_dim_set, 1 + j, name);
19✔
418
                }
19✔
419
            }
19✔
420
        }
19✔
421

422
        domain = isl_set_intersect(domain, loop_bound);
71✔
423
        stmt->set_domain(domain);
71✔
424

425
        // Update Schedule
426
        isl_map *schedule = isl_map_copy(stmt->schedule());
71✔
427

428
        // Insert dimension in input (domain)
429
        schedule = isl_map_insert_dims(schedule, isl_dim_in, 0, 1);
71✔
430
        schedule = isl_map_set_dim_name(schedule, isl_dim_in, 0, indvar.c_str());
71✔
431

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

444
        // Insert dimension in output (range/time)
445
        schedule = isl_map_insert_dims(schedule, isl_dim_out, 0, 1);
71✔
446
        schedule = isl_map_set_dim_name(schedule, isl_dim_out, 0, indvar.c_str());
71✔
447

448
        // Equate input[0] and output[0]
449
        schedule = isl_map_equate(schedule, isl_dim_in, 0, isl_dim_out, 0);
71✔
450

451
        stmt->set_schedule(schedule);
71✔
452

453
        for (auto access : stmt->accesses()) {
149✔
454
            isl_map *relation = isl_map_copy(access->relation());
149✔
455

456
            int dom_n = isl_set_dim(stmt->domain(), isl_dim_set);
149✔
457
            int rel_n_in = isl_map_dim(relation, isl_dim_in);
149✔
458

459
            if (rel_n_in < dom_n) {
149✔
460
                relation = isl_map_insert_dims(relation, isl_dim_in, 0, 1);
2✔
461
                relation = isl_map_set_dim_name(relation, isl_dim_in, 0, indvar.c_str());
2✔
462

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

476
            relation = isl_map_set_tuple_name(relation, isl_dim_in, stmt->name().c_str());
149✔
477
            access->set_relation(relation);
149✔
478
        }
149✔
479
    }
71✔
480
    isl_set_free(loop_bound_base);
60✔
481

482
    this->dimensions_.pop_back();
60✔
483
}
60✔
484

485
std::string ScopBuilder::generate_subset(
486
    analysis::AnalysisManager &analysis_manager,
487
    structured_control_flow::ControlFlowNode &node,
488
    const symbolic::MultiExpression &subset
489
) {
107✔
490
    std::string relation = "";
107✔
491
    if (!this->parameters_.empty()) {
107✔
492
        relation += "[ " + helpers::join(this->parameters_, ", ") + " ] -> ";
84✔
493
    }
84✔
494
    relation += "{ [";
107✔
495
    relation += helpers::join(this->dimensions_, ", ");
107✔
496
    relation += "] -> [";
107✔
497
    if (subset.empty()) {
107✔
498
        relation += "0";
13✔
499
    } else {
94✔
500
        for (size_t i = 0; i < subset.size(); ++i) {
214✔
501
            if (i > 0) {
120✔
502
                relation += ", ";
26✔
503
            }
26✔
504
            relation += subset.at(i)->__str__();
120✔
505
        }
120✔
506
    }
94✔
507
    relation += "] }";
107✔
508

509
    return relation;
107✔
510
}
107✔
511

512
static isl_map *tag(isl_map *Relation, isl_id *TagId) {
176✔
513
    isl_space *Space = isl_map_get_space(Relation);
176✔
514
    Space = isl_space_drop_dims(Space, isl_dim_out, 0, isl_map_dim(Relation, isl_dim_out));
176✔
515
    Space = isl_space_set_tuple_id(Space, isl_dim_out, TagId);
176✔
516
    isl_multi_aff *Tag = isl_multi_aff_domain_map(Space);
176✔
517
    Relation = isl_map_preimage_domain_multi_aff(Relation, Tag);
176✔
518
    return Relation;
176✔
519
}
176✔
520

521
static void collectInfo(
522
    Scop &S,
523
    isl_union_map *&Read,
524
    isl_union_map *&MustWrite,
525
    isl_union_map *&ReductionTagMap,
526
    isl_union_set *&TaggedStmtDomain
527
) {
33✔
528
    isl_space *Space = isl_space_copy(S.param_space());
33✔
529
    Read = isl_union_map_empty(isl_space_copy(Space));
33✔
530
    MustWrite = isl_union_map_empty(isl_space_copy(Space));
33✔
531
    ReductionTagMap = isl_union_map_empty(isl_space_copy(Space));
33✔
532
    isl_union_map *StmtSchedule = isl_union_map_empty(isl_space_copy(Space));
33✔
533

534
    std::unordered_set<std::string> ReductionArrays;
33✔
535
    for (auto stmt : S.statements())
33✔
536
        for (MemoryAccess *MA : stmt->accesses())
42✔
537
            if (MA->is_reduction_like()) ReductionArrays.insert(MA->data());
88✔
538

539
    for (auto stmt : S.statements()) {
42✔
540
        for (MemoryAccess *MA : stmt->accesses()) {
88✔
541
            isl_set *domcp = isl_set_copy(stmt->domain());
88✔
542
            isl_map *accdom = isl_map_copy(MA->relation());
88✔
543
            accdom = isl_map_set_tuple_name(accdom, isl_dim_out, MA->data().c_str());
88✔
544

545
            accdom = isl_map_intersect_domain(accdom, domcp);
88✔
546

547

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

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

570
                isl_id *id = isl_id_alloc(S.ctx(), MA->data().c_str(), MA);
88✔
571
                accdom = tag(accdom, isl_id_copy(id));
88✔
572
                isl_map *Schedule = tag(StmtScheduleMap, id);
88✔
573
                StmtSchedule = isl_union_map_add_map(StmtSchedule, Schedule);
88✔
574
            }
88✔
575

576
            if (MA->access_type() == AccessType::READ) {
88✔
577
                Read = isl_union_map_add_map(Read, accdom);
46✔
578
            } else {
46✔
579
                MustWrite = isl_union_map_add_map(MustWrite, accdom);
42✔
580
            }
42✔
581
        }
88✔
582
    }
42✔
583

584
    StmtSchedule = isl_union_map_intersect_params(StmtSchedule, isl_set_universe(isl_space_copy(Space)));
33✔
585
    TaggedStmtDomain = isl_union_map_domain(StmtSchedule);
33✔
586

587
    ReductionTagMap = isl_union_map_coalesce(ReductionTagMap);
33✔
588
    Read = isl_union_map_coalesce(Read);
33✔
589
    MustWrite = isl_union_map_coalesce(MustWrite);
33✔
590

591
    isl_space_free(Space);
33✔
592
}
33✔
593

594
static isl_union_flow *
595
buildFlow(isl_union_map *Snk, isl_union_map *Src, isl_union_map *MaySrc, isl_union_map *Kill, isl_schedule *Schedule) {
132✔
596
    isl_union_access_info *AI;
132✔
597

598
    AI = isl_union_access_info_from_sink(isl_union_map_copy(Snk));
132✔
599
    if (MaySrc) AI = isl_union_access_info_set_may_source(AI, isl_union_map_copy(MaySrc));
132✔
600
    if (Src) AI = isl_union_access_info_set_must_source(AI, isl_union_map_copy(Src));
132✔
601
    if (Kill) AI = isl_union_access_info_set_kill(AI, isl_union_map_copy(Kill));
132✔
602
    AI = isl_union_access_info_set_schedule(AI, isl_schedule_copy(Schedule));
132✔
603
    auto Flow = isl_union_access_info_compute_flow(AI);
132✔
604
    return Flow;
132✔
605
}
132✔
606

607
static isl_stat get_map_range_space(isl_map *Map, void *User) {
2✔
608
    isl_space **Space = (isl_space **) User;
2✔
609
    if (*Space) {
2✔
610
        isl_map_free(Map);
×
611
        return isl_stat_ok;
×
612
    }
×
613
    *Space = isl_space_range(isl_map_get_space(Map));
2✔
614
    isl_map_free(Map);
2✔
615
    return isl_stat_ok;
2✔
616
}
2✔
617

618
static isl_stat fix_set_to_zero(isl_set *Set, void *User) {
×
619
    isl_union_set **UserUS = (isl_union_set **) User;
×
620

621
    int dims = isl_set_dim(Set, isl_dim_set);
×
622
    for (int i = 0; i < dims; ++i) {
×
623
        Set = isl_set_fix_si(Set, isl_dim_set, i, 0);
×
624
    }
×
625

626
    *UserUS = isl_union_set_union(*UserUS, isl_union_set_from_set(Set));
×
627
    return isl_stat_ok;
×
628
}
×
629

630
isl_union_map *Dependences::dependences(int Kinds) const {
27✔
631
    assert(has_valid_dependences() && "No valid dependences available");
27✔
632
    isl_space *Space = isl_union_map_get_space(RAW);
27✔
633
    isl_union_map *Deps = isl_union_map_empty(Space);
27✔
634

635
    if (Kinds & TYPE_RAW) Deps = isl_union_map_union(Deps, isl_union_map_copy(RAW));
27✔
636

637
    if (Kinds & TYPE_WAR) Deps = isl_union_map_union(Deps, isl_union_map_copy(WAR));
27✔
638

639
    if (Kinds & TYPE_WAW) Deps = isl_union_map_union(Deps, isl_union_map_copy(WAW));
27✔
640

641
    if (Kinds & TYPE_RED) Deps = isl_union_map_union(Deps, isl_union_map_copy(RED));
27✔
642

643
    if (Kinds & TYPE_TC_RED) Deps = isl_union_map_union(Deps, isl_union_map_copy(TC_RED));
27✔
644

645
    Deps = isl_union_map_coalesce(Deps);
27✔
646
    Deps = isl_union_map_detect_equalities(Deps);
27✔
647
    return Deps;
27✔
648
}
27✔
649

650
struct DepCallbackInfo {
651
    std::unordered_map<std::string, analysis::LoopCarriedDependency> *deps;
652
    analysis::LoopCarriedDependency type;
653
    const sdfg::structured_control_flow::StructuredLoop *loop;
654
};
655

656
static isl_stat collect_deps(isl_map *bmap, void *user) {
156✔
657
    auto *info = static_cast<DepCallbackInfo *>(user);
156✔
658

659
    if (info->loop) {
156✔
660
        bool exists_for_dim = false;
156✔
661
        std::string indvar_name = info->loop->indvar()->get_name();
156✔
662

663
        isl_map *map_for_deltas = isl_map_copy(bmap);
156✔
664
        isl_space *map_space = isl_map_get_space(map_for_deltas);
156✔
665
        isl_space *dom_space = isl_space_domain(isl_space_copy(map_space));
156✔
666
        if (isl_space_is_wrapping(dom_space)) {
156✔
667
            map_for_deltas = isl_map_domain_factor_domain(map_for_deltas);
80✔
668
        }
80✔
669
        isl_space_free(dom_space);
156✔
670

671
        isl_space *ran_space = isl_space_range(map_space);
156✔
672
        if (isl_space_is_wrapping(ran_space)) {
156✔
673
            map_for_deltas = isl_map_range_factor_domain(map_for_deltas);
80✔
674
        }
80✔
675
        isl_space_free(ran_space);
156✔
676

677
        isl_space *map_space_final = isl_map_get_space(map_for_deltas);
156✔
678
        isl_space *domain_space = isl_space_domain(isl_space_copy(map_space_final));
156✔
679
        isl_space *range_space = isl_space_range(isl_space_copy(map_space_final));
156✔
680
        isl_space_free(map_space_final);
156✔
681

682
        if (!isl_space_is_equal(domain_space, range_space)) {
156✔
683
            isl_space_free(domain_space);
78✔
684
            isl_space_free(range_space);
78✔
685
            isl_map_free(map_for_deltas);
78✔
686
            isl_map_free(bmap);
78✔
687
            return isl_stat_ok;
78✔
688
        }
78✔
689
        isl_space_free(domain_space);
78✔
690
        isl_space_free(range_space);
78✔
691

692
        isl_set *deltas = isl_map_deltas(map_for_deltas);
78✔
693
        int dims = isl_set_dim(deltas, isl_dim_set);
78✔
694
        int dim_idx = -1;
78✔
695

696
        for (int i = 0; i < dims; ++i) {
107✔
697
            const char *name = isl_set_get_dim_name(deltas, isl_dim_set, i);
103✔
698
            if (name && indvar_name == name) {
103✔
699
                dim_idx = i;
74✔
700
                break;
74✔
701
            }
74✔
702
        }
103✔
703

704
        if (dim_idx != -1) {
78✔
705
            // Check if there are any dependencies carried by this dimension.
706
            // This means we check if there exists a dependence vector d such that:
707
            // d[0...dim_idx-1] = 0 AND d[dim_idx] != 0
708

709
            for (int i = 0; i < dim_idx; ++i) {
99✔
710
                deltas = isl_set_fix_si(deltas, isl_dim_set, i, 0);
25✔
711
            }
25✔
712

713
            isl_set *zero_at_dim = isl_set_universe(isl_set_get_space(deltas));
74✔
714
            zero_at_dim = isl_set_fix_si(zero_at_dim, isl_dim_set, dim_idx, 0);
74✔
715

716
            if (!isl_set_is_subset(deltas, zero_at_dim)) {
74✔
717
                exists_for_dim = true;
62✔
718
            }
62✔
719
            isl_set_free(zero_at_dim);
74✔
720
        }
74✔
721
        isl_set_free(deltas);
78✔
722

723
        if (!exists_for_dim) {
78✔
724
            isl_map_free(bmap);
16✔
725
            return isl_stat_ok;
16✔
726
        }
16✔
727
    }
78✔
728

729
    isl_space *space = isl_map_get_space(bmap);
62✔
730
    isl_space *domain_space = isl_space_domain(space);
62✔
731

732
    if (isl_space_is_wrapping(domain_space)) {
62✔
733
        isl_space *domain_space_copy = isl_space_copy(domain_space);
31✔
734
        isl_space *unwrapped = isl_space_unwrap(domain_space_copy);
31✔
735
        isl_space *range = isl_space_range(unwrapped);
31✔
736

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

754
    isl_space_free(domain_space);
62✔
755
    isl_map_free(bmap);
62✔
756
    return isl_stat_ok;
62✔
757
}
156✔
758

759
std::unordered_map<std::string, analysis::LoopCarriedDependency> Dependences::
760
    dependencies(const sdfg::structured_control_flow::StructuredLoop &loop) const {
30✔
761
    std::unordered_map<std::string, analysis::LoopCarriedDependency> deps;
30✔
762
    DepCallbackInfo info;
30✔
763
    info.deps = &deps;
30✔
764
    info.loop = &loop;
30✔
765

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

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

776
    if (WAR) {
30✔
777
        info.type = analysis::LoopCarriedDependency::LOOP_CARRIED_DEPENDENCY_READ_WRITE;
30✔
778
        isl_union_map_foreach_map(WAR, collect_deps, &info);
30✔
779
    }
30✔
780

781
    return deps;
30✔
782
}
30✔
783

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

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

788
void Dependences::set_reduction_dependences(MemoryAccess *memory_access, isl_map *deps) {
×
789
    reduction_dependences_[memory_access] = deps;
×
790
}
×
791

792
void Dependences::calculate_dependences(Scop &S) {
33✔
793
    isl_union_map *Read, *MustWrite, *ReductionTagMap;
33✔
794
    isl_union_set *TaggedStmtDomain;
33✔
795
    collectInfo(S, Read, MustWrite, ReductionTagMap, TaggedStmtDomain);
33✔
796

797
    isl_schedule *Schedule = isl_schedule_copy(S.schedule_tree());
33✔
798

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

813
        // Compute an identity map from each statement in domain to itself.
814
        // IdentityTags = { [Stmt[i] -> Stmt[i] }
815
        isl_union_map *IdentityMap = isl_union_set_identity(isl_union_set_copy(TaggedStmtDomain));
×
816
        isl_union_pw_multi_aff *IdentityTags = isl_union_pw_multi_aff_from_union_map(IdentityMap);
×
817

818
        isl_union_pw_multi_aff *Tags = isl_union_pw_multi_aff_union_add(ReductionTags, IdentityTags);
×
819

820
        // By pulling back Tags from Schedule, we have a schedule tree that can
821
        // be used to compute normal dependences, as well as 'tagged' reduction
822
        // dependences.
823
        Schedule = isl_schedule_pullback_union_pw_multi_aff(Schedule, Tags);
×
824
    }
×
825

826
    isl_union_map *StrictWAW = nullptr;
33✔
827
    {
33✔
828
        RAW = WAW = WAR = RED = nullptr;
33✔
829
        isl_union_map *Write = isl_union_map_copy(MustWrite);
33✔
830

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

890
        isl_union_flow *Flow = buildFlow(Write, Write, Read, nullptr, Schedule);
33✔
891
        StrictWAW = isl_union_flow_get_must_dependence(Flow);
33✔
892
        isl_union_flow_free(Flow);
33✔
893

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

898
        Flow = buildFlow(Write, nullptr, Read, nullptr, Schedule);
33✔
899
        WAR = isl_union_flow_get_may_dependence(Flow);
33✔
900
        isl_union_flow_free(Flow);
33✔
901

902
        Flow = buildFlow(Write, nullptr, Write, nullptr, Schedule);
33✔
903
        WAW = isl_union_flow_get_may_dependence(Flow);
33✔
904
        isl_union_flow_free(Flow);
33✔
905

906
        isl_union_map_free(Write);
33✔
907
        isl_union_map_free(MustWrite);
33✔
908
        isl_union_map_free(Read);
33✔
909
        isl_schedule_free(Schedule);
33✔
910

911
        RAW = isl_union_map_coalesce(RAW);
33✔
912
        WAW = isl_union_map_coalesce(WAW);
33✔
913
        WAR = isl_union_map_coalesce(WAR);
33✔
914

915
        // End of max_operations scope.
916
    }
33✔
917

918
    isl_union_map *STMT_RAW =
33✔
919
        isl_union_map_intersect_domain(isl_union_map_copy(RAW), isl_union_set_copy(TaggedStmtDomain));
33✔
920
    isl_union_map *STMT_WAW =
33✔
921
        isl_union_map_intersect_domain(isl_union_map_copy(WAW), isl_union_set_copy(TaggedStmtDomain));
33✔
922
    isl_union_map *STMT_WAR =
33✔
923
        isl_union_map_intersect_domain(isl_union_map_copy(WAR), isl_union_set_copy(TaggedStmtDomain));
33✔
924
    isl_union_set_free(TaggedStmtDomain);
33✔
925

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

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

953
    // Step 2)
954
    RED = isl_union_map_intersect(RED, isl_union_map_copy(RAW));
33✔
955
    if (isl_union_map_is_empty(RED)) {
33✔
956
        isl_union_map_free(StrictWAW);
33✔
957
    } else {
33✔
958
        RED = isl_union_map_intersect(RED, StrictWAW);
×
959
    }
×
960

961
    if (!isl_union_map_is_empty(RED)) {
33✔
962
        // Step 3)
963
        RAW = isl_union_map_subtract(RAW, isl_union_map_copy(RED));
×
964
        WAW = isl_union_map_subtract(WAW, isl_union_map_copy(RED));
×
965
        WAR = isl_union_map_subtract(WAR, isl_union_map_copy(RED));
×
966

967
        // Step 4)
968
        add_privatization_dependences();
×
969
    } else {
33✔
970
        TC_RED = isl_union_map_empty(isl_union_map_get_space(RED));
33✔
971
    }
33✔
972

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

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

991
            isl_set *AccDomW = isl_map_wrap(isl_map_copy(MA->relation()));
×
992
            isl_union_map *AccRedDepU =
×
993
                isl_union_map_intersect_domain(isl_union_map_copy(TC_RED), isl_union_set_from_set(AccDomW));
×
994
            if (isl_union_map_is_empty(AccRedDepU)) {
×
995
                isl_union_map_free(AccRedDepU);
×
996
                continue;
×
997
            }
×
998

999
            isl_map *AccRedDep = isl_map_from_union_map(AccRedDepU);
×
1000
            RED_SIN = isl_union_map_add_map(RED_SIN, isl_map_copy(AccRedDep));
×
1001
            AccRedDep = isl_map_zip(AccRedDep);
×
1002
            AccRedDep = isl_set_unwrap(isl_map_domain(AccRedDep));
×
1003
            set_reduction_dependences(MA, AccRedDep);
×
1004
        }
×
1005
    }
42✔
1006

1007
    assert(
33✔
1008
        isl_union_map_is_equal(RED_SIN, TC_RED) &&
33✔
1009
        "Intersecting the reduction dependence domain with the wrapped access "
33✔
1010
        "relation is not enough, we need to loosen the access relation also"
33✔
1011
    );
33✔
1012
    isl_union_map_free(RED_SIN);
33✔
1013

1014
    RAW = isl_union_map_zip(RAW);
33✔
1015
    WAW = isl_union_map_zip(WAW);
33✔
1016
    WAR = isl_union_map_zip(WAR);
33✔
1017
    RED = isl_union_map_zip(RED);
33✔
1018
    TC_RED = isl_union_map_zip(TC_RED);
33✔
1019

1020
    RAW = isl_union_set_unwrap(isl_union_map_domain(RAW));
33✔
1021
    WAW = isl_union_set_unwrap(isl_union_map_domain(WAW));
33✔
1022
    WAR = isl_union_set_unwrap(isl_union_map_domain(WAR));
33✔
1023
    RED = isl_union_set_unwrap(isl_union_map_domain(RED));
33✔
1024
    TC_RED = isl_union_set_unwrap(isl_union_map_domain(TC_RED));
33✔
1025

1026
    RAW = isl_union_map_union(RAW, STMT_RAW);
33✔
1027
    WAW = isl_union_map_union(WAW, STMT_WAW);
33✔
1028
    WAR = isl_union_map_union(WAR, STMT_WAR);
33✔
1029

1030
    RAW = isl_union_map_coalesce(RAW);
33✔
1031
    WAW = isl_union_map_coalesce(WAW);
33✔
1032
    WAR = isl_union_map_coalesce(WAR);
33✔
1033
    RED = isl_union_map_coalesce(RED);
33✔
1034
    TC_RED = isl_union_map_coalesce(TC_RED);
33✔
1035
}
33✔
1036

1037
void Dependences::add_privatization_dependences() {
×
1038
    isl_union_map *PrivRAW, *PrivWAW, *PrivWAR;
×
1039

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

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

1055
    isl_union_set_foreach_set(Universe, fix_set_to_zero, &Zero);
×
1056
    isl_union_set_free(Universe);
×
1057

1058
    isl_union_map *NonPositive = isl_union_set_lex_le_union_set(UDeltas, Zero);
×
1059

1060
    TC_RED = isl_union_map_subtract(TC_RED, NonPositive);
×
1061

1062
    TC_RED = isl_union_map_union(TC_RED, isl_union_map_reverse(isl_union_map_copy(TC_RED)));
×
1063
    TC_RED = isl_union_map_coalesce(TC_RED);
×
1064

1065
    isl_union_map **Maps[] = {&RAW, &WAW, &WAR};
×
1066
    isl_union_map **PrivMaps[] = {&PrivRAW, &PrivWAW, &PrivWAR};
×
1067
    for (unsigned u = 0; u < 3; u++) {
×
1068
        isl_union_map **Map = Maps[u], **PrivMap = PrivMaps[u];
×
1069

1070
        *PrivMap = isl_union_map_apply_range(isl_union_map_copy(*Map), isl_union_map_copy(TC_RED));
×
1071
        *PrivMap =
×
1072
            isl_union_map_union(*PrivMap, isl_union_map_apply_range(isl_union_map_copy(TC_RED), isl_union_map_copy(*Map)));
×
1073

1074
        *Map = isl_union_map_union(*Map, *PrivMap);
×
1075
    }
×
1076
}
×
1077

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

1087
    isl_map *schedule_deps = isl_map_from_union_map(deps);
3✔
1088
    unsigned Dimension = isl_map_dim(schedule_deps, isl_dim_out) - 1;
3✔
1089

1090
    for (unsigned i = 0; i < Dimension; i++)
4✔
1091
        schedule_deps = isl_map_equate(schedule_deps, isl_dim_out, i, isl_dim_in, i);
1✔
1092

1093
    isl_set *Deltas = isl_map_deltas(schedule_deps);
3✔
1094
    isl_set *Distance = isl_set_universe(isl_set_get_space(Deltas));
3✔
1095

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

1099
    Distance = isl_set_lower_bound_si(Distance, isl_dim_set, Dimension, 1);
3✔
1100
    Distance = isl_set_intersect(Distance, Deltas);
3✔
1101

1102
    bool IsParallel = isl_set_is_empty(Distance);
3✔
1103
    if (IsParallel || !min_distance_ptr) {
3✔
1104
        isl_set_free(Distance);
3✔
1105
        return IsParallel;
3✔
1106
    }
3✔
1107

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

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

1116
    return false;
×
1117
}
3✔
1118

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1182
ScopToSDFG::ScopToSDFG(Scop &scop, const Dependences &dependences, builder::StructuredSDFGBuilder &builder)
1183
    : scop_(scop), dependences_(dependences), builder_(builder) {
7✔
1184
    for (auto *stmt : scop_.statements()) {
7✔
1185
        stmt_map_[stmt->name()] = stmt;
7✔
1186
    }
7✔
1187
}
7✔
1188

1189
struct NameConnectInfo {
1190
    std::vector<std::string> names;
1191
};
1192

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

×
1200
    isl_space *space = isl_map_get_space(map);
×
1201
    isl_local_space *ls = isl_local_space_from_space(space);
×
1202

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

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

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

×
1221
            isl_basic_map *eq_bmap = isl_basic_map_from_constraint(c);
×
1222
            isl_map *eq_map = isl_map_from_basic_map(eq_bmap);
×
1223

×
1224
            bool is_subset = isl_map_is_subset(map, eq_map);
×
1225
            isl_map_free(eq_map);
×
1226

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

1241
static __isl_give isl_ast_node *after_each_for(__isl_take isl_ast_node *node, __isl_keep isl_ast_build *build, void *user) {
12✔
1242
    auto *deps = static_cast<const Dependences *>(user);
12✔
1243
    isl_union_map *schedule = isl_ast_build_get_schedule(build);
12✔
1244
    bool is_parallel = deps->is_parallel(schedule);
12✔
1245
    isl_union_map_free(schedule);
12✔
1246

1247
    if (is_parallel) {
12✔
1248
        isl_id *id = isl_id_alloc(isl_ast_node_get_ctx(node), "parallel", nullptr);
10✔
1249
        node = isl_ast_node_set_annotation(node, id);
10✔
1250
    }
10✔
1251
    return node;
12✔
1252
}
12✔
1253

1254
structured_control_flow::ControlFlowNode &ScopToSDFG::build(analysis::AnalysisManager &analysis_manager) {
7✔
1255
    isl_ctx *ctx = scop_.ctx();
7✔
1256

1257
    // 1. Create AST Builder
1258
    isl_ast_build *ast_builder = isl_ast_build_alloc(ctx);
7✔
1259
    ast_builder = isl_ast_build_set_after_each_for(ast_builder, &after_each_for, (void *) &dependences_);
7✔
1260

1261
    // 2. Generate AST from Schedule
1262
    isl_schedule *schedule = isl_schedule_copy(scop_.schedule_tree());
7✔
1263
    isl_ast_node *root_node = isl_ast_build_node_from_schedule(ast_builder, schedule);
7✔
1264

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

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

1278
    isl_ast_node_free(root_node);
7✔
1279
    isl_ast_build_free(ast_builder);
7✔
1280

1281
    return target_sequence;
7✔
1282
}
7✔
1283

1284
void ScopToSDFG::visit_node(isl_ast_node *node, structured_control_flow::Sequence &scope) {
19✔
1285
    if (!node) return;
19✔
1286

1287
    switch (isl_ast_node_get_type(node)) {
19✔
1288
        case isl_ast_node_for:
12✔
1289
            visit_for(node, scope);
12✔
1290
            break;
12✔
1291
        case isl_ast_node_if:
×
1292
            visit_if(node, scope);
×
1293
            break;
×
1294
        case isl_ast_node_block:
×
1295
            visit_block(node, scope);
×
1296
            break;
×
NEW
1297
        case isl_ast_node_mark:
×
NEW
1298
            visit_mark(node, scope);
×
NEW
1299
            break;
×
1300
        case isl_ast_node_user:
7✔
1301
            visit_user(node, scope);
7✔
1302
            break;
7✔
1303
        default: {
×
1304
            throw std::runtime_error("Unsupported AST node type encountered during SCoP to SDFG conversion.");
×
1305
            break;
×
1306
        }
×
1307
    }
19✔
1308
}
19✔
1309

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

1318
    // Convert to Symbolic
1319
    auto id = isl_ast_expr_get_id(iterator);
12✔
1320
    symbolic::Symbol sym_iter = symbolic::symbol(isl_id_get_name(id));
12✔
1321
    if (!builder_.subject().exists(sym_iter->get_name())) {
12✔
1322
        builder_.add_container(sym_iter->get_name(), types::Scalar(types::PrimitiveType::Int64));
12✔
1323
    }
12✔
1324
    symbolic::Expression sym_init = convert_expr(init);
12✔
1325
    symbolic::Condition sym_cond = convert_cond(cond);
12✔
1326
    symbolic::Expression step = convert_expr(inc);
12✔
1327
    symbolic::Expression update_expr = symbolic::add(sym_iter, step);
12✔
1328

1329
    // Create map for parallel loops
1330
    bool is_parallel = false;
12✔
1331

1332
    isl_id *annotation = isl_ast_node_get_annotation(node);
12✔
1333
    if (annotation) {
12✔
1334
        if (std::string(isl_id_get_name(annotation)) == "parallel") {
10✔
1335
            is_parallel = true;
10✔
1336
        }
10✔
1337
        isl_id_free(annotation);
10✔
1338
    }
10✔
1339

1340
    if (is_parallel) {
12✔
1341
        auto &loop = builder_.add_map(
10✔
1342
            scope, sym_iter, sym_cond, sym_init, update_expr, structured_control_flow::ScheduleType_Sequential::create()
10✔
1343
        );
10✔
1344

1345
        // Recurse into body
1346
        visit_node(body, loop.root());
10✔
1347
    } else {
10✔
1348
        auto &loop = builder_.add_for(scope, sym_iter, sym_cond, sym_init, update_expr);
2✔
1349

1350
        // Recurse into body
1351
        visit_node(body, loop.root());
2✔
1352
    }
2✔
1353

1354
    isl_ast_expr_free(iterator);
12✔
1355
    isl_ast_expr_free(init);
12✔
1356
    isl_ast_expr_free(cond);
12✔
1357
    isl_ast_expr_free(inc);
12✔
1358
    isl_ast_node_free(body);
12✔
1359
    isl_id_free(id);
12✔
1360
}
12✔
1361

1362
void ScopToSDFG::visit_if(isl_ast_node *node, structured_control_flow::Sequence &scope) {
×
1363
    isl_ast_expr *cond = isl_ast_node_if_get_cond(node);
×
1364
    isl_ast_node *then_node = isl_ast_node_if_get_then(node);
×
1365
    isl_ast_node *else_node = isl_ast_node_if_get_else(node);
×
1366

1367
    auto &if_stmt = builder_.add_if_else(scope);
×
1368

1369
    // Then Block
1370
    symbolic::Condition sym_cond = convert_cond(cond);
×
1371
    auto &then_seq = builder_.add_case(if_stmt, sym_cond);
×
1372
    visit_node(then_node, then_seq);
×
1373

1374
    // Else Block (if exists)
1375
    if (else_node) {
×
1376
        auto &else_seq = builder_.add_case(if_stmt, symbolic::Not(sym_cond));
×
1377
        visit_node(else_node, else_seq);
×
1378
    }
×
1379

1380
    isl_ast_expr_free(cond);
×
1381
    isl_ast_node_free(then_node);
×
1382
    isl_ast_node_free(else_node);
×
1383
}
×
1384

1385
void ScopToSDFG::visit_block(isl_ast_node *node, structured_control_flow::Sequence &scope) {
×
1386
    isl_ast_node_list *list = isl_ast_node_block_get_children(node);
×
1387
    int n = isl_ast_node_list_n_ast_node(list);
×
1388

1389
    // Just iterate and append to current sequence
1390
    for (int i = 0; i < n; ++i) {
×
1391
        isl_ast_node *child = isl_ast_node_list_get_ast_node(list, i);
×
1392
        visit_node(child, scope);
×
1393
        isl_ast_node_free(child);
×
1394
    }
×
1395
    isl_ast_node_list_free(list);
×
1396
}
×
1397

NEW
1398
void ScopToSDFG::visit_mark(isl_ast_node *node, structured_control_flow::Sequence &scope) {
×
NEW
1399
    isl_ast_node *child = isl_ast_node_mark_get_node(node);
×
NEW
1400
    visit_node(child, scope);
×
NEW
1401
    isl_ast_node_free(child);
×
NEW
1402
}
×
1403

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

1409
    // A more robust way to get tuple name directly from expr if it is an ID or Call:
1410
    isl_id *id = isl_ast_expr_get_id(expr); // If it's just an ID
7✔
1411
    if (!id && isl_ast_expr_get_type(expr) == isl_ast_expr_op) {
7✔
1412
        // It's likely a call: S(i, j)
1413
        // The first "arg" or the operation type might hold ID.
1414
        // Actually, isl_ast_expr_get_op_type(expr) == isl_ast_expr_op_call
1415
        isl_ast_expr *func = isl_ast_expr_get_op_arg(expr, 0);
7✔
1416
        id = isl_ast_expr_get_id(func);
7✔
1417
        isl_ast_expr_free(func);
7✔
1418
    }
7✔
1419

1420
    if (id) {
7✔
1421
        std::string name = isl_id_get_name(id);
7✔
1422
        if (stmt_map_.count(name)) {
7✔
1423
            ScopStatement *stmt = stmt_map_[name];
7✔
1424

1425
            // Get args of expr
1426
            int n_args = isl_ast_expr_get_op_n_arg(expr);
7✔
1427
            std::vector<symbolic::Expression> args;
7✔
1428
            for (int i = 1; i < n_args; ++i) {
19✔
1429
                isl_ast_expr *arg_expr = isl_ast_expr_get_op_arg(expr, i);
12✔
1430
                symbolic::Expression arg_sym = convert_expr(arg_expr);
12✔
1431
                args.push_back(arg_sym);
12✔
1432
                isl_ast_expr_free(arg_expr);
12✔
1433
            }
12✔
1434

1435
            // Add transition mapping stmt->iterator_i = arg_i
1436
            assert(args.size() == stmt->iterators().size());
7✔
1437
            Assignments assignments;
7✔
1438
            for (size_t i = 0; i < stmt->iterators().size(); ++i) {
19✔
1439
                assignments[stmt->iterators().at(i)] = args[i];
12✔
1440
            }
12✔
1441
            if (!assignments.empty()) {
7✔
1442
                builder_.add_block(scope, assignments);
7✔
1443
            }
7✔
1444

1445
            if (!stmt->expression().is_null()) {
7✔
1446
                std::string rhs = (*stmt->writes().begin())->data();
1✔
1447
                builder_.add_block(scope, {{symbolic::symbol(rhs), stmt->expression()}});
1✔
1448
            } else {
6✔
1449
                auto &new_block = builder_.add_block(scope);
6✔
1450
                auto &new_code_node =
6✔
1451
                    static_cast<data_flow::CodeNode &>(builder_.copy_node(new_block, *stmt->code_node()));
6✔
1452

1453
                // Create Access Node Mapping
1454
                auto &old_graph = stmt->code_node()->get_parent();
6✔
1455
                auto &new_graph = new_code_node.get_parent();
6✔
1456
                std::unordered_map<const data_flow::DataFlowNode *, data_flow::DataFlowNode *> in_node_map;
6✔
1457
                for (auto &iedge : old_graph.in_edges(*stmt->code_node())) {
6✔
1458
                    if (in_node_map.find(&iedge.src()) == in_node_map.end()) {
6✔
1459
                        in_node_map[&iedge.src()] = &builder_.copy_node(new_block, iedge.src());
6✔
1460
                    }
6✔
1461
                    builder_.add_memlet(
6✔
1462
                        new_block,
6✔
1463
                        *in_node_map[&iedge.src()],
6✔
1464
                        iedge.src_conn(),
6✔
1465
                        new_code_node,
6✔
1466
                        iedge.dst_conn(),
6✔
1467
                        iedge.subset(),
6✔
1468
                        iedge.base_type(),
6✔
1469
                        iedge.debug_info()
6✔
1470
                    );
6✔
1471
                }
6✔
1472

1473
                std::unordered_map<const data_flow::DataFlowNode *, data_flow::DataFlowNode *> out_node_map;
6✔
1474
                for (auto &oedge : old_graph.out_edges(*stmt->code_node())) {
6✔
1475
                    if (out_node_map.find(&oedge.dst()) == out_node_map.end()) {
6✔
1476
                        out_node_map[&oedge.dst()] = &builder_.copy_node(new_block, oedge.dst());
6✔
1477
                    }
6✔
1478
                    builder_.add_memlet(
6✔
1479
                        new_block,
6✔
1480
                        new_code_node,
6✔
1481
                        oedge.src_conn(),
6✔
1482
                        *out_node_map[&oedge.dst()],
6✔
1483
                        oedge.dst_conn(),
6✔
1484
                        oedge.subset(),
6✔
1485
                        oedge.base_type(),
6✔
1486
                        oedge.debug_info()
6✔
1487
                    );
6✔
1488
                }
6✔
1489
            }
6✔
1490
        }
7✔
1491
        isl_id_free(id);
7✔
1492
    }
7✔
1493
    isl_ast_expr_free(expr);
7✔
1494
}
7✔
1495

1496
symbolic::Expression ScopToSDFG::convert_expr(isl_ast_expr *expr) {
60✔
1497
    if (!expr) return SymEngine::null;
60✔
1498

1499
    isl_ast_expr_type type = isl_ast_expr_get_type(expr);
60✔
1500
    if (type == isl_ast_expr_int) {
60✔
1501
        isl_val *v = isl_ast_expr_get_val(expr);
26✔
1502
        long val = isl_val_get_num_si(v);
26✔
1503
        isl_val_free(v);
26✔
1504
        return symbolic::integer(val);
26✔
1505
    } else if (type == isl_ast_expr_id) {
34✔
1506
        isl_id *id = isl_ast_expr_get_id(expr);
34✔
1507
        std::string name = isl_id_get_name(id);
34✔
1508
        isl_id_free(id);
34✔
1509
        return symbolic::symbol(name);
34✔
1510
    } else if (type == isl_ast_expr_op) {
34✔
1511
        isl_ast_op_type op = isl_ast_expr_get_op_type(expr);
×
1512
        int n_args = isl_ast_expr_get_op_n_arg(expr);
×
1513

1514
        if (n_args == 1) {
×
1515
            isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
×
1516
            symbolic::Expression res;
×
1517
            if (op == isl_ast_op_minus) {
×
1518
                res = symbolic::mul(symbolic::integer(-1), convert_expr(arg0));
×
1519
            }
×
1520
            isl_ast_expr_free(arg0);
×
1521
            return res;
×
1522
        }
×
1523

1524
        if (n_args == 2) {
×
1525
            isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
×
1526
            isl_ast_expr *arg1 = isl_ast_expr_get_op_arg(expr, 1);
×
1527
            symbolic::Expression s0 = convert_expr(arg0);
×
1528
            symbolic::Expression s1 = convert_expr(arg1);
×
1529
            isl_ast_expr_free(arg0);
×
1530
            isl_ast_expr_free(arg1);
×
1531

1532
            switch (op) {
×
1533
                case isl_ast_op_add:
×
1534
                    return symbolic::add(s0, s1);
×
1535
                case isl_ast_op_sub:
×
1536
                    return symbolic::sub(s0, s1);
×
1537
                case isl_ast_op_mul:
×
1538
                    return symbolic::mul(s0, s1);
×
1539
                case isl_ast_op_div:
×
1540
                case isl_ast_op_pdiv_q:
×
1541
                case isl_ast_op_fdiv_q:
×
1542
                    return symbolic::div(s0, s1);
×
1543
                case isl_ast_op_pdiv_r:
×
1544
                case isl_ast_op_zdiv_r:
×
1545
                    return symbolic::mod(s0, s1);
×
1546
                case isl_ast_op_min:
×
1547
                    return symbolic::min(s0, s1);
×
1548
                case isl_ast_op_max:
×
1549
                    return symbolic::max(s0, s1);
×
1550
                default:
×
1551
                    break;
×
1552
            }
×
1553
        }
×
1554
    }
×
1555
    return SymEngine::null;
×
1556
}
60✔
1557

1558
symbolic::Condition ScopToSDFG::convert_cond(isl_ast_expr *expr) {
12✔
1559
    if (!expr) return SymEngine::null;
12✔
1560

1561
    if (isl_ast_expr_get_type(expr) != isl_ast_expr_op) {
12✔
1562
        return SymEngine::null;
×
1563
    }
×
1564

1565
    isl_ast_op_type op = isl_ast_expr_get_op_type(expr);
12✔
1566
    int n_args = isl_ast_expr_get_op_n_arg(expr);
12✔
1567

1568
    if (n_args == 2) {
12✔
1569
        isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
12✔
1570
        isl_ast_expr *arg1 = isl_ast_expr_get_op_arg(expr, 1);
12✔
1571

1572
        symbolic::Condition res;
12✔
1573

1574
        switch (op) {
12✔
1575
            case isl_ast_op_eq:
×
1576
                res = symbolic::Eq(convert_expr(arg0), convert_expr(arg1));
×
1577
                break;
×
1578
            case isl_ast_op_lt:
10✔
1579
                res = symbolic::Lt(convert_expr(arg0), convert_expr(arg1));
10✔
1580
                break;
10✔
1581
            case isl_ast_op_le:
2✔
1582
                res = symbolic::Le(convert_expr(arg0), convert_expr(arg1));
2✔
1583
                break;
2✔
1584
            case isl_ast_op_gt:
×
1585
                res = symbolic::Gt(convert_expr(arg0), convert_expr(arg1));
×
1586
                break;
×
1587
            case isl_ast_op_ge:
×
1588
                res = symbolic::Ge(convert_expr(arg0), convert_expr(arg1));
×
1589
                break;
×
1590
            case isl_ast_op_and:
×
1591
            case isl_ast_op_and_then:
×
1592
                res = symbolic::And(convert_cond(arg0), convert_cond(arg1));
×
1593
                break;
×
1594
            case isl_ast_op_or:
×
1595
            case isl_ast_op_or_else:
×
1596
                res = symbolic::Or(convert_cond(arg0), convert_cond(arg1));
×
1597
                break;
×
1598
            default:
×
1599
                break;
×
1600
        }
12✔
1601

1602
        isl_ast_expr_free(arg0);
12✔
1603
        isl_ast_expr_free(arg1);
12✔
1604
        return res;
12✔
1605
    }
12✔
1606

1607
    return SymEngine::null;
×
1608
}
12✔
1609

1610
ScopAnalysis::ScopAnalysis(StructuredSDFG &sdfg) : Analysis(sdfg) {}
×
1611

1612
void ScopAnalysis::run(analysis::AnalysisManager &analysis_manager) {
×
1613
    this->scops_.clear();
×
1614
    this->dependences_.clear();
×
1615

1616
    auto &loop_analysis = analysis_manager.get<LoopAnalysis>();
×
1617
    for (auto &loop : loop_analysis.loops()) {
×
1618
        if (!dynamic_cast<structured_control_flow::StructuredLoop *>(loop)) {
×
1619
            continue;
×
1620
        }
×
1621
        auto sloop = static_cast<structured_control_flow::StructuredLoop *>(loop);
×
1622

1623
        ScopBuilder builder(this->sdfg_, *sloop);
×
1624
        auto scop = builder.build(analysis_manager);
×
1625
        if (!scop) {
×
1626
            continue;
×
1627
        }
×
1628
        auto dependences = std::make_unique<Dependences>(*scop);
×
1629
        if (!dependences->has_valid_dependences()) {
×
1630
            continue;
×
1631
        }
×
1632

1633
        this->scops_[loop] = std::move(scop);
×
1634
        this->dependences_[loop] = std::move(dependences);
×
1635
    }
×
1636
}
×
1637

1638
bool ScopAnalysis::has(const structured_control_flow::ControlFlowNode *node) const {
×
1639
    return this->scops_.find(node) != this->scops_.end();
×
1640
}
×
1641

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

1644
const Dependences &ScopAnalysis::dependences(const structured_control_flow::ControlFlowNode *node) const {
×
1645
    return *this->dependences_.at(node);
×
1646
}
×
1647

1648
} // namespace analysis
1649
} // 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