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

daisytuner / sdfglib / 21081067013

16 Jan 2026 09:15PM UTC coverage: 64.755% (+1.9%) from 62.838%
21081067013

Pull #458

github

web-flow
Merge 57ece7200 into 69d31be1e
Pull Request #458: enables ISL tests

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

145 existing lines in 2 files now uncovered.

19049 of 29417 relevant lines covered (64.76%)

392.7 hits per line

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

72.77
/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
    std::cout << "Finished building SCoP for node " << node_.element_id() << "\n";
41✔
161
    if (this->scop_ == nullptr) {
41✔
162
        return nullptr;
×
UNCOV
163
    }
×
164

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

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

173
    if (auto block = dynamic_cast<structured_control_flow::Block *>(&node)) {
106✔
174
        std::cout << "Visiting block " << block->element_id() << "\n";
50✔
175
        this->visit_block(analysis_manager, *block);
50✔
176
        std::cout << "Finished visiting block " << block->element_id() << "\n";
50✔
177
    } else if (auto sequence = dynamic_cast<structured_control_flow::Sequence *>(&node)) {
56✔
178
        std::cout << "Visiting sequence " << sequence->element_id() << "\n";
2✔
179
        this->visit_sequence(analysis_manager, *sequence);
2✔
180
        std::cout << "Finished visiting sequence " << sequence->element_id() << "\n";
2✔
181
    } else if (auto loop = dynamic_cast<structured_control_flow::StructuredLoop *>(&node)) {
54✔
182
        std::cout << "Visiting structured loop " << loop->element_id() << "\n";
54✔
183
        this->visit_structured_loop(analysis_manager, *loop);
54✔
184
        std::cout << "Finished visiting structured loop " << loop->element_id() << "\n";
54✔
185
    } else {
54✔
UNCOV
186
        this->scop_ = nullptr;
×
UNCOV
187
        return;
×
188
    }
×
189
}
106✔
190

191
void ScopBuilder::visit_sequence(analysis::AnalysisManager &analysis_manager, structured_control_flow::Sequence &sequence) {
56✔
192
    for (size_t i = 0; i < sequence.size(); ++i) {
121✔
193
        this->visit(analysis_manager, sequence.at(i).first);
65✔
194
        if (this->scop_ == nullptr) {
65✔
UNCOV
195
            return;
×
UNCOV
196
        }
×
197

198
        auto &transition = sequence.at(i).second;
65✔
199
        size_t j = 0;
65✔
200
        for (const auto &assignment : transition.assignments()) {
65✔
201
            isl_set *domain = isl_set_universe(isl_space_set_alloc(scop_->ctx(), 0, 0));
2✔
202
            auto statement = std::make_unique<ScopStatement>(
2✔
203
                "S_" + std::to_string(transition.element_id()) + "_" + std::to_string(j), domain, assignment.second
2✔
204
            );
2✔
205
            j++;
2✔
206

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

225
            for (auto &sym : symbolic::atoms(assignment.second)) {
2✔
226
                AccessType access_type = AccessType::READ;
2✔
227
                std::string data = sym->get_name();
2✔
228
                isl_map *isl_relation = isl_map_read_from_str(scop_->ctx(), relation.c_str());
2✔
229
                if (!isl_relation) {
2✔
UNCOV
230
                    this->scop_ = nullptr;
×
UNCOV
231
                    return;
×
UNCOV
232
                }
×
233
                isl_relation = isl_map_set_tuple_name(isl_relation, isl_dim_in, statement->name().c_str());
2✔
234
                auto memory_access = std::make_unique<MemoryAccess>(access_type, isl_relation, data, nullptr);
2✔
235
                statement->insert(memory_access);
2✔
236
            }
2✔
237

238
            this->scop_->insert(statement);
2✔
239
        }
2✔
240
    }
65✔
241
}
56✔
242

