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

daisytuner / sdfglib / 19279785005

11 Nov 2025 10:06PM UTC coverage: 61.729% (-0.04%) from 61.767%
19279785005

push

github

web-flow
Merge pull request #342 from daisytuner/local-mem-access-range

allow mem access range analysis to form expressions of local parameters

30 of 34 new or added lines in 7 files covered. (88.24%)

18 existing lines in 2 files now uncovered.

10547 of 17086 relevant lines covered (61.73%)

108.11 hits per line

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

86.49
/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
    auto builder = MemAccessRangesBuilder(sdfg_, node, users, assumptions_analysis);
13✔
35

36
    auto& worklist = builder.worklist_;
13✔
37

38
    // Initialize worklist with containers
39
    for (const auto& container : target_containers) {
28✔
40
        worklist.push_back(new WorkItem{&container});
15✔
41
    }
42

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

51
    this->ranges_.insert_or_assign(&node, std::move(builder.ranges_));
13✔
52
}
13✔
53

54
void MemAccessRanges::run(analysis::AnalysisManager& analysis_manager) {
9✔
55
    this->analysis_manager_ = &analysis_manager;
9✔
56
    std::unordered_set<std::string> containers;
9✔
57

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

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

72
    this->run(sdfg_.root(), containers);
9✔
73
}
9✔
74

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

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

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

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

117
bool MemAccessRange::saw_read() const { return saw_read_; }
8✔
118
bool MemAccessRange::saw_write() const { return saw_write_; }
8✔
119
bool MemAccessRange::is_undefined() const { return undefined_; }
8✔
120

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

123
void MemAccessRangesBuilder::process_workItem(WorkItem* item) {
15✔
124
    analysis::UsersView users_(users_analysis_, node_);
15✔
125

126
    const auto* varName = item->var_name;
15✔
127

128
    const auto& reads = users_.reads(*varName);
15✔
129
    process_direct_users(item, false, reads);
15✔
130

131
    const auto& writes = users_.writes(*varName);
15✔
132
    process_direct_users(item, true, writes);
15✔
133

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

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

146
    if (!item->dims.empty()) {
15✔
147
        std::vector<std::pair<symbolic::Expression, symbolic::Expression>> finalDims;
11✔
148
        finalDims.reserve(item->dims.size());
11✔
149

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

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

174
            finalDims.emplace_back(std::move(lb), std::move(ub));
13✔
175
        }
13✔
176

177
        this->ranges_.emplace(
22✔
178
            std::piecewise_construct,
179
            std::forward_as_tuple(*varName),
11✔
180
            std::forward_as_tuple(*varName, item->saw_read, item->saw_write, item->undefined, std::move(finalDims))
11✔
181
        );
182
    }
11✔
183
}
15✔
184

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

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

217
        item->saw_read |= !is_write;
17✔
218
        item->saw_write |= is_write;
17✔
219

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

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

247
                ++dimIdx;
13✔
248
            }
13✔
249
        }
250
    }
17✔
251
}
30✔
252

253
} // namespace analysis
254
} // 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

© 2025 Coveralls, Inc