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

daisytuner / docc / 26463753889

26 May 2026 05:18PM UTC coverage: 60.864% (-0.02%) from 60.886%
26463753889

Pull #719

github

web-flow
Merge 0b90ddd88 into 707dadcf8
Pull Request #719: Libnode ptr edges

961 of 1749 new or added lines in 52 files covered. (54.95%)

90 existing lines in 29 files now uncovered.

35222 of 57870 relevant lines covered (60.86%)

11043.61 hits per line

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

62.39
/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(
100✔
32
          element_id, debug_info, vertex, parent, code, outputs, inputs, true, data_flow::ImplementationType_NONE
100✔
33
      ),
100✔
34
      transfer_direction_(transfer_direction), buffer_lifecycle_(buffer_lifecycle), size_(std::move(size)) {}
100✔
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(
100✔
47
          element_id,
100✔
48
          debug_info,
100✔
49
          vertex,
100✔
50
          parent,
100✔
51
          code,
100✔
52
          output_conns(transfer_direction, buffer_lifecycle),
100✔
53
          input_conns(transfer_direction, buffer_lifecycle),
100✔
54
          transfer_direction,
100✔
55
          buffer_lifecycle,
100✔
56
          size
100✔
57
      ) {}
100✔
58

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

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

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

93
int DataOffloadingNode::host_ptr_input_idx() const {
54✔
94
    if (transfer_direction_ != DataTransferDirection::NONE) {
54✔
95
        return 0;
50✔
96
    } else {
50✔
97
        return -1;
4✔
98
    }
4✔
99
}
54✔
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

NEW
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_; }
266✔
116

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

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

121
const symbolic::Expression DataOffloadingNode::alloc_size() const { return this->size(); }
×
122

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

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

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

169
symbolic::Expression DataOffloadingNode::flop() const { return symbolic::zero(); }
×
170

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

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

192
    return true; // add more checks in sub-classes
×
193
}
×
194

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

206
    return true; // add more checks in sub-classes
6✔
207
}
6✔
208

209
bool DataOffloadingNode::is_d2h() const { return is_D2H(this->transfer_direction()); }
106✔
210

211
bool DataOffloadingNode::is_h2d() const { return is_H2D(this->transfer_direction()); }
98✔
212

213
bool DataOffloadingNode::has_transfer() const { return this->is_d2h() || this->is_h2d(); }
3✔
214

215
bool DataOffloadingNode::is_free() const { return is_FREE(this->buffer_lifecycle()); }
53✔
216

217
bool DataOffloadingNode::is_alloc() const { return is_ALLOC(this->buffer_lifecycle()); }
43✔
218

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

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

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

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

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

NEW
279
bool DataOffloadingNode::update_edge_removed(const std::string& out_conn) { return false; }
×
280

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

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

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

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

310
    return j;
4✔
311
}
4✔
312

313
} // namespace offloading
314
} // 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