243
void ScopBuilder::visit_block(analysis::AnalysisManager &analysis_manager, structured_control_flow::Block &block) {
50✔
244
    auto &graph = block.dataflow();
50✔
245
    for (auto node : graph.topological_sort()) {
156✔
246
        if (auto code_node = dynamic_cast<data_flow::CodeNode *>(node)) {
156✔
247
            if (dynamic_cast<data_flow::LibraryNode *>(code_node)) {
49✔
UNCOV
248
                if (!dynamic_cast<math::cmath::CMathNode *>(code_node)) {
×
UNCOV
249
                    this->scop_ = nullptr;
×
UNCOV
250
                    return;
×
UNCOV
251
                }
×
UNCOV
252
            }
×
253

254
            isl_set *domain = isl_set_universe(isl_space_set_alloc(scop_->ctx(), 0, 0));
49✔
255
            auto statement =
49✔
256
                std::make_unique<ScopStatement>("S_" + std::to_string(code_node->element_id()), domain, code_node);
49✔
257

258
            // Add reads
259
            for (auto &iedge : graph.in_edges(*node)) {
59✔
260
                if (dynamic_cast<data_flow::ConstantNode *>(&iedge.src())) {
59✔
261
                    continue;
7✔
262
                }
7✔
263

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

277
            // Add writes
278
            for (auto &oedge : graph.out_edges(*node)) {
49✔
279
                AccessType access_type = AccessType::WRITE;
49✔
280

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

294
            this->scop_->insert(statement);
49✔
295
        }
49✔
296
    }
156✔
297
}
50✔
298

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

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

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

357
    // Construct constraints string.
358
    std::string set_str;
54✔
359
    if (this->dimensions_.size() > 1 || !this->parameters_.empty()) {
54✔
360
        std::vector<std::string> params = this->parameters_;
43✔
361
        for (int i = 0; i < this->dimensions_.size() - 1; ++i) {
58✔
362
            params.push_back(this->dimensions_[i]);
15✔
363
        }
15✔
364
        set_str += "[ " + helpers::join(params, ", ") + " ] -> ";
43✔
365
    }
43✔
366
    set_str += "{ [" + indvar + "] : " + condition + " }";
54✔
367
    std::cout << "Loop Bound Set String: " << set_str << std::endl;
54✔
368

369
    isl_ctx *ctx = scop_->ctx();
54✔
370
    isl_set *loop_bound_base = isl_set_read_from_str(ctx, set_str.c_str());
54✔
371
    if (!loop_bound_base) {
54✔
UNCOV
372
        DEBUG_PRINTLN("Failed to parse loop bound set: " << set_str);
×
373
        this->scop_ = nullptr;
×
374
        return;
×
UNCOV
375
    }
×
376
    char *loop_bound_str = isl_set_to_str(loop_bound_base);
54✔
377
    std::cout << "Loop Bound Set: " << loop_bound_str << std::endl;
54✔
378
    free(loop_bound_str);
54✔
379

380
    size_t stmt_start = this->scop_->statements().size();
54✔
381
    this->visit_sequence(analysis_manager, loop.root());
54✔
382
    if (this->scop_ == nullptr) {
54✔
UNCOV
383
        return;
×
UNCOV
384
    }
×
385

386
    auto stmts = this->scop_->statements();
54✔
387
    for (size_t i = stmt_start; i < stmts.size(); ++i) {
119✔
388
        ScopStatement *stmt = stmts[i];
65✔
389
        isl_set *domain = isl_set_copy(stmt->domain());
65✔
390

391
        // Insert indvar dimension at 0
392
        domain = isl_set_insert_dims(domain, isl_dim_set, 0, 1);
65✔
393
        domain = isl_set_set_dim_name(domain, isl_dim_set, 0, indvar.c_str());
65✔
394

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

407
        // Prepare loop bound set with compatible dimensions
408
        isl_set *loop_bound = isl_set_copy(loop_bound_base);
65✔
409
        int n_dims = isl_set_dim(domain, isl_dim_set);
65✔
410
        int inner_dims = n_dims - 1;
65✔
411

412
        if (inner_dims > 0) {
65✔
413
            loop_bound = isl_set_add_dims(loop_bound, isl_dim_set, inner_dims);
16✔
414

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

425
        domain = isl_set_intersect(domain, loop_bound);
65✔
426
        stmt->set_domain(domain);
65✔
427

428
        // Update Schedule
429
        isl_map *schedule = isl_map_copy(stmt->schedule());
65✔
430

431
        // Insert dimension in input (domain)
432
        schedule = isl_map_insert_dims(schedule, isl_dim_in, 0, 1);
65✔
433
        schedule = isl_map_set_dim_name(schedule, isl_dim_in, 0, indvar.c_str());
65✔
434

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

447
        // Insert dimension in output (range/time)
448
        schedule = isl_map_insert_dims(schedule, isl_dim_out, 0, 1);
65✔
449
        schedule = isl_map_set_dim_name(schedule, isl_dim_out, 0, indvar.c_str());
65✔
450

451
        // Equate input[0] and output[0]
452
        schedule = isl_map_equate(schedule, isl_dim_in, 0, isl_dim_out, 0);
65✔
453

454
        stmt->set_schedule(schedule);
65✔
455

456
        for (auto access : stmt->accesses()) {
137✔
457
            isl_map *relation = isl_map_copy(access->relation());
137✔
458

459
            int dom_n = isl_set_dim(stmt->domain(), isl_dim_set);
137✔
460
            int rel_n_in = isl_map_dim(relation, isl_dim_in);
137✔
461

462
            if (rel_n_in < dom_n) {
137✔
463
                relation = isl_map_insert_dims(relation, isl_dim_in, 0, 1);
2✔
464
                relation = isl_map_set_dim_name(relation, isl_dim_in, 0, indvar.c_str());
2✔
465

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

479
            relation = isl_map_set_tuple_name(relation, isl_dim_in, stmt->name().c_str());
137✔
480
            access->set_relation(relation);
137✔
481
        }
137✔
482
    }
65✔
483
    isl_set_free(loop_bound_base);
54✔
484

485
    this->dimensions_.pop_back();
54✔
486
}
54✔
487

488
std::string ScopBuilder::generate_subset(
489
    analysis::AnalysisManager &analysis_manager,
490
    structured_control_flow::ControlFlowNode &node,
491
    const symbolic::MultiExpression &subset
492
) {
101✔
493
    std::string relation = "";
101✔
494
    if (!this->parameters_.empty()) {
101✔
495
        relation += "[ " + helpers::join(this->parameters_, ", ") + " ] -> ";
78✔
496
    }
78✔
497
    relation += "{ [";
101✔
498
    relation += helpers::join(this->dimensions_, ", ");
101✔
499
    relation += "] -> [";
101✔
500
    if (subset.empty()) {
101✔
501
        relation += "0";
13✔
502
    } else {
88✔
503
        for (size_t i = 0; i < subset.size(); ++i) {
196✔
504
            if (i > 0) {
108✔
505
                relation += ", ";
20✔
506
            }
20✔
507
            relation += subset.at(i)->__str__();
108✔
508
        }
108✔
509
    }
88✔
510
    relation += "] }";
101✔
511

512
    return relation;
101✔
513
}
101✔
514

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

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

537
    std::unordered_set<std::string> ReductionArrays;
29✔
538
    for (auto stmt : S.statements())
29✔
539
        for (MemoryAccess *MA : stmt->accesses())
38✔
540
            if (MA->is_reduction_like()) ReductionArrays.insert(MA->data());
80✔
541

542
    for (auto stmt : S.statements()) {
38✔
543
        for (MemoryAccess *MA : stmt->accesses()) {
80✔
544
            isl_set *domcp = isl_set_copy(stmt->domain());
80✔
545
            isl_map *accdom = isl_map_copy(MA->relation());
80✔
546
            accdom = isl_map_set_tuple_name(accdom, isl_dim_out, MA->data().c_str());
80✔
547

548
            accdom = isl_map_intersect_domain(accdom, domcp);
80✔
549

550

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

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

573
                isl_id *id = isl_id_alloc(S.ctx(), MA->data().c_str(), MA);
80✔
574
                accdom = tag(accdom, isl_id_copy(id));
80✔
575
                isl_map *Schedule = tag(StmtScheduleMap, id);
80✔
576
                StmtSchedule = isl_union_map_add_map(StmtSchedule, Schedule);
80✔
577
            }
80✔
578

579
            if (MA->access_type() == AccessType::READ) {
80✔
580
                Read = isl_union_map_add_map(Read, accdom);
42✔
581
            } else {
42✔
582
                MustWrite = isl_union_map_add_map(MustWrite, accdom);
38✔
583
            }
38✔
584
        }
80✔
585
    }
38✔
586

587
    StmtSchedule = isl_union_map_intersect_params(StmtSchedule, isl_set_universe(isl_space_copy(Space)));
29✔
588
    TaggedStmtDomain = isl_union_map_domain(StmtSchedule);
29✔
589

590
    ReductionTagMap = isl_union_map_coalesce(ReductionTagMap);
29✔
591
    Read = isl_union_map_coalesce(Read);
29✔
592
    MustWrite = isl_union_map_coalesce(MustWrite);
29✔
593

594
    isl_space_free(Space);
29✔
595
}
29✔
596

597
static isl_union_flow *
598
buildFlow(isl_union_map *Snk, isl_union_map *Src, isl_union_map *MaySrc, isl_union_map *Kill, isl_schedule *Schedule) {
116✔
599
    isl_union_access_info *AI;
116✔
600

601
    AI = isl_union_access_info_from_sink(isl_union_map_copy(Snk));
116✔
602
    if (MaySrc) AI = isl_union_access_info_set_may_source(AI, isl_union_map_copy(MaySrc));
116✔
603
    if (Src) AI = isl_union_access_info_set_must_source(AI, isl_union_map_copy(Src));
116✔
604
    if (Kill) AI = isl_union_access_info_set_kill(AI, isl_union_map_copy(Kill));
116✔
605
    AI = isl_union_access_info_set_schedule(AI, isl_schedule_copy(Schedule));
116✔
606
    auto Flow = isl_union_access_info_compute_flow(AI);
116✔
607
    return Flow;
116✔
608
}
116✔
609

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

621
static isl_stat fix_set_to_zero(isl_set *Set, void *User) {
×
UNCOV
622
    isl_union_set **UserUS = (isl_union_set **) User;
×
623

624
    int dims = isl_set_dim(Set, isl_dim_set);
×
625
    for (int i = 0; i < dims; ++i) {
×
626
        Set = isl_set_fix_si(Set, isl_dim_set, i, 0);
×
UNCOV
627
    }
×
628

UNCOV
629
    *UserUS = isl_union_set_union(*UserUS, isl_union_set_from_set(Set));
×
630
    return isl_stat_ok;
×
UNCOV
631
}
×
632

633
isl_union_map *Dependences::dependences(int Kinds) const {
11✔
634
    assert(has_valid_dependences() && "No valid dependences available");
11✔
635
    isl_space *Space = isl_union_map_get_space(RAW);
11✔
636
    isl_union_map *Deps = isl_union_map_empty(Space);
11✔
637

638
    if (Kinds & TYPE_RAW) Deps = isl_union_map_union(Deps, isl_union_map_copy(RAW));
11✔
639

640
    if (Kinds & TYPE_WAR) Deps = isl_union_map_union(Deps, isl_union_map_copy(WAR));
11✔
641

642
    if (Kinds & TYPE_WAW) Deps = isl_union_map_union(Deps, isl_union_map_copy(WAW));
11✔
643

644
    if (Kinds & TYPE_RED) Deps = isl_union_map_union(Deps, isl_union_map_copy(RED));
11✔
645

646
    if (Kinds & TYPE_TC_RED) Deps = isl_union_map_union(Deps, isl_union_map_copy(TC_RED));
11✔
647

648
    Deps = isl_union_map_coalesce(Deps);
11✔
649
    Deps = isl_union_map_detect_equalities(Deps);
11✔
650
    return Deps;
11✔
651
}
11✔
652

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

659
static isl_stat collect_deps(isl_map *bmap, void *user) {
156✔
660
    auto *info = static_cast<DepCallbackInfo *>(user);
156✔
661

662
    if (info->loop) {
156✔
663
        bool exists_for_dim = false;
156✔
664
        std::string indvar_name = info->loop->indvar()->get_name();
156✔
665

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

674
        isl_space *ran_space = isl_space_range(map_space);
156✔
675
        if (isl_space_is_wrapping(ran_space)) {
156✔
676
            map_for_deltas = isl_map_range_factor_domain(map_for_deltas);
80✔
677
        }
80✔
678
        isl_space_free(ran_space);
156✔
679

680
        isl_space *map_space_final = isl_map_get_space(map_for_deltas);
156✔
681
        isl_space *domain_space = isl_space_domain(isl_space_copy(map_space_final));
156✔
682
        isl_space *range_space = isl_space_range(isl_space_copy(map_space_final));
156✔
683
        isl_space_free(map_space_final);
156✔
684

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

695
        isl_set *deltas = isl_map_deltas(map_for_deltas);
78✔
696
        int dims = isl_set_dim(deltas, isl_dim_set);
78✔
697
        int dim_idx = -1;
78✔
698

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

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

712
            for (int i = 0; i < dim_idx; ++i) {
99✔
713
                deltas = isl_set_fix_si(deltas, isl_dim_set, i, 0);
25✔
714
            }
25✔
715

716
            isl_set *zero_at_dim = isl_set_universe(isl_set_get_space(deltas));
74✔
717
            zero_at_dim = isl_set_fix_si(zero_at_dim, isl_dim_set, dim_idx, 0);
74✔
718

719
            if (!isl_set_is_subset(deltas, zero_at_dim)) {
74✔
720
                exists_for_dim = true;
62✔
721
            }
62✔
722
            isl_set_free(zero_at_dim);
74✔
723
        }
74✔
724
        isl_set_free(deltas);
78✔
725

726
        if (!exists_for_dim) {
78✔
727
            isl_map_free(bmap);
16✔
728
            return isl_stat_ok;
16✔
729
        }
16✔
730
    }
78✔
731

732
    isl_space *space = isl_map_get_space(bmap);
62✔
733
    isl_space *domain_space = isl_space_domain(space);
62✔
734

735
    if (isl_space_is_wrapping(domain_space)) {
62✔
736
        isl_space *domain_space_copy = isl_space_copy(domain_space);
31✔
737
        isl_space *unwrapped = isl_space_unwrap(domain_space_copy);
31✔
738
        isl_space *range = isl_space_range(unwrapped);
31✔
739

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

757
    isl_space_free(domain_space);
62✔
758
    isl_map_free(bmap);
62✔
759
    return isl_stat_ok;
62✔
760
}
156✔
761

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

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

774
    if (RAW) {
30✔
775
        info.type = analysis::LoopCarriedDependency::LOOP_CARRIED_DEPENDENCY_READ_WRITE;
30✔
776
        isl_union_map_foreach_map(RAW, collect_deps, &info);
30✔
777
    }
30✔
778

779
    if (WAR) {
30✔
780
        info.type = analysis::LoopCarriedDependency::LOOP_CARRIED_DEPENDENCY_READ_WRITE;
30✔
781
        isl_union_map_foreach_map(WAR, collect_deps, &info);
30✔
782
    }
30✔
783

784
    return deps;
30✔
785
}
30✔
786

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

UNCOV
789
isl_map *Dependences::reduction_dependences(MemoryAccess *MA) const { return reduction_dependences_.at(MA); }
×
790

UNCOV
791
void Dependences::set_reduction_dependences(MemoryAccess *memory_access, isl_map *deps) {
×
792
    reduction_dependences_[memory_access] = deps;
×
793
}
×
794

795
void Dependences::calculate_dependences(Scop &S) {
29✔
796
    isl_union_map *Read, *MustWrite, *ReductionTagMap;
29✔
797
    isl_union_set *TaggedStmtDomain;
29✔
798
    collectInfo(S, Read, MustWrite, ReductionTagMap, TaggedStmtDomain);
29✔
799

800
    isl_schedule *Schedule = isl_schedule_copy(S.schedule_tree());
29✔
801

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

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

821
        isl_union_pw_multi_aff *Tags = isl_union_pw_multi_aff_union_add(ReductionTags, IdentityTags);
×
822

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

829
    isl_union_map *StrictWAW = nullptr;
29✔
830
    {
29✔
831
        RAW = WAW = WAR = RED = nullptr;
29✔
832
        isl_union_map *Write = isl_union_map_copy(MustWrite);
29✔
833

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

893
        isl_union_flow *Flow = buildFlow(Write, Write, Read, nullptr, Schedule);
29✔
894
        StrictWAW = isl_union_flow_get_must_dependence(Flow);
29✔
895
        isl_union_flow_free(Flow);
29✔
896

897
        Flow = buildFlow(Read, nullptr, Write, nullptr, Schedule);
29✔
898
        RAW = isl_union_flow_get_may_dependence(Flow);
29✔
899
        isl_union_flow_free(Flow);
29✔
900

901
        Flow = buildFlow(Write, nullptr, Read, nullptr, Schedule);
29✔
902
        WAR = isl_union_flow_get_may_dependence(Flow);
29✔
903
        isl_union_flow_free(Flow);
29✔
904

905
        Flow = buildFlow(Write, nullptr, Write, nullptr, Schedule);
29✔
906
        WAW = isl_union_flow_get_may_dependence(Flow);
29✔
907
        isl_union_flow_free(Flow);
29✔
908

909
        isl_union_map_free(Write);
29✔
910
        isl_union_map_free(MustWrite);
29✔
911
        isl_union_map_free(Read);
29✔
912
        isl_schedule_free(Schedule);
29✔
913

914
        RAW = isl_union_map_coalesce(RAW);
29✔
915
        WAW = isl_union_map_coalesce(WAW);
29✔
916
        WAR = isl_union_map_coalesce(WAR);
29✔
917

918
        // End of max_operations scope.
919
    }
29✔
920

921
    isl_union_map *STMT_RAW =
29✔
922
        isl_union_map_intersect_domain(isl_union_map_copy(RAW), isl_union_set_copy(TaggedStmtDomain));
29✔
923
    isl_union_map *STMT_WAW =
29✔
924
        isl_union_map_intersect_domain(isl_union_map_copy(WAW), isl_union_set_copy(TaggedStmtDomain));
29✔
925
    isl_union_map *STMT_WAR =
29✔
926
        isl_union_map_intersect_domain(isl_union_map_copy(WAR), isl_union_set_copy(TaggedStmtDomain));
29✔
927
    isl_union_set_free(TaggedStmtDomain);
29✔
928

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

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

956
    // Step 2)
957
    RED = isl_union_map_intersect(RED, isl_union_map_copy(RAW));
29✔
958
    if (isl_union_map_is_empty(RED)) {
29✔
959
        isl_union_map_free(StrictWAW);
29✔
960
    } else {
29✔
961
        RED = isl_union_map_intersect(RED, StrictWAW);
×
962
    }
×
963

964
    if (!isl_union_map_is_empty(RED)) {
29✔
965
        // Step 3)
UNCOV
966
        RAW = isl_union_map_subtract(RAW, isl_union_map_copy(RED));
×
UNCOV
967
        WAW = isl_union_map_subtract(WAW, isl_union_map_copy(RED));
×
UNCOV
968
        WAR = isl_union_map_subtract(WAR, isl_union_map_copy(RED));
×
969

970
        // Step 4)
UNCOV
971
        add_privatization_dependences();
×
972
    } else {
29✔
973
        TC_RED = isl_union_map_empty(isl_union_map_get_space(RED));
29✔
974
    }
29✔
975

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

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

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

1002
            isl_map *AccRedDep = isl_map_from_union_map(AccRedDepU);
×
1003
            RED_SIN = isl_union_map_add_map(RED_SIN, isl_map_copy(AccRedDep));
×
1004
            AccRedDep = isl_map_zip(AccRedDep);
×
1005
            AccRedDep = isl_set_unwrap(isl_map_domain(AccRedDep));
×
UNCOV
1006
            set_reduction_dependences(MA, AccRedDep);
×
1007
        }
