• 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

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

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

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

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

40
BLAS_Layout GEMMNode::layout() const { return this->layout_; };
4✔
41

42
BLAS_Transpose GEMMNode::trans_a() const { return this->trans_a_; };
5✔
43

44
BLAS_Transpose GEMMNode::trans_b() const { return this->trans_b_; };
5✔
45

46
symbolic::Expression GEMMNode::m() const { return this->m_; };
19✔
47

48
symbolic::Expression GEMMNode::n() const { return this->n_; };
21✔
49

50
symbolic::Expression GEMMNode::k() const { return this->k_; };
19✔
51

52
symbolic::Expression GEMMNode::lda() const { return this->lda_; };
11✔
53

54
symbolic::Expression GEMMNode::ldb() const { return this->ldb_; };
11✔
55

56
symbolic::Expression GEMMNode::ldc() const { return this->ldc_; };
11✔
57

58
symbolic::SymbolSet GEMMNode::symbols() const {
×
59
    symbolic::SymbolSet syms;
×
60

61
    for (auto& atom : symbolic::atoms(this->m_)) {
×
62
        syms.insert(atom);
×
63
    }
×
64
    for (auto& atom : symbolic::atoms(this->n_)) {
×
65
        syms.insert(atom);
×
66
    }
×
67
    for (auto& atom : symbolic::atoms(this->k_)) {
×
68
        syms.insert(atom);
×
69
    }
×
70
    for (auto& atom : symbolic::atoms(this->lda_)) {
×
71
        syms.insert(atom);
×
72
    }
×
73
    for (auto& atom : symbolic::atoms(this->ldb_)) {
×
74
        syms.insert(atom);
×
75
    }
×
76
    for (auto& atom : symbolic::atoms(this->ldc_)) {
×
77
        syms.insert(atom);
×
78
    }
×
79

80
    return syms;
×
81
};
×
82

83
void GEMMNode::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
×
84
    this->m_ = symbolic::subs(this->m_, old_expression, new_expression);
×
85
    this->n_ = symbolic::subs(this->n_, old_expression, new_expression);
×
86
    this->k_ = symbolic::subs(this->k_, old_expression, new_expression);
×
87
    this->lda_ = symbolic::subs(this->lda_, old_expression, new_expression);
×
88
    this->ldb_ = symbolic::subs(this->ldb_, old_expression, new_expression);
×
89
    this->ldc_ = symbolic::subs(this->ldc_, old_expression, new_expression);
×
90
};
×
91

92
void GEMMNode::replace(const symbolic::ExpressionMapping& replacements) {
×
93
    this->m_ = symbolic::subs(this->m_, replacements);
×
94
    this->n_ = symbolic::subs(this->n_, replacements);
×
95
    this->k_ = symbolic::subs(this->k_, replacements);
×
96
    this->lda_ = symbolic::subs(this->lda_, replacements);
×
97
    this->ldb_ = symbolic::subs(this->ldb_, replacements);
×
98
    this->ldc_ = symbolic::subs(this->ldc_, replacements);
×
99
};
×
100

101
void GEMMNode::validate(const Function& function) const { BLASNode::validate(function); }
14✔
102

