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

daisytuner / docc / 22949872003

11 Mar 2026 11:15AM UTC coverage: 63.681% (-0.9%) from 64.6%
22949872003

push

github

web-flow
Merge pull request #569 from daisytuner/HIPtarget

ROCmTarget

191 of 803 new or added lines in 15 files covered. (23.79%)

3 existing lines in 2 files now uncovered.

24700 of 38787 relevant lines covered (63.68%)

370.4 hits per line

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

50.89
/opt/src/passes/offloading/data_transfer_minimization_pass.cpp
1
#include "sdfg/passes/offloading/data_transfer_minimization_pass.h"
2

3
#include <cstddef>
4
#include <string>
5
#include <unordered_set>
6
#include <utility>
7

8
#include "sdfg/analysis/analysis.h"
9
#include "sdfg/analysis/scope_analysis.h"
10
#include "sdfg/analysis/users.h"
11
#include "sdfg/data_flow/access_node.h"
12
#include "sdfg/data_flow/code_node.h"
13
#include "sdfg/data_flow/data_flow_graph.h"
14
#include "sdfg/data_flow/library_node.h"
15
#include "sdfg/data_flow/memlet.h"
16
#include "sdfg/data_flow/tasklet.h"
17
#include "sdfg/element.h"
18
#include "sdfg/exceptions.h"
19
#include "sdfg/helpers/helpers.h"
20
#include "sdfg/structured_control_flow/block.h"
21
#include "sdfg/structured_control_flow/control_flow_node.h"
22
#include "sdfg/structured_control_flow/sequence.h"
23
#include "sdfg/symbolic/symbolic.h"
24
#include "sdfg/targets/cuda/cuda_data_offloading_node.h"
25
#include "sdfg/targets/offloading/data_offloading_node.h"
26
#include "sdfg/targets/rocm/rocm_data_offloading_node.h"
27
#include "sdfg/types/pointer.h"
28
#include "sdfg/visitor/structured_sdfg_visitor.h"
29

