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

daisytuner / docc / 23249128422

18 Mar 2026 02:12PM UTC coverage: 63.938% (+0.3%) from 63.617%
23249128422

Pull #584

github

web-flow
Merge 0fcde60dc into 64d54d7de
Pull Request #584: adds diamond tiling test

18 of 20 new or added lines in 1 file covered. (90.0%)

1180 existing lines in 28 files now uncovered.

26122 of 40855 relevant lines covered (63.94%)

407.69 hits per line

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

55.26
/sdfg/src/data_flow/memlet.cpp
1
#include <sdfg/data_flow/memlet.h>
2

3
#include "sdfg/data_flow/library_node.h"
4
#include "sdfg/data_flow/tasklet.h"
5
#include "sdfg/function.h"
6
#include "sdfg/symbolic/symbolic.h"
7
#include "sdfg/types/type.h"
8
#include "sdfg/types/utils.h"
9

10
namespace sdfg {
11
namespace data_flow {
12

13
Memlet::Memlet(
14
    size_t element_id,
15
    const DebugInfo& debug_info,
16
    const graph::Edge& edge,
17
    DataFlowGraph& parent,
18
    DataFlowNode& src,
19
    const std::string& src_conn,
20
    DataFlowNode& dst,
21
    const std::string& dst_conn,
22
    const Subset& subset,
23
    const types::IType& base_type
24
)
25
    : Element(element_id, debug_info), edge_(edge), parent_(&parent), src_(src), dst_(dst), src_conn_(src_conn),
5,763✔
26
      dst_conn_(dst_conn), subset_(subset), base_type_(base_type.clone()) {
5,763✔
27

28
      };
5,763✔
29

30
void Memlet::validate(const Function& function) const {
13,792✔
31
    // Validate subset
32
    for (const auto& dim : this->subset_) {
13,792✔
33
        // Null ptr check
34
        if (dim.is_null()) {
13,775✔
35
            throw InvalidSDFGException("Memlet: Subset dimensions cannot be null");
×
36
        }
×
37
    }
13,775✔
38

39
    // Validate connections
40
    switch (this->type()) {
13,792✔
41
        case MemletType::Computational: {
13,614✔
42
            // Criterion: Must connect a code node and an access node with void connector at access node
43
            const AccessNode* data_node = nullptr;
13,614✔
44
            const CodeNode* code_node = nullptr;
13,614✔
45
            if (this->src_conn_ == "void") {
13,614✔
46
                data_node = dynamic_cast<const AccessNode*>(&this->src_);
8,448✔
47
                code_node = dynamic_cast<const CodeNode*>(&this->dst_);
8,448✔
48
                if (!data_node || !code_node) {
8,448✔
49
                    throw InvalidSDFGException("Memlet: Computation memlets must connect a code node and an access node"
×
50
                    );
×
51
                }
×
52

53
                // Criterion: Non-void connector must be an input of the code node
54
                if (std::find(code_node->inputs().begin(), code_node->inputs().end(), this->dst_conn_) ==
8,448✔
55
                    code_node->inputs().end()) {
8,448✔
56
                    throw InvalidSDFGException("Memlet: Computation memlets must have an input in the code node");
×
57
                }
×
58
            } else if (this->dst_conn_ == "void") {
8,448✔
59
                data_node = dynamic_cast<const AccessNode*>(&this->dst_);
5,166✔
60
                code_node = dynamic_cast<const CodeNode*>(&this->src_);
5,166✔
61
                if (!data_node || !code_node) {
5,166✔
62
                    throw InvalidSDFGException("Memlet: Computation memlets must connect a code node and an access node"
×
63
                    );
×
64
                }
×
65

66
                // Criterion: Non-void connector must be an output of the code node
67
                if (std::find(code_node->outputs().begin(), code_node->outputs().end(), this->src_conn_) ==
5,166✔
68
                    code_node->outputs().end()) {
5,166✔
69
                    throw InvalidSDFGException("Memlet: Computation memlets must have an output in the code node");
×
70
                }
×
71
            } else {
5,166✔
72
                throw InvalidSDFGException(
×
73
                    "Memlet: Computation memlets must have void connector at source or destination"
×
74
                );
×
75
            }
×
76

77
            // If tensor, check that the type is consistenly defined
78
            if (this->base_type_->type_id() == types::TypeID::Tensor) {
13,614✔
79
                auto& tensor_type = dynamic_cast<const types::Tensor&>(*this->base_type_);
5,537✔
80
                if (tensor_type.is_scalar()) {
5,537✔
81
                    if (auto const_node = dynamic_cast<const data_flow::ConstantNode*>(data_node)) {
78✔
82
                        if (const_node->type().type_id() != types::TypeID::Scalar) {
30✔
83
                            throw InvalidSDFGException(
×
84
                                "Memlet: Scalar tensors must reference scalar buffers. Base type: " +
×
85
                                this->base_type_->print() + " Buffer type: " + const_node->type().print()
×
86
                            );
×
87
                        }
×
88
                    } else {
48✔
89
                        auto& buffer_type = function.type(data_node->data());
48✔
90
                        if (buffer_type.type_id() != types::TypeID::Scalar) {
48✔
91
                            throw InvalidSDFGException(
×
92
                                "Memlet: Scalar tensors must reference scalar buffers. Base type: " +
×
93
                                this->base_type_->print() + " Buffer type: " + buffer_type.print()
×
94
                            );
×
95
                        }
×
96
                    }
48✔
97
                } else {
5,459✔
98
                    auto& buffer_type = function.type(data_node->data());
5,459✔
99
                    if (buffer_type.type_id() != types::TypeID::Pointer) {
5,459✔
100
                        throw InvalidSDFGException(
×
101
                            "Memlet: Non-scalar tensors must reference pointer buffers. Base type: " +
×
102
                            this->base_type_->print() + " Buffer type: " + buffer_type.print()
×
103
                        );
×
104
                    }
×
105
                    if (this->subset_.size() > tensor_type.shape().size()) {
5,459✔
106
                        throw InvalidSDFGException(
×
107
                            "Memlet: Subset dimensions must match base type dimensions. Base type: " +
×
108
                            this->base_type_->print() + " Subset Dim: " + std::to_string(this->subset_.size())
×
109
                        );
×
110
                    }
×
111
                    if (tensor_type.shape().size() != tensor_type.strides().size()) {
5,459✔
112
                        throw InvalidSDFGException(
×
113
                            "Memlet: Tensor types must have the same number of shape and stride dimensions. Base "
×
114
                            "type: " +
×
115
                            this->base_type_->print()
×
116
                        );
×
117
                    }
×
118
                }
5,459✔
119
            }
5,537✔
120
            break;
13,614✔
121
        }
13,614✔
122
        case MemletType::Reference: {
13,614✔
123
            // Criterion: Destination must be an access node with a pointer type
124
            auto dst_node = dynamic_cast<const AccessNode*>(&this->dst_);
132✔
125
            if (!dst_node) {
132✔
126
                throw InvalidSDFGException("Memlet: Reference memlets must have an access node destination");
×
127
            }
×
128
            auto dst_data = dst_node->data();
132✔
129
            // Criterion: Destination must be non-constant
130
            if (helpers::is_number(dst_data) || symbolic::is_nullptr(symbolic::symbol(dst_data))) {
132✔
131
                throw InvalidSDFGException("Memlet: Reference memlets must have a non-constant destination");
×
132
            }
×
133

134
            // Criterion: Destination must be a pointer
135
            auto& dst_type = function.type(dst_data);
132✔
136
            if (dst_type.type_id() != types::TypeID::Pointer) {
132✔
137
                throw InvalidSDFGException("Memlet: Reference memlets must have a pointer destination");
×
138
            }
×
139

140
            // Criterion: Source must be an access node
141
            if (this->src_conn_ != "void") {
132✔
142
                throw InvalidSDFGException("Memlet: Reference memlets must have a void source");
×
143
            }
×
144
            auto src_node = dynamic_cast<const AccessNode*>(&this->src_);
132✔
145
            if (!src_node) {
132✔
146
                throw InvalidSDFGException("Memlet: Reference memlets must have an access node source");
×
147
            }
×
148

149
            // Case: Constant
150
            if (helpers::is_number(src_node->data()) || symbolic::is_nullptr(symbolic::symbol(src_node->data()))) {
132✔
151
                if (!this->subset_.empty()) {
4✔
152
                    throw InvalidSDFGException("Memlet: Reference memlets for raw addresses must not have a subset");
×
153
                }
×
154
                return;
4✔
155
            }
4✔
156

157
            // Case: Container
158
            // Criterion: Must be contiguous memory reference
159
            // Throws exception if not contiguous
160
            types::infer_type(function, *this->base_type_, this->subset_);
128✔
161
            break;
128✔
162
        }
132✔
163
        case MemletType::Dereference_Src: {
28✔
164
            if (this->src_conn_ != "void") {
28✔
165
                throw InvalidSDFGException("Memlet: Dereference memlets must have a void destination");
×
166
            }
×
167

168
            auto src_node = dynamic_cast<const AccessNode*>(&this->src_);
28✔
169
            if (!src_node) {
28✔
170
                throw InvalidSDFGException("Memlet: Dereference memlets must have an access node source");
×
171
            }
×
172
            auto dst_node = dynamic_cast<const AccessNode*>(&this->dst_);
28✔
173
            if (!dst_node) {
28✔
174
                throw InvalidSDFGException("Memlet: Dereference memlets must have an access node destination");
×
175
            }
×
176

177
            // Criterion: Dereference memlets must have '0' as the only dimension
178
            if (this->subset_.size() != 1) {
28✔
179
                throw InvalidSDFGException("Memlet: Dereference memlets must have '0' as the only dimension");
×
180
            }
×
181
            if (!symbolic::eq(this->subset_[0], symbolic::zero())) {
28✔
182
                throw InvalidSDFGException("Memlet: Dereference memlets must have '0' as the only dimension");
×
183
            }
×
184

185
            // Criterion: Source must be a pointer
186
            if (auto const_node = dynamic_cast<const ConstantNode*>(src_node)) {
28✔
187
                if (const_node->type().type_id() != types::TypeID::Pointer &&
×
188
                    const_node->type().type_id() != types::TypeID::Scalar) {
×
189
                    throw InvalidSDFGException("Memlet: Dereference memlets must have a pointer source");
×
190
                }
×
191
            } else {
28✔
192
                auto src_data = src_node->data();
28✔
193
                auto& src_type = function.type(src_data);
28✔
194
                if (src_type.type_id() != types::TypeID::Pointer) {
28✔
195
                    throw InvalidSDFGException("Memlet: Dereference memlets must have a pointer source");
×
196
                }
×
197
            }
28✔
198

199
            // Criterion: Must be typed pointer
200
            auto base_pointer_type = dynamic_cast<const types::Pointer*>(this->base_type_.get());
28✔
201
            if (!base_pointer_type) {
28✔
202
                throw InvalidSDFGException("Memlet: Dereference memlets must have a typed pointer base type");
×
203
            }
×
204
            if (!base_pointer_type->has_pointee_type()) {
28✔
205
                throw InvalidSDFGException("Memlet: Dereference memlets must have a pointee type");
×
206
            }
×
207

208
            break;
28✔
209
        }
28✔
210
        case MemletType::Dereference_Dst: {
28✔
211
            if (this->dst_conn_ != "void") {
18✔
212
                throw InvalidSDFGException("Memlet: Dereference memlets must have a void source");
×
213
            }
×
214

215
            auto src_node = dynamic_cast<const AccessNode*>(&this->src_);
18✔
216
            if (!src_node) {
18✔
217
                throw InvalidSDFGException("Memlet: Dereference memlets must have an access node source");
×
218
            }
×
219
            auto dst_node = dynamic_cast<const AccessNode*>(&this->dst_);
18✔
220
            if (!dst_node) {
18✔
221
                throw InvalidSDFGException("Memlet: Dereference memlets must have an access node destination");
×
222
            }
×
223

224
            // Criterion: Dereference memlets must have '0' as the only dimension
225
            if (this->subset_.size() != 1) {
18✔
226
                throw InvalidSDFGException("Memlet: Dereference memlets must have '0' as the only dimension");
×
227
            }
×
228
            if (!symbolic::eq(this->subset_[0], symbolic::zero())) {
18✔
229
                throw InvalidSDFGException("Memlet: Dereference memlets must have '0' as the only dimension");
×
230
            }
×
231

232
            // Criterion: src type cannot be a function
233
            const sdfg::types::IType* src_type;
18✔
234
            if (auto const_node = dynamic_cast<const data_flow::ConstantNode*>(src_node)) {
18✔
235
                src_type = &const_node->type();
2✔
236
            } else {
16✔
237
                src_type = &function.type(src_node->data());
16✔
238
            }
16✔
239
            if (src_type->type_id() == types::TypeID::Function) {
18✔
240
                throw InvalidSDFGException("Memlet: Dereference memlets cannot have source of type Function");
×
241
            }
×
242

243
            // Criterion: Destination must be a pointer
244
            if (auto const_node = dynamic_cast<const ConstantNode*>(dst_node)) {
18✔
245
                throw InvalidSDFGException("Memlet: Dereference memlets must have a non-constant destination");
×
246
            }
×
247
            auto dst_data = dst_node->data();
18✔
248
            auto& dst_type = function.type(dst_data);
18✔
249
            if (dst_type.type_id() != types::TypeID::Pointer) {
18✔
250
                throw InvalidSDFGException("Memlet: Dereference memlets must have a pointer destination");
×
251
            }
×
252

253
            // Criterion: Must be typed pointer
254
            auto base_pointer_type = dynamic_cast<const types::Pointer*>(this->base_type_.get());
18✔
255
            if (!base_pointer_type) {
18✔
256
                throw InvalidSDFGException("Memlet: Dereference memlets must have a typed pointer base type");
×
257
            }
×
258
            if (!base_pointer_type->has_pointee_type()) {
18✔
259
                throw InvalidSDFGException("Memlet: Dereference memlets must have a pointee type");
×
260
            }
×
261

262
            break;
18✔
263
        }
18✔
264
        default:
18✔
265
            throw InvalidSDFGException("Memlet: Invalid memlet type");
×
266
    }
13,792✔
267
};
13,792✔
268

269
const graph::Edge Memlet::edge() const { return this->edge_; };
1,586✔
270

271
const DataFlowGraph& Memlet::get_parent() const { return *this->parent_; };
×
272

273
DataFlowGraph& Memlet::get_parent() { return *this->parent_; };
43✔
274

275
MemletType Memlet::type() const {
25,025✔
276
    if (this->dst_conn_ == "ref") {
25,025✔
277
        return Reference;
252✔
278
    } else if (this->dst_conn_ == "deref") {
24,773✔
279
        return Dereference_Src;
98✔
280
    } else if (this->src_conn_ == "deref") {
24,675✔
281
        return Dereference_Dst;
62✔
282
    } else {
24,613✔
283
        return Computational;
24,613✔
284
    }
24,613✔
285
}
25,025✔
286

UNCOV
287
bool Memlet::is_src_read() const {
×
UNCOV
288
    if (src_conn_ == "void" || src_conn_ == "deref") { // anything else is not an access node on the input
×
UNCOV
289
        auto t = type();
×
UNCOV
290
        if (t == Computational) {
×
UNCOV
291
            return true;
×
UNCOV
292
        }
×
UNCOV
293
        if (t == Dereference_Dst || t == Dereference_Src) {
×
UNCOV
294
            return true;
×
UNCOV
295
        }
×
UNCOV
296
        if (t == Reference && !subset_.empty() && base_type_ && base_type_->type_id() == types::TypeID::Pointer) {
×
UNCOV
297
            return true; // we hide the read of src for pointer types
×
UNCOV
298
        }
×
UNCOV
299
    }
×
UNCOV
300
    return false;
×
UNCOV
301
}
×
302

UNCOV
303
bool Memlet::is_src_direct_read() const {
×
UNCOV
304
    if (src_conn_ == "void" || src_conn_ == "deref") { // anything else is not an access node on the input
×
UNCOV
305
        auto t = type();
×
UNCOV
306
        if (t == Computational && base_type_ && (base_type_->type_id() != types::TypeID::Pointer || subset_.empty())) {
×
UNCOV
307
            return true;
×
UNCOV
308
        }
×
UNCOV
309
        if (t == Dereference_Dst) {
×
UNCOV
310
            return true;
×
UNCOV
311
        }
×
UNCOV
312
    }
×
313
    return false;
×
314
}
×
315

316
bool Memlet::is_src_pointed_to_read() const {
2✔
317
    if (src_conn_ == "void" || src_conn_ == "deref") {
2✔
318
        auto t = type();
2✔
319
        if (t == Dereference_Src) {
2✔
320
            return true;
×
321
        }
×
322
        if (t == Computational && base_type_->type_id() == types::TypeID::Pointer && !subset_.empty()) {
2✔
323
            return true; // implicitly reads src, because we are crazy
1✔
324
        }
1✔
325
    }
2✔
326
    return false;
1✔
327
}
2✔
328

329
bool Memlet::is_src_address_leak() const {
486✔
330
    if (src_conn_ == "void" || src_conn_ == "deref") {
486✔
331
        auto t = type();
486✔
332
        if (t == Reference) {
486✔
UNCOV
333
            if (subset_.empty()) {
×
UNCOV
334
                return true;
×
UNCOV
335
            }
×
UNCOV
336
            if (!subset_.empty() && base_type_ && base_type_->type_id() != types::TypeID::Pointer) {
×
UNCOV
337
                return true;
×
UNCOV
338
            }
×
UNCOV
339
        }
×
340
    }
486✔
341
    return false;
486✔
342
}
486✔
343

344
bool Memlet::is_src_pointed_to_address_leak(const types::IType& src_type) const {
511✔
345
    if (src_conn_ == "void" || src_conn_ == "deref") {
511✔
346
        auto t = type();
511✔
347
        if (src_type.type_id() == types::TypeID::Pointer) { // even if we use it as integer
511✔
348
            if (t == Computational && base_type_ && base_type_->type_id() != types::TypeID::Pointer) { // reinterpret as
230✔
349
                                                                                                       // not pointer,
350
                                                                                                       // but the
351
                                                                                                       // pointer is
352
                                                                                                       // still read
353
                return true;
1✔
354
            }
1✔
355
            if (t == Dereference_Dst) {
229✔
356
                return true;
1✔
357
            }
1✔
358
        }
229✔
359
        if (base_type_ && base_type_->type_id() == types::TypeID::Pointer) { // read as pointer, so more hidden things
509✔
360
                                                                             // possible
361
            if (t == Reference && !subset_.empty()) { // = address calc of ptr + subsets
248✔
362
                return true;
1✔
363
            }
1✔
364
            if (t == Dereference_Dst) { // straight reads the contents of the ptr
247✔
UNCOV
365
                return true;
×
UNCOV
366
            }
×
367
            if (t == Computational && subset().empty()) {
247✔
368
                return true;
22✔
369
            }
22✔
370
        }
247✔
371
    }
509✔
372
    return false;
486✔
373
}
511✔
374

375
bool Memlet::is_dst_write() const {
301✔
376
    if (dst_conn_ == "void" || dst_conn_ == "deref" || dst_conn_ == "ref") { // everyting else is not an access node at
301✔
377
                                                                             // dst
378
        auto t = type();
301✔
379
        if (t == Reference) {
301✔
380
            return true;
1✔
381
        }
1✔
382
        if (t == Computational) { // we already checked that dst is access node. So subset & type must be for that
300✔
383
            if (base_type_ && (base_type_->type_id() != types::TypeID::Pointer || subset_.empty())) {
299✔
384
                return true; // we either write to dst contents or if its a pointer, not to sth. indirect
201✔
385
            }
201✔
386
        }
299✔
387
        if (t == Dereference_Src) {
99✔
UNCOV
388
            return true;
×
UNCOV
389
        }
×
390
    }
99✔
391
    return false;
99✔
392
}
301✔
393

UNCOV
394
bool Memlet::is_dst_read() const {
×
UNCOV
395
    if (dst_conn_ == "void" || dst_conn_ == "deref" || dst_conn_ == "ref") {
×
396
        // everyting else is not an access node at dst
UNCOV
397
        auto t = type();
×
UNCOV
398
        if (t == Dereference_Dst) {
×
UNCOV
399
            return true;
×
UNCOV
400
        }
×
UNCOV
401
        if (t == Computational) { // we already checked that dst is access node. So subset & type must be for that
×
UNCOV
402
            if (base_type_ && base_type_->type_id() == types::TypeID::Pointer && !subset_.empty()) {
×
UNCOV
403
                return true; // we use dst only as base address for the actual write
×
UNCOV
404
            }
×
UNCOV
405
        }
×
UNCOV
406
    }
×
UNCOV
407
    return false;
×
UNCOV
408
}
×
409

410
bool Memlet::is_dst_pointed_to_write() const {
9✔
411
    if (dst_conn_ == "void" || dst_conn_ == "deref" || dst_conn_ == "ref") {
9✔
412
        auto t = type();
9✔
413
        if (t == Dereference_Dst) {
9✔
UNCOV
414
            return true;
×
UNCOV
415
        }
×
416
        if (t == Computational) { // we already checked that dst is access node. So subset & type must be for that
9✔
417
            if (!subset_.empty() && base_type_ && base_type_->type_id() == types::TypeID::Pointer) {
9✔
418
                return true; // we use dst only as base address for the actual write
4✔
419
            }
4✔
420
        }
9✔
421
    }
9✔
422
    return false;
5✔
423
}
9✔
424

425
const DataFlowNode& Memlet::src() const { return this->src_; };
2,490✔
426

427
DataFlowNode& Memlet::src() { return this->src_; };
8,598✔
428

429
const DataFlowNode& Memlet::dst() const { return this->dst_; };
1,608✔
430

431
DataFlowNode& Memlet::dst() { return this->dst_; };
2,732✔
432

433
const std::string& Memlet::src_conn() const { return this->src_conn_; };
1,133✔
434

435
const std::string& Memlet::dst_conn() const { return this->dst_conn_; };
7,301✔
436

437
const Subset& Memlet::subset() const { return this->subset_; };
21,699✔
438

439
void Memlet::set_subset(const Subset& subset) { this->subset_ = subset; };
69✔
440

441
const types::IType& Memlet::base_type() const { return *this->base_type_; };
8,705✔
442

443
void Memlet::set_base_type(const types::IType& base_type) { this->base_type_ = base_type.clone(); };
58✔
444

445
std::unique_ptr<types::IType> Memlet::result_type(const Function& function) const {
4,555✔
446
    return types::infer_type(function, *this->base_type_, this->subset_);
4,555✔
447
};
4,555✔
448

449
std::unique_ptr<Memlet> Memlet::clone(
450
    size_t element_id, const graph::Edge& edge, DataFlowGraph& parent, DataFlowNode& src, DataFlowNode& dst
UNCOV
451
) const {
×
UNCOV
452
    return std::unique_ptr<Memlet>(new Memlet(
×
UNCOV
453
        element_id,
×
UNCOV
454
        this->debug_info_,
×
UNCOV
455
        edge,
×
UNCOV
456
        parent,
×
UNCOV
457
        src,
×
UNCOV
458
        this->src_conn_,
×
UNCOV
459
        dst,
×
UNCOV
460
        this->dst_conn_,
×
UNCOV
461
        this->subset_,
×
UNCOV
462
        *this->base_type_
×
UNCOV
463
    ));
×
UNCOV
464
};
×
465

466
void Memlet::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
209✔
467
    Subset new_subset;
209✔
468
    for (auto& dim : this->subset_) {
209✔
469
        new_subset.push_back(symbolic::subs(dim, old_expression, new_expression));
190✔
470
    }
190✔
471
    this->subset_ = new_subset;
209✔
472
};
209✔
473

474
} // namespace data_flow
475
} // 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