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

daisytuner / sdfglib / 18651924023

20 Oct 2025 12:26PM UTC coverage: 60.977% (-0.6%) from 61.539%
18651924023

push

github

web-flow
Merge pull request #286 from daisytuner/reserved-names

removes restricted globals filtering in codegen

8 of 20 new or added lines in 2 files covered. (40.0%)

342 existing lines in 17 files now uncovered.

9174 of 15045 relevant lines covered (60.98%)

92.1 hits per line

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

88.1
/src/analysis/assumptions_analysis.cpp
1
#include "sdfg/analysis/assumptions_analysis.h"
2

3
#include <utility>
4
#include <vector>
5

6
#include "sdfg/analysis/scope_analysis.h"
7
#include "sdfg/analysis/users.h"
8
#include "sdfg/symbolic/assumptions.h"
9
#include "sdfg/symbolic/extreme_values.h"
10
#include "sdfg/symbolic/polynomials.h"
11
#include "sdfg/symbolic/series.h"
12

13
namespace sdfg {
14
namespace analysis {
15

16
symbolic::Expression AssumptionsAnalysis::cnf_to_upper_bound(const symbolic::CNF& cnf, const symbolic::Symbol indvar) {
118✔
17
    std::vector<symbolic::Expression> candidates;
118✔
18

19
    for (const auto& clause : cnf) {
248✔
20
        for (const auto& literal : clause) {
261✔
21
            // Comparison: indvar < expr
22
            if (SymEngine::is_a<SymEngine::StrictLessThan>(*literal)) {
131✔
23
                auto lt = SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(literal);
117✔
24
                if (symbolic::eq(lt->get_arg1(), indvar) && !symbolic::uses(lt->get_arg2(), indvar)) {
117✔
25
                    auto ub = symbolic::sub(lt->get_arg2(), symbolic::one());
117✔
26
                    candidates.push_back(ub);
117✔
27
                }
117✔
28
            }
117✔
29
            // Comparison: indvar <= expr
30
            else if (SymEngine::is_a<SymEngine::LessThan>(*literal)) {
14✔
31
                auto le = SymEngine::rcp_static_cast<const SymEngine::LessThan>(literal);
7✔
32
                if (symbolic::eq(le->get_arg1(), indvar) && !symbolic::uses(le->get_arg2(), indvar)) {
7✔
33
                    candidates.push_back(le->get_arg2());
6✔
34
                }
6✔
35
            }
7✔
36
            // Comparison: indvar == expr
37
            else if (SymEngine::is_a<SymEngine::Equality>(*literal)) {
7✔
38
                auto eq = SymEngine::rcp_static_cast<const SymEngine::Equality>(literal);
×
39
                if (symbolic::eq(eq->get_arg1(), indvar) && !symbolic::uses(eq->get_arg2(), indvar)) {
×
40
                    candidates.push_back(eq->get_arg2());
×
41
                }
×
42
            }
×
43
        }
44
    }
45

46
    if (candidates.empty()) {
118✔
47
        return SymEngine::null;
6✔
48
    }
49

50
    // Return the smallest upper bound across all candidate constraints
51
    symbolic::Expression result = candidates[0];
112✔
52
    for (size_t i = 1; i < candidates.size(); ++i) {
123✔
53
        result = symbolic::min(result, candidates[i]);
11✔
54
    }
11✔
55

56
    return result;
112✔
57
}
230✔
58

59
AssumptionsAnalysis::AssumptionsAnalysis(StructuredSDFG& sdfg)
306✔
60
    : Analysis(sdfg) {
153✔
61

62
      };
153✔
63

64
void AssumptionsAnalysis::visit_block(structured_control_flow::Block* block, analysis::AnalysisManager& analysis_manager) {
196✔
65
    return;
196✔
66
};
67

68
void AssumptionsAnalysis::
69
    visit_sequence(structured_control_flow::Sequence* sequence, analysis::AnalysisManager& analysis_manager) {
320✔
70
    return;
320✔
71
};
72

73
void AssumptionsAnalysis::
74
    visit_if_else(structured_control_flow::IfElse* if_else, analysis::AnalysisManager& analysis_manager) {
21✔
75
    return;
21✔
76
};
77

78
void AssumptionsAnalysis::
79
    visit_while(structured_control_flow::While* while_loop, analysis::AnalysisManager& analysis_manager) {
8✔
80
    return;
8✔
81
};
82

83
void AssumptionsAnalysis::visit_for(structured_control_flow::For* for_loop, analysis::AnalysisManager& analysis_manager) {
98✔
84
    auto indvar = for_loop->indvar();
98✔
85
    auto update = for_loop->update();
98✔
86

87
    // Add new assumptions
88
    auto& body = for_loop->root();
98✔
89
    if (this->assumptions_.find(&body) == this->assumptions_.end()) {
98✔
90
        this->assumptions_.insert({&body, symbolic::Assumptions()});
98✔
91
    }
98✔
92
    auto& body_assumptions = this->assumptions_[&body];
98✔
93

94
    // By definition: indvar and condition symbols are constant
95
    symbolic::SymbolSet syms = {indvar};
98✔
96
    for (auto& sym : symbolic::atoms(for_loop->condition())) {
278✔
97
        syms.insert(sym);
180✔
98
    }
99
    for (auto& sym : syms) {
278✔
100
        if (body_assumptions.find(sym) == body_assumptions.end()) {
180✔
101
            body_assumptions.insert({sym, symbolic::Assumption(sym)});
180✔
102
        }
180✔
103
        body_assumptions[sym].constant(true);
180✔
104
    }
105

106
    // Map of indvar
107
    body_assumptions[indvar].map(update);
98✔
108

109
    // Bounds of indvar
110

111
    // Prove that update is monotonic -> assume bounds
112
    auto& assums = this->get(*for_loop);
98✔
113
    if (!symbolic::series::is_monotonic(update, indvar, assums)) {
98✔
114
        return;
3✔
115
    }
116

117
    // Assumption 2: init is lower bound
118
    body_assumptions[indvar].lower_bound(for_loop->init());
95✔
119
    try {
120
        auto cnf = symbolic::conjunctive_normal_form(for_loop->condition());
95✔
121
        auto ub = cnf_to_upper_bound(cnf, indvar);
95✔
122
        if (ub == SymEngine::null) {
95✔
123
            return;
6✔
124
        }
125
        // Assumption 3: ub is upper bound
126
        body_assumptions[indvar].upper_bound(ub);
89✔
127

128
        // Assumption 4: any ub symbol is at least init
129
        for (auto& sym : symbolic::atoms(ub)) {
163✔
130
            body_assumptions[sym].lower_bound(symbolic::add(for_loop->init(), symbolic::one()));
74✔
131
        }
132
    } catch (const symbolic::CNFException& e) {
95✔
133
        return;
134
    }
×
135
}
98✔
136

137
void AssumptionsAnalysis::visit_map(structured_control_flow::Map* map, analysis::AnalysisManager& analysis_manager) {
23✔
138
    auto indvar = map->indvar();
23✔
139
    auto update = map->update();
23✔
140

141
    // Add new assumptions
142
    auto& body = map->root();
23✔
143
    if (this->assumptions_.find(&body) == this->assumptions_.end()) {
23✔
144
        this->assumptions_.insert({&body, symbolic::Assumptions()});
23✔
145
    }
23✔
146
    auto& body_assumptions = this->assumptions_[&body];
23✔
147

148
    // By definition: indvar and condition symbols are constant
149
    symbolic::SymbolSet syms = {indvar};
23✔
150
    for (auto& sym : symbolic::atoms(map->condition())) {
68✔
151
        syms.insert(sym);
45✔
152
    }
153
    for (auto& sym : syms) {
68✔
154
        if (body_assumptions.find(sym) == body_assumptions.end()) {
45✔
155
            body_assumptions.insert({sym, symbolic::Assumption(sym)});
45✔
156
        }
45✔
157
        body_assumptions[sym].constant(true);
45✔
158
    }
159

160
    // Map of indvar
161
    body_assumptions[indvar].map(update);
23✔
162

163
    // Bounds of indvar
164

165
    // Prove that update is monotonic -> assume bounds
166
    auto& assums = this->get(*map);
23✔
167
    if (!symbolic::series::is_monotonic(update, indvar, assums)) {
23✔
168
        return;
×
169
    }
170

171
    // Assumption 2: init is lower bound
172
    body_assumptions[indvar].lower_bound(map->init());
23✔
173
    try {
174
        auto cnf = symbolic::conjunctive_normal_form(map->condition());
23✔
175
        auto ub = cnf_to_upper_bound(cnf, indvar);
23✔
176
        if (ub == SymEngine::null) {
23✔
177
            return;
×
178
        }
179
        // Assumption 3: ub is upper bound
180
        body_assumptions[indvar].upper_bound(ub);
23✔
181

182
        // Assumption 4: any ub symbol is at least init
183
        for (auto& sym : symbolic::atoms(ub)) {
45✔
184
            body_assumptions[sym].lower_bound(symbolic::add(map->init(), symbolic::one()));
22✔
185
        }
186
    } catch (const symbolic::CNFException& e) {
23✔
187
        return;
188
    }
×
189
};
23✔
190

191
void AssumptionsAnalysis::traverse(structured_control_flow::Sequence& root, analysis::AnalysisManager& analysis_manager) {
153✔
192
    std::list<structured_control_flow::ControlFlowNode*> queue = {&root};
153✔
193
    while (!queue.empty()) {
828✔
194
        auto current = queue.front();
675✔
195
        queue.pop_front();
675✔
196

197
        if (auto block_stmt = dynamic_cast<structured_control_flow::Block*>(current)) {
675✔
198
            this->visit_block(block_stmt, analysis_manager);
196✔
199
        } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
675✔
200
            this->visit_sequence(sequence_stmt, analysis_manager);
320✔
201
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
676✔
202
                queue.push_back(&sequence_stmt->at(i).first);
356✔
203
            }
356✔
204
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
479✔
205
            this->visit_if_else(if_else_stmt, analysis_manager);
21✔
206
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
58✔
207
                queue.push_back(&if_else_stmt->at(i).first);
37✔
208
            }
37✔
209
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
159✔
210
            this->visit_while(while_stmt, analysis_manager);
8✔
211
            queue.push_back(&while_stmt->root());
8✔
212
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(current)) {
138✔
213
            this->visit_for(for_stmt, analysis_manager);
98✔
214
            queue.push_back(&for_stmt->root());
98✔
215
        } else if (auto map_stmt = dynamic_cast<structured_control_flow::Map*>(current)) {
130✔
216
            this->visit_map(map_stmt, analysis_manager);
23✔
217
            queue.push_back(&map_stmt->root());
23✔
218
        }
23✔
219
    }
