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

daisytuner / sdfglib / 18718471628

22 Oct 2025 01:51PM UTC coverage: 61.746% (-0.6%) from 62.358%
18718471628

push

github

web-flow
Merge pull request #288 from daisytuner/InstrumentationInfo

Instrumentation info

104 of 310 new or added lines in 20 files covered. (33.55%)

25 existing lines in 6 files now uncovered.

9746 of 15784 relevant lines covered (61.75%)

100.97 hits per line

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

82.38
/src/analysis/flop_analysis.cpp
1
#include "sdfg/analysis/flop_analysis.h"
2
#include <cassert>
3
#include <cstddef>
4
#include <unordered_map>
5
#include <vector>
6
#include "sdfg/analysis/analysis.h"
7
#include "sdfg/analysis/assumptions_analysis.h"
8
#include "sdfg/analysis/loop_analysis.h"
9
#include "sdfg/data_flow/tasklet.h"
10
#include "sdfg/structured_control_flow/block.h"
11
#include "sdfg/structured_control_flow/control_flow_node.h"
12
#include "sdfg/structured_control_flow/if_else.h"
13
#include "sdfg/structured_control_flow/return.h"
14
#include "sdfg/structured_control_flow/sequence.h"
15
#include "sdfg/structured_control_flow/structured_loop.h"
16
#include "sdfg/structured_control_flow/while.h"
17
#include "sdfg/structured_sdfg.h"
18
#include "sdfg/symbolic/assumptions.h"
19
#include "sdfg/symbolic/polynomials.h"
20
#include "sdfg/symbolic/symbolic.h"
21
#include "symengine/functions.h"
22
#include "symengine/symengine_rcp.h"
23

