Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6cbe966
For #1823. Add Kernel symbols when Kernel is created.
arporter Jan 21, 2026
ba8f599
#1823 first sketch of mixin [skip ci]
arporter Jan 21, 2026
5b4b68b
#1823 WIP fixing module-inlining for multiple kernel calls [skip ci]
arporter Jan 21, 2026
95b5710
#1823 WIP removing .module_inline setter and fixing tests [skip ci]
arporter Jan 22, 2026
1bb59eb
#1823 fix module-inline tests
arporter Jan 23, 2026
3811893
#1823 WIP fixing tests [skip ci]
arporter Jan 23, 2026
3082aa8
#1823 WIP fixing more tests [skip ci]
arporter Jan 26, 2026
2ae13c8
Merge branch 'master' into 1823_transform_inlined_kerns_only
arporter Jan 26, 2026
564e46d
#1823 more test fixes [skip ci]
arporter Jan 26, 2026
134cd05
#1823 fix lint [skip ci]
arporter Jan 26, 2026
06341f7
#1823 WIP fixing tests [skip ci]
arporter Jan 26, 2026
0230c2f
#1823 more test fixing
arporter Jan 27, 2026
8fec073
Merge branch 'master' into 1823_transform_inlined_kerns_only
arporter Jan 27, 2026
240f86d
#1823 WIP fixing examples [skip ci]
arporter Jan 27, 2026
b269f7a
#1823 add docstrings to new mixin
arporter Jan 27, 2026
5a88b29
Merge branch 'master' into 1823_transform_inlined_kerns_only
arporter Feb 2, 2026
9d8ac4a
#1823 begin adding new tests [skip ci]
arporter Feb 2, 2026
1c07cb1
Merge branch 'master' into 1823_transform_inlined_kerns_only
arporter Feb 4, 2026
2e0dbc1
#1823 get coverage of new mixin
arporter Feb 4, 2026
f73cd68
#1823 remove unused/unreachable code and reinstate some tests
arporter Feb 4, 2026
2eaf62c
#1823 allow for ContainerSymbol from which kernel is imported to be i…
arporter Feb 4, 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
20 changes: 12 additions & 8 deletions examples/gocean/eg1/opencl_transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,26 @@
''' Module providing a PSyclone transformation script that converts the
Schedule of each Invoke to use OpenCL. '''

from psyclone.psyGen import TransInfo, InvokeSchedule
from psyclone.domain.gocean.transformations import GOOpenCLTrans, \
GOMoveIterationBoundariesInsideKernelTrans
from psyclone.psyGen import InvokeSchedule
from psyclone.domain.common.transformations import KernelModuleInlineTrans
from psyclone.domain.gocean.transformations import (
GOOpenCLTrans, GOMoveIterationBoundariesInsideKernelTrans)
from psyclone.psyir.nodes import FileContainer
from psyclone.transformations import KernelImportsToArguments


def trans(psyir):
def trans(psyir: FileContainer):
'''
Transformation routine for use with PSyclone. Converts any imported-
variable accesses into kernel arguments and then applies the OpenCL
transformation to the PSy layer.

:param psyir: the PSyIR of the PSy-layer.
:type psyir: :py:class:`psyclone.psyir.nodes.FileContainer`

'''
# Get the necessary transformations
tinfo = TransInfo()
import_trans = tinfo.get_trans_name('KernelImportsToArguments')
import_trans = KernelImportsToArguments()
mod_inline_trans = KernelModuleInlineTrans()
move_boundaries_trans = GOMoveIterationBoundariesInsideKernelTrans()
cltrans = GOOpenCLTrans()

Expand All @@ -67,9 +69,11 @@ def trans(psyir):
continue

# Remove the imports from inside each kernel and move PSy-layer
# loop boundaries inside the kernel as a mask.
# loop boundaries inside the kernel as a mask. To do this we must
# first module-inline the kernel into the PSy layer module.
for kern in schedule.kernels():
print("Update kernel: " + kern.name)
mod_inline_trans.apply(kern)
move_boundaries_trans.apply(kern)
import_trans.apply(kern)

Expand Down
8 changes: 6 additions & 2 deletions examples/gocean/eg3/ocl_trans.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,28 @@
from psyclone.psyGen import InvokeSchedule
from psyclone.psyir.transformations import (
FoldConditionalReturnExpressionsTrans)
from psyclone.domain.common.transformations import KernelModuleInlineTrans
from psyclone.domain.gocean.transformations import (
GOOpenCLTrans, GOMoveIterationBoundariesInsideKernelTrans)
from psyclone.psyir.nodes import FileContainer


def trans(psyir):
def trans(psyir: FileContainer):
'''
Applies OpenCL to the given PSy-layer.

:param psyir: the PSyIR of the PSy-layer.
:type psyir: :py:class:`psyclone.psyir.nodes.FileContainer`

'''
mod_inline_trans = KernelModuleInlineTrans()
ocl_trans = GOOpenCLTrans()
fold_trans = FoldConditionalReturnExpressionsTrans()
move_boundaries_trans = GOMoveIterationBoundariesInsideKernelTrans()

# Provide kernel-specific OpenCL optimization options
for idx, kern in enumerate(psyir.kernels()):
# Kernel has to be module-inlined first.
mod_inline_trans.apply(kern)
# Move the PSy-layer loop boundaries inside the kernel as a kernel
# mask, this allows to iterate through the whole domain
move_boundaries_trans.apply(kern)
Expand Down
2 changes: 1 addition & 1 deletion examples/gocean/eg4/acc_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def trans(psyir):
# Convert any accesses to imported data into kernel arguments, put an
# 'acc routine' directive inside, and module-inline each kernel
for kern in schedule.coded_kernels():
itrans.apply(kern)
if kern.name == "kern_use_var_code":
g2localtrans.apply(kern)
ktrans.apply(kern)
itrans.apply(kern)
13 changes: 8 additions & 5 deletions examples/gocean/eg4/ocl_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,25 @@
'''

from psyclone.transformations import KernelImportsToArguments
from psyclone.domain.gocean.transformations import GOOpenCLTrans, \
GOMoveIterationBoundariesInsideKernelTrans
from psyclone.domain.common.transformations import KernelModuleInlineTrans
from psyclone.domain.gocean.transformations import (
GOOpenCLTrans, GOMoveIterationBoundariesInsideKernelTrans)
from psyclone.psyir.nodes import FileContainer


def trans(psyir):
def trans(psyir: FileContainer):
'''
Transformation routine for use with PSyclone. Applies the OpenCL
transform to the first Invoke in the psy object.
transform to the first Invoke in the PSy-layer.

:param psyir: the PSyIR of the PSy-layer.
:type psyir: :py:class:`psyclone.psyir.nodes.FileContainer`

'''
# Convert any kernel accesses to imported data into arguments
mod_inline_trans = KernelModuleInlineTrans()
ktrans = KernelImportsToArguments()
for kern in psyir.kernels():
mod_inline_trans.apply(kern)
ktrans.apply(kern)

# Provide kernel-specific OpenCL optimization options
Expand Down
19 changes: 5 additions & 14 deletions examples/lfric/eg14/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,12 @@ PSYROOT=../../..
include ../lfric_common.mk

GENERATED_FILES = *.o *.mod $(EXEC) main_alg.f90 main_psy.f90 \
other_alg_mod_psy.f90 other_alg_mod_alg.f90 \
testkern_w0_kernel_?_mod.f90
other_alg_mod_psy.f90 other_alg_mod_alg.f90

F90 ?= gfortran
F90FLAGS ?= -Wall -g

OBJ = main_psy.o main_alg.o other_alg_mod_psy.o other_alg_mod_alg.o \
testkern_w0_kernel_0_mod.o
OBJ = main_psy.o main_alg.o other_alg_mod_psy.o other_alg_mod_alg.o

EXEC = example_openacc

Expand All @@ -70,13 +68,8 @@ PROFILE_LINK=-ldummy

.PHONY: transform compile run

# This makefile assumes that the transformed kernel will be named
# 'testkern_w0_kernel_0_mod.f90'. However, if it already exists then PSyclone
# will create 'testkern_..._1_mod.f90' so remove it first. Also remove
# main_psy.f90, since testkern_w0_kernel_0_mod will not be recreated
# if main_psy.f90 exists
transform:
rm -f testkern_w0_kernel_0_mod.f90 main_psy.f90
rm -f main_psy.f90
${MAKE} main_psy.f90
${MAKE} other_alg_mod_psy.f90

Expand All @@ -86,8 +79,6 @@ transform:
${PSYCLONE} -api lfric -dm -s ./acc_parallel.py --profile invokes \
-opsy $*_psy.f90 -oalg $*_alg.f90 $<

testkern_w0_kernel_0_mod.f90: main_psy.f90

compile: transform ${EXEC}

run: compile
Expand All @@ -101,8 +92,8 @@ $(PROFILE_LIB):
$(MAKE) -C $(PROFILE_PATH)

# Dependencies
main_psy.o: other_alg_mod_psy.o testkern_w0_kernel_0_mod.o
main_alg.o: other_alg_mod_alg.o main_psy.o testkern_w0_kernel_0_mod.o
main_psy.o: other_alg_mod_psy.o
main_alg.o: other_alg_mod_alg.o main_psy.o

%.o: %.F90
$(F90) $(F90FLAGS) -I$(PROFILE_PATH) -c $<
Expand Down
4 changes: 4 additions & 0 deletions examples/lfric/eg14/acc_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
-s option.

'''
from psyclone.domain.common.transformations import KernelModuleInlineTrans
from psyclone.domain.lfric import LFRicConstants
from psyclone.psyGen import CodedKern, InvokeSchedule
from psyclone.psyir.transformations import ACCKernelsTrans
Expand All @@ -60,6 +61,7 @@ def trans(psyir):

ctrans = LFRicColourTrans()
enter_data_trans = ACCEnterDataTrans()
mod_inline_trans = KernelModuleInlineTrans()
kernel_trans = ACCKernelsTrans()
rtrans = ACCRoutineTrans()

Expand All @@ -86,4 +88,6 @@ def trans(psyir):
# adds '!$acc routine' which ensures the kernel is compiled for the
# OpenACC device.
for kernel in subroutine.walk(CodedKern):
# Module inlining is a pre-requisite for kernel transformations.
mod_inline_trans.apply(kernel)
rtrans.apply(kernel)
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
ContainerSymbol, ImportInterface,
GenericInterfaceSymbol, RoutineSymbol, Symbol, SymbolError, SymbolTable)
from psyclone.psyir.nodes import (
Call, Container, FileContainer, Routine, ScopingNode,
Call, Container, FileContainer, Reference, Routine, ScopingNode,
IntrinsicCall, )
from psyclone.utils import transformation_documentation_wrapper

Expand Down Expand Up @@ -620,6 +620,9 @@ def apply(self, node, options=None, **kwargs):
sym = node.scope.symbol_table.lookup(external_callee_name)
table = sym.find_symbol_table(node)
table.rename_symbol(sym, caller_name)
new_sym = sym
else:
new_sym = node.scope.symbol_table.lookup(caller_name)

# Update the Kernel to point to the updated PSyIR and set
# the module-inline flag to avoid generating the kernel imports
Expand All @@ -637,6 +640,6 @@ def apply(self, node, options=None, **kwargs):
# point to the module-inlined version.
for kern in cntr.walk(CodedKern, stop_type=CodedKern):
if kern.name == node.name:
kern.module_inline = True
# pylint: disable=protected-access
kern._schedules = updated_routines
kern.routine = Reference(new_sym)
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# -----------------------------------------------------------------------------
# BSD 3-Clause License
#
# Copyright (c) 2026, Science and Technology Facilities Council.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
# Author: A. R. Porter, STFC Daresbury Lab

"""
This module provides the KernelTransformationMixin class.

"""

from typing import Union

from psyclone.psyGen import CodedKern
from psyclone.psyir.nodes.container import Container
from psyclone.psyir.nodes.node import Node
from psyclone.psyir.transformations.transformation_error import (
TransformationError)


class KernelTransformationMixin:
"""
A mixin class to be used by all Transformations that act upon PSyKAl
Kernels.

Provides functionality to check that a Kernel has been module-inlined
before subsequent transformations are applied to it.

"""
def _check_kernel_is_local(self, node: Union[Node, CodedKern]) -> None:
"""
Check that the supplied kernel node has been module inlined.

:param node: the PSyKAl Kernel to check.

:raises TransformationError: if the supplied Kernel has not been
module inlined.
:raises TransformationError: if the supplied Kernel (call) is not
within a Container or the Container does not contain the
implementation of the Kernel.

"""
if not isinstance(node, CodedKern):
return

msg_text = (f"Cannot transform this Kernel call to '{node.name}' "
f"because")

rsymbol = node.scope.symbol_table.lookup(node.name, otherwise=None)
if not rsymbol or rsymbol.is_import or rsymbol.is_unresolved:
raise TransformationError(
f"{msg_text} it is not module inlined (i.e. local to the "
f"current module). Use KernelModuleInlineTrans() first."
)
container = node.ancestor(Container)
if not container:
raise TransformationError(
f"{msg_text} because there is no ancestor Container in which "
f"to look for its implementation."
)
names = container.resolve_routine(node.name)
for name in names:
rt = container.find_routine_psyir(name, allow_private=True)
if not rt:
raise TransformationError(
f"{msg_text} the ancestor Container does not contain "
f"a Routine named '{name}'"
)
Loading
Loading