220
};
153✔
221

222
void AssumptionsAnalysis::run(analysis::AnalysisManager& analysis_manager) {
153✔
223
    this->assumptions_.clear();
153✔
224

225
    // Add sdfg assumptions
226
    this->assumptions_.insert({&sdfg_.root(), symbolic::Assumptions()});
153✔
227

228
    // Add additional assumptions
229
    for (auto& entry : this->additional_assumptions_) {
153✔
230
        this->assumptions_[&sdfg_.root()][entry.first] = entry.second;
×
231
    }
232

233
    this->scope_analysis_ = &analysis_manager.get<ScopeAnalysis>();
153✔
234

235
    // Forward propagate for each node
236
    this->traverse(sdfg_.root(), analysis_manager);
153✔
237
};
153✔
238

239
const symbolic::Assumptions AssumptionsAnalysis::
240
    get(structured_control_flow::ControlFlowNode& node, bool include_trivial_bounds) {
783✔
241
    // Compute assumptions on the fly
242

243
    // Node-level assumptions
244
    symbolic::Assumptions assums;
783✔
245
    if (this->assumptions_.find(&node) != this->assumptions_.end()) {
783✔
246
        for (auto& entry : this->assumptions_[&node]) {
689✔
247
            assums.insert({entry.first, entry.second});
440✔
248
        }
249
    }
249✔
250

251
    auto scope = scope_analysis_->parent_scope(&node);
783✔
252
    while (scope != nullptr) {
3,491✔
253
        if (this->assumptions_.find(scope) != this->assumptions_.end()) {
2,708✔
254
            for (auto& entry : this->assumptions_[scope]) {
3,024✔
255
                if (assums.find(entry.first) == assums.end()) {
1,414✔
256
                    // New assumption
257
                    assums.insert({entry.first, entry.second});
1,183✔
258
                } else {
1,183✔
259
                    // Merge assumptions from lower scopes
260
                    auto& lower_assum = assums[entry.first];
231✔
261
                    auto lower_ub = lower_assum.upper_bound();
231✔
262
                    auto lower_lb = lower_assum.lower_bound();
231✔
263
                    auto new_ub = symbolic::min(entry.second.upper_bound(), lower_ub);
231✔
264
                    auto new_lb = symbolic::max(entry.second.lower_bound(), lower_lb);
231✔
265
                    lower_assum.upper_bound(new_ub);
231✔
266
                    lower_assum.lower_bound(new_lb);
231✔
267

268
                    if (lower_assum.map() == SymEngine::null) {
231✔
269
                        lower_assum.map(entry.second.map());
231✔
270
                    }
231✔
271
                    if (!lower_assum.constant()) {
231✔
UNCOV
272
                        lower_assum.constant(entry.second.constant());
×
273
                    }
×
274
                }
231✔
275
            }
276
        }
1,610✔
277
        scope = scope_analysis_->parent_scope(scope);
2,708✔
278
    }
279

280
    if (include_trivial_bounds) {
783✔
281
        for (auto& entry : sdfg_.assumptions()) {
2,999✔
282
            if (assums.find(entry.first) == assums.end()) {
2,383✔
283
                assums.insert({entry.first, entry.second});
897✔
284
            }
897✔
285
        }
286
    }
616✔
287

288
    return assums;
783✔
289
};
783✔
290

