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

daisytuner / docc / 30542544763

30 Jul 2026 12:26PM UTC coverage: 64.777% (+0.04%) from 64.738%
30542544763

Pull #912

github

web-flow
Merge f7e2d82b1 into 4a519a416
Pull Request #912: Disallow Imperfect Collapse and Nested Parallelism with Data Dependencies

82 of 93 new or added lines in 4 files covered. (88.17%)

44430 of 68589 relevant lines covered (64.78%)

720.08 hits per line

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

84.97
/opt/src/targets/gpu/gpu_map_utils.cpp
1
#include "sdfg/targets/gpu/gpu_map_utils.h"
2

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

6
#include "sdfg/analysis/arguments_analysis.h"
7
#include "sdfg/analysis/assumptions_analysis.h"
8
#include "sdfg/analysis/loop_analysis.h"
9
#include "sdfg/analysis/users.h"
10
#include "sdfg/structured_control_flow/sequence.h"
11
#include "sdfg/targets/cuda/cuda.h"
12
#include "sdfg/targets/rocm/rocm.h"
13

14
namespace sdfg {
15
namespace gpu {
16

17
template<typename ScheduleT>
18
symbolic::Expression find_nested_gpu_blocksize(
19
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
20
) {
24✔
21
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
24✔
22
    auto loops = loop_analysis.descendants(&node);
24✔
23
    loops.insert(&node);
24✔
24

25
    // Check for repeated dimensions in loop tree paths
26
    auto loop_tree_paths = loop_analysis.loop_tree_paths(&node);
24✔
27
    for (auto& path : loop_tree_paths) {
24✔
28
        bool foundX = false;
24✔
29
        bool foundY = false;
24✔
30
        bool foundZ = false;
24✔
31
        for (auto& loop : path) {
39✔
32
            if (auto map = dyn_cast<structured_control_flow::Map*>(loop)) {
39✔
33
                if (map->schedule_type().value() == ScheduleT::value()) {
39✔
34
                    auto dim = ScheduleT::dimension(map->schedule_type());
39✔
35
                    if (dim == GPUDimension::X) {
39✔
36
                        if (foundX) {
12✔
37
                            throw InvalidSDFGException("Nested map in GPU kernel has repeated X dimension");
×
38
                        }
×
39
                        foundX = true;
12✔
40
                    } else if (dim == GPUDimension::Y) {
27✔
41
                        if (foundY) {
12✔
42
                            throw InvalidSDFGException("Nested map in GPU kernel has repeated Y dimension");
×
43
                        }
×
44
                        foundY = true;
12✔
45
                    } else if (dim == GPUDimension::Z) {
15✔
46
                        if (foundZ) {
15✔
47
                            throw InvalidSDFGException("Nested map in GPU kernel has repeated Z dimension");
×
48
                        }
×
49
                        foundZ = true;
15✔
50
                    }
15✔
51
                }
39✔
52
            }
39✔
53
        }
39✔
54
    }
24✔
55

56
    // Find block size for the requested dimension
57
    for (auto loop : loops) {
33✔
58
        if (auto map = dyn_cast<structured_control_flow::Map*>(loop)) {
33✔
59
            if (map->schedule_type().value() != ScheduleT::value() &&
33✔
60
                map->schedule_type().value() != structured_control_flow::ScheduleType_Sequential::value()) {
33✔
61
                throw InvalidSDFGException("Nested map in GPU kernel not GPU or Sequential");
×
62
            }
×
63

64
            if (map->schedule_type().value() == structured_control_flow::ScheduleType_Sequential::value()) {
33✔
65
                continue;
×
66
            }
×
67

68
            if (ScheduleT::dimension(map->schedule_type()) == dimension) {
33✔
69
                return ScheduleT::block_size(map->schedule_type());
13✔
70
            }
13✔
71
        }
33✔
72
    }
33✔
73
    return symbolic::one();
11✔
74
}
24✔
75

76
template<typename ScheduleT>
77
symbolic::Expression find_nested_gpu_iterations(
78
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
79
) {
24✔
80
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
24✔
81
    auto loops = loop_analysis.descendants(&node);
24✔
82
    loops.insert(&node);
24✔
83

84
    symbolic::Expression max_num_iterations = symbolic::one();
24✔
85

86
    for (auto loop : loops) {
39✔
87
        if (auto map = dyn_cast<structured_control_flow::Map*>(loop)) {
39✔
88
            if (map->schedule_type().value() != ScheduleT::value() &&
39✔
89
                map->schedule_type().value() != structured_control_flow::ScheduleType_Sequential::value()) {
39✔
90
                throw InvalidSDFGException("Nested map in GPU kernel not GPU or Sequential");
×
91
            }
×
92
            if (map->schedule_type().value() == structured_control_flow::ScheduleType_Sequential::value()) {
39✔
93
                continue;
×
94
            }
×
95
            if (ScheduleT::dimension(map->schedule_type()) != dimension) {
39✔
96
                continue;
26✔
97
            }
26✔
98

99
            // Note: arbitrary `init` and `stride` are permitted here; the
100
            // dispatcher emits `indvar = init + thread_flat_id * stride` so
101
            // the body sees the natural strided value. `num_iterations()`
102
            // already accounts for both.
103
            auto num_iterations = map->num_iterations();
13✔
104
            if (num_iterations.is_null()) {
13✔
105
                throw InvalidSDFGException("Cannot determine number of iterations for nested map in GPU kernel");
×
106
            }
×
107
            max_num_iterations = symbolic::max(max_num_iterations, num_iterations);
13✔
108
        }
13✔
109
    }
39✔
110
    return max_num_iterations;
24✔
111
}
24✔
112

113
template<typename ScheduleT>
114
bool is_outermost_gpu_map(structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager) {
16✔
115
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
16✔
116
    auto& loop_tree = loop_analysis.loop_tree();
16✔
117
    structured_control_flow::ControlFlowNode* ancestor = loop_tree.at(&node);
16✔
118
    while (ancestor != nullptr) {
16✔
119
        if (auto map = dyn_cast<structured_control_flow::Map*>(ancestor)) {
8✔
120
            if (map->schedule_type().value() == ScheduleT::value()) {
8✔
121
                return false;
8✔
122
            }
8✔
123
        }
8✔
124
        ancestor = loop_tree.at(ancestor);
×
125
    }
×
126
    return true;
8✔
127
}
16✔
128

129
template<typename ScheduleT>
130
symbolic::SymbolSet get_gpu_indvars(
131
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
132
) {
24✔
133
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
24✔
134
    auto loops = loop_analysis.descendants(&node);
24✔
135
    loops.insert(&node);
24✔
136
    symbolic::SymbolSet indvars;
24✔
137
    for (const auto& loop : loops) {
39✔
138
        if (auto map = dyn_cast<structured_control_flow::Map*>(loop)) {
39✔
139
            if (map->schedule_type().value() == ScheduleT::value()) {
39✔
140
                if (ScheduleT::dimension(map->schedule_type()) == dimension) {
39✔
141
                    indvars.insert(map->indvar());
13✔
142
                }
13✔
143
            }
39✔
144
        }
39✔
145
    }
39✔
146
    return indvars;
24✔
147
}
24✔
148

149
template<typename ScheduleT>
150
std::vector<structured_control_flow::Map*>
151
get_gpu_maps(structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension) {
12✔
152
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
12✔
153
    auto loops = loop_analysis.descendants(&node);
12✔
154
    loops.insert(&node);
12✔
155
    std::vector<structured_control_flow::Map*> maps;
12✔
156
    for (const auto& loop : loops) {
24✔
157
        if (auto map = dyn_cast<structured_control_flow::Map*>(loop)) {
24✔
158
            if (map->schedule_type().value() == ScheduleT::value()) {
24✔
159
                if (ScheduleT::dimension(map->schedule_type()) == dimension) {
24✔
160
                    maps.push_back(map);
8✔
161
                }
8✔
162
            }
24✔
163
        }
24✔
164
    }
24✔
165
    return maps;
12✔
166
}
12✔
167

168
bool nested_parallelization_replicates_accumulation(
169
    structured_control_flow::Map& loop, analysis::AnalysisManager& analysis_manager
170
) {
16✔
171
    auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
16✔
172

173
    // The outermost enclosing GPU map is the kernel scope. Everything below it is
174
    // folded into a single flattened launch, so adding a dimension for `loop`
175
    // replicates its siblings (and the siblings of any ancestor up to this map)
176
    // across the new dimension's threads.
177
    auto is_parallelized = [](structured_control_flow::Map* map) {
23✔
178
        return map->schedule_type().value() != structured_control_flow::ScheduleType_Sequential::value();
23✔
179
    };
23✔
180

181
    structured_control_flow::Map* outermost = nullptr;
16✔
182
    for (auto* ancestor = loop_analysis.parent_loop(&loop); ancestor != nullptr;
34✔
183
         ancestor = loop_analysis.parent_loop(ancestor)) {
18✔
184
        if (auto* map = dyn_cast<structured_control_flow::Map*>(ancestor)) {
18✔
185
            if (is_parallelized(map)) {
17✔
186
                outermost = map;
17✔
187
            }
17✔
188
        }
17✔
189
    }
18✔
190
    if (outermost == nullptr) {
16✔
191
        // No enclosing GPU map: nothing is folded, hence nothing is replicated.
NEW
192
        return false;
×
NEW
193
    }
×
194

195
    auto& users = analysis_manager.get<analysis::Users>();
16✔
196
    auto& arguments_analysis = analysis_manager.get<analysis::ArgumentsAnalysis>();
16✔
197
    // Containers whose entire lifetime is confined to the kernel are privatized per
198
    // thread (registers/stack, per-thread allocation), so a read-modify-write on one
199
    // races nothing. Only an accumulation on a container that escapes the kernel
200
    // (function argument/external or a transient living outside) is a hazard. Loop
201
    // induction variables are locals by definition, so this subsumes loop-control
202
    // bookkeeping without any special-casing.
203
    const auto& locals = arguments_analysis.locals(analysis_manager, *outermost);
16✔
204
    auto is_local = [&locals](const std::string& container) { return locals.count(container) != 0; };
16✔
205

206
    // A subtree performs an unsafe accumulation if it reads and writes the same
207
    // non-local container (e.g. `acc[i] += x`). A plain store is idempotent under
208
    // replication and therefore allowed.
209
    auto accumulates_on_shared = [&](structured_control_flow::ControlFlowNode& node) {
16✔
210
        analysis::UsersView view(users, node);
4✔
211
        std::unordered_set<std::string> writes;
4✔
212
        std::unordered_set<std::string> reads;
4✔
213
        for (auto* u : view.writes()) {
12✔
214
            writes.insert(u->container());
12✔
215
        }
12✔
216
        for (auto* u : view.moves()) {
4✔
NEW
217
            writes.insert(u->container());
×
NEW
218
        }
×
219
        // Views alias memory; treat conservatively as both a read and a write.
220
        for (auto* u : view.views()) {
4✔
NEW
221
            writes.insert(u->container());
×
NEW
222
            reads.insert(u->container());
×
NEW
223
        }
×
224
        for (auto* u : view.reads()) {
32✔
225
            reads.insert(u->container());
32✔
226
        }
32✔
227
        for (const auto& container : writes) {
6✔
228
            if (reads.count(container) != 0 && !is_local(container)) {
6✔
229
                return true;
2✔
230
            }
2✔
231
        }
6✔
232
        return false;
2✔
233
    };
4✔
234

235
    // Walk from `loop` up to (but excluding) the outermost GPU map, inspecting the
236
    // siblings at each level. Siblings above the kernel are not replicated and are
237
    // therefore not considered.
238
    structured_control_flow::ControlFlowNode* node = &loop;
16✔
239
    while (node != outermost) {
31✔
240
        auto* sequence = dynamic_cast<structured_control_flow::Sequence*>(node->get_parent());
17✔
241
        if (sequence == nullptr) {
17✔
NEW
242
            break;
×
NEW
243
        }
×
244
        for (size_t i = 0; i < sequence->size(); ++i) {
36✔
245
            auto& sibling = sequence->at(i);
21✔
246
            if (&sibling == node) {
21✔
247
                continue;
15✔
248
            }
15✔
249
            // A sibling that is itself a GPU map is already parallelized: codegen maps
250
            // it onto its own threads, so its accumulation is not replicated.
251
            if (auto* sibling_map = dyn_cast<structured_control_flow::Map*>(&sibling)) {
6✔
252
                if (is_parallelized(sibling_map)) {
6✔
253
                    continue;
2✔
254
                }
2✔
255
            }
6✔
256
            if (accumulates_on_shared(sibling)) {
4✔
257
                return true;
2✔
258
            }
2✔
259
        }
4✔
260
        node = sequence->get_parent();
15✔
261
        if (node == nullptr) {
15✔
NEW
262
            break;
×
NEW
263
        }
×
264
    }
15✔
265

266
    return false;
14✔
267
}
16✔
268

269

270
// Explicit template instantiations for CUDA
271
template symbolic::Expression find_nested_gpu_blocksize<cuda::ScheduleType_CUDA>(
272
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
273
);
274

275
template symbolic::Expression find_nested_gpu_iterations<cuda::ScheduleType_CUDA>(
276
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
277
);
278

279
template bool is_outermost_gpu_map<
280
    cuda::ScheduleType_CUDA>(structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager);
281

282
template symbolic::SymbolSet get_gpu_indvars<cuda::ScheduleType_CUDA>(
283
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
284
);
285

286
template std::vector<structured_control_flow::Map*> get_gpu_maps<cuda::ScheduleType_CUDA>(
287
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
288
);
289

290
// Explicit template instantiations for ROCM
291
template symbolic::Expression find_nested_gpu_blocksize<rocm::ScheduleType_ROCM>(
292
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
293
);
294

295
template symbolic::Expression find_nested_gpu_iterations<rocm::ScheduleType_ROCM>(
296
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
297
);
298

299
template bool is_outermost_gpu_map<
300
    rocm::ScheduleType_ROCM>(structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager);
301

302
template symbolic::SymbolSet get_gpu_indvars<rocm::ScheduleType_ROCM>(
303
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
304
);
305

306
template std::vector<structured_control_flow::Map*> get_gpu_maps<rocm::ScheduleType_ROCM>(
307
    structured_control_flow::Map& node, analysis::AnalysisManager& analysis_manager, GPUDimension dimension
308
);
309

310
} // namespace gpu
311
} // namespace sdfg
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc