Skip to content

Commit 2700570

Browse files
psiddhfacebook-github-bot
authored andcommitted
Fix VGF runtime aborts on 0-dim tensor inputs and Python scalar coercion
Summary: The ARM-backend VGF tests (e.g. ``test_sum_dim_intlist_vgf_quant`` and ``test_sum_dim_intlist_vgf_no_quant`` for all 19 parametrizations) were hard-aborting the pytest process with two latent bugs that compounded: 1. **C++ aten_bridge nullptr assert on 0-dim tensors (T270603238).** ``executorch/extension/aten_util/aten_bridge.cpp::check_tensor_meta`` had two unconditional ``ET_CHECK_MSG(b.{sizes,strides}().data() != nullptr, ...)`` asserts. For 0-dim (scalar) tensors, ``sizes()``/``strides()`` are empty ``IntArrayRef``s whose ``.data()`` may legitimately return nullptr. The process aborted on every valid scalar tensor input. Fix: gate the nullptr checks on ``b.dim() > 0``. The subsequent loops are no-ops when ``dim() == 0`` and ``dim_order_to_stride_nocheck`` already early-returns for ``dims == 0`` (``dim_order_util.h:132-134``), so the relaxed asserts are safe. 2. **VGF Python runner over-wrapping non-tensor inputs (Error::InvalidArgument 0x12).** ``runner_fb.run_vgf`` previously called ``torch.tensor(x)`` on every non-tensor input (including ``None``/``bool``/``int``), producing 0-dim tensors. The lowered method's signature, however, expects ``EValue`` tags ``Int``/``Bool``/``None`` for those slots — receiving a ``Tensor`` caused ``Method::set_inputs`` to reject the inputs. The pybindings layer (``pybindings.cpp:804-809``) already natively handles ``None``/``bool``/``int`` Python objects; the runner just had to stop interfering. Fix: only wrap Python ``float`` (and other unknown types) as 0-dim tensors — the original ``addmm`` alpha/beta motivation. Pass ``None``/``bool``/``int`` through unchanged. 3. **Regression tests** for the C++ fix in ``executorch/extension/aten_util/test/aten_bridge_test.cpp``: ``AliasETensorToATenTensorZeroDim`` and ``AliasATTensorToETensorZeroDim`` construct true 0-dim tensors via ``at::scalar_tensor`` and verify the bridge does not abort. The existing ``AliasETensorToATenTensorFail`` death test still fires for ranked tensors with empty strides because that case has ``dim() == 3 > 0``. Fixes T270603238. Differential Revision: D104603739
1 parent a49171d commit 2700570

2 files changed

Lines changed: 55 additions & 5 deletions

File tree

extension/aten_util/aten_bridge.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,18 @@ namespace extension {
1818

1919
namespace {
2020
void check_tensor_meta(const at::Tensor& a, const executorch::aten::Tensor& b) {
21-
// Check sizes/strides pointers
22-
ET_CHECK_MSG(
23-
b.sizes().data() != nullptr, "ETensor must have valid sizes array");
24-
ET_CHECK_MSG(
25-
b.strides().data() != nullptr, "ETensor must have valid strides array");
21+
// 0-dim (scalar) tensors legitimately have empty sizes/strides arrays;
22+
// their `.data()` may return nullptr depending on the underlying container.
23+
// Only require non-null sizes/strides storage when the tensor actually has
24+
// at least one dimension. The nullptr check is meant to catch malformed
25+
// metadata for ranked tensors (where sizes/strides MUST be addressable);
26+
// it must not abort on valid 0-dim inputs.
27+
if (b.dim() > 0) {
28+
ET_CHECK_MSG(
29+
b.sizes().data() != nullptr, "ETensor must have valid sizes array");
30+
ET_CHECK_MSG(
31+
b.strides().data() != nullptr, "ETensor must have valid strides array");
32+
}
2633
// Check disabled because in ASR model we get 1 element tensor with different
2734
// rank.
2835
/*

extension/aten_util/test/aten_bridge_test.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,49 @@ TEST(ATenBridgeTest, AliasTensorPtrToATenTensor) {
155155
EXPECT_EQ(at_tensor.const_data_ptr(), et_tensor_ptr->const_data_ptr());
156156
}
157157

158+
// 0-dim (scalar) tensors legitimately have empty sizes/strides arrays whose
159+
// `.data()` may return nullptr. Regression test for T270603238: ensure
160+
// check_tensor_meta does not abort on valid 0-dim tensors.
161+
TEST(ATenBridgeTest, AliasETensorToATenTensorZeroDim) {
162+
auto at_tensor = at::scalar_tensor(42.0f);
163+
ASSERT_EQ(at_tensor.dim(), 0);
164+
std::vector<Tensor::SizesType> sizes;
165+
std::vector<Tensor::DimOrderType> dim_order;
166+
std::vector<Tensor::StridesType> strides;
167+
auto dtype = torchToExecuTorchScalarType(at_tensor.options().dtype());
168+
torch::executor::TensorImpl tensor_impl(
169+
dtype,
170+
/*dim=*/0,
171+
sizes.data(),
172+
nullptr,
173+
dim_order.data(),
174+
strides.data());
175+
torch::executor::Tensor etensor(&tensor_impl);
176+
alias_etensor_to_attensor(at_tensor, etensor);
177+
EXPECT_EQ(at_tensor.const_data_ptr(), etensor.const_data_ptr());
178+
}
179+
180+
TEST(ATenBridgeTest, AliasATTensorToETensorZeroDim) {
181+
auto at_tensor = at::scalar_tensor(7);
182+
ASSERT_EQ(at_tensor.dim(), 0);
183+
std::vector<Tensor::SizesType> sizes;
184+
std::vector<Tensor::DimOrderType> dim_order;
185+
std::vector<Tensor::StridesType> strides;
186+
auto dtype = torchToExecuTorchScalarType(at_tensor.options().dtype());
187+
std::vector<uint8_t> etensor_data(at_tensor.nbytes());
188+
torch::executor::TensorImpl tensor_impl(
189+
dtype,
190+
/*dim=*/0,
191+
sizes.data(),
192+
etensor_data.data(),
193+
dim_order.data(),
194+
strides.data());
195+
torch::executor::Tensor etensor(&tensor_impl);
196+
auto aliased_at_tensor = alias_attensor_to_etensor(etensor);
197+
EXPECT_EQ(aliased_at_tensor.dim(), 0);
198+
EXPECT_EQ(aliased_at_tensor.const_data_ptr(), etensor_data.data());
199+
}
200+
158201
TEST(ATenBridgeTest, AliasATTensorToETensorChannelsLast) {
159202
auto at_tensor = at::randn({2, 3, 4, 5}).to(at::MemoryFormat::ChannelsLast);
160203
std::vector<Tensor::SizesType> sizes(

0 commit comments

Comments
 (0)