×
1008
    }
38✔
1009

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

1017
    RAW = isl_union_map_zip(RAW);
29✔
1018
    WAW = isl_union_map_zip(WAW);
29✔
1019
    WAR = isl_union_map_zip(WAR);
29✔
1020
    RED = isl_union_map_zip(RED);
29✔
1021
    TC_RED = isl_union_map_zip(TC_RED);
29✔
1022

1023
    RAW = isl_union_set_unwrap(isl_union_map_domain(RAW));
29✔
1024
    WAW = isl_union_set_unwrap(isl_union_map_domain(WAW));
29✔
1025
    WAR = isl_union_set_unwrap(isl_union_map_domain(WAR));
29✔
1026
    RED = isl_union_set_unwrap(isl_union_map_domain(RED));
29✔
1027
    TC_RED = isl_union_set_unwrap(isl_union_map_domain(TC_RED));
29✔
1028

1029
    RAW = isl_union_map_union(RAW, STMT_RAW);
29✔
1030
    WAW = isl_union_map_union(WAW, STMT_WAW);
29✔
1031
    WAR = isl_union_map_union(WAR, STMT_WAR);
29✔
1032

1033
    RAW = isl_union_map_coalesce(RAW);
29✔
1034
    WAW = isl_union_map_coalesce(WAW);
29✔
1035
    WAR = isl_union_map_coalesce(WAR);
29✔
1036
    RED = isl_union_map_coalesce(RED);
29✔
1037
    TC_RED = isl_union_map_coalesce(TC_RED);
29✔
1038
}
29✔
1039

UNCOV
1040
void Dependences::add_privatization_dependences() {
×
UNCOV
1041
    isl_union_map *PrivRAW, *PrivWAW, *PrivWAR;
×
1042

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

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

1058
    isl_union_set_foreach_set(Universe, fix_set_to_zero, &Zero);
×
1059
    isl_union_set_free(Universe);
×
1060

1061
    isl_union_map *NonPositive = isl_union_set_lex_le_union_set(UDeltas, Zero);
×
1062

1063
    TC_RED = isl_union_map_subtract(TC_RED, NonPositive);
×
1064

1065
    TC_RED = isl_union_map_union(TC_RED, isl_union_map_reverse(isl_union_map_copy(TC_RED)));
×
UNCOV
1066
    TC_RED = isl_union_map_coalesce(TC_RED);
×
1067

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

1073
        *PrivMap = isl_union_map_apply_range(isl_union_map_copy(*Map), isl_union_map_copy(TC_RED));
×
1074
        *PrivMap =
×
1075
            isl_union_map_union(*PrivMap, isl_union_map_apply_range(isl_union_map_copy(TC_RED), isl_union_map_copy(*Map)));
×
1076

UNCOV
1077
        *Map = isl_union_map_union(*Map, *PrivMap);
×
1078
    }
