From 11110b46b4340c1417e308084a5578073ee0663a Mon Sep 17 00:00:00 2001 From: Fabio Luporini Date: Tue, 20 Oct 2026 15:43:05 +0100 Subject: [PATCH] compiler: Include compiler settings in JIT cache keys Make Compiler a Signer using the effective compiler command and file extensions. Include it in the operator JIT digest so changes to flags or external library search paths produce distinct cache keys. Preserve option order and cover stable and distinct names with library-directory, CFLAGS, and LDFLAGS regressions. --- devito/arch/compiler.py | 7 +++++-- devito/operator/operator.py | 2 +- tests/test_operator.py | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/devito/arch/compiler.py b/devito/arch/compiler.py index 1faf77cc90..ef248cbdf0 100644 --- a/devito/arch/compiler.py +++ b/devito/arch/compiler.py @@ -22,7 +22,7 @@ from devito.logger import debug, warning from devito.parameters import configuration from devito.tools import ( - as_list, change_directory, filter_ordered, make_tempdir, memoized_func + Signer, as_list, change_directory, filter_ordered, make_tempdir, memoized_func ) __all__ = ['compiler_registry', 'sniff_mpi_distro'] @@ -148,7 +148,7 @@ def call_capture_output(cmd): return _call_capture_output(cmd) -class Compiler(GCCToolchain): +class Compiler(GCCToolchain, Signer): """ Base class for all compiler classes. @@ -457,6 +457,9 @@ def __getstate__(self): # The superclass would otherwise only return a subset of attributes return self.__dict__ + def _signature_items(self): + return (str(self._cmdline([])), self.src_ext, self.so_ext) + def add_include_dirs(self, dirs): self.include_dirs = filter_ordered(self.include_dirs + as_list(dirs)) diff --git a/devito/operator/operator.py b/devito/operator/operator.py index b3690caf74..8f3c02f983 100644 --- a/devito/operator/operator.py +++ b/devito/operator/operator.py @@ -811,7 +811,7 @@ def arguments(self, **kwargs): @cached_property def _soname(self): """A unique name for the shared object resulting from JIT compilation.""" - return Signer._digest(self, configuration) + return Signer._digest(self, configuration, self._compiler) @cached_property def _printer(self): diff --git a/tests/test_operator.py b/tests/test_operator.py index 4518b32429..e6fc86463a 100644 --- a/tests/test_operator.py +++ b/tests/test_operator.py @@ -161,6 +161,43 @@ def test_compiler_uniqueness(self): assert op0._compiler is not op2._compiler assert op1._compiler is not op2._compiler + @pytest.mark.parametrize('rpath', [False, True]) + def test_jit_library_dirs(self, rpath, tmp_path): + grid = Grid(shape=(3, 3, 3)) + u = TimeFunction(name='u', grid=grid) + eq = Eq(u.forward, u + 1) + + operators = [] + for name in ('lib0', 'lib1', 'lib1'): + op = Operator(eq, name='library_paths') + op._compiler.add_library_dirs([str(tmp_path/name)], rpath=rpath) + operators.append(op) + + op0, op1, op2 = operators + assert str(op0) == str(op1) == str(op2) + assert op0._soname != op1._soname + assert op1._soname == op2._soname + + @pytest.mark.parametrize('envvar, flags', [ + ('CFLAGS', ('-O1 -O2', '-O2 -O1')), + ('LDFLAGS', ('-Wl,-rpath,/lib0 -Wl,-rpath,/lib1', + '-Wl,-rpath,/lib1 -Wl,-rpath,/lib0')) + ]) + def test_jit_compiler_flags(self, envvar, flags, monkeypatch): + grid = Grid(shape=(3, 3, 3)) + u = TimeFunction(name='u', grid=grid) + eq = Eq(u.forward, u + 1) + + operators = [] + for value in (*flags, flags[-1]): + monkeypatch.setenv(envvar, value) + operators.append(Operator(eq, name='compiler_flags', compiler='custom')) + + op0, op1, op2 = operators + assert str(op0) == str(op1) == str(op2) + assert op0._soname != op1._soname + assert op1._soname == op2._soname + class TestCodeGen: