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

daisytuner / sdfglib / 16779684622

06 Aug 2025 02:21PM UTC coverage: 64.3% (-1.0%) from 65.266%
16779684622

push

github

web-flow
Merge pull request #172 from daisytuner/opaque-pointers

Opaque pointers, typed memlets, untyped tasklet connectors

330 of 462 new or added lines in 38 files covered. (71.43%)

382 existing lines in 30 files now uncovered.

8865 of 13787 relevant lines covered (64.3%)

116.73 hits per line

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

46.51
/src/transformations/loop_slicing.cpp
1
#include "sdfg/transformations/loop_slicing.h"
2
#include <stdexcept>
3

4
#include "sdfg/analysis/assumptions_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)
1✔
16
    : loop_(loop) {
1✔
17

18
      };
1✔
19

UNCOV
20
std::string LoopSlicing::name() const { return "LoopSlicing"; };
×
21

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

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

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

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

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

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

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

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

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

99
            return false;
×
100
        }
1✔
101
    }
×
102

103
    return false;
×
104
};
1✔
105

106
void LoopSlicing::apply(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
1✔
107
    auto& sdfg = builder.subject();
1✔
108

109
    auto& body = loop_.root();
1✔
110
    auto indvar = loop_.indvar();
1✔
111

112
    // Collect loop locals
113
    auto& users = analysis_manager.get<analysis::Users>();
1✔
114
    auto locals = users.locals(body);
1✔
115

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

132
    auto branch_1 = if_else->at(0);
1✔
133
    auto condition_1 = branch_1.second;
1✔
134
    auto bound = analysis::LoopAnalysis::canonical_bound(&loop_, analysis_manager.get<analysis::AssumptionsAnalysis>());
1✔
135

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

147
    auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
1✔
148
    auto parent = static_cast<structured_control_flow::Sequence*>(scope_analysis.parent_scope(&loop_));
1✔
149

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

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

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

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

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

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

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

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

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

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

265
    body_body_slice.replace(indvar, indvar_slice);
1✔
266

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

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

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

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

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

287
    analysis_manager.invalidate_all();
1✔
288
};
1✔
289

UNCOV
290
void LoopSlicing::to_json(nlohmann::json& j) const {
×
UNCOV
291
    std::string loop_type;
×
UNCOV
292
    if (dynamic_cast<structured_control_flow::For*>(&loop_)) {
×
UNCOV
293
        loop_type = "for";
×
UNCOV
294
    } else if (dynamic_cast<structured_control_flow::Map*>(&loop_)) {
×
295
        loop_type = "map";
×
296
    } else {
×
297
        throw std::runtime_error("Unsupported loop type for serialization of loop: " + loop_.indvar()->get_name());
×
298
    }
299

UNCOV
300
    j["transformation_type"] = this->name();
×
UNCOV
301
    j["subgraph"] = {{"0", {{"element_id", this->loop_.element_id()}, {"type", loop_type}}}};
×
UNCOV
302
};
×
303

UNCOV
304
LoopSlicing LoopSlicing::from_json(builder::StructuredSDFGBuilder& builder, const nlohmann::json& desc) {
×
UNCOV
305
    auto loop_id = desc["subgraph"]["0"]["element_id"].get<size_t>();
×
UNCOV
306
    auto element = builder.find_element_by_id(loop_id);
×
UNCOV
307
    if (element == nullptr) {
×
308
        throw std::runtime_error("Element with ID " + std::to_string(loop_id) + " not found.");
×
309
    }
UNCOV
310
    auto loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element);
×
311

UNCOV
312
    if (loop == nullptr) {
×
313
        throw std::runtime_error("Element with ID " + std::to_string(loop_id) + " is not a For loop.");
×
314
    }
315

UNCOV
316
    return LoopSlicing(*loop);
×
317
};
×
318

319
} // namespace transformations
320
} // 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