From d1aa9be48f48309edb0a02cb05f8deb29bf6191e Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Mon, 8 Apr 2024 07:38:52 +0000 Subject: [PATCH] Transformation: Remove obsolete RemoveCallTrafo and rename to drhook --- scripts/loki_transform.py | 2 +- ...est_utility_routines.py => test_drhook.py} | 44 +---- transformations/transformations/__init__.py | 2 +- transformations/transformations/drhook.py | 93 ++++++++++ .../transformations/utility_routines.py | 163 ------------------ 5 files changed, 96 insertions(+), 208 deletions(-) rename transformations/tests/{test_utility_routines.py => test_drhook.py} (75%) create mode 100644 transformations/transformations/drhook.py delete mode 100644 transformations/transformations/utility_routines.py diff --git a/scripts/loki_transform.py b/scripts/loki_transform.py index ea5e71153..530e8b825 100644 --- a/scripts/loki_transform.py +++ b/scripts/loki_transform.py @@ -33,7 +33,7 @@ DataOffloadTransformation, GlobalVariableAnalysis, GlobalVarOffloadTransformation ) from transformations.derived_types import DerivedTypeArgumentsTransformation -from transformations.utility_routines import DrHookTransformation, RemoveCallsTransformation +from transformations.drhook import DrHookTransformation from transformations.pool_allocator import TemporariesPoolAllocatorTransformation from transformations.single_column_claw import ExtractSCATransformation, CLAWTransformation from transformations.single_column_coalesced import ( diff --git a/transformations/tests/test_utility_routines.py b/transformations/tests/test_drhook.py similarity index 75% rename from transformations/tests/test_utility_routines.py rename to transformations/tests/test_drhook.py index 2557ab4cd..aaf0b7b08 100644 --- a/transformations/tests/test_utility_routines.py +++ b/transformations/tests/test_drhook.py @@ -14,7 +14,7 @@ ) from conftest import available_frontends -from transformations import DrHookTransformation, RemoveCallsTransformation +from transformations import DrHookTransformation @pytest.fixture(scope='module', name='config') @@ -197,45 +197,3 @@ def test_dr_hook_transformation_remove(frontend, config, source): assert not drhook_calls assert not drhook_imports assert 'zhook_handle' not in item.ir.variables - - -@pytest.mark.parametrize('include_intrinsics', (True, False)) -@pytest.mark.parametrize('kernel_only', (True, False)) -@pytest.mark.parametrize('frontend', available_frontends( - xfail=[(OMNI, 'Incomplete source tree impossible with OMNI')] -)) -def test_utility_routine_removal(frontend, config, source, include_intrinsics, kernel_only): - """ - Test removal of utility calls and intrinsics with custom patterns. - """ - scheduler_config = SchedulerConfig.from_dict(config) - scheduler = Scheduler(paths=source, config=scheduler_config, frontend=frontend) - scheduler.process( - transformation=RemoveCallsTransformation( - routines=['ABOR1', 'WRITE(NULOUT', 'DR_HOOK'], - include_intrinsics=include_intrinsics, kernel_only=kernel_only - ) - ) - - routine = scheduler['rick_rolled#never_gonna_give'].ir - transformed = routine.to_fortran() - assert '[SUBROUTINE CALL]' not in transformed - assert '[INLINE CONDITIONAL]' not in transformed - assert ('dave' not in transformed) == include_intrinsics - assert ('[WRITE INTRINSIC]' not in transformed) == include_intrinsics - assert 'zhook_handle' not in routine.variables - - for r in routine.members: - transformed = r.to_fortran() - assert '[SUBROUTINE CALL]' not in transformed - assert '[INLINE CONDITIONAL]' not in transformed - assert ('dave' not in transformed) == include_intrinsics - assert 'zhook_handle' not in routine.variables - - routine = Sourcefile.from_file(source/'never_gonna_give.F90', frontend=frontend)['i_hope_you_havent_let_me_down'] - assert 'zhook_handle' in routine.variables - assert len([call for call in FindNodes(CallStatement).visit(routine.body) if call.name == 'dr_hook']) == 2 - - driver = scheduler['#rick_astley'].ir - drhook_calls = [call for call in FindNodes(CallStatement).visit(driver.body) if call.name == 'dr_hook'] - assert len(drhook_calls) == (2 if kernel_only else 0) diff --git a/transformations/transformations/__init__.py b/transformations/transformations/__init__.py index a846eee95..2b689d23b 100644 --- a/transformations/transformations/__init__.py +++ b/transformations/transformations/__init__.py @@ -13,7 +13,7 @@ from transformations.single_column_claw import * # noqa from transformations.single_column_coalesced import * # noqa from transformations.single_column_coalesced_vector import * # noqa -from transformations.utility_routines import * # noqa +from transformations.drhook import * # noqa from transformations.scc_cuf import * # noqa from transformations.pool_allocator import * # noqa diff --git a/transformations/transformations/drhook.py b/transformations/transformations/drhook.py new file mode 100644 index 000000000..86d224983 --- /dev/null +++ b/transformations/transformations/drhook.py @@ -0,0 +1,93 @@ +# (C) Copyright 2018- ECMWF. +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. + +""" +Utility transformations to update or remove calls to DR_HOOK. +""" + +from loki import ( + FindNodes, Transformer, Transformation, CallStatement, + Conditional, as_tuple, Literal, Intrinsic, Import +) + + +__all__ = ['DrHookTransformation'] + + +def remove_unused_drhook_import(routine): + """ + Remove unsed DRHOOK imports and corresponding handle. + + Parameters + ---------- + routine : :any:`Subroutine` + The subroutine from which to remove DRHOOK import/handle. + """ + + mapper = {} + for imp in FindNodes(Import).visit(routine.spec): + if imp.module.lower() == 'yomhook': + mapper[imp] = None + + if mapper: + routine.spec = Transformer(mapper).visit(routine.spec) + + #Remove unused zhook_handle + routine.variables = as_tuple(v for v in routine.variables if v != 'zhook_handle') + + +class DrHookTransformation(Transformation): + """ + Re-write or remove the DrHook label markers in transformed + kernel routines + + Parameters + ---------- + remove : bool + Remove calls to ``DR_HOOK`` + mode : str + Transformation mode to insert into DrHook labels + """ + def __init__(self, remove=False, mode=None, **kwargs): + self.remove = remove + self.mode = mode + super().__init__(**kwargs) + + def transform_subroutine(self, routine, **kwargs): + """ + Apply transformation to subroutine object + """ + role = kwargs['item'].role + + # Leave DR_HOOK annotations in driver routine + if role == 'driver': + return + + for r in routine.members: + self.transform_subroutine(r, **kwargs) + + mapper = {} + for call in FindNodes(CallStatement).visit(routine.body): + # Lazily changing the DrHook label in-place + if call.name == 'DR_HOOK': + if self.remove: + mapper[call] = None + else: + new_label = f'{call.arguments[0].value.upper()}_{str(self.mode).upper()}' + new_args = (Literal(value=new_label),) + call.arguments[1:] + mapper[call] = call.clone(arguments=new_args) + + if self.remove: + for cond in FindNodes(Conditional).visit(routine.body): + if cond.inline and 'LHOOK' in as_tuple(cond.condition): + mapper[cond] = None + + routine.body = Transformer(mapper).visit(routine.body) + + #Get rid of unused import and variable + if self.remove: + remove_unused_drhook_import(routine) diff --git a/transformations/transformations/utility_routines.py b/transformations/transformations/utility_routines.py deleted file mode 100644 index ea4695024..000000000 --- a/transformations/transformations/utility_routines.py +++ /dev/null @@ -1,163 +0,0 @@ -# (C) Copyright 2018- ECMWF. -# This software is licensed under the terms of the Apache Licence Version 2.0 -# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -# In applying this licence, ECMWF does not waive the privileges and immunities -# granted to it by virtue of its status as an intergovernmental organisation -# nor does it submit to any jurisdiction. - -""" -Utility transformations to update or remove calls to DR_HOOK and other -utility routines -""" - -from loki import ( - FindNodes, Transformer, Transformation, CallStatement, - Conditional, as_tuple, Literal, Intrinsic, Import -) - - -__all__ = ['DrHookTransformation', 'RemoveCallsTransformation'] - -def remove_unused_drhook_import(routine): - """ - Remove unsed DRHOOK imports and corresponding handle. - - Parameters - ---------- - routine : :any:`Subroutine` - The subroutine from which to remove DRHOOK import/handle. - """ - - mapper = {} - for imp in FindNodes(Import).visit(routine.spec): - if imp.module.lower() == 'yomhook': - mapper[imp] = None - - if mapper: - routine.spec = Transformer(mapper).visit(routine.spec) - - #Remove unused zhook_handle - routine.variables = as_tuple(v for v in routine.variables if v != 'zhook_handle') - -class DrHookTransformation(Transformation): - """ - Re-write or remove the DrHook label markers in transformed - kernel routines - - Parameters - ---------- - remove : bool - Remove calls to ``DR_HOOK`` - mode : str - Transformation mode to insert into DrHook labels - """ - def __init__(self, remove=False, mode=None, **kwargs): - self.remove = remove - self.mode = mode - super().__init__(**kwargs) - - def transform_subroutine(self, routine, **kwargs): - """ - Apply transformation to subroutine object - """ - role = kwargs['item'].role - - # Leave DR_HOOK annotations in driver routine - if role == 'driver': - return - - for r in routine.members: - self.transform_subroutine(r, **kwargs) - - mapper = {} - for call in FindNodes(CallStatement).visit(routine.body): - # Lazily changing the DrHook label in-place - if call.name == 'DR_HOOK': - if self.remove: - mapper[call] = None - else: - new_label = f'{call.arguments[0].value.upper()}_{str(self.mode).upper()}' - new_args = (Literal(value=new_label),) + call.arguments[1:] - mapper[call] = call.clone(arguments=new_args) - - if self.remove: - for cond in FindNodes(Conditional).visit(routine.body): - if cond.inline and 'LHOOK' in as_tuple(cond.condition): - mapper[cond] = None - - routine.body = Transformer(mapper).visit(routine.body) - - #Get rid of unused import and variable - if self.remove: - remove_unused_drhook_import(routine) - - -class RemoveCallsTransformation(Transformation): - """ - Removes specified :any:`CallStatement` objects from any :any:`Subroutine`. - - In addition, this transformation will also remove inline conditionals that - guard the respective utility calls, in order to preserve consistent code. - - Parameters - ---------- - routines : list of str - List of subroutine names to remove - include_intrinsics : bool - Option to extend searches to :any:`Intrinsic` nodes to - capture print/write statements - kernel_only : bool - Option to only remove calls in routines marked as "kernel"; default: ``False`` - """ - def __init__(self, routines, include_intrinsics=False, kernel_only=False, **kwargs): - self.routines = as_tuple(routines) - self.include_intrinsics = include_intrinsics - self.kernel_only = kernel_only - super().__init__(**kwargs) - - def transform_subroutine(self, routine, **kwargs): - """ - Apply transformation to subroutine object - """ - - # Skip driver layer if requested - role = kwargs.get('role', None) - if role and role == 'driver' and self.kernel_only: - return - - for r in routine.members: - self.transform_subroutine(r, **kwargs) - - mapper = {} - - # First remove inline conditionals with calls to specified routines or intrinsics - # This check happesn first, as we would leave empty-bodies conditionals otherwise - inline_conditionals = tuple( - cond for cond in FindNodes(Conditional).visit(routine.body) if cond.inline - ) - for cond in inline_conditionals: - if len(cond.body) == 1 and isinstance(cond.body[0], CallStatement): - if cond.body[0].name in self.routines: - mapper[cond] = None - - if self.include_intrinsics: - if len(cond.body) == 1 and isinstance(cond.body[0], Intrinsic): - if any(str(r).lower() in cond.body[0].text.lower() for r in self.routines): - mapper[cond] = None - - # Find direct calls to specified routines - for call in FindNodes(CallStatement).visit(routine.body): - if call.name in self.routines: - mapper[call] = None - - # Include intrinsics that match the routine name partially - if self.include_intrinsics: - for intr in FindNodes(Intrinsic).visit(routine.body): - if any(str(r).lower() in intr.text.lower() for r in self.routines): - mapper[intr] = None - - routine.body = Transformer(mapper).visit(routine.body) - - #Get rid of unused DRHOOK import and handle - if 'dr_hook' in [r.lower() for r in self.routines]: - remove_unused_drhook_import(routine)