From 9f5cde47dde063f16b70459b4f2b630440b73d51 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Sat, 22 Jul 2023 17:28:56 +0530 Subject: [PATCH 1/6] Use -funroll-loops -ffast-math optimisations --- src/runtime/lpython/lpython.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/lpython/lpython.py b/src/runtime/lpython/lpython.py index 4ec0faad4a..b1d2e78b88 100644 --- a/src/runtime/lpython/lpython.py +++ b/src/runtime/lpython/lpython.py @@ -687,9 +687,9 @@ def get_rtlib_dir(): gcc_flags = "" if platform.system() == "Linux": - gcc_flags = " -shared -fPIC " + gcc_flags = " -shared -fPIC -funroll-loops -ffast-math " elif platform.system() == "Darwin": - gcc_flags = " -bundle -flat_namespace -undefined suppress " + gcc_flags = " -bundle -flat_namespace -undefined suppress -funroll-loops -ffast-math " else: raise NotImplementedError("Platform not implemented") From 0b54b65dfe0efc7d9d9a38992f207cd1c4eafacc Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Sat, 22 Jul 2023 18:49:47 +0530 Subject: [PATCH 2/6] Make backend compulsory and allow passing custom optimisation flags according to the backend --- integration_tests/lpython_decorator_01.py | 2 +- integration_tests/lpython_decorator_02.py | 4 ++-- src/runtime/lpython/lpython.py | 23 +++++++++++++++++++---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/integration_tests/lpython_decorator_01.py b/integration_tests/lpython_decorator_01.py index eaab04b5b1..de8cff794d 100644 --- a/integration_tests/lpython_decorator_01.py +++ b/integration_tests/lpython_decorator_01.py @@ -1,7 +1,7 @@ from numpy import array from lpython import i32, f64, lpython -@lpython +@lpython("c", ["-ffast-math", "-funroll-loops", "-O3"]) def fast_sum(n: i32, x: f64[:]) -> f64: s: f64 = 0.0 i: i32 diff --git a/integration_tests/lpython_decorator_02.py b/integration_tests/lpython_decorator_02.py index e2212cabcd..b0d83e115b 100644 --- a/integration_tests/lpython_decorator_02.py +++ b/integration_tests/lpython_decorator_02.py @@ -3,14 +3,14 @@ n = TypeVar("n") -@lpython +@lpython("c", ["-ffast-math", "-funroll-loops"]) def multiply_01(n: i32, x: f64[:]) -> f64[n]: i: i32 for i in range(n): x[i] *= 5.0 return x -@lpython +@lpython("c") def multiply_02(n: i32, x: i64[:], y: i64[:]) -> i64[n]: z: i64[n]; i: i32 for i in range(n): diff --git a/src/runtime/lpython/lpython.py b/src/runtime/lpython/lpython.py index b1d2e78b88..784bbb47aa 100644 --- a/src/runtime/lpython/lpython.py +++ b/src/runtime/lpython/lpython.py @@ -647,7 +647,7 @@ def ccallable(f): def ccallback(f): return f -class lpython: +class Lpython: """ The @lpython decorator compiles a given function using LPython. @@ -657,7 +657,7 @@ class lpython: to access CPython features that are not supported by LPython. """ - def __init__(self, function): + def __init__(self, function, backend, optimisation_flags): def get_rtlib_dir(): current_dir = os.path.dirname(os.path.abspath(__file__)) return os.path.join(current_dir, "..") @@ -678,6 +678,14 @@ def get_rtlib_dir(): file.write("@pythoncallable") file.write(source_code) + if backend != "c": + raise NotImplementedError("Backend %s is not supported with @lpython yet."%(backend)) + + opt_flags = " " + if optimisation_flags is not None: + for opt_flag in optimisation_flags: + opt_flags += opt_flag + " " + # ---------------------------------------------------------------------- # Generate the shared library # TODO: Use LLVM instead of C backend @@ -687,12 +695,14 @@ def get_rtlib_dir(): gcc_flags = "" if platform.system() == "Linux": - gcc_flags = " -shared -fPIC -funroll-loops -ffast-math " + gcc_flags = " -shared -fPIC" elif platform.system() == "Darwin": - gcc_flags = " -bundle -flat_namespace -undefined suppress -funroll-loops -ffast-math " + gcc_flags = " -bundle -flat_namespace -undefined suppress" else: raise NotImplementedError("Platform not implemented") + gcc_flags += opt_flags + from numpy import get_include from distutils.sysconfig import get_python_inc, get_python_lib, \ get_python_version @@ -718,6 +728,11 @@ def __call__(self, *args, **kwargs): self.fn_name) return function(*args, **kwargs) +def lpython(backend, optimisation_flags=None): + def _lpython(function): + return Lpython(function, backend, optimisation_flags) + return _lpython + def bitnot(x, bitsize): return (~x) % (2 ** bitsize) From 4da626c2017c30c90a7d638ad0a6e4fd76c23643 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Sat, 22 Jul 2023 21:36:59 +0530 Subject: [PATCH 3/6] Allow passing optional custom optimisation flags --- integration_tests/lpython_decorator_01.py | 2 +- integration_tests/lpython_decorator_02.py | 4 +- src/runtime/lpython/lpython.py | 49 +++++++++++++---------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/integration_tests/lpython_decorator_01.py b/integration_tests/lpython_decorator_01.py index de8cff794d..ac90dc9b67 100644 --- a/integration_tests/lpython_decorator_01.py +++ b/integration_tests/lpython_decorator_01.py @@ -1,7 +1,7 @@ from numpy import array from lpython import i32, f64, lpython -@lpython("c", ["-ffast-math", "-funroll-loops", "-O3"]) +@lpython(optimisation_flags=["-ffast-math", "-funroll-loops", "-O3"]) def fast_sum(n: i32, x: f64[:]) -> f64: s: f64 = 0.0 i: i32 diff --git a/integration_tests/lpython_decorator_02.py b/integration_tests/lpython_decorator_02.py index b0d83e115b..20963025d1 100644 --- a/integration_tests/lpython_decorator_02.py +++ b/integration_tests/lpython_decorator_02.py @@ -3,14 +3,14 @@ n = TypeVar("n") -@lpython("c", ["-ffast-math", "-funroll-loops"]) +@lpython(optimisation_flags=["-ffast-math", "-funroll-loops"]) def multiply_01(n: i32, x: f64[:]) -> f64[n]: i: i32 for i in range(n): x[i] *= 5.0 return x -@lpython("c") +@lpython def multiply_02(n: i32, x: i64[:], y: i64[:]) -> i64[n]: z: i64[n]; i: i32 for i in range(n): diff --git a/src/runtime/lpython/lpython.py b/src/runtime/lpython/lpython.py index 784bbb47aa..6d5252178e 100644 --- a/src/runtime/lpython/lpython.py +++ b/src/runtime/lpython/lpython.py @@ -3,6 +3,7 @@ import ctypes import platform from dataclasses import dataclass as py_dataclass, is_dataclass as py_is_dataclass +import functools # TODO: this does not seem to restrict other imports @@ -647,30 +648,28 @@ def ccallable(f): def ccallback(f): return f -class Lpython: - """ - The @lpython decorator compiles a given function using LPython. +class LpythonJITCache: - The decorator should be used from CPython mode, i.e., when the module is - being run using CPython. When possible, it is recommended to use LPython - for the main program, and use the @cpython decorator from the LPython mode - to access CPython features that are not supported by LPython. - """ + def __init__(self): + self.pyfunc2compiledfunc = {} + + def compile(self, function, backend, optimisation_flags): + if function in self.pyfunc2compiledfunc: + return self.pyfunc2compiledfunc[function] - def __init__(self, function, backend, optimisation_flags): def get_rtlib_dir(): current_dir = os.path.dirname(os.path.abspath(__file__)) return os.path.join(current_dir, "..") - self.fn_name = function.__name__ + fn_name = function.__name__ # Get the source code of the function source_code = getsource(function) source_code = source_code[source_code.find('\n'):] - dir_name = "./lpython_decorator_" + self.fn_name + dir_name = "./lpython_decorator_" + fn_name if not os.path.exists(dir_name): os.mkdir(dir_name) - filename = dir_name + "/" + self.fn_name + filename = dir_name + "/" + fn_name # Open the file for writing with open(filename + ".py", "w") as file: @@ -716,21 +715,29 @@ def get_rtlib_dir(): # ---------------------------------------------------------------------- # Compile the C file and create a shared library + shared_library_name = "lpython_module_" + fn_name r = os.system("gcc -g" + gcc_flags + python_path + numpy_path + - filename + ".c -o lpython_module_" + self.fn_name + ".so " + + filename + ".c -o " + shared_library_name + ".so " + rt_path_01 + rt_path_02 + python_lib) assert r == 0, "Failed to create the shared library" + self.pyfunc2compiledfunc[function] = (shared_library_name, fn_name) + return self.pyfunc2compiledfunc[function] - def __call__(self, *args, **kwargs): - import sys; sys.path.append('.') - # import the symbol from the shared library - function = getattr(__import__("lpython_module_" + self.fn_name), - self.fn_name) - return function(*args, **kwargs) +lpython_jit_cache = LpythonJITCache() -def lpython(backend, optimisation_flags=None): +# Taken from https://stackoverflow.com/a/24617244 +def lpython(original_function=None, backend="c", optimisation_flags=None): def _lpython(function): - return Lpython(function, backend, optimisation_flags) + @functools.wraps(function) + def __lpython(*args, **kwargs): + import sys; sys.path.append('.') + lib_name, fn_name = lpython_jit_cache.compile( + function, backend, optimisation_flags) + return getattr(__import__(lib_name), fn_name)(*args, **kwargs) + return __lpython + + if original_function: + return _lpython(original_function) return _lpython def bitnot(x, bitsize): From 5cff2f61c8942d801b5fa3098e166b2c40a51c33 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Sat, 22 Jul 2023 23:47:28 +0530 Subject: [PATCH 4/6] Make backend compulsory if optimisations are provided --- integration_tests/lpython_decorator_01.py | 2 +- integration_tests/lpython_decorator_02.py | 2 +- src/runtime/lpython/lpython.py | 18 ++++++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/integration_tests/lpython_decorator_01.py b/integration_tests/lpython_decorator_01.py index ac90dc9b67..65b1082c86 100644 --- a/integration_tests/lpython_decorator_01.py +++ b/integration_tests/lpython_decorator_01.py @@ -1,7 +1,7 @@ from numpy import array from lpython import i32, f64, lpython -@lpython(optimisation_flags=["-ffast-math", "-funroll-loops", "-O3"]) +@lpython(backend="c", backend_optimisation_flags=["-ffast-math", "-funroll-loops", "-O3"]) def fast_sum(n: i32, x: f64[:]) -> f64: s: f64 = 0.0 i: i32 diff --git a/integration_tests/lpython_decorator_02.py b/integration_tests/lpython_decorator_02.py index 20963025d1..423243f7cd 100644 --- a/integration_tests/lpython_decorator_02.py +++ b/integration_tests/lpython_decorator_02.py @@ -3,7 +3,7 @@ n = TypeVar("n") -@lpython(optimisation_flags=["-ffast-math", "-funroll-loops"]) +@lpython(backend="c", backend_optimisation_flags=["-ffast-math", "-funroll-loops"]) def multiply_01(n: i32, x: f64[:]) -> f64[n]: i: i32 for i in range(n): diff --git a/src/runtime/lpython/lpython.py b/src/runtime/lpython/lpython.py index 6d5252178e..a92959136e 100644 --- a/src/runtime/lpython/lpython.py +++ b/src/runtime/lpython/lpython.py @@ -657,6 +657,12 @@ def compile(self, function, backend, optimisation_flags): if function in self.pyfunc2compiledfunc: return self.pyfunc2compiledfunc[function] + if optimisation_flags is not None and backend is None: + raise ValueError("backend must be specified if backend_optimisation_flags are provided.") + + if backend is None: + backend = "c" + def get_rtlib_dir(): current_dir = os.path.dirname(os.path.abspath(__file__)) return os.path.join(current_dir, "..") @@ -726,13 +732,21 @@ def get_rtlib_dir(): lpython_jit_cache = LpythonJITCache() # Taken from https://stackoverflow.com/a/24617244 -def lpython(original_function=None, backend="c", optimisation_flags=None): +def lpython(original_function=None, backend=None, backend_optimisation_flags=None): + """ + The @lpython decorator compiles a given function using LPython. + + The decorator should be used from CPython mode, i.e., when the module is + being run using CPython. When possible, it is recommended to use LPython + for the main program, and use the @cpython decorator from the LPython mode + to access CPython features that are not supported by LPython. + """ def _lpython(function): @functools.wraps(function) def __lpython(*args, **kwargs): import sys; sys.path.append('.') lib_name, fn_name = lpython_jit_cache.compile( - function, backend, optimisation_flags) + function, backend, backend_optimisation_flags) return getattr(__import__(lib_name), fn_name)(*args, **kwargs) return __lpython From ebcfc22536ea8e80ef1c1ebadc0dda1ab6a89faa Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Sun, 23 Jul 2023 00:04:43 +0530 Subject: [PATCH 5/6] Fix doc --- src/runtime/lpython/lpython.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/runtime/lpython/lpython.py b/src/runtime/lpython/lpython.py index a92959136e..e238d2f6d9 100644 --- a/src/runtime/lpython/lpython.py +++ b/src/runtime/lpython/lpython.py @@ -734,12 +734,12 @@ def get_rtlib_dir(): # Taken from https://stackoverflow.com/a/24617244 def lpython(original_function=None, backend=None, backend_optimisation_flags=None): """ - The @lpython decorator compiles a given function using LPython. + The @lpython decorator compiles a given function using LPython. - The decorator should be used from CPython mode, i.e., when the module is - being run using CPython. When possible, it is recommended to use LPython - for the main program, and use the @cpython decorator from the LPython mode - to access CPython features that are not supported by LPython. + The decorator should be used from CPython mode, i.e., when the module is + being run using CPython. When possible, it is recommended to use LPython + for the main program, and use the @cpython decorator from the LPython mode + to access CPython features that are not supported by LPython. """ def _lpython(function): @functools.wraps(function) From dce735deb275fb4eedf4ab6f3038da0d0f2c94a1 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Sun, 23 Jul 2023 00:10:08 +0530 Subject: [PATCH 6/6] Run failed cases verbosely --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 74e20ea76a..0b16387e9a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -349,7 +349,7 @@ jobs: - name: Test Linux shell: bash -e -l {0} run: | - ctest + ctest --rerun-failed --output-on-failure ./run_tests.py -s cd integration_tests ./run_tests.py -b llvm c