Skip to content

Commit 2c190cb

Browse files
manuelcandalesfacebook-github-bot
authored andcommitted
Fix & cleanup op as_strided_copy (#697)
Summary: Pull Request resolved: #697 Fix for empty tensor or empty size ghstack-source-id: 203341571 exported-using-ghexport Reviewed By: SS-JIA Differential Revision: D49616945 fbshipit-source-id: e7ed26613eef8da49ac794f7891af3272cd51ed3
1 parent fe4e70d commit 2c190cb

File tree

4 files changed

+88
-91
lines changed

4 files changed

+88
-91
lines changed

kernels/portable/cpu/op_as_strided_copy.cpp

Lines changed: 26 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9+
#include <executorch/kernels/portable/cpu/util/copy_ops_util.h>
910
#include <executorch/runtime/kernel/kernel_includes.h>
10-
#include <cstring>
1111

1212
namespace torch {
1313
namespace executor {
@@ -17,40 +17,6 @@ using Tensor = exec_aten::Tensor;
1717
using ScalarType = exec_aten::ScalarType;
1818

1919
namespace {
20-
size_t compute_storage_nbytes(
21-
IntArrayRef sizes,
22-
IntArrayRef strides,
23-
size_t itemsize_bytes) {
24-
// size of the underlying storage is 1 bigger than the offset
25-
// of the last element according to stride
26-
size_t size = 1;
27-
for (size_t i = 0; i < sizes.size(); ++i) {
28-
if (sizes[i] == 0) {
29-
return 0;
30-
}
31-
size += strides[i] * (sizes[i] - 1);
32-
}
33-
return size * itemsize_bytes;
34-
}
35-
36-
void check_inbounds_for_storage(
37-
const Tensor& self,
38-
ArrayRef<int64_t> size,
39-
ArrayRef<int64_t> stride,
40-
int64_t storage_offset) {
41-
size_t storage_size_bytes =
42-
compute_storage_nbytes(size, stride, self.element_size());
43-
size_t storage_offset_bytes = storage_offset * self.element_size();
44-
if (storage_size_bytes == 0) {
45-
return;
46-
}
47-
size_t new_storage_size_bytes = self.nbytes();
48-
ET_CHECK_MSG(
49-
storage_size_bytes + storage_offset_bytes <= new_storage_size_bytes,
50-
"Requiring a storage size of %zd are out of bounds for storage of size %zd",
51-
storage_size_bytes + storage_offset_bytes,
52-
new_storage_size_bytes);
53-
}
5420

5521
/**
5622
* Copy input_data to output_data according to the stride and shape recursively
@@ -81,39 +47,8 @@ void as_strided_copy(
8147
}
8248
}
8349

84-
void check_preconditions(
85-
const Tensor& self,
86-
ArrayRef<int64_t> size,
87-
ArrayRef<int64_t> stride,
88-
optional<int64_t> storage_offset,
89-
Tensor& out) {
90-
ET_CHECK_SAME_DTYPE2(self, out);
91-
ET_CHECK_MSG(
92-
size.size() == stride.size(), "mismatch in length of strides and shape");
93-
for (const auto& val : stride) {
94-
ET_CHECK_MSG(
95-
val >= 0,
96-
"as_strided: Negative strides are not supported at the moment");
97-
}
98-
ET_CHECK_MSG(
99-
out.sizes().size() == size.size(),
100-
"output tensor should have same shape as size");
101-
for (size_t i = 0; i < out.sizes().size(); ++i) {
102-
ET_CHECK_MSG(
103-
out.sizes().at(i) == size.at(i),
104-
"output tensor should have same shape as size");
105-
}
106-
int64_t offset = storage_offset.has_value() ? storage_offset.value() : 0;
107-
ET_CHECK_MSG(offset >= 0, "Negative storage offset");
108-
check_inbounds_for_storage(self, size, stride, offset);
109-
}
110-
11150
} // namespace
11251

113-
/**
114-
* Copy the tener `self` to `out`, assume `self` and `out` have same type and
115-
* shape
116-
*/
11752
Tensor& as_strided_copy_out(
11853
RuntimeContext& ctx,
11954
const Tensor& self,
@@ -123,34 +58,35 @@ Tensor& as_strided_copy_out(
12358
Tensor& out) {
12459
(void)ctx;
12560

126-
torch::executor::Error err = resize_tensor(out, size);
127-
ET_CHECK_MSG(
128-
err == torch::executor::Error::Ok,
129-
"Failed to resize out Tensor in as_strided_copy_out");
61+
ET_KERNEL_CHECK(
62+
ctx,
63+
check_as_strided_copy_args(self, size, stride, storage_offset, out),
64+
InvalidArgument,
65+
out);
66+
67+
ET_KERNEL_CHECK(
68+
ctx,
69+
resize_tensor(out, size) == torch::executor::Error::Ok,
70+
InvalidArgument,
71+
out);
72+
73+
if (self.numel() == 0) {
74+
return out;
75+
}
13076

131-
check_preconditions(self, size, stride, storage_offset, out);
13277
size_t offset = storage_offset.has_value() ? storage_offset.value() : 0;
13378

134-
#define AS_STRIDED_COPY_TENSOR(ctype, dtype) \
135-
case ScalarType::dtype: \
136-
as_strided_copy<ctype>( \
137-
/*input_data=*/self.mutable_data_ptr<ctype>() + offset, \
138-
/*output_data=*/out.mutable_data_ptr<ctype>(), \
139-
out, \
140-
size, \
141-
stride, \
142-
/*dim=*/0); \
143-
break;
79+
ET_SWITCH_ALL_TYPES(self.scalar_type(), ctx, __func__, CTYPE, [&] {
80+
CTYPE* self_data = self.mutable_data_ptr<CTYPE>() + offset;
81+
CTYPE* out_data = out.mutable_data_ptr<CTYPE>();
82+
83+
if (size.empty()) {
84+
out_data[0] = self_data[0];
85+
} else {
86+
as_strided_copy<CTYPE>(self_data, out_data, out, size, stride, 0);
87+
}
88+
});
14489

145-
switch (self.scalar_type()) {
146-
ET_FORALL_SCALAR_TYPES(AS_STRIDED_COPY_TENSOR)
147-
default:
148-
ET_CHECK_MSG(
149-
false,
150-
"Unhandled dtype %" PRId8,
151-
static_cast<int8_t>(self.scalar_type()));
152-
}
153-
#undef AS_STRIDED_COPY_TENSOR
15490
return out;
15591
}
15692

kernels/portable/cpu/targets.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ _ATEN_OPS = (
9292
op_target(
9393
name = "op_as_strided_copy",
9494
deps = [
95-
":scalar_utils",
95+
"//executorch/kernels/portable/cpu/util:copy_ops_util",
9696
],
9797
),
9898
op_target(

kernels/portable/cpu/util/copy_ops_util.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,60 @@ namespace executor {
1515

1616
using Tensor = exec_aten::Tensor;
1717

18+
namespace {
19+
20+
size_t as_strided_copy_compute_storage_nbytes(
21+
IntArrayRef sizes,
22+
IntArrayRef strides,
23+
size_t itemsize_bytes) {
24+
// size of the underlying storage is 1 bigger than the offset
25+
// of the last element according to stride
26+
size_t size = 1;
27+
for (size_t i = 0; i < sizes.size(); ++i) {
28+
if (sizes[i] == 0) {
29+
return 0;
30+
}
31+
size += strides[i] * (sizes[i] - 1);
32+
}
33+
return size * itemsize_bytes;
34+
}
35+
36+
} // namespace
37+
38+
bool check_as_strided_copy_args(
39+
const Tensor& in,
40+
ArrayRef<int64_t> size,
41+
ArrayRef<int64_t> stride,
42+
optional<int64_t> storage_offset,
43+
Tensor& out) {
44+
ET_LOG_AND_RETURN_IF_FALSE(tensors_have_same_dtype(in, out));
45+
ET_LOG_MSG_AND_RETURN_IF_FALSE(
46+
size.size() == stride.size(), "mismatch in length of strides and shape");
47+
for (const auto& val : stride) {
48+
ET_LOG_MSG_AND_RETURN_IF_FALSE(
49+
val >= 0,
50+
"as_strided: Negative strides are not supported at the moment");
51+
}
52+
53+
int64_t offset = storage_offset.has_value() ? storage_offset.value() : 0;
54+
ET_LOG_MSG_AND_RETURN_IF_FALSE(offset >= 0, "Negative storage offset");
55+
56+
// Check that the requested storage is within bounds of input storage
57+
size_t storage_size_bytes =
58+
as_strided_copy_compute_storage_nbytes(size, stride, in.element_size());
59+
size_t storage_offset_bytes = offset * in.element_size();
60+
if (storage_size_bytes == 0) {
61+
return true;
62+
}
63+
size_t new_storage_size_bytes = in.nbytes();
64+
ET_LOG_MSG_AND_RETURN_IF_FALSE(
65+
storage_size_bytes + storage_offset_bytes <= new_storage_size_bytes,
66+
"Requiring a storage size of %zd are out of bounds for storage of size %zd",
67+
storage_size_bytes + storage_offset_bytes,
68+
new_storage_size_bytes);
69+
return true;
70+
}
71+
1872
bool check_cat_args(
1973
exec_aten::ArrayRef<Tensor> tensors,
2074
int64_t dim,

kernels/portable/cpu/util/copy_ops_util.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
namespace torch {
1414
namespace executor {
1515

16+
bool check_as_strided_copy_args(
17+
const Tensor& in,
18+
ArrayRef<int64_t> size,
19+
ArrayRef<int64_t> stride,
20+
optional<int64_t> storage_offset,
21+
Tensor& out);
22+
1623
bool check_cat_args(
1724
exec_aten::ArrayRef<Tensor> tensors,
1825
int64_t dim,

0 commit comments

Comments
 (0)