diff --git a/loki/transformations/extract/__init__.py b/loki/transformations/extract/__init__.py index 6cde05b07..26ef18a5c 100644 --- a/loki/transformations/extract/__init__.py +++ b/loki/transformations/extract/__init__.py @@ -17,7 +17,7 @@ """ from loki.transformations.extract.internal import * # noqa -from loki.transformations.extract.marked import * # noqa +from loki.transformations.extract.outline import * # noqa from loki.batch import Transformation @@ -33,16 +33,16 @@ class ExtractTransformation(Transformation): Parameters ---------- - inline_internals : bool + extract_internals : bool Extract internal procedure (see :any:`extract_internal_procedures`); default: False. - inline_marked : bool - Extract :any:`Subroutine` objects marked by pragma annotations - (see :any:`extract_marked_subroutines`); default: True. + do_outline : bool + Outline pragma-annotated code regions to :any:`Subroutine` objects. + (see :any:`outline_pragma_regions`); default: True. """ - def __init__(self, extract_internals=False, extract_marked=True): + def __init__(self, extract_internals=False, outline_regions=True): self.extract_internals = extract_internals - self.extract_marked = extract_marked + self.outline_regions = outline_regions def transform_module(self, module, **kwargs): """ @@ -57,9 +57,9 @@ def transform_module(self, module, **kwargs): module.contains.append(new_routines) # Extract pragma-marked code regions into standalone subroutines - if self.extract_marked: + if self.outline_regions: for routine in module.subroutines: - new_routines = extract_marked_subroutines(routine) + new_routines = outline_pragma_regions(routine) module.contains.append(new_routines) def transform_file(self, sourcefile, **kwargs): @@ -75,7 +75,7 @@ def transform_file(self, sourcefile, **kwargs): sourcefile.ir.append(new_routines) # Extract pragma-marked code regions into standalone subroutines - if self.extract_marked: + if self.outline_regions: for routine in sourcefile.subroutines: - new_routines = extract_marked_subroutines(routine) + new_routines = outline_pragma_regions(routine) sourcefile.ir.append(new_routines) diff --git a/loki/transformations/extract/marked.py b/loki/transformations/extract/outline.py similarity index 94% rename from loki/transformations/extract/marked.py rename to loki/transformations/extract/outline.py index c323e9ab0..130f5de0d 100644 --- a/loki/transformations/extract/marked.py +++ b/loki/transformations/extract/outline.py @@ -17,17 +17,16 @@ from loki.tools import as_tuple, CaseInsensitiveDict -__all__ = ['extract_marked_subroutines'] +__all__ = ['outline_pragma_regions'] -def extract_marked_subroutines(routine): +def outline_pragma_regions(routine): """ - Convert regions annotated with ``!$loki extract`` pragmas to subroutine calls. - + Convert regions annotated with ``!$loki outline`` pragmas to subroutine calls. The pragma syntax for regions to convert to subroutines is - ``!$loki extract [name(...)] [in(...)] [out(...)] [inout(...)]`` - and ``!$loki end extract``. + ``!$loki outline [name(...)] [in(...)] [out(...)] [inout(...)]`` + and ``!$loki end outline``. A new subroutine is created with the provided name (or an auto-generated default name derived from the current subroutine name) and the content of the pragma region as body. @@ -51,12 +50,12 @@ def extract_marked_subroutines(routine): with pragma_regions_attached(routine): with dataflow_analysis_attached(routine): for region in FindNodes(PragmaRegion).visit(routine.body): - if not is_loki_pragma(region.pragma, starts_with='extract'): + if not is_loki_pragma(region.pragma, starts_with='outline'): continue # Name the external routine - parameters = get_pragma_parameters(region.pragma, starts_with='extract') - name = parameters.get('name', f'{routine.name}_extracted_{counter}') + parameters = get_pragma_parameters(region.pragma, starts_with='outline') + name = parameters.get('name', f'{routine.name}_outlined_{counter}') counter += 1 # Create the external subroutine containing the routine's imports and the region's body diff --git a/loki/transformations/extract/tests/test_extract_transformation.py b/loki/transformations/extract/tests/test_extract_transformation.py index 8f3364842..94e99759d 100644 --- a/loki/transformations/extract/tests/test_extract_transformation.py +++ b/loki/transformations/extract/tests/test_extract_transformation.py @@ -15,9 +15,9 @@ @pytest.mark.parametrize('frontend', available_frontends()) -@pytest.mark.parametrize('extract_marked', [False, True]) +@pytest.mark.parametrize('outline_regions', [False, True]) @pytest.mark.parametrize('extract_internals', [False, True]) -def test_extract_transformation_module(extract_internals, extract_marked, frontend): +def test_extract_transformation_module(extract_internals, outline_regions, frontend): """ Test basic subroutine extraction from marker pragmas in modules. """ @@ -37,13 +37,13 @@ def test_extract_transformation_module(extract_internals, extract_marked, fronte y(i,:) = b(i) end do - !$loki extract name(test1) + !$loki outline name(test1) do i=1, n do j=1, n+1 x(i) = x(i) + y(i, j) end do end do - !$loki end extract + !$loki end outline do i=1, n call plus_one(x, i=i) @@ -62,26 +62,26 @@ def test_extract_transformation_module(extract_internals, extract_marked, fronte module = Module.from_source(fcode, frontend=frontend) ExtractTransformation( - extract_internals=extract_internals, extract_marked=extract_marked + extract_internals=extract_internals, outline_regions=outline_regions ).apply(module) routines = tuple(r for r in module.contains.body if isinstance(r, Subroutine)) - assert len(routines) == 1 + (1 if extract_internals else 0) + (1 if extract_marked else 0) + assert len(routines) == 1 + (1 if extract_internals else 0) + (1 if outline_regions else 0) assert ('plus_one' in module) == extract_internals - assert ('test1' in module) == extract_marked + assert ('test1' in module) == outline_regions outer = module['outer'] - assert len(FindNodes(ir.CallStatement).visit(outer.body)) == (2 if extract_marked else 1) + assert len(FindNodes(ir.CallStatement).visit(outer.body)) == (2 if outline_regions else 1) outer_internals = tuple(r for r in outer.contains.body if isinstance(r, Subroutine)) assert len(outer_internals) == (0 if extract_internals else 1) @pytest.mark.parametrize('frontend', available_frontends()) -@pytest.mark.parametrize('extract_marked', [False, True]) +@pytest.mark.parametrize('outline_regions', [False, True]) @pytest.mark.parametrize('extract_internals', [False, True]) -def test_extract_transformation_sourcefile(extract_internals, extract_marked, frontend): +def test_extract_transformation_sourcefile(extract_internals, outline_regions, frontend): """ - Test internal procedure extraction from subroutines. + Test internal procedure extraction and region outlining from subroutines. """ fcode = """ subroutine outer(n, a, b) @@ -95,13 +95,13 @@ def test_extract_transformation_sourcefile(extract_internals, extract_marked, fr y(i,:) = b(i) end do - !$loki extract name(test1) + !$loki outline name(test1) do i=1, n do j=1, n+1 x(i) = x(i) + y(i, j) end do end do - !$loki end extract + !$loki end outline do i=1, n call plus_one(x, i=i) @@ -119,15 +119,15 @@ def test_extract_transformation_sourcefile(extract_internals, extract_marked, fr sourcefile = Sourcefile.from_source(fcode, frontend=frontend) ExtractTransformation( - extract_internals=extract_internals, extract_marked=extract_marked + extract_internals=extract_internals, outline_regions=outline_regions ).apply(sourcefile) routines = tuple(r for r in sourcefile.ir.body if isinstance(r, Subroutine)) - assert len(routines) == 1 + (1 if extract_internals else 0) + (1 if extract_marked else 0) + assert len(routines) == 1 + (1 if extract_internals else 0) + (1 if outline_regions else 0) assert ('plus_one' in sourcefile) == extract_internals - assert ('test1' in sourcefile) == extract_marked + assert ('test1' in sourcefile) == outline_regions outer = sourcefile['outer'] - assert len(FindNodes(ir.CallStatement).visit(outer.body)) == (2 if extract_marked else 1) + assert len(FindNodes(ir.CallStatement).visit(outer.body)) == (2 if outline_regions else 1) outer_internals = tuple(r for r in outer.contains.body if isinstance(r, Subroutine)) assert len(outer_internals) == (0 if extract_internals else 1) diff --git a/loki/transformations/extract/tests/test_extract_marked.py b/loki/transformations/extract/tests/test_outline.py similarity index 84% rename from loki/transformations/extract/tests/test_extract_marked.py rename to loki/transformations/extract/tests/test_outline.py index c9ef897fa..a17ae7361 100644 --- a/loki/transformations/extract/tests/test_extract_marked.py +++ b/loki/transformations/extract/tests/test_outline.py @@ -14,7 +14,7 @@ from loki.ir import FindNodes, Section, Assignment, CallStatement, Intrinsic from loki.tools import as_tuple -from loki.transformations.extract.marked import extract_marked_subroutines +from loki.transformations.extract.outline import outline_pragma_regions @pytest.fixture(scope='function', name='builder') @@ -24,23 +24,23 @@ def fixture_builder(tmp_path): @pytest.mark.parametrize('frontend', available_frontends()) -def test_extract_marked_subroutines(tmp_path, frontend): +def test_outline_pragma_regions(tmp_path, frontend): """ - A very simple :any:`extract_marked_subroutine` test case + A very simple :any:`outline_pragma_regions` test case """ fcode = """ -subroutine test_extract(a, b, c) +subroutine test_outline(a, b, c) integer, intent(out) :: a, b, c a = 5 a = 1 -!$loki extract in(a) out(b) +!$loki outline in(a) out(b) b = a -!$loki end extract +!$loki end outline c = a + b -end subroutine test_extract +end subroutine test_outline """ routine = Subroutine.from_source(fcode, frontend=frontend) filepath = tmp_path/(f'{routine.name}_{frontend}.f90') @@ -54,8 +54,8 @@ def test_extract_marked_subroutines(tmp_path, frontend): assert len(FindNodes(CallStatement).visit(routine.body)) == 0 # Apply transformation - routines = extract_marked_subroutines(routine) - assert len(routines) == 1 and routines[0].name == f'{routine.name}_extracted_0' + routines = outline_pragma_regions(routine) + assert len(routines) == 1 and routines[0].name == f'{routine.name}_outlined_0' assert len(FindNodes(Assignment).visit(routine.body)) == 3 assert len(FindNodes(Assignment).visit(routines[0].body)) == 1 @@ -73,30 +73,30 @@ def test_extract_marked_subroutines(tmp_path, frontend): @pytest.mark.parametrize('frontend', available_frontends()) -def test_extract_marked_subroutines_multiple(tmp_path, frontend): +def test_outline_pragma_regions_multiple(tmp_path, frontend): """ Test hoisting with multiple groups and multiple regions per group """ fcode = """ -subroutine test_extract_mult(a, b, c) +subroutine test_outline_mult(a, b, c) integer, intent(out) :: a, b, c a = 1 a = a + 1 a = a + 1 -!$loki extract name(oiwjfklsf) inout(a) +!$loki outline name(oiwjfklsf) inout(a) a = a + 1 -!$loki end extract +!$loki end outline a = a + 1 -!$loki extract in(a) out(b) +!$loki outline in(a) out(b) b = a -!$loki end extract +!$loki end outline -!$loki extract in(a,b) out(c) +!$loki outline in(a,b) out(c) c = a + b -!$loki end extract -end subroutine test_extract_mult +!$loki end outline +end subroutine test_outline_mult """ routine = Subroutine.from_source(fcode, frontend=frontend) filepath = tmp_path/(f'{routine.name}_{frontend}.f90') @@ -110,10 +110,10 @@ def test_extract_marked_subroutines_multiple(tmp_path, frontend): assert len(FindNodes(CallStatement).visit(routine.body)) == 0 # Apply transformation - routines = extract_marked_subroutines(routine) + routines = outline_pragma_regions(routine) assert len(routines) == 3 assert routines[0].name == 'oiwjfklsf' - assert all(routines[i].name == f'{routine.name}_extracted_{i}' for i in (1,2)) + assert all(routines[i].name == f'{routine.name}_outlined_{i}' for i in (1,2)) assert len(FindNodes(Assignment).visit(routine.body)) == 4 assert all(len(FindNodes(Assignment).visit(r.body)) == 1 for r in routines) @@ -131,32 +131,32 @@ def test_extract_marked_subroutines_multiple(tmp_path, frontend): @pytest.mark.parametrize('frontend', available_frontends()) -def test_extract_marked_subroutines_arguments(tmp_path, frontend): +def test_outline_pragma_regions_arguments(tmp_path, frontend): """ Test hoisting with multiple groups and multiple regions per group and automatic derivation of arguments """ fcode = """ -subroutine test_extract_args(a, b, c) +subroutine test_outline_args(a, b, c) integer, intent(out) :: a, b, c a = 1 a = a + 1 a = a + 1 -!$loki extract name(func_a) +!$loki outline name(func_a) a = a + 1 -!$loki end extract +!$loki end outline a = a + 1 -!$loki extract name(func_b) +!$loki outline name(func_b) b = a -!$loki end extract +!$loki end outline ! partially override arguments -!$loki extract name(func_c) inout(b) +!$loki outline name(func_c) inout(b) c = a + b -!$loki end extract -end subroutine test_extract_args +!$loki end outline +end subroutine test_outline_args """ routine = Subroutine.from_source(fcode, frontend=frontend) filepath = tmp_path/(f'{routine.name}_{frontend}.f90') @@ -170,7 +170,7 @@ def test_extract_marked_subroutines_arguments(tmp_path, frontend): assert len(FindNodes(CallStatement).visit(routine.body)) == 0 # Apply transformation - routines = extract_marked_subroutines(routine) + routines = outline_pragma_regions(routine) assert len(routines) == 3 assert [r.name for r in routines] == ['func_a', 'func_b', 'func_c'] @@ -202,35 +202,35 @@ def test_extract_marked_subroutines_arguments(tmp_path, frontend): @pytest.mark.parametrize('frontend', available_frontends()) -def test_extract_marked_subroutines_arrays(tmp_path, frontend): +def test_outline_pragma_regions_arrays(tmp_path, frontend): """ Test hoisting with array variables """ fcode = """ -subroutine test_extract_arr(a, b, n) +subroutine test_outline_arr(a, b, n) integer, intent(out) :: a(n), b(n) integer, intent(in) :: n integer :: j -!$loki extract +!$loki outline do j=1,n a(j) = j end do -!$loki end extract +!$loki end outline -!$loki extract +!$loki outline do j=1,n b(j) = j end do -!$loki end extract +!$loki end outline -!$loki extract +!$loki outline do j=1,n-1 b(j) = b(j+1) - a(j) end do b(n) = 1 -!$loki end extract -end subroutine test_extract_arr +!$loki end outline +end subroutine test_outline_arr """ routine = Subroutine.from_source(fcode, frontend=frontend) @@ -249,7 +249,7 @@ def test_extract_marked_subroutines_arrays(tmp_path, frontend): assert len(FindNodes(CallStatement).visit(routine.body)) == 0 # Apply transformation - routines = extract_marked_subroutines(routine) + routines = outline_pragma_regions(routine) assert len(FindNodes(Assignment).visit(routine.body)) == 0 assert len(FindNodes(CallStatement).visit(routine.body)) == 3 @@ -276,49 +276,49 @@ def test_extract_marked_subroutines_arrays(tmp_path, frontend): @pytest.mark.parametrize('frontend', available_frontends()) -def test_extract_marked_subroutines_imports(tmp_path, builder, frontend): +def test_outline_pragma_regions_imports(tmp_path, builder, frontend): """ Test hoisting with correct treatment of imports """ fcode_module = """ -module extract_mod +module outline_mod implicit none integer, parameter :: param = 1 integer :: arr1(10) integer :: arr2(10) -end module extract_mod +end module outline_mod """.strip() fcode = """ -module test_extract_imps_mod +module test_outline_imps_mod implicit none contains - subroutine test_extract_imps(a, b) - use extract_mod, only: param, arr1, arr2 + subroutine test_outline_imps(a, b) + use outline_mod, only: param, arr1, arr2 integer, intent(out) :: a(10), b(10) integer :: j -!$loki extract +!$loki outline do j=1,10 a(j) = param end do -!$loki end extract +!$loki end outline -!$loki extract +!$loki outline do j=1,10 arr1(j) = j+1 end do -!$loki end extract +!$loki end outline arr2(:) = arr1(:) -!$loki extract +!$loki outline do j=1,10 b(j) = arr2(j) - a(j) end do -!$loki end extract - end subroutine test_extract_imps -end module test_extract_imps_mod +!$loki end outline + end subroutine test_outline_imps +end module test_outline_imps_mod """ ext_module = Module.from_source(fcode_module, frontend=frontend, xmods=[tmp_path]) module = Module.from_source(fcode, frontend=frontend, definitions=ext_module, xmods=[tmp_path]) @@ -338,7 +338,7 @@ def test_extract_marked_subroutines_imports(tmp_path, builder, frontend): assert len(FindNodes(CallStatement).visit(module.subroutines[0].body)) == 0 # Apply transformation - routines = extract_marked_subroutines(module.subroutines[0]) + routines = outline_pragma_regions(module.subroutines[0]) assert len(FindNodes(Assignment).visit(module.subroutines[0].body)) == 1 assert len(FindNodes(CallStatement).visit(module.subroutines[0].body)) == 3