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

daisytuner / sdfglib / 15796425724

21 Jun 2025 02:00PM UTC coverage: 64.059% (-0.5%) from 64.591%
15796425724

push

github

web-flow
Merge pull request #95 from daisytuner/data-dependencies

Extends Data Dependency Analysis

993 of 1197 new or added lines in 19 files covered. (82.96%)

55 existing lines in 5 files now uncovered.

8058 of 12579 relevant lines covered (64.06%)

150.28 hits per line

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

54.73
/src/transformations/loop_slicing.cpp
1
#include "sdfg/transformations/loop_slicing.h"
2

3
#include "sdfg/analysis/assumptions_analysis.h"
4
#include "sdfg/analysis/data_parallelism_analysis.h"
5
#include "sdfg/analysis/loop_analysis.h"
6
#include "sdfg/analysis/scope_analysis.h"
7
#include "sdfg/deepcopy/structured_sdfg_deep_copy.h"
8
#include "sdfg/structured_control_flow/structured_loop.h"
9

10
namespace sdfg {
11
namespace transformations {
12

13
enum class LoopSlicingType { Init, Bound, Split_Lt, Split_Le };
14

15
LoopSlicing::LoopSlicing(structured_control_flow::StructuredLoop& loop)
4✔
16
    : loop_(loop) {
4✔
17

18
      };
4✔
19

20
std::string LoopSlicing::name() const { return "LoopSlicing"; };
2✔
21

22
bool LoopSlicing::can_be_applied(builder::StructuredSDFGBuilder& builder,
4✔
23
                                 analysis::AnalysisManager& analysis_manager) {
24
    auto& sdfg = builder.subject();
4✔
25

26
    auto& assumptions_analysis = analysis_manager.get<analysis::AssumptionsAnalysis>();
4✔
27
    if (!analysis::LoopAnalysis::is_contiguous(&loop_, assumptions_analysis)) {
4✔
UNCOV
28
        return false;
×
29
    }
30

31
    // Collect moving symbols
32
    std::unordered_set<std::string> moving_symbols;
4✔
33
    auto& all_users = analysis_manager.get<analysis::Users>();
4✔
34
    auto& body = loop_.root();
4✔
35
    analysis::UsersView users(all_users, body);
4✔
36
    for (auto& entry : users.writes()) {
4✔
37
        auto& type = sdfg.type(entry->container());
×
38
        if (!dynamic_cast<const types::Scalar*>(&type)) {
×
39
            continue;
×
40
        }
41
        if (!types::is_integer(type.primitive_type())) {
×
42
            continue;
×
43
        }
44
        moving_symbols.insert(entry->container());
×
45
    }
46

47
    // Check if loop is sliced by if-elses
48
    auto indvar = loop_.indvar();
4✔
49
    for (size_t i = 0; i < body.size(); i++) {
4✔
50
        auto child = body.at(i);
4✔
51
        if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&child.first)) {
4✔
52
            if (child.second.assignments().size() > 0) {
4✔
53
                return false;
×
54
            }
55
            if (if_else->size() != 2) {
4✔
56
                return false;
×
57
            }
58

59
            // Validate condition
60
            auto branch_1 = if_else->at(0);
4✔
61
            auto condition_1 = branch_1.second;
4✔
62
            if (!symbolic::uses(condition_1, indvar)) {
4✔
63
                return false;
×
64
            }
65
            auto condition_2 = if_else->at(1).second;
4✔
66
            if (!symbolic::eq(condition_1, condition_2->logical_not())) {
4✔
67
                return false;
×
68
            }
69
            for (auto& atom : symbolic::atoms(condition_1)) {
8✔
70
                auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(atom);
4✔
71
                if (moving_symbols.find(sym->get_name()) != moving_symbols.end()) {
4✔
72
                    return false;
×
73
                }
74
            }
4✔
75
            auto bound = analysis::DataParallelismAnalysis::bound(loop_);
4✔
76
            if (bound == SymEngine::null) {
4✔
77
                return false;
×
78
            }
79

80
            // Case: indvar == init
81
            if (symbolic::eq(condition_1, symbolic::Eq(indvar, loop_.init()))) {
4✔
82
                return true;
4✔
83
            }
84

85
            // Case: indvar == bound - 1
86
            if (symbolic::eq(condition_1,
×
87
                             symbolic::Eq(indvar, symbolic::sub(bound, symbolic::one())))) {
×
88
                return true;
×
89
            }
90

91
            // Case: indvar < new_bound
92
            if (SymEngine::is_a<SymEngine::StrictLessThan>(*condition_1)) {
×
93
                return true;
×
94
            }
95

96
            // Case: indvar <= new_bound
97
            if (SymEngine::is_a<SymEngine::LessThan>(*condition_1)) {
×
98
                return true;
×
99
            }
100

101
            return false;
×
102
        }
4✔
103
    }
×
104

105
    return false;
×
106
};
4✔
107

