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

daisytuner / sdfglib / 19162352523

07 Nov 2025 08:16AM UTC coverage: 61.556% (-0.4%) from 61.911%
19162352523

push

github

web-flow
rewrite of arg captures to work on a scope level (#319)

* rewrite of arg captures to work on a scope level

* adding first unit tests for arg capturing

* Fix scoped arg captures

* Fix debug output

* Only write capture fila name to index

* Switch to element-id based capture storage

* Reenable debug prints

* Fix serialization deserialization of arg capture index

* Use fake node ids in rtl test

* Add debug output for capture file creation

* Boost coverage

---------

Co-authored-by: Nora Hagmeyer <nora.hagmeyer@daisytuner.com>

171 of 320 new or added lines in 18 files covered. (53.44%)

17 existing lines in 5 files now uncovered.

10265 of 16676 relevant lines covered (61.56%)

100.85 hits per line

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

41.77
/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(
4✔
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 {
60✔
34
    return this->nodes_.count(&node);
60✔
35
}
36

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

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

NEW
46
    auto& node_plan = this->nodes_.at(&node);
×
NEW
47
    std::cout << "Emitting begin instrumentation for node " << node.element_id() << " with " << node_plan.size()
×
NEW
48
              << " arguments." << std::endl;
×
NEW
49
    this->emit_arg_captures(stream, language_extension, node_plan, false, std::to_string(node.element_id()));
×
50

NEW
51
    stream << "}" << std::endl;
×
NEW
52
}
×
53

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

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

NEW
63
    stream << "}" << std::endl;
×
64

NEW
65
    stream << "__daisy_capture_end(__capture_ctx);" << std::endl;
×
NEW
66
}
×
67

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

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

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

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

99
    std::cout << "Created arg capture plan for " << nodes.size() << " nodes." << std::endl;
2✔
100

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

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

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

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

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

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

NEW
243
        return analyze_type_rec(sdfg, analysis_manager, dims, max_dim, dim_idx + 1, *inner, arg_idx, range, var_name);
×
244
    }
245

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

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

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

271
    symbolic::Expression dims[3];
12✔
272

273
    int dim_count = 0;
4✔
274
    types::PrimitiveType inner_type;
275

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

282
    bool is_read = range ? range->saw_read() : true;
4✔
283
    bool is_written = range ? range->saw_write() : true;
4✔
284
    if (dim_count == 0) {
4✔
285
        plan.insert(
8✔
286
            {var_name,
8✔
287
             CaptureVarPlan(
4✔
288
                 is_read || is_written,
4✔
289
                 is_written && (is_read || is_external),
4✔
290
                 CaptureVarType::CapRaw,
291
                 arg_idx,
4✔
292
                 is_external,
4✔
293
                 inner_type
4✔
294
             )}
295
        );
296
    } else if (dim_count == 1) {
4✔
NEW
297
        plan.insert(
×
NEW
298
            {var_name,
×
NEW
299
             CaptureVarPlan(is_read, is_written, CaptureVarType::Cap1D, arg_idx, is_external, inner_type, dims[0])}
×
300
        );
NEW
301
    } else if (dim_count == 2) {
×
NEW
302
        plan.insert(
×
NEW
303
            {var_name,
×
NEW
304
             CaptureVarPlan(is_read, is_written, CaptureVarType::Cap2D, arg_idx, is_external, inner_type, dims[0], dims[1])
×
305
            }
306
        );
NEW
307
    } else if (dim_count == 3) {
×
NEW
308
        plan.insert(
×
NEW
309
            {var_name,
×
NEW
310
             CaptureVarPlan(
×
NEW
311
                 is_read, is_written, CaptureVarType::Cap3D, arg_idx, is_external, inner_type, dims[0], dims[1], dims[2]
×
312
             )}
313
        );
NEW
314
    }
×
315

316
    std::cout << "Successfully added capture plan " << std::endl;
4✔
317
    return true;
4✔
318
}
4✔
319

