From d8be1eaedc7561ea4e84edebad6d7c74878246a4 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 10 Jun 2015 20:57:44 -0500 Subject: [PATCH] Revamp how Fortran source processing is done, expose passes in Python transforms --- bin/loopy | 29 ++- doc/reference.rst | 6 + examples/fortran/foo.floopy | 15 +- examples/fortran/matmul.floopy | 6 +- examples/fortran/outerprod.py | 8 - examples/fortran/sparse.floopy | 6 +- examples/fortran/tagging.floopy | 7 +- examples/fortran/volumeKernel.floopy | 6 +- examples/fortran/volumeKernelSimple.floopy | 6 +- examples/python/hello-loopy-lp.py | 11 ++ loopy/__init__.py | 3 + loopy/frontend/fortran/__init__.py | 211 +++++++++++++++------ loopy/frontend/fortran/translator.py | 51 +---- test/test_fortran.py | 48 ++--- 14 files changed, 240 insertions(+), 173 deletions(-) delete mode 100644 examples/fortran/outerprod.py create mode 100644 examples/python/hello-loopy-lp.py diff --git a/bin/loopy b/bin/loopy index 0f7b9a607..e607a6793 100644 --- a/bin/loopy +++ b/bin/loopy @@ -71,23 +71,23 @@ def main(): with open(args.infile, "r") as infile_fd: infile_content = infile_fd.read() - # {{{ path wrangling + if args.lang == "loopy": + # {{{ path wrangling - from os.path import dirname, abspath - from os import getcwd + from os.path import dirname, abspath + from os import getcwd - infile_dirname = dirname(args.infile) - if infile_dirname: - infile_dirname = abspath(infile_dirname) - else: - infile_dirname = getcwd() + infile_dirname = dirname(args.infile) + if infile_dirname: + infile_dirname = abspath(infile_dirname) + else: + infile_dirname = getcwd() - import sys - sys.path.append(infile_dirname) + import sys + sys.path.append(infile_dirname) - # }}} + # }}} - if args.lang == "loopy": data_dic = {} data_dic["lp"] = lp data_dic["np"] = np @@ -131,9 +131,8 @@ def main(): defines_to_python_code(defines_fd.read()) + pre_transform_code) - from loopy.frontend.fortran import f2loopy - kernels = f2loopy(infile_content, pre_transform_code=pre_transform_code, - use_c_preprocessor=(args.lang == "fpp")) + kernels = lp.parse_transformed_fortran( + infile_content, pre_transform_code=pre_transform_code) if args.name is not None: kernels = [kernel for kernel in kernels diff --git a/doc/reference.rst b/doc/reference.rst index af39de655..6ed83d430 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -334,10 +334,16 @@ function, which is responsible for creating kernels: .. autofunction:: make_kernel +.. autofunction:: parse_fortran + +.. autofunction:: parse_transformed_fortran + .. autofunction:: make_copy_kernel .. autofunction:: fuse_kernels +.. autofunction:: c_preprocess + Transforming Kernels -------------------- diff --git a/examples/fortran/foo.floopy b/examples/fortran/foo.floopy index 9b0575607..6b8741e11 100644 --- a/examples/fortran/foo.floopy +++ b/examples/fortran/foo.floopy @@ -1,8 +1,3 @@ -!$loopy begin define -! define("factor 4.0") -! define("real_type real*8") -!$loopy end define - subroutine fill(out, a, n) implicit none @@ -17,13 +12,19 @@ subroutine fill(out, a, n) end do end -!$loopy begin transform +!$loopy begin ! +! SOURCE = lp.c_preprocess(SOURCE, [ +! "factor 4.0", +! "real_type real*8", +! ]) +! fill, = lp.parse_fortran(SOURCE, FILENAME) ! fill = lp.split_iname(fill, "i", 128, ! outer_tag="g.0", inner_tag="l.0") ! fill = lp.split_iname(fill, "i_1", 128, ! outer_tag="g.0", inner_tag="l.0") +! RESULT = [fill] ! -!$loopy end transform +!$loopy end ! vim:filetype=floopy diff --git a/examples/fortran/matmul.floopy b/examples/fortran/matmul.floopy index ea85b0c6b..3352449d7 100644 --- a/examples/fortran/matmul.floopy +++ b/examples/fortran/matmul.floopy @@ -12,7 +12,8 @@ subroutine dgemm(m,n,l,alpha,a,b,c) end do end subroutine -!$loopy begin transform +!$loopy begin +! dgemm, = lp.parse_fortran(SOURCE, FILENAME) ! dgemm = lp.split_iname(dgemm, "i", 16, ! outer_tag="g.0", inner_tag="l.1") ! dgemm = lp.split_iname(dgemm, "j", 8, @@ -23,4 +24,5 @@ end subroutine ! dgemm = lp.extract_subst(dgemm, "b_acc", "b[i1,i2]", parameters="i1, i2") ! dgemm = lp.precompute(dgemm, "a_acc", "k_inner,i_inner") ! dgemm = lp.precompute(dgemm, "b_acc", "j_inner,k_inner") -!$loopy end transform +! RESULT = [dgemm] +!$loopy end diff --git a/examples/fortran/outerprod.py b/examples/fortran/outerprod.py deleted file mode 100644 index 4122c8437..000000000 --- a/examples/fortran/outerprod.py +++ /dev/null @@ -1,8 +0,0 @@ -lp_knl = lp.make_kernel( - "{[i,j]: 0<=i,j", "exec"), def_dict) + proc_dict["SOURCE"] = source + proc_dict["FILENAME"] = filename - def_dict["_MODULE_SOURCE_CODE"] = define_code - exec(compile(define_code, "", "exec"), def_dict) + from os.path import dirname, abspath + from os import getcwd - p.parse(source, file_name) + infile_dirname = dirname(filename) + if infile_dirname: + infile_dirname = abspath(infile_dirname) + else: + infile_dirname = getcwd() - tokens = [] - while True: - tok = p.token() + import sys + prev_sys_path = sys.path + try: + if infile_dirname: + sys.path = prev_sys_path + [infile_dirname] - if not tok: - break + if pre_transform_code is not None: + proc_dict["_MODULE_SOURCE_CODE"] = pre_transform_code + exec(compile(pre_transform_code, + "", "exec"), proc_dict) - if tok.type == "CPP_COMMENT": - continue + proc_dict["_MODULE_SOURCE_CODE"] = transform_code + exec(compile(transform_code, filename, "exec"), proc_dict) - tokens.append(tok.value) + finally: + sys.path = prev_sys_path - source = "".join(tokens) + if "RESULT" not in proc_dict: + raise LoopyError("transform code did not set RESULT") + return proc_dict["RESULT"] + + +def parse_fortran(source, filename="", free_form=True, strict=True): + """ + :returns: a list of :class:`loopy.LoopKernel` objects + """ from fparser import api tree = api.parse(source, isfree=free_form, isstrict=strict, analyze=False, ignore_comments=False) from loopy.frontend.fortran.translator import F2LoopyTranslator - f2loopy = F2LoopyTranslator(file_name) + f2loopy = F2LoopyTranslator(filename) f2loopy(tree) - return f2loopy.make_kernels(pre_transform_code=pre_transform_code, - transform_code_context=transform_code_context) + return f2loopy.make_kernels() + # vim: foldmethod=marker diff --git a/loopy/frontend/fortran/translator.py b/loopy/frontend/fortran/translator.py index eba76338d..9d6e3ea95 100644 --- a/loopy/frontend/fortran/translator.py +++ b/loopy/frontend/fortran/translator.py @@ -208,15 +208,9 @@ class F2LoopyTranslator(FTreeWalkerBase): self.kernels = [] - # Flag to record whether 'loopy begin transform' comment - # has been seen. - self.in_transform_code = False - self.instruction_tags = [] self.conditions = [] - self.transform_code_lines = [] - self.filename = filename self.index_dtype = None @@ -606,17 +600,7 @@ class F2LoopyTranslator(FTreeWalkerBase): faulty_loopy_pragma_match = self.faulty_loopy_pragma.match( stripped_comment_line) - if stripped_comment_line == "$loopy begin transform": - if self.in_transform_code: - raise TranslationError("can't enter transform code twice") - self.in_transform_code = True - - elif stripped_comment_line == "$loopy end transform": - if not self.in_transform_code: - raise TranslationError("can't leave transform code twice") - self.in_transform_code = False - - elif begin_tag_match: + if begin_tag_match: tag = begin_tag_match.group(1) if tag in self.instruction_tags: raise TranslationError("nested begin tag for tag '%s'" % tag) @@ -629,9 +613,6 @@ class F2LoopyTranslator(FTreeWalkerBase): "end tag without begin tag for tag '%s'" % tag) self.instruction_tags.remove(tag) - elif self.in_transform_code: - self.transform_code_lines.append(node.content) - elif faulty_loopy_pragma_match is not None: from warnings import warn warn("The comment line '%s' was not recognized as a loopy directive" @@ -641,18 +622,12 @@ class F2LoopyTranslator(FTreeWalkerBase): # }}} - def make_kernels(self, pre_transform_code=None, transform_code_context=None): + def make_kernels(self): kernel_names = [ sub.subprogram_name for sub in self.kernels] - if transform_code_context is None: - proc_dict = {} - else: - proc_dict = transform_code_context.copy() - - proc_dict["lp"] = lp - proc_dict["np"] = np + result = [] for sub in self.kernels: # {{{ figure out arguments @@ -704,25 +679,11 @@ class F2LoopyTranslator(FTreeWalkerBase): from loopy.loop import fuse_loop_domains knl = fuse_loop_domains(knl) + knl = lp.fold_constants(knl) - proc_dict[sub.subprogram_name] = lp.fold_constants(knl) - - from loopy.tools import remove_common_indentation - transform_code = remove_common_indentation( - "\n".join(self.transform_code_lines), - require_leading_newline=False) - - if pre_transform_code is not None: - proc_dict["_MODULE_SOURCE_CODE"] = pre_transform_code - exec(compile(pre_transform_code, - "", "exec"), proc_dict) - - proc_dict["_MODULE_SOURCE_CODE"] = transform_code - exec(compile(transform_code, - "", "exec"), proc_dict) + result.append(knl) - return [proc_dict[knl_name] - for knl_name in kernel_names] + return result # }}} diff --git a/test/test_fortran.py b/test/test_fortran.py index ce27424d8..124035e52 100644 --- a/test/test_fortran.py +++ b/test/test_fortran.py @@ -58,16 +58,20 @@ def test_fill(ctx_factory): end do end - !$loopy begin transform + !$loopy begin ! - ! fill = lp.split_iname(fill, "i", 128, + ! fill, = lp.parse_fortran(SOURCE) + ! fill = lp.split_iname(fill, "i", split_amount, ! outer_tag="g.0", inner_tag="l.0") + ! RESULT = [fill] ! - !$loopy end transform + !$loopy end """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_transformed_fortran(fortran_src, + pre_transform_code="split_amount = 128") + + assert "i_inner" in knl.all_inames() ctx = ctx_factory() @@ -88,8 +92,7 @@ def test_fill_const(ctx_factory): end """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_fortran(fortran_src) ctx = ctx_factory() @@ -112,8 +115,7 @@ def test_asterisk_in_shape(ctx_factory): end """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_fortran(fortran_src) ctx = ctx_factory() queue = cl.CommandQueue(ctx) @@ -137,8 +139,7 @@ def test_temporary_to_subst(ctx_factory): end """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_fortran(fortran_src) ref_knl = knl @@ -165,8 +166,7 @@ def test_temporary_to_subst_two_defs(ctx_factory): end """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_fortran(fortran_src) ref_knl = knl @@ -194,8 +194,7 @@ def test_temporary_to_subst_indices(ctx_factory): end """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_fortran(fortran_src) knl = lp.fix_parameters(knl, n=5) @@ -232,8 +231,7 @@ def test_if(ctx_factory): end """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_fortran(fortran_src) ref_knl = knl @@ -267,8 +265,7 @@ def test_tagged(ctx_factory): end """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_fortran(fortran_src) assert sum(1 for insn in lp.find_instructions(knl, "*$input")) == 2 @@ -294,8 +291,7 @@ def test_matmul(ctx_factory, buffer_inames): end subroutine """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_fortran(fortran_src) assert len(knl.domains) == 1 @@ -357,8 +353,7 @@ def test_batched_sparse(): """ - from loopy.frontend.fortran import f2loopy - knl, = f2loopy(fortran_src) + knl, = lp.parse_fortran(fortran_src) knl = lp.split_iname(knl, "i", 128) knl = lp.tag_inames(knl, {"i_outer": "g.0"}) @@ -393,12 +388,11 @@ def test_fuse_kernels(ctx_factory): xd_line = "result(e,i,j) = result(e,i,j) + d(i,k)*q(e,i,k)" yd_line = "result(e,i,j) = result(e,i,j) + d(i,k)*q(e,k,j)" - from loopy.frontend.fortran import f2loopy - xderiv, = f2loopy( + xderiv, = lp.parse_fortran( fortran_template.format(line=xd_line, name="xderiv")) - yderiv, = f2loopy( + yderiv, = lp.parse_fortran( fortran_template.format(line=yd_line, name="yderiv")) - xyderiv, = f2loopy( + xyderiv, = lp.parse_fortran( fortran_template.format( line=(xd_line + "\n" + yd_line), name="xyderiv")) -- GitLab