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

daisytuner / sdfglib / 15262928007

26 May 2025 10:36PM UTC coverage: 58.284% (-2.0%) from 60.304%
15262928007

push

github

web-flow
Merge pull request #36 from daisytuner/sdfg-validation

Introduces new definition of memlets plus sanity checks

104 of 266 new or added lines in 6 files covered. (39.1%)

241 existing lines in 15 files now uncovered.

7908 of 13568 relevant lines covered (58.28%)

96.1 hits per line

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

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

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

5
namespace sdfg {
6
namespace builder {
7

8
Function& SDFGBuilder::function() const { return static_cast<Function&>(*this->sdfg_); };
77✔
9

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

13
      };
×
14

15
SDFGBuilder::SDFGBuilder(const std::string& name)
51✔
16
    : FunctionBuilder(), sdfg_(new SDFG(name)) {
51✔
17

18
      };
51✔
19

20
SDFG& SDFGBuilder::subject() const { return *this->sdfg_; };
20✔
21

22
std::unique_ptr<SDFG> SDFGBuilder::move() { return std::move(this->sdfg_); };
33✔
23

24
/***** Section: Control-Flow Graph *****/
25

26
control_flow::State& SDFGBuilder::add_state(bool is_start_state, const DebugInfo& debug_info) {
67✔
27
    auto vertex = boost::add_vertex(this->sdfg_->graph_);
67✔
28
    auto res = this->sdfg_->states_.insert(
67✔
29
        {vertex, std::unique_ptr<control_flow::State>(
67✔
30
                     new control_flow::State(this->element_counter_, debug_info, vertex))});
67✔
31
    this->element_counter_++;
67✔
32
    assert(res.second);
67✔
33
    (*res.first).second->dataflow_->parent_ = (*res.first).second.get();
67✔
34

35
    if (is_start_state) {
67✔
36
        this->sdfg_->start_state_ = (*res.first).second.get();
14✔
37
    }
14✔
38

39
    return *(*res.first).second;
67✔
40
};
×
41

42
control_flow::State& SDFGBuilder::add_state_before(const control_flow::State& state,
1✔
43
                                                   bool is_start_state,
44
                                                   const DebugInfo& debug_info) {
45
    auto& new_state = this->add_state(false, debug_info);
1✔
46

47
    std::vector<const control_flow::InterstateEdge*> to_redirect;
1✔
48
    for (auto& e : this->sdfg_->in_edges(state)) to_redirect.push_back(&e);
2✔
49

50
    // Redirect control-flow
51
    for (auto edge : to_redirect) {
2✔
52
        this->add_edge(edge->src(), new_state, edge->condition());
1✔
53

54
        auto desc = edge->edge();
1✔
55
        this->sdfg_->edges_.erase(desc);
1✔
56
        boost::remove_edge(desc, this->sdfg_->graph_);
1✔
57
    }
58
    this->add_edge(new_state, state);
1✔
59

60
    if (is_start_state) {
1✔
61
        this->sdfg_->start_state_ = &new_state;
×
62
    }
×
63

64
    return new_state;
1✔
65
};
1✔
66

67
control_flow::State& SDFGBuilder::add_state_after(const control_flow::State& state,
5✔
68
                                                  bool connect_states,
69
                                                  const DebugInfo& debug_info) {
70
    auto& new_state = this->add_state(false, debug_info);
5✔
71

72
    std::vector<const control_flow::InterstateEdge*> to_redirect;
5✔
73
    for (auto& e : this->sdfg_->out_edges(state)) to_redirect.push_back(&e);
6✔
74

75
    // Redirect control-flow
76
    for (auto& edge : to_redirect) {
6✔
77
        this->add_edge(new_state, edge->dst(), edge->condition());
1✔
78

79
        auto desc = edge->edge();
1✔
80
        this->sdfg_->edges_.erase(desc);
1✔
81
        boost::remove_edge(desc, this->sdfg_->graph_);
1✔
82
    }
83
    if (connect_states) {
5✔
84
        this->add_edge(state, new_state);
4✔
85
    }
4✔
86

87
    return new_state;
5✔
88
};
5✔
89

90
control_flow::InterstateEdge& SDFGBuilder::add_edge(const control_flow::State& src,
44✔
91
                                                    const control_flow::State& dst,
92
                                                    const DebugInfo& debug_info) {
93
    return this->add_edge(src, dst, symbolic::Assignments{}, SymEngine::boolTrue, debug_info);
44✔
94
};
×
95

96
control_flow::InterstateEdge& SDFGBuilder::add_edge(const control_flow::State& src,
8✔
97
                                                    const control_flow::State& dst,
98
                                                    const symbolic::Condition condition,
99
                                                    const DebugInfo& debug_info) {
100
    return this->add_edge(src, dst, symbolic::Assignments{}, condition, debug_info);
8✔
101
};
×
102

103
control_flow::InterstateEdge& SDFGBuilder::add_edge(const control_flow::State& src,
2✔
104
                                                    const control_flow::State& dst,
105
                                                    const symbolic::Assignments& assignments,
106
                                                    const DebugInfo& debug_info) {
107
    return this->add_edge(src, dst, assignments, SymEngine::boolTrue, debug_info);
2✔
108
};
×
109

110
control_flow::InterstateEdge& SDFGBuilder::add_edge(const control_flow::State& src,
56✔
111
                                                    const control_flow::State& dst,
112
                                                    const symbolic::Assignments& assignments,
113
                                                    const symbolic::Condition condition,
114
                                                    const DebugInfo& debug_info) {
115
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, this->sdfg_->graph_);
56✔
116
    assert(edge.second);
56✔
117

118
    auto res = this->sdfg_->edges_.insert(
56✔
119
        {edge.first,
56✔
120
         std::unique_ptr<control_flow::InterstateEdge>(new control_flow::InterstateEdge(
112✔
121
             this->element_counter_, debug_info, edge.first, src, dst, condition, assignments))});
56✔
122
    this->element_counter_++;
56✔
123
    assert(res.second);
56✔
124

125
    return *(*res.first).second;
56✔
126
};
×
127

128
void SDFGBuilder::remove_edge(const control_flow::InterstateEdge& edge) {
×
129
    auto desc = edge.edge();
×
130
    this->sdfg_->edges_.erase(desc);
×
131

132
    boost::remove_edge(desc, this->sdfg_->graph_);
×
133
};
×
134

135
std::tuple<control_flow::State&, control_flow::State&, control_flow::State&> SDFGBuilder::add_loop(
1✔
136
    const control_flow::State& state, sdfg::symbolic::Symbol iterator,
137
    sdfg::symbolic::Expression init, sdfg::symbolic::Condition cond,
138
    sdfg::symbolic::Expression update, const DebugInfo& debug_info) {
139
    // Init: iterator = init
140
    auto& init_state = this->add_state_after(state, true, debug_info);
1✔
141
    const graph::Edge init_edge_desc = (*this->sdfg_->in_edges(init_state).begin()).edge_;
1✔
142
    auto& init_edge = this->sdfg_->edges_[init_edge_desc];
1✔
143
    init_edge->assignments_.insert({iterator, init});
1✔
144

145
    // Final state
146
    auto& final_state = this->add_state_after(init_state, false, debug_info);
1✔
147

148
    // Init -> early_exit -> final
149
    auto& early_exit_state = this->add_state(false, debug_info);
1✔
150
    this->add_edge(init_state, early_exit_state, symbolic::Not(cond));
1✔
151
    this->add_edge(early_exit_state, final_state);
1✔
152

153
    // Init -> header -> body
154
    auto& header_state = this->add_state(false, debug_info);
1✔
155
    this->add_edge(init_state, header_state, cond);
1✔
156

157
    auto& body_state = this->add_state(false, debug_info);
1✔
158
    this->add_edge(header_state, body_state);
1✔
159

160
    auto& update_state = this->add_state(false, debug_info);
1✔
161
    this->add_edge(body_state, update_state, {{iterator, update}});
1✔
162

163
    // Back edge and exit edge
164
    this->add_edge(update_state, header_state, cond);
1✔
165
    this->add_edge(update_state, final_state, symbolic::Not(cond));
1✔
166

167
    return {init_state, body_state, final_state};
1✔
168
};
×
169

170
/***** Section: Dataflow Graph *****/
171

172
data_flow::AccessNode& SDFGBuilder::add_access(control_flow::State& state, const std::string& data,
6✔
173
                                               const DebugInfo& debug_info) {
174
    // Check: Data exists
175
    if (!this->subject().exists(data)) {
6✔
176
        throw InvalidSDFGException("Data does not exist in SDFG: " + data);
×
177
    }
178

179
    auto& dataflow = state.dataflow();
6✔
180
    auto vertex = boost::add_vertex(dataflow.graph_);
6✔
181
    auto res = dataflow.nodes_.insert(
6✔
182
        {vertex, std::unique_ptr<data_flow::AccessNode>(new data_flow::AccessNode(
12✔
183
                     this->element_counter_, debug_info, vertex, dataflow, data))});
6✔
184
    this->element_counter_++;
6✔
185
    return dynamic_cast<data_flow::AccessNode&>(*(res.first->second));
6✔
186
};
×
187