103
passes::LibNodeExpander::ExpandOutcome GEMMNode::
104
    expand(passes::LibNodeExpander::ExpandContext& context, structured_control_flow::Block& block) {
8✔
105
    auto& dataflow = this->get_parent();
8✔
106

107
    if (trans_a_ == BLAS_Transpose::ConjTrans || trans_b_ == BLAS_Transpose::ConjTrans) {
8✔
NEW
108
        return context.unable();
×
109
    }
×
110

111
    auto primitive_type = scalar_primitive();
8✔
112
    if (primitive_type == types::PrimitiveType::Void) {
8✔
NEW
113
        return context.unable();
×
114
    }
×
115

116
    types::Scalar scalar_type(primitive_type);
8✔
117

118
    auto in_edges = dataflow.in_edges(*this);
8✔
119
    auto in_edges_it = in_edges.begin();
8✔
120

121
    data_flow::Memlet* iedge_a = nullptr;
8✔
122
    data_flow::Memlet* iedge_b = nullptr;
8✔
123
    data_flow::Memlet* iedge_c = nullptr;
8✔
124
    data_flow::Memlet* alpha_edge = nullptr;
8✔
125
    data_flow::Memlet* beta_edge = nullptr;
8✔
126
    while (in_edges_it != in_edges.end()) {
48✔
127
        auto& edge = *in_edges_it;
40✔
128
        auto dst_conn = edge.dst_conn();
40✔
129
        if (dst_conn == "__A") {
40✔
130
            iedge_a = &edge;
8✔
131
        } else if (dst_conn == "__B") {
32✔
132
            iedge_b = &edge;
8✔
133
        } else if (dst_conn == "__C") {
24✔
134
            iedge_c = &edge;
8✔
135
        } else if (dst_conn == "__alpha") {
16✔
136
            alpha_edge = &edge;
8✔
137
        } else if (dst_conn == "__beta") {
8✔
138
            beta_edge = &edge;
8✔
139
        } else {
8✔
140
            throw InvalidSDFGException("GEMMNode has unexpected input: " + dst_conn);
×
141
        }
×
142
        ++in_edges_it;
40✔
143
    }
40✔
144

145
    using Dir = passes::LibNodeExpander::InputUse;
8✔
146
    auto standalone = context.replacement_requires_access_nodes(
8✔
147
        {Dir::IndirectRead, Dir::IndirectRead, Dir::IndirectReadWrite, Dir::Scalar, Dir::Scalar}
8✔
148
    );
8✔
149

150
    if (!standalone) {
8✔
NEW
151
        return context.unable();
×
NEW
152
    }
×
153

154
    // Add new graph after the current block
155
    auto& new_sequence = standalone->replace_with_sequence();
8✔
156
    auto& builder = standalone->builder();
8✔
157

158
    // Add maps
159
    std::vector<symbolic::Expression> indvar_ends{this->m(), this->n(), this->k()};
8✔
160
    data_flow::Subset new_subset;
8✔
161
    structured_control_flow::Sequence* last_scope = &new_sequence;
8✔
162
    structured_control_flow::StructuredLoop* last_map = nullptr;
8✔
163
    structured_control_flow::StructuredLoop* output_loop = nullptr;
8✔
164
    std::vector<std::string> indvar_names{"_i", "_j", "_k"};
8✔
165

166
    std::string sum_var = builder.find_new_name("_sum");
8✔
167
    builder.add_container(sum_var, scalar_type);
8✔
168

169
    for (size_t i = 0; i < 3; i++) {
32✔
170
        auto dim_begin = symbolic::zero();
24✔
171
        auto& dim_end = indvar_ends[i];
24✔
172

173
        std::string indvar_str = builder.find_new_name(indvar_names[i]);
24✔
174
        builder.add_container(indvar_str, types::Scalar(types::PrimitiveType::UInt64));
24✔
175

176
        auto indvar = symbolic::symbol(indvar_str);
24✔
177
        auto init = dim_begin;
24✔
178
        auto update = symbolic::add(indvar, symbolic::one());
24✔
179
        auto condition = symbolic::Lt(indvar, dim_end);
24✔
180
        if (i < 2) {
24✔
181
            last_map = &builder.add_map(
16✔
182
                *last_scope,
16✔
183
                indvar,
16✔
184
                condition,
16✔
185
                init,
16✔
186
                update,
16✔
187
                structured_control_flow::ScheduleType_Sequential::create(),
16✔
188
                {},
16✔
189
                block.debug_info()
16✔
190
            );
16✔
191
        } else {
16✔
192
            last_map = &builder.add_for(*last_scope, indvar, condition, init, update, {}, block.debug_info());
8✔
193
        }
8✔
194
        last_scope = &last_map->root();
24✔
195

196
        if (i == 1) {
24✔
197
            output_loop = last_map;
8✔
198
        }
8✔
199

200
        new_subset.push_back(indvar);
24✔
201
    }
24✔
202

203

204
    // Add code
205
    auto& init_block = builder.add_block_before(output_loop->root(), *last_map, {}, block.debug_info());
8✔
206
    auto& sum_init = builder.add_access(init_block, sum_var, block.debug_info());
8✔
207

208
    auto& zero_node = builder.add_constant(init_block, "0.0", alpha_edge->base_type(), block.debug_info());
8✔
209
    auto& init_tasklet = builder.add_tasklet(init_block, data_flow::assign, "_out", {"_in"}, block.debug_info());
8✔
210
    builder.add_computational_memlet(init_block, zero_node, init_tasklet, "_in", {}, block.debug_info());
8✔
211
    builder.add_computational_memlet(init_block, init_tasklet, "_out", sum_init, {}, block.debug_info());
8✔
212

213
    auto& code_block = builder.add_block(*last_scope, {}, block.debug_info());
8✔
214
    auto& input_node_a_new = standalone->add_indirect_read_access(code_block, A_INPUT_IDX);
8✔
215
    auto& input_node_b_new = standalone->add_indirect_read_access(code_block, B_INPUT_IDX);
8✔
216

217
    auto& core_fma =
8✔
218
        builder.add_tasklet(code_block, data_flow::fp_fma, "_out", {"_in1", "_in2", "_in3"}, block.debug_info());
8✔
219
    auto& sum_in = builder.add_access(code_block, sum_var, block.debug_info());
8✔
220
    auto& sum_out = builder.add_access(code_block, sum_var, block.debug_info());
8✔
221
    builder.add_computational_memlet(code_block, sum_in, core_fma, "_in3", {}, block.debug_info());
8✔
222

223
    // Row-major indexing: address = ld * row + col
224
    // No transpose: A is m×k, access A[i, k] => lda*i + k
225
    // Transpose:    A is k×m stored, access A[k, i] => lda*k + i
226
    symbolic::Expression a_idx = (trans_a_ == BLAS_Transpose::Trans)
8✔
227
                                     ? symbolic::add(symbolic::mul(lda(), new_subset[2]), new_subset[0])
8✔
228
                                     : symbolic::add(symbolic::mul(lda(), new_subset[0]), new_subset[2]);
8✔
229
    builder.add_computational_memlet(
8✔
230
        code_block, input_node_a_new, core_fma, "_in1", {a_idx}, iedge_a->base_type(), iedge_a->debug_info()
8✔
231
    );
8✔
232
    // No transpose: B is k×n, access B[k, j] => ldb*k + j
233
    // Transpose:    B is n×k stored, access B[j, k] => ldb*j + k
234
    symbolic::Expression b_idx = (trans_b_ == BLAS_Transpose::Trans)
8✔
235
                                     ? symbolic::add(symbolic::mul(ldb(), new_subset[1]), new_subset[2])
8✔
236
                                     : symbolic::add(symbolic::mul(ldb(), new_subset[2]), new_subset[1]);
8✔
237
    builder.add_computational_memlet(
8✔
238
        code_block, input_node_b_new, core_fma, "_in2", {b_idx}, iedge_b->base_type(), iedge_b->debug_info()
8✔
239
    );
8✔
240
    builder.add_computational_memlet(code_block, core_fma, "_out", sum_out, {}, iedge_c->debug_info());
8✔
241

242
    auto& flush_block = builder.add_block_after(output_loop->root(), *last_map, {}, block.debug_info());
8✔
243
    auto& sum_final = builder.add_access(flush_block, sum_var, block.debug_info());
8✔
244
    auto& input_node_c_new = standalone->add_indirect_read_access(flush_block, C_INPUT_IDX);
8✔
245
    symbolic::Expression c_idx = symbolic::add(symbolic::mul(ldc(), new_subset[0]), new_subset[1]);
8✔
246

247
    auto& scale_sum_tasklet =
8✔
248
        builder.add_tasklet(flush_block, data_flow::TaskletCode::fp_mul, "_out", {"_in1", "_in2"}, block.debug_info());
8✔
249
    builder.add_computational_memlet(flush_block, sum_final, scale_sum_tasklet, "_in1", {}, block.debug_info());
8✔
250
    auto& alpha_node = standalone->add_scalar_input_access(flush_block, ALPHA_INPUT_IDX);
8✔
251
    builder.add_computational_memlet(flush_block, alpha_node, scale_sum_tasklet, "_in2", {}, block.debug_info());
8✔
252

253
    std::string scaled_sum_temp = builder.find_new_name("scaled_sum_temp");
8✔
254
    builder.add_container(scaled_sum_temp, scalar_type);
8✔
255
    auto& scaled_sum_final = builder.add_access(flush_block, scaled_sum_temp, block.debug_info());
8✔
256
    builder.add_computational_memlet(
8✔
257
        flush_block, scale_sum_tasklet, "_out", scaled_sum_final, {}, scalar_type, block.debug_info()
8✔
258
    );
8✔
259

260
    auto& scale_input_tasklet =
8✔
261
        builder.add_tasklet(flush_block, data_flow::TaskletCode::fp_mul, "_out", {"_in1", "_in2"}, block.debug_info());
8✔
262
    builder.add_computational_memlet(
8✔
263
        flush_block, input_node_c_new, scale_input_tasklet, "_in1", {c_idx}, iedge_c->base_type(), iedge_c->debug_info()
8✔
264
    );
8✔
265
    auto& beta_node = standalone->add_scalar_input_access(flush_block, BETA_INPUT_IDX);
8✔
266
    builder.add_computational_memlet(flush_block, beta_node, scale_input_tasklet, "_in2", {}, block.debug_info());
8✔
267

268
    std::string scaled_input_temp = builder.find_new_name("scaled_input_temp");
8✔
269
    builder.add_container(scaled_input_temp, scalar_type);
8✔
270
    auto& scaled_input_c = builder.add_access(flush_block, scaled_input_temp, block.debug_info());
8✔
271
    builder.add_computational_memlet(
8✔
272
        flush_block, scale_input_tasklet, "_out", scaled_input_c, {}, scalar_type, block.debug_info()
8✔
273
    );
8✔
274

275
    auto& flush_add_tasklet =
8✔
276
        builder.add_tasklet(flush_block, data_flow::TaskletCode::fp_add, "_out", {"_in1", "_in2"}, block.debug_info());
8✔
277
    auto& output_node_new = standalone->add_indirect_write_access(flush_block, C_INPUT_IDX);
8✔
278
    builder.add_computational_memlet(
8✔
279
        flush_block, scaled_sum_final, flush_add_tasklet, "_in1", {}, scalar_type, block.debug_info()
8✔
280
    );
8✔
281
    builder.add_computational_memlet(
8✔
282
        flush_block, scaled_input_c, flush_add_tasklet, "_in2", {}, scalar_type, block.debug_info()
8✔
283
    );
8✔
284
    builder.add_computational_memlet(
8✔
285
        flush_block, flush_add_tasklet, "_out", output_node_new, {c_idx}, iedge_c->base_type(), iedge_c->debug_info()
8✔
286
    );
8✔
287

288
    return standalone->successfully_expanded();
8✔
289
}
8✔
290

