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

daisytuner / sdfglib / 20814928552

08 Jan 2026 11:14AM UTC coverage: 62.374% (+0.04%) from 62.339%
20814928552

Pull #441

github

web-flow
Merge 2499fcd3e into eb5229985
Pull Request #441: modernize out local storage

66 of 70 new or added lines in 1 file covered. (94.29%)

13 existing lines in 1 file now uncovered.

15029 of 24095 relevant lines covered (62.37%)

88.84 hits per line

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

81.93
/src/transformations/out_local_storage.cpp
1
#include "sdfg/transformations/out_local_storage.h"
2

3
#include <cassert>
4
#include <cstddef>
5
#include <string>
6

7
#include "sdfg/analysis/scope_analysis.h"
8
#include "sdfg/analysis/type_analysis.h"
9
#include "sdfg/builder/structured_sdfg_builder.h"
10
#include "sdfg/data_flow/access_node.h"
11
#include "sdfg/data_flow/memlet.h"
12
#include "sdfg/passes/structured_control_flow/dead_cfg_elimination.h"
13
#include "sdfg/passes/structured_control_flow/sequence_fusion.h"
14
#include "sdfg/structured_control_flow/sequence.h"
15
#include "sdfg/structured_control_flow/structured_loop.h"
16
#include "sdfg/symbolic/symbolic.h"
17
#include "sdfg/transformations/utils.h"
18
#include "sdfg/types/array.h"
19
#include "sdfg/types/scalar.h"
20

