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

daisytuner / sdfglib / 19271908853

11 Nov 2025 04:23PM UTC coverage: 61.746% (-0.02%) from 61.767%
19271908853

Pull #342

github

web-flow
Merge bb8d82280 into f599d1bac
Pull Request #342: allow mem access range analysis to form expressions of local parameters

29 of 31 new or added lines in 3 files covered. (93.55%)

18 existing lines in 2 files now uncovered.

10548 of 17083 relevant lines covered (61.75%)

108.13 hits per line

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

86.75
/src/analysis/mem_access_range_analysis.cpp
1

2
#include "sdfg/analysis/mem_access_range_analysis.h"
3

4
#include <stdbool.h>
5
#include <symengine/basic.h>
6
#include <symengine/functions.h>
7
#include <symengine/infinity.h>
8
#include <symengine/number.h>
9
#include <symengine/symengine_rcp.h>
10

11
#include <tuple>
12
#include <unordered_set>
13
#include <utility>
14
#include <vector>
15

16
#include "sdfg/analysis/analysis.h"
17
#include "sdfg/analysis/assumptions_analysis.h"
18
#include "sdfg/analysis/mem_access_range_analysis_internal.h"
19
#include "sdfg/analysis/users.h"
20
#include "sdfg/helpers/helpers.h"
21
#include "sdfg/symbolic/extreme_values.h"
22
#include "sdfg/symbolic/symbolic.h"
23

