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

daisytuner / docc / 25206942913

01 May 2026 07:44AM UTC coverage: 64.532%. First build
25206942913

push

github

web-flow
Merge pull request #694 from daisytuner/local-storage-type

adds StorageType to InLocalStorage and OutLocalStorage

438 of 484 new or added lines in 2 files covered. (90.5%)

31254 of 48432 relevant lines covered (64.53%)

684.19 hits per line

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

81.27
/opt/src/transformations/in_local_storage.cpp
1
#include "sdfg/transformations/in_local_storage.h"
2

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

6
#include "sdfg/analysis/memory_layout_analysis.h"
7
#include "sdfg/analysis/scope_analysis.h"
8
#include "sdfg/analysis/users.h"
9
#include "sdfg/builder/structured_sdfg_builder.h"
10
#include "sdfg/data_flow/access_node.h"
11
#include "sdfg/data_flow/library_nodes/barrier_local_node.h"
12
#include "sdfg/data_flow/memlet.h"
13
#include "sdfg/passes/structured_control_flow/dead_cfg_elimination.h"
14
#include "sdfg/passes/structured_control_flow/sequence_fusion.h"
15
#include "sdfg/structured_control_flow/sequence.h"
16
#include "sdfg/structured_control_flow/structured_loop.h"
17
#include "sdfg/symbolic/symbolic.h"
18
#include "sdfg/targets/gpu/gpu_schedule_type.h"
19
#include "sdfg/types/array.h"
20
#include "sdfg/types/pointer.h"
21
#include "sdfg/types/scalar.h"
22

