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

daisytuner / sdfglib / 20764569418

06 Jan 2026 10:50PM UTC coverage: 62.168% (+21.4%) from 40.764%
20764569418

push

github

web-flow
Merge pull request #433 from daisytuner/clang-coverage

updates clang coverage flags

14988 of 24109 relevant lines covered (62.17%)

88.57 hits per line

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

37.08
/src/codegen/instrumentation/arg_capture_plan.cpp
1
#include "sdfg/codegen/instrumentation/arg_capture_plan.h"
2

3
#include <iostream>
4
#include <memory>
5
#include <string>
6
#include <utility>
7

8
#include "sdfg/analysis/analysis.h"
9
#include "sdfg/analysis/loop_analysis.h"
10
#include "sdfg/analysis/type_analysis.h"
11
#include "sdfg/analysis/users.h"
12
#include "sdfg/codegen/language_extension.h"
13
#include "sdfg/helpers/helpers.h"
14
#include "sdfg/symbolic/symbolic.h"
15

16
namespace sdfg {
17
namespace codegen {
18

19
CaptureVarPlan::CaptureVarPlan(
20
    bool capture_input,
21
    bool capture_output,
22
    CaptureVarType type,
23
    int argIdx,
24
    bool isExternal,
25
    sdfg::types::PrimitiveType innerType,
26
    const sdfg::symbolic::Expression dim1,
27
    const sdfg::symbolic::Expression dim2,
28
    const sdfg::symbolic::Expression dim3
29
)
30
    : capture_input(capture_input), capture_output(capture_output), type(type), arg_idx(argIdx),
4✔
31
      is_external(isExternal), inner_type(innerType), dim1(dim1), dim2(dim2), dim3(dim3) {}
4✔
32

33
bool ArgCapturePlan::should_instrument(const structured_control_flow::ControlFlowNode& node) const {
62✔
34
    return this->nodes_.count(&node);
62✔
35
}
62✔
36

37
void ArgCapturePlan::begin_instrumentation(
38
    const structured_control_flow::ControlFlowNode& node, PrettyPrinter& stream, LanguageExtension& language_extension
39
) const {
×
40
    stream << "const bool __daisy_cap_en_" << node.element_id() << " = __daisy_capture_enter(__capture_ctx, "
×
41
           << node.element_id() << ");" << std::endl;
×
42

43
    stream << "if (__daisy_cap_en_" << node.element_id() << ")";
×
44
    stream << "{" << std::endl;
×
45

46
    auto& node_plan = this->nodes_.at(&node);
×
47
    this->emit_arg_captures(stream, language_extension, node_plan, false, std::to_string(node.element_id()));
×
48

49
    stream << "}" << std::endl;
×
50
}
×
51

52
void ArgCapturePlan::end_instrumentation(
53
    const structured_control_flow::ControlFlowNode& node, PrettyPrinter& stream, LanguageExtension& language_extension
54
) const {
×
55
    stream << "if (__daisy_cap_en_" << node.element_id() << ")";
×
56
    stream << "{" << std::endl;
×
57

58
    auto& node_plan = this->nodes_.at(&node);
×
59
    this->emit_arg_captures(stream, language_extension, node_plan, true, std::to_string(node.element_id()));
×
60

61
    stream << "}" << std::endl;
×
62

63
    stream << "__daisy_capture_end(__capture_ctx);" << std::endl;
×
64
}
×
65

66
std::unique_ptr<ArgCapturePlan> ArgCapturePlan::none(StructuredSDFG& sdfg) {
43✔
67
    return std::make_unique<ArgCapturePlan>(
43✔
68
        sdfg,
43✔
69
        std::unordered_map<
43✔
70
            const structured_control_flow::ControlFlowNode*,
43✔
71
            std::unordered_map<std::string, CaptureVarPlan>>{}
43✔
72
    );
43✔
73
}
43✔
74

75
std::unique_ptr<ArgCapturePlan> ArgCapturePlan::root(StructuredSDFG& sdfg) {
×
76
    analysis::AnalysisManager analysis_manager(sdfg);
×
77
    std::unordered_map<const structured_control_flow::ControlFlowNode*, std::unordered_map<std::string, CaptureVarPlan>>
×
78
        nodes;
×
79
    auto root_plan = create_capture_plan(sdfg, analysis_manager, sdfg.root());
×
80
    nodes.insert({&sdfg.root(), root_plan});
×
81
    return std::make_unique<ArgCapturePlan>(sdfg, nodes);
×
82
}
×
83

84
std::unique_ptr<ArgCapturePlan> ArgCapturePlan::outermost_loops_plan(StructuredSDFG& sdfg) {
2✔
85
    analysis::AnalysisManager analysis_manager(sdfg);
2✔
86
    auto& loop_tree_analysis = analysis_manager.get<analysis::LoopAnalysis>();
2✔
87
    auto ols = loop_tree_analysis.outermost_loops();
2✔
88

89
    std::unordered_map<const structured_control_flow::ControlFlowNode*, std::unordered_map<std::string, CaptureVarPlan>>
2✔
90
        nodes;
2✔
91
    for (size_t i = 0; i < ols.size(); i++) {
2✔
92
        auto& loop = ols[i];
×
93
        auto loop_plan = create_capture_plan(sdfg, analysis_manager, *loop);
×
94
        nodes.insert({loop, loop_plan});
×
95
    }
×
96

97
    DEBUG_PRINTLN("Created arg capture plan for " + std::to_string(nodes.size()) + " nodes.");
2✔
98

99
    return std::make_unique<ArgCapturePlan>(sdfg, nodes);
2✔
100
}
2✔
101

102
void ArgCapturePlan::emit_arg_captures(
103
    PrettyPrinter& stream,
104
    LanguageExtension& language_extension,
105
    const std::unordered_map<std::string, CaptureVarPlan>& plan,
106
    bool after,
107
    std::string element_id
108
) const {
×
109
    auto afterBoolStr = after ? "true" : "false";
×
110
    for (auto& [argName, varPlan] : plan) {
×
111
        auto argIdx = varPlan.arg_idx;
×
112
        if ((!after && varPlan.capture_input) || (after && varPlan.capture_output)) {
×
113
            std::string safe_name;
×
114
            if (varPlan.is_external) {
×
115
                safe_name = language_extension.external_prefix() + argName;
×
116
            } else {
×
117
                safe_name = argName;
×
118
            }
×
119
            switch (varPlan.type) {
×
120
                case CaptureVarType::CapRaw: {
×
121
                    stream << "\t__daisy_capture_raw(" << "__capture_ctx, " << argIdx << ", " << "&" << safe_name
×
122
                           << ", "
×
123
                           << "sizeof(" << safe_name << "), " << varPlan.inner_type << ", " << afterBoolStr << ", "
×
124
                           << element_id << ");" << std::endl;
×
125
                    break;
×
126
                }
×
127
                case CaptureVarType::Cap1D: {
×
128
                    stream << "\t__daisy_capture_1d(" << "__capture_ctx, " << argIdx << ", " << safe_name << ", "
×
129
                           << "sizeof(" << language_extension.primitive_type(varPlan.inner_type) << "), "
×
130
                           << varPlan.inner_type << ", " << language_extension.expression(varPlan.dim1) << ", "
×
131
                           << afterBoolStr << ", " << element_id << ");" << std::endl;
×
132
                    break;
×
133
                }
×
134
                case CaptureVarType::Cap2D: {
×
135
                    stream << "\t__daisy_capture_2d(" << "__capture_ctx, " << argIdx << ", " << safe_name << ", "
×
136
                           << "sizeof(" << language_extension.primitive_type(varPlan.inner_type) << "), "
×
137
                           << varPlan.inner_type << ", " << language_extension.expression(varPlan.dim1) << ", "
×
138
                           << language_extension.expression(varPlan.dim2) << ", " << afterBoolStr << ", " << element_id
×
139
                           << ");" << std::endl;
×
140
                    break;
×
141
                }
×
142
                case CaptureVarType::Cap3D: {
×
143
                    stream << "\t__daisy_capture_3d(" << "__capture_ctx, " << argIdx << ", " << safe_name << ", "
×
144
                           << "sizeof(" << language_extension.primitive_type(varPlan.inner_type) << "), "
×
145
                           << varPlan.inner_type << ", " << language_extension.expression(varPlan.dim1) << ", "
×
146
                           << language_extension.expression(varPlan.dim2) << ", "
×
147
                           << language_extension.expression(varPlan.dim3) << ", " << afterBoolStr << ", " << element_id
×
148
                           << ");" << std::endl;
×
149
                    break;
×
150
                }
×
151
                default: {
×
152
                    DEBUG_PRINTLN(
×
153
                        "Unknown capture type " << static_cast<int>(varPlan.type) << " for arg " << argIdx << " at "
×
154
                                                << (after ? "result" : "input") << " time"
×
155
                    );
×
156
                    break;
×
157
                }
×
158
            }
×
159
        }
×
160
    }
×
161
}
×
162

163
std::tuple<int, types::PrimitiveType> ArgCapturePlan::analyze_type_rec(
164
    StructuredSDFG& sdfg,
165
    analysis::AnalysisManager& analysis_manager,
166
    symbolic::Expression* dims,
167
    int max_dim,
168
    int dim_idx,
169
    const types::IType& type,
170
    int arg_idx,
171
    const analysis::MemAccessRange* range,
172
    std::string var_name
173
) {
4✔
174
    if (dim_idx > max_dim) {
4✔
175
        DEBUG_PRINTLN("arg" << arg_idx << ": data nesting deeper than " << max_dim << ", ignoring");
×
176
        return std::make_tuple(-1, types::Void);
×
177
    }
×
178

179
    if (auto* scalarType = dynamic_cast<const types::Scalar*>(&type)) {
4✔
180
        return std::make_tuple(dim_idx, scalarType->primitive_type());
4✔
181
    } else if (auto structureType = dynamic_cast<const sdfg::types::Structure*>(&type)) {
4✔
182
        return std::make_tuple(dim_idx, types::Void);
×
183
    } else if (auto* arrayType = dynamic_cast<const types::Array*>(&type)) {
×
184
        dims[dim_idx] = arrayType->num_elements();
×
185
        auto& inner = arrayType->element_type();
×
186

187
        return analyze_type_rec(sdfg, analysis_manager, dims, max_dim, dim_idx + 1, inner, arg_idx, range, var_name);
×
188
    } else if (auto* ptrType = dynamic_cast<const types::Pointer*>(&type)) {
×
189
        if (!range || range->is_undefined()) {
×
190
            DEBUG_PRINTLN("arg" << arg_idx << " dim" << dim_idx << ": missing range, cannot capture!");
×
191
            return std::make_tuple(-2, types::Void);
×
192
        }
×
193
        if (range->dims().size() <= dim_idx) {
×
194
            DEBUG_PRINTLN("arg" << arg_idx << " dim" << dim_idx << ": missing dimension in range, cannot capture!");
×
195
            return std::make_tuple(-2, types::Void);
×
196
        }
×
197
        const auto& dim = range->dims().at(dim_idx);
×
198
        if (!symbolic::eq(dim.first, symbolic::zero())) {
×
199
            DEBUG_PRINTLN(
×
200
                "arg" << arg_idx << " dim" << dim_idx << ": has upper bound " << dim.second->__str__()
×
201
                      << ", but does not start at 0, cannot capture"
×
202
            );
×
203
            return std::make_tuple(-2, types::Void);
×
204
        }
×
205

206
        dims[dim_idx] = symbolic::add(dim.second, symbolic::one());
×
207
        const types::IType* inner = nullptr;
×
208
        if (ptrType->has_pointee_type()) {
×
209
            inner = &(ptrType->pointee_type());
×
210
        } else {
×
211
            if (dim_idx > 0) {
×
212
                DEBUG_PRINTLN(
×
213
                    "arg" << arg_idx << " dim" << dim_idx << ": missing pointee type for dim > 0, cannot capture!"
×
214
                );
×
215
                return std::make_tuple(-2, types::Void);
×
216
            } else {
×
217
                auto& type_analysis = analysis_manager.get<analysis::TypeAnalysis>();
×
218
                auto outer = type_analysis.get_outer_type(var_name);
×
219
                if (outer != nullptr) {
×
220
                    if (auto* ptrType_new = dynamic_cast<const types::Pointer*>(outer)) {
×
221
                        if (ptrType_new->has_pointee_type()) {
×
222
                            inner = &(ptrType_new->pointee_type());
×
223
                        } else {
×
224
                            DEBUG_PRINTLN(
×
225
                                "arg" << arg_idx << " dim" << dim_idx << ": missing pointee type, cannot capture!"
×
226
                            );
×
227
                            return std::make_tuple(-2, types::Void);
×
228
                        }
×
229
                    }
×
230
                } else {
×
231
                    DEBUG_PRINTLN(
×
232
                        "arg" << arg_idx << " dim" << dim_idx
×
233
                              << ": could not infer type from container, cannot capture!"
×
234
                    );
×
235
                    return std::make_tuple(-2, types::Void);
×
236
                }
×
237
            }
×
238
            if (inner == nullptr) {
×
239
                DEBUG_PRINTLN(
×
240
                    "arg" << arg_idx << " dim" << dim_idx << ": could not infer type from container, cannot capture!"
×
241
                );
×
242
                return std::make_tuple(-2, types::Void);
×
243
            }
×
244
        }
×
245

246
        return analyze_type_rec(sdfg, analysis_manager, dims, max_dim, dim_idx + 1, *inner, arg_idx, range, var_name);
×
247
    }
×
248

249
    DEBUG_PRINTLN("arg" << arg_idx << ": unsupported type " << type.print() << ", cannot capture!");
×
250
    return std::make_tuple(-1, types::Void);
×
251
}
4✔
252

253
bool ArgCapturePlan::add_capture_plan(
254
    StructuredSDFG& sdfg,
255
    analysis::AnalysisManager& analysis_manager,
256
    structured_control_flow::ControlFlowNode& node,
257
    const std::string& var_name,
258
    int arg_idx,
259
    bool is_external,
260
    std::unordered_map<std::string, CaptureVarPlan>& plan,
261
    analysis::MemAccessRanges& ranges
262
) {
4✔
263
    const types::IType* type = nullptr;
4✔
264
    if (is_external) {
4✔
265
        auto& pointer_type = dynamic_cast<const types::Pointer&>(sdfg.type(var_name));
×
266
        assert(pointer_type.has_pointee_type() && "Externals must have a pointee type");
×
267
        type = &pointer_type.pointee_type();
×
268
    } else {
4✔
269
        type = &sdfg.type(var_name);
4✔
270
    }
4✔
271

272
    const auto* range = ranges.get(var_name, node, {var_name});
4✔
273

274
    symbolic::Expression dims[3];
4✔
275

276
    int dim_count = 0;
4✔
277
    types::PrimitiveType inner_type;
4✔
278

279
    std::tie(dim_count, inner_type) =
4✔
280
        analyze_type_rec(sdfg, analysis_manager, dims, 3, 0, *type, arg_idx, range, var_name);
4✔
281
    if (inner_type == types::Void || dim_count < 0 || dim_count > 3) {
4✔
282
        return false;
×
283
    }
×
284

285
    bool is_read = range ? range->saw_read() : true;
4✔
286
    bool is_written = range ? range->saw_write() : true;
4✔
287
    if (dim_count == 0) {
4✔
288
        plan.insert(
4✔
289
            {var_name,
4✔
290
             CaptureVarPlan(
4✔
291
                 is_read || is_written,
4✔
292
                 is_written && (is_read || is_external),
4✔
293
                 CaptureVarType::CapRaw,
4✔
294
                 arg_idx,
4✔
295
                 is_external,
4✔
296
                 inner_type
4✔
297
             )}
4✔
298
        );
4✔
299
    } else if (dim_count == 1) {
4✔
300
        plan.insert(
×
301
            {var_name,
×
302
             CaptureVarPlan(
×
303
                 is_read || is_written, is_written, CaptureVarType::Cap1D, arg_idx, is_external, inner_type, dims[0]
×
304
             )}
×
305
        );
×
306
    } else if (dim_count == 2) {
×
307
        plan.insert(
×
308
            {var_name,
×
309
             CaptureVarPlan(
×
310
                 is_read || is_written,
×
311
                 is_written,
×
312
                 CaptureVarType::Cap2D,
×
313
                 arg_idx,
×
314
                 is_external,
×
315
                 inner_type,
×
316
                 dims[0],
×
317
                 dims[1]
×
318
             )}
×
319
        );
×
320
    } else if (dim_count == 3) {
×
321
        plan.insert(
×
322
            {var_name,
×
323
             CaptureVarPlan(
×
324
                 is_read || is_written,
×
325
                 is_written,
×
326
                 CaptureVarType::Cap3D,
×
327
                 arg_idx,
×
328
                 is_external,
×
329
                 inner_type,
×
330
                 dims[0],
×
331
                 dims[1],
×
332
                 dims[2]
×
333
             )}
×
334
        );
×
335
    }
×
336

337
    DEBUG_PRINTLN("Successfully added capture plan ");
4✔
338
    return true;
4✔
339
}
4✔
340

341
std::unordered_map<std::string, CaptureVarPlan> ArgCapturePlan::create_capture_plan(
342
    StructuredSDFG& sdfg, analysis::AnalysisManager& analysis_manager, structured_control_flow::ControlFlowNode& node
343
) {
2✔
344
    auto arguments = find_arguments(sdfg, analysis_manager, node);
2✔
345

346
    // Sort arguments to have a deterministic order
347
    std::vector<std::string> args;
2✔
348
    for (auto& [arg_name, flags] : arguments) {
4✔
349
        args.push_back(arg_name);
4✔
350
    }
4✔
351
    std::sort(args.begin(), args.end());
2✔
352
    DEBUG_PRINTLN("Found " + std::to_string(args.size()) + " arguments for region " + std::to_string(node.element_id()));
2✔
353

354
    // Determine ranges per arguments
355
    auto& ranges_analysis = analysis_manager.get<analysis::MemAccessRanges>();
2✔
356
    bool working = true;
2✔
357
    int arg_idx = -1;
2✔
358
    std::unordered_map<std::string, CaptureVarPlan> plan;
2✔
359
    for (auto& arg_name : args) {
4✔
360
        if (sdfg.is_external(arg_name)) {
4✔
361
            continue;
×
362
        }
×
363

364
        ++arg_idx;
4✔
365
        working &= add_capture_plan(sdfg, analysis_manager, node, arg_name, arg_idx, false, plan, ranges_analysis);
4✔
366
    }
4✔
367

368
    for (auto& arg_name : args) {
4✔
369
        if (!sdfg.is_external(arg_name) || sdfg.type(arg_name).type_id() == types::TypeID::Function) {
4✔
370
            continue;
4✔
371
        }
4✔
372

373
        ++arg_idx;
×
374
        working &= add_capture_plan(sdfg, analysis_manager, node, arg_name, arg_idx, true, plan, ranges_analysis);
×
375
    }
×
376
    if (!working) {
2✔
377
        DEBUG_PRINTLN("could not create capture plan, returning empty plan");
×
378
        return std::unordered_map<std::string, CaptureVarPlan>{};
×
379
    }
×
380

381
    std::cout << "Created capture plan for " << plan.size() << " arguments." << std::endl;
2✔
382
    return plan;
2✔
383
}
2✔
384

385
std::unordered_map<std::string, std::pair<bool, bool>> ArgCapturePlan::find_arguments(
386
    StructuredSDFG& sdfg, analysis::AnalysisManager& analysis_manager, structured_control_flow::ControlFlowNode& node
387
) {
5✔
388
    // Infer arguments of scope
389
    auto& users = analysis_manager.get<analysis::Users>();
5✔
390
    analysis::UsersView scope_users(users, node);
5✔
391

392
    std::unordered_map<std::string, std::pair<bool, bool>> all_containers;
5✔
393
    for (auto& user : scope_users.uses()) {
12✔
394
        if (symbolic::is_nullptr(symbolic::symbol(user->container()))) {
12✔
395
            continue;
×
396
        }
×
397

398
        bool is_read, is_write;
12✔
399
        switch (user->use()) {
12✔
400
            case analysis::READ:
4✔
401
                is_read = true;
4✔
402
                is_write = false;
4✔
403
                break;
4✔
404
            case analysis::WRITE:
8✔
405
                is_read = false;
8✔
406
                is_write = true;
8✔
407
                break;
8✔
408
            case analysis::MOVE:
×
409
            case analysis::VIEW:
×
410
            default:
×
411
                is_read = true;
×
412
                is_write = true;
×
413
        }
12✔
414
        auto it = all_containers.insert({user->container(), {is_read, is_write}});
12✔
415
        if (!it.second) {
12✔
416
            it.first->second.first |= is_read;
4✔
417
            it.first->second.second |= is_write;
4✔
418
        }
4✔
419
    }
12✔
420

421
    std::unordered_map<std::string, std::pair<bool, bool>> arguments;
5✔
422
    for (auto& [container, flags] : all_containers) {
8✔
423
        if (sdfg.is_argument(container) || sdfg.is_external(container)) {
8✔
424
            arguments.insert({container, {flags.first, flags.second}});
4✔
425
            continue;
4✔
426
        }
4✔
427

428
        size_t total_uses = users.uses(container).size();
4✔
429
        size_t scope_uses = scope_users.uses(container).size();
4✔
430

431
        if (scope_uses < total_uses) {
4✔
432
            arguments.insert({container, {flags.first, flags.second}});
4✔
433
        }
4✔
434
    }
4✔
435

436
    return arguments;
5✔
437
}
5✔
438

439
} // namespace codegen
440
} // 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