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

daisytuner / sdfglib / 17653942017

11 Sep 2025 06:34PM UTC coverage: 59.145% (-0.6%) from 59.755%
17653942017

push

github

web-flow
Merge pull request #224 from daisytuner/revert-210-NewDebugInfo

Revert "New debug info"

313 of 466 new or added lines in 44 files covered. (67.17%)

21 existing lines in 4 files now uncovered.

9274 of 15680 relevant lines covered (59.15%)

115.92 hits per line

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

66.67
/src/data_flow/library_nodes/math/ml/conv.cpp
1
#include "sdfg/data_flow/library_nodes/math/ml/conv.h"
2

3
#include "sdfg/analysis/analysis.h"
4
#include "sdfg/builder/structured_sdfg_builder.h"
5

6
#include "sdfg/analysis/scope_analysis.h"
7

8
namespace sdfg {
9
namespace math {
10
namespace ml {
11

12
ConvNode::ConvNode(
2✔
13
    size_t element_id,
14
    const DebugInfo& debug_info,
15
    const graph::Vertex vertex,
16
    data_flow::DataFlowGraph& parent,
17
    bool has_bias,
18
    std::vector<size_t> dilations,
19
    std::vector<size_t> kernel_shape,
20
    std::vector<size_t> pads,
21
    std::vector<size_t> strides
22
)
23
    : MathNode(
2✔
24
          element_id,
2✔
25
          debug_info,
2✔
26
          vertex,
2✔
27
          parent,
2✔
28
          LibraryNodeType_Conv,
29
          {"Y"},
2✔
30
          {"X", "W"},
2✔
31
          data_flow::ImplementationType_NONE
32
      ),
33
      has_bias_(has_bias), dilations_(dilations), kernel_shape_(kernel_shape), pads_(pads), strides_(strides) {
2✔
34
    if (has_bias_) {
2✔
35
        this->inputs_.push_back("B");
×
36
    }
×
37
}
2✔
38

39
void ConvNode::validate(const Function& function) const {
×
40
    // TODO: Implement
41
}
×
42

43
bool ConvNode::has_bias() const { return has_bias_; }
×
44

45
std::vector<size_t> ConvNode::dilations() const { return dilations_; }
×
46

47
std::vector<size_t> ConvNode::kernel_shape() const { return kernel_shape_; }
×
48

49
std::vector<size_t> ConvNode::pads() const { return pads_; }
×
50

51
std::vector<size_t> ConvNode::strides() const { return strides_; }
×
52

53
bool ConvNode::expand(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
2✔
54
    auto& sdfg = builder.subject();
2✔
55
    auto& dataflow = this->get_parent();
2✔
56
    auto& block = static_cast<structured_control_flow::Block&>(*dataflow.get_parent());
2✔
57

58
    auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
2✔
59
    auto& parent = static_cast<structured_control_flow::Sequence&>(*scope_analysis.parent_scope(&block));
2✔
60
    int index = parent.index(block);
2✔
61
    auto& transition = parent.at(index).second;
2✔
62

63
    const data_flow::Memlet* iedge_X = nullptr;
2✔
64
    const data_flow::Memlet* iedge_W = nullptr;
2✔
65
    const data_flow::Memlet* iedge_B = nullptr;
2✔
66
    for (const auto& iedge : dataflow.in_edges(*this)) {
6✔
67
        if (iedge.dst_conn() == "X") {
4✔
68
            iedge_X = &iedge;
2✔
69
        } else if (iedge.dst_conn() == "W") {
4✔
70
            iedge_W = &iedge;
2✔
71
        } else if (iedge.dst_conn() == "B") {
2✔
72
            iedge_B = &iedge;
×
73
        }
×
74
    }
75

76
    const data_flow::Memlet* oedge_Y = nullptr;
2✔
77
    for (const auto& oedge : dataflow.out_edges(*this)) {
2✔
78
        if (oedge.src_conn() == "Y") {
2✔
79
            oedge_Y = &oedge;
2✔
80
            break;
2✔
81
        }
82
    }
83

84
    // Find names of input and output containers
85
    std::string X_name = static_cast<const data_flow::AccessNode&>(iedge_X->src()).data();
2✔
86
    std::string W_name = static_cast<const data_flow::AccessNode&>(iedge_W->src()).data();
2✔
87
    std::string Y_name = static_cast<const data_flow::AccessNode&>(oedge_Y->dst()).data();
2✔
88

89
    data_flow::Subset dims_X = iedge_X->end_subset();
2✔
90
    data_flow::Subset dims_W = iedge_W->end_subset();
2✔
91
    data_flow::Subset dims_B;
2✔
92
    if (iedge_B != nullptr) {
2✔
93
        dims_B = iedge_B->end_subset();
×
94
    }
×
95
    data_flow::Subset dims_Y = oedge_Y->end_subset();
2✔
96

97
    auto& new_sequence = builder.add_sequence_before(parent, block, transition.assignments(), block.debug_info());
2✔
98
    structured_control_flow::Sequence* last_scope = &new_sequence;
2✔
99

100
    /************************
101
     * Parallel dimensions *
102
     ************************/
103
    // Generate one Map per parallel dimension of the output tensor (Y).
104
    const auto& begin_Y_subset = oedge_Y->begin_subset();
2✔
105
    const auto& end_Y_subset = oedge_Y->end_subset();
2✔
106

107
    data_flow::Subset out_subset;
2✔
108
    std::vector<symbolic::Expression> parallel_syms;
2✔
109
    structured_control_flow::Map* last_map = nullptr;
2✔
110
    for (size_t dim = 0; dim < begin_Y_subset.size(); ++dim) {
10✔
111
        const auto& dim_begin = begin_Y_subset[dim];
8✔
112
        const auto& dim_end = end_Y_subset[dim];
8✔
113

114
        std::string indvar_str = builder.find_new_name("_i");
8✔
115
        builder.add_container(indvar_str, types::Scalar(types::PrimitiveType::UInt64));
8✔
116

117
        auto indvar = symbolic::symbol(indvar_str);
8✔
118
        auto init = dim_begin;
8✔
119
        auto update = symbolic::add(indvar, symbolic::one());
8✔
120
        auto condition = symbolic::Lt(indvar, symbolic::add(dim_end, symbolic::one()));
8✔
121

122
        last_map = &builder.add_map(
16✔
123
            *last_scope,
8✔
124
            indvar,
125
            condition,
126
            init,
127
            update,
128
            structured_control_flow::ScheduleType_Sequential::create(),
8✔
129
            {},
8✔
130
            block.debug_info()
8✔
131
        );
132
        last_scope = &last_map->root();
8✔
133
        out_subset.push_back(indvar);
8✔
134
        parallel_syms.push_back(indvar);
8✔
135
    }
8✔
136

137
    /************************
138
     * Reduction dimensions *
139
     ************************/
140
    // For convolution, we reduce over input channels and kernel dimensions.
141
    // Assuming weight tensor layout (M, C, k1, k2, ...), skip the first dim (output channels).
142
    const auto& begin_W_subset = iedge_W->begin_subset();
2✔
143
    const auto& end_W_subset = iedge_W->end_subset();
2✔
144

145
    std::vector<symbolic::Expression> reduction_syms;
2✔
146
    structured_control_flow::For* last_for = nullptr;
2✔
147
    for (size_t dim = 1; dim < begin_W_subset.size(); ++dim) {
8✔
148
        const auto& dim_begin = begin_W_subset[dim];
6✔
149
        const auto& dim_end = end_W_subset[dim];
6✔
150

151
        std::string indvar_str = builder.find_new_name("_r");
6✔
152
        builder.add_container(indvar_str, types::Scalar(types::PrimitiveType::UInt64));
6✔
153

154
        auto indvar = symbolic::symbol(indvar_str);
6✔
155
        auto init = dim_begin;
6✔
156
        auto update = symbolic::add(indvar, symbolic::one());
6✔
157
        auto condition = symbolic::Lt(indvar, symbolic::add(dim_end, symbolic::one()));
6✔
158

159
        last_for = &builder.add_for(*last_scope, indvar, condition, init, update, {}, block.debug_info());
6✔
160
        last_scope = &last_for->root();
6✔
161
        reduction_syms.push_back(indvar);
6✔
162
    }
6✔
163

164
    // Add innermost code block – convolution computation.
165
    auto& code_block = builder.add_block(*last_scope, {}, block.debug_info());
2✔
166

167
    // Reuse debug infos from original access nodes (if available).
168
    const DebugInfo& dbg_X = iedge_X->src().debug_info();
2✔
169
    const DebugInfo& dbg_W = iedge_W->src().debug_info();
2✔
170
    const DebugInfo& dbg_Y = oedge_Y->dst().debug_info();
2✔
171
    const DebugInfo dbg_B = (iedge_B != nullptr) ? iedge_B->src().debug_info() : DebugInfo();
2✔
172

173
    // Create new access nodes inside the innermost block.
174
    auto& X_acc = builder.add_access(code_block, X_name, dbg_X);
2✔
175
    auto& W_acc = builder.add_access(code_block, W_name, dbg_W);
2✔
176
    auto& Y_acc_in = builder.add_access(code_block, Y_name, dbg_Y);
2✔
177
    auto& Y_acc_out = builder.add_access(code_block, Y_name, dbg_Y);
2✔
178
    // Bias handled after reduction loops; no need to access B inside the reduction tasklet.
179

180
    /********************
181
     * Build subsets    *
182
     ********************/
183
    // Helper lambdas to safely fetch stride/dilation/pad values.
184
    auto int_expr = [](size_t v) { return symbolic::integer(static_cast<int64_t>(v)); };
14✔
185

186
    auto get_stride = [&](size_t idx) -> symbolic::Expression {
6✔
187
        if (idx < strides_.size()) {
4✔
188
            return int_expr(strides_[idx]);
4✔
189
        }
190
        return symbolic::one();
×
191
    };
4✔
192

193
    auto get_dilation = [&](size_t idx) -> symbolic::Expression {
6✔
194
        if (idx < dilations_.size()) {
4✔
195
            return int_expr(dilations_[idx]);
4✔
196
        }
197
        return symbolic::one();
×
198
    };
4✔
199

200
    auto get_pad = [&](size_t idx) -> symbolic::Expression {
6✔
201
        if (idx < pads_.size()) {
4✔
202
            return int_expr(pads_[idx]);
4✔
203
        }
204
        return symbolic::zero();
×
205
    };
4✔
206

207
    const size_t spatial_dims = kernel_shape_.size();
2✔
208

209
    // Extract commonly-used indices.
210
    auto get_parallel_sym = [&](size_t idx) -> symbolic::Expression {
10✔
211
        if (idx < parallel_syms.size()) return parallel_syms[idx];
8✔
212
        return symbolic::zero();
×
213
    };
8✔
214

215
    auto get_reduction_sym = [&](size_t idx) -> symbolic::Expression {
12✔
216
        if (idx < reduction_syms.size()) return reduction_syms[idx];
10✔
217
        return symbolic::zero();
×
218
    };
10✔
219

220
    auto N_idx = get_parallel_sym(0);
2✔
221
    auto M_idx = get_parallel_sym(1);
2✔
222

223
    // Input channel and kernel indices come from reduction variables.
224
    auto C_idx = get_reduction_sym(0);
2✔
225

226
    // Build X subset.
227
    data_flow::Subset subset_X;
2✔
228
    subset_X.push_back(N_idx); // Batch dim
2✔
229
    subset_X.push_back(C_idx); // Input channel dim
2✔
230
    for (size_t d = 0; d < spatial_dims; ++d) {
6✔
231
        symbolic::Expression out_d = get_parallel_sym(2 + d);
4✔
232
        symbolic::Expression ker_d = get_reduction_sym(1 + d);
4✔
233

234
        auto in_d = symbolic::
4✔
235
            sub(symbolic::add(symbolic::mul(out_d, get_stride(d)), symbolic::mul(ker_d, get_dilation(d))), get_pad(d));
4✔
236
        subset_X.push_back(in_d);
4✔
237
    }
4✔
238

239
    // Build W subset.
240
    data_flow::Subset subset_W;
2✔
241
    subset_W.push_back(M_idx); // Output channel (filter)
2✔
242
    subset_W.push_back(C_idx); // Input channel
2✔
243
    for (size_t d = 0; d < spatial_dims; ++d) {
6✔
244
        symbolic::Expression ker_d = get_reduction_sym(1 + d);
4✔
245
        subset_W.push_back(ker_d);
4✔
246
    }
4✔
247

248
    // Output Y subset is simply the parallel indices computed earlier.
249
    data_flow::Subset subset_Y = out_subset;
2✔
250

251
    // Bias subset (only along output channels).
252
    data_flow::Subset subset_B;
2✔
253
    if (has_bias_) {
2✔
254
        subset_B.push_back(M_idx);
×
255
    }
×
256

257
    /************************
258
     * Add computation node *
259
     ************************/
260
    // Create tasklet performing fused-multiply-add: _out = _x * _w + _y
261
    if (has_bias_) {
2✔
262
        // Bias will be added after reduction, so no change here.
263
    }
×
264

265
    auto& tasklet =
2✔
266
        builder.add_tasklet(code_block, data_flow::TaskletCode::fma, "_out", {"_x", "_w", "_y"}, block.debug_info());
2✔
267

268
    // Connect memlets.
269
    builder
4✔
270
        .add_computational_memlet(code_block, X_acc, tasklet, "_x", subset_X, iedge_X->base_type(), block.debug_info());
2✔
271
    builder
4✔
272
        .add_computational_memlet(code_block, W_acc, tasklet, "_w", subset_W, iedge_W->base_type(), block.debug_info());
2✔
273
    builder
4✔
274
        .add_computational_memlet(code_block, Y_acc_in, tasklet, "_y", subset_Y, oedge_Y->base_type(), block.debug_info());
2✔
275
    builder.add_computational_memlet(
4✔
276
        code_block, tasklet, "_out", Y_acc_out, subset_Y, oedge_Y->base_type(), block.debug_info()
2✔
277
    );
278

279
    // Bias: add once per output element outside reduction loops.
280
    if (has_bias_) {
2✔
281
        // Insert after the reduction loops (i.e., right after they finish).
282
        // We add a single tasklet in the parent scope (last_map root).
283
        std::string B_name = static_cast<const data_flow::AccessNode&>(iedge_B->src()).data();
×
NEW
284
        auto& bias_block = builder.add_block(last_map->root(), {}, block.debug_info());
×
285
        auto& B_acc_local = builder.add_access(bias_block, B_name, dbg_B);
×
286
        auto& Y_acc2_in = builder.add_access(bias_block, Y_name, dbg_Y);
×
287
        auto& Y_acc2_out = builder.add_access(bias_block, Y_name, dbg_Y);
×
288

NEW
289
        auto& bias_tasklet =
×
NEW
290
            builder.add_tasklet(bias_block, data_flow::TaskletCode::add, "_out", {"_bias", "_y"}, block.debug_info());
×
291
        builder.add_computational_memlet(
×
NEW
292
            bias_block, B_acc_local, bias_tasklet, "_bias", subset_B, iedge_B->base_type(), block.debug_info()
×
293
        );
294
        builder.add_computational_memlet(
×
NEW
295
            bias_block, Y_acc2_in, bias_tasklet, "_y", subset_Y, oedge_Y->base_type(), block.debug_info()
×
296
        );
297
        builder.add_computational_memlet(
×
NEW
298
            bias_block, bias_tasklet, "_out", Y_acc2_out, subset_Y, oedge_Y->base_type(), block.debug_info()
×
299
        );
300
    }
×
301

302
    // Clean up block
303
    builder.remove_memlet(block, *iedge_X);
2✔
304
    builder.remove_memlet(block, *iedge_W);
2✔
305
    if (iedge_B != nullptr) {
2✔
306
        builder.remove_memlet(block, *iedge_B);
×
307
    }
×
308
    builder.remove_memlet(block, *oedge_Y);
2✔
309
    builder.remove_node(block, *this);
2✔
310
    builder.remove_child(parent, index + 1);
2✔
311

312
    return true;
313
}
2✔
314

315
std::unique_ptr<data_flow::DataFlowNode> ConvNode::
316
    clone(size_t element_id, const graph::Vertex vertex, data_flow::DataFlowGraph& parent) const {
×
317
    return std::unique_ptr<data_flow::DataFlowNode>(new ConvNode(
×
318
        element_id,
×
319
        this->debug_info(),
×
320
        vertex,
×
321
        parent,
×
322
        this->has_bias_,
×
323
        this->dilations_,
×
324
        this->kernel_shape_,
×
325
        this->pads_,
×
326
        this->strides_
×
327
    ));
328
}
×
329

330
nlohmann::json ConvNodeSerializer::serialize(const data_flow::LibraryNode& library_node) {
×
331
    const ConvNode& relu_node = static_cast<const ConvNode&>(library_node);
×
332
    nlohmann::json j;
×
333

334
    j["code"] = relu_node.code().value();
×
335
    j["outputs"] = relu_node.outputs();
×
336
    j["inputs"] = relu_node.inputs();
×
337
    j["has_bias"] = relu_node.has_bias();
×
338
    j["dilations"] = relu_node.dilations();
×
339
    j["kernel_shape"] = relu_node.kernel_shape();
×
340
    j["pads"] = relu_node.pads();
×
341
    j["strides"] = relu_node.strides();
×
342

343
    return j;
×
344
}
×
345

346
data_flow::LibraryNode& ConvNodeSerializer::deserialize(
×
347
    const nlohmann::json& j, builder::StructuredSDFGBuilder& builder, structured_control_flow::Block& parent
348
) {
349
    auto code = j["code"].get<std::string>();
×
350
    if (code != LibraryNodeType_Conv.value()) {
×
351
        throw std::runtime_error("Invalid library node code");
×
352
    }
353

354
    sdfg::serializer::JSONSerializer serializer;
×
NEW
355
    DebugInfo debug_info = serializer.json_to_debug_info(j["debug_info"]);
×
356

357
    auto outputs = j.at("outputs").get<std::vector<std::string>>();
×
358
    auto inputs = j.at("inputs").get<std::vector<std::string>>();
×
359
    auto has_bias = j.at("has_bias").get<bool>();
×
360
    auto dilations = j.at("dilations").get<std::vector<size_t>>();
×
361
    auto kernel_shape = j.at("kernel_shape").get<std::vector<size_t>>();
×
362
    auto pads = j.at("pads").get<std::vector<size_t>>();
×
363
    auto strides = j.at("strides").get<std::vector<size_t>>();
×
364

365
    return builder.add_library_node<ConvNode>(parent, debug_info, has_bias, dilations, kernel_shape, pads, strides);
×
366
}
×
367

368
} // namespace ml
369
} // namespace math
370
} // 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