Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #249. OCLTrans() generates OpenCL kernels (also fixes fparser2 stmt_fns) #387

Merged
merged 30 commits into from
Jun 12, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b33b987
#249 fparser2 statement functions fix and rename_and_write outputs op…
sergisiso May 21, 2019
db105b4
#249 Fixed pytest using tmpdir fixture
sergisiso May 21, 2019
ed5d035
Merge remote-tracking branch 'origin/master' into 249_OCLTransKernels
sergisiso May 21, 2019
b35fdfe
#249 Statement function correction unit-testing
sergisiso Jun 3, 2019
0695060
#249 gen_c_code always use 'e' for scientific notation
sergisiso Jun 3, 2019
876970f
#249 Fixes in gocean1p0 unit test
sergisiso Jun 3, 2019
77ae128
Merge remote-tracking branch 'origin/master' into 249_OCLTransKernels
sergisiso Jun 3, 2019
505540c
#249 Fixed unit-test issues
sergisiso Jun 3, 2019
9ee0535
#249 Remove unnecessary renaming
sergisiso Jun 3, 2019
5d41644
#249 Added some comments
sergisiso Jun 4, 2019
2abd6c2
Merge branch '249_OCLTransKernels' of github.com:stfc/PSyclone into 2…
sergisiso Jun 4, 2019
a09dff2
#249 Added missing unit-test and fixed style issue
sergisiso Jun 4, 2019
bf48327
#249 Updated references of examples/gocean/eg3 that don't have GO_ pr…
sergisiso Jun 6, 2019
9017cb4
#249 Updated OpenCL documentation
sergisiso Jun 6, 2019
44c1e69
#249 Removed Issue reference
sergisiso Jun 6, 2019
b5dd0bd
Merge remote-tracking branch 'origin/master' into 249_OCLTransKernels
sergisiso Jun 6, 2019
1cebdce
#249 Fixed some issues with gocean/eg3
sergisiso Jun 7, 2019
63d3cfc
#249 Addressed reviewer's comments
sergisiso Jun 7, 2019
5e991ba
#249 Fixed unittest compileopencl, OCLTrans only accepts single kerne…
sergisiso Jun 10, 2019
c5a68c6
#249 Addressed reviewer's comments
sergisiso Jun 10, 2019
d4651fa
#249 Removed OpenCL single kernel renaming limitation
sergisiso Jun 10, 2019
e327a46
#249 Removed commented code on gocean/eg3
sergisiso Jun 10, 2019
3ddd9f4
#249 Removed in-progress gocean/eg3 Makefile
sergisiso Jun 10, 2019
724ede9
#249 Added Error docstring for Part_Ref handler
sergisiso Jun 11, 2019
903b643
#249 Update documentation and OCLTrans docstring
sergisiso Jun 11, 2019
47f37be
#249 Minor changes in the test suite
sergisiso Jun 11, 2019
dfe5244
#249 Fixed pylint issues
sergisiso Jun 11, 2019
8db3970
#249 Addressed reviewers comments
sergisiso Jun 12, 2019
e20fb6a
#249 Added a xfail for a module variable access, xpasses now make the…
sergisiso Jun 12, 2019
752e2f0
#387 update changelog and UG
arporter Jun 12, 2019
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
17 changes: 10 additions & 7 deletions examples/gocean/eg3/README
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,14 @@ transformation to the Schedule of the Invoke. This will generate the OpenCL
driver layer to stdout and a 'kernel_name'.cl file for each of the kernels
referenced in alg.f90 traslated to OpenCL.

Each OpenCL kernels needs to be compiled before buidling the driver layer,
and example of the full building workflow would be:
Each OpenCL kernel needs to be compiled before buidling the driver layer.
For example, the steps to generate the code using the Intel OpenCL SDK
(https://software.intel.com/en-us/opencl-sdk) are::

psyclone -oalg psyalg.f90 -opsy psylayer.f90 -api "gocean1.0" -s ./ocl_trans.py alg.f90
# Pre-build OpenCL kernels, e.g.:
ioc64 -cmd=build -device=cpu -input=kernels.cl -spirv64=kernels.spir -bo="-cl-std=CL1.2"
PSYCLONE_KERNELS_FILE=kernels.spirv
# Compile and link driver layer
psyclone -oalg psyalg.f90 -opsy psylayer.f90 -api "gocean1.0" \
-s ./ocl_trans.py alg.f90

# Pre-build OpenCL kernels
ioc64 -cmd=build -device=cpu -input=kernels.cl -spirv64=kernels.spirv \
-bo="-cl-std=CL1.2"
export PSYCLONE_KERNELS_FILE=kernels.spirv
4 changes: 2 additions & 2 deletions examples/gocean/eg3/alg.f90
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ program simple
z_fld = r2d_field(model_grid, F_POINTS)
h_fld = r2d_field(model_grid, T_POINTS)

write(*,*) "Simulation start"
do ncycle=1, 100

call invoke( compute_cu(CU_fld, p_fld, u_fld), &
compute_cv(CV_fld, p_fld, v_fld), &
compute_z(z_fld, p_fld, u_fld, v_fld), &
compute_h(h_fld, p_fld, u_fld, v_fld) )

end do
write(*,*) "Simulation end"

end program simple
1 change: 0 additions & 1 deletion examples/gocean/eg3/compute_cu_mod.f90
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ module compute_cu_mod

private

!public invoke_compute_cu
public compute_cu, compute_cu_code

type, extends(kernel_type) :: compute_cu
Expand Down
1 change: 0 additions & 1 deletion examples/gocean/eg3/compute_h_mod.f90
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ module compute_h_mod

private

!public invoke_compute_h
public compute_h, compute_h_code

type, extends(kernel_type) :: compute_h
Expand Down
3 changes: 1 addition & 2 deletions examples/gocean/eg3/compute_z_mod.f90
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ module compute_z_mod

private

!public invoke_compute_z
public compute_z, compute_z_code

type, extends(kernel_type) :: compute_z
Expand All @@ -58,7 +57,7 @@ module compute_z_mod
/)
!> This kernel operates on fields that live on an
!! orthogonal, regular grid.
!integer :: GRID_TYPE = GO_ORTHOGONAL_REGULAR
integer :: GRID_TYPE = GO_ORTHOGONAL_REGULAR

!> This kernel writes only to internal points of the
!! simulation domain.
Expand Down
27 changes: 19 additions & 8 deletions src/psyclone/psyGen.py
Original file line number Diff line number Diff line change
Expand Up @@ -3790,8 +3790,13 @@ def rename_and_write(self):
continue

# Use the suffix we have determined to rename all relevant quantities
# within the AST of the kernel code
self._rename_ast(new_suffix)
# within the AST of the kernel code.
# We can't rename OpenCL kernels as the Invoke set_args functions
# have already been generated. The link to an specific kernel
# implementation is delayed to run-time in OpenCL. (e.g. FortCL has
# the PSYCLONE_KERNELS_FILE environment variable)
arporter marked this conversation as resolved.
Show resolved Hide resolved
if not self.root.opencl:
self._rename_ast(new_suffix)

# Kernel is now self-consistent so unset the modified flag
self.modified = False
Expand Down Expand Up @@ -5403,12 +5408,16 @@ def iterateitems(nodes):
"declarations for fparser nodes {1}."
"".format(str(arg_list), nodes))

# fparser2 does not always handle Statement Functions correctly, this
# loop checks for Stmt_Functions that should be an array statement
# and recovers them, otherwise it raises an error as currently
# Statement Functions are not supported in PSyIR.
for stmtfn in walk_ast(nodes, [Fortran2003.Stmt_Function_Stmt]):
arporter marked this conversation as resolved.
Show resolved Hide resolved
(fn_name, arg_list, scalar_expr) = stmtfn.items
try:
symbol = parent.symbol_table.lookup(fn_name.string)
if symbol.is_array:
# This is an array assignment worngly categorized as a
# This is an array assignment wrongly categorized as a
# statement_function by fparser2.
array_name = fn_name
if hasattr(arg_list, 'items'):
Expand All @@ -5431,9 +5440,10 @@ def iterateitems(nodes):
self.process_nodes(parent=assignment, nodes=[scalar_expr],
nodes_parent=scalar_expr)
else:
raise GenerationError(
"Could not process '{0}'. Symbol '{1}' is not "
"declared as an array on the SymbolTable."
raise InternalError(
"Could not process '{0}'. Symbol '{1}' is in the"
" SymbolTable but it is not an array as expected, so"
" it can not be recovered as an array assignment."
"".format(str(stmtfn), symbol.name))
except KeyError:
raise NotImplementedError(
Expand Down Expand Up @@ -5972,8 +5982,6 @@ def _part_ref_handler(self, node, parent):
return uop
if reference_name == 'real':
if len(node.items) != 2:
# TODO: Note that real(var, kind) expressions are not supported
# because Fortran kinds are still not caputred (Issue #375)
raise GenerationError(
"Unexpected fparser2 node when parsing the real() "
"intrinsic, 2 items were expected but found '{0}'."
Expand All @@ -5986,6 +5994,9 @@ def _part_ref_handler(self, node, parent):
argument = node.items[1].items[0]
if len(node.items[1].items) > 1:
# If it has more than a single argument create a CodeBlock
# TODO: Note that real(var, kind) expressions are not
# supported because Fortran kinds are still not captured
# (Issue #375)
arporter marked this conversation as resolved.
Show resolved Hide resolved
raise NotImplementedError()
else:
argument = node.items[1]
Expand Down
37 changes: 20 additions & 17 deletions src/psyclone/tests/gocean1p0_opencl_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def outputdir(tmpdir, monkeypatch):
'''Sets the Psyclone _kernel_output_dir Config parameter to tmpdir.'''
config = Config.get()
monkeypatch.setattr(config, "_kernel_output_dir", str(tmpdir))
return tmpdir


# ----------------------------------------------------------------------------
Expand Down Expand Up @@ -88,7 +89,6 @@ def test_opencl_compiler_works(outputdir):
old_pwd.chdir()


# ----------------------------------------------------------------------------
def test_use_stmts(outputdir):
''' Test that generating code for OpenCL results in the correct
module use statements. '''
Expand All @@ -105,6 +105,9 @@ def test_use_stmts(outputdir):
use iso_c_binding'''
assert expected in generated_code
assert "if (first_time) then" in generated_code

generated_code = str(psy.gen).lower()
arporter marked this conversation as resolved.
Show resolved Hide resolved
assert expected in generated_code
assert GOcean1p0OpenCLBuild(outputdir).code_compiles(psy)


Expand All @@ -127,7 +130,7 @@ def test_psy_init(outputdir):
" ! Initialise the OpenCL environment/device\n"
" CALL ocl_env_init\n"
" ! The kernels this PSy layer module requires\n"
" kernel_names(1) = \"compute_cu_0_code\"\n"
" kernel_names(1) = \"compute_cu_code\"\n"
" ! Create the OpenCL kernel objects. Expects to find all of "
"the compiled\n"
" ! kernels in PSYCLONE_KERNELS_FILE.\n"
Expand All @@ -147,11 +150,11 @@ def test_set_kern_args(outputdir, monkeypatch):
otrans.apply(sched)
generated_code = str(psy.gen)
# Check we've only generated one set-args routine
assert generated_code.count("SUBROUTINE compute_cu_0_code_set_args("
assert generated_code.count("SUBROUTINE compute_cu_code_set_args("
"kernel_obj, nx, cu_fld, p_fld, u_fld)") == 1
# Declarations
expected = '''\
SUBROUTINE compute_cu_0_code_set_args(kernel_obj, nx, cu_fld, p_fld, u_fld)
SUBROUTINE compute_cu_code_set_args(kernel_obj, nx, cu_fld, p_fld, u_fld)
USE clfortran, ONLY: clSetKernelArg
USE iso_c_binding, ONLY: c_sizeof, c_loc, c_intptr_t
USE ocl_utils_mod, ONLY: check_status
Expand All @@ -160,20 +163,20 @@ def test_set_kern_args(outputdir, monkeypatch):
INTEGER(KIND=c_intptr_t), target :: kernel_obj'''
assert expected in generated_code
expected = '''\
! Set the arguments for the compute_cu_0_code OpenCL Kernel
! Set the arguments for the compute_cu_code OpenCL Kernel
ierr = clSetKernelArg(kernel_obj, 0, C_SIZEOF(nx), C_LOC(nx))
ierr = clSetKernelArg(kernel_obj, 1, C_SIZEOF(cu_fld), C_LOC(cu_fld))
CALL check_status('clSetKernelArg: arg 1 of compute_cu_0_code', ierr)
CALL check_status('clSetKernelArg: arg 1 of compute_cu_code', ierr)
ierr = clSetKernelArg(kernel_obj, 2, C_SIZEOF(p_fld), C_LOC(p_fld))
CALL check_status('clSetKernelArg: arg 2 of compute_cu_0_code', ierr)
CALL check_status('clSetKernelArg: arg 2 of compute_cu_code', ierr)
ierr = clSetKernelArg(kernel_obj, 3, C_SIZEOF(u_fld), C_LOC(u_fld))
CALL check_status('clSetKernelArg: arg 3 of compute_cu_0_code', ierr)
END SUBROUTINE compute_cu_0_code_set_args'''
CALL check_status('clSetKernelArg: arg 3 of compute_cu_code', ierr)
END SUBROUTINE compute_cu_code_set_args'''
assert expected in generated_code
assert generated_code.count("SUBROUTINE time_smooth_0_code_set_args("
assert generated_code.count("SUBROUTINE time_smooth_code_set_args("
"kernel_obj, nx, u_fld, "
"unew_fld, uold_fld)") == 1
assert ("CALL compute_cu_0_code_set_args(kernel_compute_cu_0_code, "
assert ("CALL compute_cu_code_set_args(kernel_compute_cu_code, "
"p_fld%grid%nx, cu_fld%device_ptr, p_fld%device_ptr, "
"u_fld%device_ptr)" in generated_code)
assert GOcean1p0OpenCLBuild(outputdir).code_compiles(psy)
Expand All @@ -188,7 +191,7 @@ def test_set_kern_float_arg(outputdir):
otrans.apply(sched)
generated_code = str(psy.gen)
expected = '''\
SUBROUTINE bc_ssh_0_code_set_args(kernel_obj, nx, a_scalar, ssh_fld, tmask)
SUBROUTINE bc_ssh_code_set_args(kernel_obj, nx, a_scalar, ssh_fld, tmask)
USE clfortran, ONLY: clSetKernelArg
USE iso_c_binding, ONLY: c_sizeof, c_loc, c_intptr_t
USE ocl_utils_mod, ONLY: check_status
Expand All @@ -200,15 +203,15 @@ def test_set_kern_float_arg(outputdir):
'''
assert expected in generated_code
expected = '''\
! Set the arguments for the bc_ssh_0_code OpenCL Kernel
! Set the arguments for the bc_ssh_code OpenCL Kernel
ierr = clSetKernelArg(kernel_obj, 0, C_SIZEOF(nx), C_LOC(nx))
ierr = clSetKernelArg(kernel_obj, 1, C_SIZEOF(a_scalar), C_LOC(a_scalar))
CALL check_status('clSetKernelArg: arg 1 of bc_ssh_0_code', ierr)
CALL check_status('clSetKernelArg: arg 1 of bc_ssh_code', ierr)
ierr = clSetKernelArg(kernel_obj, 2, C_SIZEOF(ssh_fld), C_LOC(ssh_fld))
CALL check_status('clSetKernelArg: arg 2 of bc_ssh_0_code', ierr)
CALL check_status('clSetKernelArg: arg 2 of bc_ssh_code', ierr)
ierr = clSetKernelArg(kernel_obj, 3, C_SIZEOF(tmask), C_LOC(tmask))
CALL check_status('clSetKernelArg: arg 3 of bc_ssh_0_code', ierr)
END SUBROUTINE bc_ssh_0_code_set_args'''
CALL check_status('clSetKernelArg: arg 3 of bc_ssh_code', ierr)
END SUBROUTINE bc_ssh_code_set_args'''
assert expected in generated_code
assert GOcean1p0OpenCLBuild(outputdir).code_compiles(psy)

Expand Down
24 changes: 20 additions & 4 deletions src/psyclone/tests/psyGen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4199,9 +4199,24 @@ def test_fparser2astprocessor_process_declarations_stmt_functions(
'''
from fparser.common.readfortran import FortranStringReader
from fparser.two.Fortran2003 import Specification_Part
from fparser.two.Fortran2003 import Stmt_Function_Stmt
fake_parent = KernelSchedule("dummy_schedule")
processor = Fparser2ASTProcessor()

# If 'a' is not declared it could be a statement function, which are
# unsupported and produce a NotImplementedError.
reader = FortranStringReader("a(x) = 1")
fparser2spec = Stmt_Function_Stmt(reader)
arporter marked this conversation as resolved.
Show resolved Hide resolved
with pytest.raises(NotImplementedError) as error:
processor.process_declarations(fake_parent, [fparser2spec], [])
assert "Could not process '" in str(error.value)
assert "'. Statement Function declarations are not supported." \
in str(error.value)

# The code below checks that misclassified Statment_Functions are
# recovered as arrays, this may become unecessary after fparser/#171
# is fixed.

# If 'a' is not declared it could be a statement function, which are
# unsupported and produce a NotImplementedError.
reader = FortranStringReader("a(x) = 1")
Expand All @@ -4227,8 +4242,8 @@ def test_fparser2astprocessor_process_declarations_stmt_functions(
reader = FortranStringReader("b(x, y) = 1")
fparser2spec = Specification_Part(reader).content[0]
fake_parent.symbol_table.add(Symbol('b', 'real', shape=[None, None]))
arporter marked this conversation as resolved.
Show resolved Hide resolved
fake_parent.symbol_table.add(Symbol('x', 'real', shape=[]))
fake_parent.symbol_table.add(Symbol('y', 'real', shape=[]))
fake_parent.symbol_table.add(Symbol('x', 'integer', shape=[]))
fake_parent.symbol_table.add(Symbol('y', 'integer', shape=[]))
processor.process_declarations(fake_parent, [fparser2spec], [])
assert len(fake_parent.children) == 1
array = fake_parent.children[0].children[0]
Expand All @@ -4237,10 +4252,11 @@ def test_fparser2astprocessor_process_declarations_stmt_functions(

# Test that if symbol is not an array, it raises GenerationError
fake_parent.symbol_table.lookup('b')._shape = []
with pytest.raises(GenerationError) as error:
with pytest.raises(InternalError) as error:
processor.process_declarations(fake_parent, [fparser2spec], [])
assert "Could not process '" in str(error.value)
assert "'. Symbol 'b' is not declared as an array on the SymbolTable." \
assert "'. Symbol 'b' is in the SymbolTable but it is not an array as " \
"expected, so it can not be recovered as an array assignment." \
in str(error.value)


Expand Down
5 changes: 5 additions & 0 deletions src/psyclone/tests/psyclone_test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,11 @@ def _code_compiles(self, psy_ast):
# Build the kernels. We allow kernels to also be located in
# the temporary directory that we have been passed.
for fort_file in kernel_modules:

# Skip file if it is not Fortran
if fort_file.endswith(".cl"):
arporter marked this conversation as resolved.
Show resolved Hide resolved
continue

name = self.find_fortran_file([self.base_path,
str(self._tmpdir)], fort_file)
self.compile_file(name)
Expand Down