188
data_flow::Tasklet& SDFGBuilder::add_tasklet(
3✔
189
    control_flow::State& state, const data_flow::TaskletCode code,
190
    const std::pair<std::string, sdfg::types::Scalar>& output,
191
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
192
    const DebugInfo& debug_info) {
193
    // Check: Duplicate inputs
194
    std::unordered_set<std::string> input_names;
3✔
195
    for (auto& input : inputs) {
8✔
196
        if (input_names.find(input.first) != input_names.end()) {
5✔
NEW
197
            throw InvalidSDFGException("Input " + input.first + " already exists in SDFG");
×
198
        }
199
        input_names.insert(input.first);
5✔
200
    }
201

202
    auto& dataflow = state.dataflow();
3✔
203
    auto vertex = boost::add_vertex(dataflow.graph_);
3✔
204
    auto res =
205
        dataflow.nodes_.insert({vertex, std::unique_ptr<data_flow::Tasklet>(new data_flow::Tasklet(
6✔
206
                                            this->element_counter_, debug_info, vertex, dataflow,
3✔
207
                                            code, output, inputs, symbolic::__true__()))});
3✔
208
    this->element_counter_++;
3✔
209
    return dynamic_cast<data_flow::Tasklet&>(*(res.first->second));
3✔
210
};
3✔
211

212
data_flow::Memlet& SDFGBuilder::add_memlet(control_flow::State& state, data_flow::DataFlowNode& src,
6✔
213
                                           const std::string& src_conn,
214
                                           data_flow::DataFlowNode& dst,
215
                                           const std::string& dst_conn,
216
                                           const data_flow::Subset& subset,
217
                                           const DebugInfo& debug_info) {
218
    auto& function_ = this->function();
6✔
219

220
    // Check - Case 1: Access Node -> Access Node
221
    // - src_conn or dst_conn must be refs. The other must be void.
222
    // - The side of the memlet that is void, is dereferenced.
223
    // - The dst type must always be a pointer after potential dereferencing.
224
    // - The src type can be any type after dereferecing (&dereferenced_src_type).
225
    if (dynamic_cast<data_flow::AccessNode*>(&src) && dynamic_cast<data_flow::AccessNode*>(&dst)) {
6✔
NEW
226
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
×
NEW
227
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
×
NEW
228
        if (src_conn == "refs") {
×
NEW
229
            if (dst_conn != "void") {
×
NEW
230
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
231
            }
232

NEW
233
            auto& dst_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
×
NEW
234
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
×
NEW
235
                throw InvalidSDFGException("dst type must be a pointer");
×
236
            }
237

NEW
238
            auto& src_type = function_.type(src_node.data());
×
NEW
239
            if (!dynamic_cast<const types::Pointer*>(&src_type)) {
×
NEW
240
                throw InvalidSDFGException("src type must be a pointer");
×
241
            }
NEW
242
        } else if (src_conn == "void") {
×
NEW
243
            if (dst_conn != "refs") {
×
NEW
244
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
245
            }
246

NEW
247
            if (symbolic::is_pointer(symbolic::symbol(src_node.data()))) {
×
NEW
248
                throw InvalidSDFGException("src_conn is void: src cannot be a raw pointer");
×
249
            }
250

251
            // Trivially correct but checks inference
NEW
252
            auto& src_type = types::infer_type(function_, function_.type(src_node.data()), subset);
×
NEW
253
            types::Pointer ref_type(src_type);
×
254
            if (!dynamic_cast<const types::Pointer*>(&ref_type)) {
255
                throw InvalidSDFGException("src type must be a pointer");
256
            }
257

NEW
258
            auto& dst_type = function_.type(dst_node.data());
×
NEW
259
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
×
NEW
260
                throw InvalidSDFGException("dst type must be a pointer");
×
261
            }
NEW
262
        } else {
×
NEW
263
            throw InvalidSDFGException("Invalid src connector: " + src_conn);
×
264
        }
265
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
9✔
266
               dynamic_cast<data_flow::Tasklet*>(&dst)) {
3✔
267
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
3✔
268
        auto& dst_node = dynamic_cast<data_flow::Tasklet&>(dst);
3✔
269
        if (src_conn != "void") {
3✔
NEW
270
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
271
        }
272
        bool found = false;
3✔
273
        for (auto& input : dst_node.inputs()) {
3✔
274
            if (input.first == dst_conn) {
3✔
275
                found = true;
3✔
276
                break;
3✔
277
            }
278
        }
279
        if (!found) {
3✔
NEW
280
            throw InvalidSDFGException("dst_conn not found in tasklet: " + dst_conn);
×
281
        }
282
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
3✔
283
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
3✔
NEW
284
            throw InvalidSDFGException("Tasklets inputs must be scalars");
×
285
        }