24
namespace sdfg {
25
namespace analysis {
26

27
MemAccessRanges::MemAccessRanges(StructuredSDFG& sdfg) : Analysis(sdfg), graph_() {}
9✔
28

29
void MemAccessRanges::
30
    run(structured_control_flow::ControlFlowNode& node, std::unordered_set<std::string> target_containers) {
13✔
31
    auto& users = analysis_manager_->get<Users>();
13✔
32
    auto& assumptions_analysis = analysis_manager_->get<AssumptionsAnalysis>();
13✔
33

34
    if (&node != &sdfg_.root()) {
13✔
35
        std::cout << "Warning: MemAccessRanges::run called on non-root node. Results may be incomplete." << std::endl;
2✔
36
    }
2✔
37

38
    auto builder = MemAccessRangesBuilder(sdfg_, node, users, assumptions_analysis);
13✔
39

40
    auto& worklist = builder.worklist_;
13✔
41

42
    // Initialize worklist with containers
43
    for (const auto& container : target_containers) {
28✔
44
        worklist.push_back(new WorkItem{&container});
15✔
45
    }
46

47
    // Iterate over all variables and their users
48
    while (!worklist.empty()) {
28✔
49
        auto* workItem = worklist.front();
15✔
50
        builder.process_workItem(workItem);
15✔
51
        worklist.pop_front();
15✔
52
        delete workItem;
15✔
53
    }
54

55
    this->ranges_.insert_or_assign(&node, std::move(builder.ranges_));
13✔
56
}
13✔
57

58
void MemAccessRanges::run(analysis::AnalysisManager& analysis_manager) {
9✔
59
    this->analysis_manager_ = &analysis_manager;
9✔
60
    std::unordered_set<std::string> containers;
9✔
61

62
    // Collect argument names
63
    for (auto& arg : sdfg_.arguments()) {
28✔
64
        if (sdfg_.type(arg).type_id() != types::TypeID::Scalar) {
19✔
65
            containers.insert(arg);
11✔
66
        }
11✔
67
    }
68

69
    // Collect external names
70
    for (auto& ext : sdfg_.externals()) {
9✔
71
        if (sdfg_.type(ext).type_id() != types::TypeID::Scalar) {
×
72
            containers.insert(ext);
×
73
        }
×
74
    }
75

76
    this->run(sdfg_.root(), containers);
9✔
77
}
9✔
78

79
const MemAccessRange* MemAccessRanges::get(const std::string& varName) const {
11✔
80
    auto ranges = this->ranges_.find(&sdfg_.root());
11✔
81
    if (ranges == this->ranges_.end()) {
11✔
82
        return nullptr;
×
83
    }
84
    auto res = ranges->second.find(varName);
11✔
85
    if (res != ranges->second.end()) {
11✔
86
        return &res->second;
8✔
87
    } else {
88
        return nullptr;
3✔
89
    }
90
}
11✔
91

92
const MemAccessRange* MemAccessRanges::
93
    get(const std::string& varName,
4✔
94
        structured_control_flow::ControlFlowNode& node,
95
        std::unordered_set<std::string> target_nodes) {
96
    auto ranges = this->ranges_.find(&node);
4✔
97
    this->run(node, target_nodes);
4✔
98
    ranges = this->ranges_.find(&node);
4✔
99
    if (ranges == this->ranges_.end()) {
4✔
100
        return nullptr;
×
101
    }
102
    auto res = ranges->second.find(varName);
4✔
103
    if (res != ranges->second.end()) {
4✔
104
        return &res->second;
×
105
    } else {
106
        return nullptr;
4✔
107
    }
108
}
4✔
109

110
MemAccessRange::MemAccessRange(
11✔
111
    const std::string& name,
112
    bool saw_read,
113
    bool saw_write,
114
    bool undefined,
115
    const std::vector<std::pair<symbolic::Expression, symbolic::Expression>>&& dims
116
)
117
    : name_(name), saw_read_(saw_read), saw_write_(saw_write), undefined_(undefined), dims_(dims) {}
11✔
118

119
const std::string& MemAccessRange::get_name() const { return name_; }
8✔
120

121
bool MemAccessRange::saw_read() const { return saw_read_; }
8✔
122
bool MemAccessRange::saw_write() const { return saw_write_; }
8✔
123
bool MemAccessRange::is_undefined() const { return undefined_; }
8✔
124

125
const std::vector<std::pair<symbolic::Expression, symbolic::Expression>>& MemAccessRange::dims() const { return dims_; }
8✔
126

127
void MemAccessRangesBuilder::process_workItem(WorkItem* item) {
15✔
128
    analysis::UsersView users_(users_analysis_, node_);
15✔
129

130
    const auto* varName = item->var_name;
15✔
131

132
    const auto& reads = users_.reads(*varName);
15✔
133
    process_direct_users(item, false, reads);
15✔
134

135
    const auto& writes = users_.writes(*varName);
15✔
136
    process_direct_users(item, true, writes);
15✔
137

138
    const auto& views = users_.views(*varName);
15✔
139
    if (!views.empty()) {
15✔
140
        DEBUG_PRINTLN("Found views for " << *varName << " => not rangeable!");
×
141
        item->undefined = true;
×
142
    }
×
143

144
    const auto& moves = users_.moves(*varName);
15✔
145
    if (!moves.empty()) {
15✔
146
        DEBUG_PRINTLN("Found moves for " << *varName << " => not rangeable!");
×
147
        item->undefined = true;
×
148
    }
×
149

150
    if (!item->dims.empty()) {
15✔
151
        std::vector<std::pair<symbolic::Expression, symbolic::Expression>> finalDims;
11✔
152
        finalDims.reserve(item->dims.size());
11✔
153

154
        for (auto& dim : item->dims) {
24✔
155
            auto& lowerExprs = std::get<0>(dim);
13✔
156
            bool isLowerUndefined = std::get<1>(dim);
13✔
157
            symbolic::Expression lb = (!lowerExprs.empty() && !isLowerUndefined)
26✔
158
                                          ? SymEngine::min(lowerExprs)
12✔
159
                                          : SymEngine::RCP<const SymEngine::Basic>();
1✔
160
            auto& upperExprs = std::get<2>(dim);
13✔
161
            bool isUpperUndefined = std::get<3>(dim);
13✔
162
            symbolic::Expression ub = (!upperExprs.empty() && !isUpperUndefined)
26✔
163
                                          ? SymEngine::max(upperExprs)
12✔
164
                                          : SymEngine::RCP<const SymEngine::Basic>();
1✔
165

166
            if (lb.is_null() || ub.is_null()) {
13✔
167
                item->undefined = true;
1✔
168
            }
1✔
169
            if (!lb.is_null() && SymEngine::is_a<SymEngine::Infty>(*lb)) {
13✔
UNCOV
170
                lb = SymEngine::null;
×
UNCOV
171
                item->undefined = true;
×
UNCOV
172
            }
×
173
            if (!ub.is_null() && SymEngine::is_a<SymEngine::Infty>(*ub)) {
13✔
UNCOV
174
                ub = SymEngine::null;
×
UNCOV
175
                item->undefined = true;
×
UNCOV
176
            }
×
177

178
            finalDims.emplace_back(std::move(lb), std::move(ub));
13✔
179
        }
13✔
180

181
        this->ranges_.emplace(
22✔
182
            std::piecewise_construct,
183
            std::forward_as_tuple(*varName),
11✔
184
            std::forward_as_tuple(*varName, item->saw_read, item->saw_write, item->undefined, std::move(finalDims))
11✔
185
        );
186
    }
11✔
187
}
15✔
188

189
void MemAccessRangesBuilder::process_direct_users(WorkItem* item, bool is_write, std::vector<User*> accesses) {
30✔
190
    for (auto& access : accesses) {
47✔
191
        // The actual range analysis replaces symbols used in subsets
192
        // by their lower/upper bounds according to the assumptions analysis.
193
        // For this, we take the immediate scope to get the richest assumptions.
194
        const auto& user_scope = analysis::Users::scope(access);
17✔
195
        auto assums = assumptions_analysis_.get(*user_scope, false);
17✔
196

197
        // The final expression must be an expression w.r.t parameters,
198
        // i.e., constant symbols w.r.t the actual node.
199
        // Note we can compute this more efficiently once, but
200
        // we want to move this to the assumptions analysis anyway
201
        analysis::UsersView users_view(users_analysis_, node_);
17✔
202
        symbolic::SymbolSet params;
17✔
203
        for (auto& user : users_view.uses()) {
148✔
204
            if (user->container() == symbolic::__nullptr__()->get_name()) {
131✔
NEW
205
                continue;
×
206
            }
207
            auto& type = sdfg_.type(user->container());
131✔
208
            if (type.type_id() != types::TypeID::Scalar) {
131✔
209
                continue;
21✔
210
            }
211
            auto& scalar_type = static_cast<const types::Scalar&>(type);
110✔
212
            if (!types::is_integer(scalar_type.primitive_type())) {
110✔
NEW
213
                continue;
×
214
            }
215
            if (users_view.writes(user->container()).size() > 0) {
110✔
216
                continue;
100✔
217
            }
218
            params.insert(symbolic::symbol(user->container()));
10✔
219
        }
220

221
        item->saw_read |= !is_write;
17✔
222
        item->saw_write |= is_write;
17✔
223

224
        auto subsets = access->subsets();
17✔
225
        for (const auto& subset : subsets) {
34✔
226
            auto subsetDims = subset.size();
17✔
227
            item->dims.reserve(subsetDims);
17✔
228
            for (size_t i = item->dims.size(); i < subsetDims; ++i) {
30✔
229
                item->dims.emplace_back(std::make_tuple<
26✔
230
                                        std::vector<symbolic::Expression>,
231
                                        bool,
232
                                        std::vector<symbolic::Expression>,
233
                                        bool>({}, false, {}, false));
13✔
234
            }
13✔
235
            int dimIdx = 0;
17✔
236
            for (auto& dim : subset) {
30✔
237
                auto lb = symbolic::minimum(dim, params, assums);
13✔
238
                auto ub = symbolic::maximum(dim, params, assums);
13✔
239

240
                if (lb.is_null() || symbolic::has<SymEngine::Infty>(lb)) {
13✔
241
                    std::get<1>(item->dims[dimIdx]) = true;
1✔
242
                } else {
1✔
243
                    std::get<0>(item->dims[dimIdx]).push_back(lb);
12✔
244
                }
245
                if (ub.is_null() || symbolic::has<SymEngine::Infty>(ub)) {
13✔
246
                    std::get<3>(item->dims[dimIdx]) = true;
1✔
247
                } else {
1✔
248
                    std::get<2>(item->dims[dimIdx]).push_back(ub);
12✔
249
                }
250

251
                ++dimIdx;
13✔
252
            }
13✔
253
        }
254
    }
17✔
255
}
30✔
256

257
} // namespace analysis
258
} // 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