291
const symbolic::Assumptions AssumptionsAnalysis::
292
    get(structured_control_flow::ControlFlowNode& from,
228✔
293
        structured_control_flow::ControlFlowNode& to,
294
        bool include_trivial_bounds) {
295
    auto assums_from = this->get(from, include_trivial_bounds);
228✔
296
    auto assums_to = this->get(to, include_trivial_bounds);
228✔
297

298
    // Add lower scope assumptions to outer
299
    // ignore constants assumption
300
    for (auto& entry : assums_from) {
1,212✔
301
        if (assums_to.find(entry.first) == assums_to.end()) {
984✔
UNCOV
302
            auto assums_safe = assums_to;
×
303
            assums_safe.at(entry.first).constant(false);
×
304
            assums_to.insert({entry.first, assums_safe.at(entry.first)});
×
305
        } else {
×
306
            auto lower_assum = assums_to[entry.first];
984✔
307
            auto lower_ub = lower_assum.upper_bound();
984✔
308
            auto lower_lb = lower_assum.lower_bound();
984✔
309
            auto new_ub = symbolic::min(entry.second.upper_bound(), lower_ub);
984✔
310
            auto new_lb = symbolic::max(entry.second.lower_bound(), lower_lb);
984✔
311
            lower_assum.upper_bound(new_ub);
984✔
312
            lower_assum.lower_bound(new_lb);
984✔
313

314
            if (lower_assum.map() == SymEngine::null) {
984✔
315
                lower_assum.map(entry.second.map());
456✔
316
            }
456✔
317
            lower_assum.constant(entry.second.constant());
984✔
318
            assums_to[entry.first] = lower_assum;
984✔
319
        }
984✔
320
    }
321

322
    return assums_to;
228✔
323
}
228✔
324

UNCOV
325
void AssumptionsAnalysis::add(symbolic::Assumptions& assums, structured_control_flow::ControlFlowNode& node) {
×
326
    if (this->assumptions_.find(&node) == this->assumptions_.end()) {
×
327
        return;
×
328
    }
329

UNCOV
330
    for (auto& entry : this->assumptions_[&node]) {
×
331
        if (assums.find(entry.first) == assums.end()) {
×
332
            assums.insert({entry.first, entry.second});
×
333
        } else {
×
334
            assums[entry.first] = entry.second;
×
335
        }
336
    }
UNCOV
337
}
×
338

339
} // namespace analysis
340
} // 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