Skip to content

Commit

Permalink
add handle for custom device (#61)
Browse files Browse the repository at this point in the history
This pull request addresses issue
#41 by implementing a new
feature in Pyccel that allows users to define a custom device

**Commit Summary**

- Adding handler for custom device and its code generation.
- Adding test

---------

Co-authored-by: EmilyBourne <louise.bourne@gmail.com>
  • Loading branch information
smazouz42 and EmilyBourne committed Jul 19, 2024
1 parent 2ffa7fc commit 8eef19d
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
- #59 : Updated `cuda` clash checker.
- #42 : Add support for custom kernel in`cuda`.
- #42 : Add Cuda module to Pyccel. Add support for `cuda.synchronize` function.
- #41 : Add support for custom device in`cuda`.

## \[UNRELEASED\]

Expand Down
25 changes: 24 additions & 1 deletion docs/cuda.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,27 @@ threadsperblock = 1
# Call your kernel function
my_kernel[blockspergrid, threadsperblock]()

```
```

### device

Device functions are similar to kernels, but are executed within the context of a kernel. They can be called only from kernels or device functions, and are typically used for operations that are too small to justify launching a separate kernel, or for operations that need to be performed repeatedly within the context of a kernel.

```python
from pyccel.decorators import device, kernel

@device
def add(x, y):
return x + y

@kernel
def my_kernel():
x = 1
y = 2
z = add(x, y)
print(z)

my_kernel[1, 1]()

```

7 changes: 4 additions & 3 deletions pyccel/codegen/printing/cucode.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ def function_signature(self, expr, print_arg_names = True):
str
Signature of the function.
"""
cuda_decorater = '__global__' if 'kernel' in expr.decorators else ''
cuda_decorator = '__global__' if 'kernel' in expr.decorators else \
'__device__' if 'device' in expr.decorators else ''
c_function_signature = super().function_signature(expr, print_arg_names)
return f'{cuda_decorater} {c_function_signature}'
return f'{cuda_decorator} {c_function_signature}'

def _print_KernelCall(self, expr):
func = expr.funcdef
Expand All @@ -109,7 +110,7 @@ def _print_ModuleHeader(self, expr):
cuda_headers = ""
for f in expr.module.funcs:
if not f.is_inline:
if 'kernel' in f.decorators: # Checking for 'kernel' decorator
if 'kernel' in f.decorators or 'device' in f.decorators:
cuda_headers += self.function_signature(f) + ';\n'
else:
funcs += self.function_signature(f) + ';\n'
Expand Down
19 changes: 19 additions & 0 deletions pyccel/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
__all__ = (
'allow_negative_index',
'bypass',
'device',
'elemental',
'inline',
'private',
Expand Down Expand Up @@ -141,3 +142,21 @@ def __getitem__(self, args):
return self._f

return KernelAccessor(f)

def device(f):
"""
Decorator for marking a function as a GPU device function.
This decorator is used to mark a Python function as a GPU device function.
Parameters
----------
f : Function
The function to be marked as a device.
Returns
-------
f
The function marked as a device.
"""
return f
2 changes: 1 addition & 1 deletion pyccel/errors/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@
INVALID_KERNEL_LAUNCH_CONFIG = 'Expected exactly 2 parameters for kernel launch'
INVALID_KERNEL_CALL_BP_GRID = 'Invalid Block per grid parameter for Kernel call'
INVALID_KERNEL_CALL_TP_BLOCK = 'Invalid Thread per Block parameter for Kernel call'

INVAlID_DEVICE_CALL = 'A function decorated with "device" should be called only from a "kernel" or another "device" function.'



7 changes: 6 additions & 1 deletion pyccel/parser/semantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,10 @@
UNUSED_DECORATORS, UNSUPPORTED_POINTER_RETURN_VALUE, PYCCEL_RESTRICTION_OPTIONAL_NONE,
PYCCEL_RESTRICTION_PRIMITIVE_IMMUTABLE, PYCCEL_RESTRICTION_IS_ISNOT,
FOUND_DUPLICATED_IMPORT, UNDEFINED_WITH_ACCESS, MACRO_MISSING_HEADER_OR_FUNC, PYCCEL_RESTRICTION_INHOMOG_SET,
MISSING_KERNEL_CONFIGURATION,
MISSING_KERNEL_CONFIGURATION, INVAlID_DEVICE_CALL,
INVALID_KERNEL_LAUNCH_CONFIG, INVALID_KERNEL_CALL_BP_GRID, INVALID_KERNEL_CALL_TP_BLOCK)


from pyccel.parser.base import BasicParser
from pyccel.parser.syntactic import SyntaxParser

Expand Down Expand Up @@ -1061,6 +1062,10 @@ def _handle_function(self, expr, func, args, *, is_method = False, use_build_fun
FunctionCall/PyccelFunction
The semantic representation of the call.
"""

if isinstance(func, FunctionDef) and 'device' in func.decorators:
if 'kernel' not in self.scope.decorators and 'device' not in self.scope.decorators:
errors.report(INVAlID_DEVICE_CALL,symbol=expr, severity='fatal')
if isinstance(func, PyccelFunctionDef):
if use_build_functions:
annotation_method = '_build_' + func.cls_name.__name__
Expand Down
31 changes: 31 additions & 0 deletions tests/cuda/test_device_semantic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# pylint: disable=missing-function-docstring, missing-module-docstring
import pytest

from pyccel import epyccel
from pyccel.decorators import device
from pyccel.errors.errors import Errors, PyccelSemanticError
from pyccel.errors.messages import (INVAlID_DEVICE_CALL,)


@pytest.mark.cuda
def test_invalid_device_call():
def invalid_device_call():
@device
def device_call():
pass
def fake_kernel_call():
device_call()

fake_kernel_call()

errors = Errors()

with pytest.raises(PyccelSemanticError):
epyccel(invalid_device_call, language="cuda")

assert errors.has_errors()

assert errors.num_messages() == 1

error_info = [*errors.error_info_map.values()][0][0]
assert INVAlID_DEVICE_CALL == error_info.message
18 changes: 18 additions & 0 deletions tests/pyccel/scripts/kernel/device_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# pylint: disable=missing-function-docstring, missing-module-docstring
from pyccel.decorators import device, kernel
from pyccel import cuda

@device
def device_call():
print("Hello from device")

@kernel
def kernel_call():
device_call()

def f():
kernel_call[1,1]()
cuda.synchronize()

if __name__ == '__main__':
f()
8 changes: 8 additions & 0 deletions tests/pyccel/test_pyccel.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,14 @@ def test_kernel_collision(gpu_available):
pyccel_test("scripts/kernel/kernel_name_collision.py",
language="cuda", execute_code=gpu_available)

#------------------------------------------------------------------------------

@pytest.mark.cuda
def test_device_call(gpu_available):
types = str
pyccel_test("scripts/kernel/device_test.py",
language="cuda", output_dtype=types, execute_code=gpu_available)

#------------------------------------------------------------------------------
def test_print_strings(language):
types = str
Expand Down

0 comments on commit 8eef19d

Please sign in to comment.