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

daisytuner / sdfglib / 21491323166

29 Jan 2026 07:10PM UTC coverage: 66.308% (+0.5%) from 65.778%
21491323166

push

github

web-flow
Merge pull request #481 from daisytuner/refactor-cutout

Refactor cutout

96 of 160 new or added lines in 7 files covered. (60.0%)

4 existing lines in 3 files now uncovered.

22723 of 34269 relevant lines covered (66.31%)

382.43 hits per line

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

68.2
/sdfg/src/builder/sdfg_builder.cpp
1
#include "sdfg/builder/sdfg_builder.h"
2

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

6
namespace sdfg {
7
namespace builder {
8

9
Function& SDFGBuilder::function() const { return static_cast<Function&>(*this->sdfg_); };
1,434✔
10

11
SDFGBuilder::SDFGBuilder(std::unique_ptr<SDFG>& sdfg)
12
    : FunctionBuilder(), sdfg_(std::move(sdfg)) {
×
13

14
      };
×
15

16
SDFGBuilder::SDFGBuilder(const std::string& name, FunctionType type)
17
    : FunctionBuilder(), sdfg_(new SDFG(name, type)) {
227✔
18

19
      };
227✔
20

21
SDFGBuilder::SDFGBuilder(const std::string& name, FunctionType type, const types::IType& return_type)
22
    : FunctionBuilder(), sdfg_(new SDFG(name, type, return_type)) {
8✔
23

24
      };
8✔
25

26
SDFG& SDFGBuilder::subject() const { return *this->sdfg_; };
140✔
27

28
std::unique_ptr<SDFG> SDFGBuilder::move() { return std::move(this->sdfg_); };
20✔
29

30
void SDFGBuilder::rename_container(const std::string& old_name, const std::string& new_name) const {
×
31
    FunctionBuilder::rename_container(old_name, new_name);
×
32

33
    for (auto& entry : this->sdfg_->states_) {
×
34
        entry.second->replace(symbolic::symbol(old_name), symbolic::symbol(new_name));
×
35
    }
×
36
    for (auto& entry : this->sdfg_->edges_) {
×
37
        entry.second->replace(symbolic::symbol(old_name), symbolic::symbol(new_name));
×
38
    }
×
39
}
×
40

41
/***** Section: Control-Flow Graph *****/
42

43
control_flow::State& SDFGBuilder::add_state(bool is_start_state, const DebugInfo& debug_info) {
241✔
44
    auto vertex = boost::add_vertex(this->sdfg_->graph_);
241✔
45
    auto res = this->sdfg_->states_.insert(
241✔
46
        {vertex,
241✔
47
         std::unique_ptr<control_flow::State>(new control_flow::State(this->new_element_id(), debug_info, vertex))}
241✔
48
    );
241✔
49

50
    assert(res.second);
241✔
51
    (*res.first).second->dataflow_->parent_ = (*res.first).second.get();
241✔
52

53
    if (is_start_state) {
241✔
54
        this->sdfg_->start_state_ = (*res.first).second.get();
43✔
55
    }
43✔
56

57
    return *(*res.first).second;
241✔
58
};
241✔
59

60
control_flow::State& SDFGBuilder::
61
    add_state_before(const control_flow::State& state, bool is_start_state, const DebugInfo& debug_info) {
1✔
62
    auto& new_state = this->add_state(false, debug_info);
1✔
63

64
    std::vector<const control_flow::InterstateEdge*> to_redirect;
1✔
65
    for (auto& e : this->sdfg_->in_edges(state)) to_redirect.push_back(&e);
1✔
66

67
    // Redirect control-flow
68
    for (auto edge : to_redirect) {
1✔
69
        this->add_edge(edge->src(), new_state, edge->condition());
1✔
70

71
        auto desc = edge->edge();
1✔
72
        this->sdfg_->edges_.erase(desc);
1✔
73
        boost::remove_edge(desc, this->sdfg_->graph_);
1✔
74
    }
1✔
75
    this->add_edge(new_state, state);
1✔
76

77
    if (is_start_state) {
1✔
78
        this->sdfg_->start_state_ = &new_state;
×
79
    }
×
80

81
    return new_state;
1✔
82
};
1✔
83

84
control_flow::State& SDFGBuilder::
85
    add_state_after(const control_flow::State& state, bool connect_states, const DebugInfo& debug_info) {
4✔
86
    auto& new_state = this->add_state(false, debug_info);
4✔
87

88
    std::vector<const control_flow::InterstateEdge*> to_redirect;
4✔
89
    for (auto& e : this->sdfg_->out_edges(state)) to_redirect.push_back(&e);
4✔
90

91
    // Redirect control-flow
92
    for (auto& edge : to_redirect) {
4✔
93
        this->add_edge(new_state, edge->dst(), edge->condition());
1✔
94

95
        auto desc = edge->edge();
1✔
96
        this->sdfg_->edges_.erase(desc);
1✔
97
        boost::remove_edge(desc, this->sdfg_->graph_);
1✔
98
    }
1✔
99
    if (connect_states) {
4✔
100
        this->add_edge(state, new_state);
3✔
101
    }
3✔
102

103
    return new_state;
4✔
104
};
4✔
105

106
control_flow::ReturnState& SDFGBuilder::add_return_state(const std::string& data, const DebugInfo& debug_info) {
26✔
107
    auto vertex = boost::add_vertex(this->sdfg_->graph_);
26✔
108
    auto res = this->sdfg_->states_.insert(
26✔
109
        {vertex,
26✔
110
         std::unique_ptr<
26✔
111
             control_flow::State>(new control_flow::ReturnState(this->new_element_id(), debug_info, vertex, data))}
26✔
112
    );
26✔
113

114
    assert(res.second);
26✔
115
    (*res.first).second->dataflow_->parent_ = (*res.first).second.get();
26✔
116

117
    return static_cast<control_flow::ReturnState&>(*(*res.first).second);
26✔
118
};
26✔
119

120
control_flow::ReturnState& SDFGBuilder::
121
    add_return_state_after(const control_flow::State& state, const std::string& data, const DebugInfo& debug_info) {
4✔
122
    auto& new_state = this->add_return_state(data, debug_info);
4✔
123

124
    std::vector<const control_flow::InterstateEdge*> to_redirect;
4✔
125
    for (auto& e : this->sdfg_->out_edges(state)) to_redirect.push_back(&e);
4✔
126

127
    // Redirect control-flow
128
    for (auto& edge : to_redirect) {
4✔
129
        this->add_edge(new_state, edge->dst(), edge->condition());
×
130

131
        auto desc = edge->edge();
×
132
        this->sdfg_->edges_.erase(desc);
×
133
        boost::remove_edge(desc, this->sdfg_->graph_);
×
134
    }
×
135
    this->add_edge(state, new_state);
4✔
136

137
    return new_state;
4✔
138
};
4✔
139

140
control_flow::ReturnState& SDFGBuilder::
141
    add_constant_return_state(const std::string& data, const types::IType& type, const DebugInfo& debug_info) {
1✔
142
    auto vertex = boost::add_vertex(this->sdfg_->graph_);
1✔
143
    auto res = this->sdfg_->states_.insert(
1✔
144
        {vertex,
1✔
145
         std::unique_ptr<
1✔
146
             control_flow::State>(new control_flow::ReturnState(this->new_element_id(), debug_info, vertex, data, type))
1✔
147
        }
1✔
148
    );
1✔
149

150
    assert(res.second);
1✔
151
    (*res.first).second->dataflow_->parent_ = (*res.first).second.get();
1✔
152

153
    return static_cast<control_flow::ReturnState&>(*(*res.first).second);
1✔
154
};
1✔
155

156
control_flow::ReturnState& SDFGBuilder::add_constant_return_state_after(
157
    const control_flow::State& state, const std::string& data, const types::IType& type, const DebugInfo& debug_info
158
) {
×
159
    auto& new_state = this->add_constant_return_state(data, type, debug_info);
×
160

161
    std::vector<const control_flow::InterstateEdge*> to_redirect;
×
162
    for (auto& e : this->sdfg_->out_edges(state)) to_redirect.push_back(&e);
×
163

164
    // Redirect control-flow
165
    for (auto& edge : to_redirect) {
×
166
        this->add_edge(new_state, edge->dst(), edge->condition());
×
167

168
        auto desc = edge->edge();
×
169
        this->sdfg_->edges_.erase(desc);
×
170
        boost::remove_edge(desc, this->sdfg_->graph_);
×
171
    }
×
172
    this->add_edge(state, new_state);
×
173

174
    return new_state;
×
175
};
×
176

177
control_flow::InterstateEdge& SDFGBuilder::
178
    add_edge(const control_flow::State& src, const control_flow::State& dst, const DebugInfo& debug_info) {
75✔
179
    return this->add_edge(src, dst, control_flow::Assignments{}, SymEngine::boolTrue, debug_info);
75✔
180
};
75✔
181

182
control_flow::InterstateEdge& SDFGBuilder::add_edge(
183
    const control_flow::State& src,
184
    const control_flow::State& dst,
185
    const symbolic::Condition condition,
186
    const DebugInfo& debug_info
187
) {
28✔
188
    return this->add_edge(src, dst, control_flow::Assignments{}, condition, debug_info);
28✔
189
};
28✔
190

191
control_flow::InterstateEdge& SDFGBuilder::add_edge(
192
    const control_flow::State& src,
193
    const control_flow::State& dst,
194
    const control_flow::Assignments& assignments,
195
    const DebugInfo& debug_info
196
) {
19✔
197
    return this->add_edge(src, dst, assignments, SymEngine::boolTrue, debug_info);
19✔
198
};
19✔
199

200
control_flow::InterstateEdge& SDFGBuilder::add_edge(
201
    const control_flow::State& src,
202
    const control_flow::State& dst,
203
    const control_flow::Assignments& assignments,
204
    const symbolic::Condition condition,
205
    const DebugInfo& debug_info
206
) {
167✔
207
    if (dynamic_cast<const control_flow::ReturnState*>(&src) != nullptr) {
167✔
208
        throw InvalidSDFGException("Cannot add edge from ReturnState");
1✔
209
    }
1✔
210

211
    for (auto& entry : assignments) {
166✔
212
        auto& lhs = entry.first;
33✔
213
        auto& type = this->function().type(lhs->get_name());
33✔
214
        if (type.type_id() == types::TypeID::Scalar) {
33✔
215
            if (!types::is_integer(type.primitive_type())) {
33✔
NEW
216
                throw InvalidSDFGException("Assignment - LHS: must be integer type");
×
NEW
217
            }
×
218
        } else if (type.type_id() == types::TypeID::Reference) {
33✔
NEW
219
            auto* reference = dynamic_cast<const sdfg::codegen::Reference*>(&type);
×
NEW
220
            assert(reference != nullptr);
×
NEW
221
            auto& referenced_type = reference->reference_type();
×
NEW
222
            if (referenced_type.type_id() != types::TypeID::Scalar) {
×
NEW
223
                throw InvalidSDFGException("Assignment - LHS: must be a reference to a scalar type");
×
NEW
224
            }
×
NEW
225
            if (!types::is_integer(referenced_type.primitive_type())) {
×
NEW
226
                throw InvalidSDFGException("Assignment - LHS: must be integer type");
×
NEW
227
            }
×
NEW
228
        } else {
×
NEW
229
            throw InvalidSDFGException("Assignment - LHS: must be scalar type or a reference thereof");
×
UNCOV
230
        }
×
231

232
        auto& rhs = entry.second;
33✔
233
        for (auto& atom : symbolic::atoms(rhs)) {
33✔
234
            if (symbolic::is_nullptr(atom)) {
21✔
235
                continue;
×
236
            }
×
237
            auto& atom_type = this->function().type(atom->get_name());
21✔
238

239
            // Scalar integers
240
            if (atom_type.type_id() == types::TypeID::Scalar) {
21✔
241
                if (!types::is_integer(atom_type.primitive_type())) {
21✔
242
                    throw InvalidSDFGException("Assignment - RHS: must evaluate to integer type");
×
243
                }
×
244
                continue;
21✔
245
            } else if (atom_type.type_id() == types::TypeID::Reference) {
21✔
NEW
246
                auto* reference = dynamic_cast<const sdfg::codegen::Reference*>(&atom_type);
×
NEW
247
                assert(reference != nullptr);
×
NEW
248
                auto& referenced_type = reference->reference_type();
×
NEW
249
                if (referenced_type.type_id() != types::TypeID::Scalar) {
×
NEW
250
                    throw InvalidSDFGException("Assignment - RHS: must be a reference to a scalar type");
×
NEW
251
                }
×
NEW
252
                if (!types::is_integer(referenced_type.primitive_type())) {
×
NEW
253
                    throw InvalidSDFGException("Assignment - RHS: must evaluate to integer type");
×
NEW
254
                }
×
NEW
255
                continue;
×
256
            } else if (atom_type.type_id() == types::TypeID::Pointer) {
×
257
                continue;
×
258
            } else {
×
NEW
259
                throw InvalidSDFGException("Assignment - RHS: must evaluate to scalar, reference or pointer type");
×
260
            }
×
261
        }
21✔
262
    }
33✔
263

264
    for (auto& atom : symbolic::atoms(condition)) {
166✔
265
        if (symbolic::is_nullptr(atom)) {
75✔
266
            continue;
1✔
267
        }
1✔
268
        auto& atom_type = this->function().type(atom->get_name());
74✔
269
        if (atom_type.type_id() != types::TypeID::Scalar && atom_type.type_id() != types::TypeID::Pointer) {
74✔
270
            throw InvalidSDFGException("Condition: must be integer type or pointer type");
×
271
        }
×
272
    }
74✔
273

274
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, this->sdfg_->graph_);
166✔
275
    assert(edge.second);
166✔
276

277
    auto res = this->sdfg_->edges_.insert(
166✔
278
        {edge.first,
166✔
279
         std::unique_ptr<control_flow::InterstateEdge>(new control_flow::InterstateEdge(
166✔
280
             this->new_element_id(), debug_info, edge.first, src, dst, condition, assignments
166✔
281
         ))}
166✔
282
    );
166✔
283

284
    assert(res.second);
166✔
285

286
    return *(*res.first).second;
166✔
287
};
166✔
288

289
void SDFGBuilder::remove_edge(const control_flow::InterstateEdge& edge) {
×
290
    auto desc = edge.edge();
×
291
    this->sdfg_->edges_.erase(desc);
×
292

293
    boost::remove_edge(desc, this->sdfg_->graph_);
×
294
};
×
295

296
std::tuple<control_flow::State&, control_flow::State&, control_flow::State&> SDFGBuilder::add_loop(
297
    const control_flow::State& state,
298
    sdfg::symbolic::Symbol iterator,
299
    sdfg::symbolic::Expression init,
300
    sdfg::symbolic::Condition cond,
301
    sdfg::symbolic::Expression update,
302
    const DebugInfo& debug_info
303
) {
1✔
304
    // Init: iterator = init
305
    auto& init_state = this->add_state_after(state, true, debug_info);
1✔
306
    const graph::Edge init_edge_desc = (*this->sdfg_->in_edges(init_state).begin()).edge_;
1✔
307
    auto& init_edge = this->sdfg_->edges_[init_edge_desc];
1✔
308
    init_edge->assignments_.insert({iterator, init});
1✔
309

310
    // Final state
311
    auto& final_state = this->add_state_after(init_state, false, debug_info);
1✔
312

313
    // Init -> early_exit -> final
314
    auto& early_exit_state = this->add_state(false, debug_info);
1✔
315
    this->add_edge(init_state, early_exit_state, symbolic::Not(cond));
1✔
316
    this->add_edge(early_exit_state, final_state);
1✔
317

318
    // Init -> header -> body
319
    auto& header_state = this->add_state(false, debug_info);
1✔
320
    this->add_edge(init_state, header_state, cond);
1✔
321

322
    auto& body_state = this->add_state(false, debug_info);
1✔
323
    this->add_edge(header_state, body_state);
1✔
324

325
    auto& update_state = this->add_state(false, debug_info);
1✔
326
    this->add_edge(body_state, update_state, {{iterator, update}});
1✔
327

328
    // Back edge and exit edge
329
    this->add_edge(update_state, header_state, cond);
1✔
330
    this->add_edge(update_state, final_state, symbolic::Not(cond));
1✔
331

332
    return {init_state, body_state, final_state};
1✔
333
};
1✔
334

335
/***** Section: Dataflow Graph *****/
336

337
data_flow::AccessNode& SDFGBuilder::
338
    add_access(control_flow::State& state, const std::string& data, const DebugInfo& debug_info) {
142✔
339
    auto& dataflow = state.dataflow();
142✔
340
    auto vertex = boost::add_vertex(dataflow.graph_);
142✔
341
    auto res = dataflow.nodes_.insert(
142✔
342
        {vertex,
142✔
343
         std::unique_ptr<
142✔
344
             data_flow::AccessNode>(new data_flow::AccessNode(this->new_element_id(), debug_info, vertex, dataflow, data)
142✔
345
         )}
142✔
346
    );
142✔
347

348
    return static_cast<data_flow::AccessNode&>(*(res.first->second));
142✔
349
};
142✔
350

351
data_flow::ConstantNode& SDFGBuilder::add_constant(
352
    control_flow::State& state, const std::string& data, const types::IType& type, const DebugInfo& debug_info
353
) {
19✔
354
    auto& dataflow = state.dataflow();
19✔
355
    auto vertex = boost::add_vertex(dataflow.graph_);
19✔
356
    auto res = dataflow.nodes_.insert(
19✔
357
        {vertex,
19✔
358
         std::unique_ptr<data_flow::ConstantNode>(
19✔
359
             new data_flow::ConstantNode(this->new_element_id(), debug_info, vertex, dataflow, data, type)
19✔
360
         )}
19✔
361
    );
19✔
362

363
    return static_cast<data_flow::ConstantNode&>(*(res.first->second));
19✔
364
};
19✔
365

366
data_flow::Tasklet& SDFGBuilder::add_tasklet(
367
    control_flow::State& state,
368
    const data_flow::TaskletCode code,
369
    const std::string& output,
370
    const std::vector<std::string>& inputs,
371
    const DebugInfo& debug_info
372
) {
67✔
373
    auto& dataflow = state.dataflow();
67✔
374
    auto vertex = boost::add_vertex(dataflow.graph_);
67✔
375
    auto res = dataflow.nodes_.insert(
67✔
376
        {vertex,
67✔
377
         std::unique_ptr<data_flow::Tasklet>(
67✔
378
             new data_flow::Tasklet(this->new_element_id(), debug_info, vertex, dataflow, code, output, inputs)
67✔
379
         )}
67✔
380
    );
67✔
381

382
    return static_cast<data_flow::Tasklet&>(*(res.first->second));
67✔
383
};
67✔
384

385
data_flow::Memlet& SDFGBuilder::add_memlet(
386
    control_flow::State& state,
387
    data_flow::DataFlowNode& src,
388
    const std::string& src_conn,
389
    data_flow::DataFlowNode& dst,
390
    const std::string& dst_conn,
391
    const data_flow::Subset& subset,
392
    const types::IType& base_type,
393
    const DebugInfo& debug_info
394
) {
143✔
395
    auto& dataflow = state.dataflow();
143✔
396
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, dataflow.graph_);
143✔
397
    auto res = dataflow.edges_.insert(
143✔
398
        {edge.first,
143✔
399
         std::unique_ptr<data_flow::Memlet>(new data_flow::Memlet(
143✔
400
             this->new_element_id(), debug_info, edge.first, dataflow, src, src_conn, dst, dst_conn, subset, base_type
143✔
401
         ))}
143✔
402
    );
143✔
403

404
    auto& memlet = static_cast<data_flow::Memlet&>(*(res.first->second));
143✔
405

406
    return memlet;
143✔
407
};
143✔
408