286
    } else if (dynamic_cast<data_flow::Tasklet*>(&src) &&
9✔
287
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
3✔
288
        auto& src_node = dynamic_cast<data_flow::Tasklet&>(src);
3✔
289
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
3✔
290
        if (src_conn != src_node.outputs()[0].first) {
3✔
NEW
291
            throw InvalidSDFGException("src_conn must match tasklet output name");
×
292
        }
293
        if (dst_conn != "void") {
3✔
NEW
294
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
295
        }
296

297
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
3✔
298
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
3✔
NEW
299
            throw InvalidSDFGException("Tasklet output must be a scalar");
×
300
        }
301
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
3✔
NEW
302
               dynamic_cast<data_flow::LibraryNode*>(&dst)) {
×
NEW
303
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
×
NEW
304
        auto& dst_node = dynamic_cast<data_flow::LibraryNode&>(dst);
×
NEW
305
        if (src_conn != "void") {
×
NEW
306
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
307
        }
NEW
308
        bool found = false;
×
NEW
309
        for (auto& input : dst_node.inputs()) {
×
NEW
310
            if (input.first == dst_conn) {
×
NEW
311
                found = true;
×
NEW
312
                break;
×
313
            }
314
        }
NEW
315
        if (!found) {
×
NEW
316
            throw InvalidSDFGException("dst_conn not found in library node: " + dst_conn);
×
317
        }
318

NEW
319
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
×
NEW
320
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
×
NEW
321
            throw InvalidSDFGException("Library node inputs must be scalars");
×
322
        }
NEW
323
    } else if (dynamic_cast<data_flow::LibraryNode*>(&src) &&
×
NEW
324
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
×
NEW
325
        auto& src_node = dynamic_cast<data_flow::LibraryNode&>(src);
×
NEW
326
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
×
NEW
327
        if (dst_conn != "void") {
×
NEW
328
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
329
        }
NEW
330
        bool found = false;
×
NEW
331
        for (auto& output : src_node.outputs()) {
×
NEW
332
            if (output.first == src_conn) {
×
NEW
333
                found = true;
×
NEW
334
                break;
×
335
            }
336
        }
NEW
337
        if (!found) {
×
NEW
338
            throw InvalidSDFGException("src_conn not found in library node: " + src_conn);
×
339
        }
340

NEW
341
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
×
NEW
342
        if (!dynamic_cast<const types::Pointer*>(&element_type)) {
×
NEW
343
            throw InvalidSDFGException("Access node must be a pointer");
×
344
        }
NEW
345
    } else {
×
NEW
346
        throw InvalidSDFGException("Invalid src or dst node type");
×
347
    }
348

349
    auto& dataflow = state.dataflow();
6✔
350
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, dataflow.graph_);
6✔
351
    auto res = dataflow.edges_.insert(
6✔
352
        {edge.first, std::unique_ptr<data_flow::Memlet>(
6✔
353
                         new data_flow::Memlet(this->element_counter_, debug_info, edge.first,
12✔
354
                                               dataflow, src, src_conn, dst, dst_conn, subset))});
6✔
355
    this->element_counter_++;
6✔
356
    return dynamic_cast<data_flow::Memlet&>(*(res.first->second));
6✔
357
};
×
358

359
data_flow::LibraryNode& SDFGBuilder::add_library_node(
2✔
360
    control_flow::State& state, const data_flow::LibraryNodeType& call,
361
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& outputs,
362
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
363
    const bool has_side_effect, const DebugInfo& debug_info) {
364
    auto& dataflow = state.dataflow();
2✔
365

366
    auto vertex = boost::add_vertex(dataflow.graph_);
2✔
367
    auto res = dataflow.nodes_.insert(
2✔
368
        {vertex, std::unique_ptr<data_flow::LibraryNode>(new data_flow::LibraryNode(
4✔
369
                     this->element_counter_, debug_info, vertex, dataflow, outputs, inputs, call,
2✔
370
                     has_side_effect))});
2✔
371
    this->element_counter_++;
2✔
372
    return dynamic_cast<data_flow::LibraryNode&>(*(res.first->second));
2✔
373
}
×
374

375
}  // namespace builder
376
}  // 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