Skip to content

Commit 8f1def8

Browse files
committed
dsl: Look a DimensionTuple up by the Dimension asked for
`__getitem_hook__` matched on `_defines` overlap alone. A derived Dimension carries its parent in `_defines`, so for a Bundle indexed by `(p_rec, rp_recx)` -- `rp_recx` being a `CustomDimension` whose parent is `p_rec` -- the lookup for `rp_recx` matched the `p_rec` entry first and returned the number of sparse points where the number of interpolation weights was meant. That size becomes the innermost stride in `_generate_fsz`, so the receiver kernels of a vectorized Operator read `w[p*npoint + rp]` instead of `w[p*2 + rp]` and run off the end of the array. Observed as an out-of-bounds `__global__` read under compute-sanitizer and a run-to-run varying, sometimes NaN, elastic TTI gradient on CUDA. Try an exact hit before falling back to the overlap, in both `__getitem_hook__` and `dindex`.
1 parent 4109b58 commit 8f1def8

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

devito/types/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,21 @@ class Stagger(Tag):
4545
class DimensionTuple(EnrichedTuple):
4646

4747
def __getitem_hook__(self, dim):
48+
# An exact hit wins over a `_defines` overlap. A derived Dimension
49+
# carries its parent in `_defines`, so a plain overlap test would
50+
# match the parent's entry first and silently return the wrong
51+
# component (e.g. a Bundle's `rp_rec` sub-dimension picking up the
52+
# `p_rec` size).
53+
if dim in self.getters:
54+
return self.getters[dim]
4855
for d in self.getters:
4956
if d._defines & dim._defines:
5057
return self.getters[d]
5158
raise KeyError
5259

5360
def dindex(self, dim):
61+
if dim in self.getters:
62+
return list(self.getters).index(dim)
5463
for d in self.getters:
5564
if d._defines & dim._defines:
5665
return list(self.getters).index(d)

tests/test_linearize.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
)
99
from devito.ir import Call, Callable, DummyExpr, Expression, FindNodes, SymbolRegistry
1010
from devito.passes import Graph, generate_macros, linearize
11-
from devito.types import Array, Bundle, DefaultDimension
11+
from devito.types import Array, Bundle, CustomDimension, DefaultDimension
1212

1313

1414
def test_basic():
@@ -716,3 +716,28 @@ def test_cire_n_strides():
716716
# NOTE: not exact equality because `op2` slightly changes the order of
717717
# arithmetic operations, which in turn causes some rounding differences
718718
assert np.allclose(u.data, u1.data, rtol=1e-4)
719+
720+
721+
def test_bundle_derived_dim_stride():
722+
"""
723+
A Bundle's stride must come from the Dimension that is asked for, not from
724+
a Dimension that merely appears in its `_defines`.
725+
726+
`MatrixSparseTimeFunction`-style interpolation introduces a `CustomDimension`
727+
whose parent is the sparse Dimension, so `rp._defines` contains `p`. Looking
728+
the shape up by `_defines` overlap alone matched `p` first and gave the
729+
Bundle the number of points as its innermost stride instead of the number of
730+
interpolation weights, which reads out of bounds on the device.
731+
"""
732+
grid = Grid(shape=(4, 4))
733+
p = DefaultDimension(name='p', default_value=5)
734+
rp = CustomDimension(name='rp', parent=p, symbolic_size=2)
735+
736+
w0 = Function(name='w0', dimensions=(p, rp), shape=(5, 2))
737+
w1 = Function(name='w1', dimensions=(p, rp), shape=(5, 2))
738+
bundle = Bundle(name='w0w1', components=(w0, w1), grid=grid)
739+
740+
assert rp._defines & p._defines # the overlap that used to mislead
741+
assert bundle.symbolic_shape[p] is not bundle.symbolic_shape[rp]
742+
assert bundle.symbolic_shape[rp] == 2
743+
assert bundle.symbolic_shape.dindex(rp) == 1

0 commit comments

Comments
 (0)