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

daisytuner / sdfglib / 19199364488

08 Nov 2025 10:17PM UTC coverage: 61.163% (-0.002%) from 61.165%
19199364488

Pull #331

github

web-flow
Merge a4e3b3560 into 48e3f6918
Pull Request #331: allow interpretation of pointers as ints in symbolic expressions

55 of 99 new or added lines in 20 files covered. (55.56%)

3 existing lines in 3 files now uncovered.

10243 of 16747 relevant lines covered (61.16%)

106.06 hits per line

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

41.84
/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

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, "
×
NEW
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
    std::cout << "Emitting begin instrumentation for node " << node.element_id() << " with " << node_plan.size()
×
48
              << " arguments." << std::endl;
×
49
    this->emit_arg_captures(stream, language_extension, node_plan, false, std::to_string(node.element_id()));
×
50

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

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

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

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

65
    stream << "__daisy_capture_end(__capture_ctx);" << std::endl;
×
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
    );
75
}
×
76

77
std::unique_ptr<ArgCapturePlan> ArgCapturePlan::root(StructuredSDFG& sdfg) {
×
78
    analysis::AnalysisManager analysis_manager(sdfg);
×
79
    std::unordered_map<const structured_control_flow::ControlFlowNode*, std::unordered_map<std::string, CaptureVarPlan>>
80
        nodes;
×
81
    auto root_plan = create_capture_plan(sdfg, analysis_manager, sdfg.root());
×
82
    nodes.insert({&sdfg.root(), root_plan});
×
83
    return std::make_unique<ArgCapturePlan>(sdfg, nodes);
×
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✔
94
        auto& loop = ols[i];
×
95
        auto loop_plan = create_capture_plan(sdfg, analysis_manager, *loop);
×
96
        nodes.insert({loop, loop_plan});
×
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

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 {
111
    auto afterBoolStr = after ? "true" : "false";
×
112
    for (auto& [argName, varPlan] : plan) {
×
113
        std::cout << "Emitting capture for arg " << argName << " at " << (after ? "result" : "input") << " time"
×
114
                  << std::endl;
×
115
        auto argIdx = varPlan.arg_idx;
×
116
        if ((!after && varPlan.capture_input) || (after && varPlan.capture_output)) {
×
117
            switch (varPlan.type) {
×
118
                case CaptureVarType::CapRaw: {
119
                    stream << "\t__daisy_capture_raw(" << "__capture_ctx, " << argIdx << ", " << "&" << argName << ", "
×
120
                           << "sizeof(" << argName << "), " << varPlan.inner_type << ", " << afterBoolStr << ", "
×
121
                           << element_id << ");" << std::endl;
×
122
                    break;
×
123
                }
124
                case CaptureVarType::Cap1D: {
125
                    stream << "\t__daisy_capture_1d(" << "__capture_ctx, " << argIdx << ", " << argName << ", "
×
126
                           << "sizeof(" << language_extension.primitive_type(varPlan.inner_type) << "), "
×
127
                           << varPlan.inner_type << ", " << language_extension.expression(varPlan.dim1) << ", "
×
128
                           << afterBoolStr << ", " << element_id << ");" << std::endl;
×
129
                    break;
×
130
                }
131
                case CaptureVarType::Cap2D: {
132
                    stream << "\t__daisy_capture_2d(" << "__capture_ctx, " << argIdx << ", " << argName << ", "
×
133
                           << "sizeof(" << language_extension.primitive_type(varPlan.inner_type) << "), "
×
134
                           << varPlan.inner_type << ", " << language_extension.expression(varPlan.dim1) << ", "
×
135
                           << language_extension.expression(varPlan.dim2) << ", " << afterBoolStr << ", " << element_id
×
136
                           << ");" << std::endl;
×
137
                    break;
×
138
                }
139
                case CaptureVarType::Cap3D: {
140
                    stream << "\t__daisy_capture_3d(" << "__capture_ctx, " << argIdx << ", " << argName << ", "
×
141
                           << "sizeof(" << language_extension.primitive_type(varPlan.inner_type) << "), "
×
142
                           << varPlan.inner_type << ", " << language_extension.expression(varPlan.dim1) << ", "
×
143
                           << language_extension.expression(varPlan.dim2) << ", "
×
144
                           << language_extension.expression(varPlan.dim3) << ", " << afterBoolStr << ", " << element_id
×
145
                           << ");" << std::endl;
×
146
                    break;
×
147
                }
148
                default: {
149
                    DEBUG_PRINTLN(
×
150
                        "Unknown capture type " << static_cast<int>(varPlan.type) << " for arg " << argIdx << " at "
151
                                                << (after ? "result" : "input") << " time"
152
                    );
153
                    break;
×
154
                }
155
            }
156
        }
×
157
    }
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✔
172
        DEBUG_PRINTLN("arg" << arg_idx << ": data nesting deeper than " << max_dim << ", ignoring");
×
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✔
178
    } else if (auto structureType = dynamic_cast<const sdfg::types::Structure*>(&type)) {
×
179
        return std::make_tuple(dim_idx, types::Void);
×
180
    } else if (auto* arrayType = dynamic_cast<const types::Array*>(&type)) {
×
181
        dims[dim_idx] = arrayType->num_elements();
×
182
        auto& inner = arrayType->element_type();
×
183

184
        return analyze_type_rec(sdfg, analysis_manager, dims, max_dim, dim_idx + 1, inner, arg_idx, range, var_name);
×
185
    } else if (auto* ptrType = dynamic_cast<const types::Pointer*>(&type)) {
×
186
        if (!range || range->is_undefined()) {
×
187
            DEBUG_PRINTLN("arg" << arg_idx << " dim" << dim_idx << ": missing range, cannot capture!");
×
188
            return std::make_tuple(-2, types::Void);
×
189
        }
190
        if (range->dims().size() <= dim_idx) {
×
191
            DEBUG_PRINTLN("arg" << arg_idx << " dim" << dim_idx << ": missing dimension in range, cannot capture!");
×
192
            return std::make_tuple(-2, types::Void);
×
193
        }
194
        const auto& dim = range->dims().at(dim_idx);
×
195
        if (!symbolic::eq(dim.first, symbolic::zero())) {
×
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
            );
200
            return std::make_tuple(-2, types::Void);
×
201
        }
202

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

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

246
    DEBUG_PRINTLN("arg" << arg_idx << ": unsupported type " << type.print() << ", cannot capture!");
×
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✔
262
        auto& pointer_type = dynamic_cast<const types::Pointer&>(sdfg.type(var_name));
×
263
        assert(pointer_type.has_pointee_type() && "Externals must have a pointee type");
×
264
        type = &pointer_type.pointee_type();
×
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✔
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✔
297
        plan.insert(
×
298
            {var_name,
×
299
             CaptureVarPlan(is_read, is_written, CaptureVarType::Cap1D, arg_idx, is_external, inner_type, dims[0])}
×
300
        );
301
    } else if (dim_count == 2) {
×
302
        plan.insert(
×
303
            {var_name,
×
304
             CaptureVarPlan(is_read, is_written, CaptureVarType::Cap2D, arg_idx, is_external, inner_type, dims[0], dims[1])
×
305
            }
306
        );
307
    } else if (dim_count == 3) {
×
308
        plan.insert(
×
309
            {var_name,
×
310
             CaptureVarPlan(
×
311
                 is_read, is_written, CaptureVarType::Cap3D, arg_idx, is_external, inner_type, dims[0], dims[1], dims[2]
×
312
             )}
313
        );
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✔
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

352
        ++arg_idx;
×
353
        working &= add_capture_plan(sdfg, analysis_manager, node, arg_name, arg_idx, false, plan, ranges_analysis);
×
354
    }
