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

daisytuner / docc / 26520678771

27 May 2026 03:22PM UTC coverage: 60.864% (-0.02%) from 60.886%
26520678771

Pull #719

github

web-flow
Merge 99c5e4f9d into 707dadcf8
Pull Request #719: Libnode ptr edges

961 of 1749 new or added lines in 52 files covered. (54.95%)

90 existing lines in 29 files now uncovered.

35222 of 57870 relevant lines covered (60.86%)

11043.61 hits per line

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

60.43
/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),
7,382✔
26
      dst_conn_(dst_conn), subset_(subset), base_type_(base_type.clone()) {
7,382✔
27

28
      };
7,382✔
29

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

39
    // Validate connections
40
    switch (this->type()) {
19,459✔
41
        case MemletType::Computational: {
19,010✔
42
            // Criterion: Must connect a code node and an access node with void connector at access node
43
            const AccessNode* data_node = nullptr;
19,010✔
44
            const CodeNode* code_node = nullptr;
19,010✔
45
            if (this->src_conn_ == "void") {
19,010✔
46
                data_node = dynamic_cast<const AccessNode*>(&this->src_);
12,201✔
47
                code_node = dynamic_cast<const CodeNode*>(&this->dst_);
12,201✔
48
                if (!data_node || !code_node) {
12,201✔
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_) ==
12,201✔
55
                    code_node->inputs().end()) {
12,201✔
56
                    throw InvalidSDFGException("Memlet: Computation memlets must have an input in the code node");
×
57
                }
×
58
            } else if (this->dst_conn_ == "void") {
12,201✔
59
                data_node = dynamic_cast<const AccessNode*>(&this->dst_);
6,809✔
60
                code_node = dynamic_cast<const CodeNode*>(&this->src_);
6,809✔
61
                if (!data_node || !code_node) {
6,809✔
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_) ==
6,809✔
68
                    code_node->outputs().end()) {
6,809✔
NEW
69
                    throw InvalidSDFGException(
×
NEW
70
                        "Memlet " + std::to_string(element_id_) + " attached to non-existent " + this->src_conn_ +
×
NEW
71
                        " connector on #" + std::to_string(code_node->element_id())
×
NEW
72
                    );
×
UNCOV
73
                }
×
74
            } else {
6,809✔
75
                throw InvalidSDFGException(
×
76
                    "Memlet: Computation memlets must have void connector at source or destination"
×
77
                );
×
78
            }
×
79

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

265
            break;
18✔
266
        }
18✔
267
        default:
18✔
268
            throw InvalidSDFGException("Memlet: Invalid memlet type");
×
269
    }
19,459✔
270
};
19,459✔
271

272
const graph::Edge Memlet::edge() const { return this->edge_; };
1,747✔
273

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

276
DataFlowGraph& Memlet::get_parent() { return *this->parent_; };
979✔
277

278
MemletType Memlet::type() const {
34,171✔
279
    if (this->dst_conn_ == "ref") {
34,171✔
280
        return Reference;
626✔
281
    } else if (this->dst_conn_ == "deref") {
33,545✔
282
        return Dereference_Src;
92✔
283
    } else if (this->src_conn_ == "deref") {
33,453✔
284
        return Dereference_Dst;
61✔
285
    } else {
33,392✔
286
        return Computational;
33,392✔
287
    }
33,392✔
288
}
34,171✔
289

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

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

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

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

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

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

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

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

428
const DataFlowNode& Memlet::src() const { return this->src_; };
3,853✔
429

430
DataFlowNode& Memlet::src() { return this->src_; };
11,189✔
431

432
const DataFlowNode& Memlet::dst() const { return this->dst_; };
1,888✔
433

434
DataFlowNode& Memlet::dst() { return this->dst_; };
3,376✔
435

436
const std::string& Memlet::src_conn() const { return this->src_conn_; };
612✔
437

438
const std::string& Memlet::dst_conn() const { return this->dst_conn_; };
9,657✔
439

440
const Subset& Memlet::subset() const { return this->subset_; };
22,071✔
441

442
void Memlet::set_subset(const Subset& subset) { this->subset_ = subset; };
153✔
443

444
const types::IType& Memlet::base_type() const { return *this->base_type_; };
10,460✔
445

446
void Memlet::set_base_type(const types::IType& base_type) { this->base_type_ = base_type.clone(); };
126✔
447

448
std::unique_ptr<types::IType> Memlet::result_type(const Function& function) const {
5,986✔
449
    return types::infer_type(function, *this->base_type_, this->subset_);
5,986✔
450
};
5,986✔
451

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

469
void Memlet::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
605✔
470
    Subset new_subset;
605✔
471
    for (auto& dim : this->subset_) {
623✔
472
        new_subset.push_back(symbolic::subs(dim, old_expression, new_expression));
623✔
473
    }
623✔
474
    this->subset_ = new_subset;
605✔
475
};
605✔
476

477
} // namespace data_flow
478
} // 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