23
namespace sdfg {
24
namespace transformations {
25

26
InLocalStorage::InLocalStorage(
27
    structured_control_flow::StructuredLoop& loop,
28
    const data_flow::AccessNode& access_node,
29
    const types::StorageType& storage_type
30
)
31
    : loop_(loop), access_node_(access_node), container_(access_node.data()), storage_type_(storage_type) {}
28✔
32

33
std::string InLocalStorage::name() const { return "InLocalStorage"; }
7✔
34

35
bool InLocalStorage::can_be_applied(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
28✔
36
    auto& sdfg = builder.subject();
28✔
37
    auto& body = this->loop_.root();
28✔
38

39
    tile_info_ = TileInfo{};
28✔
40

41
    // Criterion: Container must exist
42
    if (!sdfg.exists(this->container_)) {
28✔
43
        return false;
×
44
    }
×
45

46
    auto& type = sdfg.type(this->container_);
28✔
47

48
    // Criterion: Container must be Array or Pointer (not Scalar)
49
    if (type.type_id() != types::TypeID::Pointer && type.type_id() != types::TypeID::Array) {
28✔
50
        return false;
1✔
51
    }
1✔
52

53
    // Criterion: Container must be used in the loop body
54
    auto& users = analysis_manager.get<analysis::Users>();
27✔
55
    analysis::UsersView body_users(users, body);
27✔
56
    if (body_users.uses(this->container_).empty()) {
27✔
57
        return false;
2✔
58
    }
2✔
59

60
    // Criterion: Container must be read-only within the loop (no writes)
61
    if (!body_users.writes(this->container_).empty()) {
25✔
62
        return false;
1✔
63
    }
1✔
64

65
    // Use MemoryLayoutAnalysis tile API
66
    auto& mla = analysis_manager.get<analysis::MemoryLayoutAnalysis>();
24✔
67
    auto* tile = mla.tile(loop_, this->container_);
24✔
68
    if (!tile) {
24✔
69
        return false;
×
70
    }
×
71

72
    // Get overapproximated extents (integer upper bounds)
73
    auto extents = tile->extents_approx();
24✔
74
    if (extents.empty()) {
24✔
75
        return false;
×
76
    }
×
77

78
    // Store tile info (before substitution, bases/strides stay symbolic)
79
    tile_info_.dimensions = extents;
24✔
80
    tile_info_.bases = tile->min_subset;
24✔
81
    tile_info_.strides =
24✔
82
        std::vector<symbolic::Expression>(tile->layout.strides().begin(), tile->layout.strides().end());
24✔
83
    tile_info_.offset = tile->layout.offset();
24✔
84

85
    // GPU shared memory: resolve symbolic extents using GPU block sizes and
86
    // require at least one cooperative dimension
87
    if (storage_type_.is_nv_shared()) {
24✔
88
        auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
5✔
89
        auto ancestors = scope_analysis.ancestor_scopes(&loop_);
5✔
90

91
        // Build substitution map: symbolic GPU map bounds → integer block sizes
92
        // E.g., Map condition "i < N" with block_size=32 → N=32
93
        for (auto* node : ancestors) {
23✔
94
            if (auto* ancestor_map = dynamic_cast<structured_control_flow::Map*>(node)) {
23✔
95
                if (!gpu::is_gpu_schedule(ancestor_map->schedule_type())) {
9✔
NEW
96
                    continue;
×
NEW
97
                }
×
98
                auto block_size = gpu::gpu_block_size(ancestor_map->schedule_type());
9✔
99
                // Extract symbolic bound from condition: Lt(indvar, BOUND)
100
                auto condition = ancestor_map->condition();
9✔
101
                if (SymEngine::is_a<SymEngine::StrictLessThan>(*condition)) {
9✔
102
                    auto stl = SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(condition);
9✔
103
                    auto rhs = stl->get_args()[1];
9✔
104
                    auto iter_count = symbolic::sub(rhs, ancestor_map->init());
9✔
105
                    if (!SymEngine::is_a<SymEngine::Integer>(*iter_count)) {
9✔
106
                        // Symbolic bound — substitute with block size in extents and bases
107
                        for (auto& ext : tile_info_.dimensions) {
16✔
108
                            ext = symbolic::simplify(symbolic::subs(ext, iter_count, block_size));
16✔
109
                        }
16✔
110
                        for (auto& base : tile_info_.bases) {
16✔
111
                            base = symbolic::simplify(symbolic::subs(base, iter_count, block_size));
16✔
112
                        }
16✔
113
                    }
9✔
114
                }
9✔
115
            }
9✔
116
        }
23✔
117

118
        // Also resolve the loop's own bound if symbolic and matches a block size
119
        // E.g., For k = 0..K where K is a parameter — check if K can be resolved
120
        // from any GPU ancestor map
121
        // (Already handled above: if K appears as a GPU map bound, it's substituted)
122

123
        // Criterion: All extents must now be provably integer
124
        for (auto& ext : tile_info_.dimensions) {
9✔
125
            if (!SymEngine::is_a<SymEngine::Integer>(*ext)) {
9✔
126
                return false;
2✔
127
            }
2✔
128
        }
9✔
129

130
        // Criterion: At least one cooperative dimension
131
        bool has_cooperative_dim = false;
3✔
132
        for (auto* node : ancestors) {
6✔
133
            if (auto* ancestor_map = dynamic_cast<structured_control_flow::Map*>(node)) {
6✔
134
                if (!gpu::is_gpu_schedule(ancestor_map->schedule_type())) {
3✔
NEW
135
                    continue;
×
NEW
136
                }
×
137
                // A GPU dim is cooperative if its indvar does NOT appear in any tile base
138
                bool appears_in_bases = false;
3✔
139
                for (auto& base : tile_info_.bases) {
5✔
140
                    if (symbolic::uses(base, ancestor_map->indvar())) {
5✔
NEW
141
                        appears_in_bases = true;
×
NEW
142
                        break;
×
NEW
143
                    }
×
144
                }
5✔
145
                if (!appears_in_bases) {
3✔
146
                    has_cooperative_dim = true;
3✔
147
                    break;
3✔
148
                }
3✔
149
            }
3✔
150
        }
6✔
151
        if (!has_cooperative_dim) {
3✔
NEW
152
            return false;
×
NEW
153
        }
×
154
    } else {
19✔
155
        // CPU path: All extents must be provably integer
156
        for (auto& ext : tile_info_.dimensions) {
31✔
157
            if (!SymEngine::is_a<SymEngine::Integer>(*ext)) {
31✔
NEW
158
                return false;
×
NEW
159
            }
×
160
        }
31✔
161
    }
19✔
162

163
    return true;
22✔
164
}
24✔
165

166
void InLocalStorage::apply(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
14✔
167
    auto& sdfg = builder.subject();
14✔
168
    auto& users = analysis_manager.get<analysis::Users>();
14✔
169
    auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
14✔
170

171
    auto parent_node = scope_analysis.parent_scope(&loop_);
14✔
172
    auto parent = dynamic_cast<structured_control_flow::Sequence*>(parent_node);
14✔
173
    if (!parent) {
14✔
174
        throw InvalidSDFGException("InLocalStorage: Parent of loop must be a Sequence!");
×
175
    }
×
176

177
    // Get type information
178
    auto& type = sdfg.type(this->container_);
14✔
179
    types::Scalar scalar_type(type.primitive_type());
14✔
180

181
    // Create local buffer name
182
    local_name_ = "__daisy_in_local_storage_" + this->container_;
14✔
183

184
    // Collect varying dimensions (extent > 1) and compute buffer layout
185
    std::vector<size_t> varying_dims;
14✔
186
    std::vector<symbolic::Expression> dim_sizes;
14✔
187
    for (size_t d = 0; d < tile_info_.dimensions.size(); d++) {
38✔
188
        auto& dim_size = tile_info_.dimensions.at(d);
24✔
189
        if (!symbolic::eq(dim_size, symbolic::integer(1))) {
24✔
190
            varying_dims.push_back(d);
21✔
191
            dim_sizes.push_back(dim_size);
21✔
192
        }
21✔
193
    }
24✔
194

195
    // Compute total buffer size
196
    symbolic::Expression total_size = symbolic::integer(1);
14✔
197
    for (auto& ds : dim_sizes) {
21✔
198
        total_size = symbolic::mul(total_size, ds);
21✔
199
    }
21✔
200

201
    // Helper: build linearized local index from per-dimension symbolic expressions
202
    auto linearize_exprs = [&](const std::vector<symbolic::Expression>& indices) -> symbolic::Expression {
14✔
203
        symbolic::Expression linear_idx = symbolic::integer(0);
11✔
204
        symbolic::Expression stride = symbolic::integer(1);
11✔
205
        for (int i = indices.size() - 1; i >= 0; i--) {
29✔
206
            linear_idx = symbolic::add(linear_idx, symbolic::mul(indices[i], stride));
18✔
207
            stride = symbolic::mul(stride, dim_sizes[i]);
18✔
208
        }
18✔
209
        return linear_idx;
11✔
210
    };
11✔
211

212
    // Helper: build linearized local index from per-dimension indvars (symbols)
213
    auto linearize = [&](const std::vector<symbolic::Symbol>& indvars) -> symbolic::Expression {
14✔
214
        std::vector<symbolic::Expression> exprs(indvars.begin(), indvars.end());
11✔
215
        return linearize_exprs(exprs);
11✔
216
    };
11✔
217

218
    // Helper: build source subset (base[d] + copy_indvar[d]) for original container
219
    bool is_pointer = (type.type_id() == types::TypeID::Pointer);
14✔
220
    auto build_original_subset = [&](const std::vector<symbolic::Expression>& copy_indices) -> data_flow::Subset {
14✔
221
        std::vector<symbolic::Expression> full_indices;
14✔
222
        size_t var_idx = 0;
14✔
223
        for (size_t d = 0; d < tile_info_.dimensions.size(); d++) {
38✔
224
            if (!symbolic::eq(tile_info_.dimensions.at(d), symbolic::integer(1))) {
24✔
225
                full_indices.push_back(symbolic::add(tile_info_.bases.at(d), copy_indices.at(var_idx++)));
21✔
226
            } else {
21✔
227
                full_indices.push_back(tile_info_.bases.at(d));
3✔
228
            }
3✔
229
        }
24✔
230

231
        if (is_pointer) {
14✔
232
            symbolic::Expression linear = tile_info_.offset;
14✔
233
            for (size_t d = 0; d < full_indices.size(); d++) {
38✔
234
                linear = symbolic::add(linear, symbolic::mul(tile_info_.strides.at(d), full_indices.at(d)));
24✔
235
            }
24✔
236
            return {linear};
14✔
237
        } else {
14✔
238
            return data_flow::Subset(full_indices.begin(), full_indices.end());
×
239
        }
×
240
    };
14✔
241

242
    // ==================================================================
243
    // Branch: GPU cooperative path vs CPU sequential path
244
    // ==================================================================
245
    if (storage_type_.is_nv_shared()) {
14✔
246
        // ============================================================
247
        // GPU COOPERATIVE PATH
248
        // ============================================================
249
        auto ancestors = scope_analysis.ancestor_scopes(&loop_);
3✔
250

251
        // Collect cooperative GPU dimensions (indvar not in tile bases)
252
        struct CoopDim {
3✔
253
            symbolic::Symbol indvar;
3✔
254
            symbolic::Integer block_size;
3✔
255
            gpu::GPUDimension dimension;
3✔
256
        };
3✔
257
        std::vector<CoopDim> coop_dims;
3✔
258

259
        for (auto* node : ancestors) {
15✔
260
            if (auto* ancestor_map = dynamic_cast<structured_control_flow::Map*>(node)) {
15✔
261
                if (!gpu::is_gpu_schedule(ancestor_map->schedule_type())) {
6✔
NEW
262
                    continue;
×
NEW
263
                }
×
264
                bool appears_in_bases = false;
6✔
265
                for (auto& base : tile_info_.bases) {
8✔
266
                    if (symbolic::uses(base, ancestor_map->indvar())) {
8✔
267
                        appears_in_bases = true;
2✔
268
                        break;
2✔
269
                    }
2✔
270
                }
8✔
271
                if (!appears_in_bases) {
6✔
272
                    coop_dims.push_back(
4✔
273
                        {ancestor_map->indvar(),
4✔
274
                         gpu::gpu_block_size(ancestor_map->schedule_type()),
4✔
275
                         gpu::gpu_dimension(ancestor_map->schedule_type())}
4✔
276
                    );
4✔
277
                }
4✔
278
            }
6✔
279
        }
15✔
280

281
        // Compute total cooperative thread count
282
        symbolic::Expression total_coop_threads = symbolic::integer(1);
3✔
283
        for (auto& cd : coop_dims) {
4✔
284
            total_coop_threads = symbolic::mul(total_coop_threads, cd.block_size);
4✔
285
        }
4✔
286

287
        // Create the local buffer with NV_Shared storage
288
        types::Array buffer_type(storage_type_, 0, {}, scalar_type, total_size);
3✔
289
        builder.add_container(local_name_, buffer_type);
3✔
290

291
        // Emit: barrier → guarded cooperative copy → barrier → loop
292
        // 1. Barrier before copy
293
        auto& barrier_block1 = builder.add_block_before(*parent, loop_, {}, loop_.debug_info());
3✔
294
        builder.add_library_node<data_flow::BarrierLocalNode>(barrier_block1, {});
3✔
295

296
        // 2. Cooperative copy with if_else guard
297
        // Flatten cooperative thread index: coop_flat = sum(indvar[i] * product(block_size[j] for j>i))
298
        symbolic::Expression coop_flat = symbolic::integer(0);
3✔
299
        symbolic::Expression coop_stride = symbolic::integer(1);
3✔
300
        for (int i = coop_dims.size() - 1; i >= 0; i--) {
7✔
301
            coop_flat = symbolic::add(coop_flat, symbolic::mul(coop_dims[i].indvar, coop_stride));
4✔
302
            coop_stride = symbolic::mul(coop_stride, coop_dims[i].block_size);
4✔
303
        }
4✔
304

305
        // Each thread loads elements strided by total_coop_threads
306
        // Thread t loads elements: t, t + total_threads, t + 2*total_threads, ...
307
        // We emit a loop: for (idx = coop_flat; idx < total_size; idx += total_coop_threads)
308
        auto idx_name = "__daisy_ils_coop_" + this->container_;
3✔
309
        types::Scalar idx_type(types::PrimitiveType::UInt64);
3✔
310
        builder.add_container(idx_name, idx_type);
3✔
311
        auto idx_var = symbolic::symbol(idx_name);
3✔
312

313
        auto copy_init = coop_flat;
3✔
314
        auto copy_condition = symbolic::Lt(idx_var, total_size);
3✔
315
        auto copy_update = symbolic::add(idx_var, total_coop_threads);
3✔
316

317
        auto& copy_loop = builder.add_map_before(
3✔
318
            *parent,
3✔
319
            loop_,
3✔
320
            idx_var,
3✔
321
            copy_condition,
3✔
322
            copy_init,
3✔
323
            copy_update,
3✔
324
            structured_control_flow::ScheduleType_Sequential::create(),
3✔
325
            {},
3✔
326
            loop_.debug_info()
3✔
327
        );
3✔
328

329
        // Decompose flat idx back into per-dimension indices for source subset
330
        // idx maps to varying_dims in row-major order
331
        auto& copy_scope = copy_loop.root();
3✔
332
        auto& copy_block = builder.add_block(copy_scope);
3✔
333
        auto& copy_src = builder.add_access(copy_block, this->container_);
3✔
334
        auto& copy_dst = builder.add_access(copy_block, local_name_);
3✔
335
        auto& copy_tasklet = builder.add_tasklet(copy_block, data_flow::TaskletCode::assign, "_out", {"_in"});
3✔
336

337
        // Decompose idx_var into per-dim indices
338
        std::vector<symbolic::Expression> copy_indices;
3✔
339
        symbolic::Expression remainder = idx_var;
3✔
340
        for (size_t i = 0; i < dim_sizes.size(); i++) {
6✔
341
            if (i < dim_sizes.size() - 1) {
3✔
342
                // integer division: idx / (product of remaining dims)
NEW
343
                symbolic::Expression divisor = symbolic::integer(1);
×
NEW
344
                for (size_t j = i + 1; j < dim_sizes.size(); j++) {
×
NEW
345
                    divisor = symbolic::mul(divisor, dim_sizes[j]);
×
NEW
346
                }
×
NEW
347
                auto quotient = symbolic::div(remainder, divisor);
×
NEW
348
                copy_indices.push_back(quotient);
×
NEW
349
                remainder = symbolic::mod(remainder, divisor);
×
350
            } else {
3✔
351
                copy_indices.push_back(remainder);
3✔
352
            }
3✔
353
        }
3✔
354

355
        auto copy_src_subset = build_original_subset(copy_indices);
3✔
356
        data_flow::Subset copy_dst_subset = {idx_var};
3✔
357

358
        builder.add_computational_memlet(copy_block, copy_src, copy_tasklet, "_in", copy_src_subset, type);
3✔
359
        builder.add_computational_memlet(copy_block, copy_tasklet, "_out", copy_dst, copy_dst_subset, buffer_type);
3✔
360

361
        // 3. Barrier after copy
362
        auto& barrier_block2 = builder.add_block_before(*parent, loop_, {}, loop_.debug_info());
3✔
363
        builder.add_library_node<data_flow::BarrierLocalNode>(barrier_block2, {});
3✔
364
    } else {
11✔
365
        // ============================================================
366
        // CPU SEQUENTIAL PATH
367
        // ============================================================
368
        // Create the local buffer with specified storage type
369
        types::Array buffer_type(storage_type_, 0, {}, scalar_type, total_size);
11✔
370
        builder.add_container(local_name_, buffer_type);
11✔
371

372
        std::vector<symbolic::Symbol> copy_indvars;
11✔
373
        structured_control_flow::Sequence* copy_scope = parent;
11✔
374
        bool first_copy_loop = true;
11✔
375

376
        for (size_t i = 0; i < varying_dims.size(); i++) {
29✔
377
            size_t d = varying_dims[i];
18✔
378
            auto indvar_name = "__daisy_ils_" + this->container_ + "_d" + std::to_string(d);
18✔
379
            types::Scalar indvar_type(types::PrimitiveType::UInt64);
18✔
380
            builder.add_container(indvar_name, indvar_type);
18✔
381
            auto indvar = symbolic::symbol(indvar_name);
18✔
382
            copy_indvars.push_back(indvar);
18✔
383

384
            auto init = symbolic::integer(0);
18✔
385
            auto condition = symbolic::Lt(indvar, dim_sizes[i]);
18✔
386
            auto update = symbolic::add(indvar, symbolic::integer(1));
18✔
387

388
            if (first_copy_loop) {
18✔
389
                auto& copy_loop = builder.add_map_before(
11✔
390
                    *copy_scope,
11✔
391
                    loop_,
11✔
392
                    indvar,
11✔
393
                    condition,
11✔
394
                    init,
11✔
395
                    update,
11✔
396
                    structured_control_flow::ScheduleType_Sequential::create(),
11✔
397
                    {},
11✔
398
                    loop_.debug_info()
11✔
399
                );
11✔
400
                copy_scope = &copy_loop.root();
11✔
401
                first_copy_loop = false;
11✔
402
            } else {
11✔
403
                auto& copy_loop = builder.add_map(
7✔
404
                    *copy_scope,
7✔
405
                    indvar,
7✔
406
                    condition,
7✔
407
                    init,
7✔
408
                    update,
7✔
409
                    structured_control_flow::ScheduleType_Sequential::create(),
7✔
410
                    {},
7✔
411
                    loop_.debug_info()
7✔
412
                );
7✔
413
                copy_scope = &copy_loop.root();
7✔
414
            }
7✔
415
        }
18✔
416

417
        // Create copy block
418
        auto& copy_block = builder.add_block(*copy_scope);
11✔
419
        auto& copy_src = builder.add_access(copy_block, this->container_);
11✔
420
        auto& copy_dst = builder.add_access(copy_block, local_name_);
11✔
421
        auto& copy_tasklet = builder.add_tasklet(copy_block, data_flow::TaskletCode::assign, "_out", {"_in"});
11✔
422

423
        std::vector<symbolic::Expression> copy_exprs(copy_indvars.begin(), copy_indvars.end());
11✔
424
        auto copy_src_subset = build_original_subset(copy_exprs);
11✔
425
        data_flow::Subset copy_dst_subset = {linearize(copy_indvars)};
11✔
426

427
        builder.add_computational_memlet(copy_block, copy_src, copy_tasklet, "_in", copy_src_subset, type);
11✔
428
        types::Array buffer_type_ref(storage_type_, 0, {}, scalar_type, total_size);
11✔
429
        builder.add_computational_memlet(copy_block, copy_tasklet, "_out", copy_dst, copy_dst_subset, buffer_type_ref);
11✔
430
    }
11✔
431

432
    // ==================================================================
433
    // Update accesses in the main loop to use the local buffer
434
    // ==================================================================
435
    types::Array buffer_type(storage_type_, 0, {}, scalar_type, total_size);
14✔
436
    analysis::UsersView body_users(users, loop_.root());
14✔
437
    auto& mla = analysis_manager.get<analysis::MemoryLayoutAnalysis>();
14✔
438

439
    for (auto* user : body_users.uses(this->container_)) {
18✔
440
        auto element = user->element();
18✔
441
        if (auto memlet = dynamic_cast<data_flow::Memlet*>(element)) {
18✔
442
            auto* access = mla.access(*memlet);
×
443
            if (access && access->subset.size() == tile_info_.dimensions.size()) {
×
444
                std::vector<symbolic::Expression> local_indices;
×
445
                for (size_t d = 0; d < tile_info_.dimensions.size(); d++) {
×
446
                    if (!symbolic::eq(tile_info_.dimensions.at(d), symbolic::integer(1))) {
×
447
                        local_indices.push_back(symbolic::sub(access->subset.at(d), tile_info_.bases.at(d)));
×
448
                    }
×
449
                }
×
450

NEW
451
                symbolic::Expression linear_idx = linearize_exprs(local_indices);
×
452
                memlet->set_subset({linear_idx});
×
453
                memlet->set_base_type(buffer_type);
×
454
            } else {
×
455
                auto& old_subset = memlet->subset();
×
456
                if (old_subset.size() == tile_info_.dimensions.size()) {
×
457
                    std::vector<symbolic::Expression> local_indices;
×
458
                    for (size_t d = 0; d < tile_info_.dimensions.size(); d++) {
×
459
                        if (!symbolic::eq(tile_info_.dimensions.at(d), symbolic::integer(1))) {
×
460
                            local_indices.push_back(symbolic::sub(old_subset.at(d), tile_info_.bases.at(d)));
×
461
                        }
×
462
                    }
×
463

NEW
464
                    symbolic::Expression linear_idx = linearize_exprs(local_indices);
×
465
                    memlet->set_subset({linear_idx});
×
466
                    memlet->set_base_type(buffer_type);
×
467
                }
×
468
            }
×
469
        }
×
470
    }
18✔
471

472
    // Replace container name in the loop body
473
    loop_.replace(symbolic::symbol(this->container_), symbolic::symbol(local_name_));
14✔
474

475
    // Cleanup
476
    analysis_manager.invalidate_all();
14✔
477

478
    passes::SequenceFusion sf_pass;
14✔
479
    passes::DeadCFGElimination dce_pass;
14✔
480
    bool applies = false;
14✔
481
    do {
14✔
482
        applies = false;
14✔
483
        applies |= dce_pass.run(builder, analysis_manager);
14✔
484
        applies |= sf_pass.run(builder, analysis_manager);
14✔
485
    } while (applies);
14✔
486
}
14✔
487

488
void InLocalStorage::to_json(nlohmann::json& j) const {
6✔
489
    std::string loop_type;
6✔
490
    if (dynamic_cast<structured_control_flow::For*>(&loop_)) {
6✔
491
        loop_type = "for";
6✔
492
    } else if (dynamic_cast<structured_control_flow::Map*>(&loop_)) {
6✔
493
        loop_type = "map";
×
494
    } else {
×
495
        throw std::runtime_error("Unsupported loop type for serialization of loop: " + loop_.indvar()->get_name());
×
496
    }
×
497
    j["subgraph"] = {
6✔
498
        {"0", {{"element_id", this->loop_.element_id()}, {"type", loop_type}}},
6✔
499
        {"1", {{"element_id", this->access_node_.element_id()}, {"type", "access_node"}}}
6✔
500
    };
6✔
501
    j["transformation_type"] = this->name();
6✔
502
    j["container"] = container_;
6✔
503
}
6✔
504

505
InLocalStorage InLocalStorage::from_json(builder::StructuredSDFGBuilder& builder, const nlohmann::json& desc) {
1✔
506
    auto loop_id = desc["subgraph"]["0"]["element_id"].get<size_t>();
1✔
507
    auto element = builder.find_element_by_id(loop_id);
1✔
508
    if (!element) {
1✔
509
        throw InvalidTransformationDescriptionException("Element with ID " + std::to_string(loop_id) + " not found.");
×
510
    }
×
511
    auto loop = dynamic_cast<structured_control_flow::StructuredLoop*>(element);
1✔
512
    if (!loop) {
1✔
513
        throw InvalidTransformationDescriptionException(
×
514
            "Element with ID " + std::to_string(loop_id) + " is not a structured loop."
×
515
        );
×
516
    }
×
517

518
    auto access_node = dynamic_cast<
1✔
519
        data_flow::AccessNode*>(builder.find_element_by_id(desc.at("subgraph").at("1").at("element_id").get<size_t>()));
1✔
520
    if (!access_node) {
1✔
521
        throw InvalidTransformationDescriptionException(
×
522
            "Access node with ID " + std::to_string(desc.at("subgraph").at("1").at("element_id").get<size_t>()) +
×
523
            " not found."
×
524
        );
×
525
    }
×
526

527
    return InLocalStorage(*loop, *access_node);
1✔
528
}
1✔
529

530
} // namespace transformations
531
} // 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