×
1079
}
×
1080

1081
bool Dependences::is_parallel(isl_union_map *schedule, isl_pw_aff **min_distance_ptr) const {
×
1082
    isl_set *Deltas, *Distance;
×
1083
    isl_union_map *deps = this->dependences(TYPE_RAW | TYPE_WAR | TYPE_WAW);
×
1084
    std::cout << "Dependences for parallel check: " << isl_union_map_to_str(deps) << std::endl;
×
UNCOV
1085
    isl_map *schedule_deps;
×
1086
    unsigned Dimension;
×
1087
    bool IsParallel;
×
1088

1089
    deps = isl_union_map_apply_range(deps, isl_union_map_copy(schedule));
×
1090
    deps = isl_union_map_apply_domain(deps, isl_union_map_copy(schedule));
×
1091

1092
    if (isl_union_map_is_empty(deps)) {
×
1093
        isl_union_map_free(deps);
×
UNCOV
1094
        return true;
×
UNCOV
1095
    }
×
1096

UNCOV
1097
    schedule_deps = isl_map_from_union_map(deps);
×
1098
    Dimension = isl_map_dim(schedule_deps, isl_dim_out) - 1;
×
1099

UNCOV
1100
    for (unsigned i = 0; i < Dimension; i++)
×
1101
        schedule_deps = isl_map_equate(schedule_deps, isl_dim_out, i, isl_dim_in, i);
×
1102

1103
    Deltas = isl_map_deltas(schedule_deps);
×
1104
    Distance = isl_set_universe(isl_set_get_space(Deltas));
×
1105

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

UNCOV
1109
    Distance = isl_set_lower_bound_si(Distance, isl_dim_set, Dimension, 1);
×
UNCOV
1110
    Distance = isl_set_intersect(Distance, Deltas);
×
1111

UNCOV
1112
    IsParallel = isl_set_is_empty(Distance);
×
1113
    if (IsParallel || !min_distance_ptr) {
×
UNCOV
1114
        isl_set_free(Distance);
×
1115
        return IsParallel;
×
1116
    }
×
1117

1118
    Distance = isl_set_project_out(Distance, isl_dim_set, 0, Dimension);
×
1119
    Distance = isl_set_coalesce(Distance);
×
1120

1121
    // This last step will compute a expression for the minimal value in the
1122
    // distance polyhedron Distance with regards to the first (outer most)
1123
    // dimension.
1124
    *min_distance_ptr = isl_pw_aff_coalesce(isl_set_dim_min(Distance, 0));
×
1125

1126
    return false;
×
UNCOV
1127
}
×
1128

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

1132
    Deps = isl_union_map_apply_domain(Deps, isl_union_map_copy(Schedule));
2✔
1133
    Deps = isl_union_map_apply_range(Deps, isl_union_map_copy(Schedule));
2✔
1134

1135
    isl_space *ScheduleSpace = NULL;
2✔
1136
    isl_union_map_foreach_map(Schedule, get_map_range_space, &ScheduleSpace);
2✔
1137
    isl_union_map_free(Schedule);
2✔
1138

1139
    if (!ScheduleSpace) {
2✔
UNCOV
1140
        isl_union_map_free(Deps);
×
1141
        return true; /* Empty schedule considered valid/trivial */
×
1142
    }
×
1143

1144
    isl_set *Zero = isl_set_universe(isl_space_copy(ScheduleSpace));
2✔
1145
    int dims = isl_set_dim(Zero, isl_dim_set);
2✔
1146
    for (int i = 0; i < dims; ++i) Zero = isl_set_fix_si(Zero, isl_dim_set, i, 0);
4✔
1147

1148
    isl_union_set *UDeltas = isl_union_map_deltas(Deps);
2✔
1149
    isl_set *Deltas = isl_union_set_extract_set(UDeltas, ScheduleSpace);
2✔
1150
    isl_union_set_free(UDeltas);
2✔
1151

1152
    isl_space *Space = isl_set_get_space(Deltas);
2✔
1153
    isl_space *MapSpace = isl_space_map_from_set(isl_space_copy(Space));
2✔
1154
    isl_map *NonPositive = isl_map_universe(MapSpace);
2✔
1155

1156
    isl_multi_pw_aff *Identity = isl_multi_pw_aff_identity_on_domain_space(Space);
2✔
1157
    NonPositive = isl_map_lex_le_at_multi_pw_aff(NonPositive, Identity);
2✔
1158

1159
    NonPositive = isl_map_intersect_domain(NonPositive, Deltas);
2✔
1160
    NonPositive = isl_map_intersect_range(NonPositive, Zero);
2✔
1161

1162
    bool IsEmpty = isl_map_is_empty(NonPositive);
2✔
1163

1164
    isl_map_free(NonPositive);
2✔
1165
    return IsEmpty;
2✔
1166
}
2✔
1167

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

1172
    for (ScopStatement *Stmt : scop.statements()) {
1✔
1173
        isl_map *StmtScat;
1✔
1174

1175
        auto Lookup = new_schedule.find(Stmt);
1✔
1176
        if (Lookup == new_schedule.end()) {
1✔
1177
            StmtScat = isl_map_copy(Stmt->schedule());
×
1178
        } else {
1✔
1179
            StmtScat = isl_map_copy(Lookup->second);
1✔
1180
        }
1✔
1181

1182
        Schedule = isl_union_map_union(Schedule, isl_union_map_from_map(StmtScat));
1✔
1183
    }
1✔
1184

1185
    return check_validity(this, scop, Schedule);
1✔
1186
}
1✔
1187

1188
bool Dependences::is_valid(Scop &scop, isl_schedule *schedule) const {
1✔
1189
    return check_validity(this, scop, isl_schedule_get_map(schedule));
1✔
1190
}
1✔
1191

1192
ScopToSDFG::ScopToSDFG(Scop &scop, builder::StructuredSDFGBuilder &builder) : scop_(scop), builder_(builder) {
5✔
1193
    for (auto *stmt : scop_.statements()) {
5✔
1194
        stmt_map_[stmt->name()] = stmt;
5✔
1195
    }
5✔
1196
}
5✔
1197

1198
struct NameConnectInfo {
1199
    std::vector<std::string> names;
1200
};
1201

1202
static isl_stat collect_dim_names(isl_map *map, void *user) {
5✔
1203
    auto *info = static_cast<NameConnectInfo *>(user);
5✔
1204
    int dim = isl_map_dim(map, isl_dim_out);
5✔
1205
    if (dim > info->names.size()) {
5✔
1206
        info->names.resize(dim);
5✔
1207
    }
5✔
1208

1209
    isl_space *space = isl_map_get_space(map);
5✔
1210
    isl_local_space *ls = isl_local_space_from_space(space);
5✔
1211

1212
    for (int i = 0; i < dim; ++i) {
13✔
1213
        if (!info->names[i].empty()) {
8✔
1214
            continue;
×
1215
        }
×
1216

1217
        const char *name = isl_map_get_dim_name(map, isl_dim_out, i);
8✔
1218
        if (name) {
8✔
1219
            info->names[i] = name;
×
1220
            continue;
×
UNCOV
1221
        }
×
1222

1223
        // Try to recover from input dimensions
1224
        int in_dim = isl_map_dim(map, isl_dim_in);
8✔
1225
        for (int j = 0; j < in_dim; ++j) {
11✔
1226
            isl_constraint *c = isl_constraint_alloc_equality(isl_local_space_copy(ls));
11✔
1227
            c = isl_constraint_set_coefficient_si(c, isl_dim_out, i, 1);
11✔
1228
            c = isl_constraint_set_coefficient_si(c, isl_dim_in, j, -1);
11✔
1229

1230
            isl_basic_map *eq_bmap = isl_basic_map_from_constraint(c);
11✔
1231
            isl_map *eq_map = isl_map_from_basic_map(eq_bmap);
11✔
1232

1233
            bool is_subset = isl_map_is_subset(map, eq_map);
11✔
1234
            isl_map_free(eq_map);
11✔
1235

1236
            if (is_subset) {
11✔
1237
                const char *in_name = isl_map_get_dim_name(map, isl_dim_in, j);
8✔
1238
                if (in_name) {
8✔
1239
                    info->names[i] = in_name;
8✔
1240
                    break;
8✔
1241
                }
8✔
1242
            }
8✔
1243
        }
11✔
1244
    }
8✔
1245
    isl_local_space_free(ls);
5✔
1246
    isl_map_free(map);
5✔
1247
    return isl_stat_ok;
5✔
1248
}
5✔
1249

1250
void ScopToSDFG::build(analysis::AnalysisManager &analysis_manager) {
5✔
1251
    isl_ctx *ctx = scop_.ctx();
5✔
1252

1253
    // 1. Create AST Builder
1254
    isl_ast_build *ast_builder = isl_ast_build_alloc(ctx);
5✔
1255

1256
    // 2. Generate AST from Schedule
1257
    isl_schedule *schedule = isl_schedule_copy(scop_.schedule_tree());
5✔
1258
    isl_union_map *schedule_map = isl_union_map_copy(scop_.schedule());
5✔
1259

1260
    // Apply dimension names to schedule
1261
    NameConnectInfo info;
5✔
1262
    isl_union_map_foreach_map(schedule_map, collect_dim_names, &info);
5✔
1263
    isl_union_map_free(schedule_map);
5✔
1264

1265
    if (!info.names.empty()) {
5✔
1266
        isl_id_list *iterators = isl_id_list_alloc(ctx, info.names.size());
5✔
1267
        for (int i = 0; i < info.names.size(); ++i) {
13✔
1268
            std::string id_name = info.names[i].empty() ? "c" + std::to_string(i) : info.names[i];
8✔
1269
            iterators = isl_id_list_add(iterators, isl_id_alloc(ctx, id_name.c_str(), nullptr));
8✔
1270
        }
8✔
1271
        ast_builder = isl_ast_build_set_iterators(ast_builder, iterators);
5✔
1272
    }
5✔
1273

1274
    isl_ast_node *root_node = isl_ast_build_node_from_schedule(ast_builder, schedule);
5✔
1275

1276
    // 3. Start Traversal
1277
    auto &scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
5✔
1278
    auto parent_scope = scope_analysis.parent_scope(&scop_.node());
5✔
1279
    if (!parent_scope) {
5✔
1280
        return; // Cannot build SDFG without a parent scope
×
UNCOV
1281
    }
×
1282
    auto parent_sequence = static_cast<structured_control_flow::Sequence *>(parent_scope);
5✔
1283
    int index = parent_sequence->index(scop_.node());
5✔
1284
    auto &target_sequence = builder_.add_sequence_before(*parent_sequence, scop_.node(), {}, scop_.node().debug_info());
5✔
1285

1286
    visit_node(root_node, target_sequence);
5✔
1287
    builder_.remove_child(*parent_sequence, index + 1);
5✔
1288

1289
    isl_ast_node_free(root_node);
5✔
1290
    isl_ast_build_free(ast_builder);
5✔
1291
}
5✔
1292

1293
void ScopToSDFG::visit_node(isl_ast_node *node, structured_control_flow::Sequence &scope) {
13✔
1294
    if (!node) return;
13✔
1295

1296
    switch (isl_ast_node_get_type(node)) {
13✔
1297
        case isl_ast_node_for:
8✔
1298
            visit_for(node, scope);
8✔
1299
            break;
8✔
1300
        case isl_ast_node_if:
×
1301
            visit_if(node, scope);
×
1302
            break;
×
1303
        case isl_ast_node_block:
×
1304
            visit_block(node, scope);
×
1305
            break;
×
1306
        case isl_ast_node_user:
5✔
1307
            visit_user(node, scope);
5✔
1308
            break;
5✔
UNCOV
1309
        case isl_ast_node_mark: {
×
1310
            // Handle markers (e.g., parallelism annotations) here if needed
UNCOV
1311
            isl_ast_node *child = isl_ast_node_mark_get_node(node);
×
1312
            visit_node(child, scope);
×
1313
            isl_ast_node_free(child);
×
1314
            break;
×
1315
        }
×
1316
        default:
×
UNCOV
1317
            break;
×
1318
    }
13✔
1319
}
13✔
1320

