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

daisytuner / sdfglib / 15656007340

14 Jun 2025 08:51PM UTC coverage: 13.234% (-49.9%) from 63.144%
15656007340

Pull #76

github

web-flow
Merge 9586c8161 into 413c53212
Pull Request #76: New Loop Dependency Analysis

361 of 465 new or added lines in 7 files covered. (77.63%)

6215 existing lines in 110 files now uncovered.

1612 of 12181 relevant lines covered (13.23%)

13.64 hits per line

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

37.82
/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/structured_control_flow/sequence.h"
9
#include "sdfg/symbolic/assumptions.h"
10
#include "sdfg/symbolic/conjunctive_normal_form.h"
11
#include "sdfg/symbolic/extreme_values.h"
12
#include "sdfg/symbolic/polynomials.h"
13
#include "sdfg/symbolic/series.h"
14

15
namespace sdfg {
16
namespace analysis {
17

18
AssumptionsAnalysis::AssumptionsAnalysis(StructuredSDFG& sdfg)
78✔
19
    : Analysis(sdfg) {
39✔
20

21
      };
39✔
22

23
void AssumptionsAnalysis::visit_block(structured_control_flow::Block* block,
45✔
24
                                      analysis::AnalysisManager& analysis_manager) {
25
    return;
45✔
26
};
27

28
void AssumptionsAnalysis::visit_sequence(structured_control_flow::Sequence* sequence,
93✔
29
                                         analysis::AnalysisManager& analysis_manager) {
30
    return;
93✔
31
};
32

UNCOV
33
void AssumptionsAnalysis::visit_if_else(structured_control_flow::IfElse* if_else,
×
34
                                        analysis::AnalysisManager& analysis_manager) {
UNCOV
35
    auto& users = analysis_manager.get<analysis::Users>();
×
UNCOV
36
    for (size_t i = 0; i < if_else->size(); i++) {
×
UNCOV
37
        auto& scope = if_else->at(i).first;
×
UNCOV
38
        auto condition = if_else->at(i).second;
×
UNCOV
39
        auto symbols = symbolic::atoms(condition);
×
40

41
        // Assumption: symbols are read-only in scope
UNCOV
42
        bool read_only = true;
×
UNCOV
43
        analysis::UsersView scope_users(users, scope);
×
UNCOV
44
        for (auto& sym : symbols) {
×
UNCOV
45
            if (scope_users.writes(sym->get_name()).size() > 0) {
×
46
                read_only = false;
×
47
                break;
×
48
            }
49
        }
UNCOV
50
        if (!read_only) {
×
51
            continue;
×
52
        }
53

54
        try {
UNCOV
55
            auto cnf = symbolic::conjunctive_normal_form(condition);
×
56

57
            // Assumption: no or conditions
UNCOV
58
            bool has_complex_clauses = false;
×
UNCOV
59
            for (auto& clause : cnf) {
×
UNCOV
60
                if (clause.size() > 1) {
×
61
                    has_complex_clauses = true;
×
62
                    break;
×
63
                }
64
            }
UNCOV
65
            if (has_complex_clauses) {
×
66
                continue;
×
67
            }
68

UNCOV
69
            for (auto& sym : symbols) {
×
UNCOV
70
                symbolic::Expression ub = symbolic::infty(1);
×
UNCOV
71
                symbolic::Expression lb = symbolic::infty(-1);
×
UNCOV
72
                for (auto& clause : cnf) {
×
UNCOV
73
                    auto& literal = clause[0];
×
74
                    // Literal does not use symbol
UNCOV
75
                    if (!symbolic::uses(literal, sym)) {
×
UNCOV
76
                        continue;
×
77
                    }
78

UNCOV
79
                    if (SymEngine::is_a<SymEngine::Equality>(*literal)) {
×
UNCOV
80
                        auto eq = SymEngine::rcp_dynamic_cast<const SymEngine::Equality>(literal);
×
UNCOV
81
                        auto lhs = eq->get_args()[0];
×
UNCOV
82
                        auto rhs = eq->get_args()[1];
×
NEW
83
                        if (SymEngine::eq(*lhs, *sym) && !symbolic::uses(rhs, sym)) {
×
UNCOV
84
                            ub = rhs;
×
UNCOV
85
                            lb = rhs;
×
UNCOV
86
                            break;
×
NEW
87
                        } else if (SymEngine::eq(*rhs, *sym) && !symbolic::uses(lhs, sym)) {
×
UNCOV
88
                            ub = lhs;
×
UNCOV
89
                            lb = lhs;
×
UNCOV
90
                            break;
×
91
                        }
UNCOV
92
                    } else if (SymEngine::is_a<SymEngine::StrictLessThan>(*literal)) {
×
93
                        auto lt =
UNCOV
94
                            SymEngine::rcp_dynamic_cast<const SymEngine::StrictLessThan>(literal);
×
UNCOV
95
                        auto lhs = lt->get_args()[0];
×
UNCOV
96
                        auto rhs = lt->get_args()[1];
×
NEW
97
                        if (SymEngine::eq(*lhs, *sym) && !symbolic::uses(rhs, sym)) {
×
UNCOV
98
                            if (symbolic::eq(ub, symbolic::infty(1))) {
×
UNCOV
99
                                ub = rhs;
×
UNCOV
100
                            } else {
×
101
                                ub = symbolic::min(ub, rhs);
×
102
                            }
NEW
103
                        } else if (SymEngine::eq(*rhs, *sym) && !symbolic::uses(lhs, sym)) {
×
UNCOV
104
                            if (symbolic::eq(lb, symbolic::infty(-1))) {
×
UNCOV
105
                                lb = lhs;
×
UNCOV
106
                            } else {
×
107
                                lb = symbolic::max(lb, lhs);
×
108
                            }
UNCOV
109
                        }
×
UNCOV
110
                    } else if (SymEngine::is_a<SymEngine::LessThan>(*literal)) {
×
111
                        auto lt = SymEngine::rcp_dynamic_cast<const SymEngine::LessThan>(literal);
×
112
                        auto lhs = lt->get_args()[0];
×
113
                        auto rhs = lt->get_args()[1];
×
NEW
114
                        if (SymEngine::eq(*lhs, *sym) && !symbolic::uses(rhs, sym)) {
×
115
                            if (symbolic::eq(ub, symbolic::infty(1))) {
×
116
                                ub = rhs;
×
117
                            } else {
×
118
                                ub = symbolic::min(ub, rhs);
×
119
                            }
NEW
120
                        } else if (SymEngine::eq(*rhs, *sym) && !symbolic::uses(lhs, sym)) {
×
121
                            if (symbolic::eq(lb, symbolic::infty(-1))) {
×
122
                                lb = lhs;
×
123
                            } else {
×
124
                                lb = symbolic::max(lb, lhs);
×
125
                            }
126
                        }
×
127
                    }
×
128
                }
129

130
                // Failed to infer anything
UNCOV
131
                if (symbolic::eq(ub, symbolic::infty(1)) && symbolic::eq(lb, symbolic::infty(-1))) {
×
UNCOV
132
                    continue;
×
133
                }
134

UNCOV
135
                if (this->assumptions_.find(&scope) == this->assumptions_.end()) {
×
UNCOV
136
                    this->assumptions_.insert({&scope, symbolic::Assumptions()});
×
UNCOV
137
                }
×
UNCOV
138
                auto& scope_assumptions = this->assumptions_[&scope];
×
UNCOV
139
                if (scope_assumptions.find(sym) == scope_assumptions.end()) {
×
UNCOV
140
                    scope_assumptions.insert({sym, symbolic::Assumption(sym)});
×
UNCOV
141
                }
×
142

UNCOV
143
                if (!symbolic::eq(ub, symbolic::infty(1))) {
×
UNCOV
144
                    scope_assumptions[sym].upper_bound(ub);
×
UNCOV
145
                }
×
UNCOV
146
                if (!symbolic::eq(lb, symbolic::infty(-1))) {
×
UNCOV
147
                    scope_assumptions[sym].lower_bound(lb);
×
UNCOV
148
                }
×
UNCOV
149
            }
×
UNCOV
150
        } catch (const symbolic::CNFException& e) {
×
151
            continue;
152
        }
×
UNCOV
153
    }
×
UNCOV
154
};
×
155

156
void AssumptionsAnalysis::visit_while(structured_control_flow::While* while_loop,
×
157
                                      analysis::AnalysisManager& analysis_manager) {
158
    return;
×
159
};
160

161
void AssumptionsAnalysis::visit_for(structured_control_flow::For* for_loop,
54✔
162
                                    analysis::AnalysisManager& analysis_manager) {
163
    auto& assums = this->get(*for_loop);
54✔
164

165
    // Prove that update is monotonic
166
    auto indvar = for_loop->indvar();
54✔
167
    auto update = for_loop->update();
54✔
168
    if (!symbolic::is_monotonic(update, indvar, assums)) {
54✔
UNCOV
169
        return;
×
170
    }
171

172
    // Add new assumptions
173
    auto& body = for_loop->root();
54✔
174
    if (this->assumptions_.find(&body) == this->assumptions_.end()) {
54✔
175
        this->assumptions_.insert({&body, symbolic::Assumptions()});
54✔
176
    }
54✔
177
    auto& body_assumptions = this->assumptions_[&body];
54✔
178
    if (body_assumptions.find(indvar) == body_assumptions.end()) {
54✔
179
        body_assumptions.insert({indvar, symbolic::Assumption(indvar)});
54✔
180
    }
54✔
181

182
    // monotonic => init is lower bound
183
    body_assumptions[indvar].lower_bound(for_loop->init());
54✔
184
    try {
185
        auto cnf = symbolic::conjunctive_normal_form(for_loop->condition());
54✔
186
        auto ub = symbolic::upper_bound(cnf, indvar);
54✔
187
        if (ub == SymEngine::null) {
54✔
UNCOV
188
            return;
×
189
        }
190
        body_assumptions[indvar].upper_bound(ub);
54✔
191
    } catch (const symbolic::CNFException& e) {
54✔
192
        return;
193
    }
×
194
}
54✔
195

196
void AssumptionsAnalysis::visit_map(structured_control_flow::Map* map,
×
197
                                    analysis::AnalysisManager& analysis_manager) {
198
    auto indvar = map->indvar();
×
199

200
    auto& body = map->root();
×
201
    if (this->assumptions_.find(&body) == this->assumptions_.end()) {
×
202
        this->assumptions_.insert({&body, symbolic::Assumptions()});
×
203
    }
×
204
    auto& body_assumptions = this->assumptions_[&body];
×
205
    if (body_assumptions.find(indvar) == body_assumptions.end()) {
×
206
        body_assumptions.insert({indvar, symbolic::Assumption(indvar)});
×
207
    }
×
208
    body_assumptions[indvar].lower_bound(symbolic::zero());
×
209
    body_assumptions[indvar].upper_bound(symbolic::sub(map->num_iterations(), symbolic::one()));
×
210
};
×
211

212
void AssumptionsAnalysis::traverse(structured_control_flow::Sequence& root,
39✔
213
                                   analysis::AnalysisManager& analysis_manager) {
214
    std::list<structured_control_flow::ControlFlowNode*> queue = {&root};
39✔
215
    while (!queue.empty()) {
231✔
216
        auto current = queue.front();
192✔
217
        queue.pop_front();
192✔
218

219
        if (auto block_stmt = dynamic_cast<structured_control_flow::Block*>(current)) {
192✔
220
            this->visit_block(block_stmt, analysis_manager);
45✔
221
        } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
192✔
222
            this->visit_sequence(sequence_stmt, analysis_manager);
93✔
223
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
192✔
224
                queue.push_back(&sequence_stmt->at(i).first);
99✔
225
            }
99✔
226
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
147✔
UNCOV
227
            this->visit_if_else(if_else_stmt, analysis_manager);
×
UNCOV
228
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
UNCOV
229
                queue.push_back(&if_else_stmt->at(i).first);
×
UNCOV
230
            }
×
231
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
54✔
232
            this->visit_while(while_stmt, analysis_manager);
×
233
            queue.push_back(&while_stmt->root());
×
234
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(current)) {
54✔
235
            this->visit_for(for_stmt, analysis_manager);
54✔
236
            queue.push_back(&for_stmt->root());
54✔
237
        } else if (auto map_stmt = dynamic_cast<structured_control_flow::Map*>(current)) {
54✔
238
            this->visit_map(map_stmt, analysis_manager);
×
239
            queue.push_back(&map_stmt->root());
×
240
        }
×
241
    }
242
};
39✔
243

244
void AssumptionsAnalysis::run(analysis::AnalysisManager& analysis_manager) {
39✔
245
    this->assumptions_.clear();
39✔
246

247
    // Add sdfg assumptions
248
    this->assumptions_.insert({&sdfg_.root(), symbolic::Assumptions()});
39✔
249
    for (auto& entry : sdfg_.assumptions()) {
148✔
250
        this->assumptions_[&sdfg_.root()][entry.first] = entry.second;
109✔
251
    }
252

253
    // Add additional assumptions
254
    for (auto& entry : this->additional_assumptions_) {
39✔
255
        this->assumptions_[&sdfg_.root()][entry.first] = entry.second;
×
256
    }
257

258
    // Forward propagate for each node
259
    this->traverse(sdfg_.root(), analysis_manager);
39✔
260
};
39✔
261

262
const symbolic::Assumptions AssumptionsAnalysis::get(
136✔
263
    structured_control_flow::ControlFlowNode& node) {
264
    // Compute assumptions on the fly
265

266
    // Node-level assumptions
267
    symbolic::Assumptions assums;
136✔
268
    if (this->assumptions_.find(&node) != this->assumptions_.end()) {
136✔
269
        for (auto& entry : this->assumptions_[&node]) {
44✔
270
            assums.insert({entry.first, entry.second});
22✔
271
        }
272
    }
22✔
273

274
    AnalysisManager manager(this->sdfg_);
136✔
275
    auto& scope_analysis = manager.get<ScopeAnalysis>();
136✔
276

277
    auto scope = scope_analysis.parent_scope(&node);
136✔
278
    while (scope != nullptr) {
454✔
279
        // Don't overwrite lower scopes' assumptions
280
        if (this->assumptions_.find(scope) != this->assumptions_.end()) {
318✔
281
            for (auto& entry : this->assumptions_[scope]) {
696✔
282
                if (assums.find(entry.first) == assums.end()) {
480✔
283
                    assums.insert({entry.first, entry.second});
378✔
284
                }
378✔
285
            }
286
        }
216✔
287
        scope = scope_analysis.parent_scope(scope);
318✔
288
    }
289

290
    return assums;
136✔
291
};
136✔
292

293
}  // namespace analysis
294
}  // 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