24
namespace sdfg {
25
namespace analysis {
26

27
/// An expression is a parameter expression if all its symbols are parameters
28
bool FlopAnalysis::is_parameter_expression(const symbolic::Expression& expr) {
15✔
29
    if (expr.is_null()) {
15✔
30
        return false;
×
31
    }
32
    for (auto& sym : symbolic::atoms(expr)) {
25✔
33
        if (!this->parameters_.contains(sym)) {
10✔
34
            return false;
5✔
35
        }
36
    }
37
    return true;
10✔
38
}
15✔
39

40
symbolic::ExpressionSet FlopAnalysis::choose_bounds(const symbolic::ExpressionSet& bounds) {
5✔
41
    symbolic::ExpressionSet result;
5✔
42
    for (auto& bound : bounds) {
8✔
43
        if (symbolic::eq(bound, SymEngine::NegInf) || symbolic::eq(bound, SymEngine::Inf)) {
3✔
44
            // Skip infinities
45
            continue;
×
46
        } else if (SymEngine::is_a<SymEngine::Integer>(*bound)) {
3✔
47
            // Collect integers
48
            result.insert(bound);
1✔
49
        } else if (!symbolic::contains_dynamic_sizeof(bound) && this->is_parameter_expression(bound)) {
3✔
50
            // Collect parameter expressions if they do not contain dynamic_sizeof
51
            result.insert(bound);
×
52
        }
×
53
    }
54
    if (result.empty()) {
5✔
55
        // Fallback if no integers or parameter expressions were found
56
        return bounds;
4✔
57
    } else {
58
        return result;
1✔
59
    }
60
}
5✔
61

62
symbolic::Expression FlopAnalysis::
63
    replace_loop_indices(const symbolic::Expression expr, symbolic::Assumptions& assumptions) {
38✔
64
    symbolic::Expression result = expr;
38✔
65
    auto atoms = symbolic::atoms(result);
38✔
66
    for (auto sym : atoms) {
53✔
67
        if (!assumptions.contains(sym)) continue;
15✔
68
        symbolic::Assumption assumption = assumptions.at(sym);
13✔
69
        if (!assumption.constant() || assumption.map().is_null()) continue;
13✔
70
        symbolic::Expression ub, lb;
2✔
71
        if (assumption.tight_upper_bound().is_null()) {
2✔
72
            auto bounds = this->choose_bounds(assumption.upper_bounds());
×
73
            if (bounds.empty()) {
×
74
                ub = assumption.upper_bound();
×
75
            } else {
×
76
                ub = SymEngine::min(std::vector<symbolic::Expression>(bounds.begin(), bounds.end()));
×
77
            }
78
        } else {
×
79
            ub = assumption.tight_upper_bound();
2✔
80
        }
81
        if (assumption.tight_lower_bound().is_null()) {
2✔
82
            auto bounds = this->choose_bounds(assumption.lower_bounds());
×
83
            if (bounds.empty()) {
×
84
                lb = assumption.lower_bound();
×
85
            } else {
×
86
                lb = SymEngine::max(std::vector<symbolic::Expression>(bounds.begin(), bounds.end()));
×
87
            }
88
        } else {
×
89
            lb = assumption.tight_lower_bound();
2✔
90
        }
91
        result = symbolic::subs(result, sym, symbolic::div(symbolic::sub(ub, lb), symbolic::integer(2)));
2✔
92
        this->precise_ = false;
2✔
93
    }
15✔
94
    return result;
38✔
95
}
38✔
96

97
symbolic::Expression FlopAnalysis::visit(structured_control_flow::ControlFlowNode& node, AnalysisManager& analysis_manager) {
22✔
98
    if (auto sequence = dynamic_cast<structured_control_flow::Sequence*>(&node)) {
22✔
UNCOV
99
        return this->visit_sequence(*sequence, analysis_manager);
×
100
    } else if (auto block = dynamic_cast<structured_control_flow::Block*>(&node)) {
22✔
101
        return this->visit_block(*block, analysis_manager);
12✔
102
    } else if (auto structured_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(&node)) {
10✔
103
        return this->visit_structured_loop(*structured_loop, analysis_manager);
7✔
104
    } else if (auto if_else = dynamic_cast<structured_control_flow::IfElse*>(&node)) {
3✔
105
        return this->visit_if_else(*if_else, analysis_manager);
1✔
106
    } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(&node)) {
2✔
107
        return this->visit_while(*while_loop, analysis_manager);
1✔
108
    } else if (dynamic_cast<structured_control_flow::Return*>(&node)) {
1✔
UNCOV
109
        return symbolic::zero();
×
110
    } else if (dynamic_cast<structured_control_flow::Break*>(&node)) {
1✔
111
        return symbolic::zero();
1✔
UNCOV
112
    } else if (dynamic_cast<structured_control_flow::Continue*>(&node)) {
×
UNCOV
113
        return symbolic::zero();
×
114
    } else {
115
        return SymEngine::null;
×
116
        this->precise_ = false;
117
    }
118
}
22✔
119

120
symbolic::Expression FlopAnalysis::
121
    visit_sequence(structured_control_flow::Sequence& sequence, AnalysisManager& analysis_manager) {
19✔
122
    symbolic::Expression result = symbolic::zero();
19✔
123
    bool is_null = false;
19✔
124

125
    for (size_t i = 0; i < sequence.size(); i++) {
41✔
126
        symbolic::Expression tmp = this->visit(sequence.at(i).first, analysis_manager);
22✔
127
        this->flops_[&sequence.at(i).first] = tmp;
22✔
128
        if (tmp.is_null()) is_null = true;
22✔
129
        if (!is_null) result = symbolic::add(result, tmp);
22✔
130
    }
22✔
131

132
    if (is_null) {
19✔
133
        this->precise_ = false;
1✔
134
        return SymEngine::null;
1✔
135
    }
136
    return result;
18✔
137
}
19✔
138

139
symbolic::Expression FlopAnalysis::visit_block(structured_control_flow::Block& block, AnalysisManager& analysis_manager) {
12✔
140
    auto& dfg = block.dataflow();
12✔
141

142
    symbolic::Expression tasklets_result = symbolic::zero();
12✔
143
    for (auto tasklet : dfg.tasklets()) {
25✔
144
        if (tasklet->code() == data_flow::TaskletCode::fp_fma) {
13✔
145
            tasklets_result = symbolic::add(tasklets_result, symbolic::integer(2));
4✔
146
        } else if (data_flow::is_floating_point(tasklet->code())) {
13✔
147
            tasklets_result = symbolic::add(tasklets_result, symbolic::one());
6✔
148
        }
6✔
149
    }
150

151
    symbolic::Expression libnodes_result = symbolic::zero();
12✔
152
    for (auto libnode : dfg.library_nodes()) {
13✔
153
        symbolic::Expression tmp = libnode->flop();
1✔
154
        if (tmp.is_null()) {
1✔
155
            this->precise_ = false;
×
156
            return SymEngine::null;
×
157
        }
158
        libnodes_result = symbolic::add(libnodes_result, tmp);
1✔
159
    }
1✔
160

161
    // Filter the loop index variables in libnodes_result, and replace them by (upper_bound - lower_bound) / 2
162
    auto& assumptions_analysis = analysis_manager.get<AssumptionsAnalysis>();
12✔
163
    auto block_assumptions = assumptions_analysis.get(block);
12✔
164
    libnodes_result = this->replace_loop_indices(libnodes_result, block_assumptions);
12✔
165

166
    return symbolic::add(tasklets_result, libnodes_result);
12✔
167
}
12✔
168

169
symbolic::Expression FlopAnalysis::
170
    visit_structured_loop(structured_control_flow::StructuredLoop& loop, AnalysisManager& analysis_manager) {
7✔
171
    symbolic::Expression tmp = this->visit_sequence(loop.root(), analysis_manager);
7✔
172
    this->flops_[&loop.root()] = tmp;
7✔
173
    if (tmp.is_null()) {
7✔
174
        this->precise_ = false;
×
175
        return SymEngine::null;
×
176
    }
177

178
    // Require existance of assumptions for the loop indvar
179
    auto indvar = loop.indvar();
7✔
180
    auto& assumptions_analysis = analysis_manager.get<AssumptionsAnalysis>();
7✔
181
    auto loop_assumptions = assumptions_analysis.get(loop.root());
7✔
182
    if (!loop_assumptions.contains(indvar)) {
7✔
183
        this->precise_ = false;
×
184
        return SymEngine::null;
×
185
    }
186
    bool done;
187

188
    // Determine initial value of loop
189
    symbolic::Expression init = SymEngine::null;
7✔
190
    done = false;
7✔
191
    if (!loop_assumptions[indvar].tight_lower_bound().is_null()) {
7✔
192
        init = this->replace_loop_indices(loop_assumptions[indvar].tight_lower_bound(), loop_assumptions);
7✔
193
        done = this->is_parameter_expression(init);
7✔
194
    }
7✔
195
    if (!done && !symbolic::eq(loop_assumptions[indvar].lower_bound(), SymEngine::NegInf)) {
7✔
196
        auto bounds = this->choose_bounds(loop_assumptions[indvar].lower_bounds());
2✔
197
        if (!bounds.empty()) {
2✔
198
            init = this->replace_loop_indices(
1✔
199
                SymEngine::max(std::vector<symbolic::Expression>(bounds.begin(), bounds.end())), loop_assumptions
1✔
200
            );
201
            this->precise_ = false;
1✔
202
            done = this->is_parameter_expression(init);
1✔
203
        }
1✔
204
    }
2✔
205
    if (!done) {
7✔
206
        init = this->replace_loop_indices(loop.init(), loop_assumptions);
1✔
207
        this->precise_ = false;
1✔
208
    }
1✔
209
    if (init.is_null()) {
7✔
210
        this->precise_ = false;
×
211
        return SymEngine::null;
×
212
    }
213

214
    // Determine bound of loop
215
    symbolic::Expression bound;
7✔
216
    done = false;
7✔
217
    if (!loop_assumptions[indvar].tight_upper_bound().is_null()) {
7✔
218
        bound = this->replace_loop_indices(loop_assumptions[indvar].tight_upper_bound(), loop_assumptions);
6✔
219
        done = this->is_parameter_expression(bound);
6✔
220
    }
6✔
221
    if (!done && !symbolic::eq(loop_assumptions[indvar].upper_bound(), SymEngine::Inf)) {
7✔
222
        auto bounds = this->choose_bounds(loop_assumptions[indvar].upper_bounds());
3✔
223
        if (!bounds.empty()) {
3✔
224
            bound = this->replace_loop_indices(
1✔
225
                SymEngine::min(std::vector<symbolic::Expression>(bounds.begin(), bounds.end())), loop_assumptions
1✔
226
            );
227
            this->precise_ = false;
1✔
228
            done = this->is_parameter_expression(bound);
1✔
229
        }
1✔
230
    }
3✔
231
    if (!done) {
7✔
232
        auto canonical_bound = LoopAnalysis::canonical_bound(&loop, assumptions_analysis);
3✔
233
        if (!canonical_bound.is_null()) {
3✔
234
            bound = this->replace_loop_indices(symbolic::sub(canonical_bound, symbolic::one()), loop_assumptions);
3✔
235
            this->precise_ = false;
3✔
236
        }
3✔
237
    }
3✔
238
    if (bound.is_null()) {
7✔
239
        this->precise_ = false;
×
240
        return SymEngine::null;
×
241
    }
242

243
    // Determine stride of loop
244
    symbolic::SymbolVec symbols = {indvar};
7✔
245
    auto update_polynomial = symbolic::polynomial(loop.update(), symbols);
7✔
246
    if (update_polynomial.is_null()) {
7✔
247
        this->precise_ = false;
×
248
        return SymEngine::null;
×
249
    }
250
    auto update_coeffs = symbolic::affine_coefficients(update_polynomial, symbols);
7✔
251

252
    // For now, only allow polynomial of the form: 1 * indvar + n
253
    assert(update_coeffs.contains(indvar) && symbolic::eq(update_coeffs[indvar], symbolic::one()));
14✔
254
    symbolic::Expression stride =
255
        this->replace_loop_indices(update_coeffs[symbolic::symbol("__daisy_constant__")], loop_assumptions);
7✔
256

257
    return symbolic::mul(symbolic::div(symbolic::add(symbolic::sub(bound, init), symbolic::one()), stride), tmp);
7✔
258
}
7✔
259

260
symbolic::Expression FlopAnalysis::
261
    visit_if_else(structured_control_flow::IfElse& if_else, AnalysisManager& analysis_manager) {
1✔
262
    if (if_else.size() == 0) return symbolic::zero();
1✔
263

264
    std::vector<symbolic::Expression> sub_flops;
1✔
265
    bool is_null = false;
1✔
266

267
    for (size_t i = 0; i < if_else.size(); i++) {
3✔
268
        symbolic::Expression tmp = this->visit_sequence(if_else.at(i).first, analysis_manager);
2✔
269
        this->flops_[&if_else.at(i).first] = tmp;
2✔
270
        if (tmp.is_null()) is_null = true;
2✔
271
        if (!is_null) sub_flops.push_back(tmp);
2✔
272
    }
2✔
273

274
    this->precise_ = false;
1✔
275
    if (is_null) {
1✔
276
        return SymEngine::null;
×
277
    }
278
    return SymEngine::max(sub_flops);
1✔
279
}
1✔
280

281
symbolic::Expression FlopAnalysis::visit_while(structured_control_flow::While& loop, AnalysisManager& analysis_manager) {
1✔
282
    this->flops_[&loop.root()] = this->visit_sequence(loop.root(), analysis_manager);
1✔
283
    this->precise_ = false;
1✔
284
    // Return null because there is now good way to simply estimate the FLOPs of a while loop
285
    return SymEngine::null;
1✔
286
}
×
287

288
void FlopAnalysis::run(AnalysisManager& analysis_manager) {
9✔
289
    this->flops_.clear();
9✔
290
    this->precise_ = true;
9✔
291

292
    auto& assumptions_analysis = analysis_manager.get<AssumptionsAnalysis>();
9✔
293
    this->parameters_ = assumptions_analysis.parameters();
9✔
294

295
    this->flops_[&this->sdfg_.root()] = this->visit_sequence(this->sdfg_.root(), analysis_manager);
9✔
296
}
9✔
297

298
FlopAnalysis::FlopAnalysis(StructuredSDFG& sdfg) : Analysis(sdfg) {}
9✔
299

300
bool FlopAnalysis::contains(const structured_control_flow::ControlFlowNode* node) {
39✔
301
    return this->flops_.contains(node);
39✔
302
}
303

304
symbolic::Expression FlopAnalysis::get(const structured_control_flow::ControlFlowNode* node) {
39✔
305
    return this->flops_[node];
39✔
306
}
307

UNCOV
308
std::unordered_map<const structured_control_flow::ControlFlowNode*, symbolic::Expression> FlopAnalysis::get() {
×
UNCOV
309
    return this->flops_;
×
310
}
311

312
bool FlopAnalysis::precise() { return this->precise_; }
9✔
313

314
} // namespace analysis
315
} // 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