1321
void ScopToSDFG::visit_for(isl_ast_node *node, structured_control_flow::Sequence &scope) {
8✔
1322
    // Extract Loop Components from ISL
1323
    isl_ast_expr *iterator = isl_ast_node_for_get_iterator(node);
8✔
1324
    isl_ast_expr *init = isl_ast_node_for_get_init(node);
8✔
1325
    isl_ast_expr *cond = isl_ast_node_for_get_cond(node);
8✔
1326
    isl_ast_expr *inc = isl_ast_node_for_get_inc(node);
8✔
1327
    isl_ast_node *body = isl_ast_node_for_get_body(node);
8✔
1328

1329
    // Convert to Symbolic
1330
    auto id = isl_ast_expr_get_id(iterator);
8✔
1331
    symbolic::Symbol sym_iter = symbolic::symbol(isl_id_get_name(id));
8✔
1332
    symbolic::Expression sym_init = convert_expr(init);
8✔
1333
    symbolic::Condition sym_cond = convert_cond(cond);
8✔
1334
    symbolic::Expression step = convert_expr(inc);
8✔
1335
    symbolic::Expression update_expr = symbolic::add(sym_iter, step);
8✔
1336

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

1339
    // Recurse into body
1340
    visit_node(body, loop.root());
8✔
1341

1342
    isl_ast_expr_free(iterator);
8✔
1343
    isl_ast_expr_free(init);
8✔
1344
    isl_ast_expr_free(cond);
8✔
1345
    isl_ast_expr_free(inc);
8✔
1346
    isl_ast_node_free(body);
8✔
1347
    isl_id_free(id);
8✔
1348
}
8✔
1349

UNCOV
1350
void ScopToSDFG::visit_if(isl_ast_node *node, structured_control_flow::Sequence &scope) {
×
UNCOV
1351
    isl_ast_expr *cond = isl_ast_node_if_get_cond(node);
×
1352
    isl_ast_node *then_node = isl_ast_node_if_get_then(node);
×
1353
    isl_ast_node *else_node = isl_ast_node_if_get_else(node);
×
1354

1355
    auto &if_stmt = builder_.add_if_else(scope);
×
1356

1357
    // Then Block
1358
    symbolic::Condition sym_cond = convert_cond(cond);
×
1359
    auto &then_seq = builder_.add_case(if_stmt, sym_cond);
×
1360
    visit_node(then_node, then_seq);
×
1361

1362
    // Else Block (if exists)
1363
    if (else_node) {
×
1364
        auto &else_seq = builder_.add_case(if_stmt, symbolic::Not(sym_cond));
×
UNCOV
1365
        visit_node(else_node, else_seq);
×
UNCOV
1366
    }
×
1367

1368
    isl_ast_expr_free(cond);
×
1369
    isl_ast_node_free(then_node);
×
1370
    isl_ast_node_free(else_node);
×
1371
}
×
1372

1373
void ScopToSDFG::visit_block(isl_ast_node *node, structured_control_flow::Sequence &scope) {
×
UNCOV
1374
    isl_ast_node_list *list = isl_ast_node_block_get_children(node);
×
1375
    int n = isl_ast_node_list_n_ast_node(list);
×
1376

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

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

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

1402
    if (id) {
5✔
1403
        std::string name = isl_id_get_name(id);
5✔
1404
        if (stmt_map_.count(name)) {
5✔
1405
            ScopStatement *stmt = stmt_map_[name];
5✔
1406

1407
            if (!stmt->expression().is_null()) {
5✔
1408
                std::string rhs = (*stmt->writes().begin())->data();
1✔
1409
                builder_.add_block(scope, {{symbolic::symbol(rhs), stmt->expression()}});
1✔
1410
            } else {
4✔
1411
                auto &new_block = builder_.add_block(scope);
4✔
1412
                auto &new_code_node =
4✔
1413
                    static_cast<data_flow::CodeNode &>(builder_.copy_node(new_block, *stmt->code_node()));
4✔
1414

1415
                // Create Access Node Mapping
1416
                auto &old_graph = stmt->code_node()->get_parent();
4✔
1417
                auto &new_graph = new_code_node.get_parent();
4✔
1418
                std::unordered_map<const data_flow::DataFlowNode *, data_flow::DataFlowNode *> in_node_map;
4✔
1419
                for (auto &iedge : old_graph.in_edges(*stmt->code_node())) {
4✔
1420
                    if (in_node_map.find(&iedge.src()) == in_node_map.end()) {
4✔
1421
                        in_node_map[&iedge.src()] = &builder_.copy_node(new_block, iedge.src());
4✔
1422
                    }
4✔
1423
                    builder_.add_memlet(
4✔
1424
                        new_block,
4✔
1425
                        *in_node_map[&iedge.src()],
4✔
1426
                        iedge.src_conn(),
4✔
1427
                        new_code_node,
4✔
1428
                        iedge.dst_conn(),
4✔
1429
                        iedge.subset(),
4✔
1430
                        iedge.base_type(),
4✔
1431
                        iedge.debug_info()
4✔
1432
                    );
4✔
1433
                }
4✔
1434

1435
                std::unordered_map<const data_flow::DataFlowNode *, data_flow::DataFlowNode *> out_node_map;
4✔
1436
                for (auto &oedge : old_graph.out_edges(*stmt->code_node())) {
4✔
1437
                    if (out_node_map.find(&oedge.dst()) == out_node_map.end()) {
4✔
1438
                        out_node_map[&oedge.dst()] = &builder_.copy_node(new_block, oedge.dst());
4✔
1439
                    }
4✔
1440
                    builder_.add_memlet(
4✔
1441
                        new_block,
4✔
1442
                        new_code_node,
4✔
1443
                        oedge.src_conn(),
4✔
1444
                        *out_node_map[&oedge.dst()],
4✔
1445
                        oedge.dst_conn(),
4✔
1446
                        oedge.subset(),
4✔
1447
                        oedge.base_type(),
4✔
1448
                        oedge.debug_info()
4✔
1449
                    );
4✔
1450
                }
4✔
1451
            }
4✔
1452
        }
5✔
1453
        isl_id_free(id);
5✔
1454
    }
5✔
1455
    isl_ast_expr_free(expr);
5✔
1456
}
5✔
1457

