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

daisytuner / docc / 24823673078

23 Apr 2026 07:53AM UTC coverage: 64.167%. First build
24823673078

push

github

web-flow
Merge pull request #689 from daisytuner/rewrite-storage-transformation-interfaces

Make *LocalStorage* transformations interfaces compatible with Transer Tuning

69 of 97 new or added lines in 6 files covered. (71.13%)

30773 of 47958 relevant lines covered (64.17%)

574.36 hits per line

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

88.62
/opt/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, const data_flow::AccessNode& access_node)
25
    : loop_(loop), access_node_(access_node), container_(access_node.data()) {};
8✔
26

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

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

33
    // Criterion: Check if container exists and is used in the loop
34
    auto& users = analysis_manager.get<analysis::Users>();
6✔
35
    analysis::UsersView body_users(users, body);
6✔
36
    if (body_users.uses(this->container_).size() == 0) {
6✔
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_);
5✔
42
    auto first_access = accesses.at(0);
5✔
43
    auto first_subset = first_access->subsets().at(0);
5✔
44

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

64
    // Criterion: Check if accesses do not depend on containers written in the loop
65
    auto writes = body_users.writes();
5✔
66
    symbolic::SymbolSet written_containers;
5✔
67
    for (auto write : writes) {
7✔
68
        written_containers.insert(symbolic::symbol(write->container()));
7✔
69
    }
7✔
70
    for (auto subset : first_access->subsets()) {
5✔
71
        for (auto access : subset) {
5✔
72
            for (auto atom : symbolic::atoms(access)) {
5✔
73
                if (written_containers.contains(atom)) {
5✔
74
                    return false;
×
75
                }
×
76
            }
5✔
77
        }
5✔
78
    }
5✔
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()) {
5✔
83
        for (auto access : subset) {
5✔
84
            for (auto atom : symbolic::atoms(access)) {
5✔
85
                if (symbolic::eq(atom, this->loop_.indvar())) {
5✔
86
                    this->requires_array_ = true;
1✔
87
                    break;
1✔
88
                }
1✔
89
            }
5✔
90
            if (this->requires_array_) {
5✔
91
                break;
1✔
92
            }
1✔
93
        }
5✔
94
        if (this->requires_array_) {
5✔
95
            break;
1✔
96
        }
1✔
97
    }
5✔
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_) {
5✔
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;
5✔
109
};
5✔
110

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

118
    // End of transformation
119

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

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

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

143
    auto replacement_name = "__daisy_out_local_storage_" + this->container_;
1✔
144

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

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

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

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

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

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

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

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

229
    analysis::TypeAnalysis type_analysis(sdfg, &loop_, analysis_manager);
3✔
230
    auto type = type_analysis.get_outer_type(container_);
3✔
231

232
    auto replacement_name = "__daisy_out_local_storage_" + this->container_;
3✔
233

234
    types::Scalar scalar_type(type->primitive_type());
3✔
235
    builder.add_container(replacement_name, scalar_type);
3✔
236

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

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

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

275
    this->loop_.replace(symbolic::symbol(this->container_), symbolic::symbol(replacement_name));
3✔
276
};
3✔
277

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

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

302
    auto access_node = dynamic_cast<
1✔
303
        data_flow::AccessNode*>(builder.find_element_by_id(desc.at("subgraph").at("1").at("element_id").get<size_t>()));
1✔
304
    if (!access_node) {
1✔
NEW
305
        throw InvalidTransformationDescriptionException(
×
NEW
306
            "Access node with ID " + std::to_string(desc.at("subgraph").at("1").at("element_id").get<size_t>()) +
×
NEW
307
            " not found."
×
NEW
308
        );
×
NEW
309
    }
×
310

311
    return OutLocalStorage(*loop, *access_node);
1✔
312
};
1✔
313

314
} // namespace transformations
315
} // 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