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

daisytuner / docc / 24538134775

16 Apr 2026 10:52PM UTC coverage: 64.329% (+0.04%) from 64.286%
24538134775

Pull #687

github

web-flow
Merge 36b70a163 into 4f1161c02
Pull Request #687: Removes deprecated assumptions analysis functions

569 of 695 new or added lines in 16 files covered. (81.87%)

64 existing lines in 13 files now uncovered.

30791 of 47865 relevant lines covered (64.33%)

573.71 hits per line

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

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

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

6
#include "sdfg/analysis/analysis.h"
7
#include "sdfg/analysis/scope_analysis.h"
8
#include "sdfg/analysis/users.h"
9
#include "sdfg/data_flow/access_node.h"
10
#include "sdfg/data_flow/memlet.h"
11
#include "sdfg/structured_control_flow/structured_loop.h"
12
#include "sdfg/symbolic/assumptions.h"
13
#include "sdfg/symbolic/polynomials.h"
14
#include "sdfg/symbolic/symbolic.h"
15
#include "sdfg/types/type.h"
16

17
namespace sdfg {
18
namespace analysis {
19

20
AssumptionsAnalysis::AssumptionsAnalysis(StructuredSDFG& sdfg)
21
    : Analysis(sdfg) {
263✔
22

23
      };
263✔
24

25
void AssumptionsAnalysis::run(analysis::AnalysisManager& analysis_manager) {
263✔
26
    this->assumptions_.clear();
263✔
27
    this->assumptions_with_trivial_.clear();
263✔
28
    this->ref_assumptions_.clear();
263✔
29
    this->ref_assumptions_with_trivial_.clear();
263✔
30

31
    this->parameters_.clear();
263✔
32
    this->users_analysis_ = &analysis_manager.get<Users>();
263✔
33

34
    // Determine parameters
35
    this->determine_parameters(analysis_manager);
263✔
36

37
    // Initialize root assumptions with SDFG-level assumptions
38
    this->assumptions_.insert({&sdfg_.root(), this->additional_assumptions_});
263✔
39
    auto& initial = this->assumptions_[&sdfg_.root()];
263✔
40

41
    this->assumptions_with_trivial_.insert({&sdfg_.root(), initial});
263✔
42
    auto& initial_with_trivial = this->assumptions_with_trivial_[&sdfg_.root()];
263✔
43
    for (auto& entry : sdfg_.assumptions()) {
1,643✔
44
        if (initial_with_trivial.find(entry.first) == initial_with_trivial.end()) {
1,643✔
45
            initial_with_trivial.insert({entry.first, entry.second});
1,643✔
46
        } else {
1,643✔
47
            for (auto& lb : entry.second.lower_bounds()) {
×
48
                initial_with_trivial.at(entry.first).add_lower_bound(lb);
×
49
            }
×
50
            for (auto& ub : entry.second.upper_bounds()) {
×
51
                initial_with_trivial.at(entry.first).add_upper_bound(ub);
×
52
            }
×
53
        }
×
54
    }
1,643✔
55

56
    // Traverse and propagate
57
    this->traverse(sdfg_.root(), initial, initial_with_trivial);
263✔
58
};
263✔
59

60
void AssumptionsAnalysis::traverse(
61
    structured_control_flow::ControlFlowNode& current,
62
    const symbolic::Assumptions& outer_assumptions,
63
    const symbolic::Assumptions& outer_assumptions_with_trivial
64
) {
2,676✔
65
    this->propagate_ref(current, outer_assumptions, outer_assumptions_with_trivial);
2,676✔
66

67
    if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(&current)) {
2,676✔
68
        for (size_t i = 0; i < sequence_stmt->size(); i++) {
2,677✔
69
            this->traverse(sequence_stmt->at(i).first, outer_assumptions, outer_assumptions_with_trivial);
1,563✔
70
        }
1,563✔
71
    } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(&current)) {
1,562✔
72
        for (size_t i = 0; i < if_else_stmt->size(); i++) {
19✔
73
            this->traverse(if_else_stmt->at(i).first, outer_assumptions, outer_assumptions_with_trivial);
12✔
74
        }
12✔
75
    } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(&current)) {
1,555✔
76
        this->traverse(while_stmt->root(), outer_assumptions, outer_assumptions_with_trivial);
8✔
77
    } else if (auto loop_stmt = dynamic_cast<structured_control_flow::StructuredLoop*>(&current)) {
1,547✔
78
        this->traverse_structured_loop(loop_stmt, outer_assumptions, outer_assumptions_with_trivial);
830✔
79
    } else {
830✔
80
        // Other control flow nodes (e.g., Block) do not introduce assumptions or comprise scopes
81
    }
717✔
82
};
2,676✔
83

84
void AssumptionsAnalysis::traverse_structured_loop(
85
    structured_control_flow::StructuredLoop* loop,
86
    const symbolic::Assumptions& outer_assumptions,
87
    const symbolic::Assumptions& outer_assumptions_with_trivial
88
) {
830✔
89
    // A structured loop induces assumption for the loop body
90
    auto& body = loop->root();
830✔
91
    symbolic::Assumptions body_assumptions;
830✔
92

93
    // Define all constant symbols
94
    auto indvar = loop->indvar();
830✔
95
    auto update = loop->update();
830✔
96
    auto init = loop->init();
830✔
97

98
    // By definition, all symbols in the loop condition are constant within the loop body
99
    symbolic::SymbolSet loop_syms = symbolic::atoms(loop->condition());
830✔
100
    for (auto& sym : loop_syms) {
1,785✔
101
        body_assumptions.insert({sym, symbolic::Assumption(sym)});
1,785✔
102
        body_assumptions[sym].constant(true);
1,785✔
103
    }
1,785✔
104

105
    // Define map of indvar
106
    body_assumptions[indvar].map(update);
830✔
107
    body_assumptions[indvar].constant(true);
830✔
108

109
    // Monotonic -> infer bounds on indvar
110
    symbolic::Integer stride = loop->stride();
830✔
111
    if (!stride.is_null() && loop->is_monotonic()) {
830✔
112
        body_assumptions[indvar].add_lower_bound(init);
829✔
113
        body_assumptions[indvar].tight_lower_bound(init);
829✔
114

115
        auto ub = loop->canonical_bound_upper();
829✔
116
        if (!ub.is_null()) {
829✔
117
            // Convert into inclusive bound
118
            symbolic::Expression ub_inclusive;
825✔
119
            if (SymEngine::is_a<SymEngine::Min>(*ub)) {
825✔
120
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub);
88✔
121
                std::vector<symbolic::Expression> inclusive_args;
88✔
122
                for (size_t i = 0; i < min->get_args().size(); i++) {
276✔
123
                    auto arg = min->get_args()[i];
188✔
124
                    inclusive_args.push_back(symbolic::sub(arg, symbolic::one()));
188✔
125
                }
188✔
126
                ub_inclusive = inclusive_args.at(0);
88✔
127
                for (size_t i = 1; i < inclusive_args.size(); i++) {
188✔
128
                    ub_inclusive = symbolic::min(ub_inclusive, inclusive_args.at(i));
100✔
129
                }
100✔
130
            } else {
737✔
131
                ub_inclusive = symbolic::sub(ub, symbolic::one());
737✔
132
            }
737✔
133

134
            // ub is a general upper bound
135
            // Compute tight upper bound based on stride
136
            if (symbolic::eq(stride, symbolic::one())) {
825✔
137
                // Stride == 1: tight upper bound is simply ub - 1
138
                body_assumptions[indvar].tight_upper_bound(ub_inclusive);
739✔
139
            } else if (!stride.is_null()) {
739✔
140
                // Non-unit stride: tight upper bound = init + idiv(ub_inclusive - init, stride) * stride
141
                // This is the largest value of init + k*stride that is <= ub_inclusive
142
                auto range = symbolic::sub(ub_inclusive, init);
86✔
143
                auto num_steps = symbolic::div(range, stride);
86✔
144
                auto tight_ub = symbolic::add(init, symbolic::mul(num_steps, stride));
86✔
145
                body_assumptions[indvar].tight_upper_bound(tight_ub);
86✔
146
            }
86✔
147

148
            // If combined bound, each arg is also an upper bound
149
            if (SymEngine::is_a<SymEngine::Min>(*ub)) {
825✔
150
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub);
88✔
151
                for (size_t i = 0; i < min->get_args().size(); i++) {
276✔
152
                    auto arg = min->get_args()[i];
188✔
153
                    auto arg_inclusive = symbolic::sub(arg, symbolic::one());
188✔
154
                    body_assumptions[indvar].add_upper_bound(arg_inclusive);
188✔
155
                }
188✔
156
            } else {
737✔
157
                body_assumptions[indvar].add_upper_bound(ub_inclusive);
737✔
158
            }
737✔
159

160
            // Furthermore, we can infer lower bounds for each upper bound's symbol
161
            // For a loop to execute, we need ub > init, so ub >= init + 1
162
            auto min_ub_value = symbolic::add(init, symbolic::one());
825✔
163

164
            // Helper to infer lower bound for symbols in an expression
165
            // If expr = coeff * sym + offset and we need expr >= min_ub_value,
166
            // then sym >= (min_ub_value - offset) / coeff
167
            auto infer_symbol_lower_bound = [&](const symbolic::Expression& expr) {
925✔
168
                auto atoms = symbolic::atoms(expr);
925✔
169
                for (const auto& sym : atoms) {
953✔
170
                    auto bound = symbolic::solve_affine_bound(expr, sym, min_ub_value, true);
953✔
171
                    if (!bound.is_null()) {
953✔
172
                        body_assumptions[sym].add_lower_bound(bound);
887✔
173
                    }
887✔
174
                }
953✔
175
            };
925✔
176

177
            if (SymEngine::is_a<SymEngine::Min>(*ub)) {
825✔
178
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub);
88✔
179
                for (size_t i = 0; i < min->get_args().size(); i++) {
276✔
180
                    auto arg = min->get_args()[i];
188✔
181
                    infer_symbol_lower_bound(arg);
188✔
182
                }
188✔
183
            } else {
737✔
184
                infer_symbol_lower_bound(ub);
737✔
185
            }