291
symbolic::Expression GEMMNode::flop() const {
×
292
    return flops(symbolic::__true__(), symbolic::__true__(), symbolic::__true__(), symbolic::__true__());
×
293
}
×
294

295
symbolic::Expression GEMMNode::flops(
296
    symbolic::Condition alpha_non_zero,
297
    symbolic::Condition alpha_non_ident,
298
    symbolic::Condition beta_non_zero,
299
    symbolic::Condition beta_non_ident
300
) const {
×
301
    auto res_elems = symbolic::mul(this->m_, this->n_);
×
302

303
    // conditional on alpha != 0.0
304
    auto mm_mul_ops = symbolic::mul(symbolic::mul(res_elems, this->k_), alpha_non_zero);
×
305
    auto mm_sum_ops = symbolic::mul(symbolic::mul(res_elems, symbolic::sub(this->k_, symbolic::one())), alpha_non_zero);
×
306
    // conditional on alpha != 1.0 && alpha != 0.0
307
    auto mm_alpha_scale_ops = symbolic::mul(res_elems, symbolic::And(alpha_non_ident, alpha_non_zero));
×
308
    // conditional on beta != 1.0 && beta != 0.0
309
    auto mm_beta_scale_ops = symbolic::mul(res_elems, symbolic::And(beta_non_ident, beta_non_zero));
×
310
    auto mm_beta_scaled_sum_ops = symbolic::mul(res_elems, beta_non_zero);
×
311
    auto mul_ops = symbolic::add(mm_mul_ops, symbolic::add(mm_alpha_scale_ops, mm_beta_scale_ops));
×
312
    auto add_ops = symbolic::add(mm_sum_ops, mm_beta_scaled_sum_ops);
×
313
    return symbolic::add(mul_ops, add_ops);
×
314
}
×
315