409
data_flow::Memlet& SDFGBuilder::add_computational_memlet(
410
    control_flow::State& state,
411
    data_flow::AccessNode& src,
412
    data_flow::Tasklet& dst,
413
    const std::string& dst_conn,
414
    const data_flow::Subset& subset,
415
    const types::IType& base_type,
416
    const DebugInfo& debug_info
417
) {
×
418
    return this->add_memlet(state, src, "void", dst, dst_conn, subset, base_type, debug_info);
×
419
};
×
420

421
data_flow::Memlet& SDFGBuilder::add_computational_memlet(
422
    control_flow::State& state,
423
    data_flow::Tasklet& src,
424
    const std::string& src_conn,
425
    data_flow::AccessNode& dst,
426
    const data_flow::Subset& subset,
427
    const types::IType& base_type,
428
    const DebugInfo& debug_info
429
) {
×
430
    return this->add_memlet(state, src, src_conn, dst, "void", subset, base_type, debug_info);
×
431
};
×
432

433
data_flow::Memlet& SDFGBuilder::add_computational_memlet(
434
    control_flow::State& state,
435
    data_flow::AccessNode& src,
436
    data_flow::Tasklet& dst,
437
    const std::string& dst_conn,
438
    const data_flow::Subset& subset,
439
    const DebugInfo& debug_info
440
) {
75✔
441
    const types::IType* src_type = nullptr;
75✔
442
    if (auto cnode = dynamic_cast<data_flow::ConstantNode*>(&src)) {
75✔
443
        src_type = &cnode->type();
4✔
444
    } else {
71✔
445
        src_type = &this->sdfg_->type(src.data());
71✔
446
    }
71✔
447
    auto& base_type = types::infer_type(*this->sdfg_, *src_type, subset);
75✔
448
    if (base_type.type_id() != types::TypeID::Scalar) {
75✔
449
        throw InvalidSDFGException("Computational memlet must have a scalar type");
×
450
    }
×
451
    return this->add_memlet(state, src, "void", dst, dst_conn, subset, *src_type, debug_info);
75✔
452
};
75✔
453

