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

daisytuner / docc / 26521975951

27 May 2026 03:45PM UTC coverage: 60.869% (-0.02%) from 60.886%
26521975951

push

github

web-flow
Libnode ptr edges (#719)

Migrating SDFGs to treat pointers as inputs to libNodes / Calls as scalars.
A pointer will only appear in an output edge if its actually returned from the function (like malloc).

* Stdlib, Blas and Tensor Matmul nodes were migrated to this new format. Other, currently transitory Tensor Nodes are not yet migrated.
* DOCC version was bumped to incorporate previous docc-llvm versions (up to 0.4.0) that had been counted separately.
! Until all passes consider the use / leak of pointers as uncertainty / hiding potential writes, TensorNodes are declared as general side-effect.
* Lots of utility functions to centralize the creation (and edges) of various libNodes that needed to be changed.
* Fixed & unified docc paths across python and llvm front-ends.
* Skip BlockFusion test that fails to its libNodes currently having side effects
~ Prevent a crash in DotViz when using symbolic offsets into structs
* Removing old ConstProp pass, it is not safe for the new pointer representation and should not be all too critical

961 of 1749 new or added lines in 52 files covered. (54.95%)

87 existing lines in 28 files now uncovered.

35225 of 57870 relevant lines covered (60.87%)

11046.32 hits per line

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

34.25
/sdfg/src/data_flow/library_nodes/math/tensor/tensor_layout.cpp
1
#include "sdfg/data_flow/library_nodes/math/tensor/tensor_layout.h"
2

3
#include "sdfg/serializer/json_serializer.h"
4

5
namespace sdfg::math::tensor {
6

7
TensorLayout::TensorLayout(
8
    const symbolic::MultiExpression& shape, const symbolic::MultiExpression& strides, const symbolic::Expression offset
9
)
10
    : shape_(shape), strides_(strides), offset_(offset) {
4,446✔
11
    if (strides.empty()) {
4,446✔
12
        strides_ = linear_strides();
2,977✔
13
    }
2,977✔
14
}
4,446✔
15

16
void TensorLayout::serialize_to_json(nlohmann::json& j) const {
×
17
    nlohmann::json shape_arr = nlohmann::json::array();
×
18
    sdfg::serializer::JSONSerializer serializer;
×
19

20
    for (auto& dim : shape_) {
×
21
        shape_arr.push_back(serializer.expression(dim));
×
22
    }
×
23
    j["shape"] = shape_arr;
×
24

25
    nlohmann::json stride_arr = nlohmann::json::array();
×
26
    for (auto& stride : strides_) {
×
27
        stride_arr.push_back(serializer.expression(stride));
×
28
    }
×
29
    j["strides"] = stride_arr;
×
30

31
    j["offset"] = serializer.expression(offset_);
×
32
}
×
33

34
std::string TensorLayout::toStr() const {
×
35
    std::stringstream ss;
×
36
    ss << "TLayout(shape=[";
×
37
    for (auto& s : shape_) {
×
38
        ss << s->__str__() << ",";
×
39
    }
×
40
    ss << "], strides=[";
×
41
    for (auto& s : strides_) {
×
42
        ss << s->__str__() << ",";
×
43
    }
×
44
    ss << "])";
×
45
    return ss.str();
×
46
}
×
47

48
symbolic::MultiExpression TensorLayout::linear_strides(const symbolic::MultiExpression& shape) {
2,978✔
49
    symbolic::MultiExpression lin_strides;
2,978✔
50
    if (shape.empty()) {
2,978✔
51
        return lin_strides; // no shape -> no strides. Just a wrapper hiding a scalar
30✔
52
    }
30✔
53
    std::size_t dims = shape.size();
2,948✔
54
    lin_strides.resize(dims);
2,948✔
55
    lin_strides[dims - 1] = symbolic::integer(1);
2,948✔
56
    for (int i = static_cast<int>(dims) - 2; i >= 0; --i) {
4,743✔
57
        lin_strides[i] = symbolic::mul(lin_strides.at(i + 1), shape.at(i + 1));
1,795✔
58
    }
1,795✔
59

60
    return std::move(lin_strides);
2,948✔
61
}
2,978✔
62
void TensorLayout::collect_symbols(symbolic::SymbolSet& syms) const {
26✔
63
    for (const auto& dim : shape_) {
100✔
64
        for (auto& atom : symbolic::atoms(dim)) {
100✔
65
            syms.insert(atom);
4✔
66
        }
4✔
67
    }
100✔
68
    for (const auto& dim : strides_) {
100✔
69
        for (auto& atom : symbolic::atoms(dim)) {
100✔
70
            syms.insert(atom);
2✔
71
        }
2✔
72
    }
100✔
73
    for (auto& atom : symbolic::atoms(offset_)) {
26✔
74
        syms.insert(atom);
×
75
    }
×
76
}
26✔
77

78
void TensorLayout::replace_symbols(const symbolic::Expression& old, const symbolic::Expression& new_expr) {
×
79
    for (auto& dim : shape_) {
×
80
        dim = symbolic::subs(dim, old, new_expr);
×
81
    }
×
82
    for (auto& stride : strides_) {
×
83
        stride = symbolic::subs(stride, old, new_expr);
×
84
    }
×
85
    offset_ = symbolic::subs(offset_, old, new_expr);
×
86
}
×
87

88
symbolic::Expression TensorLayout::total_elements() const { return SymEngine::mul(shape_); }
1✔
89

90
symbolic::MultiExpression TensorLayout::linear_strides() const { return std::move(linear_strides(shape_)); }
2,977✔
91

92
TensorLayout TensorLayout::deserialize_from_json(const nlohmann::json& j) {
×
93
    symbolic::MultiExpression shape;
×
94
    for (const auto& dim : j["shape"]) {
×
95
        shape.push_back(symbolic::parse(dim.get<std::string>()));
×
96
    }
×
97

98
    symbolic::MultiExpression strides;
×
99
    for (const auto& stride : j["strides"]) {
×
100
        strides.push_back(symbolic::parse(stride.get<std::string>()));
×
101
    }
×
102

103
    symbolic::Expression offset = symbolic::parse(j["offset"].get<std::string>());
×
104

105
    return std::move(TensorLayout(shape, strides, offset));
×
106
}
×
107

108
std::ostream& operator<<(std::ostream& stream, const TensorLayout& layout) {
×
109
    stream << "{shape[";
×
110
    for (size_t i = 0; i < layout.shape().size(); ++i) {
×
111
        if (i > 0) stream << ", ";
×
112
        stream << layout.shape().at(i)->__str__();
×
113
    }
×
114
    stream << "], strides=[";
×
115
    for (size_t i = 0; i < layout.strides().size(); ++i) {
×
116
        if (i > 0) stream << ", ";
×
117
        stream << layout.strides().at(i)->__str__();
×
118
    }
×
119
    stream << "]";
×
120
    if (SymEngine::neq(*layout.offset(), *symbolic::integer(0))) {
×
121
        stream << ", off=" << layout.offset()->__str__();
×
122
    }
×
123
    stream << "}";
×
124

125
    return stream;
×
126
}
×
127

128
bool TensorLayout::has_linear_accesses_no_padding(symbolic::MultiExpression shape, symbolic::MultiExpression strides) {
8✔
129
    auto basic_strides = types::Tensor::strides_from_shape(shape);
8✔
130
    if (basic_strides.size() != strides.size()) {
8✔
131
        return false;
×
132
    }
×
133
    for (size_t i = 0; i < strides.size(); i++) {
26✔
134
        if (!symbolic::eq(basic_strides.at(i), strides.at(i))) {
18✔
135
            return false;
×
136
        }
×
137
    }
18✔
138
    return true;
8✔
139
}
8✔
140

141
bool TensorLayout::has_linear_accesses_no_padding() const { return has_linear_accesses_no_padding(shape_, strides_); }
8✔
142

143
bool TensorLayout::has_transposed_strides_no_padding() const {
×
144
    if (shape_.size() < 2) {
×
145
        return false;
×
146
    }
×
147
    symbolic::MultiExpression new_shape;
×
148
    new_shape.reserve(shape_.size());
×
149
    for (size_t i = 0; i < shape_.size() - 2; i++) {
×
150
        new_shape.push_back(shape_.at(i));
×
151
    }
×
152
    new_shape.push_back(shape_.at(shape_.size() - 1));
×
153
    new_shape.push_back(shape_.at(shape_.size() - 2));
×
154
    symbolic::MultiExpression transposed_strides(strides_);
×
155
    transposed_strides[strides_.size() - 2] = strides_.at(strides_.size() - 1);
×
156
    transposed_strides[strides_.size() - 1] = strides_.at(strides_.size() - 2);
×
157
    return TensorLayout::has_linear_accesses_no_padding(new_shape, transposed_strides);
×
158
}
×
159

160
bool TensorLayout::operator==(const TensorLayout& other) const {
7✔
161
    if (!symbolic::eq(this->offset_, other.offset_)) {
7✔
NEW
162
        return false;
×
NEW
163
    }
×
164

165
    if (this->shape_.size() != other.shape_.size()) {
7✔
NEW
166
        return false;
×
NEW
167
    }
×
168
    for (size_t i = 0; i < this->shape_.size(); ++i) {
11✔
169
        if (!symbolic::eq(this->get_dim(i), other.get_dim(i))) {
5✔
170
            return false;
1✔
171
        }
1✔
172
    }
5✔
173

174
    if (this->strides_.size() != other.strides_.size()) {
6✔
NEW
175
        return false;
×
NEW
176
    }
×
177
    for (size_t i = 0; i < this->strides_.size(); ++i) {
9✔
178
        if (!symbolic::eq(this->get_stride(i), other.get_stride(i))) {
3✔
NEW
179
            return false;
×
NEW
180
        }
×
181
    }
3✔
182

183
    return true;
6✔
184
};
6✔
185

NEW
186
std::unique_ptr<TensorLayout> TensorLayout::newaxis(size_t axis) const {
×
NEW
187
    if (axis > this->shape_.size()) {
×
NEW
188
        throw std::out_of_range("axis out of range for newaxis");
×
NEW
189
    }
×
190

NEW
191
    symbolic::MultiExpression new_shape = this->shape_;
×
NEW
192
    symbolic::MultiExpression new_strides = this->strides_;
×
193

NEW
194
    new_shape.insert(new_shape.begin() + axis, SymEngine::integer(1));
×
NEW
195
    new_strides.insert(new_strides.begin() + axis, SymEngine::integer(0));
×
196

NEW
197
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
NEW
198
}
×
199

200
std::unique_ptr<TensorLayout> TensorLayout::flip(size_t axis) const {
1✔
201
    if (axis >= shape_.size()) {
1✔
NEW
202
        throw std::out_of_range("axis out of range for flip");
×
NEW
203
    }
×
204

205
    symbolic::MultiExpression new_strides = this->strides_;
1✔
206

207
    // Negate the stride for the specified axis
208
    new_strides[axis] = SymEngine::neg(this->strides_[axis]);
1✔
209

210
    // Compute new offset: offset += stride * (shape - 1)
211
    auto shape_minus_one = SymEngine::sub(this->shape_[axis], SymEngine::integer(1));
1✔
212
    auto offset_adjustment = SymEngine::mul(this->strides_[axis], shape_minus_one);
1✔
213

214
    symbolic::Expression new_offset = SymEngine::add(this->offset_, offset_adjustment);
1✔
215

216
    return std::make_unique<TensorLayout>(this->shape_, new_strides, new_offset);
1✔
217
}
1✔
218

NEW
219
std::unique_ptr<TensorLayout> TensorLayout::unsqueeze(size_t axis) const { return this->newaxis(axis); }
×
220

NEW
221
std::unique_ptr<TensorLayout> TensorLayout::squeeze(size_t axis) const {
×
NEW
222
    if (axis >= this->shape_.size()) {
×
NEW
223
        throw std::out_of_range("axis out of range for squeeze");
×
NEW
224
    }
×
225

NEW
226
    if (!SymEngine::is_a<SymEngine::Integer>(*this->shape_.at(axis))) {
×
NEW
227
        throw std::invalid_argument("cannot squeeze axis with symbolic size");
×
NEW
228
    }
×
NEW
229
    auto dim_size = SymEngine::rcp_dynamic_cast<const SymEngine::Integer>(this->shape_.at(axis))->as_int();
×
NEW
230
    if (dim_size != 1) {
×
NEW
231
        throw std::invalid_argument("cannot squeeze axis with size != 1");
×
NEW
232
    }
×
233

NEW
234
    symbolic::MultiExpression new_shape = this->shape_;
×
NEW
235
    symbolic::MultiExpression new_strides = this->strides_;
×
236

NEW
237
    new_shape.erase(new_shape.begin() + axis);
×
NEW
238
    new_strides.erase(new_strides.begin() + axis);
×
239

NEW
240
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
NEW
241
}
×
242

NEW
243
std::unique_ptr<TensorLayout> TensorLayout::squeeze() const {
×
NEW
244
    symbolic::MultiExpression new_shape;
×
NEW
245
    symbolic::MultiExpression new_strides;
×
246

NEW
247
    for (size_t i = 0; i < this->shape_.size(); ++i) {
×
NEW
248
        bool is_size_one = false;
×
NEW
249
        if (SymEngine::is_a<SymEngine::Integer>(*this->shape_.at(i))) {
×
NEW
250
            auto dim_size = SymEngine::rcp_dynamic_cast<const SymEngine::Integer>(this->shape_.at(i))->as_int();
×
NEW
251
            is_size_one = (dim_size == 1);
×
NEW
252
        }
×
253

NEW
254
        if (!is_size_one) {
×
NEW
255
            new_shape.push_back(this->shape_.at(i));
×
NEW
256
            new_strides.push_back(this->strides_.at(i));
×
NEW
257
        }
×
NEW
258
    }
×
259

NEW
260
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
NEW
261
}
×
262

263
std::unique_ptr<TensorLayout> TensorLayout::reshape(const symbolic::MultiExpression& new_shape) const {
1✔
264
    // Compute the total number of elements in the current shape
265
    symbolic::Expression total_elements = this->total_elements();
1✔
266

267
    // Compute the total number of elements in the new shape
268
    symbolic::Expression new_total_elements = symbolic::one();
1✔
269
    for (const auto& dim : new_shape) {
2✔
270
        new_total_elements = symbolic::mul(new_total_elements, dim);
2✔
271
    }
2✔
272

273
    // Check if the total number of elements matches
274
    if (!symbolic::eq(total_elements, new_total_elements)) {
1✔
NEW
275
        throw std::invalid_argument("total number of elements must match for reshape");
×
NEW
276
    }
×
277

278
    // Compute new strides based on the new shape
279
    symbolic::MultiExpression new_strides = linear_strides(new_shape);
1✔
280

281
    return std::make_unique<TensorLayout>(new_shape, new_strides, offset_);
1✔
282
}
1✔
283

284
} // namespace sdfg::math::tensor
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