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

daisytuner / docc / 28182474503

25 Jun 2026 02:25PM UTC coverage: 61.9% (+0.3%) from 61.644%
28182474503

Pull #812

github

web-flow
Merge aafbca6ca into fe9abd1cb
Pull Request #812: Define Buffer reuse pass for offloaded containers

290 of 317 new or added lines in 2 files covered. (91.48%)

1 existing line in 1 file now uncovered.

38657 of 62451 relevant lines covered (61.9%)

987.8 hits per line

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

62.56
/sdfg/src/offloading/data_offloading_node.cpp
1
#include "sdfg/targets/offloading/data_offloading_node.h"
2

3
#include <cstddef>
4
#include <string>
5
#include <vector>
6

7
#include "sdfg/data_flow/data_flow_graph.h"
8
#include "sdfg/data_flow/library_node.h"
9
#include "sdfg/element.h"
10
#include "sdfg/exceptions.h"
11
#include "sdfg/graph/graph.h"
12
#include "sdfg/symbolic/symbolic.h"
13

14
namespace sdfg {
15
namespace offloading {
16

17
constexpr bool dump_offload_node_ids = true;
18

19
DataOffloadingNode::DataOffloadingNode(
20
    size_t element_id,
21
    const DebugInfo& debug_info,
22
    const graph::Vertex vertex,
23
    data_flow::DataFlowGraph& parent,
24
    const data_flow::LibraryNodeCode code,
25
    const std::vector<std::string>& outputs,
26
    const std::vector<std::string>& inputs,
27
    DataTransferDirection transfer_direction,
28
    BufferLifecycle buffer_lifecycle,
29
    symbolic::Expression size
30
)
31
    : data_flow::LibraryNode(
304✔
32
          element_id, debug_info, vertex, parent, code, outputs, inputs, true, data_flow::ImplementationType_NONE
304✔
33
      ),
304✔
34
      transfer_direction_(transfer_direction), buffer_lifecycle_(buffer_lifecycle), size_(std::move(size)) {}
304✔
35

36
DataOffloadingNode::DataOffloadingNode(
37
    size_t element_id,
38
    const DebugInfo& debug_info,
39
    const graph::Vertex vertex,
40
    data_flow::DataFlowGraph& parent,
41
    const data_flow::LibraryNodeCode code,
42
    DataTransferDirection transfer_direction,
43
    BufferLifecycle buffer_lifecycle,
44
    symbolic::Expression size
45
)
46
    : DataOffloadingNode(
304✔
47
          element_id,
304✔
48
          debug_info,
304✔
49
          vertex,
304✔
50
          parent,
304✔
51
          code,
304✔
52
          output_conns(transfer_direction, buffer_lifecycle),
304✔
53
          input_conns(transfer_direction, buffer_lifecycle),
304✔
54
          transfer_direction,
304✔
55
          buffer_lifecycle,
304✔
56
          size
304✔
57
      ) {}
304✔
58

59
std::vector<std::string> DataOffloadingNode::
60
    output_conns(DataTransferDirection transfer_direction, BufferLifecycle buffer_lifecycle) {
304✔
61
    if (is_ALLOC(buffer_lifecycle)) {
304✔
62
        return {"_dev"};
146✔
63
    } else {
158✔
64
        return {};
158✔
65
    }
158✔
66
}
304✔
67

68
std::vector<std::string> DataOffloadingNode::
69
    input_conns(DataTransferDirection transfer_direction, BufferLifecycle buffer_lifecycle) {
304✔
70
    if (is_H2D(transfer_direction) && is_ALLOC(buffer_lifecycle)) {
304✔
71
        return {"_hst"};
58✔
72
    } else if (!is_NONE(transfer_direction)) {
246✔
73
        return {"_hst", "_dev"};
58✔
74
    } else if (is_FREE(buffer_lifecycle)) {
188✔
75
        return {"_dev"};
100✔
76
    } else {
100✔
77
        return {};
88✔
78
    }
88✔
79
}
304✔
80

81
int DataOffloadingNode::dev_ptr_input_idx() const {
51✔
82
    if (transfer_direction_ == DataTransferDirection::NONE && buffer_lifecycle_ == BufferLifecycle::FREE) {
51✔
83
        return 0;
6✔
84
    } else if (transfer_direction_ == DataTransferDirection::D2H) {
45✔
85
        return 1;
28✔
86
    } else if (transfer_direction_ == DataTransferDirection::H2D && buffer_lifecycle_ != BufferLifecycle::ALLOC) {
28✔
87
        return 1;
2✔
88
    } else {
15✔
89
        return -1;
15✔
90
    }
15✔
91
}
51✔
92

93
int DataOffloadingNode::host_ptr_input_idx() const {
56✔
94
    if (transfer_direction_ != DataTransferDirection::NONE) {
56✔
95
        return 0;
52✔
96
    } else {
52✔
97
        return -1;
4✔
98
    }
4✔
99
}
56✔
100

101
int DataOffloadingNode::dev_ptr_output_idx() const {
7✔
102
    if (buffer_lifecycle_ == BufferLifecycle::ALLOC) {
7✔
103
        return 0;
6✔
104
    } else {
6✔
105
        return -1;
1✔
106
    }
1✔
107
}
7✔
108

109
const std::string& DataOffloadingNode::dev_in_conn() const { return inputs_.at(dev_ptr_input_idx()); }
2✔
110

111
const std::string& DataOffloadingNode::dev_out_conn() const { return outputs_.at(dev_ptr_output_idx()); }
4✔
112

113
const std::string& DataOffloadingNode::host_in_conn() const { return inputs_.at(host_ptr_input_idx()); }
×
114

115
DataTransferDirection DataOffloadingNode::transfer_direction() const { return this->transfer_direction_; }
1,158✔
116

117
BufferLifecycle DataOffloadingNode::buffer_lifecycle() const { return this->buffer_lifecycle_; }
929✔
118

119
const symbolic::Expression DataOffloadingNode::size() const { return this->size_; }
682✔
120

121
void DataOffloadingNode::set_size(const symbolic::Expression& size) { this->size_ = size; }
20✔
122

UNCOV
123
const symbolic::Expression DataOffloadingNode::alloc_size() const { return this->size(); }
×
124

125
symbolic::SymbolSet DataOffloadingNode::symbols() const {
260✔
126
    if (this->size().is_null()) {
260✔
127
        return {};
×
128
    } else {
260✔
129
        return symbolic::atoms(this->size());
260✔
130
    }
260✔
131
}
260✔
132

133
void DataOffloadingNode::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
143✔
134
    if (!this->size_.is_null()) {
143✔
135
        this->size_ = symbolic::subs(this->size_, old_expression, new_expression);
143✔
136
    }
143✔
137
}
143✔
138

139
std::string DataOffloadingNode::toStr() const {
×
140
    std::string direction, lifecycle;
×
141
    switch (this->transfer_direction()) {
×
142
        case DataTransferDirection::D2H:
×
143
            direction = " D2H";
×
144
            break;
×
145
        case DataTransferDirection::H2D:
×
146
            direction = " H2D";
×
147
            break;
×
148
        default:
×
149
            direction = " NONE";
×
150
            break;
×
151
    }
×
152
    switch (this->buffer_lifecycle()) {
×
153
        case BufferLifecycle::FREE:
×
154
            lifecycle = " FREE";
×
155
            break;
×
156
        case BufferLifecycle::ALLOC:
×
157
            lifecycle = " ALLOC";
×
158
            break;
×
159
        default:
×
160
            lifecycle = " NO_CHANGE";
×
161
            break;
×
162
    }
×
163
    std::string res = std::string(this->code_.value());
×
164
    if (dump_offload_node_ids) {
×
165
        res += " #" + std::to_string(element_id_);
×
166
    }
×
167
    res += direction + lifecycle;
×
168
    return res;
×
169
}
×
170

171
symbolic::Expression DataOffloadingNode::flop() const { return symbolic::zero(); }
×
172

173
bool DataOffloadingNode::is_compatible_with(const DataOffloadingNode& other) const {
22✔
174
    if (code() != other.code()) {
22✔
175
        return false;
×
176
    }
×
177
    if (!symbolic::null_safe_eq(size(), other.size())) {
22✔
178
        return false;
×
179
    }
×
180
    return true;
22✔
181
}
22✔
182

183
bool DataOffloadingNode::redundant_with(const DataOffloadingNode& other) const {
×
184
    if (!is_compatible_with(other)) {
×
185
        return false;
×
186
    }
×
187
    if ((static_cast<int8_t>(transfer_direction()) + static_cast<int8_t>(other.transfer_direction())) != 0) {
×
188
        return false; // not the inverse
×
189
    }
×
190
    if ((static_cast<int8_t>(buffer_lifecycle()) + static_cast<int8_t>(other.buffer_lifecycle())) != 0) {
×
191
        return false;
×
192
    }
×
193

194
    return true; // add more checks in sub-classes
×
195
}
×
196

197
bool DataOffloadingNode::equal_with(const DataOffloadingNode& other) const {
6✔
198
    if (!is_compatible_with(other)) {
6✔
199
        return false;
×
200
    }
×
201
    if (this->transfer_direction() != other.transfer_direction()) {
6✔
202
        return false;
×
203
    }
×
204
    if (this->buffer_lifecycle() != other.buffer_lifecycle()) {
6✔
205
        return false;
×
206
    }
×
207

208
    return true; // add more checks in sub-classes
6✔
209
}
6✔
210

211
bool DataOffloadingNode::is_d2h() const { return is_D2H(this->transfer_direction()); }
552✔
212

213
bool DataOffloadingNode::is_h2d() const { return is_H2D(this->transfer_direction()); }
544✔
214

215
bool DataOffloadingNode::has_transfer() const { return this->is_d2h() || this->is_h2d(); }
153✔
216

217
bool DataOffloadingNode::is_free() const { return is_FREE(this->buffer_lifecycle()); }
351✔
218

219
bool DataOffloadingNode::is_alloc() const { return is_ALLOC(this->buffer_lifecycle()); }
516✔
220

221
void DataOffloadingNode::remove_h2d() {
3✔
222
    if (this->is_h2d()) {
3✔
223
        if (!this->is_alloc()) {
3✔
224
            throw InvalidSDFGException("DataOffloadingNode: Tried removing h2d but node has no other purpose");
×
225
        }
×
226
        this->transfer_direction_ = DataTransferDirection::NONE;
3✔
227
        this->inputs_.erase(this->inputs_.begin()); // Standard nodes only have one, others need to override
3✔
228
    }
3✔
229
}
3✔
230

231
data_flow::PointerAccessType DataOffloadingNode::pointer_access_type(int input_idx) const {
5✔
232
    if (is_h2d() && input_idx == host_ptr_input_idx()) {
5✔
233
        return data_flow::PointerAccessMeta::create_read_only(size_, true);
3✔
234
    } else if (is_h2d() && !is_alloc() && input_idx == dev_ptr_input_idx()) {
3✔
235
        return data_flow::PointerAccessMeta::create_full_write_only(size_, true);
×
236
    } else if (is_d2h() && input_idx == dev_ptr_input_idx()) {
2✔
237
        return data_flow::PointerAccessMeta::create_read_only(size_, true);
×
238
    } else if (is_d2h() && input_idx == host_ptr_input_idx()) {
2✔
239
        return data_flow::PointerAccessMeta::create_full_write_only(size_, true);
2✔
240
    } else if (is_d2h() && is_free() && input_idx == dev_ptr_input_idx()) {
2✔
241
        return data_flow::PointerAccessMeta::create_invalidate();
×
242
    } else if (is_d2h() && !is_free() && input_idx == 1) {
×
243
        return data_flow::PointerAccessMeta::create_read_only(size_, true);
×
244
    } else {
×
245
        return LibraryNode::pointer_access_type(input_idx);
×
246
    }
×
247
}
5✔
248

249
void DataOffloadingNode::remove_free() {
3✔
250
    if (this->is_free()) {
3✔
251
        if (!this->has_transfer()) {
3✔
252
            throw InvalidSDFGException("DataOffloadingNode: Tried removing free but no data transfer direction present"
×
253
            );
×
254
        }
×
255
        this->buffer_lifecycle_ = BufferLifecycle::NO_CHANGE;
3✔
256
    }
3✔
257
}
3✔
258

259
void DataOffloadingNode::remove_d2h() {
4✔
260
    if (this->is_d2h()) {
4✔
261
        if (!this->is_free()) {
4✔
262
            throw InvalidSDFGException("DataOffloadingNode: Tried removing d2h but node has no other purpose");
×
263
        }
×
264
        this->transfer_direction_ = DataTransferDirection::NONE;
4✔
265
        this->inputs_.erase(this->inputs_.begin());
4✔
266
    }
4✔
267
}
4✔
268

269
data_flow::EdgeRemoveOption DataOffloadingNode::
270
    can_remove_out_edge(const data_flow::DataFlowGraph& graph, const data_flow::Memlet* memlet) const {
×
271
    if (graph.out_edges_for_connector(*this, memlet->src_conn()).size() > 1) {
×
272
        return data_flow::EdgeRemoveOption::Trivially;
×
273
    } else if (is_alloc() && outputs_.size() == 1 && memlet->src_conn() == outputs_.at(0)) {
×
274
        // the node in its entirety is dead if it the alloc is not needed
275
        return data_flow::EdgeRemoveOption::RemoveNodeAfter;
×
276
    } else {
×
277
        return data_flow::EdgeRemoveOption::NotRemovable;
×
278
    }
×
279
}
×
280

281
bool DataOffloadingNode::update_edge_removed(const std::string& out_conn) { return false; }
×
282

283
data_flow::EdgeRemoveOption DataOffloadingNode::
284
    can_remove_in_edge(const data_flow::DataFlowGraph& graph, const data_flow::Memlet* memlet) const {
1✔
285
    if (is_h2d() && is_alloc() && memlet->dst_conn() == inputs_.at(host_ptr_input_idx())) {
1✔
286
        return data_flow::EdgeRemoveOption::RequiresUpdate;
×
287
    } else if (is_d2h() && is_NO_CHANGE(this->buffer_lifecycle_) &&
1✔
288
               memlet->dst_conn() == inputs_.at(host_ptr_input_idx())) {
1✔
289
        return data_flow::EdgeRemoveOption::RemoveNodeAfter;
1✔
290
    } else {
1✔
291
        return data_flow::EdgeRemoveOption::NotRemovable;
×
292
    }
×
293
}
1✔
294

295
nlohmann::json DataOffloadingNodeSerializer::serialize(const sdfg::data_flow::LibraryNode& library_node) {
4✔
296
    const auto& node = static_cast<const DataOffloadingNode&>(library_node);
4✔
297
    nlohmann::json j;
4✔
298

299
    // Library node properties
300
    j["code"] = std::string(library_node.code().value());
4✔
301

302
    // Offloading node properties
303
    sdfg::serializer::JSONSerializer serializer;
4✔
304
    if (node.size().is_null()) {
4✔
305
        j["size"] = nlohmann::json::value_t::null;
1✔
306
    } else {
3✔
307
        j["size"] = serializer.expression(node.size());
3✔
308
    }
3✔
309
    j["transfer_direction"] = static_cast<int8_t>(node.transfer_direction());
4✔
310
    j["buffer_lifecycle"] = static_cast<int8_t>(node.buffer_lifecycle());
4✔
311

312
    return j;
4✔
313
}
4✔
314

315
} // namespace offloading
316
} // 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