454
data_flow::Memlet& SDFGBuilder::add_computational_memlet(
455
    control_flow::State& state,
456
    data_flow::Tasklet& src,
457
    const std::string& src_conn,
458
    data_flow::AccessNode& dst,
459
    const data_flow::Subset& subset,
460
    const DebugInfo& debug_info
461
) {
68✔
462
    auto& dst_type = this->function().type(dst.data());
68✔
463
    auto& base_type = types::infer_type(this->function(), dst_type, subset);
68✔
464
    if (base_type.type_id() != types::TypeID::Scalar) {
68✔
465
        throw InvalidSDFGException("Computational memlet must have a scalar type");
×
466
    }
×
467
    return this->add_memlet(state, src, src_conn, dst, "void", subset, dst_type, debug_info);
68✔
468
};
68✔
469

470
data_flow::Memlet& SDFGBuilder::add_computational_memlet(
471
    control_flow::State& state,
472
    data_flow::AccessNode& src,
473
    data_flow::LibraryNode& dst,
474
    const std::string& dst_conn,
475
    const data_flow::Subset& subset,
476
    const types::IType& base_type,
477
    const DebugInfo& debug_info
478
) {
×
479
    return this->add_memlet(state, src, "void", dst, dst_conn, subset, base_type, debug_info);
×
480
};
×
481