1458
symbolic::Expression ScopToSDFG::convert_expr(isl_ast_expr *expr) {
32✔
1459
    if (!expr) return SymEngine::null;
32✔
1460

1461
    isl_ast_expr_type type = isl_ast_expr_get_type(expr);
32✔
1462
    if (type == isl_ast_expr_int) {
32✔
1463
        isl_val *v = isl_ast_expr_get_val(expr);
18✔
1464
        long val = isl_val_get_num_si(v);
18✔
1465
        isl_val_free(v);
18✔
1466
        return symbolic::integer(val);
18✔
1467
    } else if (type == isl_ast_expr_id) {
18✔
1468
        isl_id *id = isl_ast_expr_get_id(expr);
14✔
1469
        std::string name = isl_id_get_name(id);
14✔
1470
        isl_id_free(id);
14✔
1471
        return symbolic::symbol(name);
14✔
1472
    } else if (type == isl_ast_expr_op) {
14✔
1473
        isl_ast_op_type op = isl_ast_expr_get_op_type(expr);
×
UNCOV
1474
        int n_args = isl_ast_expr_get_op_n_arg(expr);
×
1475

1476
        if (n_args == 1) {
×
1477
            isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
×
1478
            symbolic::Expression res;
×
1479
            if (op == isl_ast_op_minus) {
×
1480
                res = symbolic::mul(symbolic::integer(-1), convert_expr(arg0));
×
1481
            }
×
UNCOV
1482
            isl_ast_expr_free(arg0);
×
1483
            return res;
×
1484
        }
×
1485

1486
        if (n_args == 2) {
×
1487
            isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
×
1488
            isl_ast_expr *arg1 = isl_ast_expr_get_op_arg(expr, 1);
×
1489
            symbolic::Expression s0 = convert_expr(arg0);
×
1490
            symbolic::Expression s1 = convert_expr(arg1);
×
1491
            isl_ast_expr_free(arg0);
×
1492
            isl_ast_expr_free(arg1);
×
1493

1494
            switch (op) {
×
1495
                case isl_ast_op_add:
×
1496
                    return symbolic::add(s0, s1);
×
1497
                case isl_ast_op_sub:
×
1498
                    return symbolic::sub(s0, s1);
×
1499
                case isl_ast_op_mul:
×
1500
                    return symbolic::mul(s0, s1);
×
1501
                case isl_ast_op_div:
×
1502
                case isl_ast_op_pdiv_q:
×
1503
                case isl_ast_op_fdiv_q:
×
1504
                    return symbolic::div(s0, s1);
×
1505
                case isl_ast_op_pdiv_r:
×
1506
                case isl_ast_op_zdiv_r:
×
1507
                    return symbolic::mod(s0, s1);
×
UNCOV
1508
                case isl_ast_op_min:
×
1509
                    return symbolic::min(s0, s1);
×
1510
                case isl_ast_op_max:
×
UNCOV
1511
                    return symbolic::max(s0, s1);
×
1512
                default:
×
1513
                    break;
×
1514
            }
×
UNCOV
1515
        }
×
1516
    }
×
1517
    return SymEngine::null;
×
1518
}
32✔
1519

1520
symbolic::Condition ScopToSDFG::convert_cond(isl_ast_expr *expr) {
8✔
1521
    if (!expr) return SymEngine::null;
8✔
1522

1523
    if (isl_ast_expr_get_type(expr) != isl_ast_expr_op) {
8✔
UNCOV
1524
        return SymEngine::null;
×
1525
    }
×
1526

1527
    isl_ast_op_type op = isl_ast_expr_get_op_type(expr);
8✔
1528
    int n_args = isl_ast_expr_get_op_n_arg(expr);
8✔
1529

1530
    if (n_args == 2) {
8✔
1531
        isl_ast_expr *arg0 = isl_ast_expr_get_op_arg(expr, 0);
8✔
1532
        isl_ast_expr *arg1 = isl_ast_expr_get_op_arg(expr, 1);
8✔
1533

1534
        symbolic::Condition res;
8✔
1535

1536
        switch (op) {
8✔
1537
            case isl_ast_op_eq:
×
1538
                res = symbolic::Eq(convert_expr(arg0), convert_expr(arg1));
×
1539
                break;
×
1540
            case isl_ast_op_lt:
6✔
1541
                res = symbolic::Lt(convert_expr(arg0), convert_expr(arg1));
6✔
1542
                break;
6✔
1543
            case isl_ast_op_le:
2✔
1544
                res = symbolic::Le(convert_expr(arg0), convert_expr(arg1));
2✔
1545
                break;
2✔
1546
            case isl_ast_op_gt:
×
1547
                res = symbolic::Gt(convert_expr(arg0), convert_expr(arg1));
×
1548
                break;
×
1549
            case isl_ast_op_ge:
×
1550
                res = symbolic::Ge(convert_expr(arg0), convert_expr(arg1));
×
1551
                break;
×
UNCOV
1552
            case isl_ast_op_and:
×
1553
            case isl_ast_op_and_then:
×
1554
                res = symbolic::And(convert_cond(arg0), convert_cond(arg1));
×
1555
                break;
×
1556
            case isl_ast_op_or:
×
UNCOV
1557
            case isl_ast_op_or_else:
×
1558
                res = symbolic::Or(convert_cond(arg0), convert_cond(arg1));
×
1559
                break;
×
UNCOV
1560
            default:
×
1561
                break;
×
1562
        }
8✔
1563

1564
        isl_ast_expr_free(arg0);
8✔
1565
        isl_ast_expr_free(arg1);
8✔
1566
        return res;
8✔
1567
    }
8✔
1568

1569
    return SymEngine::null;
×
1570
}
8✔
1571

1572
ScopAnalysis::ScopAnalysis(StructuredSDFG &sdfg) : Analysis(sdfg) {}
×
1573

1574
void ScopAnalysis::run(analysis::AnalysisManager &analysis_manager) {
×
1575
    this->scops_.clear();
×
1576
    this->dependences_.clear();
×
1577

1578
    auto &loop_analysis = analysis_manager.get<LoopAnalysis>();
×
1579
    for (auto &loop : loop_analysis.loops()) {
×
1580
        if (!dynamic_cast<structured_control_flow::StructuredLoop *>(loop)) {
×
1581
            continue;
×
1582
        }
×
UNCOV
1583
        auto sloop = static_cast<structured_control_flow::StructuredLoop *>(loop);
×
1584

1585
        ScopBuilder builder(this->sdfg_, *sloop);
×
1586
        auto scop = builder.build(analysis_manager);
×
1587
        if (!scop) {
×
UNCOV
1588
            continue;
×
1589
        }
×
1590
        auto dependences = std::make_unique<Dependences>(*scop);
×
1591
        if (!dependences->has_valid_dependences()) {
×
UNCOV
1592
            continue;
×
1593
        }
×
1594

1595
        this->scops_[loop] = std::move(scop);
×
1596
        this->dependences_[loop] = std::move(dependences);
×
1597
    }
×
UNCOV
1598
}
×
1599

UNCOV
1600
bool ScopAnalysis::has(const structured_control_flow::ControlFlowNode *node) const {
×
UNCOV
1601
    return this->scops_.find(node) != this->scops_.end();
×
UNCOV
1602
}
×
1603

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

UNCOV
1606
const Dependences &ScopAnalysis::dependences(const structured_control_flow::ControlFlowNode *node) const {
×
UNCOV
1607
    return *this->dependences_.at(node);
×
UNCOV
1608
}
×
1609

1610
} // namespace analysis
1611
} // 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