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

daisytuner / docc / 27500480732

14 Jun 2026 01:33PM UTC coverage: 61.642% (+0.1%) from 61.54%
27500480732

Pull #764

github

web-flow
Merge 3432db11b into 6b2e310be
Pull Request #764: Deprecates monolithic GPU transformations in favor of composable transformations

202 of 224 new or added lines in 2 files covered. (90.18%)

81 existing lines in 6 files now uncovered.

36559 of 59309 relevant lines covered (61.64%)

1132.66 hits per line

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

69.49
/opt/src/transformations/offloading/offload_transform.cpp
1
#include "sdfg/transformations/offloading/offload_transform.h"
2

3
#include <map>
4
#include <string>
5

6
#include "sdfg/analysis/type_analysis.h"
7
#include "sdfg/data_flow/access_node.h"
8
#include "sdfg/structured_control_flow/block.h"
9
#include "sdfg/structured_control_flow/if_else.h"
10
#include "sdfg/structured_control_flow/map.h"
11
#include "sdfg/symbolic/symbolic.h"
12

13
#include "sdfg/data_flow/library_node.h"
14
#include "sdfg/optimization_report/pass_report_consumer.h"
15
#include "sdfg/types/utils.h"
16
#include "sdfg/visitor/immutable_structured_sdfg_visitor.h"
17
#include "symengine/symengine_rcp.h"
18

