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

daisytuner / docc / 23087278095

14 Mar 2026 11:44AM UTC coverage: 63.927% (+0.3%) from 63.617%
23087278095

push

github

web-flow
Merge pull request #568 from daisytuner/dead-data-elimination

Working on memory ownership & escape analysis

475 of 637 new or added lines in 28 files covered. (74.57%)

6 existing lines in 3 files now uncovered.

26010 of 40687 relevant lines covered (63.93%)

402.05 hits per line

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

70.15
/sdfg/src/data_flow/library_nodes/math/tensor/elementwise_node.cpp
1
#include "sdfg/data_flow/library_nodes/math/tensor/elementwise_node.h"
2

3
#include "sdfg/analysis/analysis.h"
4
#include "sdfg/builder/structured_sdfg_builder.h"
5
#include "sdfg/data_flow/tasklet.h"
6
#include "sdfg/types/type.h"
7

8
#include "sdfg/analysis/scope_analysis.h"
9

10
namespace sdfg {
11
namespace math {
12
namespace tensor {
13

14
ElementWiseUnaryNode::ElementWiseUnaryNode(
15
    size_t element_id,
16
    const DebugInfo& debug_info,
17
    const graph::Vertex vertex,
18
    data_flow::DataFlowGraph& parent,
19
    const data_flow::LibraryNodeCode& code,
20
    const std::vector<symbolic::Expression>& shape
21
)
22
    : TensorNode(element_id, debug_info, vertex, parent, code, {"Y"}, {"X"}, data_flow::ImplementationType_NONE),
76✔
23
      shape_(shape) {}
76✔
24

25
void ElementWiseUnaryNode::validate(const Function& function) const {
49✔
26
    TensorNode::validate(function);
49✔
27

28
    auto& graph = this->get_parent();
49✔
29

30
    auto& oedge = *graph.out_edges(*this).begin();
49✔
31
    auto& tensor_output = static_cast<const types::Tensor&>(oedge.base_type());
49✔
32
    if (tensor_output.shape().size() != this->shape_.size()) {
49✔
33
        throw InvalidSDFGException(
×
34
            "Library Node: Output tensor shape must match node shape. Output shape: " +
×
35
            std::to_string(tensor_output.shape().size()) + " Node shape: " + std::to_string(this->shape_.size())
×
36
        );
×
37
    }
×
38
    for (size_t i = 0; i < this->shape_.size(); ++i) {
162✔
39
        if (!symbolic::eq(tensor_output.shape().at(i), this->shape_.at(i))) {
113✔
40
            throw InvalidSDFGException(
×
41
                "Library Node: Output tensor shape does not match expected shape. Output shape: " +
×
42
                tensor_output.shape().at(i)->__str__() + " Expected shape: " + this->shape_.at(i)->__str__()
×
43
            );
×
44
        }
×
45
    }
113✔
46

47
    for (auto& iedge : graph.in_edges(*this)) {
49✔
48
        auto& tensor_input = static_cast<const types::Tensor&>(iedge.base_type());
48✔
49
        // Case 1: Scalar input is allowed as secondary input
50
        if (tensor_input.is_scalar()) {
48✔
51
            continue;
1✔
52
        }
1✔
53

54
        // Case 2: Tensor input
55
        if (tensor_input.shape().size() != this->shape_.size()) {
47✔
56
            throw InvalidSDFGException(
×
57
                "Library Node: Input tensor shape must match node shape. Input shape: " +
×
58
                std::to_string(tensor_input.shape().size()) + " Node shape: " + std::to_string(this->shape_.size())
×
59
            );
×
60
        }
×
61
        for (size_t i = 0; i < this->shape_.size(); ++i) {
160✔
62
            if (!symbolic::eq(tensor_input.shape().at(i), this->shape_.at(i))) {
113✔
63
                throw InvalidSDFGException(
×
64
                    "Library Node: Input tensor shape does not match expected shape. Input shape: " +
×
65
                    tensor_input.shape().at(i)->__str__() + " Expected shape: " + this->shape_.at(i)->__str__()
×
66
                );
×
67
            }
×
68
        }
113✔
69
    }
47✔
70
}
49✔
71

72
symbolic::SymbolSet ElementWiseUnaryNode::symbols() const {
2✔
73
    symbolic::SymbolSet syms;
2✔
74
    for (const auto& dim : shape_) {
4✔
75
        for (auto& atom : symbolic::atoms(dim)) {
4✔
76
            syms.insert(atom);
×
77
        }
×
78
    }
4✔
79
    return syms;
2✔
80
}
2✔
81

82
void ElementWiseUnaryNode::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
×
83
    for (auto& dim : shape_) {
×
84
        dim = symbolic::subs(dim, old_expression, new_expression);
×
85
    }
×
86
}
×
87

88
bool ElementWiseUnaryNode::expand(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
70✔
89
    auto& dataflow = this->get_parent();
70✔
90
    auto& block = static_cast<structured_control_flow::Block&>(*dataflow.get_parent());
70✔
91
    if (dataflow.in_degree(*this) != 1 || dataflow.out_degree(*this) != 1) {
70✔
92
        return false;
×
93
    }
×
94

95
    auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
70✔
96
    auto& parent = static_cast<structured_control_flow::Sequence&>(*scope_analysis.parent_scope(&block));
70✔
97
    int index = parent.index(block);
70✔
98
    auto& transition = parent.at(index).second;
70✔
99

100
    auto& input = this->inputs_.at(0);
70✔
101
    auto& output = this->outputs_.at(0);
70✔
102

103
    auto& iedge = *dataflow.in_edges(*this).begin();
70✔
104
    auto& oedge = *dataflow.out_edges(*this).begin();
70✔
105

106
    // Checks if legal
107
    auto& input_node = static_cast<data_flow::AccessNode&>(iedge.src());
70✔
108
    auto& output_node = static_cast<data_flow::AccessNode&>(oedge.dst());
70✔
109
    if (dataflow.in_degree(input_node) != 0 || dataflow.out_degree(output_node) != 0) {
70✔
110
        return false;
×
111
    }
×
112

113
    // Add new graph after the current block
114
    auto& new_sequence = builder.add_sequence_before(parent, block, transition.assignments(), block.debug_info());
70✔
115

116
    // Add maps
117
    data_flow::Subset new_subset;
70✔
118
    structured_control_flow::Sequence* last_scope = &new_sequence;
70✔
119
    structured_control_flow::Map* last_map = nullptr;
70✔
120
    std::vector<symbolic::Expression> loop_vars;
70✔
121

122
    for (size_t i = 0; i < this->shape_.size(); i++) {
236✔
123
        std::string indvar_str = builder.find_new_name("_i");
166✔
124
        builder.add_container(indvar_str, types::Scalar(types::PrimitiveType::UInt64));
166✔
125

126
        auto indvar = symbolic::symbol(indvar_str);
166✔
127
        auto init = symbolic::zero();
166✔
128
        auto update = symbolic::add(indvar, symbolic::one());
166✔
129
        auto condition = symbolic::Lt(indvar, this->shape_[i]);
166✔
130
        last_map = &builder.add_map(
166✔
131
            *last_scope,
166✔
132
            indvar,
166✔
133
            condition,
166✔
134
            init,
166✔
135
            update,
166✔
136
            structured_control_flow::ScheduleType_Sequential::create(),
166✔
137
            {},
166✔
138
            block.debug_info()
166✔
139
        );
166✔
140
        last_scope = &last_map->root();
166✔
141

142
        loop_vars.push_back(indvar);
166✔
143
    }
166✔
144

145
    bool success = this->expand_operation(
70✔
146
        builder,
70✔
147
        analysis_manager,
70✔
148
        *last_scope,
70✔
149
        input_node.data(),
70✔
150
        output_node.data(),
70✔
151
        static_cast<const types::Tensor&>(iedge.base_type()),
70✔
152
        static_cast<const types::Tensor&>(oedge.base_type()),
70✔
153
        loop_vars
70✔
154
    );
70✔
155
    if (!success) {
70✔
156
        return false;
×
157
    }
×
158

159
    // Clean up block
160
    builder.remove_memlet(block, iedge);
70✔
161
    builder.remove_memlet(block, oedge);
70✔
162
    builder.remove_node(block, input_node);
70✔
163
    builder.remove_node(block, output_node);
70✔
164
    builder.remove_node(block, *this);
70✔
165
    builder.remove_child(parent, index + 1);
70✔
166

167
    return true;
70✔
168
}
70✔
169

170
ElementWiseBinaryNode::ElementWiseBinaryNode(
171
    size_t element_id,
172
    const DebugInfo& debug_info,
173
    const graph::Vertex vertex,
174
    data_flow::DataFlowGraph& parent,
175
    const data_flow::LibraryNodeCode& code,
176
    const std::vector<symbolic::Expression>& shape
177
)
178
    : TensorNode(element_id, debug_info, vertex, parent, code, {"C"}, {"A", "B"}, data_flow::ImplementationType_NONE),
42✔
179
      shape_(shape) {}
42✔
180

181
void ElementWiseBinaryNode::validate(const Function& function) const {
30✔
182
    TensorNode::validate(function);
30✔
183

184
    auto& graph = this->get_parent();
30✔
185

186
    auto& oedge = *graph.out_edges(*this).begin();
30✔
187
    auto& tensor_output = static_cast<const types::Tensor&>(oedge.base_type());
30✔
188
    if (tensor_output.shape().size() != this->shape_.size()) {
30✔
189
        throw InvalidSDFGException(
×
190
            "Library Node: Output tensor shape must match node shape. Output shape: " +
×
191
            std::to_string(tensor_output.shape().size()) + " Node shape: " + std::to_string(this->shape_.size())
×
192
        );
×
193
    }
×
194
    for (size_t i = 0; i < this->shape_.size(); ++i) {
87✔
195
        if (!symbolic::eq(tensor_output.shape().at(i), this->shape_.at(i))) {
57✔
196
            throw InvalidSDFGException(
×
197
                "Library Node: Output tensor shape does not match expected shape. Output shape: " +
×
198
                tensor_output.shape().at(i)->__str__() + " Expected shape: " + this->shape_.at(i)->__str__()
×
199
            );
×
200
        }
×
201
    }
57✔
202

203
    for (auto& iedge : graph.in_edges(*this)) {
58✔
204
        auto& tensor_input = static_cast<const types::Tensor&>(iedge.base_type());
58✔
205
        // Case 1: Scalar input is allowed as secondary input
206
        if (tensor_input.is_scalar()) {
58✔
207
            continue;
6✔
208
        }
6✔
209

210
        // Case 2: Tensor input
211
        if (tensor_input.shape().size() != this->shape_.size()) {
52✔
212
            throw InvalidSDFGException(
×
213
                "Library Node: Input tensor shape must match node shape. Input shape: " +
×
214
                std::to_string(tensor_input.shape().size()) + " Node shape: " + std::to_string(this->shape_.size())
×
215
            );
×
216
        }
×
217
        for (size_t i = 0; i < this->shape_.size(); ++i) {
164✔
218
            if (!symbolic::eq(tensor_input.shape().at(i), this->shape_.at(i))) {
112✔
219
                throw InvalidSDFGException(
×
220
                    "Library Node: Input tensor shape does not match expected shape. Input shape: " +
×
221
                    tensor_input.shape().at(i)->__str__() + " Expected shape: " + this->shape_.at(i)->__str__()
×
222
                );
×
223
            }
×
224
        }
112✔
225
    }
52✔
226
}
30✔
227

228
symbolic::SymbolSet ElementWiseBinaryNode::symbols() const {
×
229
    symbolic::SymbolSet syms;
×
230
    for (const auto& dim : shape_) {
×
231
        for (auto& atom : symbolic::atoms(dim)) {
×
232
            syms.insert(atom);
×
233
        }
×
234
    }
×
235
    return syms;
×
236
}
×
237

238
void ElementWiseBinaryNode::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
×
239
    for (auto& dim : shape_) {
×
240
        dim = symbolic::subs(dim, old_expression, new_expression);
×
241
    }
×
242
}
×
243

244
void ElementWiseBinaryNode::create_input_memlet(
245
    builder::StructuredSDFGBuilder& builder,
246
    const std::string& input_conn,
247
    const std::string& input_name,
248
    const types::Tensor& input_type,
249
    const data_flow::Subset& subset,
250
    structured_control_flow::Block& code_block,
251
    data_flow::CodeNode& code_node
252
) {
54✔
253
    if (builder.subject().exists(input_name)) {
54✔
254
        auto& input_node = builder.add_access(code_block, input_name);
54✔
255
        if (input_type.is_scalar()) {
54✔
NEW
256
            builder.add_computational_memlet(code_block, input_node, code_node, input_conn, {}, input_type);
×
257
        } else {
54✔
258
            builder.add_computational_memlet(code_block, input_node, code_node, input_conn, subset, input_type);
54✔
259
        }
54✔
260
    } else {
54✔
NEW
261
        types::Scalar const_type(input_type.primitive_type());
×
NEW
262
        auto& input_node = builder.add_constant(code_block, input_name, const_type);
×
NEW
263
        builder.add_computational_memlet(code_block, input_node, code_node, input_conn, {}, input_type);
×
NEW
264
    }
×
265
}
54✔
266

267
bool ElementWiseBinaryNode::expand(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
31✔
268
    auto& dataflow = this->get_parent();
31✔
269
    auto& block = static_cast<structured_control_flow::Block&>(*dataflow.get_parent());
31✔
270
    if (dataflow.in_degree(*this) != 2 || dataflow.out_degree(*this) != 1) {
31✔
271
        return false;
×
272
    }
×
273
    auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
31✔
274
    auto& parent = static_cast<structured_control_flow::Sequence&>(*scope_analysis.parent_scope(&block));
31✔
275
    int index = parent.index(block);
31✔
276
    auto& transition = parent.at(index).second;
31✔
277

278
    auto& input_a = this->inputs_.at(0);
31✔
279
    auto& input_b = this->inputs_.at(1);
31✔
280
    auto& output = this->outputs_.at(0);
31✔
281

282
    auto iedge_a = &(*dataflow.in_edges(*this).begin());
31✔
283
    auto iedge_b = &(*(++dataflow.in_edges(*this).begin()));
31✔
284
    if (iedge_a->dst_conn() != "A") {
31✔
285
        std::swap(iedge_a, iedge_b);
×
286
    }
×
287
    auto& oedge = *dataflow.out_edges(*this).begin();
31✔
288

289
    // Checks if legal
290
    auto& input_node_a = static_cast<data_flow::AccessNode&>(iedge_a->src());
31✔
291
    auto& input_node_b = static_cast<data_flow::AccessNode&>(iedge_b->src());
31✔
292
    auto& output_node = static_cast<data_flow::AccessNode&>(oedge.dst());
31✔
293
    if (dataflow.in_degree(input_node_a) != 0 || dataflow.in_degree(input_node_b) != 0 ||
31✔
294
        dataflow.out_degree(output_node) != 0) {
31✔
295
        return false;
×
296
    }
×
297

298
    // Add new graph after the current block
299
    auto& new_sequence = builder.add_sequence_before(parent, block, transition.assignments(), block.debug_info());
31✔
300

301
    // Add maps
302
    structured_control_flow::Sequence* last_scope = &new_sequence;
31✔
303
    structured_control_flow::Map* last_map = nullptr;
31✔
304
    std::vector<symbolic::Expression> loop_vars;
31✔
305

306
    for (size_t i = 0; i < this->shape_.size(); i++) {
92✔
307
        std::string indvar_str = builder.find_new_name("_i");
61✔
308
        builder.add_container(indvar_str, types::Scalar(types::PrimitiveType::UInt64));
61✔
309

310
        auto indvar = symbolic::symbol(indvar_str);
61✔
311
        auto init = symbolic::zero();
61✔
312
        auto update = symbolic::add(indvar, symbolic::one());
61✔
313
        auto condition = symbolic::Lt(indvar, this->shape_[i]);
61✔
314
        last_map = &builder.add_map(
61✔
315
            *last_scope,
61✔
316
            indvar,
61✔
317
            condition,
61✔
318
            init,
61✔
319
            update,
61✔
320
            structured_control_flow::ScheduleType_Sequential::create(),
61✔
321
            {},
61✔
322
            block.debug_info()
61✔
323
        );
61✔
324
        last_scope = &last_map->root();
61✔
325

326
        loop_vars.push_back(indvar);
61✔
327
    }
61✔
328

329
    // Add tasklet block
330
    bool success = this->expand_operation(
31✔
331
        builder,
31✔
332
        analysis_manager,
31✔
333
        *last_scope,
31✔
334
        input_node_a.data(),
31✔
335
        input_node_b.data(),
31✔
336
        output_node.data(),
31✔
337
        static_cast<const types::Tensor&>(iedge_a->base_type()),
31✔
338
        static_cast<const types::Tensor&>(iedge_b->base_type()),
31✔
339
        static_cast<const types::Tensor&>(oedge.base_type()),
31✔
340
        loop_vars
31✔
341
    );
31✔
342
    if (!success) {
31✔
343
        return false;
×
344
    }
×
345

346
    // Clean up block
347
    builder.remove_memlet(block, *iedge_a);
31✔
348
    builder.remove_memlet(block, *iedge_b);
31✔
349
    builder.remove_memlet(block, oedge);
31✔
350
    builder.remove_node(block, input_node_a);
31✔
351
    // Only remove input_node_b if it's different from input_node_a
352
    if (&input_node_b != &input_node_a) {
31✔
353
        builder.remove_node(block, input_node_b);
31✔
354
    }
31✔
355
    builder.remove_node(block, output_node);
31✔
356
    builder.remove_node(block, *this);
31✔
357
    builder.remove_child(parent, index + 1);
31✔
358

359
    return true;
31✔
360
}
31✔
361

362
} // namespace tensor
363
} // namespace math
364
} // 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