21
namespace sdfg {
22
namespace transformations {
23

24
OutLocalStorage::OutLocalStorage(structured_control_flow::StructuredLoop& loop, std::string container)
25
    : loop_(loop), container_(container) {};
3✔
26

27
std::string OutLocalStorage::name() const { return "OutLocalStorage"; };
×
28

29
bool OutLocalStorage::can_be_applied(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
3✔
30
    auto& body = this->loop_.root();
3✔
31
    this->requires_array_ = false;
3✔
32

33
    // Criterion: Check if container exists and is used in the loop
34
    auto& users = analysis_manager.get<analysis::Users>();
3✔
35
    analysis::UsersView body_users(users, body);
3✔
36
    if (body_users.uses(this->container_).size() == 0) {
3✔
37
        return false;
1✔
38
    }
1✔
39

40
    // Criterion: Check if all accesses to the container within the loop are identical
41
    auto accesses = body_users.uses(this->container_);
2✔
42
    auto first_access = accesses.at(0);
2✔
43
    auto first_subset = first_access->subsets().at(0);
2✔
44

45
    if (accesses.size() > 1) {
2✔
46
        for (auto access : accesses) {
4✔
47
            if (first_access->subsets().size() != access->subsets().size()) {
4✔
48
                return false;
×
49
            }
×
50
            for (size_t i = 0; i < first_access->subsets().size(); i++) {
8✔
51
                auto subset = access->subsets().at(i);
4✔
52
                if (first_subset.size() != subset.size()) {
4✔
53
                    return false;
×
54
                }
×
55
                for (size_t j = 0; j < first_subset.size(); j++) {
6✔
56
                    if (!symbolic::eq(first_subset.at(j), subset.at(j))) {
2✔
57
                        return false;
×
58
                    }
×
59
                }
2✔
60
            }
4✔
61
        }
4✔
62
    }
2✔
63

64
    // Criterion: Check if accesses do not depend on containers written in the loop
65
    auto writes = body_users.writes();
2✔
66
    symbolic::SymbolSet written_containers;
2✔
67
    for (auto write : writes) {
2✔
68
        written_containers.insert(symbolic::symbol(write->container()));
2✔
69
    }
2✔
70
    for (auto subset : first_access->subsets()) {
2✔
71
        for (auto access : subset) {
2✔
72
            for (auto atom : symbolic::atoms(access)) {
1✔
73
                if (written_containers.contains(atom)) {
1✔
74
                    return false;
×
75
                }
×
76
            }
1✔
77
        }
1✔
78
    }
2✔
79

80
    // Soft Criterion: Check if the accesses do not depend on the loop iteration
81
    // Decide if an array or scalar is required
82
    for (auto subset : first_access->subsets()) {
2✔
83
        for (auto access : subset) {
2✔
84
            for (auto atom : symbolic::atoms(access)) {
1✔
85
                if (symbolic::eq(atom, this->loop_.indvar())) {
1✔
86
                    this->requires_array_ = true;
1✔
87
                    break;
1✔
88
                }
1✔
89
            }
1✔
90
            if (this->requires_array_) {
1✔
91
                break;
1✔
92
            }
1✔
93
        }
1✔
94
        if (this->requires_array_) {
2✔
95
            break;
1✔
96
        }
1✔
97
    }
2✔
98

99
    // Criterion: Check if the loop iteration count is known and an Integer when an array is
100
    // required
101
    if (this->requires_array_) {
2✔
102
        auto iteration_count = get_iteration_count(this->loop_);
1✔
103
        if (iteration_count == SymEngine::null) {
1✔
104
            return false;
×
105
        }
×
106
    }
1✔
107

108
    return true;
2✔
109
};
2✔
110

111
void OutLocalStorage::apply(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
2✔
112
    if (requires_array_) {
2✔
113
        apply_array(builder, analysis_manager);
1✔
114
    } else {
1✔
115
        apply_scalar(builder, analysis_manager);
1✔
116
    }
1✔
117

118
    // End of transformation
119

120
    analysis_manager.invalidate_all();
2✔
121

122
    passes::SequenceFusion sf_pass;
2✔
123
    passes::DeadCFGElimination dce_pass;
2✔
124
    bool applies = false;
2✔
125
    do {
2✔
126
        applies = false;
2✔
127
        applies |= dce_pass.run(builder, analysis_manager);
2✔
128
        applies |= sf_pass.run(builder, analysis_manager);
2✔
129
    } while (applies);
2✔
130
};
2✔
131

132
void OutLocalStorage::apply_array(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
1✔
133
    auto& sdfg = builder.subject();
1✔
134
    auto& users = analysis_manager.get<analysis::Users>();
1✔
135
    auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
1✔
136
    auto parent_node = scope_analysis.parent_scope(&loop_);
1✔
137
    auto parent = dynamic_cast<structured_control_flow::Sequence*>(parent_node);
1✔
138
    if (!parent) {
1✔
NEW
139
        throw InvalidSDFGException("OutLocalStorage: Parent of loop must be a Sequence!");
×
NEW
140
    }
×
141

142
    analysis::TypeAnalysis type_analysis(sdfg, &loop_, analysis_manager);
1✔
143
    auto type = type_analysis.get_outer_type(container_);
1✔
144

145
    auto replacement_name = "__daisy_out_local_storage_" + this->container_;
1✔
146

147
    auto iteration_count = get_iteration_count(this->loop_);
1✔
148
    types::Scalar scalar_type(type->primitive_type());
1✔
149
    types::Array array_type(scalar_type, iteration_count);
1✔
150
    builder.add_container(replacement_name, array_type);
1✔
151

152
    auto indvar_name = "__daisy_out_local_storage_" + this->loop_.indvar()->get_name();
1✔
153
    types::Scalar indvar_type(sdfg.type(loop_.indvar()->get_name()).primitive_type());
1✔
154
    builder.add_container(indvar_name, indvar_type);
1✔
155
    auto indvar = symbolic::symbol(indvar_name);
1✔
156
    auto init = loop_.init();
1✔
157
    auto update = symbolic::subs(loop_.update(), loop_.indvar(), indvar);
1✔
158
    auto condition = symbolic::subs(loop_.condition(), loop_.indvar(), indvar);
1✔
159

160
    analysis::UsersView body_users(users, loop_.root());
1✔
161
    auto accesses = body_users.uses(this->container_);
1✔
162
    auto first_access = accesses.at(0);
1✔
163
    auto first_subset = first_access->subsets().at(0);
1✔
164
    auto& init_loop = builder.add_for_before(*parent, loop_, indvar, condition, init, update, {}, loop_.debug_info());
1✔
165
    auto& init_body = init_loop.root();
1✔
166
    auto& init_block = builder.add_block(init_body);
1✔
167
    auto& init_access_read = builder.add_access(init_block, this->container_);
1✔
168
    auto& init_access_write = builder.add_access(init_block, replacement_name);
1✔
169
    auto& init_tasklet = builder.add_tasklet(init_block, data_flow::TaskletCode::assign, "_out", {"_in"});
1✔
170
    auto& init_memlet_in =
1✔
171
        builder.add_computational_memlet(init_block, init_access_read, init_tasklet, "_in", first_subset, *type);
1✔
172
    init_memlet_in.replace(loop_.indvar(), indvar);
1✔
173
    builder.add_computational_memlet(init_block, init_tasklet, "_out", init_access_write, {indvar}, array_type);
1✔
174

175
    auto& reset_loop = builder.add_for_after(*parent, loop_, indvar, condition, init, update, {}, loop_.debug_info());
1✔
176
    auto& reset_body = reset_loop.root();
1✔
177
    auto& reset_block = builder.add_block(reset_body);
1✔
178
    auto& reset_access_read = builder.add_access(reset_block, replacement_name);
1✔
179
    auto& reset_access_write = builder.add_access(reset_block, this->container_);
1✔
180
    auto& reset_tasklet = builder.add_tasklet(reset_block, data_flow::TaskletCode::assign, "_out", {"_in"});
1✔
181
    builder.add_computational_memlet(reset_block, reset_access_read, reset_tasklet, "_in", {indvar}, array_type);
1✔
182
    auto& reset_memlet_out =
1✔
183
        builder.add_computational_memlet(reset_block, reset_tasklet, "_out", reset_access_write, first_subset, *type);
1✔
184
    reset_memlet_out.replace(loop_.indvar(), indvar);
1✔
185

186
    for (auto user : body_users.uses(this->container_)) {
2✔
187
        auto element = user->element();
2✔
188
        if (auto memlet = dynamic_cast<data_flow::Memlet*>(element)) {
2✔
189
            auto subset = memlet->subset();
×
190
            subset.clear();
×
191
            subset.push_back(this->loop_.indvar());
×
192
            memlet->set_subset(subset);
×
193
        }
×
194
    }
2✔
195

196
    for (auto user : body_users.uses(this->container_)) {
2✔
197
        auto element = user->element();
2✔
198
        if (auto access = dynamic_cast<data_flow::AccessNode*>(element)) {
2✔
199
            for (auto& iedge : access->get_parent().in_edges(*access)) {
2✔
200
                auto memlet = &iedge;
1✔
201
                auto subset = memlet->subset();
1✔
202
                subset.clear();
1✔
203
                subset.push_back(this->loop_.indvar());
1✔
204
                memlet->set_subset(subset);
1✔
205
                memlet->set_base_type(array_type);
1✔
206
            }
1✔
207
            for (auto& oedge : access->get_parent().out_edges(*access)) {
2✔
208
                auto memlet = &oedge;
1✔
209
                auto subset = memlet->subset();
1✔
210
                subset.clear();
1✔
211
                subset.push_back(this->loop_.indvar());
1✔
212
                memlet->set_subset(subset);
1✔
213
                memlet->set_base_type(array_type);
1✔
214
            }
1✔
215
        }
2✔
216
    }
2✔
217

218
    loop_.replace(symbolic::symbol(this->container_), symbolic::symbol(replacement_name));
1✔
219
};
1✔
220

221
void OutLocalStorage::apply_scalar(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
1✔
222
    auto& sdfg = builder.subject();
1✔
223
    auto& users = analysis_manager.get<analysis::Users>();
1✔
224
    auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
1✔
225
    auto parent_node = scope_analysis.parent_scope(&loop_);
1✔
226
    auto parent = dynamic_cast<structured_control_flow::Sequence*>(parent_node);
1✔
227
    if (!parent) {
1✔
NEW
228
        throw InvalidSDFGException("OutLocalStorage: Parent of loop must be a Sequence!");
×
NEW
229
    }
×
230

231
    analysis::TypeAnalysis type_analysis(sdfg, &loop_, analysis_manager);
1✔
232
    auto type = type_analysis.get_outer_type(container_);
1✔
233

234
    auto replacement_name = "__daisy_out_local_storage_" + this->container_;
1✔
235

236
    types::Scalar scalar_type(type->primitive_type());
1✔
237
    builder.add_container(replacement_name, scalar_type);
1✔
238

239
    analysis::UsersView body_users(users, loop_.root());
1✔
240
    auto accesses = body_users.uses(this->container_);
1✔
241
    auto first_access = accesses.at(0);
1✔
242
    auto first_subset = first_access->subsets().at(0);
1✔
243
    auto& init_block = builder.add_block_before(*parent, loop_, {}, loop_.debug_info());
1✔
244
    auto& init_access_read = builder.add_access(init_block, this->container_);
1✔
245
    auto& init_access_write = builder.add_access(init_block, replacement_name);
1✔
246
    auto& init_tasklet = builder.add_tasklet(init_block, data_flow::TaskletCode::assign, "_out", {"_in"});
1✔
247
    builder.add_computational_memlet(init_block, init_access_read, init_tasklet, "_in", first_subset, *type);
1✔
248
    builder.add_computational_memlet(init_block, init_tasklet, "_out", init_access_write, {}, scalar_type);
1✔
249

250
    auto& reset_block = builder.add_block_after(*parent, loop_, {}, loop_.debug_info());
1✔
251
    auto& reset_access_read = builder.add_access(reset_block, replacement_name);
1✔
252
    auto& reset_access_write = builder.add_access(reset_block, this->container_);
1✔
253
    auto& reset_tasklet = builder.add_tasklet(reset_block, data_flow::TaskletCode::assign, "_out", {"_in"});
1✔
254
    builder.add_computational_memlet(reset_block, reset_access_read, reset_tasklet, "_in", {}, scalar_type);
1✔
255
    builder.add_computational_memlet(reset_block, reset_tasklet, "_out", reset_access_write, first_subset, *type);
1✔
256

257
    for (auto user : body_users.uses(this->container_)) {
2✔
258
        auto element = user->element();
2✔
259
        if (auto access = dynamic_cast<data_flow::AccessNode*>(element)) {
2✔
260
            for (auto& iedge : access->get_parent().in_edges(*access)) {
2✔
261
                auto memlet = &iedge;
1✔
262
                auto subset = memlet->subset();
1✔
263
                subset.clear();
1✔
264
                memlet->set_subset(subset);
1✔
265
                memlet->set_base_type(scalar_type);
1✔
266
            }
1✔
267
            for (auto& oedge : access->get_parent().out_edges(*access)) {
2✔
268
                auto memlet = &oedge;
1✔
269
                auto subset = memlet->subset();
1✔
270
                subset.clear();
1✔
271
                memlet->set_subset(subset);
1✔
272
                memlet->set_base_type(scalar_type);
1✔
273
            }
1✔
274
        }
2✔
275
    }
2✔
276

277
    this->loop_.replace(symbolic::symbol(this->container_), symbolic::symbol(replacement_name));
1✔
278
};
1✔
279

280
void OutLocalStorage::to_json(nlohmann::json& j) const {
×
281
    std::string loop_type;
×
282
    if (dynamic_cast<structured_control_flow::For*>(&loop_)) {
×
283
        loop_type = "for";
×
284
    } else if (dynamic_cast<structured_control_flow::Map*>(&loop_)) {
×
285
        loop_type = "map";
×
286
    } else {
×
287
        throw std::runtime_error("Unsupported loop type for serialization of loop: " + loop_.indvar()->get_name());
×
288
    }
×
289
    j["subgraph"] = {{"0", {{"element_id", this->loop_.element_id()}, {"type", loop_type}}}};
×
290
    j["transformation_type"] = this->name();
×
291
    j["container"] = container_;
×
292
};
×
293

294
OutLocalStorage OutLocalStorage::from_json(builder::StructuredSDFGBuilder& builder, const nlohmann::json& desc) {
×
295
    auto loop_id = desc["subgraph"]["0"]["element_id"].get<size_t>();
×
296
    std::string container = desc["container"].get<std::string>();
×
297
    auto element = builder.find_element_by_id(loop_id);
×
298
    if (!element) {
×
299
        throw InvalidTransformationDescriptionException("Element with ID " + std::to_string(loop_id) + " not found.");
×
300
    }
×
301
    auto loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element);
×
302

303
    return OutLocalStorage(*loop, container);
×
304
};
×
305

306
} // namespace transformations
307
} // 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