482
data_flow::Memlet& SDFGBuilder::add_computational_memlet(
483
    control_flow::State& state,
484
    data_flow::LibraryNode& src,
485
    const std::string& src_conn,
486
    data_flow::AccessNode& dst,
487
    const data_flow::Subset& subset,
488
    const types::IType& base_type,
489
    const DebugInfo& debug_info
490
) {
×
491
    return this->add_memlet(state, src, src_conn, dst, "void", subset, base_type, debug_info);
×
492
};
×
493

494
data_flow::Memlet& SDFGBuilder::add_reference_memlet(
495
    control_flow::State& state,
496
    data_flow::AccessNode& src,
497
    data_flow::AccessNode& dst,
498
    const data_flow::Subset& subset,
499
    const types::IType& base_type,
500
    const DebugInfo& debug_info
501
) {
×
502
    return this->add_memlet(state, src, "void", dst, "ref", subset, base_type, debug_info);
×
503
};
×
504

505
data_flow::Memlet& SDFGBuilder::add_dereference_memlet(
506
    control_flow::State& state,
507
    data_flow::AccessNode& src,
508
    data_flow::AccessNode& dst,
509
    bool derefs_src,
510
    const types::IType& base_type,
511
    const DebugInfo& debug_info
512
) {
×
513
    if (derefs_src) {
×
514
        return this->add_memlet(state, src, "void", dst, "deref", {symbolic::zero()}, base_type, debug_info);
×
515
    } else {
×
516
        return this->add_memlet(state, src, "deref", dst, "void", {symbolic::zero()}, base_type, debug_info);
×
517
    }
×
518
};
×
519

520
} // namespace builder
521
} // 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