Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
90fdd9b
compiler: Add FunctionMap type
FabioLuporini Jul 17, 2025
98c2570
compiler: Add ULONG to __all__
FabioLuporini Jul 17, 2025
29c09e5
compiler: Improve lowering of LocalObjects
FabioLuporini Jul 17, 2025
f6b6ddd
compiler: Add LocalObject._mem_shared
FabioLuporini Jul 22, 2025
22d82c3
compiler: Add LocalType._C_tag
FabioLuporini Jul 28, 2025
b21e8eb
compiler: Move and enhance FunctionMap
FabioLuporini Jul 29, 2025
d0de4ef
arch: async-loads -> async-pipe
FabioLuporini Jul 29, 2025
a00c114
compiler: Fix IREq.__repr__
FabioLuporini Aug 8, 2025
52f2c8f
compiler: Generalize ideriv lowering
FabioLuporini Aug 8, 2025
0068be3
compiler: Avoid CSE across Reserved keywords
FabioLuporini Aug 11, 2025
425dc6b
compiler: Introduce Terminal mixin for SymPy subclasses
FabioLuporini Aug 11, 2025
3b7c0f7
compiler: Pass ctx down to _map_function_on_high_bw_mem
FabioLuporini Aug 12, 2025
30c857b
compiler: Enhance _alloc_object_on_low_lat_mem
FabioLuporini Aug 12, 2025
87f98de
compiler: Fix abstract_object(Array)
FabioLuporini Aug 12, 2025
8b06238
compiler: Avoid spurious items in sub_iters and dirs
FabioLuporini Aug 13, 2025
2f9688d
misc: Fix typo
FabioLuporini Aug 14, 2025
8d1ef90
compiler: Pass kwargs to make_parallel
FabioLuporini Aug 14, 2025
a9184ea
compiler: Tweak pairwise_or
FabioLuporini Nov 26, 2025
67739c4
compiler: Enhance ListInitializer
FabioLuporini Dec 4, 2025
184d9ff
compiler: Bump SafeInv cost
FabioLuporini Jan 21, 2026
486aa01
compiler: Fix Cluster.used_dimensions
FabioLuporini Jan 27, 2026
ecc4b24
compiler: Split up Cluster.used_dimensions to fix Lift
FabioLuporini Feb 3, 2026
5bf02f4
tests: Turn equality into tolerance check
FabioLuporini Feb 5, 2026
68f6b08
compiler: Simplify implementation and polish docstrings
FabioLuporini Feb 5, 2026
ff9a590
misc: Fixup import ordering
FabioLuporini Feb 5, 2026
03f1196
compiler: Fix cgen printing of boolean exprs
FabioLuporini Feb 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions devito/arch/archinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1139,7 +1139,7 @@ def supports(self, query, language=None):
warning(f"Couldn't establish if `query={query}` is supported on this "
"system. Assuming it is not.")
return False
elif query == 'async-loads' and cc >= 80:
elif query == 'async-pipe' and cc >= 80:
# Asynchronous pipeline loads -- introduced in Ampere
return True
elif query in ('tma', 'thread-block-cluster') and cc >= 90: # noqa: SIM103
Expand All @@ -1156,7 +1156,7 @@ class Volta(NvidiaDevice):
class Ampere(Volta):

def supports(self, query, language=None):
if query == 'async-loads':
if query == 'async-pipe':
return True
else:
return super().supports(query, language)
Expand Down
7 changes: 7 additions & 0 deletions devito/ir/cgen/printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,13 @@ def _print_BitwiseNot(self, expr):

def _print_BitwiseBinaryOp(self, expr):
arg0, arg1 = expr.args

prec = precedence(expr)
if not arg0.is_Atom:
arg0 = self.parenthesize(arg0, prec)
if not arg1.is_Atom:
arg1 = self.parenthesize(arg1, prec)

return f'{self._print(arg0)} {expr.op} {self._print(arg1)}'

def _print_Add(self, expr, order=None):
Expand Down
31 changes: 25 additions & 6 deletions devito/ir/clusters/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,16 +171,35 @@ def free_symbols(self):
def dimensions(self):
return set().union(*[i._defines for i in self.ispace.dimensions])

@cached_property
def exprs_dimensions(self):
"""
The Dimensions that appear explicitly in the Cluster expressions.
"""
dims_explicit = {i for i in self.free_symbols if i.is_Dimension}
dims_implicit = {d for e in self.exprs for d in e.implicit_dims}
return dims_explicit | dims_implicit

@cached_property
def guards_dimensions(self):
"""
The Dimensions that appear explicitly in the Cluster guards.
"""
syms_guards = {d for e in self.guards.values() for d in e.free_symbols}
dims_guards = {i for i in syms_guards if i.is_Dimension}
return dims_guards

@cached_property
def used_dimensions(self):
"""
The Dimensions that *actually* appear among the expressions in ``self``.
These do not necessarily coincide the IterationSpace Dimensions; for
example, reduction or redundant (i.e., invariant) Dimensions won't
appear in an expression.
All the Dimensions that appear explicitly either within the expressions
or the guards.

Note that, in some cases, some of the IterationSpace Dimensions might
not appear here among the used Dimensions -- for example, reduction or
redundant (i.e., invariant) Dimensions.
"""
idims = set.union(*[set(e.implicit_dims) for e in self.exprs])
return {i for i in self.free_symbols if i.is_Dimension} | idims
return self.exprs_dimensions | self.guards_dimensions

@cached_property
def dist_dimensions(self):
Expand Down
6 changes: 4 additions & 2 deletions devito/ir/equations/equation.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@ def __repr__(self):
if not self.is_Reduction:
return super().__repr__()
elif self.operation is OpInc:
return f'{self.lhs} += {self.rhs}'
return f'Inc({self.lhs}, {self.rhs})'
else:
return f'{self.lhs} = {self.operation}({self.rhs})'
return f'Eq({self.lhs}, {self.operation}({self.rhs}))'

__str__ = __repr__

# Pickling support
__reduce_ex__ = Pickable.__reduce_ex__
Expand Down
3 changes: 3 additions & 0 deletions devito/ir/iet/visitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ def _gen_value(self, obj, mode=1, masked=()):
qualifiers = [v for k, v in self._qualifiers_mapper.items()
if getattr(obj.function, k, False) and v not in masked]

if obj.is_LocalObject and mode == 2:
qualifiers.extend(as_tuple(obj._C_tag))

if (obj._mem_stack or obj._mem_constant) and mode == 1:
strtype = self.ccode(obj._C_typedata)
strshape = ''.join(f'[{self.ccode(i)}]' for i in obj.symbolic_shape)
Expand Down
4 changes: 3 additions & 1 deletion devito/ir/support/guards.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,9 @@ def pairwise_or(*guards):

# Analysis
for guard in guards:
if guard is true or guard is None:
if guard is true:
return true
elif guard is None:
continue
elif isinstance(guard, And):
components = guard.args
Expand Down
17 changes: 9 additions & 8 deletions devito/ir/support/space.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,17 +789,19 @@ def __init__(self, intervals, sub_iterators=None, directions=None):
super().__init__(intervals)

# Normalize sub-iterators
sub_iterators = dict([(k, tuple(filter_ordered(as_tuple(v))))
for k, v in (sub_iterators or {}).items()])
sub_iterators = sub_iterators or {}
sub_iterators = {d: tuple(filter_ordered(as_tuple(v)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's quite a lot of tuple conversion but doubt can be changed

for d, v in sub_iterators.items() if d in self.intervals}
sub_iterators.update({i.dim: () for i in self.intervals
if i.dim not in sub_iterators})
self._sub_iterators = frozendict(sub_iterators)

# Normalize directions
if directions is None:
self._directions = frozendict([(i.dim, Any) for i in self.intervals])
else:
self._directions = frozendict(directions)
directions = directions or {}
directions = {d: v for d, v in directions.items() if d in self.intervals}
directions.update({i.dim: Any for i in self.intervals
if i.dim not in directions})
self._directions = frozendict(directions)

def __repr__(self):
ret = ', '.join([f"{repr(i)}{repr(self.directions[i.dim])}"
Expand All @@ -821,8 +823,7 @@ def __lt__(self, other):
return len(self.itintervals) < len(other.itintervals)

def __hash__(self):
return hash((super().__hash__(), self.sub_iterators,
self.directions))
return hash((super().__hash__(), self.sub_iterators, self.directions))

def __contains__(self, d):
try:
Expand Down
4 changes: 2 additions & 2 deletions devito/passes/clusters/cse.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@

from devito.finite_differences.differentiable import IndexDerivative
from devito.ir import Cluster, Scope, cluster_pass
from devito.symbolics import estimate_cost, q_leaf, q_terminal
from devito.symbolics import Reserved, estimate_cost, q_leaf, q_terminal, search
from devito.symbolics.manipulation import _uxreplace
from devito.symbolics.search import search
from devito.tools import DAG, as_list, as_tuple, extract_dtype, frozendict
from devito.types import Eq, Symbol, Temp

Expand Down Expand Up @@ -399,6 +398,7 @@ def _(expr):

@_catch.register(Indexed)
@_catch.register(Symbol)
@_catch.register(Reserved)
def _(expr):
"""
Handler for objects preventing CSE to propagate through their arguments.
Expand Down
46 changes: 30 additions & 16 deletions devito/passes/clusters/derivatives.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import numpy as np
from sympy import S

from devito.finite_differences import IndexDerivative
from devito.finite_differences import IndexDerivative, Weights
from devito.ir import Backward, Forward, Interval, IterationSpace, Queue
from devito.passes.clusters.misc import fuse
from devito.symbolics import BasicWrapperMixin, reuse_if_untouched, uxreplace
Expand Down Expand Up @@ -91,17 +91,39 @@ def _core(expr, c, ispace, weights, reusables, mapper, **kwargs):


@_core.register(Symbol)
@_core.register(Indexed)
@_core.register(BasicWrapperMixin)
def _(expr, c, ispace, weights, reusables, mapper, **kwargs):
return expr, []


@_core.register(Indexed)
def _(expr, c, ispace, weights, reusables, mapper, **kwargs):
if not isinstance(expr.function, Weights):
return expr, []

# Lower or reuse a previously lowered Weights array
sregistry = kwargs['sregistry']
subs_user = kwargs['subs']

w0 = expr.function
k = tuple(w0.weights)
try:
w = weights[k]
except KeyError:
name = sregistry.make_name(prefix='w')
dtype = infer_dtype([w0.dtype, c.dtype]) # At least np.float32
initvalue = tuple(i.subs(subs_user) for i in k)
w = weights[k] = w0._rebuild(name=name, dtype=dtype, initvalue=initvalue)

rebuilt = expr._subs(w0.indexed, w.indexed)

return rebuilt, []


@_core.register(IndexDerivative)
def _(expr, c, ispace, weights, reusables, mapper, **kwargs):
sregistry = kwargs['sregistry']
options = kwargs['options']
subs_user = kwargs['subs']

try:
cbk0 = deriv_schedule_registry[options['deriv-schedule']]
Expand All @@ -114,18 +136,10 @@ def _(expr, c, ispace, weights, reusables, mapper, **kwargs):

# Create the concrete Weights array, or reuse an already existing one
# if possible
name = sregistry.make_name(prefix='w')
w0 = ideriv.weights.function
dtype = infer_dtype([w0.dtype, c.dtype]) # At least np.float32
k = tuple(w0.weights)
try:
w = weights[k]
except KeyError:
initvalue = tuple(i.subs(subs_user) for i in k)
w = weights[k] = w0._rebuild(name=name, dtype=dtype, initvalue=initvalue)
w, _ = _core(ideriv.weights, c, ispace, weights, reusables, mapper, **kwargs)

# Replace the abstract Weights array with the concrete one
subs = {w0.indexed: w.indexed}
subs = {ideriv.weights.base: w.base}
init = uxreplace(init, subs)
ideriv = uxreplace(ideriv, subs)

Expand All @@ -152,13 +166,13 @@ def _(expr, c, ispace, weights, reusables, mapper, **kwargs):
ispace1 = IterationSpace.union(ispace, ispace0, relations=extra)

# The Symbol that will hold the result of the IndexDerivative computation
# NOTE: created before recurring so that we ultimately get a sound ordering
# NOTE: created before recursing so that we ultimately get a sound ordering
try:
s = reusables.pop()
assert np.can_cast(s.dtype, dtype)
assert np.can_cast(s.dtype, w.dtype)
except KeyError:
name = sregistry.make_name(prefix='r')
s = Symbol(name=name, dtype=dtype)
s = Symbol(name=name, dtype=w.dtype)

# Go inside `expr` and recursively lower any nested IndexDerivatives
expr, processed = _core(expr, c, ispace1, weights, reusables, mapper, **kwargs)
Expand Down
14 changes: 7 additions & 7 deletions devito/passes/clusters/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def callback(self, clusters, prefix):
continue

# Is `c` a real candidate -- is there at least one invariant Dimension?
if any(d._defines & hope_invariant for d in c.used_dimensions):
if any(d._defines & hope_invariant for d in c.exprs_dimensions):
processed.append(c)
continue

Expand All @@ -69,16 +69,16 @@ def callback(self, clusters, prefix):
# All of the inner Dimensions must appear in the write-to region
# otherwise we would violate data dependencies. Consider
#
# 1) 2) 3)
# for i for i for i
# for x for x for x
# r = f(a[x]) for y for y
# r[x] = f(a[x, y]) r[x, y] = f(a[x, y])
# 1) 2) 3)
# for i for i for i
# for x for x for x
# r = f(a[x]) for y for y
# r[x] = f(a[x, y]) r[x, y] = f(a[x, y])
#
# In 1) and 2) lifting is infeasible; in 3) the statement can
# be lifted outside the `i` loop as `r`'s write-to region contains
# both `x` and `y`
xed = {d._defines for d in c.used_dimensions if d not in outer}
xed = {d._defines for d in c.exprs_dimensions if d not in outer}
if not all(i & set(w.dimensions) for i, w in product(xed, c.scope.writes)):
processed.append(c)
continue
Expand Down
Loading
Loading