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

daisytuner / docc / 28806128926

06 Jul 2026 04:16PM UTC coverage: 62.96%. First build
28806128926

push

github

web-flow
New LibNodeExpansion pass (#740)

* Switched expansion "pipeline" to new LibNodeExpansionPass that can recursively expand using a LibNodeExpander impl.
- removed access to analysis_manager from expansion methods, as those would currently not be appropriately maintained between nodes
+ New expansion API handles more of the boilerplate code (checking for standalone, creating boundary access nodes, removing old elements)
* Also migrated sdfg-json-to-c.cpp to not using the expansion pipeline anymore
* toStr() for ReduceNodes and giving PyStructuredSDFG access to the output_dir for additional dumping
~ DotVisualizer : Fix on nested sequences.
+ DotVisualizer: visualize empty sequences
* DotVisualizer default-enabled show block/loop ids
 * while MathNodes still have an expand-method, the new infrastructure is based upon "Expander" classes. The MathNodeExpander just redirects to the method for now
 ~ Broadcast node still used old ptr-output semantics
 * updated tensor & blas node expand to new expand API, that handles more of the boilerplate code (checking, removing old nodes, creating standalone-replacement nodes)
 - removed Transpose Node. Was unused and on old ptr-semantics
 + StructuredSDFGBuilder.add_sequence_at, add_for_at, add_map_at
 * switched StructuredSDFGBuilder internally to use ptr of Assignments to express using default assignments when needed
 * updated tests to use the new expand_single_math_node helper function, instead of the method directly
 + pass and expand-single helper functions are ready to use other expanders
 + EinsumExpansionPass is basically the legacy ExpansionPass, only restricted to EinsumNodes. It still needs to run in a pipeline to ensure finding multiple EinsumNodes per block. This is temporary, because Einsum is the only node that already supported splitting a block, which the new expansion disallows, as it should handle it itself when needed (but that part is not yet implemented)

594 of 962 new or added lines in 31 files covered. (61.75%)

40554 of 64412 relevant lines covered (62.96%)

963.11 hits per line

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

46.67
/sdfg/src/data_flow/library_nodes/math/blas/batched_gemm_node.cpp
1
#include "sdfg/data_flow/library_nodes/math/blas/batched_gemm_node.h"
2
#include "sdfg/data_flow/library_nodes/math/blas/gemm_node.h"
3

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

7
namespace sdfg {
8
namespace math {
9
namespace blas {
10

11
BatchedGEMMNode::BatchedGEMMNode(
12
    size_t element_id,
13
    const DebugInfo& debug_info,
14
    const graph::Vertex vertex,
15
    data_flow::DataFlowGraph& parent,
16
    const data_flow::ImplementationType& implementation_type,
17
    const BLAS_Precision& precision,
18
    const BLAS_Layout& layout,
19
    const BLAS_Transpose& trans_a,
20
    const BLAS_Transpose& trans_b,
21
    symbolic::Expression batch_count,
22
    symbolic::Expression m,
23
    symbolic::Expression n,
24
    symbolic::Expression k,
25
    symbolic::Expression lda,
26
    symbolic::Expression ldb,
27
    symbolic::Expression ldc,
28
    symbolic::Expression stride_a,
29
    symbolic::Expression stride_b,
30
    symbolic::Expression stride_c
31
)
32
    : BLASNode(
9✔
33
          element_id,
9✔
34
          debug_info,
9✔
35
          vertex,
9✔
36
          parent,
9✔
37
          LibraryNodeType_BatchedGEMM,
9✔
38
          {},
9✔
39
          {"__A", "__B", "__C", "__alpha", "__beta"},
9✔
40
          implementation_type,
9✔
41
          precision
9✔
42
      ),
9✔
43
      layout_(layout), trans_a_(trans_a), trans_b_(trans_b), batch_count_(batch_count), m_(m), n_(n), k_(k), lda_(lda),
9✔
44
      ldb_(ldb), ldc_(ldc), stride_a_(stride_a), stride_b_(stride_b), stride_c_(stride_c) {}
9✔
45

46
BLAS_Layout BatchedGEMMNode::layout() const { return this->layout_; }
×
47

48
BLAS_Transpose BatchedGEMMNode::trans_a() const { return this->trans_a_; }
×
49

50
BLAS_Transpose BatchedGEMMNode::trans_b() const { return this->trans_b_; }
×
51

52
symbolic::Expression BatchedGEMMNode::batch_count() const { return this->batch_count_; }
8✔
53

54
symbolic::Expression BatchedGEMMNode::m() const { return this->m_; }
2✔
55

56
symbolic::Expression BatchedGEMMNode::n() const { return this->n_; }
2✔
57

58
symbolic::Expression BatchedGEMMNode::k() const { return this->k_; }
2✔
59

60
symbolic::Expression BatchedGEMMNode::lda() const { return this->lda_; }
2✔
61

62
symbolic::Expression BatchedGEMMNode::ldb() const { return this->ldb_; }
2✔
63

64
symbolic::Expression BatchedGEMMNode::ldc() const { return this->ldc_; }
2✔
65

66
symbolic::Expression BatchedGEMMNode::stride_a() const { return this->stride_a_; }
2✔
67

68
symbolic::Expression BatchedGEMMNode::stride_b() const { return this->stride_b_; }
2✔
69

70
symbolic::Expression BatchedGEMMNode::stride_c() const { return this->stride_c_; }
2✔
71

72
symbolic::SymbolSet BatchedGEMMNode::symbols() const {
×
73
    symbolic::SymbolSet syms;
×
74

75
    for (auto& atom : symbolic::atoms(this->batch_count_)) {
×
76
        syms.insert(atom);
×
77
    }
×
78
    for (auto& atom : symbolic::atoms(this->m_)) {
×
79
        syms.insert(atom);
×
80
    }
×
81
    for (auto& atom : symbolic::atoms(this->n_)) {
×
82
        syms.insert(atom);
×
83
    }
×
84
    for (auto& atom : symbolic::atoms(this->k_)) {
×
85
        syms.insert(atom);
×
86
    }
×
87
    for (auto& atom : symbolic::atoms(this->lda_)) {
×
88
        syms.insert(atom);
×
89
    }
×
90
    for (auto& atom : symbolic::atoms(this->ldb_)) {
×
91
        syms.insert(atom);
×
92
    }
×
93
    for (auto& atom : symbolic::atoms(this->ldc_)) {
×
94
        syms.insert(atom);
×
95
    }
×
96
    for (auto& atom : symbolic::atoms(this->stride_a_)) {
×
97
        syms.insert(atom);
×
98
    }
×
99
    for (auto& atom : symbolic::atoms(this->stride_b_)) {
×
100
        syms.insert(atom);
×
101
    }
×
102
    for (auto& atom : symbolic::atoms(this->stride_c_)) {
×
103
        syms.insert(atom);
×
104
    }
×
105

106
    return syms;
×
107
}
×
108

109
void BatchedGEMMNode::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
×
110
    this->batch_count_ = symbolic::subs(this->batch_count_, old_expression, new_expression);
×
111
    this->m_ = symbolic::subs(this->m_, old_expression, new_expression);
×
112
    this->n_ = symbolic::subs(this->n_, old_expression, new_expression);
×
113
    this->k_ = symbolic::subs(this->k_, old_expression, new_expression);
×
114
    this->lda_ = symbolic::subs(this->lda_, old_expression, new_expression);
×
115
    this->ldb_ = symbolic::subs(this->ldb_, old_expression, new_expression);
×
116
    this->ldc_ = symbolic::subs(this->ldc_, old_expression, new_expression);
×
117
    this->stride_a_ = symbolic::subs(this->stride_a_, old_expression, new_expression);
×
118
    this->stride_b_ = symbolic::subs(this->stride_b_, old_expression, new_expression);
×
119
    this->stride_c_ = symbolic::subs(this->stride_c_, old_expression, new_expression);
×
120
}
×
121

122
void BatchedGEMMNode::replace(const symbolic::ExpressionMapping& replacements) {
×
123
    this->batch_count_ = symbolic::subs(this->batch_count_, replacements);
×
124
    this->m_ = symbolic::subs(this->m_, replacements);
×
125
    this->n_ = symbolic::subs(this->n_, replacements);
×
126
    this->k_ = symbolic::subs(this->k_, replacements);
×
127
    this->lda_ = symbolic::subs(this->lda_, replacements);
×
128
    this->ldb_ = symbolic::subs(this->ldb_, replacements);
×
129
    this->ldc_ = symbolic::subs(this->ldc_, replacements);
×
130
    this->stride_a_ = symbolic::subs(this->stride_a_, replacements);
×
131
    this->stride_b_ = symbolic::subs(this->stride_b_, replacements);
×
132
    this->stride_c_ = symbolic::subs(this->stride_c_, replacements);
×
133
}
×
134

135
void BatchedGEMMNode::validate(const Function& function) const { BLASNode::validate(function); }
5✔
136

137
symbolic::Expression BatchedGEMMNode::flop() const {
×
138
    // batch_count * (2*m*n*k) approximately
139
    auto res_elems = symbolic::mul(this->m_, this->n_);
×
140
    auto mm_mul_ops = symbolic::mul(res_elems, this->k_);
×
141
    auto mm_sum_ops = symbolic::mul(res_elems, symbolic::sub(this->k_, symbolic::one()));
×
142
    auto per_batch = symbolic::add(mm_mul_ops, mm_sum_ops);
×
143
    return symbolic::mul(this->batch_count_, per_batch);
×
144
}
×
145

146
std::unique_ptr<data_flow::DataFlowNode> BatchedGEMMNode::
147
    clone(size_t element_id, const graph::Vertex vertex, data_flow::DataFlowGraph& parent) const {
×
148
    auto node_clone = std::unique_ptr<BatchedGEMMNode>(new BatchedGEMMNode(
×
149
        element_id,
×
150
        this->debug_info(),
×
151
        vertex,
×
152
        parent,
×
153
        this->implementation_type_,
×
154
        this->precision_,
×
155
        this->layout_,
×
156
        this->trans_a_,
×
157
        this->trans_b_,
×
158
        this->batch_count_,
×
159
        this->m_,
×
160
        this->n_,
×
161
        this->k_,
×
162
        this->lda_,
×
163
        this->ldb_,
×
164
        this->ldc_,
×
165
        this->stride_a_,
×
166
        this->stride_b_,
×
167
        this->stride_c_
×
168
    ));
×
169
    return std::move(node_clone);
×
170
}
×
171

172
std::string BatchedGEMMNode::toStr() const {
×
173
    return LibraryNode::toStr() + "(" + static_cast<char>(precision_) + ", " +
×
174
           std::string(BLAS_Layout_to_short_string(layout_)) + ", " + BLAS_Transpose_to_char(trans_a_) +
×
175
           BLAS_Transpose_to_char(trans_b_) + ", batch=" + batch_count_->__str__() + ", " + m_->__str__() + ", " +
×
176
           n_->__str__() + ", " + k_->__str__() + ", lda=" + lda_->__str__() + ", ldb=" + ldb_->__str__() +
×
177
           ", ldc=" + ldc_->__str__() + ", strA=" + stride_a_->__str__() + ", strB=" + stride_b_->__str__() +
×
178
           ", strC=" + stride_c_->__str__() + ")";
×
179
}
×
180

181
data_flow::PointerAccessType BatchedGEMMNode::pointer_access_type(int input_idx) const {
×
182
    if (input_idx == 0) { // A: batched m x k
×
183
        auto per_batch_range = GEMMNode::calc_matrix_access_range(m_, k_, lda_, trans_a_, layout_);
×
184
        return data_flow::PointerAccessMeta::create_read_only(symbolic::mul(batch_count_, per_batch_range), true);
×
185
    } else if (input_idx == 1) { // B: batched k x n
×
186
        auto per_batch_range = GEMMNode::calc_matrix_access_range(k_, n_, ldb_, trans_b_, layout_);
×
187
        return data_flow::PointerAccessMeta::create_read_only(symbolic::mul(batch_count_, per_batch_range), true);
×
188
    } else if (input_idx == 2) {
×
189
        auto per_batch_range = GEMMNode::calc_matrix_access_range(m_, n_, ldc_, BLAS_Transpose::No, layout_);
×
190
        auto range = symbolic::mul(batch_count_, per_batch_range);
×
191

192
        // Match GEMM handling: use full-write for dense layout and generic access otherwise.
193
        if (symbolic::eq(ldc_, n_)) {
×
194
            return data_flow::PointerAccessMeta::create_full_write_only(range, true);
×
195
        } else {
×
196
            auto pattern = data_flow::ConvexAccessPattern::create(range);
×
197
            return data_flow::PointerAccessMeta::create_generic(pattern->ref(), std::move(pattern), true);
×
198
        }
×
199
    } else {
×
200
        return LibraryNode::pointer_access_type(input_idx);
×
201
    }
×
202
}
×
203

204
passes::LibNodeExpander::ExpandOutcome BatchedGEMMNode::
205
    expand(passes::LibNodeExpander::ExpandContext& context, structured_control_flow::Block& block) {
2✔
206
    auto& dataflow = this->get_parent();
2✔
207

208
    if (trans_a_ == BLAS_Transpose::ConjTrans || trans_b_ == BLAS_Transpose::ConjTrans) {
2✔
NEW
209
        return context.unable();
×
210
    }
×
211

212
    auto primitive_type = scalar_primitive();
2✔
213
    if (primitive_type == types::PrimitiveType::Void) {
2✔
NEW
214
        return context.unable();
×
215
    }
×
216

217
    types::Scalar scalar_type(primitive_type);
2✔
218

219
    auto in_edges = dataflow.in_edges(*this);
2✔
220

221
    data_flow::Memlet* iedge_a = dataflow.in_edges_by_connector(*this).at(0);
2✔
222
    data_flow::Memlet* iedge_b = dataflow.in_edges_by_connector(*this).at(1);
2✔
223
    data_flow::Memlet* iedge_c = dataflow.in_edges_by_connector(*this).at(2);
2✔
224
    data_flow::Memlet* alpha_edge = dataflow.in_edges_by_connector(*this).at(3);
2✔
225
    data_flow::Memlet* beta_edge = dataflow.in_edges_by_connector(*this).at(4);
2✔
226

227
    using Use = passes::LibNodeExpander::InputUse;
2✔
228
    auto standalone = context.replacement_requires_access_nodes(
2✔
229
        {Use::IndirectRead, Use::IndirectRead, Use::IndirectWrite, Use::Scalar, Use::Scalar}
2✔
230
    );
2✔
231

232
    if (!standalone) {
2✔
NEW
233
        return context.unable();
×
234
    }
×
235

236
    auto& new_sequence = standalone->replace_with_sequence();
2✔
237
    auto& builder = standalone->builder();
2✔
238

239
    // Batch loop (outermost)
240
    std::string batch_indvar_str = builder.find_new_name("_batch");
2✔
241
    builder.add_container(batch_indvar_str, types::Scalar(types::PrimitiveType::UInt64));
2✔
242
    auto batch_indvar = symbolic::symbol(batch_indvar_str);
2✔
243
    auto& batch_loop = builder.add_map(
2✔
244
        new_sequence,
2✔
245
        batch_indvar,
2✔
246
        symbolic::Lt(batch_indvar, this->batch_count()),
2✔
247
        symbolic::zero(),
2✔
248
        symbolic::add(batch_indvar, symbolic::one()),
2✔
249
        structured_control_flow::ScheduleType_Sequential::create(),
2✔
250
        {},
2✔
251
        block.debug_info()
2✔
252
    );
2✔
253

254
    // Inner loops: i (m), j (n), k (k)
255
    std::vector<symbolic::Expression> indvar_ends{this->m(), this->n(), this->k()};
2✔
256
    data_flow::Subset new_subset;
2✔
257
    structured_control_flow::Sequence* last_scope = &batch_loop.root();
2✔
258
    structured_control_flow::StructuredLoop* last_map = nullptr;
2✔
259
    structured_control_flow::StructuredLoop* output_loop = nullptr;
2✔
260
    std::vector<std::string> indvar_names{"_i", "_j", "_k"};
2✔
261

262
    std::string sum_var = builder.find_new_name("_sum");
2✔
263
    builder.add_container(sum_var, scalar_type);
2✔
264

265
    for (size_t i = 0; i < 3; i++) {
8✔
266
        auto dim_begin = symbolic::zero();
6✔
267
        auto& dim_end = indvar_ends[i];
6✔
268

269
        std::string indvar_str = builder.find_new_name(indvar_names[i]);
6✔
270
        builder.add_container(indvar_str, types::Scalar(types::PrimitiveType::UInt64));
6✔
271

272
        auto indvar = symbolic::symbol(indvar_str);
6✔
273
        auto init = dim_begin;
6✔
274
        auto update = symbolic::add(indvar, symbolic::one());
6✔
275
        auto condition = symbolic::Lt(indvar, dim_end);
6✔
276
        if (i < 2) {
6✔
277
            last_map = &builder.add_map(
4✔
278
                *last_scope,
4✔
279
                indvar,
4✔
280
                condition,
4✔
281
                init,
4✔
282
                update,
4✔
283
                structured_control_flow::ScheduleType_Sequential::create(),
4✔
284
                {},
4✔
285
                block.debug_info()
4✔
286
            );
4✔
287
        } else {
4✔
288
            last_map = &builder.add_for(*last_scope, indvar, condition, init, update, {}, block.debug_info());
2✔
289
        }
2✔
290
        last_scope = &last_map->root();
6✔
291

292
        if (i == 1) {
6✔
293
            output_loop = last_map;
2✔
294
        }
2✔
295

296
        new_subset.push_back(indvar);
6✔
297
    }
6✔
298

299
    // Batch offsets for A, B, C
300
    auto a_batch_offset = symbolic::mul(batch_indvar, stride_a_);
2✔
301
    auto b_batch_offset = symbolic::mul(batch_indvar, stride_b_);
2✔
302
    auto c_batch_offset = symbolic::mul(batch_indvar, stride_c_);
2✔
303

304
    // Init sum = 0
305
    auto& init_block = builder.add_block_before(output_loop->root(), *last_map, {}, block.debug_info());
2✔
306
    auto& sum_init = builder.add_access(init_block, sum_var, block.debug_info());
2✔
307

308
    auto& zero_node = builder.add_constant(init_block, "0.0", alpha_edge->base_type(), block.debug_info());
2✔
309
    auto& init_tasklet = builder.add_tasklet(init_block, data_flow::assign, "_out", {"_in"}, block.debug_info());
2✔
310
    builder.add_computational_memlet(init_block, zero_node, init_tasklet, "_in", {}, block.debug_info());
2✔
311
    builder.add_computational_memlet(init_block, init_tasklet, "_out", sum_init, {}, block.debug_info());
2✔
312

313
    // FMA: sum += A[batch_offset + ...] * B[batch_offset + ...]
314
    auto& code_block = builder.add_block(*last_scope, {}, block.debug_info());
2✔
315
    auto& input_node_a_new = standalone->add_indirect_read_access(code_block, A_INPUT_IDX);
2✔
316
    auto& input_node_b_new = standalone->add_indirect_read_access(code_block, B_INPUT_IDX);
2✔
317

318
    auto& core_fma =
2✔
319
        builder.add_tasklet(code_block, data_flow::fp_fma, "_out", {"_in1", "_in2", "_in3"}, block.debug_info());
2✔
320
    auto& sum_in = builder.add_access(code_block, sum_var, block.debug_info());
2✔
321
    auto& sum_out = builder.add_access(code_block, sum_var, block.debug_info());
2✔
322
    builder.add_computational_memlet(code_block, sum_in, core_fma, "_in3", {}, block.debug_info());
2✔
323

324
    // Row-major indexing with batch offset
325
    // No transpose: A is m×k, access A[batch*stride_a + lda*i + k]
326
    // Transpose:    A is k×m stored, access A[batch*stride_a + lda*k + i]
327
    symbolic::Expression a_idx =
2✔
328
        (trans_a_ == BLAS_Transpose::Trans)
2✔
329
            ? symbolic::add(a_batch_offset, symbolic::add(symbolic::mul(lda(), new_subset[2]), new_subset[0]))
2✔
330
            : symbolic::add(a_batch_offset, symbolic::add(symbolic::mul(lda(), new_subset[0]), new_subset[2]));
2✔
331
    builder.add_computational_memlet(
2✔
332
        code_block, input_node_a_new, core_fma, "_in1", {a_idx}, iedge_a->base_type(), iedge_a->debug_info()
2✔
333
    );
2✔
334
    // No transpose: B is k×n, access B[batch*stride_b + ldb*k + j]
335
    // Transpose:    B is n×k stored, access B[batch*stride_b + ldb*j + k]
336
    symbolic::Expression b_idx =
2✔
337
        (trans_b_ == BLAS_Transpose::Trans)
2✔
338
            ? symbolic::add(b_batch_offset, symbolic::add(symbolic::mul(ldb(), new_subset[1]), new_subset[2]))
2✔
339
            : symbolic::add(b_batch_offset, symbolic::add(symbolic::mul(ldb(), new_subset[2]), new_subset[1]));
2✔
340
    builder.add_computational_memlet(
2✔
341
        code_block, input_node_b_new, core_fma, "_in2", {b_idx}, iedge_b->base_type(), iedge_b->debug_info()
2✔
342
    );
2✔
343
    builder.add_computational_memlet(code_block, core_fma, "_out", sum_out, {}, iedge_c->debug_info());
2✔
344

345
    // Flush: C[batch*stride_c + ldc*i + j] = alpha * sum + beta * C[...]
346
    auto& flush_block = builder.add_block_after(output_loop->root(), *last_map, {}, block.debug_info());
2✔
347
    auto& sum_final = builder.add_access(flush_block, sum_var, block.debug_info());
2✔
348
    auto& input_node_c_new = standalone->add_indirect_read_access(flush_block, C_INPUT_IDX);
2✔
349
    symbolic::Expression c_idx =
2✔
350
        symbolic::add(c_batch_offset, symbolic::add(symbolic::mul(ldc(), new_subset[0]), new_subset[1]));
2✔
351

352
    // alpha * sum
353
    auto& scale_sum_tasklet =
2✔
354
        builder.add_tasklet(flush_block, data_flow::TaskletCode::fp_mul, "_out", {"_in1", "_in2"}, block.debug_info());
2✔
355
    builder.add_computational_memlet(flush_block, sum_final, scale_sum_tasklet, "_in1", {}, block.debug_info());
2✔
356
    auto& alpha_node = standalone->add_scalar_input_access(flush_block, ALPHA_INPUT_IDX);
2✔
357
    builder.add_computational_memlet(flush_block, alpha_node, scale_sum_tasklet, "_in2", {}, block.debug_info());
2✔
358

359
    std::string scaled_sum_temp = builder.find_new_name("scaled_sum_temp");
2✔
360
    builder.add_container(scaled_sum_temp, scalar_type);
2✔
361
    auto& scaled_sum_final = builder.add_access(flush_block, scaled_sum_temp, block.debug_info());
2✔
362
    builder.add_computational_memlet(
2✔
363
        flush_block, scale_sum_tasklet, "_out", scaled_sum_final, {}, scalar_type, block.debug_info()
2✔
364
    );
2✔
365

366
    // beta * C[...]
367
    auto& scale_input_tasklet =
2✔
368
        builder.add_tasklet(flush_block, data_flow::TaskletCode::fp_mul, "_out", {"_in1", "_in2"}, block.debug_info());
2✔
369
    builder.add_computational_memlet(
2✔
370
        flush_block, input_node_c_new, scale_input_tasklet, "_in1", {c_idx}, iedge_c->base_type(), iedge_c->debug_info()
2✔
371
    );
2✔
372
    auto& beta_node = standalone->add_scalar_input_access(flush_block, BETA_INPUT_IDX);
2✔
373
    builder.add_computational_memlet(flush_block, beta_node, scale_input_tasklet, "_in2", {}, block.debug_info());
2✔
374

375
    std::string scaled_input_temp = builder.find_new_name("scaled_input_temp");
2✔
376
    builder.add_container(scaled_input_temp, scalar_type);
2✔
377
    auto& scaled_input_c = builder.add_access(flush_block, scaled_input_temp, block.debug_info());
2✔
378
    builder.add_computational_memlet(
2✔
379
        flush_block, scale_input_tasklet, "_out", scaled_input_c, {}, scalar_type, block.debug_info()
2✔
380
    );
2✔
381

382
    // alpha*sum + beta*C
383
    auto& flush_add_tasklet =
2✔
384
        builder.add_tasklet(flush_block, data_flow::TaskletCode::fp_add, "_out", {"_in1", "_in2"}, block.debug_info());
2✔
385
    auto& output_node_new = standalone->add_indirect_write_access(flush_block, C_INPUT_IDX);
2✔
386
    builder.add_computational_memlet(
2✔
387
        flush_block, scaled_sum_final, flush_add_tasklet, "_in1", {}, scalar_type, block.debug_info()
2✔
388
    );
2✔
389
    builder.add_computational_memlet(
2✔
390
        flush_block, scaled_input_c, flush_add_tasklet, "_in2", {}, scalar_type, block.debug_info()
2✔
391
    );
2✔
392
    builder.add_computational_memlet(
2✔
393
        flush_block, flush_add_tasklet, "_out", output_node_new, {c_idx}, iedge_c->base_type(), iedge_c->debug_info()
2✔
394
    );
2✔
395

396
    return standalone->successfully_expanded();
2✔
397
}
2✔
398

399
nlohmann::json BatchedGEMMNodeSerializer::serialize(const data_flow::LibraryNode& library_node) {
×
400
    const BatchedGEMMNode& node = static_cast<const BatchedGEMMNode&>(library_node);
×
401
    nlohmann::json j;
×
402

403
    serializer::JSONSerializer serializer;
×
404
    j["code"] = node.code().value();
×
405
    j["precision"] = node.precision();
×
406
    j["layout"] = node.layout();
×
407
    j["trans_a"] = node.trans_a();
×
408
    j["trans_b"] = node.trans_b();
×
409
    j["batch_count"] = serializer.expression(node.batch_count());
×
410
    j["m"] = serializer.expression(node.m());
×
411
    j["n"] = serializer.expression(node.n());
×
412
    j["k"] = serializer.expression(node.k());
×
413
    j["lda"] = serializer.expression(node.lda());
×
414
    j["ldb"] = serializer.expression(node.ldb());
×
415
    j["ldc"] = serializer.expression(node.ldc());
×
416
    j["stride_a"] = serializer.expression(node.stride_a());
×
417
    j["stride_b"] = serializer.expression(node.stride_b());
×
418
    j["stride_c"] = serializer.expression(node.stride_c());
×
419

420
    return j;
×
421
}
×
422

423
data_flow::LibraryNode& BatchedGEMMNodeSerializer::deserialize(
424
    const nlohmann::json& j, builder::StructuredSDFGBuilder& builder, structured_control_flow::Block& parent
425
) {
×
426
    assert(j.contains("element_id"));
×
427
    assert(j.contains("code"));
×
428
    assert(j.contains("debug_info"));
×
429

430
    auto code = j["code"].get<std::string>();
×
431
    if (code != LibraryNodeType_BatchedGEMM.value()) {
×
432
        throw std::runtime_error("Invalid library node code");
×
433
    }
×
434

435
    sdfg::serializer::JSONSerializer serializer;
×
436
    DebugInfo debug_info = serializer.json_to_debug_info(j["debug_info"]);
×
437

438
    auto precision = j.at("precision").get<BLAS_Precision>();
×
439
    auto layout = j.at("layout").get<BLAS_Layout>();
×
440
    auto trans_a = j.at("trans_a").get<BLAS_Transpose>();
×
441
    auto trans_b = j.at("trans_b").get<BLAS_Transpose>();
×
442
    auto batch_count = symbolic::parse(j.at("batch_count"));
×
443
    auto m = symbolic::parse(j.at("m"));
×
444
    auto n = symbolic::parse(j.at("n"));
×
445
    auto k = symbolic::parse(j.at("k"));
×
446
    auto lda = symbolic::parse(j.at("lda"));
×
447
    auto ldb = symbolic::parse(j.at("ldb"));
×
448
    auto ldc = symbolic::parse(j.at("ldc"));
×
449
    auto stride_a = symbolic::parse(j.at("stride_a"));
×
450
    auto stride_b = symbolic::parse(j.at("stride_b"));
×
451
    auto stride_c = symbolic::parse(j.at("stride_c"));
×
452

453
    auto implementation_type = j.at("implementation_type").get<std::string>();
×
454

455
    return builder.add_library_node<BatchedGEMMNode>(
×
456
        parent,
×
457
        debug_info,
×
458
        implementation_type,
×
459
        precision,
×
460
        layout,
×
461
        trans_a,
×
462
        trans_b,
×
463
        batch_count,
×
464
        m,
×
465
        n,
×
466
        k,
×
467
        lda,
×
468
        ldb,
×
469
        ldc,
×
470
        stride_a,
×
471
        stride_b,
×
472
        stride_c
×
473
    );
×
474
}
×
475

476
} // namespace blas
477
} // namespace math
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