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

daisytuner / sdfglib / 20003838692

07 Dec 2025 11:58AM UTC coverage: 61.641% (-0.01%) from 61.651%
20003838692

push

github

web-flow
Merge pull request #376 from daisytuner/remove-max-bounds

removes max bounds by comparing against init

41 of 58 new or added lines in 1 file covered. (70.69%)

2 existing lines in 1 file now uncovered.

11276 of 18293 relevant lines covered (61.64%)

110.32 hits per line

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

68.13
/src/passes/structured_control_flow/loop_normalization.cpp
1
#include "sdfg/passes/structured_control_flow/loop_normalization.h"
2

3
#include "sdfg/analysis/assumptions_analysis.h"
4
#include "sdfg/analysis/loop_analysis.h"
5
#include "sdfg/symbolic/conjunctive_normal_form.h"
6
#include "sdfg/symbolic/polynomials.h"
7
#include "sdfg/symbolic/series.h"
8

9
namespace sdfg {
10
namespace passes {
11

12
LoopNormalization::LoopNormalization(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager)
8✔
13
    : NonStoppingStructuredSDFGVisitor(builder, analysis_manager) {
8✔
14

15
      };
8✔
16

17
bool LoopNormalization::accept(structured_control_flow::For& loop) {
8✔
18
    bool applied = false;
8✔
19

20
    // Step 1: Bring condition to CNF
21
    symbolic::Condition condition = loop.condition();
8✔
22
    sdfg::symbolic::CNF cnf;
8✔
23
    try {
24
        cnf = symbolic::conjunctive_normal_form(condition);
8✔
25
    } catch (const symbolic::CNFException e) {
8✔
26
        return false;
×
27
    }
×
28
    // Update if changed
29
    {
30
        symbolic::Condition new_condition = symbolic::__true__();
8✔
31
        for (auto& clause : cnf) {
17✔
32
            symbolic::Condition new_clause = symbolic::__false__();
9✔
33
            for (auto& literal : clause) {
19✔
34
                new_clause = symbolic::Or(new_clause, literal);
10✔
35
            }
36
            new_condition = symbolic::And(new_condition, new_clause);
9✔
37
        }
9✔
38
        if (!symbolic::eq(new_condition, condition)) {
8✔
39
            builder_.update_loop(loop, loop.indvar(), new_condition, loop.init(), loop.update());
×
40
            applied = true;
×
41
        }
×
42
        condition = new_condition;
8✔
43
    }
8✔
44

45
    // Following steps require affine update
46
    auto indvar = loop.indvar();
8✔
47
    auto update = loop.update();
8✔
48
    auto& assumptions_analysis = analysis_manager_.get<analysis::AssumptionsAnalysis>();
8✔
49
    auto assums = assumptions_analysis.get(loop, true);
8✔
50
    auto [success, coeffs] = symbolic::series::affine_int_coeffs(update, indvar, assums);
8✔
51
    if (!success) {
8✔
52
        return applied;
×
53
    }
54
    auto [mul_coeff, add_coeff] = coeffs;
16✔
55
    if (mul_coeff->as_int() != 1) {
8✔
56
        return applied;
×
57
    }
58
    int stride = add_coeff->as_int();
8✔
59
    if (stride == 0) {
8✔
60
        return applied;
×
61
    }
62

63
    // Step 2: Simplify literals of CNF that involve the induction variable
64
    symbolic::CNF new_cnf;
8✔
65
    for (auto& clause : cnf) {
17✔
66
        std::vector<symbolic::Condition> new_clause;
9✔
67
        for (auto& literal : clause) {
19✔
68
            if (SymEngine::is_a<SymEngine::Unequality>(*literal)) {
10✔
69
                auto old_literal = SymEngine::rcp_static_cast<const SymEngine::Unequality>(literal);
8✔
70
                auto lhs = old_literal->get_args().at(0);
8✔
71
                auto rhs = old_literal->get_args().at(1);
8✔
72
                if (symbolic::uses(lhs, indvar) && symbolic::uses(rhs, indvar)) {
8✔
NEW
73
                    new_clause.push_back(literal);
×
NEW
74
                    continue;
×
75
                }
76
                if (!symbolic::uses(rhs, indvar)) {
8✔
NEW
77
                    std::swap(lhs, rhs);
×
NEW
78
                }
×
79
                if (!symbolic::uses(rhs, indvar)) {
8✔
NEW
80
                    new_clause.push_back(literal);
×
NEW
81
                    continue;
×
82
                }
83

84
                // RHS is now guranteed to use the indvar
85

86
                // 1. Solve inequality for indvar (affine case)
87
                symbolic::SymbolVec syms = {indvar};
8✔
88
                auto poly_rhs = symbolic::polynomial(rhs, syms);
8✔
89
                if (poly_rhs == SymEngine::null) {
8✔
NEW
90
                    new_clause.push_back(literal);
×
NEW
91
                    continue;
×
92
                }
93
                auto coeffs_rhs = symbolic::affine_coefficients(poly_rhs, syms);
8✔
94
                auto mul_coeff_rhs = coeffs_rhs[indvar];
8✔
95
                auto add_coeff_rhs = coeffs_rhs[symbolic::symbol("__daisy_constant__")];
8✔
96

97
                auto new_rhs = symbolic::sub(lhs, add_coeff_rhs);
8✔
98
                // TODO: integer division
99
                new_rhs = symbolic::div(new_rhs, mul_coeff_rhs);
8✔
100
                auto new_lhs = indvar;
8✔
101

102
                // 2. Convert to comparison based on stride sign
103

104
                // Special cases: |stride| == 1
105
                if (stride == 1) {
8✔
106
                    auto new_literal = symbolic::Lt(new_lhs, new_rhs);
5✔
107
                    new_clause.push_back(new_literal);
5✔
108
                    continue;
109
                } else if (stride == -1) {
8✔
110
                    auto new_literal = symbolic::Gt(new_lhs, new_rhs);
1✔
111
                    new_clause.push_back(new_literal);
1✔
112
                    continue;
113
                }
1✔
114

115
                // Fallback general case
116
                new_clause.push_back(symbolic::Ne(new_lhs, new_rhs));
2✔
117
            } else if (SymEngine::is_a<SymEngine::StrictLessThan>(*literal)) {
10✔
118
                // Remove trivial max expressions
119
                auto slt = SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(literal);
2✔
120
                auto lhs = slt->get_args().at(0);
2✔
121
                auto rhs = slt->get_args().at(1);
2✔
122
                if (!symbolic::eq(lhs, indvar)) {
2✔
123
                    new_clause.push_back(literal);
1✔
124
                    continue;
1✔
125
                }
126
                if (!SymEngine::is_a<SymEngine::Max>(*rhs)) {
1✔
NEW
127
                    new_clause.push_back(literal);
×
NEW
128
                    continue;
×
129
                }
130
                auto max_expr = SymEngine::rcp_static_cast<const SymEngine::Max>(rhs);
1✔
131
                auto args = max_expr->get_args();
1✔
132
                if (args.size() != 2) {
1✔
NEW
133
                    new_clause.push_back(literal);
×
NEW
134
                    continue;
×
135
                }
136
                auto arg1 = args.at(0);;
1✔
137
                auto arg2 = args.at(1);
1✔
138
                if (!symbolic::eq(arg1, loop.init())) {
1✔
NEW
139
                    std::swap(arg1, arg2);
×
NEW
140
                }
×
141
                if (!symbolic::eq(arg1, loop.init())) {
1✔
NEW
142
                    new_clause.push_back(literal);
×
NEW
143
                    continue;
×
144
                }
145
                auto new_literal = symbolic::Lt(indvar, arg2);
1✔
146
                new_clause.push_back(new_literal);
1✔
147
            } else {
2✔
NEW
148
                new_clause.push_back(literal);
×
149
            }
150

151
        }
152
        new_cnf.push_back(new_clause);
9✔
153
    }
9✔
154
    // Update if changed
155
    {
156
        symbolic::Condition new_condition = symbolic::__true__();
8✔
157
        for (auto& clause : new_cnf) {
17✔
158
            symbolic::Condition new_clause = symbolic::__false__();
9✔
159
            for (auto& literal : clause) {
19✔
160
                new_clause = symbolic::Or(new_clause, literal);
10✔
161
            }
162
            new_condition = symbolic::And(new_condition, new_clause);
9✔
163
        }
9✔
164
        if (!symbolic::eq(new_condition, condition)) {
8✔
165
            builder_.update_loop(loop, loop.indvar(), new_condition, loop.init(), loop.update());
5✔
166
            applied = true;
5✔
167
            condition = new_condition;
5✔
168
        }
5✔
169
    }
8✔
170

171
    // Check uses of indvar in loop body are all memlets for further steps
172
    auto& users_analysis = analysis_manager_.get<analysis::Users>();
8✔
173
    analysis::UsersView body_users(users_analysis, loop.root());
8✔
174
    bool all_subsets = true;
8✔
175
    for (auto& user : body_users.uses(indvar->get_name())) {
9✔
176
        if (!dynamic_cast<data_flow::Memlet*>(user->element())) {
1✔
177
            all_subsets = false;
1✔
178
            break;
1✔
179
        }
180
    }
181
    if (!all_subsets) {
8✔
182
        return applied;
1✔
183
    }
184

185
    try {
186
        new_cnf = symbolic::conjunctive_normal_form(condition);
7✔
187
    } catch (const symbolic::CNFException e) {
7✔
188
        return applied;
×
189
    }
×
190

191
    if (stride > 0) {
7✔
192
        // Step 3: Shift loop to start from zero if possible
193
        if (!symbolic::eq(loop.init(), symbolic::zero())) {
5✔
194
            // Require integer initial value for following steps
195
            if (!SymEngine::is_a<SymEngine::Integer>(*loop.init())) {
×
196
                return applied;
×
197
            }
198

199
            auto new_init = symbolic::zero();
×
200
            auto actual_indvar = symbolic::add(indvar, loop.init());
×
201
            
202
            // T
203
            bool canonical_condition = false;
×
204
            if (new_cnf.size() == 1) {
×
205
                auto& clause = new_cnf[0];
×
206
                if (clause.size() == 1) {
×
207
                    auto literal = clause[0];
×
208
                    if (SymEngine::is_a<SymEngine::StrictLessThan>(*literal)) {
×
209
                        auto lhs = literal->get_args().at(0);
×
210
                        auto rhs = literal->get_args().at(1);
×
211
                        if (symbolic::eq(lhs, indvar) && !symbolic::uses(rhs, indvar)) {
×
212
                            condition = symbolic::Lt(indvar, symbolic::sub(rhs, loop.init()));
×
213
                            canonical_condition = true;
×
214
                        }
×
215
                    }
×
216
                }
×
217
            }
×
218
            if (!canonical_condition) {
×
219
                condition = symbolic::subs(condition, indvar, actual_indvar);
×
220
            }
×
221
            builder_.update_loop(loop, indvar, condition, new_init, loop.update());
×
222
            auto& root = loop.root();
×
223
            root.replace(indvar, actual_indvar);
×
224

225
            return true;
×
226
        }
×
227
    } else if (stride < 0) {
7✔
228
        // Step 4: Rotate loop if stride is negative
229
        if (stride != -1) {
2✔
230
            return applied;
1✔
231
        }
232
        if (new_cnf.size() != 1) {
1✔
233
            return applied;
×
234
        }
235
        auto& clause = new_cnf[0];
1✔
236
        if (clause.size() != 1) {
1✔
237
            return applied;
×
238
        }
239
        auto literal = clause[0];
1✔
240
        if (!SymEngine::is_a<SymEngine::StrictLessThan>(*literal)) {
1✔
241
            return applied;
×
242
        }
243
        auto slt = SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(literal);
1✔
244
        auto lhs = slt->get_args().at(0);
1✔
245
        auto rhs = slt->get_args().at(1);
1✔
246
        if (!symbolic::eq(rhs, indvar)) {
1✔
247
            return applied;
×
248
        }
249

250
        // Update loop parameters
251
        auto new_init = symbolic::add(lhs, symbolic::one());
1✔
252
        auto new_update = symbolic::add(indvar, symbolic::one());
1✔
253
        auto new_condition = symbolic::Lt(indvar, symbolic::add(loop.init(), symbolic::one()));
1✔
254

255
        // Replace indvar by (init - indvar) in loop body
256
        loop.root().replace(indvar, symbolic::sub(loop.init(), symbolic::sub(indvar, new_init)));
1✔
257

258
        builder_.update_loop(loop, loop.indvar(), new_condition, new_init, new_update);
1✔
259
        return true;
1✔
260
    }
1✔
261

262
    return applied;
5✔
263
};
8✔
264

265
} // namespace passes
266
} // 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