320
std::unordered_map<std::string, CaptureVarPlan> ArgCapturePlan::create_capture_plan(
2✔
321
    StructuredSDFG& sdfg, analysis::AnalysisManager& analysis_manager, structured_control_flow::ControlFlowNode& node
322
) {
323
    auto arguments = find_arguments(sdfg, analysis_manager, node);
2✔
324

325
    // Sort arguments to have a deterministic order
326
    std::vector<std::string> args;
2✔
327
    for (auto& [arg_name, flags] : arguments) {
6✔
328
        args.push_back(arg_name);
4✔
329
    }
330
    std::sort(args.begin(), args.end());
2✔
331
    std::cout << "Found " << args.size() << " arguments for region " << node.element_id() << std::endl;
2✔
332

333
    // Determine ranges per arguments
334
    auto& ranges_analysis = analysis_manager.get<analysis::MemAccessRanges>();
2✔
335
    bool working = true;
2✔
336
    int arg_idx = -1;
2✔
337
    std::unordered_map<std::string, CaptureVarPlan> plan;
2✔
338
    for (auto& arg_name : args) {
6✔
339
        if (sdfg.is_external(arg_name)) {
4✔
NEW
340
            continue;
×
341
        }
342

343
        ++arg_idx;
4✔
344
        working &= add_capture_plan(sdfg, analysis_manager, node, arg_name, arg_idx, false, plan, ranges_analysis);
4✔
345
    }
346

347
    for (auto& arg_name : args) {
6✔
348
        if (!sdfg.is_external(arg_name) || sdfg.type(arg_name).type_id() == types::TypeID::Function) {
4✔
349
            continue;
4✔
350
        }
351

NEW
352
        ++arg_idx;
×
NEW
353
        working &= add_capture_plan(sdfg, analysis_manager, node, arg_name, arg_idx, false, plan, ranges_analysis);
×
354
    }
355
    if (!working) {
2✔
NEW
356
        DEBUG_PRINTLN("could not create capture plan, returning empty plan");
×
NEW
357
        return std::unordered_map<std::string, CaptureVarPlan>{};
×
358
    }
359

360
    std::cout << "Created capture plan for " << plan.size() << " arguments." << std::endl;
2✔
361
    return plan;
2✔
362
}
2✔
363

364
std::unordered_map<std::string, std::pair<bool, bool>> ArgCapturePlan::find_arguments(
5✔
365
    StructuredSDFG& sdfg, analysis::AnalysisManager& analysis_manager, structured_control_flow::ControlFlowNode& node
366
) {
367
    // Infer arguments of scope
368
    auto& users = analysis_manager.get<analysis::Users>();
5✔
369
    analysis::UsersView scope_users(users, node);
5✔
370

371
    std::unordered_map<std::string, std::pair<bool, bool>> all_containers;
5✔
372
    for (auto& user : scope_users.uses()) {
17✔
373
        bool is_read, is_write;
374
        switch (user->use()) {
12✔
375
            case analysis::READ:
376
                is_read = true;
4✔
377
                is_write = false;
4✔
378
                break;
4✔
379
            case analysis::WRITE:
380
                is_read = false;
8✔
381
                is_write = true;
8✔
382
                break;
8✔
383
            case analysis::MOVE:
NEW
384
            case analysis::VIEW:
×
385
            default:
NEW
386
                is_read = true;
×
NEW
387
                is_write = true;
×
NEW
388
        }
×
389
        auto it = all_containers.insert({user->container(), {is_read, is_write}});
12✔
390
        if (!it.second) {
12✔
391
            it.first->second.first |= is_read;
4✔
392
            it.first->second.second |= is_write;
4✔
393
        }
4✔
394
    }
395

396
    std::unordered_map<std::string, std::pair<bool, bool>> arguments;
5✔
397
    for (auto& [container, flags] : all_containers) {
37✔
398
        if (sdfg.is_argument(container) || sdfg.is_external(container)) {
16✔
399
            arguments.insert({container, {flags.first, flags.second}});
8✔
400
            continue;
4✔
401
        }
402

403
        size_t total_uses = users.uses(container).size();
8✔
404
        size_t scope_uses = scope_users.uses(container).size();
4✔
405

406
        if (scope_uses < total_uses) {
4✔
407
            arguments.insert({container, {flags.first, flags.second}});
8✔
408
        }
4✔
409
    }
410

411
    return arguments;
5✔
412
}
5✔
413

414
} // namespace codegen
415
} // 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

© 2025 Coveralls, Inc