737✔
186
        }
825✔
187
    }
829✔
188

189
    this->propagate(body, body_assumptions, outer_assumptions, outer_assumptions_with_trivial);
830✔
190
    this->traverse(body, this->assumptions_[&body], this->assumptions_with_trivial_[&body]);
830✔
191
}
830✔
192

193
void AssumptionsAnalysis::propagate(
194
    structured_control_flow::ControlFlowNode& node,
195
    const symbolic::Assumptions& node_assumptions,
196
    const symbolic::Assumptions& outer_assumptions,
197
    const symbolic::Assumptions& outer_assumptions_with_trivial
198
) {
830✔
199
    // Propagate assumptions
200
    this->assumptions_.insert({&node, node_assumptions});
830✔
201
    auto& propagated_assumptions = this->assumptions_[&node];
830✔
202
    for (auto& entry : outer_assumptions) {
1,623✔
203
        if (propagated_assumptions.find(entry.first) == propagated_assumptions.end()) {
1,623✔
204
            // New assumption
205
            propagated_assumptions.insert({entry.first, entry.second});
1,234✔
206
            continue;
1,234✔
207
        }
1,234✔
208

209
        // Merge assumptions from lower scopes
210
        auto& lower_assum = propagated_assumptions[entry.first];
389✔
211

212
        // Add to set of bounds
213
        for (auto ub : entry.second.upper_bounds()) {
389✔
214
            lower_assum.add_upper_bound(ub);
190✔
215
        }
190✔
216
        for (auto lb : entry.second.lower_bounds()) {
435✔
217
            lower_assum.add_lower_bound(lb);
435✔
218
        }
435✔
219

220
        // Set tight bounds
221
        if (lower_assum.tight_upper_bound().is_null()) {
389✔
222
            lower_assum.tight_upper_bound(entry.second.tight_upper_bound());
381✔
223
        }
381✔
224
        if (lower_assum.tight_lower_bound().is_null()) {
389✔
225
            lower_assum.tight_lower_bound(entry.second.tight_lower_bound());
379✔
226
        }
379✔
227

228
        // Set map
229
        if (lower_assum.map().is_null()) {
389✔
230
            lower_assum.map(entry.second.map());
379✔
231
        }
379✔
232

233
        // Set constant
234
        if (!lower_assum.constant()) {
389✔
235
            lower_assum.constant(entry.second.constant());
×
236
        }
×
237
    }
389✔
238

239
    this->assumptions_with_trivial_.insert({&node, node_assumptions});
830✔
240
    auto& assumptions_with_trivial = this->assumptions_with_trivial_[&node];
830✔
241
    for (auto& entry : outer_assumptions_with_trivial) {
7,202✔
242
        if (assumptions_with_trivial.find(entry.first) == assumptions_with_trivial.end()) {
7,202✔
243
            // New assumption
244
            assumptions_with_trivial.insert({entry.first, entry.second});
5,417✔
245
            continue;
5,417✔
246
        }
5,417✔
247
        // Merge assumptions from lower scopes
248
        auto& lower_assum = assumptions_with_trivial[entry.first];
1,785✔
249

250
        // Add to set of bounds
251
        for (auto ub : entry.second.upper_bounds()) {
1,975✔
252
            lower_assum.add_upper_bound(ub);
1,975✔
253
        }
1,975✔
254
        for (auto lb : entry.second.lower_bounds()) {
2,089✔
255
            lower_assum.add_lower_bound(lb);
2,089✔
256
        }
2,089✔
257

258
        // Set tight bounds
259
        if (lower_assum.tight_upper_bound().is_null()) {
1,785✔
260
            lower_assum.tight_upper_bound(entry.second.tight_upper_bound());
960✔
261
        }
960✔
262
        if (lower_assum.tight_lower_bound().is_null()) {
1,785✔
263
            lower_assum.tight_lower_bound(entry.second.tight_lower_bound());
956✔
264
        }
956✔
265

266
        // Set map
267
        if (lower_assum.map().is_null()) {
1,785✔
268
            lower_assum.map(entry.second.map());
955✔
269
        }
955✔
270

271
        // Set constant
272
        if (!lower_assum.constant()) {
1,785✔
UNCOV
273
            lower_assum.constant(entry.second.constant());
×
UNCOV
274
        }
×
275
    }
1,785✔
276
}
830✔
277