316
std::unique_ptr<data_flow::DataFlowNode> GEMMNode::
317
    clone(size_t element_id, const graph::Vertex vertex, data_flow::DataFlowGraph& parent) const {
×
318
    auto node_clone = std::unique_ptr<GEMMNode>(new GEMMNode(
×
319
        element_id,
×
320
        this->debug_info(),
×
321
        vertex,
×
322
        parent,
×
323
        this->implementation_type_,
×
324
        this->precision_,
×
325
        this->layout_,
×
326
        this->trans_a_,
×
327
        this->trans_b_,
×
328
        this->m_,
×
329
        this->n_,
×
330
        this->k_,
×
331
        this->lda_,
×
332
        this->ldb_,
×
333
        this->ldc_
×
334
    ));
×
335
    return std::move(node_clone);
×
336
}
×
337

338
std::string GEMMNode::toStr() const {
×
339
    return LibraryNode::toStr() + "(" + static_cast<char>(precision_) + ", " +
×
340
           std::string(BLAS_Layout_to_short_string(layout_)) + ", " + BLAS_Transpose_to_char(trans_a_) +
×
341
           BLAS_Transpose_to_char(trans_b_) + ", " + m_->__str__() + ", " + n_->__str__() + ", " + k_->__str__() +
×
342
           ", " + lda_->__str__() + ", " + ldb_->__str__() + ", " + ldc_->__str__() + ")";
×
343
}
×
344

