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

daisytuner / sdfglib / 19998797369

06 Dec 2025 11:01PM UTC coverage: 61.651% (-0.07%) from 61.725%
19998797369

push

github

web-flow
Merge pull request #375 from daisytuner/loop-shift

shifts loops to start from zero

34 of 66 new or added lines in 1 file covered. (51.52%)

2 existing lines in 1 file now uncovered.

11263 of 18269 relevant lines covered (61.65%)

110.37 hits per line

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

68.99
/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)
7✔
13
    : NonStoppingStructuredSDFGVisitor(builder, analysis_manager) {
7✔
14

15
      };
7✔
16

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

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

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

63
    // Step 2: Convert inequality-literals into comparisons with loop variable on LHS
64
    symbolic::CNF new_cnf;
7✔
65
    for (auto& clause : cnf) {
15✔
66
        std::vector<symbolic::Condition> new_clause;
8✔
67
        for (auto& literal : clause) {
17✔
68
            if (!SymEngine::is_a<SymEngine::Unequality>(*literal)) {
9✔
69
                new_clause.push_back(literal);
1✔
70
                continue;
1✔
71
            }
72
            auto old_literal = SymEngine::rcp_static_cast<const SymEngine::Unequality>(literal);
8✔
73
            auto lhs = old_literal->get_args().at(0);
8✔
74
            auto rhs = old_literal->get_args().at(1);
8✔
75
            if (symbolic::uses(lhs, indvar) && symbolic::uses(rhs, indvar)) {
8✔
76
                new_clause.push_back(literal);
×
77
                continue;
×
78
            }
79
            if (!symbolic::uses(rhs, indvar)) {
8✔
80
                std::swap(lhs, rhs);
×
81
            }
×
82
            if (!symbolic::uses(rhs, indvar)) {
8✔
83
                new_clause.push_back(literal);
×
84
                continue;
×
85
            }
86

87
            // RHS is now guranteed to use the indvar
88

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

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

105
            // 2. Convert to comparison based on stride sign
106

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

118
            // TODO: Modulo case: stride != +/-1
119
            new_clause.push_back(symbolic::Ne(new_lhs, new_rhs));
2✔
120
        }
8✔
121
        new_cnf.push_back(new_clause);
8✔
122
    }
8✔
123
    // Update if changed
124
    {
125
        symbolic::Condition new_condition = symbolic::__true__();
7✔
126
        for (auto& clause : new_cnf) {
15✔
127
            symbolic::Condition new_clause = symbolic::__false__();
8✔
128
            for (auto& literal : clause) {
17✔
129
                new_clause = symbolic::Or(new_clause, literal);
9✔
130
            }
131
            new_condition = symbolic::And(new_condition, new_clause);
8✔
132
        }
8✔
133
        if (!symbolic::eq(new_condition, condition)) {
7✔
134
            builder_.update_loop(loop, loop.indvar(), new_condition, loop.init(), loop.update());
4✔
135
            applied = true;
4✔
136
            condition = new_condition;
4✔
137
        }
4✔
138
    }
7✔
139

140
    // Check uses of indvar in loop body are all memlets for further steps
141
    auto& users_analysis = analysis_manager_.get<analysis::Users>();
7✔
142
    analysis::UsersView body_users(users_analysis, loop.root());
7✔
143
    bool all_subsets = true;
7✔
144
    for (auto& user : body_users.uses(indvar->get_name())) {
8✔
145
        if (!dynamic_cast<data_flow::Memlet*>(user->element())) {
1✔
146
            all_subsets = false;
1✔
147
            break;
1✔
148
        }
149
    }
150
    if (!all_subsets) {
7✔
151
        return applied;
1✔
152
    }
153

154
    try {
155
        new_cnf = symbolic::conjunctive_normal_form(condition);
6✔
156
    } catch (const symbolic::CNFException e) {
6✔
UNCOV
157
        return applied;
×
UNCOV
158
    }
×
159

160
    if (stride > 0) {
6✔
161
        // Step 3: Shift loop to start from zero if possible
162
        if (!symbolic::eq(loop.init(), symbolic::zero())) {
4✔
163
            // Require integer initial value for following steps
NEW
164
            if (!SymEngine::is_a<SymEngine::Integer>(*loop.init())) {
×
NEW
165
                return applied;
×
166
            }
167

NEW
168
            auto new_init = symbolic::zero();
×
NEW
169
            auto actual_indvar = symbolic::add(indvar, loop.init());
×
170
            
171
            // T
NEW
172
            bool canonical_condition = false;
×
NEW
173
            if (new_cnf.size() == 1) {
×
NEW
174
                auto& clause = new_cnf[0];
×
NEW
175
                if (clause.size() == 1) {
×
NEW
176
                    auto literal = clause[0];
×
NEW
177
                    if (SymEngine::is_a<SymEngine::StrictLessThan>(*literal)) {
×
NEW
178
                        auto lhs = literal->get_args().at(0);
×
NEW
179
                        auto rhs = literal->get_args().at(1);
×
NEW
180
                        if (symbolic::eq(lhs, indvar) && !symbolic::uses(rhs, indvar)) {
×
NEW
181
                            condition = symbolic::Lt(indvar, symbolic::sub(rhs, loop.init()));
×
NEW
182
                            canonical_condition = true;
×
NEW
183
                        }
×
NEW
184
                    }
×
NEW
185
                }
×
NEW
186
            }
×
NEW
187
            if (!canonical_condition) {
×
NEW
188
                condition = symbolic::subs(condition, indvar, actual_indvar);
×
NEW
189
            }
×
NEW
190
            builder_.update_loop(loop, indvar, condition, new_init, loop.update());
×
NEW
191
            auto& root = loop.root();
×
NEW
192
            root.replace(indvar, actual_indvar);
×
193

NEW
194
            return true;
×
NEW
195
        }
×
196
    } else if (stride < 0) {
6✔
197
        // Step 4: Rotate loop if stride is negative
198
        if (stride != -1) {
2✔
199
            return applied;
1✔
200
        }
201
        if (new_cnf.size() != 1) {
1✔
NEW
202
            return applied;
×
203
        }
204
        auto& clause = new_cnf[0];
1✔
205
        if (clause.size() != 1) {
1✔
NEW
206
            return applied;
×
207
        }
208
        auto literal = clause[0];
1✔
209
        if (!SymEngine::is_a<SymEngine::StrictLessThan>(*literal)) {
1✔
NEW
210
            return applied;
×
211
        }
212
        auto slt = SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(literal);
1✔
213
        auto lhs = slt->get_args().at(0);
1✔
214
        auto rhs = slt->get_args().at(1);
1✔
215
        if (!symbolic::eq(rhs, indvar)) {
1✔
NEW
216
            return applied;
×
217
        }
218

219
        // Update loop parameters
220
        auto new_init = symbolic::add(lhs, symbolic::one());
1✔
221
        auto new_update = symbolic::add(indvar, symbolic::one());
1✔
222
        auto new_condition = symbolic::Lt(indvar, symbolic::add(loop.init(), symbolic::one()));
1✔
223

224
        // Replace indvar by (init - indvar) in loop body
225
        loop.root().replace(indvar, symbolic::sub(loop.init(), symbolic::sub(indvar, new_init)));
1✔
226

227
        builder_.update_loop(loop, loop.indvar(), new_condition, new_init, new_update);
1✔
228
        return true;
1✔
229
    }
1✔
230

231
    return applied;
4✔
232
};
7✔
233

234
} // namespace passes
235
} // 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