278
void AssumptionsAnalysis::propagate_ref(
279
    structured_control_flow::ControlFlowNode& node,
280
    const symbolic::Assumptions& outer_assumptions,
281
    const symbolic::Assumptions& outer_assumptions_with_trivial
282
) {
2,676✔
283
    this->ref_assumptions_.insert({&node, &outer_assumptions});
2,676✔
284
    this->ref_assumptions_with_trivial_.insert({&node, &outer_assumptions_with_trivial});
2,676✔
285
}
2,676✔
286

287
void AssumptionsAnalysis::determine_parameters(analysis::AnalysisManager& analysis_manager) {
263✔
288
    for (auto& container : this->sdfg_.arguments()) {
1,025✔
289
        bool readonly = true;
1,025✔
290
        Use not_allowed;
1,025✔
291
        switch (this->sdfg_.type(container).type_id()) {
1,025✔
292
            case types::TypeID::Scalar:
455✔
293
                not_allowed = Use::WRITE;
455✔
294
                break;
455✔
295
            case types::TypeID::Pointer:
332✔
296
                not_allowed = Use::MOVE;
332✔
297
                break;
332✔
298
            case types::TypeID::Array:
237✔
299
            case types::TypeID::Structure:
237✔
300
            case types::TypeID::Reference:
238✔
301
            case types::TypeID::Function:
238✔
302
            case types::TypeID::Tensor:
238✔
303
                continue;
238✔
304
        }
1,025✔
305
        for (auto user : this->users_analysis_->uses(container)) {
1,603✔
306
            if (user->use() == not_allowed) {
1,603✔
307
                readonly = false;
3✔
308
                break;
3✔
309
            }
3✔
310
        }
1,603✔
311
        if (readonly) {
787✔
312
            this->parameters_.insert(symbolic::symbol(container));
784✔
313
        }
784✔
314
    }
787✔
315
}
263✔
316

317
const symbolic::Assumptions& AssumptionsAnalysis::
318
    get(structured_control_flow::ControlFlowNode& node, bool include_trivial_bounds) {
6,780✔
319
    if (include_trivial_bounds) {
6,780✔
320
        return *this->ref_assumptions_with_trivial_[&node];
6,561✔
321
    } else {
6,561✔
322
        return *this->ref_assumptions_[&node];
219✔
323
    }
219✔
324
}
6,780✔
325

326
const symbolic::SymbolSet& AssumptionsAnalysis::parameters() { return this->parameters_; }
5✔
327

328
bool AssumptionsAnalysis::is_parameter(const symbolic::Symbol& container) {
9✔
329
    return this->parameters_.contains(container);
9✔
330
}
9✔
331

332
bool AssumptionsAnalysis::is_parameter(const std::string& container) {
9✔
333
    return this->is_parameter(symbolic::symbol(container));
9✔
334
}
9✔
335

336
} // namespace analysis
337
} // 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