355
    if (!working) {
2✔
356
        DEBUG_PRINTLN("could not create capture plan, returning empty plan");
×
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
        if (symbolic::is_nullptr(symbolic::symbol(user->container()))) {
12✔
NEW
374
            continue;
×
375
        }
376

377
        bool is_read, is_write;
378
        switch (user->use()) {
12✔
379
            case analysis::READ:
380
                is_read = true;
4✔
381
                is_write = false;
4✔
382
                break;
4✔
383
            case analysis::WRITE:
384
                is_read = false;
8✔
385
                is_write = true;
8✔
386
                break;
8✔
387
            case analysis::MOVE:
388
            case analysis::VIEW:
×
389
            default:
390
                is_read = true;
×
391
                is_write = true;
×
392
        }
×
393
        auto it = all_containers.insert({user->container(), {is_read, is_write}});
12✔
394
        if (!it.second) {
12✔
395
            it.first->second.first |= is_read;
4✔
396
            it.first->second.second |= is_write;
4✔
397
        }
4✔
398
    }
399

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

407
        size_t total_uses = users.uses(container).size();
8✔
408
        size_t scope_uses = scope_users.uses(container).size();
4✔
409

410
        if (scope_uses < total_uses) {
4✔
411
            arguments.insert({container, {flags.first, flags.second}});
8✔
412
        }
4✔
413
    }
414

415
    return arguments;
5✔
416
}
5✔
417

418
} // namespace codegen
419
} // 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