345
symbolic::Expression GEMMNode::calc_matrix_access_range(
346
    const symbolic::Expression& outer_dim,
347
    const symbolic::Expression& inner_dim,
348
    const symbolic::Expression& line_size,
349
    BLAS_Transpose trans,
350
    BLAS_Layout layout
351
) {
×
352
    if ((trans == BLAS_Transpose::No) ^ (layout == BLAS_Layout::ColMajor)) {
×
353
        return symbolic::mul(outer_dim, line_size);
×
354
    } else {
×
355
        return symbolic::mul(inner_dim, line_size);
×
356
    }
×
357
}
×
358

359

360
data_flow::PointerAccessType GEMMNode::pointer_access_type(int input_idx) const {
×
361
    if (input_idx == 0) { // A: m x k
×
362
        return data_flow::PointerAccessMeta::
×
363
            create_read_only(calc_matrix_access_range(m_, k_, lda_, trans_a_, layout_), true);
×
364
    } else if (input_idx == 1) { // B: k x n
×
365
        return data_flow::PointerAccessMeta::
×
366
            create_read_only(calc_matrix_access_range(k_, n_, ldb_, trans_b_, layout_), true);
×
367
    } else if (input_idx == 2) {
×
368
        // for beta == 0, there would no reads of C. But we currently have no mechanism to access const-prop knowledge
369
        // like tha
370
        if (symbolic::eq(ldc_, n_)) { // non-sparse access over the m x n range
×
371
            return data_flow::PointerAccessMeta::
×
372
                create_full_write_only(calc_matrix_access_range(m_, n_, ldc_, BLAS_Transpose::No, layout_), true);
×
373
        } else {
×
374
            // sparse access. But with only Convex Pattern for now, we cannot represent which values are
375
            auto pattern =
×
376
                data_flow::ConvexAccessPattern::create(calc_matrix_access_range(m_, n_, ldc_, BLAS_Transpose::No, layout_)
×
377
                );
×
378
            // full-overwritten and which are DC.
379
            return data_flow::PointerAccessMeta::create_generic(pattern->ref(), std::move(pattern), true);
×
380
        }
×
381
    } else {
×
382
        return LibraryNode::pointer_access_type(input_idx);
×
383
    }
×
384
}
×
385

386
nlohmann::json GEMMNodeSerializer::serialize(const data_flow::LibraryNode& library_node) {
×
387
    const GEMMNode& gemm_node = static_cast<const GEMMNode&>(library_node);
×
388
    nlohmann::json j;
×
389

390
    serializer::JSONSerializer serializer;
×
391
    j["code"] = gemm_node.code().value();
×
392
    j["precision"] = gemm_node.precision();
×
393
    j["layout"] = gemm_node.layout();
×
394
    j["trans_a"] = gemm_node.trans_a();
×
395
    j["trans_b"] = gemm_node.trans_b();
×
396
    j["m"] = serializer.expression(gemm_node.m());
×
397
    j["n"] = serializer.expression(gemm_node.n());
×
398
    j["k"] = serializer.expression(gemm_node.k());
×
399
    j["lda"] = serializer.expression(gemm_node.lda());
×
400
    j["ldb"] = serializer.expression(gemm_node.ldb());
×
401
    j["ldc"] = serializer.expression(gemm_node.ldc());
×
402

403
    return j;
×
404
}
×
405

406
data_flow::LibraryNode& GEMMNodeSerializer::deserialize(
407
    const nlohmann::json& j, builder::StructuredSDFGBuilder& builder, structured_control_flow::Block& parent
408
) {
×
409
    // Assertions for required fields
410
    assert(j.contains("element_id"));
×
411
    assert(j.contains("code"));
×
412
    assert(j.contains("debug_info"));
×
413

414
    auto code = j["code"].get<std::string>();
×
415
    if (code != LibraryNodeType_GEMM.value()) {
×
416
        throw std::runtime_error("Invalid library node code");
×
417
    }
×
418

419
    // Extract debug info using JSONSerializer
420
    sdfg::serializer::JSONSerializer serializer;
×
421
    DebugInfo debug_info = serializer.json_to_debug_info(j["debug_info"]);
×
422

423
    auto precision = j.at("precision").get<BLAS_Precision>();
×
424
    auto layout = j.at("layout").get<BLAS_Layout>();
×
425
    auto trans_a = j.at("trans_a").get<BLAS_Transpose>();
×
426
    auto trans_b = j.at("trans_b").get<BLAS_Transpose>();
×
427
    auto m = symbolic::parse(j.at("m"));
×
428
    auto n = symbolic::parse(j.at("n"));
×
429
    auto k = symbolic::parse(j.at("k"));
×
430
    auto lda = symbolic::parse(j.at("lda"));
×
431
    auto ldb = symbolic::parse(j.at("ldb"));
×
432
    auto ldc = symbolic::parse(j.at("ldc"));
×
433

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

436
    return builder.add_library_node<
×
437
        GEMMNode>(parent, debug_info, implementation_type, precision, layout, trans_a, trans_b, m, n, k, lda, ldb, ldc);
×
438
}
×
439

440
GEMMNodeDispatcher_BLAS::GEMMNodeDispatcher_BLAS(
441
    codegen::LanguageExtension& language_extension,
442
    const Function& function,
443
    const data_flow::DataFlowGraph& data_flow_graph,
444
    const GEMMNode& node
445
)
446
    : codegen::LibraryNodeDispatcher(language_extension, function, data_flow_graph, node) {}
×
447

448
void GEMMNodeDispatcher_BLAS::dispatch_code_with_edges(
449
    codegen::CodegenOutput& out,
450
    std::vector<codegen::DispatchInput>& inputs,
451
    std::vector<codegen::DispatchOutput>& outputs
452
) {
×
453
    auto& gemm_node = static_cast<const GEMMNode&>(this->node_);
×
454

455
    sdfg::types::Scalar base_type(types::PrimitiveType::Void);
×
456
    switch (gemm_node.precision()) {
×
457
        case BLAS_Precision::h:
×
458
            base_type = types::Scalar(types::PrimitiveType::Half);
×
459
            break;
×
460
        case BLAS_Precision::s:
×
461
            base_type = types::Scalar(types::PrimitiveType::Float);
×
462
            break;
×
463
        case BLAS_Precision::d:
×
464
            base_type = types::Scalar(types::PrimitiveType::Double);
×
465
            break;
×
466
        default:
×
467
            throw std::runtime_error("Invalid BLAS_Precision value");
×
468
    }
×
469

470
    out.library_snippet_factory.require_dependency(BLASLibDependency::instance());
×
471

472
    out.stream << "cblas_" << BLAS_Precision_to_string(gemm_node.precision()) << "gemm(";
×
473
    out.stream.changeIndent(+4);
×
474
    out.stream << BLAS_Layout_to_string(gemm_node.layout());
×
475
    out.stream << ", ";
×
476
    out.stream << BLAS_Transpose_to_string(gemm_node.trans_a());
×
477
    out.stream << ", ";
×
478
    out.stream << BLAS_Transpose_to_string(gemm_node.trans_b());
×
479
    out.stream << ", ";
×
480
    out.stream << this->language_extension_.expression(gemm_node.m());
×
481
    out.stream << ", ";
×
482
    out.stream << this->language_extension_.expression(gemm_node.n());
×
483
    out.stream << ", ";
×
484
    out.stream << this->language_extension_.expression(gemm_node.k());
×
485
    out.stream << ", ";
×
486
    out.stream << inputs.at(GEMMNode::ALPHA_INPUT_IDX).expr;
×
487
    out.stream << ", ";
×
488
    out.stream << inputs.at(GEMMNode::A_INPUT_IDX).expr;
×
489
    out.stream << ", ";
×
490
    out.stream << this->language_extension_.expression(gemm_node.lda());
×
491
    out.stream << ", ";
×
492
    out.stream << inputs.at(GEMMNode::B_INPUT_IDX).expr;
×
493
    out.stream << ", ";
×
494
    out.stream << this->language_extension_.expression(gemm_node.ldb());
×
495
    out.stream << ", ";
×
496
    out.stream << inputs.at(GEMMNode::BETA_INPUT_IDX).expr;
×
497
    out.stream << ", ";
×
498
    out.stream << inputs.at(GEMMNode::C_INPUT_IDX).expr;
×
499
    out.stream << ", ";
×
500
    out.stream << this->language_extension_.expression(gemm_node.ldc());
×
501

502
    out.stream.changeIndent(-4);
×
503
    out.stream << ");" << std::endl;
×
504
}
×
505