19
namespace sdfg {
20
namespace transformations {
21

22
class SideEffectFinder : public visitor::ImmutableStructuredSDFGVisitor {
23
private:
24
    structured_control_flow::Map& map_;
25

26
public:
27
    SideEffectFinder(StructuredSDFG& sdfg, analysis::AnalysisManager& analysis_manager, structured_control_flow::Map& map)
28
        : visitor::ImmutableStructuredSDFGVisitor(sdfg, analysis_manager), map_(map) {}
8✔
29

30
    bool visit() override { return visit_internal(map_.root()); }
8✔
31

32
    bool accept(structured_control_flow::Block& node) override {
9✔
33
        for (const auto& lib_node : node.dataflow().library_nodes()) {
9✔
34
            if (lib_node->side_effect()) {
×
35
                return true;
×
36
            }
×
37
        }
×
38
        return false;
9✔
39
    }
9✔
40
};
41

42
OffloadTransform::OffloadTransform(structured_control_flow::Map& map, bool allow_dynamic_sizes)
43
    : map_(map), allow_dynamic_sizes_(allow_dynamic_sizes) {}
14✔
44

45

46
bool OffloadTransform::can_be_applied(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
8✔
47
    auto& sdfg = builder.subject();
8✔
48

49
    auto& arguments_analysis = analysis_manager.get<analysis::ArgumentsAnalysis>();
8✔
50

51
    if (!arguments_analysis.inferred_types(analysis_manager, this->map_)) {
8✔
52
        if (report_) report_->transform_impossible(this, "unranged args");
×
53
        DEBUG_PRINTLN("Cannot apply transform: argument types not inferred");
×
54
        return false;
×
55
    }
×
56
    auto& arguments = arguments_analysis.arguments(analysis_manager, this->map_);
8✔
57

58
    // Criterion: arg Data Types must be continuous
59
    for (auto& [argument, meta] : arguments) {
17✔
60
        auto base_type = analysis::TypeAnalysis(sdfg, &map_, analysis_manager).get_outer_type(argument);
17✔
61
        if (base_type == nullptr) {
17✔
62
            if (report_) report_->transform_impossible(this, "cannot infer type");
×
63
            DEBUG_PRINTLN("Cannot apply transform: argument type cannot be inferred");
×
64
            return false;
×
65
        }
×
66
        if (!types::is_contiguous_type(*base_type, sdfg)) {
17✔
67
            if (report_) report_->transform_impossible(this, "type is not contiguous");
×
68
            DEBUG_PRINTLN("Cannot apply transform: argument type is not contiguous");
×
69
            return false;
×
70
        }
×
71
        if (meta.is_scalar && meta.is_output) {
17✔
72
            if (report_) report_->transform_impossible(this, "scalar output");
×
73
            DEBUG_PRINTLN("Cannot apply transform: map writes to scalar argument");
×
74
            return false;
×
75
        }
×
76
    }
17✔
77

78
    // Criterion: Map must start at 0
79
    if (!symbolic::eq(this->map_.init(), symbolic::zero())) {
8✔
80
        if (report_) report_->transform_impossible(this, "non zero start");
×
81
        DEBUG_PRINTLN("Cannot apply transform: map does not start at zero");
×
82
        return false;
×
83
    }
×
84

85
    // Criterion: Map cannot write to scalar arguments
86
    for (auto& [argument, meta] : arguments) {
17✔
87
        if (meta.is_scalar && meta.is_output) {
17✔
UNCOV
88
            if (report_) report_->transform_impossible(this, "scalar output");
×
UNCOV
89
            DEBUG_PRINTLN("Cannot apply transform: map writes to scalar argument");
×
UNCOV
90
            return false;
×
UNCOV
91
        }
×
92
    }
17✔
93

94
    if (!arguments_analysis.argument_size_known(analysis_manager, this->map_, allow_dynamic_sizes_)) {
8✔
95
        if (report_) report_->transform_impossible(this, "args not understood");
×
UNCOV
96
        DEBUG_PRINTLN("Cannot apply transform: argument sizes not known");
×
UNCOV
97
        return false;
×
UNCOV
98
    }
×
99

100
    // Criterion: Map cannot contain function calls with side effects (e.g. library nodes that write to memory)
101
    SideEffectFinder side_effect_finder(sdfg, analysis_manager, this->map_);
8✔
102
    if (side_effect_finder.visit()) {
8✔
UNCOV
103
        if (report_) report_->transform_impossible(this, "side effects");
×
UNCOV
104
        DEBUG_PRINTLN("Cannot apply transform: map contains library nodes with side effects");
×
UNCOV
105
        return false;
×
UNCOV
106
    }
×
107

108
    if (report_) report_->transform_possible(this);
8✔
109
    return true;
8✔
110
}
8✔
111

112
void OffloadTransform::apply(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
6✔
113
    // Schedule
114
    builder.update_schedule_type(this->map_, transformed_schedule_type());
6✔
115

116
    auto& sdfg = builder.subject();
6✔
117

118
    // Identify arguments and locals
119
    auto& arguments_analysis = analysis_manager.get<analysis::ArgumentsAnalysis>();
6✔
120

121
    auto& arguments = arguments_analysis.arguments(analysis_manager, this->map_);
6✔
122
    auto& locals = arguments_analysis.locals(analysis_manager, this->map_);
6✔
123

124
    // Infer subsets for arguments
125
    auto& argument_sizes = arguments_analysis.argument_sizes(analysis_manager, this->map_, allow_dynamic_sizes_);
6✔
126

127
    auto parent_scope = static_cast<structured_control_flow::Sequence*>(this->map_.get_parent());
6✔
128

129
    std::string container_prefix = copy_prefix() + std::to_string(parent_scope->element_id()) + "_";
6✔
130

131
    // Allocate arguments and locals
132
    allocate_locals_on_device_stack(builder, analysis_manager, locals);
6✔
133
    handle_device_setup_and_teardown(builder, arguments, argument_sizes, container_prefix);
6✔
134

135
    // Copy-in arguments to device memory & allocation
136
    for (auto& [argument, meta] : arguments) {
13✔
137
        if (!meta.is_ptr) {
13✔
138
            continue;
6✔
139
        }
6✔
140
        auto argument_device = container_prefix + argument;
7✔
141
        auto& new_block = builder.add_block_before(*parent_scope, this->map_, {}, this->map_.debug_info());
7✔
142
        auto& size = argument_sizes.at(argument);
7✔
143
        copy_to_device_with_allocation(builder, argument, argument_device, size, SymEngine::null, new_block);
7✔
144
    }
7✔
145

146
    update_map_containers(arguments, container_prefix);
6✔
147

148
    // Copy-out arguments to host memory & free
149
    for (auto& [argument, meta] : arguments) {
13✔
150
        if (!meta.is_ptr) {
13✔
151
            continue;
6✔
152
        }
6✔
153
        auto argument_device = container_prefix + argument;
7✔
154
        auto& new_block = builder.add_block_after(*parent_scope, this->map_, {}, this->map_.debug_info());
7✔
155
        auto& size = argument_sizes.at(argument);
7✔
156
        if (!skip_unneeded_d2h_ || meta.is_output) {
7✔
157
            copy_from_device_with_free(builder, new_block, argument, argument_device, size, SymEngine::null);
5✔
158
        } else {
5✔
159
            deallocate_device_arg(builder, new_block, argument_device, size, SymEngine::null);
2✔
160
        }
2✔
161
    }
7✔
162

163
    if (report_) report_->transform_applied(this);
6✔
164
}
6✔
165

166
void OffloadTransform::handle_device_setup_and_teardown(
167
    builder::StructuredSDFGBuilder& builder,
168

169
    const std::map<std::string, analysis::RegionArgument>& arguments,
170
    const std::unordered_map<std::string, symbolic::Expression>& argument_sizes,
171
    std::string prefix
172
) {
6✔
173
    // Add managed buffers for pointer arguments
174
    for (auto& [argument, meta] : arguments) {
13✔
175
        if (!meta.is_ptr || builder.subject().exists(prefix + argument)) {
13✔
176
            continue;
7✔
177
        }
7✔
178
        auto argument_device = prefix + argument;
6✔
179

180
        auto arg_size = argument_sizes.at(argument);
6✔
181

182
        add_device_buffer(builder, argument, argument_device, arg_size);
6✔
183
    }
6✔
184
}
6✔
185

186
void OffloadTransform::
187
    update_map_containers(const std::map<std::string, analysis::RegionArgument>& arguments, std::string prefix) {
6✔
188
    for (auto& [argument, meta] : arguments) {
13✔
189
        if (meta.is_ptr) {
13✔
190
            auto argument_device = prefix + argument;
7✔
191
            this->map_.replace(symbolic::symbol(argument), symbolic::symbol(argument_device));
7✔
192
        }
7✔
193
    }
13✔
194
}
6✔
195

196
} // namespace transformations
197
} // 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