108
void LoopSlicing::apply(builder::StructuredSDFGBuilder& builder,
4✔
109
                        analysis::AnalysisManager& analysis_manager) {
110
    auto& sdfg = builder.subject();
4✔
111

112
    auto& body = loop_.root();
4✔
113
    auto indvar = loop_.indvar();
4✔
114

115
    // Collect loop locals
116
    auto& users = analysis_manager.get<analysis::Users>();
4✔
117
    auto locals = users.locals(body);
4✔
118

119
    // Find the if-else that slices the loop
120
    structured_control_flow::IfElse* if_else = nullptr;
4✔
121
    size_t if_else_index = 0;
4✔
122
    for (size_t i = 0; i < body.size(); i++) {
4✔
123
        auto child = body.at(i);
4✔
124
        auto if_else_ = dynamic_cast<structured_control_flow::IfElse*>(&child.first);
4✔
125
        if (if_else_) {
4✔
126
            if_else_index = i;
4✔
127
            if_else = if_else_;
4✔
128
            break;
4✔
129
        }
130
    }
×
131
    if (if_else == nullptr) {
4✔
132
        throw InvalidSDFGException("LoopSlicing: Expected IfElse");
×
133
    }
134

135
    auto branch_1 = if_else->at(0);
4✔
136
    auto condition_1 = branch_1.second;
4✔
137
    auto bound = analysis::DataParallelismAnalysis::bound(loop_);
4✔
138

139
    LoopSlicingType slice_type = LoopSlicingType::Init;
4✔
140
    if (symbolic::eq(condition_1, symbolic::Eq(indvar, loop_.init()))) {
4✔
141
        slice_type = LoopSlicingType::Init;
4✔
142
    } else if (symbolic::eq(condition_1,
4✔
143
                            symbolic::Eq(indvar, symbolic::sub(bound, symbolic::one())))) {
×
144
        slice_type = LoopSlicingType::Bound;
×
145
    } else if (SymEngine::is_a<SymEngine::StrictLessThan>(*condition_1)) {
×
146
        slice_type = LoopSlicingType::Split_Lt;
×
147
    } else if (SymEngine::is_a<SymEngine::LessThan>(*condition_1)) {
×
148
        slice_type = LoopSlicingType::Split_Le;
×
149
    }
×
150

151
    auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
4✔
152
    auto parent =
4✔
153
        static_cast<structured_control_flow::Sequence*>(scope_analysis.parent_scope(&loop_));
4✔
154

155
    // Slice loop
156
    auto indvar_slice_str = builder.find_new_name(indvar->get_name());
4✔
157
    builder.add_container(indvar_slice_str, sdfg.type(indvar->get_name()));
4✔
158
    auto indvar_slice = SymEngine::symbol(indvar_slice_str);
4✔
159
    structured_control_flow::For* loop_slice = nullptr;
4✔
160
    structured_control_flow::For* loop_slice_2 = nullptr;
4✔
161
    switch (slice_type) {
4✔
162
        case LoopSlicingType::Init: {
163
            auto init_slice = loop_.init();
4✔
164
            auto condition_slice =
165
                symbolic::Lt(indvar_slice, symbolic::add(loop_.init(), symbolic::one()));
4✔
166
            auto increment_slice = symbolic::add(indvar_slice, symbolic::one());
4✔
167
            loop_slice = &builder
8✔
168
                              .add_for_before(*parent, loop_, indvar_slice, condition_slice,
4✔
169
                                              init_slice, increment_slice)
170
                              .first;
4✔
171
            loop_slice_2 =
4✔
172
                &builder
4✔
173
                     .add_for_after(*parent, loop_, loop_.indvar(), loop_.condition(),
8✔
174
                                    symbolic::add(loop_.init(), symbolic::one()), loop_.update())
4✔
175
                     .first;
4✔
176
            break;
177
        }
4✔
178
        case LoopSlicingType::Bound: {
179
            auto init_slice = symbolic::sub(bound, symbolic::one());
×
180
            auto condition_slice = symbolic::subs(loop_.condition(), loop_.indvar(), indvar_slice);
×
181
            auto increment_slice = symbolic::add(indvar_slice, symbolic::one());
×
182
            loop_slice = &builder
×
183
                              .add_for_after(*parent, loop_, indvar_slice, condition_slice,
×
184
                                             init_slice, increment_slice)
185
                              .first;
×
186
            loop_slice_2 = &builder
×
187
                                .add_for_before(
×
188
                                    *parent, loop_, loop_.indvar(),
×
189
                                    symbolic::Lt(loop_.indvar(),
×
190
                                                 symbolic::sub(loop_.condition(), symbolic::one())),
×
191
                                    loop_.init(), loop_.update())
×
192
                                .first;
×
193
            break;
194
        }
×
195
        case LoopSlicingType::Split_Lt: {
196
            auto init_slice = loop_.init();
×
197
            auto condition_slice =
198
                symbolic::And(symbolic::subs(condition_1, indvar, indvar_slice),
×
199
                              symbolic::subs(loop_.condition(), indvar, indvar_slice));
×
200
            auto increment_slice = symbolic::add(indvar_slice, symbolic::one());
×
201
            loop_slice = &builder
×
202
                              .add_for_before(*parent, loop_, indvar_slice, condition_slice,
×
203
                                              init_slice, increment_slice)
204
                              .first;
×
205

206
            auto condition_bound =
207
                SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(condition_1);
×
208
            auto condition_bound_args = condition_bound->get_args();
×
209
            auto condition_bound_args_bound = condition_bound_args.at(0);
×
210
            if (symbolic::eq(condition_bound_args_bound, loop_.indvar())) {
×
211
                condition_bound_args_bound = condition_bound_args.at(1);
×
212
            }
×
213

214
            loop_slice_2 = &builder
×
215
                                .add_for_after(*parent, loop_, loop_.indvar(), loop_.condition(),
×
216
                                               condition_bound_args_bound, loop_.update())
×
217
                                .first;
×
218
            break;
219
        }
×
220
        case LoopSlicingType::Split_Le: {
221
            auto init_slice = loop_.init();
×
222
            auto condition_slice =
223
                symbolic::And(symbolic::subs(condition_1, indvar, indvar_slice),
×
224
                              symbolic::subs(loop_.condition(), indvar, indvar_slice));
×
225
            auto increment_slice = symbolic::add(indvar_slice, symbolic::one());
×
226
            loop_slice = &builder
×
227
                              .add_for_before(*parent, loop_, indvar_slice, condition_slice,
×
228
                                              init_slice, increment_slice)
229
                              .first;
×
230

231
            auto condition_bound =
232
                SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(condition_1);
×
233
            auto condition_bound_args = condition_bound->get_args();
×
234
            auto condition_bound_args_bound = condition_bound_args.at(0);
×
235
            if (symbolic::eq(condition_bound_args_bound, loop_.indvar())) {
×
236
                condition_bound_args_bound = condition_bound_args.at(1);
×
237
            }
×
238

239
            loop_slice_2 =
×
240
                &builder
×
241
                     .add_for_after(*parent, loop_, loop_.indvar(), loop_.condition(),
×
242
                                    symbolic::add(condition_bound_args_bound, symbolic::one()),
×
243
                                    loop_.update())
×
244
                     .first;
×
245
            break;
246
        }
×
247
    }
248

249
    // Move loop locals to the new loop slice
250
    auto& body_slice = loop_slice->root();
4✔
251

252
    deepcopy::StructuredSDFGDeepCopy deep_copy(builder, body_slice, body);
4✔
253
    deep_copy.copy();
4✔
254
    auto& body_body_slice =
4✔
255
        dynamic_cast<structured_control_flow::Sequence&>(body_slice.at(0).first);
4✔
256

257
    auto& if_else_slice =
4✔
258
        dynamic_cast<structured_control_flow::IfElse&>(body_body_slice.at(if_else_index).first);
4✔
259
    auto& slice = builder.add_sequence_before(body_body_slice, if_else_slice).first;
4✔
260

261
    deepcopy::StructuredSDFGDeepCopy deep_copy_slice(builder, slice, if_else_slice.at(0).first);
4✔
262
    deep_copy_slice.copy();
4✔
263

264
    builder.remove_child(body_body_slice, if_else_index + 1);
4✔
265

266
    body_body_slice.replace(indvar, indvar_slice);
4✔
267

268
    // Update remaining loop
269
    builder.remove_case(*if_else, 0);
4✔
270

271
    auto& else_slice = builder.add_sequence_before(body, *if_else).first;
4✔
272
    deepcopy::StructuredSDFGDeepCopy deep_copy_else(builder, else_slice, if_else->at(0).first);
4✔
273
    deep_copy_else.copy();
4✔
274

275
    builder.remove_child(body, if_else_index + 1);
4✔
276

277
    // Rename all loop-local variables to break artificial dependencies
278
    for (auto& local : locals) {
4✔
279
        auto new_local = builder.find_new_name(local);
×
280
        builder.add_container(new_local, sdfg.type(local));
×
281
        loop_slice->root().replace(symbolic::symbol(local), symbolic::symbol(new_local));
×
282
    }
×
283

284
    // Move loop locals to the new loop
285
    builder.insert_children(loop_slice_2->root(), loop_.root(), 0);
4✔
286
    builder.remove_child(*parent, loop_);
4✔
287

288
    analysis_manager.invalidate_all();
4✔
289
};
4✔
290

291
void LoopSlicing::to_json(nlohmann::json& j) const {
2✔
292
    j["transformation_type"] = this->name();
2✔
293
    j["loop_element_id"] = loop_.element_id();
2✔
294
};
2✔
295

296
LoopSlicing LoopSlicing::from_json(builder::StructuredSDFGBuilder& builder,
1✔
297
                                   const nlohmann::json& desc) {
298
    auto loop_id = desc["loop_element_id"].get<size_t>();
1✔
299
    auto element = builder.find_element_by_id(loop_id);
1✔
300
    if (!element) {
1✔
301
        throw InvalidTransformationDescriptionException("Element with ID " +
×
302
                                                        std::to_string(loop_id) + " not found.");
×
303
    }
304
    auto loop = dynamic_cast<structured_control_flow::For*>(element);
1✔
305

306
    return LoopSlicing(*loop);
1✔
307
};
×
308

309
}  // namespace transformations
310
}  // 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