From b59ca8acf4b702bef6eca8e8512b2286a8982992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Thu, 24 Feb 2022 10:20:14 -0700 Subject: [PATCH 1/7] Improve comments and messages in test runner --- integration_tests/run_tests.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/integration_tests/run_tests.py b/integration_tests/run_tests.py index 9e6a5e563f..00912e3d38 100755 --- a/integration_tests/run_tests.py +++ b/integration_tests/run_tests.py @@ -3,6 +3,7 @@ import os import sys +# LPython and CPython tests tests = [ "exit_01.py", "expr_01.py", @@ -20,14 +21,14 @@ "test_math1.py" ] -# At present we run these tests on cpython, later we should also move to lpython +# CPython tests only test_cpython = [ "test_generics_01.py" ] def main(): - print("Compiling...") + print("Compiling LPython tests...") for pyfile in tests: basename = os.path.splitext(pyfile)[0] cmd = os.path.join("..", "src", "bin", "lpython") + " %s -o %s" % (pyfile, basename) @@ -39,7 +40,7 @@ def main(): print("Command '%s' failed." % cmd) sys.exit(1) - print("Running...") + print("Running LPython and CPython tests...") python_path="src/runtime/ltypes" for pyfile in tests: basename = os.path.splitext(pyfile)[0] @@ -57,7 +58,7 @@ def main(): print("Command '%s' failed." % cmd) sys.exit(1) - print("Running cpython tests...") + print("Running CPython tests...") for pyfile in test_cpython: cmd = "PYTHONPATH=%s python integration_tests/%s" % (python_path, pyfile) From 35c9487ee758f5127696aea1e85fb3f36b64ecfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Thu, 24 Feb 2022 10:45:30 -0700 Subject: [PATCH 2/7] Implement C interoperation in ltypes --- integration_tests/run_tests.py | 3 ++- integration_tests/test_c_interop_01.py | 9 ++++++++ src/runtime/ltypes/ltypes.py | 30 +++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 integration_tests/test_c_interop_01.py diff --git a/integration_tests/run_tests.py b/integration_tests/run_tests.py index 00912e3d38..f1828400ee 100755 --- a/integration_tests/run_tests.py +++ b/integration_tests/run_tests.py @@ -23,7 +23,8 @@ # CPython tests only test_cpython = [ - "test_generics_01.py" + "test_generics_01.py", + "test_c_interop_01.py" ] diff --git a/integration_tests/test_c_interop_01.py b/integration_tests/test_c_interop_01.py new file mode 100644 index 0000000000..167a6fbeb8 --- /dev/null +++ b/integration_tests/test_c_interop_01.py @@ -0,0 +1,9 @@ +from ltypes import ctypes, f64 +from math import pi + +@ctypes +def _lfortran_dsin(x: f64) -> f64: + pass + +assert abs(_lfortran_dsin(pi) - 0) < 1e-12 +assert abs(_lfortran_dsin(pi/2) - 1) < 1e-12 diff --git a/src/runtime/ltypes/ltypes.py b/src/runtime/ltypes/ltypes.py index 802392fbda..eab80b2cb9 100644 --- a/src/runtime/ltypes/ltypes.py +++ b/src/runtime/ltypes/ltypes.py @@ -10,7 +10,7 @@ c32 = types.new_class("c32") c64 = types.new_class("c64") -# overloading support +# Overloading support def ltype(x): """ @@ -70,3 +70,31 @@ def __call__(self, *args, **kwargs): def overload(f): overloaded_f = OverloadedFunction(f) return overloaded_f + + + +# C interoperation support + +class CTypes: + """ + A wrapper class for interfacing C via ctypes. + """ + + def __init__(self, f): + self.name = f.__name__ + self.args = f.__code__.co_varnames + self.annotations = f.__annotations__ + lib = "/Users/ondrej/repos/lpython/src/runtime/liblfortran_runtime.dylib" + import ctypes + self.library = ctypes.CDLL(lib) + self.cf = self.library[self.name] + + def __call__(self, *args, **kwargs): + if len(kwargs) > 0: + raise Exception("kwargs are not supported") + self.cf(*args) + + +def ctypes(f): + wrapped_f = CTypes(f) + return wrapped_f From 3ff2da405bccb09faa1027ab193e13b9d1a5a299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Fri, 25 Feb 2022 16:56:58 -0700 Subject: [PATCH 3/7] Rename ctypes to ccall --- integration_tests/test_c_interop_01.py | 4 ++-- src/runtime/ltypes/ltypes.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/integration_tests/test_c_interop_01.py b/integration_tests/test_c_interop_01.py index 167a6fbeb8..df678341ff 100644 --- a/integration_tests/test_c_interop_01.py +++ b/integration_tests/test_c_interop_01.py @@ -1,7 +1,7 @@ -from ltypes import ctypes, f64 +from ltypes import ccall, f64 from math import pi -@ctypes +@ccall def _lfortran_dsin(x: f64) -> f64: pass diff --git a/src/runtime/ltypes/ltypes.py b/src/runtime/ltypes/ltypes.py index eab80b2cb9..39396eea15 100644 --- a/src/runtime/ltypes/ltypes.py +++ b/src/runtime/ltypes/ltypes.py @@ -1,5 +1,6 @@ from inspect import getfullargspec, getcallargs from typing import types +import ctypes # data-types @@ -85,7 +86,6 @@ def __init__(self, f): self.args = f.__code__.co_varnames self.annotations = f.__annotations__ lib = "/Users/ondrej/repos/lpython/src/runtime/liblfortran_runtime.dylib" - import ctypes self.library = ctypes.CDLL(lib) self.cf = self.library[self.name] @@ -95,6 +95,6 @@ def __call__(self, *args, **kwargs): self.cf(*args) -def ctypes(f): +def ccall(f): wrapped_f = CTypes(f) return wrapped_f From b4aa0da1becb46bc89835eb194c3456edf6712ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Fri, 25 Feb 2022 17:01:43 -0700 Subject: [PATCH 4/7] Implement args and return types For now we hardwired them. --- src/runtime/ltypes/ltypes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/runtime/ltypes/ltypes.py b/src/runtime/ltypes/ltypes.py index 39396eea15..a176bbc3d8 100644 --- a/src/runtime/ltypes/ltypes.py +++ b/src/runtime/ltypes/ltypes.py @@ -88,11 +88,13 @@ def __init__(self, f): lib = "/Users/ondrej/repos/lpython/src/runtime/liblfortran_runtime.dylib" self.library = ctypes.CDLL(lib) self.cf = self.library[self.name] + self.cf.argtypes = [ctypes.c_double] + self.cf.restype = ctypes.c_double def __call__(self, *args, **kwargs): if len(kwargs) > 0: raise Exception("kwargs are not supported") - self.cf(*args) + return self.cf(*args) def ccall(f): From bce3e59ab37dfec76e8f0824f1504a1bc0f20225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Fri, 25 Feb 2022 17:07:33 -0700 Subject: [PATCH 5/7] Determine the signature automatically --- src/runtime/ltypes/ltypes.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/runtime/ltypes/ltypes.py b/src/runtime/ltypes/ltypes.py index a176bbc3d8..1ace31ed0d 100644 --- a/src/runtime/ltypes/ltypes.py +++ b/src/runtime/ltypes/ltypes.py @@ -82,14 +82,25 @@ class CTypes: """ def __init__(self, f): + def convert_type_to_ctype(arg): + if arg == f64: + return ctypes.c_double + else: + raise NotImplementedError("Type not implemented") self.name = f.__name__ self.args = f.__code__.co_varnames self.annotations = f.__annotations__ lib = "/Users/ondrej/repos/lpython/src/runtime/liblfortran_runtime.dylib" self.library = ctypes.CDLL(lib) self.cf = self.library[self.name] - self.cf.argtypes = [ctypes.c_double] - self.cf.restype = ctypes.c_double + argtypes = [] + for arg in self.args: + arg_type = self.annotations[arg] + arg_ctype = convert_type_to_ctype(arg_type) + argtypes.append(arg_ctype) + self.cf.argtypes = argtypes + res_type = self.annotations["return"] + self.cf.restype = convert_type_to_ctype(res_type) def __call__(self, *args, **kwargs): if len(kwargs) > 0: From bd0c4323623079d3c5f5c4151ac8d0a70626bad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Fri, 25 Feb 2022 17:08:38 -0700 Subject: [PATCH 6/7] Add a test for f32 --- integration_tests/test_c_interop_01.py | 8 +++++++- src/runtime/ltypes/ltypes.py | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/integration_tests/test_c_interop_01.py b/integration_tests/test_c_interop_01.py index df678341ff..8b022bcfb3 100644 --- a/integration_tests/test_c_interop_01.py +++ b/integration_tests/test_c_interop_01.py @@ -1,9 +1,15 @@ -from ltypes import ccall, f64 +from ltypes import ccall, f64, f32 from math import pi @ccall def _lfortran_dsin(x: f64) -> f64: pass +@ccall +def _lfortran_ssin(x: f32) -> f32: + pass + assert abs(_lfortran_dsin(pi) - 0) < 1e-12 assert abs(_lfortran_dsin(pi/2) - 1) < 1e-12 +assert abs(_lfortran_ssin(pi) - 0) < 1e-6 +assert abs(_lfortran_ssin(pi/2) - 1) < 1e-6 diff --git a/src/runtime/ltypes/ltypes.py b/src/runtime/ltypes/ltypes.py index 1ace31ed0d..6453d1d3b4 100644 --- a/src/runtime/ltypes/ltypes.py +++ b/src/runtime/ltypes/ltypes.py @@ -85,6 +85,8 @@ def __init__(self, f): def convert_type_to_ctype(arg): if arg == f64: return ctypes.c_double + elif arg == f32: + return ctypes.c_float else: raise NotImplementedError("Type not implemented") self.name = f.__name__ From b5e3a78e95fdc31521906db0a12ece821a1ab2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Fri, 25 Feb 2022 17:35:40 -0700 Subject: [PATCH 7/7] Determine the correct rtlib location --- src/runtime/ltypes/ltypes.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/runtime/ltypes/ltypes.py b/src/runtime/ltypes/ltypes.py index 6453d1d3b4..71cf38d2da 100644 --- a/src/runtime/ltypes/ltypes.py +++ b/src/runtime/ltypes/ltypes.py @@ -1,6 +1,8 @@ from inspect import getfullargspec, getcallargs from typing import types +import os import ctypes +import platform # data-types @@ -89,11 +91,25 @@ def convert_type_to_ctype(arg): return ctypes.c_float else: raise NotImplementedError("Type not implemented") + def get_rtlib_dir(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_dir, "..") + def get_crtlib_name(): + if platform.system() == "Linux": + return "liblfortran_runtime.so" + elif platform.system() == "Darwin": + return "liblfortran_runtime.dylib" + elif platform.system() == "Windows": + return "lfortran_runtime.dll" + else: + raise NotImplementedError("Platform not implemented") + def get_crtlib_path(): + return os.path.join(get_rtlib_dir(), get_crtlib_name()) self.name = f.__name__ self.args = f.__code__.co_varnames self.annotations = f.__annotations__ - lib = "/Users/ondrej/repos/lpython/src/runtime/liblfortran_runtime.dylib" - self.library = ctypes.CDLL(lib) + crtlib = get_crtlib_path() + self.library = ctypes.CDLL(crtlib) self.cf = self.library[self.name] argtypes = [] for arg in self.args: