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

daisytuner / sdfglib / 18318341707

07 Oct 2025 03:48PM UTC coverage: 61.593% (+0.5%) from 61.066%
18318341707

push

github

web-flow
Merge pull request #262 from daisytuner/tasklets

Tasklets, Intrinsics and Library Nodes

84 of 418 new or added lines in 29 files covered. (20.1%)

153 existing lines in 17 files now uncovered.

8979 of 14578 relevant lines covered (61.59%)

103.69 hits per line

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

31.43
/src/codegen/language_extensions/cpp_language_extension.cpp
1
#include "sdfg/codegen/language_extensions/cpp_language_extension.h"
2

3
#include "sdfg/codegen/utils.h"
4
#include "sdfg/data_flow/tasklet.h"
5

6
namespace sdfg {
7
namespace codegen {
8

9
std::string CPPLanguageExtension::primitive_type(const types::PrimitiveType prim_type) {
22✔
10
    switch (prim_type) {
22✔
11
        case types::PrimitiveType::Void:
12
            return "void";
2✔
13
        case types::PrimitiveType::Bool:
14
            return "bool";
1✔
15
        case types::PrimitiveType::Int8:
16
            return "signed char";
1✔
17
        case types::PrimitiveType::Int16:
18
            return "short";
1✔
19
        case types::PrimitiveType::Int32:
20
            return "int";
6✔
21
        case types::PrimitiveType::Int64:
22
            return "long long";
1✔
23
        case types::PrimitiveType::Int128:
24
            return "__int128";
×
25
        case types::PrimitiveType::UInt8:
26
            return "char";
4✔
27
        case types::PrimitiveType::UInt16:
28
            return "unsigned short";
1✔
29
        case types::PrimitiveType::UInt32:
30
            return "unsigned int";
1✔
31
        case types::PrimitiveType::UInt64:
32
            return "unsigned long long";
1✔
33
        case types::PrimitiveType::UInt128:
34
            return "unsigned __int128";
×
35
        case types::PrimitiveType::Half:
36
            return "__fp16";
×
37
        case types::PrimitiveType::BFloat:
38
            return "__bf16";
×
39
        case types::PrimitiveType::Float:
40
            return "float";
2✔
41
        case types::PrimitiveType::Double:
42
            return "double";
1✔
43
        case types::PrimitiveType::X86_FP80:
44
            return "long double";
×
45
        case types::PrimitiveType::FP128:
46
            return "__float128";
×
47
        case types::PrimitiveType::PPC_FP128:
48
            return "__float128";
×
49
    }
50

51
    throw std::runtime_error("Unknown primitive type");
×
52
};
22✔
53

54
std::string CPPLanguageExtension::
55
    declaration(const std::string& name, const types::IType& type, bool use_initializer, bool use_alignment) {
21✔
56
    std::stringstream val;
21✔
57

58
    if (auto scalar_type = dynamic_cast<const types::Scalar*>(&type)) {
21✔
59
        val << primitive_type(scalar_type->primitive_type());
10✔
60
        val << " ";
10✔
61
        val << name;
10✔
62
    } else if (auto array_type = dynamic_cast<const types::Array*>(&type)) {
21✔
63
        auto& element_type = array_type->element_type();
3✔
64
        val << declaration(name + "[" + this->expression(array_type->num_elements()) + "]", element_type);
3✔
65
    } else if (auto pointer_type = dynamic_cast<const types::Pointer*>(&type)) {
11✔
66
        if (pointer_type->has_pointee_type()) {
4✔
67
            const types::IType& pointee = pointer_type->pointee_type();
3✔
68

69
            const bool pointee_is_function_or_array = dynamic_cast<const types::Function*>(&pointee) ||
6✔
70
                                                      dynamic_cast<const types::Array*>(&pointee);
3✔
71

72
            // Parenthesise *only* when it is needed to bind tighter than [] or ()
73
            std::string decorated = pointee_is_function_or_array ? "(*" + name + ")" : "*" + name;
3✔
74

75
            val << declaration(decorated, pointee);
3✔
76
        } else {
3✔
77
            val << "void*";
1✔
78
            val << " " << name;
1✔
79
        }
80
    } else if (auto ref_type = dynamic_cast<const Reference*>(&type)) {
8✔
81
        val << declaration("&" + name, ref_type->reference_type());
×
82
    } else if (auto structure_type = dynamic_cast<const types::Structure*>(&type)) {
4✔
83
        val << structure_type->name();
4✔
84
        val << " ";
4✔
85
        val << name;
4✔
86
    } else if (auto function_type = dynamic_cast<const types::Function*>(&type)) {
4✔
87
        std::stringstream params;
×
88
        for (size_t i = 0; i < function_type->num_params(); ++i) {
×
89
            params << declaration("", function_type->param_type(symbolic::integer(i)));
×
90
            if (i + 1 < function_type->num_params()) params << ", ";
×
91
        }
×
92
        if (function_type->is_var_arg()) {
×
93
            // ISO C++ forbids empty parameter lists before ...
94
            if (function_type->num_params() > 0) {
×
95
                params << ", ";
×
96
                params << "...";
×
97
            }
×
98
        }
×
99

100
        const std::string fun_name = name + "(" + params.str() + ")";
×
101
        val << declaration(fun_name, function_type->return_type());
×
102
    } else {
×
103
        throw std::runtime_error("Unknown declaration type");
×
104
    }
105

106
    if (use_alignment && type.alignment() > 0) {
21✔
107
        val << " __attribute__((aligned(" << type.alignment() << ")))";
×
108
    }
×
109
    if (use_initializer && !type.initializer().empty()) {
21✔
110
        val << " = " << type.initializer();
×
111
    }
×
112

113
    return val.str();
21✔
114
};
21✔
115

116
std::string CPPLanguageExtension::type_cast(const std::string& name, const types::IType& type) {
1✔
117
    std::stringstream val;
1✔
118

119
    val << "reinterpret_cast";
1✔
120
    val << "<";
1✔
121
    val << declaration("", type);
1✔
122
    val << ">";
1✔
123
    val << "(" << name << ")";
1✔
124

125
    return val.str();
1✔
126
};
1✔
127

128
std::string CPPLanguageExtension::subset(const Function& function, const types::IType& type, const data_flow::Subset& sub) {
3✔
129
    if (sub.empty()) {
3✔
130
        return "";
1✔
131
    }
132

133
    if (dynamic_cast<const types::Scalar*>(&type)) {
2✔
134
        return "";
×
135
    } else if (auto array_type = dynamic_cast<const types::Array*>(&type)) {
2✔
136
        std::string subset_str = "[" + this->expression(sub.at(0)) + "]";
1✔
137

138
        if (sub.size() > 1) {
1✔
139
            data_flow::Subset element_subset(sub.begin() + 1, sub.end());
×
140
            auto& element_type = array_type->element_type();
×
141
            return subset_str + subset(function, element_type, element_subset);
×
142
        } else {
×
143
            return subset_str;
1✔
144
        }
145
    } else if (auto pointer_type = dynamic_cast<const types::Pointer*>(&type)) {
2✔
146
        std::string subset_str = "[" + this->expression(sub.at(0)) + "]";
×
147

148
        data_flow::Subset element_subset(sub.begin() + 1, sub.end());
×
149
        auto& pointee_type = pointer_type->pointee_type();
×
150
        return subset_str + subset(function, pointee_type, element_subset);
×
151
    } else if (auto structure_type = dynamic_cast<const types::Structure*>(&type)) {
1✔
152
        auto& definition = function.structure(structure_type->name());
1✔
153

154
        std::string subset_str = ".member_" + this->expression(sub.at(0));
1✔
155
        if (sub.size() > 1) {
1✔
156
            auto member = SymEngine::rcp_dynamic_cast<const SymEngine::Integer>(sub.at(0));
×
157
            auto& member_type = definition.member_type(member);
×
158
            data_flow::Subset element_subset(sub.begin() + 1, sub.end());
×
159
            return subset_str + subset(function, member_type, element_subset);
×
160
        } else {
×
161
            return subset_str;
1✔
162
        }
163
    }
1✔
164

165
    throw std::invalid_argument("Invalid subset type");
×
166
};
3✔
167

168
std::string CPPLanguageExtension::expression(const symbolic::Expression expr) {
9✔
169
    CPPSymbolicPrinter printer(this->external_variables_);
9✔
170
    return printer.apply(expr);
9✔
171
};
9✔
172

173
std::string CPPLanguageExtension::access_node(const data_flow::AccessNode& node) {
×
174
    if (dynamic_cast<const data_flow::ConstantNode*>(&node)) {
×
175
        std::string name = node.data();
×
176
        if (symbolic::is_nullptr(symbolic::symbol(name))) {
×
177
            return this->expression(symbolic::__nullptr__());
×
178
        }
179
        return name;
×
180
    } else {
×
181
        std::string name = node.data();
×
182
        if (this->external_variables_.find(name) != this->external_variables_.end()) {
×
183
            return "(&" + name + ")";
×
184
        }
185
        return name;
×
186
    }
×
187
};
×
188

189
std::string CPPLanguageExtension::tasklet(const data_flow::Tasklet& tasklet) {
×
NEW
190
    switch (tasklet.code()) {
×
191
        case data_flow::TaskletCode::assign:
NEW
192
            return tasklet.inputs().at(0);
×
193
        case data_flow::TaskletCode::fp_neg:
NEW
194
            return "-" + tasklet.inputs().at(0);
×
195
        case data_flow::TaskletCode::fp_add:
NEW
196
            return tasklet.inputs().at(0) + " + " + tasklet.inputs().at(1);
×
197
        case data_flow::TaskletCode::fp_sub:
NEW
198
            return tasklet.inputs().at(0) + " - " + tasklet.inputs().at(1);
×
199
        case data_flow::TaskletCode::fp_mul:
NEW
200
            return tasklet.inputs().at(0) + " * " + tasklet.inputs().at(1);
×
201
        case data_flow::TaskletCode::fp_div:
NEW
202
            return tasklet.inputs().at(0) + " / " + tasklet.inputs().at(1);
×
203
        case data_flow::TaskletCode::fp_rem:
NEW
204
            return "remainder(" + tasklet.inputs().at(0) + ", " + tasklet.inputs().at(1) + ")";
×
205
        case data_flow::TaskletCode::fp_fma:
NEW
206
            return tasklet.inputs().at(0) + " * " + tasklet.inputs().at(1) + " + " + tasklet.inputs().at(2);
×
207
        case data_flow::TaskletCode::fp_oeq:
NEW
208
            return tasklet.inputs().at(0) + " == " + tasklet.inputs().at(1);
×
209
        case data_flow::TaskletCode::fp_one:
NEW
210
            return tasklet.inputs().at(0) + " != " + tasklet.inputs().at(1);
×
211
        case data_flow::TaskletCode::fp_ogt:
NEW
212
            return tasklet.inputs().at(0) + " > " + tasklet.inputs().at(1);
×
213
        case data_flow::TaskletCode::fp_oge:
NEW
214
            return tasklet.inputs().at(0) + " >= " + tasklet.inputs().at(1);
×
215
        case data_flow::TaskletCode::fp_olt:
NEW
216
            return tasklet.inputs().at(0) + " < " + tasklet.inputs().at(1);
×
217
        case data_flow::TaskletCode::fp_ole:
NEW
218
            return tasklet.inputs().at(0) + " <= " + tasklet.inputs().at(1);
×
219
        case data_flow::TaskletCode::fp_ord:
NEW
220
            return "isnan(" + tasklet.inputs().at(0) + ") && isnan(" + tasklet.inputs().at(1) + ")";
×
221
        case data_flow::TaskletCode::fp_ueq:
NEW
222
            return "isnan(" + tasklet.inputs().at(0) + ") || isnan(" + tasklet.inputs().at(1) + ")" + " || " +
×
NEW
223
                   tasklet.inputs().at(0) + " == " + tasklet.inputs().at(1);
×
224
        case data_flow::TaskletCode::fp_une:
NEW
225
            return "isnan(" + tasklet.inputs().at(0) + ") || isnan(" + tasklet.inputs().at(1) + ")" + " || " +
×
NEW
226
                   tasklet.inputs().at(0) + " != " + tasklet.inputs().at(1);
×
227
        case data_flow::TaskletCode::fp_ugt:
NEW
228
            return "isnan(" + tasklet.inputs().at(0) + ") || isnan(" + tasklet.inputs().at(1) + ")" + " || " +
×
NEW
229
                   tasklet.inputs().at(0) + " > " + tasklet.inputs().at(1);
×
230
        case data_flow::TaskletCode::fp_uge:
NEW
231
            return "isnan(" + tasklet.inputs().at(0) + ") || isnan(" + tasklet.inputs().at(1) + ")" + " || " +
×
NEW
232
                   tasklet.inputs().at(0) + " >= " + tasklet.inputs().at(1);
×
233
        case data_flow::TaskletCode::fp_ult:
NEW
234
            return "isnan(" + tasklet.inputs().at(0) + ") || isnan(" + tasklet.inputs().at(1) + ")" + " || " +
×
NEW
235
                   tasklet.inputs().at(0) + " < " + tasklet.inputs().at(1);
×
236
        case data_flow::TaskletCode::fp_ule:
NEW
237
            return "isnan(" + tasklet.inputs().at(0) + ") || isnan(" + tasklet.inputs().at(1) + ")" + " || " +
×
NEW
238
                   tasklet.inputs().at(0) + " <= " + tasklet.inputs().at(1);
×
239
        case data_flow::TaskletCode::fp_uno:
NEW
240
            return "isnan(" + tasklet.inputs().at(0) + ") || isnan(" + tasklet.inputs().at(1) + ")";
×
241
        case data_flow::TaskletCode::int_add:
NEW
242
            return tasklet.inputs().at(0) + " + " + tasklet.inputs().at(1);
×
243
        case data_flow::TaskletCode::int_sub:
NEW
244
            return tasklet.inputs().at(0) + " - " + tasklet.inputs().at(1);
×
245
        case data_flow::TaskletCode::int_mul:
NEW
246
            return tasklet.inputs().at(0) + " * " + tasklet.inputs().at(1);
×
247
        case data_flow::TaskletCode::int_sdiv:
NEW
248
            return tasklet.inputs().at(0) + " / " + tasklet.inputs().at(1);
×
249
        case data_flow::TaskletCode::int_srem:
NEW
250
            return tasklet.inputs().at(0) + " % " + tasklet.inputs().at(1);
×
251
        case data_flow::TaskletCode::int_udiv:
NEW
252
            return tasklet.inputs().at(0) + " / " + tasklet.inputs().at(1);
×
253
        case data_flow::TaskletCode::int_urem:
NEW
254
            return tasklet.inputs().at(0) + " % " + tasklet.inputs().at(1);
×
255
        case data_flow::TaskletCode::int_and:
NEW
256
            return tasklet.inputs().at(0) + " & " + tasklet.inputs().at(1);
×
257
        case data_flow::TaskletCode::int_or:
NEW
258
            return tasklet.inputs().at(0) + " | " + tasklet.inputs().at(1);
×
259
        case data_flow::TaskletCode::int_xor:
NEW
260
            return tasklet.inputs().at(0) + " ^ " + tasklet.inputs().at(1);
×
261
        case data_flow::TaskletCode::int_shl:
NEW
262
            return tasklet.inputs().at(0) + " << " + tasklet.inputs().at(1);
×
263
        case data_flow::TaskletCode::int_lshr:
NEW
264
            return tasklet.inputs().at(0) + " >> " + tasklet.inputs().at(1);
×
265
        case data_flow::TaskletCode::int_ashr:
NEW
266
            return tasklet.inputs().at(0) + " >> " + tasklet.inputs().at(1);
×
267
        case data_flow::TaskletCode::int_eq:
NEW
268
            return tasklet.inputs().at(0) + " == " + tasklet.inputs().at(1);
×
269
        case data_flow::TaskletCode::int_ne:
NEW
270
            return tasklet.inputs().at(0) + " != " + tasklet.inputs().at(1);
×
271
        case data_flow::TaskletCode::int_sgt:
NEW
272
            return tasklet.inputs().at(0) + " > " + tasklet.inputs().at(1);
×
273
        case data_flow::TaskletCode::int_sge:
NEW
274
            return tasklet.inputs().at(0) + " >= " + tasklet.inputs().at(1);
×
275
        case data_flow::TaskletCode::int_slt:
NEW
276
            return tasklet.inputs().at(0) + " < " + tasklet.inputs().at(1);
×
277
        case data_flow::TaskletCode::int_sle:
NEW
278
            return tasklet.inputs().at(0) + " <= " + tasklet.inputs().at(1);
×
279
        case data_flow::TaskletCode::int_ugt:
NEW
280
            return tasklet.inputs().at(0) + " > " + tasklet.inputs().at(1);
×
281
        case data_flow::TaskletCode::int_uge:
NEW
282
            return tasklet.inputs().at(0) + " >= " + tasklet.inputs().at(1);
×
283
        case data_flow::TaskletCode::int_ult:
NEW
284
            return tasklet.inputs().at(0) + " < " + tasklet.inputs().at(1);
×
285
        case data_flow::TaskletCode::int_ule:
NEW
286
            return tasklet.inputs().at(0) + " <= " + tasklet.inputs().at(1);
×
287
    };
NEW
288
    throw std::invalid_argument("Invalid tasklet code");
×
UNCOV
289
};
×
290

291
std::string CPPLanguageExtension::zero(const types::PrimitiveType prim_type) {
×
292
    switch (prim_type) {
×
293
        case types::Void:
294
            throw InvalidSDFGException("No zero for void type possible");
×
295
        case types::Bool:
296
            return "false";
×
297
        case types::Int8:
298
            return "0";
×
299
        case types::Int16:
300
            return "0";
×
301
        case types::Int32:
302
            return "0";
×
303
        case types::Int64:
304
            return "0ll";
×
305
        case types::Int128:
306
            return "0";
×
307
        case types::UInt8:
308
            return "0u";
×
309
        case types::UInt16:
310
            return "0u";
×
311
        case types::UInt32:
312
            return "0u";
×
313
        case types::UInt64:
314
            return "0ull";
×
315
        case types::UInt128:
316
            return "0u";
×
317
        case types::Half:
318
            throw InvalidSDFGException("Currently unsupported");
×
319
        case types::BFloat:
320
            throw InvalidSDFGException("Currently unsupported");
×
321
        case types::Float:
322
            return "0.0f";
×
323
        case types::Double:
324
            return "0.0";
×
325
        case types::X86_FP80:
326
            return "0.0l";
×
327
        case types::FP128:
328
            throw InvalidSDFGException("Currently unsupported");
×
329
        case types::PPC_FP128:
330
            throw InvalidSDFGException("Currently unsupported");
×
331
    }
×
332
}
×
333

334
void CPPSymbolicPrinter::bvisit(const SymEngine::Infty& x) {
×
335
    if (x.is_negative_infinity())
×
336
        str_ = "-INFINITY";
×
337
    else if (x.is_positive_infinity())
×
338
        str_ = "INFINITY";
×
339
};
×
340

341
void CPPSymbolicPrinter::bvisit(const SymEngine::BooleanAtom& x) { str_ = x.get_val() ? "true" : "false"; };
×
342

343
void CPPSymbolicPrinter::bvisit(const SymEngine::Symbol& x) {
5✔
344
    if (symbolic::is_nullptr(symbolic::symbol(x.get_name()))) {
5✔
345
        str_ = "nullptr";
×
346
        return;
×
347
    }
348
    std::string name = x.get_name();
5✔
349
    if (this->external_variables_.find(name) != this->external_variables_.end()) {
5✔
350
        name = "(&" + name + ")";
1✔
351
    }
1✔
352
    str_ = name;
5✔
353
};
5✔
354

355
void CPPSymbolicPrinter::bvisit(const SymEngine::And& x) {
×
356
    std::ostringstream s;
×
357
    auto container = x.get_container();
×
358
    s << apply(*container.begin());
×
359
    for (auto it = ++(container.begin()); it != container.end(); ++it) {
×
360
        s << " && " << apply(*it);
×
361
    }
×
362
    str_ = parenthesize(s.str());
×
363
};
×
364

365
void CPPSymbolicPrinter::bvisit(const SymEngine::Or& x) {
×
366
    std::ostringstream s;
×
367
    auto container = x.get_container();
×
368
    s << apply(*container.begin());
×
369
    for (auto it = ++(container.begin()); it != container.end(); ++it) {
×
370
        s << " || " << apply(*it);
×
371
    }
×
372
    str_ = parenthesize(s.str());
×
373
};
×
374

375
void CPPSymbolicPrinter::bvisit(const SymEngine::Not& x) {
×
376
    str_ = "!" + apply(x.get_arg());
×
377
    str_ = parenthesize(str_);
×
378
};
×
379

380
void CPPSymbolicPrinter::bvisit(const SymEngine::Equality& x) {
×
381
    str_ = apply(x.get_args()[0]) + " == " + apply(x.get_args()[1]);
×
382
    str_ = parenthesize(str_);
×
383
};
×
384

385
void CPPSymbolicPrinter::bvisit(const SymEngine::Unequality& x) {
×
386
    str_ = apply(x.get_args()[0]) + " != " + apply(x.get_args()[1]);
×
387
    str_ = parenthesize(str_);
×
388
};
×
389

390
void CPPSymbolicPrinter::bvisit(const SymEngine::Min& x) {
×
391
    std::ostringstream s;
×
392
    auto container = x.get_args();
×
393
    if (container.size() == 1) {
×
394
        s << apply(*container.begin());
×
395
    } else {
×
396
        s << "__daisy_min(";
×
397
        s << apply(*container.begin());
×
398

399
        // Recursively apply __daisy_min to the arguments
400
        SymEngine::vec_basic subargs;
×
401
        for (auto it = ++(container.begin()); it != container.end(); ++it) {
×
402
            subargs.push_back(*it);
×
403
        }
×
404
        auto submin = SymEngine::min(subargs);
×
405
        s << ", " << apply(submin);
×
406

407
        s << ")";
×
408
    }
×
409

410
    str_ = s.str();
×
411
};
×
412

413
void CPPSymbolicPrinter::bvisit(const SymEngine::Max& x) {
×
414
    std::ostringstream s;
×
415
    auto container = x.get_args();
×
416
    if (container.size() == 1) {
×
417
        s << apply(*container.begin());
×
418
    } else {
×
419
        s << "__daisy_max(";
×
420
        s << apply(*container.begin());
×
421

422
        // Recursively apply __daisy_max to the arguments
423
        SymEngine::vec_basic subargs;
×
424
        for (auto it = ++(container.begin()); it != container.end(); ++it) {
×
425
            subargs.push_back(*it);
×
426
        }
×
427
        auto submax = SymEngine::max(subargs);
×
428
        s << ", " << apply(submax);
×
429

430
        s << ")";
×
431
    }
×
432

433
    str_ = s.str();
×
434
};
×
435

436
void CPPSymbolicPrinter::bvisit(const SymEngine::FunctionSymbol& x) {
1✔
437
    if (x.get_name() == "idiv") {
1✔
438
        str_ = "((" + apply(x.get_args()[0]) + ") / (" + apply(x.get_args()[1]) + "))";
×
439
    } else if (x.get_name() == "imod") {
1✔
440
        str_ = "((" + apply(x.get_args()[0]) + ") % (" + apply(x.get_args()[1]) + "))";
×
441
    } else if (x.get_name() == "sizeof") {
1✔
442
        auto& so = dynamic_cast<const symbolic::SizeOfTypeFunction&>(x);
1✔
443
        auto& type = so.get_type();
1✔
444
        CPPLanguageExtension lang;
1✔
445
        str_ = "sizeof(" + lang.declaration("", type) + ")";
1✔
446
    } else {
1✔
447
        throw std::runtime_error("Unsupported function symbol: " + x.get_name());
×
448
    }
449
};
1✔
450

451
void CPPSymbolicPrinter::_print_pow(
2✔
452
    std::ostringstream& o,
453
    const SymEngine::RCP<const SymEngine::Basic>& a,
454
    const SymEngine::RCP<const SymEngine::Basic>& b
455
) {
456
    if (SymEngine::eq(*a, *SymEngine::E)) {
2✔
457
        o << "exp(" << apply(b) << ")";
×
458
    } else if (SymEngine::eq(*b, *SymEngine::rational(1, 2))) {
2✔
459
        o << "sqrt(" << apply(a) << ")";
×
460
    } else if (SymEngine::eq(*b, *SymEngine::rational(1, 3))) {
2✔
461
        o << "cbrt(" << apply(a) << ")";
×
462
    } else if (SymEngine::eq(*b, *SymEngine::integer(2))) {
2✔
463
        o << "((" + apply(a) + ") * (" + apply(a) + "))";
2✔
464
    } else {
2✔
465
        o << "pow(" << apply(a) << ", " << apply(b) << ")";
×
466
    }
467
};
2✔
468

469
} // namespace codegen
470
} // 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