506
GEMMNode& add_gemm_node(
507
    builder::StructuredSDFGBuilder& builder,
508
    Block& block,
509
    const std::string& ptr_a,
510
    const std::string& ptr_b,
511
    const std::string& ptr_c,
512
    data_flow::AccessNode& alpha_node,
513
    data_flow::AccessNode& beta_node,
514
    const BLAS_Precision& precision,
515
    const BLAS_Layout& layout,
516
    const BLAS_Transpose& trans_a,
517
    const BLAS_Transpose& trans_b,
518
    symbolic::Expression& m,
519
    symbolic::Expression& n,
520
    symbolic::Expression& k,
521
    symbolic::Expression& lda,
522
    symbolic::Expression& ldb,
523
    symbolic::Expression& ldc,
524
    const types::IType& a_type,
525
    const types::IType& b_type,
526
    const types::IType& c_type,
527
    const types::IType& factor_type,
528
    DebugInfo debug_info,
529
    DebugInfo a_access_deb_info,
530
    DebugInfo b_access_deb_info,
531
    DebugInfo c_access_deb_info,
532
    DebugInfo a_edge_deb_info,
533
    DebugInfo b_edge_deb_info,
534
    DebugInfo c_edge_deb_info,
535
    data_flow::ImplementationType impl_type
536
) {
6✔
537
    auto& gemm_node = builder.add_library_node<sdfg::math::blas::GEMMNode>(
6✔
538
        block, debug_info, std::move(impl_type), precision, layout, trans_a, trans_b, m, n, k, lda, ldb, ldc
6✔
539
    );
6✔
540

541
    // Add access nodes
542
    auto& a_node_in = builder.add_access(block, ptr_a, a_access_deb_info);
6✔
543
    auto& b_node_in = builder.add_access(block, ptr_b, b_access_deb_info);
6✔
544
    auto& c_node_in = builder.add_access(block, ptr_c, c_access_deb_info);
6✔
545

546
    // Add edges
547
    builder.add_computational_memlet(block, a_node_in, gemm_node, "__A", {}, a_type, a_edge_deb_info);
6✔
548
    builder.add_computational_memlet(block, b_node_in, gemm_node, "__B", {}, b_type, b_edge_deb_info);
6✔
549
    builder.add_computational_memlet(block, c_node_in, gemm_node, "__C", {}, c_type, c_edge_deb_info);
6✔
550
    builder.add_computational_memlet(block, alpha_node, gemm_node, "__alpha", {}, factor_type, debug_info);
6✔
551
    builder.add_computational_memlet(block, beta_node, gemm_node, "__beta", {}, factor_type, debug_info);
6✔
552

553
    return static_cast<GEMMNode&>(gemm_node);
6✔
554
}
6✔
555

556
GEMMNode& add_gemm_node(
557
    builder::StructuredSDFGBuilder& builder,
558
    Block& block,
559
    const std::string& ptr_a,
560
    const std::string& ptr_b,
561
    const std::string& ptr_c,
562
    data_flow::AccessNode& alpha_node,
563
    data_flow::AccessNode& beta_node,
564
    const BLAS_Precision& precision,
565
    const BLAS_Layout& layout,
566
    const BLAS_Transpose& trans_a,
567
    const BLAS_Transpose& trans_b,
568
    symbolic::Expression& m,
569
    symbolic::Expression& n,
570
    symbolic::Expression& k,
571
    symbolic::Expression& lda,
572
    symbolic::Expression& ldb,
573
    symbolic::Expression& ldc,
574
    const types::IType& ptr_type,
575
    const types::IType& factor_type,
576
    DebugInfo debug_info,
577
    data_flow::ImplementationType impl_type
578
) {
×
579
    return add_gemm_node(
×
580
        builder,
×
581
        block,
×
582
        ptr_a,
×
583
        ptr_b,
×
584
        ptr_c,
×
585
        alpha_node,
×
586
        beta_node,
×
587
        precision,
×
588
        layout,
×
589
        trans_a,
×
590
        trans_b,
×
591
        m,
×
592
        n,
×
593
        k,
×
594
        lda,
×
595
        ldb,
×
596
        ldc,
×
597
        ptr_type,
×
598
        ptr_type,
×
599
        ptr_type,
×
600
        factor_type,
×
601
        debug_info,
×
602
        debug_info,
×
603
        debug_info,
×
604
        debug_info,
×
605
        debug_info,
×
606
        debug_info,
×
607
        debug_info,
×
608
        impl_type
×
609
    );
×
610
}
×
611

612
} // namespace blas
613
} // namespace math
614
} // 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