30
namespace sdfg {
31
namespace passes {
32

33
DataTransferMinimization::
34
    DataTransferMinimization(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager)
35
    : visitor::NonStoppingStructuredSDFGVisitor(builder, analysis_manager) {}
14✔
36

37
bool DataTransferMinimization::visit() {
14✔
38
    DEBUG_PRINTLN("Running DataTransferMinimizationPass on " << this->builder_.subject().name());
14✔
39
    return visitor::NonStoppingStructuredSDFGVisitor::visit();
14✔
40
}
14✔
41

42
bool DataTransferMinimization::accept(structured_control_flow::Sequence& sequence) {
45✔
43
    bool applied = false;
45✔
44
    offloading::DataOffloadingNode* copy_out = nullptr;
45✔
45
    structured_control_flow::Block* copy_out_block = nullptr;
45✔
46
    size_t copy_out_index = 0;
45✔
47

48
    // While a copy-out can be found:
49
    while (copy_out_index < sequence.size()) {
94✔
50
        // Find a new copy-out
51
        for (; copy_out_index < sequence.size(); copy_out_index++) {
105✔
52
            if (auto* block = dynamic_cast<structured_control_flow::Block*>(&sequence.at(copy_out_index).first)) {
64✔
53
                if (block->dataflow().library_nodes().size() == 1 && block->dataflow().tasklets().size() == 0) {
33✔
54
                    auto* libnode = *block->dataflow().library_nodes().begin();
12✔
55
                    if (auto* offloading_node = dynamic_cast<offloading::DataOffloadingNode*>(libnode)) {
12✔
56
                        if (offloading_node->is_d2h()) {
12✔
57
                            copy_out = offloading_node;
8✔
58
                            copy_out_block = block;
8✔
59
                            break;
8✔
60
                        }
8✔
61
                    }
12✔
62
                }
12✔
63
            }
33✔
64
        }
64✔
65

66
        // Find a matching copy-in
67
        size_t i;
49✔
68
        for (i = copy_out_index; i < sequence.size(); i++) {
62✔
69
            // Child must be a block
70
            auto* copy_in_block = dynamic_cast<structured_control_flow::Block*>(&sequence.at(i).first);
15✔
71
            if (!copy_in_block) {
15✔
72
                continue;
1✔
73
            }
1✔
74

75
            // Block must contain exactly one library node
76
            if (copy_in_block->dataflow().library_nodes().size() != 1 ||
14✔
77
                copy_in_block->dataflow().tasklets().size() != 0) {
14✔
78
                continue;
2✔
79
            }
2✔
80

81
            // Library node must be an offloading node
82
            auto* copy_in =
12✔
83
                dynamic_cast<offloading::DataOffloadingNode*>(*copy_in_block->dataflow().library_nodes().begin());
12✔
84
            if (!copy_in) {
12✔
85
                continue;
×
86
            }
×
87

88
            // Offloading node must be a copy-in
89
            if (!copy_in->is_h2d()) {
12✔
90
                continue;
9✔
91
            }
9✔
92

93
            // Copy-in and copy-out must be redundant
94
            if (!copy_out->redundant_with(*copy_in)) {
3✔
95
                continue;
1✔
96
            }
1✔
97

98
            // Get src and dst access nodes for copy-in & -out
99
            auto [copy_out_src, copy_out_dst] = this->get_src_and_dst(copy_out_block->dataflow(), copy_out);
2✔
100
            auto [copy_in_src, copy_in_dst] = this->get_src_and_dst(copy_in_block->dataflow(), copy_in);
2✔
101

102
            // Get the write and read users
103
            auto& users = this->analysis_manager_.get<analysis::Users>();
2✔
104
            analysis::User* write = users.get_user(copy_out_dst->data(), copy_out_dst, analysis::Use::WRITE);
2✔
105
            if (!write) {
2✔
106
                continue;
×
107
            }
×
108
            analysis::User* read = users.get_user(copy_in_src->data(), copy_in_src, analysis::Use::READ);
2✔
109
            if (!read) {
2✔
110
                continue;
×
111
            }
×
112

113
            if (copy_out_dst->data() == copy_in_src->data()) {
2✔
114
                // Ensure that the container is not written between the data transfer nodes
115
                bool used_between = false;
2✔
116
                for (auto* user : users.all_uses_between(*write, *read)) {
2✔
117
                    if (user->container() == copy_out_dst->data() && user->use() != analysis::Use::READ) {
×
118
                        used_between = true;
×
119
                        break;
×
120
                    }
×
121
                }
×
122
                if (used_between) {
2✔
123
                    continue;
×
124
                }
×
125
            } else {
2✔
126
                if (!this->check_container_dependency(
×
127
                        copy_out_block, copy_out_dst->data(), copy_in_block, copy_in_src->data()
×
128
                    )) {
×
129
                    continue;
×
130
                }
×
131
            }
×
132

133
            // Check that the container is not written after the data transfer nodes
134
            bool read_after = false;
2✔
135
            for (auto* user : users.all_uses_after(*write)) {
4✔
136
                if (user->container() == copy_out_dst->data() && user->use() == analysis::Use::READ && user != read) {
4✔
137
                    read_after = true;
1✔
138
                    break;
1✔
139
                }
1✔
140
            }
4✔
141

142
            // Debug output
143
            DEBUG_PRINTLN(
2✔
144
                "  Eliminating " << (read_after ? "(" : "") << "copy-out: " << copy_out_src->data() << " -> "
2✔
145
                                 << copy_out_dst->data() << (read_after ? ")" : "")
2✔
146
                                 << " / copy-in: " << copy_in_src->data() << " -> " << copy_in_dst->data()
2✔
147
            );
2✔
148

149
            // Get all relevant information
150
            std::string copy_out_device_container = copy_out_src->data();
2✔
151
            std::string copy_in_device_container = copy_in_dst->data();
2✔
152
            DebugInfo copy_out_src_debinfo = copy_out_src->debug_info();
2✔
153
            DebugInfo copy_in_dst_debinfo = copy_in_dst->debug_info();
2✔
154

155
            // Remove the data tranfers
156
            if (read_after && copy_out->is_free()) {
2✔
157
                copy_out->remove_free();
1✔
158
            } else if (!read_after) {
1✔
159
                this->builder_.clear_node(*copy_out_block, *copy_out);
1✔
160
            }
1✔
161
            this->builder_.clear_node(*copy_in_block, *copy_in);
2✔
162

163
            // Maps the device pointers if necessary
164
            if (copy_out_device_container != copy_in_device_container) {
2✔
165
                types::Pointer void_pointer;
×
166
                types::Pointer void_pointer_pointer(*void_pointer.clone());
×
167
                auto& in_access =
×
168
                    this->builder_.add_access(*copy_in_block, copy_out_device_container, copy_out_src_debinfo);
×
169
                auto& out_access =
×
170
                    this->builder_.add_access(*copy_in_block, copy_in_device_container, copy_in_dst_debinfo);
×
171
                this->builder_.add_reference_memlet(
×
172
                    *copy_in_block,
×
173
                    in_access,
×
174
                    out_access,
×
175
                    {symbolic::zero()},
×
176
                    void_pointer_pointer,
×
177
                    DebugInfo::merge(copy_out->debug_info(), copy_in->debug_info())
×
178
                );
×
179
            }
×
180

181
            // Invalidate users analysis
182
            this->analysis_manager_.invalidate<analysis::Users>();
2✔
183
            applied = true;
2✔
184
            break;
2✔
185
        }
2✔
186

187
        // Skip if no matching copy-in was found
188
        if (i >= sequence.size()) {
49✔
189
            copy_out_index++;
47✔
190
        }
47✔
191
    }
49✔
192

193
    return applied;
45✔
194
}
45✔
195

196
std::pair<data_flow::AccessNode*, data_flow::AccessNode*> DataTransferMinimization::
197
    get_src_and_dst(data_flow::DataFlowGraph& dfg, offloading::DataOffloadingNode* offloading_node) {
4✔
198
    if (!offloading_node->has_transfer()) {
4✔
199
        throw InvalidSDFGException(
×
200
            "DataTransferMinimization: Cannot get copy access nodes for offloading node without data transfers"
×
201
        );
×
202
    }
×
203
    data_flow::AccessNode *src, *dst;
4✔
204
    if (dynamic_cast<cuda::CUDADataOffloadingNode*>(offloading_node)) {
4✔
205
        src = this->get_in_access(offloading_node, "_src");
4✔
206
        dst = this->get_out_access(offloading_node, "_dst");
4✔
207
    } else if (dynamic_cast<rocm::ROCMDataOffloadingNode*>(offloading_node)) {
4✔
NEW
208
        src = this->get_in_access(offloading_node, "_src");
×
NEW
209
        dst = this->get_out_access(offloading_node, "_dst");
×
210
    } else {
×
211
        throw InvalidSDFGException(
×
212
            "DataTransferMinimization: Unknown offloading node encountered: " + offloading_node->code().value()
×
213
        );
×
214
    }
×
215
    return {src, dst};
4✔
216
}
4✔
217

218
data_flow::AccessNode* DataTransferMinimization::get_in_access(data_flow::CodeNode* node, const std::string& dst_conn) {
4✔
219
    auto& dfg = node->get_parent();
4✔
220
    for (auto& iedge : dfg.in_edges(*node)) {
4✔
221
        if (iedge.dst_conn() == dst_conn) {
4✔
222
            return dynamic_cast<data_flow::AccessNode*>(&iedge.src());
4✔
223
        }
4✔
224
    }
4✔
225
    return nullptr;
×
226
}
4✔
227

228
data_flow::AccessNode* DataTransferMinimization::get_out_access(data_flow::CodeNode* node, const std::string& src_conn) {
4✔
229
    auto& dfg = node->get_parent();
4✔
230
    for (auto& oedge : dfg.out_edges(*node)) {
4✔
231
        if (oedge.src_conn() == src_conn) {
4✔
232
            return static_cast<data_flow::AccessNode*>(&oedge.dst());
4✔
233
        }
4✔
234
    }
4✔
235
    return nullptr;
×
236
}
4✔
237

238
bool DataTransferMinimization::check_container_dependency(
239
    structured_control_flow::Block* copy_out_block,
240
    const std::string& copy_out_container,
241
    structured_control_flow::Block* copy_in_block,
242
    const std::string& copy_in_container
243
) {
×
244
    // Simplification: Assume blocks are in the same sequence
245
    auto& scope_analysis = this->analysis_manager_.get<analysis::ScopeAnalysis>();
×
246
    auto* copy_out_block_parent = scope_analysis.parent_scope(copy_out_block);
×
247
    auto* copy_in_block_parent = scope_analysis.parent_scope(copy_in_block);
×
248
    auto* sequence = dynamic_cast<structured_control_flow::Sequence*>(copy_out_block_parent);
×
249
    if (copy_out_block_parent != copy_in_block_parent || !sequence) {
×
250
        return false;
×
251
    }
×
252

253
    std::unordered_set<std::string> copy_out_container_captures, copy_in_container_parts;
×
254
    size_t start = sequence->index(*copy_out_block);
×
255
    size_t stop = sequence->index(*copy_in_block);
×
256
    for (size_t i = start + 1; i < stop; i++) {
×
257
        auto* block = dynamic_cast<structured_control_flow::Block*>(&sequence->at(i).first);
×
258
        if (!block) {
×
259
            continue;
×
260
        }
×
261

262
        auto& dfg = block->dataflow();
×
263
        for (auto* access_node : dfg.data_nodes()) {
×
264
            if (access_node->data() == copy_in_container) {
×
265
                // Only allow constant assignments
266
                for (auto& iedge : dfg.in_edges(*access_node)) {
×
267
                    auto* tasklet = dynamic_cast<data_flow::Tasklet*>(&iedge.src());
×
268
                    if (!tasklet || tasklet->code() != data_flow::TaskletCode::assign) {
×
269
                        continue;
×
270
                    }
×
271

272
                    auto& iedge2 = *dfg.in_edges(*tasklet).begin();
×
273
                    if (!dynamic_cast<data_flow::ConstantNode*>(&iedge2.src())) {
×
274
                        return false;
×
275
                    }
×
276
                }
×
277

278
                // Collect H2D container parts
279
                for (auto& oedge : dfg.out_edges(*access_node)) {
×
280
                    if (oedge.type() != data_flow::MemletType::Reference) {
×
281
                        continue;
×
282
                    }
×
283

284
                    auto* access_node2 = dynamic_cast<data_flow::AccessNode*>(&oedge.dst());
×
285
                    if (!access_node2) {
×
286
                        continue;
×
287
                    }
×
288

289
                    copy_in_container_parts.insert(access_node2->data());
×
290
                }
×
291
            } else if (access_node->data() == copy_out_container) {
×
292
                // Collect D2H container captures
293
                for (auto& oedge : dfg.out_edges(*access_node)) {
×
294
                    if (oedge.type() != data_flow::MemletType::Dereference_Dst) {
×
295
                        continue;
×
296
                    }
×
297

298
                    auto* access_node2 = dynamic_cast<data_flow::AccessNode*>(&oedge.dst());
×
299
                    if (!access_node2) {
×
300
                        continue;
×
301
                    }
×
302

303
                    copy_out_container_captures.insert(access_node2->data());
×
304
                }
×
305
            }
×
306
        }
×
307
    }
×
308

309
    // Find all matches between captures and parts
310
    size_t matches = 0;
×
311
    for (auto& capture : copy_out_container_captures) {
×
312
        for (auto& part : copy_in_container_parts) {
×
313
            if (capture == part) {
×
314
                matches++;
×
315
            }
×
316
        }
×
317
    }
×
318

319
    return (matches == 1);
×
320
}
×
321

322
} // namespace passes
323
} // 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