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

daisytuner / docc / 24408765437

14 Apr 2026 03:47PM UTC coverage: 64.417% (-0.05%) from 64.469%
24408765437

push

github

web-flow
Add support for DDE to remove mallocs and frees as dead-code when DataOffloadingNodes operate on them (#678)

 + generic modeling for libNodes to declare that an input pointer does not actually escape through them
 + draft of generic modeling of input-pointer access ranges and access types (read-only, full overwrite etc.)
 + DDE: Offload-Transfer-specific handling to reinterpret their fake output edges instead as only indirect writes vie the target pointer
 + DDE: option to disable all the legacy code that requires full UserAnalysis and DataDependencyAnalysis.
 * the new malloc/heap removal in DDE can now remove containers by itself, not relying on DataDependencyAnalysis for that
 * improved builder.clear_node, node can report how to deal with a dead edge, including calling an update method on the node to make it validate again after edge removal.
 + Used for OffloadTransfer nodes to support removing the output edge via dead-code and drop the transfer and maybe the entire node if it does nothing anymore.
 * Offloading: option to quickly disable omitting d2h for read-only data

92 of 162 new or added lines in 11 files covered. (56.79%)

4 existing lines in 3 files now uncovered.

30598 of 47500 relevant lines covered (64.42%)

582.72 hits per line

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

69.86
/sdfg/src/data_flow/tasklet.cpp
1
#include "sdfg/data_flow/tasklet.h"
2

3
#include "sdfg/data_flow/data_flow_graph.h"
4
#include "sdfg/symbolic/symbolic.h"
5

6
namespace sdfg {
7
namespace data_flow {
8

9
Tasklet::Tasklet(
10
    size_t element_id,
11
    const DebugInfo& debug_info,
12
    const graph::Vertex vertex,
13
    DataFlowGraph& parent,
14
    const TaskletCode code,
15
    const std::string& output,
16
    const std::vector<std::string>& inputs
17
)
18
    : CodeNode(element_id, debug_info, vertex, parent, {output}, inputs), code_(code) {};
1,490✔
19

20
void Tasklet::validate(const Function& function) const {
2,769✔
21
    auto& graph = this->get_parent();
2,769✔
22

23
    // Validate: inputs match arity
24
    if (arity(this->code_) != this->inputs_.size()) {
2,769✔
25
        throw InvalidSDFGException(
×
26
            "Tasklet (Code: " + std::to_string(this->code_) + "): Invalid number of inputs. Expected " +
×
27
            std::to_string(arity(this->code_)) + ", got " + std::to_string(this->inputs_.size())
×
28
        );
×
29
    }
×
30

31
    // Validate: inputs match type of operation
32
    for (auto& iedge : graph.in_edges(*this)) {
5,061✔
33
        auto input_type = iedge.result_type(function);
5,061✔
34
        if (is_integer(this->code_) && !types::is_integer(input_type->primitive_type())) {
5,061✔
35
            throw InvalidSDFGException(
×
36
                "Tasklet (Code: " + std::to_string(this->code_) + "): Integer operation with non-integer input type"
×
37
            );
×
38
        }
×
39
        if (is_floating_point(this->code_) && !types::is_floating_point(input_type->primitive_type())) {
5,061✔
40
            throw InvalidSDFGException(
×
41
                "Tasklet (Code: " + std::to_string(this->code_) + "): Floating point operation with integer input type"
×
42
            );
×
43
        }
×
44
    }
5,061✔
45

46
    // Validate: Edges
47
    if (graph.in_degree(*this) != this->inputs_.size()) {
2,769✔
48
        throw InvalidSDFGException(
×
49
            "Tasklet (Code: " + std::to_string(this->code_) +
×
50
            "): Number of input edges does not match number of inputs."
×
51
        );
×
52
    }
×
53
    if (graph.out_degree(*this) != this->outputs_.size()) {
2,769✔
54
        throw InvalidSDFGException(
×
55
            "Tasklet (Code: " + std::to_string(this->code_) +
×
56
            "): Number of output edges does not match number of outputs."
×
57
        );
×
58
    }
×
59
}
2,769✔
60

61
TaskletCode Tasklet::code() const { return this->code_; };
4,040✔
62

63

64
bool Tasklet::is_assign() const { return this->code_ == TaskletCode::assign; }
54✔
65

66
bool Tasklet::is_trivial(const Function& function) const {
1✔
67
    if (!this->is_assign()) {
1✔
68
        return false;
×
69
    }
×
70

71
    auto& graph = this->get_parent();
1✔
72
    auto& iedge = *graph.in_edges(*this).begin();
1✔
73
    auto& oedge = *graph.out_edges(*this).begin();
1✔
74
    auto input_type = iedge.result_type(function);
1✔
75
    auto output_type = oedge.result_type(function);
1✔
76

77
    return input_type->primitive_type() == output_type->primitive_type();
1✔
78
}
1✔
79

80
bool Tasklet::is_cast(const Function& function) const {
10✔
81
    if (!this->is_assign()) {
10✔
82
        return false;
×
83
    }
×
84

85
    auto& graph = this->get_parent();
10✔
86
    auto& iedge = *graph.in_edges(*this).begin();
10✔
87
    auto& oedge = *graph.out_edges(*this).begin();
10✔
88
    auto input_type = iedge.result_type(function);
10✔
89
    auto output_type = oedge.result_type(function);
10✔
90

91
    return input_type->primitive_type() != output_type->primitive_type();
10✔
92
}
10✔
93

94
bool Tasklet::is_zext(const Function& function) const {
13✔
95
    if (!this->is_assign()) {
13✔
96
        return false;
×
97
    }
×
98

99
    auto& graph = this->get_parent();
13✔
100
    auto& iedge = *graph.in_edges(*this).begin();
13✔
101
    auto& oedge = *graph.out_edges(*this).begin();
13✔
102
    auto input_type = iedge.result_type(function);
13✔
103
    auto output_type = oedge.result_type(function);
13✔
104

105
    if (!types::is_unsigned(input_type->primitive_type()) || !types::is_unsigned(output_type->primitive_type())) {
13✔
106
        return false;
7✔
107
    }
7✔
108
    if (types::bit_width(output_type->primitive_type()) <= types::bit_width(input_type->primitive_type())) {
6✔
109
        return false;
3✔
110
    }
3✔
111

112
    return true;
3✔
113
}
6✔
114

115
bool Tasklet::is_sext(const Function& function) const {
1✔
116
    if (!this->is_assign()) {
1✔
117
        return false;
×
118
    }
×
119

120
    auto& graph = this->get_parent();
1✔
121
    auto& iedge = *graph.in_edges(*this).begin();
1✔
122
    auto& oedge = *graph.out_edges(*this).begin();
1✔
123
    auto input_type = iedge.result_type(function);
1✔
124
    auto output_type = oedge.result_type(function);
1✔
125

126
    if (types::is_unsigned(input_type->primitive_type()) || types::is_unsigned(output_type->primitive_type())) {
1✔
127
        return false;
×
128
    }
×
129
    if (types::bit_width(output_type->primitive_type()) <= types::bit_width(input_type->primitive_type())) {
1✔
130
        return false;
×
131
    }
×
132

133
    return true;
1✔
134
}
1✔
135

136
bool Tasklet::is_trunc(const Function& function) const {
13✔
137
    if (!this->is_assign()) {
13✔
138
        return false;
×
139
    }
×
140

141
    auto& graph = this->get_parent();
13✔
142
    auto& iedge = *graph.in_edges(*this).begin();
13✔
143
    auto& oedge = *graph.out_edges(*this).begin();
13✔
144
    auto input_type = iedge.result_type(function);
13✔
145
    auto output_type = oedge.result_type(function);
13✔
146

147
    if (!types::is_integer(input_type->primitive_type()) || !types::is_integer(output_type->primitive_type())) {
13✔
148
        return false;
1✔
149
    }
1✔
150
    if (types::is_unsigned(input_type->primitive_type()) != types::is_unsigned(output_type->primitive_type())) {
12✔
151
        return false;
2✔
152
    }
2✔
153
    if (types::bit_width(output_type->primitive_type()) >= types::bit_width(input_type->primitive_type())) {
10✔
154
        return false;
7✔
155
    }
7✔
156

157
    return true;
3✔
158
}
10✔
159

160
bool Tasklet::is_fptoui(const Function& function) const {
1✔
161
    if (!this->is_assign()) {
1✔
162
        return false;
×
163
    }
×
164

165
    auto& graph = this->get_parent();
1✔
166
    auto& iedge = *graph.in_edges(*this).begin();
1✔
167
    auto& oedge = *graph.out_edges(*this).begin();
1✔
168
    auto input_type = iedge.result_type(function);
1✔
169
    auto output_type = oedge.result_type(function);
1✔
170

171
    if (!types::is_floating_point(input_type->primitive_type()) || !types::is_unsigned(output_type->primitive_type())) {
1✔
172
        return false;
×
173
    }
×
174

175
    return true;
1✔
176
}
1✔
177

178
bool Tasklet::is_fptosi(const Function& function) const {
1✔
179
    if (!this->is_assign()) {
1✔
180
        return false;
×
181
    }
×
182

183
    auto& graph = this->get_parent();
1✔
184
    auto& iedge = *graph.in_edges(*this).begin();
1✔
185
    auto& oedge = *graph.out_edges(*this).begin();
1✔
186
    auto input_type = iedge.result_type(function);
1✔
187
    auto output_type = oedge.result_type(function);
1✔
188

189
    if (!types::is_floating_point(input_type->primitive_type()) || !types::is_signed(output_type->primitive_type())) {
1✔
190
        return false;
×
191
    }
×
192

193
    return true;
1✔
194
}
1✔
195

196
bool Tasklet::is_uitofp(const Function& function) const {
1✔
197
    if (!this->is_assign()) {
1✔
198
        return false;
×
199
    }
×
200

201
    auto& graph = this->get_parent();
1✔
202
    auto& iedge = *graph.in_edges(*this).begin();
1✔
203
    auto& oedge = *graph.out_edges(*this).begin();
1✔
204
    auto input_type = iedge.result_type(function);
1✔
205
    auto output_type = oedge.result_type(function);
1✔
206

207
    if (!types::is_unsigned(input_type->primitive_type()) || !types::is_floating_point(output_type->primitive_type())) {
1✔
208
        return false;
×
209
    }
×
210

211
    return true;
1✔
212
}
1✔
213

214
bool Tasklet::is_sitofp(const Function& function) const {
1✔
215
    if (!this->is_assign()) {
1✔
216
        return false;
×
217
    }
×
218

219
    auto& graph = this->get_parent();
1✔
220
    auto& iedge = *graph.in_edges(*this).begin();
1✔
221
    auto& oedge = *graph.out_edges(*this).begin();
1✔
222
    auto input_type = iedge.result_type(function);
1✔
223
    auto output_type = oedge.result_type(function);
1✔
224

225
    if (!types::is_signed(input_type->primitive_type()) || !types::is_floating_point(output_type->primitive_type())) {
1✔
226
        return false;
×
227
    }
×
228

229
    return true;
1✔
230
}
1✔
231

232
bool Tasklet::is_fpext(const Function& function) const {
1✔
233
    if (!this->is_assign()) {
1✔
234
        return false;
×
235
    }
×
236

237
    auto& graph = this->get_parent();
1✔
238
    auto& iedge = *graph.in_edges(*this).begin();
1✔
239
    auto& oedge = *graph.out_edges(*this).begin();
1✔
240
    auto input_type = iedge.result_type(function);
1✔
241
    auto output_type = oedge.result_type(function);
1✔
242

243
    if (!types::is_floating_point(input_type->primitive_type()) ||
1✔
244
        !types::is_floating_point(output_type->primitive_type())) {
1✔
245
        return false;
×
246
    }
×
247
    if (types::bit_width(output_type->primitive_type()) <= types::bit_width(input_type->primitive_type())) {
1✔
248
        return false;
×
249
    }
×
250

251
    return true;
1✔
252
}
1✔
253

254
bool Tasklet::is_fptrunc(const Function& function) const {
1✔
255
    if (!this->is_assign()) {
1✔
256
        return false;
×
257
    }
×
258

259
    auto& graph = this->get_parent();
1✔
260
    auto& iedge = *graph.in_edges(*this).begin();
1✔
261
    auto& oedge = *graph.out_edges(*this).begin();
1✔
262
    auto input_type = iedge.result_type(function);
1✔
263
    auto output_type = oedge.result_type(function);
1✔
264

265
    if (!types::is_floating_point(input_type->primitive_type()) ||
1✔
266
        !types::is_floating_point(output_type->primitive_type())) {
1✔
267
        return false;
×
268
    }
×
269
    if (types::bit_width(output_type->primitive_type()) >= types::bit_width(input_type->primitive_type())) {
1✔
270
        return false;
×
271
    }
×
272

273
    return true;
1✔
274
}
1✔
275

276
const std::string& Tasklet::output() const { return this->outputs_[0]; };
153✔
277

278
std::unique_ptr<DataFlowNode> Tasklet::clone(size_t element_id, const graph::Vertex vertex, DataFlowGraph& parent)
279
    const {
20✔
280
    return std::unique_ptr<Tasklet>(
20✔
281
        new Tasklet(element_id, this->debug_info_, vertex, parent, this->code_, this->outputs_.at(0), this->inputs_)
20✔
282
    );
20✔
283
};
20✔
284

285
void Tasklet::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {}
191✔
286

287
EdgeRemoveOption Tasklet::can_remove_out_edge(const data_flow::DataFlowGraph& graph, const Memlet* memlet) const {
9✔
288
    if (graph.out_edges_for_connector(*this, memlet->src_conn()).size() > 1) {
9✔
NEW
289
        return EdgeRemoveOption::Trivially;
×
290
    } else {
9✔
291
        // trick to allow removal of Tasklets, without having general mark-and-sweep logic to checkl tha that all
292
        // outputs are
293
        return EdgeRemoveOption::RemoveNodeAfter;
9✔
294
    }
9✔
295
}
9✔
296

297
} // namespace data_flow
298
} // 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