diff --git a/contrib/floopy-highlighting/floopy.vim b/contrib/floopy-highlighting/floopy.vim deleted file mode 100644 index 57c09a652..000000000 --- a/contrib/floopy-highlighting/floopy.vim +++ /dev/null @@ -1,31 +0,0 @@ -" Vim highlighting for Floopy (Fortran+Loopy) source code -" ------------------------------------------------------- -" Installation: -" Just drop this file into ~/.vim/syntax/floopy.vim -" -" Then do -" :set filetype=floopy -" -" You may also include a line -" vim: filetype=floopy.python -" at the end of your file to set the file type automatically. -" -" Another option is to include the following in your .vimrc -" au BufRead,BufNewFile *.floopy set filetype=floopy - -runtime! syntax/fortran.vim - -unlet b:current_syntax -syntax include @LoopyPython syntax/python.vim - -if exists('s:current_syntax') - let b:current_syntax=s:current_syntax -else - unlet b:current_syntax -endif - -syntax region textSnipLoopyPython -\ matchgroup=Comment -\ start='$loopy begin' end='$loopy end' -\ containedin=ALL -\ contains=@LoopyPython diff --git a/doc/index.rst b/doc/index.rst index 3bc0361c5..b7fb02551 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -5,15 +5,15 @@ loopy is a code generator for array-based code in the OpenCL/CUDA execution model. Here's a very simple example of how to double the entries of a vector using loopy: -.. literalinclude:: ../examples/python/hello-loopy.py +.. literalinclude:: ../examples/hello-loopy.py :end-before: ENDEXAMPLE This example is included in the :mod:`loopy` distribution as -:download:`examples/python/hello-loopy.py <../examples/python/hello-loopy.py>`. +:download:`examples/hello-loopy.py <../examples/hello-loopy.py>`. When you run this script, the following kernel is generated, compiled, and executed: -.. literalinclude:: ../examples/python/hello-loopy.cl +.. literalinclude:: ../examples/hello-loopy.cl :language: c (See the full example for how to print the generated code.) diff --git a/doc/misc.rst b/doc/misc.rst index d6348ead7..8253e5c2b 100644 --- a/doc/misc.rst +++ b/doc/misc.rst @@ -187,11 +187,11 @@ source of examples. Here are some links: Here's a more complicated example of a loopy code: -.. literalinclude:: ../examples/python/find-centers.py +.. literalinclude:: ../examples/find-centers.py :language: python This example is included in the :mod:`loopy` distribution as -:download:`examples/python/find-centers.py <../examples/python/find-centers.py>`. +:download:`examples/find-centers.py <../examples/find-centers.py>`. What this does is find nearby "centers" satisfying some criteria for an array of points ("targets"). @@ -362,14 +362,14 @@ don't already have one, :func:`loopy.split_iname` will easily produce one. Lastly, both the array axis an the iname need the implementation tag ``"vec"``. Here is an example of this machinery in action: -.. literalinclude:: ../examples/python/vector-types.py +.. literalinclude:: ../examples/vector-types.py :language: python Note how the example slices off the last 'slab' of iterations to ensure that the bulk of the iteration does not require conditionals which would prevent successful vectorization. This generates the following code: -.. literalinclude:: ../examples/python/vector-types.cl +.. literalinclude:: ../examples/vector-types.cl :language: c What is the story with language versioning? diff --git a/doc/ref_call.rst b/doc/ref_call.rst index 58ed6e398..312f217a3 100644 --- a/doc/ref_call.rst +++ b/doc/ref_call.rst @@ -41,7 +41,7 @@ a :class:`~loopy.target.TargetBase`. Other foreign functions could be invoked by An example demonstrating registering a ``CBlasGemv`` as a loopy callable: -.. literalinclude:: ../examples/python/call-external.py +.. literalinclude:: ../examples/call-external.py Call Instruction for a kernel call ---------------------------------- diff --git a/doc/ref_creation.rst b/doc/ref_creation.rst index 48c315fbf..22ca39dbd 100644 --- a/doc/ref_creation.rst +++ b/doc/ref_creation.rst @@ -9,15 +9,6 @@ From Loop Domains and Instructions .. autofunction:: make_kernel -From Fortran ------------- - -.. autofunction:: parse_fortran - -.. autofunction:: parse_transformed_fortran - -.. autofunction:: c_preprocess - From Other Kernels ------------------ diff --git a/examples/python/call-external.py b/examples/call-external.py similarity index 100% rename from examples/python/call-external.py rename to examples/call-external.py diff --git a/examples/python/find-centers.py b/examples/find-centers.py similarity index 100% rename from examples/python/find-centers.py rename to examples/find-centers.py diff --git a/examples/fortran/ipython-integration-demo.ipynb b/examples/fortran/ipython-integration-demo.ipynb deleted file mode 100644 index 64fcb0af4..000000000 --- a/examples/fortran/ipython-integration-demo.ipynb +++ /dev/null @@ -1,133 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Loopy IPython Integration Demo" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext loopy.ipython_ext" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Without transform code" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%fortran_kernel\n", - "\n", - "subroutine fill(out, a, n)\n", - " implicit none\n", - "\n", - " real*8 a, out(n)\n", - " integer n, i\n", - "\n", - " do i = 1, n\n", - " out(i) = a\n", - " end do\n", - "end" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(prog) # noqa: F821" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## With transform code" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "split_amount = 128" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%transformed_fortran_kernel\n", - "\n", - "subroutine tr_fill(out, a, n)\n", - " implicit none\n", - "\n", - " real*8 a, out(n)\n", - " integer n, i\n", - "\n", - " do i = 1, n\n", - " out(i) = a\n", - " end do\n", - "end\n", - "\n", - "!$loopy begin\n", - "!\n", - "! tr_fill = lp.parse_fortran(SOURCE)\n", - "! tr_fill = lp.split_iname(tr_fill, \"i\", split_amount,\n", - "! outer_tag=\"g.0\", inner_tag=\"l.0\")\n", - "! RESULT = tr_fill\n", - "!\n", - "!$loopy end" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(prog) # noqa: F821" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.4" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/examples/fortran/matmul-driver.py b/examples/fortran/matmul-driver.py deleted file mode 100644 index 15e49800b..000000000 --- a/examples/fortran/matmul-driver.py +++ /dev/null @@ -1,39 +0,0 @@ -import numpy as np -import numpy.linalg as la - -import pyopencl as cl -import pyopencl.array as cla -from pyopencl import clrandom - -import loopy as lp - - -def main(): - import pathlib - fn = pathlib.Path(__file__).parent / "matmul.floopy" - - with open(fn) as inf: - source = inf.read() - - dgemm = lp.parse_transformed_fortran(source, filename=fn) - - ctx = cl.create_some_context() - queue = cl.CommandQueue(ctx) - - n = 2048 - a = cla.empty(queue, (n, n), dtype=np.float64, order="F") - b = cla.empty(queue, (n, n), dtype=np.float64, order="F") - c = cla.zeros(queue, (n, n), dtype=np.float64, order="F") - clrandom.fill_rand(a) - clrandom.fill_rand(b) - - dgemm = lp.set_options(dgemm, write_code=True) - - dgemm(queue, a=a, b=b, alpha=1, c=c) - - c_ref = (a.get() @ b.get()) - assert la.norm(c_ref - c.get())/la.norm(c_ref) < 1e-10 - - -if __name__ == "__main__": - main() diff --git a/examples/fortran/matmul.floopy b/examples/fortran/matmul.floopy deleted file mode 100644 index 733cdaac4..000000000 --- a/examples/fortran/matmul.floopy +++ /dev/null @@ -1,32 +0,0 @@ -subroutine dgemm(m,n,l,alpha,a,b,c) - implicit none - real*8 a(m,l),b(l,n),c(m,n), alpha - integer m,n,k,i,j,l - - do j = 1,n - do k = 1,l - do i = 1,m - c(i,j) = c(i,j) + alpha*b(k,j)*a(i,k) - end do - end do - end do -end subroutine - -!$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, -! outer_tag="g.1", inner_tag="l.0") -! dgemm = lp.split_iname(dgemm, "k", 32) -! -! dgemm = lp.extract_subst(dgemm, "a_acc", "a[i1,i2]", parameters="i1, i2") -! dgemm = lp.extract_subst(dgemm, "b_acc", "b[i1,i2]", parameters="i1, i2") -! dgemm = lp.precompute(dgemm, "a_acc", "k_inner,i_inner", -! precompute_outer_inames="i_outer, j_outer, k_outer", -! default_tag="l.auto") -! dgemm = lp.precompute(dgemm, "b_acc", "j_inner,k_inner", -! precompute_outer_inames="i_outer, j_outer, k_outer", -! default_tag="l.auto") -! RESULT = dgemm -!$loopy end diff --git a/examples/fortran/sparse.floopy b/examples/fortran/sparse.floopy deleted file mode 100644 index 2b156bdd7..000000000 --- a/examples/fortran/sparse.floopy +++ /dev/null @@ -1,33 +0,0 @@ -subroutine sparse(rowstarts, colindices, values, m, n, nvals, x, y) - implicit none - - integer rowstarts(m+1), colindices(nvals) - real*8 values(nvals) - real*8 x(n), y(n), rowsum - - integer m, n, rowstart, rowend, length, nvals - integer i, j - - do i = 1, m - rowstart = rowstarts(i) - rowend = rowstarts(i+1) - length = rowend - rowstart - - rowsum = 0 - do j = 1, length - rowsum = rowsum + & - x(colindices(rowstart+j-1))*values(rowstart+j-1) - end do - y(i) = rowsum - end do -end - -!$loopy begin -! sparse = lp.parse_fortran(SOURCE, FILENAME) -! sparse = lp.split_iname(sparse, "i", 128) -! sparse = lp.tag_inames(sparse, {"i_outer": "g.0"}) -! sparse = lp.tag_inames(sparse, {"i_inner": "l.0"}) -! sparse = lp.split_iname(sparse, "j", 4) -! sparse = lp.tag_inames(sparse, {"j_inner": "unr"}) -! RESULT = sparse -!$loopy end diff --git a/examples/fortran/tagging.floopy b/examples/fortran/tagging.floopy deleted file mode 100644 index c7ebb7566..000000000 --- a/examples/fortran/tagging.floopy +++ /dev/null @@ -1,36 +0,0 @@ -subroutine fill(out, a, n) - implicit none - - real_type a, out(n) - integer n, i - -!$loopy begin tagged: init - do i = 1, n - out(i) = a - end do -!$loopy end tagged: init - -!$loopy begin tagged: mult - do i = 1, n - out(i) = out(i) * factor - end do -!$loopy end tagged: mult -end - -!$loopy begin -! -! SOURCE = lp.c_preprocess(SOURCE, [ -! "factor 4.0", -! "real_type real*8", -! ]) -! fill = lp.parse_fortran(SOURCE, FILENAME) -! fill = lp.add_barrier(fill, "tag:init", "tag:mult", "gb1") -! 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 - -! vim:filetype=floopy diff --git a/examples/fortran/volumeKernel.floopy b/examples/fortran/volumeKernel.floopy deleted file mode 100644 index 211c38049..000000000 --- a/examples/fortran/volumeKernel.floopy +++ /dev/null @@ -1,81 +0,0 @@ -subroutine volumeKernel(elements, Nfields, Ngeo, Ndim, Dop, geo, Q, rhsQ ) - - implicit none - - integer elements, Nfields, Ngeo, Ndim - - real*4 Dop(Nq,Nq) - real*4 Q(Nq,Nq,Nq,Nfields,elements) - real*4 geo(Nq,Nq,Nq,Ngeo,elements) - real*4 rhsQ(Nq,Nq,Nq,Nfields,elements) - - integer e,i,j,k,d,n,cnt - - real*4 u,v,w,p, dFdr, dFds, dFdt, divF - real*4 F(Nq,Nq,Nq,Ndim) - - - do e=1,elements - do k=1,Nq - do j=1,Nq - do i=1,Nq - - u = Q(i,j,k,1,e) - v = Q(i,j,k,2,e) - w = Q(i,j,k,3,e) - p = Q(i,j,k,4,e) - - F(i,j,k,1) = -u - F(i,j,k,2) = -v - F(i,j,k,3) = -w - - end do - end do - end do - - do k=1,Nq - do j=1,Nq - do i=1,Nq - divF = 0 - cnt = 1 - do d=1,Ndim - dFdr = 0 - dFds = 0 - dFdt = 0 - - do n=1,Nq - dFdr = dFdr + Dop(i,n)*F(n,j,k,d) - dFds = dFds + Dop(j,n)*F(i,n,k,d) - dFdt = dFdt + Dop(k,n)*F(i,j,n,d) - end do - - divF = divF & - + geo(i,j,k,cnt,e)*dFdr & - + geo(i,j,k,cnt+1,e)*dFds & - + geo(i,j,k,cnt+2,e)*dFdt - cnt = cnt + Ndim - end do - - rhsQ(i,j,k,1,e) = divF - - end do - end do - end do - end do - -end subroutine volumeKernel - -!$loopy begin -! -! volumeKernel = lp.parse_fortran(SOURCE, FILENAME) -! volumeKernel = lp.split_iname(volumeKernel, -! "e", 32, outer_tag="g.1", inner_tag="g.0") -! volumeKernel = lp.fix_parameters(volumeKernel, -! Nq=5, Ndim=3) -! volumeKernel = lp.tag_inames(volumeKernel, dict( -! i="l.0", j="l.1", k="l.2", -! i_1="l.0", j_1="l.1", k_1="l.2" -! )) -! RESULT = volumeKernel -! -!$loopy end diff --git a/examples/python/global_barrier_removal.py b/examples/global_barrier_removal.py similarity index 100% rename from examples/python/global_barrier_removal.py rename to examples/global_barrier_removal.py diff --git a/examples/python/hello-loopy.cl b/examples/hello-loopy.cl similarity index 100% rename from examples/python/hello-loopy.cl rename to examples/hello-loopy.cl diff --git a/examples/python/hello-loopy.loopy b/examples/hello-loopy.loopy similarity index 100% rename from examples/python/hello-loopy.loopy rename to examples/hello-loopy.loopy diff --git a/examples/python/hello-loopy.py b/examples/hello-loopy.py similarity index 100% rename from examples/python/hello-loopy.py rename to examples/hello-loopy.py diff --git a/examples/python/ispc-stream-harness.py b/examples/ispc-stream-harness.py similarity index 100% rename from examples/python/ispc-stream-harness.py rename to examples/ispc-stream-harness.py diff --git a/examples/python/rank-one.py b/examples/rank-one.py similarity index 100% rename from examples/python/rank-one.py rename to examples/rank-one.py diff --git a/examples/python/run-ispc-harness.sh b/examples/run-ispc-harness.sh similarity index 100% rename from examples/python/run-ispc-harness.sh rename to examples/run-ispc-harness.sh diff --git a/examples/python/sparse.py b/examples/sparse.py similarity index 100% rename from examples/python/sparse.py rename to examples/sparse.py diff --git a/examples/python/tasksys.cpp b/examples/tasksys.cpp similarity index 100% rename from examples/python/tasksys.cpp rename to examples/tasksys.cpp diff --git a/examples/python/vector-types.cl b/examples/vector-types.cl similarity index 100% rename from examples/python/vector-types.cl rename to examples/vector-types.cl diff --git a/examples/python/vector-types.py b/examples/vector-types.py similarity index 100% rename from examples/python/vector-types.py rename to examples/vector-types.py diff --git a/loopy/__init__.py b/loopy/__init__.py index 911f239e5..4a43e4e3b 100644 --- a/loopy/__init__.py +++ b/loopy/__init__.py @@ -32,11 +32,6 @@ from loopy.codegen import PreambleInfo, generate_body, generate_code, generate_code_v2 from loopy.codegen.result import CodeGenerationResult, GeneratedProgram from loopy.diagnostic import LoopyError, LoopyWarning -from loopy.frontend.fortran import ( - c_preprocess, - parse_fortran, - parse_transformed_fortran, -) from loopy.kernel import KernelState, LoopKernel from loopy.kernel.creation import UniqueName, make_function, make_kernel from loopy.kernel.data import ( @@ -311,7 +306,6 @@ "auto", "auto_test_vs_ref", "buffer_array", - "c_preprocess", "change_arg_to_image", "chunk_iname", "clear_in_mem_caches", @@ -365,8 +359,6 @@ "memoize_on_disk", "merge", "pack_and_unpack_args_for_call", - "parse_fortran", - "parse_transformed_fortran", "precompute", "preprocess_kernel", "preprocess_program", diff --git a/loopy/cli.py b/loopy/cli.py index 7ab76c6bb..1697e5648 100644 --- a/loopy/cli.py +++ b/loopy/cli.py @@ -61,7 +61,7 @@ def main(): parser.add_argument("infile", metavar="INPUT_FILE") parser.add_argument("outfile", default="-", metavar="OUTPUT_FILE", help="Defaults to stdout ('-').", nargs="?") - parser.add_argument("--lang", metavar="LANGUAGE", help="loopy|fortran") + parser.add_argument("--lang", metavar="LANGUAGE", help="loopy") parser.add_argument("--target", choices=( "opencl", "ispc", "ispc-occa", "c", "c-fortran", "cuda"), default="opencl") @@ -104,13 +104,6 @@ def main(): lang = { ".py": "loopy", ".loopy": "loopy", - ".floopy": "fortran", - ".f90": "fortran", - ".F90": "fortran", - ".fpp": "fortran", - ".f": "fortran", - ".f77": "fortran", - ".F77": "fortran", }.get(ext) with open(args.infile) as infile_fd: infile_content = infile_fd.read() @@ -160,25 +153,6 @@ def main(): t_unit = [kernel] - elif lang in ["fortran", "floopy", "fpp"]: - pre_transform_code = None - if args.transform: - with open(args.transform) as xform_fd: - pre_transform_code = xform_fd.read() - - if args.occa_defines: - if pre_transform_code is None: - pre_transform_code = "" - - with open(args.occa_defines) as defines_fd: - pre_transform_code = ( - defines_to_python_code(defines_fd.read()) - + pre_transform_code) - - t_unit = lp.parse_transformed_fortran( - infile_content, pre_transform_code=pre_transform_code, - filename=args.infile) - else: raise RuntimeError("unknown language: '%s'" % args.lang) diff --git a/loopy/frontend/fortran/__init__.py b/loopy/frontend/fortran/__init__.py deleted file mode 100644 index d08a6f893..000000000 --- a/loopy/frontend/fortran/__init__.py +++ /dev/null @@ -1,367 +0,0 @@ -from __future__ import annotations - - -__copyright__ = "Copyright (C) 2013 Andreas Kloeckner" - -__license__ = """ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -import logging - - -logger = logging.getLogger(__name__) - -from pytools import ProcessLogger - -from loopy.diagnostic import LoopyError - - -def c_preprocess(source, defines=None, filename=None, include_paths=None): - """ - :arg source: a string, possibly containing C preprocessor constructs - :arg defines: a list of strings as they might occur after a - C-style ``#define`` directive, for example ``deg2rad(x) (x/180d0 * 3.14d0)``. - :return: a string - """ - try: - from ply import cpp, lex - except ImportError as err: - raise LoopyError( - "Using the C preprocessor requires PLY to be installed") from err - - input_dirname = None - if filename is None: - filename = "" - else: - from os.path import dirname - input_dirname = dirname(filename) - - lexer = lex.lex(cpp) - - from ply.cpp import Preprocessor - p = Preprocessor(lexer) - if input_dirname is not None: - p.add_path(input_dirname) - if include_paths: - for inc_path in include_paths: - p.add_path(inc_path) - - if defines: - for d in defines: - p.define(d) - - p.parse(source, filename) - - tokens = [] - while True: - tok = p.token() - - if not tok: - break - - if tok.type == "CPP_COMMENT": - continue - - tokens.append(tok.value) - - return "".join(tokens) - - -def _extract_loopy_lines(source): - lines = source.split("\n") - - import re - comment_re = re.compile(r"^\s*\!(.*)$") - - remaining_lines = [] - loopy_lines = [] - - in_loopy_code = False - for line in lines: - comment_match = comment_re.match(line) - - if comment_match is None: - if in_loopy_code: - raise LoopyError("non-comment source line in loopy block") - - remaining_lines.append(line) - - # Preserves line numbers in loopy code, for debuggability - loopy_lines.append("# "+line) - continue - - cmt = comment_match.group(1) - cmt_stripped = cmt.strip() - - if cmt_stripped == "$loopy begin": - if in_loopy_code: - raise LoopyError("can't enter loopy block twice") - in_loopy_code = True - - # Preserves line numbers in loopy code, for debuggability - loopy_lines.append("# "+line) - - elif cmt_stripped == "$loopy end": - if not in_loopy_code: - raise LoopyError("can't leave loopy block twice") - in_loopy_code = False - - # Preserves line numbers in loopy code, for debuggability - loopy_lines.append("# "+line) - - elif in_loopy_code: - loopy_lines.append(cmt) - - else: - remaining_lines.append(line) - - # Preserves line numbers in loopy code, for debuggability - loopy_lines.append("# "+line) - - return "\n".join(remaining_lines), "\n".join(loopy_lines) - - -def parse_transformed_fortran(source, free_form=True, strict=True, - pre_transform_code=None, transform_code_context=None, - filename=""): - """ - :arg source: a string of Fortran source code which must include - a snippet of transform code as described below. - :arg pre_transform_code: code that is run in the same context - as the transform - - *source* may contain snippets of loopy transform code between markers:: - - !$loopy begin - ! ... - !$loopy end - - Within the transform code, the following symbols are predefined: - - * ``lp``: a reference to the :mod:`loopy` package - * ``np``: a reference to the :mod:`numpy` package - * ``SOURCE``: the source code surrounding the transform block. - This may be processed using :func:`c_preprocess` and - :func:`parse_fortran`. - * ``FILENAME``: the file name of the code being processed - - The transform code must define ``RESULT``, conventionally a list of kernels - or a :class:`loopy.TranslationUnit`, which is returned from this function - unmodified. - - An example of *source* may look as follows:: - - subroutine fill(out, a, n) - implicit none - - real*8 a, out(n) - integer n, i - - do i = 1, n - out(i) = a - end do - end - - !$loopy begin - ! - ! fill, = lp.parse_fortran(SOURCE, FILENAME) - ! fill = lp.split_iname(fill, "i", split_amount, - ! outer_tag="g.0", inner_tag="l.0") - ! RESULT = [fill] - ! - !$loopy end - """ - - source, transform_code = _extract_loopy_lines(source) - if not transform_code: - raise LoopyError("no transform code found") - - from loopy.tools import remove_common_indentation - transform_code = remove_common_indentation( - transform_code, - require_leading_newline=False, - ignore_lines_starting_with="#") - - proc_dict = {} if transform_code_context is None else transform_code_context.copy() - - import numpy as np - - import loopy as lp - - proc_dict["lp"] = lp - proc_dict["np"] = np - - proc_dict["SOURCE"] = source - proc_dict["FILENAME"] = filename - - from os import getcwd - from os.path import abspath, dirname - - infile_dirname = dirname(filename) - infile_dirname = abspath(infile_dirname) if infile_dirname else getcwd() - - import sys - prev_sys_path = sys.path - try: - if infile_dirname: - sys.path = [*prev_sys_path, infile_dirname] - - if pre_transform_code is not None: - proc_dict["_MODULE_SOURCE_CODE"] = pre_transform_code - exec(compile(pre_transform_code, # noqa: S102 - "", "exec"), proc_dict) - - proc_dict["_MODULE_SOURCE_CODE"] = transform_code - exec(compile(transform_code, filename, "exec"), proc_dict) # noqa: S102 - - finally: - sys.path = prev_sys_path - - if "RESULT" not in proc_dict: - raise LoopyError("transform code did not set RESULT") - - return proc_dict["RESULT"] - - -def _add_assignees_to_calls(knl, all_kernels): - """ - Returns a copy of *knl* coming from the fortran parser adjusted to the - loopy specification that written variables of a call must appear in the - assignee. - - :param knl: An instance of :class:`loopy.LoopKernel`, which have incorrect - calls to the kernels in *all_kernels* by stuffing both the input and - output arguments into parameters. - - :param all_kernels: An instance of :class:`list` of loopy kernels which - may be called by *kernel*. - """ - new_insns = [] - subroutine_dict = {kernel.name: kernel for kernel in all_kernels} - from pymbolic.primitives import Call, Variable - - from loopy.kernel.instruction import ( - Assignment, - CallInstruction, - CInstruction, - _DataObliviousInstruction, - modify_assignee_for_array_call, - ) - - for insn in knl.instructions: - if isinstance(insn, CallInstruction): - if isinstance(insn.expression, Call) and ( - insn.expression.function.name in subroutine_dict): - assignees = [] - new_params = [] - subroutine = subroutine_dict[insn.expression.function.name] - for par, arg in zip(insn.expression.parameters, subroutine.args, - strict=True): - if arg.name in subroutine.get_written_variables(): - par = modify_assignee_for_array_call(par) - assignees.append(par) - if arg.name in subroutine.get_read_variables(): - new_params.append(par) - if arg.name not in (subroutine.get_written_variables() | - subroutine.get_read_variables()): - new_params.append(par) - - new_insns.append( - insn.copy( - assignees=tuple(assignees), - expression=Variable( - insn.expression.function.name)(*new_params))) - else: - new_insns.append(insn) - elif isinstance(insn, (Assignment, CInstruction, - _DataObliviousInstruction)): - new_insns.append(insn) - else: - raise NotImplementedError(type(insn).__name__) - - return knl.copy(instructions=new_insns) - - -def parse_fortran(source, filename="", free_form=None, strict=None, - seq_dependencies=None, auto_dependencies=None, target=None): - """ - :returns: a :class:`loopy.TranslationUnit` - """ - - parse_plog = ProcessLogger(logger, "parsing fortran file '%s'" % filename) - - if seq_dependencies is not None and auto_dependencies is not None: - raise TypeError( - "may not specify both seq_dependencies and auto_dependencies") - if auto_dependencies is not None: - from warnings import warn - warn("auto_dependencies is deprecated, use seq_dependencies instead", - DeprecationWarning, stacklevel=2) - seq_dependencies = auto_dependencies - - if seq_dependencies is None: - seq_dependencies = True - if free_form is None: - free_form = True - if strict is None: - strict = True - - import logging - console = logging.StreamHandler() - console.setLevel(logging.INFO) - formatter = logging.Formatter("%(name)-12s: %(levelname)-8s %(message)s") - console.setFormatter(formatter) - logging.getLogger("fparser").addHandler(console) - - from fparser import api - tree = api.parse(source, isfree=free_form, isstrict=strict, - analyze=False, ignore_comments=False) - - if tree is None: - raise LoopyError("Fortran parser was unhappy with source code " - "and returned invalid data (Sorry!)") - - from loopy.frontend.fortran.translator import F2LoopyTranslator - f2loopy = F2LoopyTranslator(filename, target=target) - f2loopy(tree) - - kernels = f2loopy.make_kernels(seq_dependencies=seq_dependencies) - - from loopy.transform.callable import merge - prog = merge(kernels) - all_kernels = [clbl.subkernel - for clbl in prog.callables_table.values()] - - for knl in all_kernels: - prog.with_kernel(_add_assignees_to_calls(knl, all_kernels)) - - if len(all_kernels) == 1: - # guesssing in the case of only one function - prog = prog.with_entrypoints(all_kernels[0].name) - - from loopy.frontend.fortran.translator import specialize_fortran_division - prog = specialize_fortran_division(prog) - - parse_plog.done() - - return prog - - -# vim: foldmethod=marker diff --git a/loopy/frontend/fortran/diagnostic.py b/loopy/frontend/fortran/diagnostic.py deleted file mode 100644 index 5d3df2a21..000000000 --- a/loopy/frontend/fortran/diagnostic.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import annotations - - -__copyright__ = "Copyright (C) 2009 Andreas Kloeckner" - -__license__ = """ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - - -class TranslatorWarning(UserWarning): - pass - - -class TranslationError(RuntimeError): - pass diff --git a/loopy/frontend/fortran/expression.py b/loopy/frontend/fortran/expression.py deleted file mode 100644 index bf0a52554..000000000 --- a/loopy/frontend/fortran/expression.py +++ /dev/null @@ -1,230 +0,0 @@ -from __future__ import annotations - - -__copyright__ = "Copyright (C) 2013 Andreas Kloeckner" - -__license__ = """ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -import re -from sys import intern -from typing import TYPE_CHECKING, ClassVar - -import numpy as np - -import pytools.lex -from pymbolic.parser import Parser as ExpressionParserBase - -from loopy.frontend.fortran.diagnostic import TranslationError - - -if TYPE_CHECKING: - from collections.abc import Mapping - - from pytools.lex import LexTable - - -_less_than = intern("less_than") -_greater_than = intern("greater_than") -_less_equal = intern("less_equal") -_greater_equal = intern("greater_equal") -_equal = intern("equal") -_not_equal = intern("not_equal") - -_not = intern("not") -_and = intern("and") -_or = intern("or") - - -def tuple_to_complex_literal(expr): - if len(expr) != 2: - raise TranslationError("complex literals must have " - "two entries") - - r, i = expr - - r = np.array(r)[()] - i = np.array(i)[()] - - dtype = (r.dtype.type(0) + i.dtype.type(0)) - dtype = np.complex64 if dtype == np.float32 else np.complex128 - - return dtype(float(r) + float(i)*1j) - - -# {{{ expression parser - -class FortranExpressionParser(ExpressionParserBase): - lex_table: ClassVar[LexTable] = [ - (_less_than, pytools.lex.RE(r"\.lt\.", re.IGNORECASE)), - (_greater_than, pytools.lex.RE(r"\.gt\.", re.IGNORECASE)), - (_less_equal, pytools.lex.RE(r"\.le\.", re.IGNORECASE)), - (_greater_equal, pytools.lex.RE(r"\.ge\.", re.IGNORECASE)), - (_equal, pytools.lex.RE(r"\.eq\.", re.IGNORECASE)), - (_not_equal, pytools.lex.RE(r"\.ne\.", re.IGNORECASE)), - - (_not, pytools.lex.RE(r"\.not\.", re.IGNORECASE)), - (_and, pytools.lex.RE(r"\.and\.", re.IGNORECASE)), - (_or, pytools.lex.RE(r"\.or\.", re.IGNORECASE)), - *ExpressionParserBase.lex_table, - ] - - def __init__(self, tree_walker): - self.tree_walker = tree_walker - - _PREC_FUNC_ARGS = 1 - - def parse_terminal(self, pstate): - scope = self.tree_walker.scope_stack[-1] - - from pymbolic.parser import _closepar, _float, _identifier, _openpar - from pymbolic.primitives import Call, Subscript, Variable - - next_tag = pstate.next_tag() - if next_tag is _float: - value = pstate.next_str_and_advance().lower() - dtype = np.float64 if "d" in value else np.float32 - - value = value.replace("d", "e") - if value.startswith("."): - value = "0"+value - - elif value.startswith("-."): - value = "-0"+value[1:] - - return dtype(float(value)) - - elif next_tag is _identifier: - name = pstate.next_str_and_advance() - - if pstate.is_at_end() or pstate.next_tag() is not _openpar: - # not a subscript - scope.use_name(name) - - return Variable(name) - - left_exp = Variable(name) - - pstate.advance() - pstate.expect_not_end() - - cls = Subscript if scope.is_known(name) else Call - - if pstate.next_tag is _closepar: - pstate.advance() - left_exp = cls(left_exp, ()) - else: - args = self.parse_expression(pstate, self._PREC_FUNC_ARGS) - if not isinstance(args, tuple): - args = (args,) - left_exp = cls(left_exp, args) - pstate.expect(_closepar) - pstate.advance() - - return left_exp - else: - return ExpressionParserBase.parse_terminal( - self, pstate) - - COMP_MAP: ClassVar[Mapping[str, str]] = { - _less_than: "<", - _less_equal: "<=", - _greater_than: ">", - _greater_equal: ">=", - _equal: "==", - _not_equal: "!=", - } - - def parse_prefix(self, pstate, min_precedence=0): - from pymbolic import primitives - from pymbolic.parser import _PREC_UNARY - - pstate.expect_not_end() - - if pstate.is_next(_not): - pstate.advance() - return primitives.LogicalNot( - self.parse_expression(pstate, _PREC_UNARY)) - else: - return ExpressionParserBase.parse_prefix(self, pstate) - - def parse_postfix(self, pstate, min_precedence, left_exp): - from pymbolic.parser import ( - _PREC_CALL, - _PREC_COMPARISON, - _PREC_LOGICAL_AND, - _PREC_LOGICAL_OR, - _openpar, - ) - from pymbolic.primitives import Comparison, LogicalAnd, LogicalOr - - next_tag = pstate.next_tag() - if next_tag is _openpar and min_precedence < _PREC_CALL: - raise TranslationError("parenthesis operator only works on names") - - elif next_tag in self.COMP_MAP and min_precedence < _PREC_COMPARISON: - pstate.advance() - left_exp = Comparison( - left_exp, - self.COMP_MAP[next_tag], - self.parse_expression(pstate, _PREC_COMPARISON)) - did_something = True - elif next_tag is _and and min_precedence < _PREC_LOGICAL_AND: - pstate.advance() - left_exp = LogicalAnd((left_exp, - self.parse_expression(pstate, _PREC_LOGICAL_AND))) - did_something = True - elif next_tag is _or and min_precedence < _PREC_LOGICAL_OR: - pstate.advance() - left_exp = LogicalOr((left_exp, - self.parse_expression(pstate, _PREC_LOGICAL_OR))) - did_something = True - else: - left_exp, did_something = ExpressionParserBase.parse_postfix( - self, pstate, min_precedence, left_exp) - - return left_exp, did_something - - def parse_expression(self, pstate, min_precedence=0): - left_exp = self.parse_prefix(pstate) - - did_something = True - while did_something: - did_something = False - if pstate.is_at_end(): - return left_exp - - result = self.parse_postfix( - pstate, min_precedence, left_exp) - left_exp, did_something = result - - from pymbolic.parser import FinalizedTuple - if isinstance(left_exp, FinalizedTuple): - # View all tuples that survive parsing as complex literals - # "FinalizedTuple" indicates that this tuple was enclosed - # in parens. - return tuple_to_complex_literal(left_exp) - - return left_exp - -# }}} - - -# vim: foldmethod=marker diff --git a/loopy/frontend/fortran/translator.py b/loopy/frontend/fortran/translator.py deleted file mode 100644 index d6111cfbb..000000000 --- a/loopy/frontend/fortran/translator.py +++ /dev/null @@ -1,951 +0,0 @@ -from __future__ import annotations - - -__copyright__ = "Copyright (C) 2013 Andreas Kloeckner" - -__license__ = """ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -import re -from sys import intern -from typing import ClassVar -from warnings import warn - -import numpy as np -from constantdict import constantdict - -import islpy as isl -from islpy import dim_type -from pymbolic.primitives import Slice, Wildcard - -import loopy as lp -from loopy.diagnostic import LoopyError, warn_with_kernel -from loopy.frontend.fortran.diagnostic import TranslationError, TranslatorWarning -from loopy.frontend.fortran.tree import FTreeWalkerBase -from loopy.kernel.instruction import LegacyStringInstructionTag -from loopy.symbolic import ( - IdentityMapper, - RuleAwareIdentityMapper, - SubstitutionRuleMappingContext, -) - - -# {{{ subscript base shifter - -class SubscriptIndexAdjuster(IdentityMapper): - """Adjust base indices of subscripts and lengths of slices.""" - - def __init__(self, scope): - self.scope = scope - super().__init__() - - def get_cache_key(self, expr): - return (*super().get_cache_key(expr), self.scope) - - def map_subscript(self, expr): - from pymbolic.primitives import Variable - assert isinstance(expr.aggregate, Variable) - - name = expr.aggregate.name - dims = self.scope.dim_map.get(name) - if dims is None: - return IdentityMapper.map_subscript(self, expr) - - subscript = expr.index - - if not isinstance(subscript, tuple): - subscript = (subscript,) - - if len(dims) != len(subscript): - raise TranslationError("inconsistent number of indices " - "to '%s'" % name) - - new_subscript = [] - for i in range(len(dims)): - if len(dims[i]) == 2: - # has an explicit base index - base_index, end_index = dims[i] - elif len(dims[i]) == 1: - base_index = 1 - end_index, = dims[i] - - sub_i = subscript[i] - if isinstance(sub_i, Slice): - start = sub_i.start - if start is None: - start = base_index - - step = sub_i.step - if step is None: - step = 1 - - stop = sub_i.stop - if stop is None: - stop = end_index - - if step == 1: - sub_i = Slice(( - start - base_index, - - # FIXME This is only correct for unit strides - stop - base_index + 1, - - step - )) - elif step == -1: - sub_i = Slice(( - start - base_index, - - # FIXME This is only correct for unit strides - stop - base_index - 1, - - step - )) - - else: - # FIXME - raise NotImplementedError("Fortran slice processing for " - "non-unit strides") - - else: - sub_i = sub_i - base_index - - new_subscript.append(sub_i) - - return expr.aggregate[self.rec(tuple(new_subscript))] - -# }}} - - -# {{{ scope - -class Scope: - def __init__(self, subprogram_name, arg_names=None): - if arg_names is None: - arg_names = set() - self.subprogram_name = subprogram_name - - # map first letter to type - self.implicit_types = {} - - # map name to dim tuple - self.dim_map = {} - - # map name to type - self.type_map = {} - - # map name to data - self.data_map = {} - - self.arg_names = arg_names - - self.index_sets = [] - - # This dict has a key for every iname that is - # currently active. These keys map to the loopy-side - # expression for the iname, which may differ because - # of non-zero lower iteration bounds or because of - # duplicate inames need to be renamed for loopy. - self.active_iname_aliases = {} - - self.active_loopy_inames = set() - - self.instructions = [] - self.temporary_variables = [] - - self.used_names = set() - - self.previous_instruction_id = None - - def known_names(self): - return (self.used_names - | set(self.dim_map.keys()) - | set(self.type_map.keys())) - - def is_known(self, name): - return (name in self.used_names - or name in self.dim_map - or name in self.type_map - or name in self.arg_names) - - def all_inames(self): - result = set() - for iset in self.index_sets: - result.update(iset.get_var_dict(dim_type.set)) - - return frozenset(result) - - def use_name(self, name): - self.used_names.add(name) - - def get_type(self, name, none_ok=False): - try: - return self.type_map[name] - except KeyError: - if self.implicit_types is None: - if none_ok: - return None - - raise TranslationError( - "no type for '%s' found in 'implicit none' routine" - % name) from None - - return self.implicit_types.get(name[0], np.dtype(np.int32)) - - def get_loopy_shape(self, name): - dims = self.dim_map.get(name, ()) - - shape = [] - for i, dim in enumerate(dims): - if len(dim) == 1: - if isinstance(dim[0], Wildcard): - shape.append(None) - else: - shape.append(dim[0]) - - elif len(dim) == 2: - if isinstance(dim[0], Wildcard): - shape.append(None) - else: - shape.append(dim[1]-dim[0]+1) - else: - raise TranslationError("dimension axis %d " - "of '%s' not understood: %s" - % (i, name, dim)) - - return tuple(shape) - - def process_expression_for_loopy(self, expr): - from pymbolic.mapper.substitutor import make_subst_func - - from loopy.symbolic import SubstitutionMapper - - submap = SubstitutionMapper( - make_subst_func(self.active_iname_aliases)) - - expr = submap(expr) - - subshift = SubscriptIndexAdjuster(self) - return subshift(expr) - - def written_vars(self): - return frozenset().union(*(insn.write_dependency_names() - for insn in self.instructions)) - - def read_vars(self): - return (frozenset().union(*(insn.read_dependency_names() - for insn in self.instructions)) - | frozenset().union(*(frozenset(bset.get_var_names(dim_type.param)) - for bset in self.index_sets))) - -# }}} - - -# {{{ fortran division specializers - -class FortranDivisionToFloorDiv(IdentityMapper): - def map_fortran_division(self, expr, *args): - from warnings import warn - - from loopy.diagnostic import LoopyWarning - warn( - "Integer division in Fortran do loop bound. " - "Loopy currently forces this to integers and gets it wrong for " - "negative arguments.", LoopyWarning) - from pymbolic.primitives import FloorDiv - return FloorDiv( - self.rec(expr.numerator, *args), - self.rec(expr.denominator, *args)) - - -class FortranDivisionSpecializer(RuleAwareIdentityMapper): - def __init__(self, rule_mapping_context, kernel, callables): - super().__init__(rule_mapping_context) - from loopy.type_inference import TypeReader - self.infer_type = TypeReader(kernel, callables) - self.kernel = kernel - - def map_fortran_division(self, expr, *args): - # We remove all these before type inference ever sees them. - from loopy.type_inference import TypeInferenceFailure - - try: - num_dtype = self.infer_type(expr.numerator).numpy_dtype - den_dtype = self.infer_type(expr.denominator).numpy_dtype - except TypeInferenceFailure: - return super().map_fortran_division(expr, *args) - - from pymbolic.primitives import FloorDiv, Quotient - if num_dtype.kind in "iub" and den_dtype.kind in "iub": - warn_with_kernel(self.kernel, - "fortran_int_div", - "Integer division in Fortran code. Loopy currently gets this " - "wrong for negative arguments.") - return FloorDiv( - self.rec(expr.numerator, *args), - self.rec(expr.denominator, *args)) - - else: - return Quotient( - self.rec(expr.numerator, *args), - self.rec(expr.denominator, *args)) - - -def _specialize_fortran_division_for_kernel(knl, callables): - rmc = SubstitutionRuleMappingContext( - knl.substitutions, knl.get_var_name_generator()) - return FortranDivisionSpecializer(rmc, knl, callables).map_kernel(knl) - - -def specialize_fortran_division(t_unit): - from loopy.kernel.function_interface import CallableKernel - from loopy.translation_unit import TranslationUnit, resolve_callables - from loopy.type_inference import infer_unknown_types - assert isinstance(t_unit, TranslationUnit) - - t_unit = resolve_callables(t_unit) - t_unit = infer_unknown_types(t_unit) - new_callables = {} - - for name, clbl in t_unit.callables_table.items(): - if isinstance(clbl, CallableKernel): - knl = clbl.subkernel - clbl = clbl.copy(subkernel=_specialize_fortran_division_for_kernel( - knl, t_unit.callables_table)) - - new_callables[name] = clbl - - return t_unit.copy(callables_table=constantdict(new_callables)) - -# }}} - - -# {{{ translator - -class F2LoopyTranslator(FTreeWalkerBase): - def __init__(self, filename, target=None): - FTreeWalkerBase.__init__(self, filename) - - self.target = target - - self.scope_stack = [] - - self.insn_id_counter = 0 - self.condition_id_counter = 0 - - self.kernels = [] - - self.instruction_tags = [] - self.conditions = [] - self.conditions_data = [] - - self.index_dtype = None - - self.block_nest = [] - - def add_instruction(self, insn): - scope = self.scope_stack[-1] - - scope.previous_instruction_id = insn.id - scope.instructions.append(insn) - - def add_expression_instruction(self, lhs, rhs): - scope = self.scope_stack[-1] - - new_id = self.get_insn_id() - - from loopy.kernel.data import Assignment - insn = Assignment( - lhs, rhs, - within_inames=frozenset( - scope.active_loopy_inames), - id=new_id, - predicates=frozenset(self.conditions), - tags=tuple(self.instruction_tags)) - - self.add_instruction(insn) - - def get_insn_id(self): - new_id = intern("insn%d" % self.insn_id_counter) - self.insn_id_counter += 1 - - return new_id - - # {{{ map_XXX functions - - def map_BeginSource(self, node): - scope = Scope(None) - self.scope_stack.append(scope) - - for c in node.content: - self.rec(c) - - def map_Subroutine(self, node): - assert not node.prefix - assert not hasattr(node, "suffix") - - scope = Scope(node.name, list(node.args)) - self.scope_stack.append(scope) - - self.block_nest.append("sub") - for c in node.content: - self.rec(c) - - self.scope_stack.pop() - - self.kernels.append(scope) - - def map_EndSubroutine(self, node): - if not self.block_nest: - raise TranslationError("no subroutine started at this point") - if self.block_nest.pop() != "sub": - raise TranslationError("mismatched end subroutine") - - return [] - - def map_Implicit(self, node): - scope = self.scope_stack[-1] - - if not node.items: - assert not scope.implicit_types - scope.implicit_types = None - - for stmt, specs in node.items: - if scope.implict_types is None: # spellchecker: disable-line - raise TranslationError("implicit decl not allowed after " - "'implicit none'") - tp = self.dtype_from_stmt(stmt) - for start, end in specs: - for char_code in range(ord(start), ord(end)+1): - scope.implicit_types[chr(char_code)] = tp - - return [] - - # {{{ types, declarations - - def map_Equivalence(self, node): - raise NotImplementedError("equivalence") - - TYPE_MAP: ClassVar[dict[tuple[str, str], type[np.generic]]] = { - ("real", ""): np.float32, - ("real", "4"): np.float32, - ("real", "8"): np.float64, - ("doubleprecision", ""): np.float64, - - ("complex", "8"): np.complex64, - ("complex", "16"): np.complex128, - - ("integer", ""): np.int32, - ("integer", "4"): np.int32, - ("integer", "8"): np.int64, - } - if hasattr(np, "float128"): - TYPE_MAP["real", "16"] = np.float128 # pylint:disable=no-member - if hasattr(np, "complex256"): - TYPE_MAP["complex", "32"] = np.complex256 # pylint:disable=no-member - - def dtype_from_stmt(self, stmt): - length, kind = stmt.selector - - if kind and not length: - length = kind - elif (length and not kind) or (not length and not kind): - pass - else: - raise RuntimeError("both length and kind specified") - - return np.dtype(self.TYPE_MAP[type(stmt).__name__.lower(), length]) - - def map_type_decl(self, node): - scope = self.scope_stack[-1] - - tp = self.dtype_from_stmt(node) - - for name, shape, initializer in self.parse_dimension_specs( - node, node.entity_decls): - if shape is not None: - assert name not in scope.dim_map - scope.dim_map[name] = shape - scope.use_name(name) - - assert name not in scope.type_map - scope.type_map[name] = tp - - assert name not in scope.data_map - scope.data_map[name] = initializer - - return [] - - map_Logical = map_type_decl # noqa: N815 - map_Integer = map_type_decl # noqa: N815 - map_Real = map_type_decl # noqa: N815 - map_Complex = map_type_decl # noqa: N815 - map_DoublePrecision = map_type_decl # noqa: N815 - - def map_Dimension(self, node): - scope = self.scope_stack[-1] - - for name, shape, initializer in self.parse_dimension_specs(node, node.items): - if initializer is not None: - raise LoopyError("initializer in dimension statement") - - if shape is not None: - assert name not in scope.dim_map - scope.dim_map[name] = shape - scope.use_name(name) - - return [] - - def map_External(self, node): - raise NotImplementedError("external") - - # }}} - - def map_Data(self, node): - scope = self.scope_stack[-1] - - for name, data in node.stmts: - name, = name - assert name not in scope.data - scope.data[name] = [ - scope.process_expression_for_loopy( - self.parse_expr(node, i)) for i in data] - - return [] - - def map_Parameter(self, node): - raise NotImplementedError("parameter") - - # {{{ I/O - - def map_Open(self, node): - raise NotImplementedError - - def map_Format(self, node): - warn("'format' unsupported", TranslatorWarning) - - def map_Write(self, node): - warn("'write' unsupported", TranslatorWarning) - - def map_Print(self, node): - warn("'print' unsupported", TranslatorWarning) - - def map_Read1(self, node): - warn("'read' unsupported", TranslatorWarning) - - # }}} - - def map_Assignment(self, node): - scope = self.scope_stack[-1] - - lhs = scope.process_expression_for_loopy( - self.parse_expr(node, node.variable)) - from pymbolic.primitives import Call, Subscript - if isinstance(lhs, Call): - raise TranslationError("function call (to '%s') on left hand side of" - "assignment--check for misspelled variable name" % lhs) - elif isinstance(lhs, Subscript): - lhs_name = lhs.aggregate.name - else: - lhs_name = lhs.name - - scope.use_name(lhs_name) - - rhs = scope.process_expression_for_loopy(self.parse_expr(node, node.expr)) - - self.add_expression_instruction(lhs, rhs) - - def map_Allocate(self, node): - raise NotImplementedError("allocate") - - def map_Deallocate(self, node): - raise NotImplementedError("deallocate") - - def map_Save(self, node): - raise NotImplementedError("save") - - def map_Line(self, node): - pass - - def map_Program(self, node): - raise NotImplementedError - - def map_Entry(self, node): - raise NotImplementedError("entry") - - # {{{ control flow - - def map_Goto(self, node): - raise NotImplementedError("goto") - - def map_Call(self, node): - from loopy.kernel.instruction import _get_assignee_var_name - scope = self.scope_stack[-1] - - new_id = self.get_insn_id() - - # {{{ comply with loopy's kernel call requirements - - callee, = (knl for knl in self.kernels - if knl.subprogram_name == node.designator) - call_params = [scope.process_expression_for_loopy(self.parse_expr(node, - item)) - for item in node.items] - callee_read_vars = callee.read_vars() - callee_written_vars = callee.written_vars() - - lpy_params = [] - lpy_assignees = [] - for param in call_params: - name = _get_assignee_var_name(param) - if name in callee_read_vars: - lpy_params.append(param) - if name in callee_written_vars: - lpy_assignees.append(param) - if name not in (callee_read_vars | callee_written_vars): - lpy_params.append(param) - - # }}} - - from pymbolic import var - - from loopy.kernel.data import CallInstruction - insn = CallInstruction( - tuple(lpy_assignees), - var(node.designator)(*lpy_params), - within_inames=frozenset( - scope.active_loopy_inames), - id=new_id, - predicates=frozenset(self.conditions), - tags=tuple(self.instruction_tags)) - - self.add_instruction(insn) - - def map_Return(self, node): - raise NotImplementedError("return") - - def map_ArithmeticIf(self, node): - raise NotImplementedError("arithmetic-if") - - def realize_conditional(self, node, context_cond=None): - scope = self.scope_stack[-1] - - cond_name = intern("loopy_cond%d" % self.condition_id_counter) - self.condition_id_counter += 1 - assert cond_name not in scope.type_map - - scope.type_map[cond_name] = np.int32 - - from pymbolic import var - cond_var = var(cond_name) - - self.add_expression_instruction( - cond_var, - scope.process_expression_for_loopy( - self.parse_expr(node, node.expr))) - - cond_expr = cond_var - if context_cond is not None: - from pymbolic.primitives import LogicalAnd - cond_expr = LogicalAnd((cond_var, context_cond)) - - self.conditions_data.append((context_cond, cond_var)) - else: - self.conditions_data.append((None, cond_var)) - - self.conditions.append(cond_expr) - - def map_If(self, node): - self.realize_conditional(node, None) - - for c in node.content: - self.rec(c) - - self.conditions_data.pop() - self.conditions.pop() - - def map_IfThen(self, node): - self.block_nest.append("if") - self.realize_conditional(node, None) - - for c in node.content: - self.rec(c) - - def construct_else_condition(self): - context_cond, prev_cond = self.conditions_data.pop() - if prev_cond is None: - raise RuntimeError("else if may not follow else") - - self.conditions.pop() - - from pymbolic.primitives import LogicalAnd, LogicalNot - else_expr = LogicalNot(prev_cond) - if context_cond is not None: - else_expr = LogicalAnd((else_expr, context_cond)) - - return else_expr - - def map_Else(self, node): - else_cond = self.construct_else_condition() - self.conditions.append(else_cond) - self.conditions_data.append((else_cond, None)) - - def map_ElseIf(self, node): - self.realize_conditional(node, self.construct_else_condition()) - - def map_EndIfThen(self, node): - if not self.block_nest: - raise TranslationError("no if block started at end if") - if self.block_nest.pop() != "if": - raise TranslationError("mismatched end if") - - self.conditions_data.pop() - self.conditions.pop() - - def map_Do(self, node): - scope = self.scope_stack[-1] - - if not node.loopcontrol: - raise NotImplementedError("unbounded do loop") - - loop_var, loop_bounds = node.loopcontrol.split("=") - loop_var = loop_var.strip() - - iname_dtype = scope.get_type(loop_var) - if self.index_dtype is None: - self.index_dtype = iname_dtype - else: - if self.index_dtype != iname_dtype: - raise LoopyError("type of '%s' (%s) does not agree with prior " - "index type (%s)" - % (loop_var, iname_dtype, self.index_dtype)) - - scope.use_name(loop_var) - loop_bounds = scope.process_expression_for_loopy( - self.parse_expr( - node, - loop_bounds, min_precedence=self.expr_parser._PREC_FUNC_ARGS)) - - if len(loop_bounds) == 2: - start, stop = loop_bounds - step = 1 - elif len(loop_bounds) == 3: - start, stop, step = loop_bounds - else: - raise RuntimeError("loop bounds not understood: %s" - % node.loopcontrol) - - if step != 1: - raise NotImplementedError( - "do loops with non-unit stride") - - if not isinstance(step, int): - raise TranslationError( - "non-constant steps not supported: %s" % step) - - from loopy.symbolic import get_dependencies - loop_bound_deps = ( - get_dependencies(start) - | get_dependencies(stop) - | get_dependencies(step)) - - # {{{ find a usable loopy-side loop name - - loopy_loop_var = loop_var - loop_var_suffix = None - while True: - already_used = False - for iset in scope.index_sets: - if loopy_loop_var in iset.get_var_dict(dim_type.set): - already_used = True - break - - if not already_used: - break - - if loop_var_suffix is None: - loop_var_suffix = 0 - - loop_var_suffix += 1 - loopy_loop_var = loop_var + "_%d" % loop_var_suffix - - loopy_loop_var = intern(loopy_loop_var) - - # }}} - - space = isl.Space.create_from_names(isl.DEFAULT_CONTEXT, - set=[loopy_loop_var], params=list(loop_bound_deps)) - - from loopy.isl_helpers import iname_rel_aff - from loopy.symbolic import aff_from_expr - index_set = ( - isl.BasicSet.universe(space) - .add_constraint( - isl.Constraint.inequality_from_aff( - iname_rel_aff(space, - loopy_loop_var, ">=", - aff_from_expr(space, 0)))) - .add_constraint( - isl.Constraint.inequality_from_aff( - iname_rel_aff(space, - loopy_loop_var, "<=", - aff_from_expr(space, FortranDivisionToFloorDiv()( - stop-start)))))) - - from pymbolic import var - scope.active_iname_aliases[loop_var] = \ - var(loopy_loop_var) + start - scope.active_loopy_inames.add(loopy_loop_var) - - scope.index_sets.append(index_set) - - self.block_nest.append("do") - - for c in node.content: - self.rec(c) - - del scope.active_iname_aliases[loop_var] - scope.active_loopy_inames.remove(loopy_loop_var) - - def map_EndDo(self, node): - if not self.block_nest: - raise TranslationError("no do loop started at end do") - if self.block_nest.pop() != "do": - raise TranslationError("mismatched end do") - - def map_Continue(self, node): - raise NotImplementedError("continue") - - def map_Stop(self, node): - raise NotImplementedError("stop") - - faulty_loopy_pragma = re.compile(r"\s*\$\s*loopy\s*") - - begin_tag_re = re.compile(r"\$loopy begin tagged:\s*(.*?)\s*$") - end_tag_re = re.compile(r"\$loopy end tagged:\s*(.*?)\s*$") - - def map_Comment(self, node): - stripped_comment_line = node.content.strip() - - begin_tag_match = self.begin_tag_re.match(stripped_comment_line) - end_tag_match = self.end_tag_re.match(stripped_comment_line) - faulty_loopy_pragma_match = self.faulty_loopy_pragma.match( - stripped_comment_line) - - if begin_tag_match: - tag = LegacyStringInstructionTag(begin_tag_match.group(1)) - if tag in self.instruction_tags: - raise TranslationError(f"nested begin tag for tag '{tag.value}'") - self.instruction_tags.append(tag) - - elif end_tag_match: - tag = LegacyStringInstructionTag(end_tag_match.group(1)) - if tag not in self.instruction_tags: - raise TranslationError( - f"end tag without begin tag for tag '{tag.value}'") - self.instruction_tags.remove(tag) - - elif faulty_loopy_pragma_match is not None: - from warnings import warn - warn("The comment line '%s' was not recognized as a loopy directive" - % stripped_comment_line) - - # }}} - - # }}} - - def make_kernels(self, seq_dependencies): - result = [] - - for sub in self.kernels: - # {{{ figure out arguments - - kernel_data = [] - for arg_name in sub.arg_names: - dims = sub.dim_map.get(arg_name) - - if sub.data_map.get(arg_name) is not None: - raise NotImplementedError( - "initializer for argument %s" % arg_name) - - if dims is not None: - # default order is set to "F" in kernel creation below - kernel_data.append( - lp.GlobalArg( - arg_name, - dtype=sub.get_type(arg_name), - shape=sub.get_loopy_shape(arg_name), - )) - else: - kernel_data.append( - lp.ValueArg(arg_name, - dtype=sub.get_type(arg_name))) - - # }}} - - # {{{ figure out temporary variables - - for var_name in ( - sub.known_names() - - set(sub.arg_names) - - sub.all_inames()): - dtype = sub.get_type(var_name, none_ok=True) - if sub.implicit_types is None and dtype is None: - continue - - kwargs = {} - if sub.data_map.get(var_name) is not None: - kwargs["read_only"] = True - kwargs["address_space"] = lp.AddressSpace.PRIVATE - kwargs["initializer"] = np.array( - sub.data_map[var_name], dtype=dtype) - - kernel_data.append( - lp.TemporaryVariable( - var_name, dtype=dtype, - shape=sub.get_loopy_shape(var_name), - **kwargs)) - - # }}} - - knl = lp.make_function( - sub.index_sets, - sub.instructions, - kernel_data, - name=sub.subprogram_name, - default_order="F", - index_dtype=self.index_dtype, - target=self.target, - seq_dependencies=seq_dependencies, - lang_version=(2018, 2), - ) - - from loopy.loop import merge_loop_domains - knl = merge_loop_domains(knl) - - knl = lp.fold_constants(knl) - - result.append(knl) - - return result - -# }}} - -# vim: foldmethod=marker diff --git a/loopy/frontend/fortran/tree.py b/loopy/frontend/fortran/tree.py deleted file mode 100644 index f1613b22f..000000000 --- a/loopy/frontend/fortran/tree.py +++ /dev/null @@ -1,133 +0,0 @@ -from __future__ import annotations - - -__copyright__ = "Copyright (C) 2009 Andreas Kloeckner" - -__license__ = """ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -import re - -from loopy.diagnostic import LoopyError -from loopy.symbolic import FortranDivision, IdentityMapper - - -class DivisionToFortranDivisionMapper(IdentityMapper): - def map_quotient(self, expr): - return FortranDivision( - self.rec(expr.numerator), - self.rec(expr.denominator)) - - -class FTreeWalkerBase: - def __init__(self, filename): - from loopy.frontend.fortran.expression import FortranExpressionParser - self.expr_parser = FortranExpressionParser(self) - self.filename = filename - - def rec(self, expr, *args, **kwargs): - mro = list(type(expr).__mro__) - dispatch_class = kwargs.pop("dispatch_class", type(self)) - - while mro: - method_name = "map_"+mro.pop(0).__name__ - - try: - method = getattr(dispatch_class, method_name) - except AttributeError: - pass - else: - return method(self, expr, *args, **kwargs) - - raise NotImplementedError( - "%s does not know how to map type '%s'" - % (type(self).__name__, - type(expr))) - - ENTITY_RE = re.compile( - r"^(?P[_0-9a-zA-Z]+)\s*" - r"(\((?P[-+*/0-9:a-zA-Z, \t]+)\))?" - r"(\s*=\s*(?P.+))?" - r"$") - - def parse_dimension_specs(self, node, dim_decls): - def parse_bounds(bounds_str): - start_end = bounds_str.split(":") - - assert 1 <= len(start_end) <= 2 - - return [self.parse_expr(node, s) for s in start_end] - - for decl in dim_decls: - entity_match = self.ENTITY_RE.match(decl) - assert entity_match - - groups = entity_match.groupdict() - name = groups["name"] - assert name - - if groups["shape"]: - shape = [parse_bounds(s) for s in groups["shape"].split(",")] - else: - shape = None - - init_str = groups["initializer"] - if init_str: - init_str = init_str.replace("(/", "[") - init_str = init_str.replace("/)", "]") - init_expr = self.parse_expr(node, init_str) - - from numbers import Number - if isinstance(init_expr, Number): - initializer = init_expr - elif isinstance(init_expr, list): - for i, item in enumerate(init_expr): - if not isinstance(item, Number): - raise LoopyError("unexpected type of " - "item %d in initializer: %s" - % (i+1, type(init_expr).__name__)) - initializer = init_expr - - else: - raise LoopyError("unexpected type of initializer: %s" - % type(init_expr).__name__) - - else: - initializer = None - - yield name, shape, initializer - - def __call__(self, expr, *args, **kwargs): - return self.rec(expr, *args, **kwargs) - - # {{{ expressions - - def parse_expr(self, node, expr_str, **kwargs): - try: - return DivisionToFortranDivisionMapper()( - self.expr_parser(expr_str, **kwargs)) - except Exception as e: - raise LoopyError( - "Error parsing expression '%s' on line %d of '%s': %s" - % (expr_str, node.item.span[0], self.filename, str(e))) from e - - # }}} - -# vim: foldmethod=marker diff --git a/loopy/ipython_ext.py b/loopy/ipython_ext.py deleted file mode 100644 index 92592bdba..000000000 --- a/loopy/ipython_ext.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import annotations - -from IPython.core.magic import Magics, cell_magic, magics_class - -import loopy as lp - - -@magics_class -class LoopyMagics(Magics): - @cell_magic - def fortran_kernel(self, line, cell): - result = lp.parse_fortran(cell) - self.shell.user_ns["prog"] = result - - @cell_magic - def transformed_fortran_kernel(self, line, cell): - result = lp.parse_transformed_fortran( - cell, - transform_code_context=self.shell.user_ns) - - self.shell.user_ns["prog"] = result - - -def load_ipython_extension(ip): - ip.register_magics(LoopyMagics) diff --git a/loopy/symbolic.py b/loopy/symbolic.py index 1c05b35cf..75f44e436 100644 --- a/loopy/symbolic.py +++ b/loopy/symbolic.py @@ -263,8 +263,6 @@ def map_rule_argument(self, expr: RuleArgument, /, map_linear_subscript: Callable[Concatenate[Self, LinearSubscript, P], Expression] = IdentityMapperBase.map_subscript - map_fortran_division: Callable[Concatenate[Self, FortranDivision, P], - Expression] = IdentityMapperBase.map_quotient class FlattenMapper(FlattenMapperBase, IdentityMapperMixin[[]]): @@ -368,9 +366,6 @@ def map_resolved_function(self, expr: ResolvedFunction, /, self.rec(expr.function, *args, **kwargs) - map_fortran_division: Callable[Concatenate[Self, FortranDivision, P], - None] = WalkMapperBase.map_quotient - class WalkMapper(WalkMapperMixin[P], WalkMapperBase[P]): pass @@ -411,12 +406,6 @@ def map_linear_subscript(self, expr: LinearSubscript, /, [self.rec(expr.aggregate, *args, **kwargs), self.rec(expr.index, *args, **kwargs)]) - def map_fortran_division(self, expr: FortranDivision, /, - *args: P.args, **kwargs: P.kwargs) -> ResultT: - return self.combine(( - self.rec(expr.numerator, *args, **kwargs), - self.rec(expr.denominator, *args, **kwargs))) - class SubstitutionMapper( SubstitutionMapperBase, IdentityMapperMixin[[]]): @@ -495,12 +484,6 @@ def map_sub_array_ref(self, expr: SubArrayRef, /, enclosing_prec: int) -> str: return f"[{inames}]: {subscr}" - def map_fortran_division(self, expr: FortranDivision, /, - enclosing_prec: int) -> str: - from pymbolic.mapper.stringifier import PREC_NONE - result = self.map_quotient(expr, PREC_NONE) # pyright: ignore[reportArgumentType] - return f"[FORTRANDIV]({result})" - class UnidirectionalUnifier(UnidirectionalUnifierBase): def map_reduction( @@ -595,9 +578,6 @@ def map_call_with_kwargs(self, expr: p.CallWithKwargs, /, # See https://github.com/inducer/loopy/pull/323 raise NotImplementedError - map_fortran_division: Callable[Concatenate[Self, FortranDivision, P], - Dependencies] = DependencyMapperBase.map_quotient - class SubstitutionRuleExpander(IdentityMapper[[]]): rules: Mapping[str, SubstitutionRule] @@ -1121,22 +1101,6 @@ def __post_init__(self) -> None: assert isinstance(iname, p.Variable) assert isinstance(self.subscript, p.Subscript) - -@p.expr_dataclass() -class FortranDivision(p.QuotientBase, LoopyExpressionBase): - """This exists for the benefit of the Fortran frontend, which specializes - to floating point division for floating point inputs and round-to-zero - division for integer inputs. - - Despite the name, this would also be usable for C semantics. (:mod:`loopy` - division semantics match Python's.) - - .. note:: - - This is not a documented expression node type. It may disappear - at any moment. - """ - # }}} diff --git a/loopy/type_inference.py b/loopy/type_inference.py index 90c8175e0..cc4d9df33 100644 --- a/loopy/type_inference.py +++ b/loopy/type_inference.py @@ -719,9 +719,6 @@ def map_reduction(self, expr: Reduction, return_tuple: bool = False): # pyright def map_sub_array_ref(self, expr: SubArrayRef): return self.rec(expr.subscript) - def map_fortran_division(self, expr): - return self.combine((self.rec(expr.numerator), self.rec(expr.denominator))) - @override def map_nan(self, expr: p.NaN): if expr.data_type is None: diff --git a/loopy/typing.py b/loopy/typing.py index 41c128548..128bee7e9 100644 --- a/loopy/typing.py +++ b/loopy/typing.py @@ -69,8 +69,6 @@ from loopy.types import LoopyType -# TODO: Fortran parser may insert dimensions of 'None', but I'd like to phase -# that out, so we're not encoding that in the type. # TODO: ArrayArgDescriptor: also looks for 'None' or 'auto' in the shape ShapeType: TypeAlias = tuple[ArithmeticExpression, ...] StridesType: TypeAlias = ShapeType diff --git a/pyproject.toml b/pyproject.toml index 0ed268693..f54a58d75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,10 +49,6 @@ dependencies = [ pyopencl = [ "pyopencl>=2022.3", ] -fortran = [ - "fparser>=0.2.0", - "ply>=3.6", -] [dependency-groups] dev = [ @@ -127,7 +123,6 @@ extend-ignore = [ [tool.ruff.lint.per-file-ignores] "test/test_loopy.py" = ["B023"] -"loopy/frontend/fortran/translator.py" = ["N802", "B028"] "proto-tests/*.py" = ["B"] "contrib/**/*.py" = ["I002"] "doc/conf.py" = ["I002", "S102"] @@ -172,8 +167,6 @@ ND = "ND" ue = "ue" # used in an ordering context, "ab" / "ba" ba = "ba" -# Fortran Loopy -floopy = "floopy" [tool.typos.files] extend-exclude = [ @@ -215,7 +208,6 @@ exclude = [ "proto-tests/*.py", "contrib/mem-pattern-explorer/*.py", "loopy/ipython_ext.py", - "loopy/frontend/fortran/*.py", "doc", ".conda-root", ".venv", @@ -240,7 +232,6 @@ project-excludes = [ "proto-tests/*.py", "contrib/mem-pattern-explorer/*.py", "loopy/ipython_ext.py", - "loopy/frontend/fortran/*.py", "doc", ".conda-root", ".venv", diff --git a/requirements.txt b/requirements.txt index 0751539e6..60ea7d1ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,10 +6,5 @@ git+https://github.com/inducer/pymbolic.git#egg=pymbolic git+https://github.com/inducer/genpy.git#egg=genpy git+https://github.com/inducer/codepy.git#egg=codepy -fparser - -# Optional, needed for using the C preprocessor on Fortran -ply>=3.6 - # Optional, for testing special math function scipy diff --git a/test/test_fortran.py b/test/test_fortran.py deleted file mode 100644 index a5056b924..000000000 --- a/test/test_fortran.py +++ /dev/null @@ -1,683 +0,0 @@ -__copyright__ = "Copyright (C) 2015 Andreas Kloeckner" - -__license__ = """ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -import logging - -import numpy as np -import pytest - -import pyopencl as cl -import pyopencl.clrandom -from pyopencl.tools import ( # noqa: F401 - pytest_generate_tests_for_pyopencl as pytest_generate_tests, -) - -import loopy as lp - - -pytest.importorskip("fparser") -logger = logging.getLogger(__name__) - - -def test_fp_prec_comparison(): - # FIXME: This test should succeed even when the number is exactly - # representable in single precision. - # - # https://gitlab.tiker.net/inducer/loopy/issues/187 - - fortran_src_dp = """ - subroutine assign_scalar(a) - real*8 a(1) - - a(1) = 1.1d0 - end - """ - - prg_dp = lp.parse_fortran(fortran_src_dp) - - fortran_src_sp = """ - subroutine assign_scalar(a) - real*8 a(1) - - a(1) = 1.1 - end - """ - - prg_sp = lp.parse_fortran(fortran_src_sp) - - assert prg_sp != prg_dp - - -def test_assign_double_precision_scalar(ctx_factory: cl.CtxFactory): - ctx = ctx_factory() - queue = cl.CommandQueue(ctx) - - fortran_src = """ - subroutine assign_scalar(a) - real*8 a(1) - - a(1) = 1.1d0 - end - """ - - t_unit = lp.parse_fortran(fortran_src) - print(lp.generate_code_v2(t_unit).device_code()) - assert "1.1;" in lp.generate_code_v2(t_unit).device_code() - - a_dev = cl.array.empty(queue, 1, dtype=np.float64, order="F") - t_unit(queue, a=a_dev) - - abs_err = abs(a_dev.get()[0] - 1.1) - assert abs_err < 1e-15 - - -def test_assign_double_precision_scalar_as_rational(ctx_factory: cl.CtxFactory): - ctx = ctx_factory() - queue = cl.CommandQueue(ctx) - - fortran_src = """ - subroutine assign_scalar(a) - real*8 a(1) - - a(1) = 11 - a(1) = a(1) / 10 - end - """ - - t_unit = lp.parse_fortran(fortran_src) - - a_dev = cl.array.empty(queue, 1, dtype=np.float64, order="F") - t_unit(queue, a=a_dev) - - abs_err = abs(a_dev.get()[0] - 1.1) - assert abs_err < 1e-15 - - -def test_assign_single_precision_scalar(ctx_factory: cl.CtxFactory): - ctx = ctx_factory() - queue = cl.CommandQueue(ctx) - - fortran_src = """ - subroutine assign_scalar(a) - real*8 a(1) - - a(1) = 1.1 - end - """ - - t_unit = lp.parse_fortran(fortran_src) - - import re - assert re.search(r"1.1000000[0-9]*f", lp.generate_code_v2(t_unit).device_code()) - - a_dev = cl.array.empty(queue, 1, dtype=np.float64, order="F") - t_unit(queue, a=a_dev) - - abs_err = abs(a_dev.get()[0] - 1.1) - assert abs_err > 1e-15 - assert abs_err < 1e-6 - - -def test_fill(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine fill(out, a, n) - implicit none - - real*8 a, out(n) - integer n, i - - do i = 1, n - out(i) = a - end do - end - - !$loopy begin - ! - ! 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 - """ - - knl = lp.parse_transformed_fortran(fortran_src, - pre_transform_code="split_amount = 128") - - assert "i_inner" in knl["fill"].all_inames() - - ctx = ctx_factory() - - lp.auto_test_vs_ref(knl, ctx, knl, parameters={"n": 5, "a": 5}) - - -def test_fill_const(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine fill(out, a, n) - implicit none - - real*8 a, out(n) - integer n, i - - do i = 1, n - out(i) = 3.45 - end do - end - """ - - knl = lp.parse_fortran(fortran_src) - - ctx = ctx_factory() - - lp.auto_test_vs_ref(knl, ctx, knl, parameters={"n": 5, "a": 5}) - - -def test_asterisk_in_shape(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine fill(out, out2, inp, n) - implicit none - - real*8 a, out(n), out2(n), inp(*) - integer n, i - - do i = 1, n - a = inp(n) - out(i) = 5*a - out2(i) = 6*a - end do - end - """ - - knl = lp.parse_fortran(fortran_src) - - ctx = ctx_factory() - queue = cl.CommandQueue(ctx) - - knl(queue, inp=np.array([1, 2, 3.]), n=3) - - -def test_assignment_to_subst(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine fill(out, out2, inp, n) - implicit none - - real*8 a, out(n), out2(n), inp(n) - integer n, i - - do i = 1, n - a = inp(i) - out(i) = 5*a - out2(i) = 6*a - end do - end - """ - - knl = lp.parse_fortran(fortran_src) - - ref_knl = knl - - knl = lp.assignment_to_subst(knl, "a", "i") - - ctx = ctx_factory() - lp.auto_test_vs_ref(ref_knl, ctx, knl, parameters={"n": 5}) - - -def test_assignment_to_subst_two_defs(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine fill(out, out2, inp, n) - implicit none - - real*8 a, out(n), out2(n), inp(n) - integer n, i - - do i = 1, n - a = inp(i) - out(i) = 5*a - a = 3*inp(n) - out2(i) = 6*a - end do - end - """ - - knl = lp.parse_fortran(fortran_src) - - ref_knl = knl - - knl = lp.assignment_to_subst(knl, "a") - - ctx = ctx_factory() - lp.auto_test_vs_ref(ref_knl, ctx, knl, parameters={"n": 5}) - - -def test_assignment_to_subst_indices(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine fill(out, out2, inp, n) - implicit none - - real*8 a(n), out(n), out2(n), inp(n) - integer n, i - - do i = 1, n - a(i) = 6*inp(i) - enddo - - do i = 1, n - out(i) = 5*a(i) - end do - end - """ - - knl = lp.parse_fortran(fortran_src) - - knl = lp.fix_parameters(knl, n=5) - - ref_knl = knl - - assert "a" in knl["fill"].temporary_variables - knl = lp.assignment_to_subst(knl, "a") - assert "a" not in knl["fill"].temporary_variables - - ctx = ctx_factory() - lp.auto_test_vs_ref(ref_knl, ctx, knl) - - -def test_if(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine fill(out, out2, inp, n) - implicit none - - real*8 a, b, out(n), out2(n), inp(n) - integer n, i, j - - do i = 1, n - a = inp(i) - if (a.ge.3) then - b = 2*a - do j = 1,3 - b = 3 * b - end do - out(i) = 5*b - else - out(i) = 4*a - endif - end do - end - """ - - knl = lp.parse_fortran(fortran_src) - - ref_knl = knl - - knl = lp.assignment_to_subst(knl, "a") - - ctx = ctx_factory() - lp.auto_test_vs_ref(ref_knl, ctx, knl, parameters={"n": 5}) - - -def test_tagged(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine rot_norm(out, alpha, out2, inp, inp2, n) - implicit none - real*8 a, b, r, out(n), out2(n), inp(n), inp2(n) - real*8 alpha - integer n, i - - do i = 1, n - !$loopy begin tagged: input - a = cos(alpha)*inp(i) + sin(alpha)*inp2(i) - b = -sin(alpha)*inp(i) + cos(alpha)*inp2(i) - !$loopy end tagged: input - - r = sqrt(a**2 + b**2) - a = a/r - b = b/r - - out(i) = a - out2(i) = b - end do - end - """ - - knl = lp.parse_fortran(fortran_src) - - assert sum(1 for insn in lp.find_instructions(knl, "tag:input")) == 2 - - -@pytest.mark.parametrize("buffer_inames", [ - "", - "i_inner,j_inner", - ]) -def test_matmul(ctx_factory: cl.CtxFactory, buffer_inames): - ctx = ctx_factory() - - if (buffer_inames and - ctx.devices[0].platform.name == "Portable Computing Language"): - pytest.skip("crashes on pocl") - - fortran_src = """ - subroutine dgemm(m,n,ell,a,b,c) - implicit none - real*8 a(m,ell),b(ell,n),c(m,n) - integer m,n,k,i,j,ell - - do j = 1,n - do i = 1,m - do k = 1,ell - c(i,j) = c(i,j) + b(k,j)*a(i,k) - end do - end do - end do - end subroutine - """ - - prog = lp.parse_fortran(fortran_src) - - assert len(prog["dgemm"].domains) == 1 - - ref_prog = prog - - prog = lp.split_iname(prog, "i", 16, - outer_tag="g.0", inner_tag="l.1") - prog = lp.split_iname(prog, "j", 8, - outer_tag="g.1", inner_tag="l.0") - prog = lp.split_iname(prog, "k", 32) - prog = lp.assume(prog, "n mod 32 = 0") - prog = lp.assume(prog, "m mod 32 = 0") - prog = lp.assume(prog, "ell mod 16 = 0") - - prog = lp.extract_subst(prog, "a_acc", "a[i1,i2]", parameters="i1, i2") - prog = lp.extract_subst(prog, "b_acc", "b[i1,i2]", parameters="i1, i2") - prog = lp.precompute(prog, "a_acc", "k_inner,i_inner", - precompute_outer_inames="i_outer, j_outer, k_outer", - default_tag="l.auto") - prog = lp.precompute(prog, "b_acc", "j_inner,k_inner", - precompute_outer_inames="i_outer, j_outer, k_outer", - default_tag="l.auto") - - prog = lp.buffer_array(prog, "c", buffer_inames=buffer_inames, - init_expression="0", store_expression="base+buffer") - - lp.auto_test_vs_ref(ref_prog, ctx, prog, - parameters={"n": 128, "m": 128, "ell": 128}) - - -@pytest.mark.xfail -def test_batched_sparse(): - fortran_src = """ - subroutine sparse(rowstarts, colindices, values, m, n, nvecs, nvals, x, y) - implicit none - - integer rowstarts(m+1), colindices(nvals) - real*8 values(nvals) - real*8 x(n, nvecs), y(n, nvecs), rowsum(nvecs) - - integer m, n, rowstart, rowend, length, nvals, nvecs - integer i, j, k - - do i = 1, m - rowstart = rowstarts(i) - rowend = rowstarts(i+1) - length = rowend - rowstart - - do k = 1, nvecs - rowsum(k) = 0 - enddo - do k = 1, nvecs - do j = 1, length - rowsum(k) = rowsum(k) + & - x(colindices(rowstart+j-1),k)*values(rowstart+j-1) - end do - end do - do k = 1, nvecs - y(i,k) = rowsum(k) - end do - end do - end - - """ - - knl = lp.parse_fortran(fortran_src) - - knl = lp.split_iname(knl, "i", 128) - knl = lp.tag_inames(knl, {"i_outer": "g.0"}) - knl = lp.tag_inames(knl, {"i_inner": "l.0"}) - knl = lp.add_prefetch(knl, "values", - default_tag="l.auto") - knl = lp.add_prefetch(knl, "colindices", - default_tag="l.auto") - knl = lp.fix_parameters(knl, nvecs=4) - - -def test_fuse_kernels(ctx_factory: cl.CtxFactory): - fortran_template = """ - subroutine {name}(nelements, ndofs, result, d, q) - implicit none - integer e, i, j, k - integer nelements, ndofs - real*8 result(nelements, ndofs, ndofs) - real*8 q(nelements, ndofs, ndofs) - real*8 d(ndofs, ndofs) - real*8 prev - - do e = 1,nelements - do i = 1,ndofs - do j = 1,ndofs - do k = 1,ndofs - {inner} - end do - end do - end do - end do - end subroutine - """ - - xd_line = """ - prev = result(e,i,j) - result(e,i,j) = prev + d(i,k)*q(e,i,k) - """ - yd_line = """ - prev = result(e,i,j) - result(e,i,j) = prev + d(i,k)*q(e,k,j) - """ - - xderiv = lp.parse_fortran( - fortran_template.format(inner=xd_line, name="xderiv")) - yderiv = lp.parse_fortran( - fortran_template.format(inner=yd_line, name="yderiv")) - xyderiv = lp.parse_fortran( - fortran_template.format( - inner=(xd_line + "\n" + yd_line), name="xyderiv")) - - knl = lp.fuse_kernels((xderiv["xderiv"], yderiv["yderiv"]), - data_flow=[("result", 0, 1)]) - knl = knl.with_kernel(lp.prioritize_loops(knl["xderiv_and_yderiv"], "e,i,j,k")) - - assert len(knl["xderiv_and_yderiv"].temporary_variables) == 2 - - ctx = ctx_factory() - lp.auto_test_vs_ref(xyderiv, ctx, knl, parameters={"nelements": 20, "ndofs": 4}) - - -def test_parse_and_fuse_two_kernels(): - fortran_src = """ - subroutine fill(out, a, n) - implicit none - - real*8 a, out(n) - integer n, i - - do i = 1, n - out(i) = a - end do - end - - subroutine twice(out, n) - implicit none - - real*8 out(n) - integer n, i - - do i = 1, n - out(i) = 2*out(i) - end do - end - - !$loopy begin - ! - ! t_unit = lp.parse_fortran(SOURCE) - ! fill = t_unit["fill"] - ! twice = t_unit["twice"] - ! knl = lp.fuse_kernels((fill, twice)) - ! print(knl) - ! RESULT = knl - ! - !$loopy end - """ - - lp.parse_transformed_fortran(fortran_src) - - -def test_precompute_some_exist(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine dgemm(m,n,ell,a,b,c) - implicit none - real*8 a(m,ell),b(ell,n),c(m,n) - integer m,n,k,i,j,ell - - do j = 1,n - do i = 1,m - do k = 1,ell - c(i,j) = c(i,j) + b(k,j)*a(i,k) - end do - end do - end do - end subroutine - """ - - knl = lp.parse_fortran(fortran_src) - - assert len(knl["dgemm"].domains) == 1 - - knl = lp.split_iname(knl, "i", 8, - outer_tag="g.0", inner_tag="l.1") - knl = lp.split_iname(knl, "j", 8, - outer_tag="g.1", inner_tag="l.0") - knl = lp.split_iname(knl, "k", 8) - knl = lp.assume(knl, "n mod 8 = 0") - knl = lp.assume(knl, "m mod 8 = 0") - knl = lp.assume(knl, "ell mod 8 = 0") - - knl = lp.extract_subst(knl, "a_acc", "a[i1,i2]", parameters="i1, i2") - knl = lp.extract_subst(knl, "b_acc", "b[i1,i2]", parameters="i1, i2") - knl = lp.precompute(knl, "a_acc", "k_inner,i_inner", - precompute_inames="ktemp,itemp", - precompute_outer_inames="i_outer, j_outer, k_outer", - default_tag="l.auto") - knl = lp.precompute(knl, "b_acc", "j_inner,k_inner", - precompute_inames="itemp,k2temp", - precompute_outer_inames="i_outer, j_outer, k_outer", - default_tag="l.auto") - - ref_knl = knl - - ctx = ctx_factory() - lp.auto_test_vs_ref(ref_knl, ctx, knl, parameters={"n": 128, "m": 128, "ell": 128}) - - -def test_fortran_subroutines(): - fortran_src = """ - subroutine twice(n, a) - implicit none - real*8 a(n) - integer i,n - - do i=1,n - a(i) = a(i) * 2 - end do - end subroutine - - subroutine twice_cross(n, a, i) - implicit none - integer i, n - real*8 a(n,n) - - call twice(n, a(1:n, i)) - call twice(n, a(i, 1:n)) - end subroutine - """ - t_unit = lp.parse_fortran(fortran_src).with_entrypoints("twice_cross") - print(lp.generate_code_v2(t_unit).device_code()) - - -def test_domain_fusion_imperfectly_nested(): - fortran_src = """ - subroutine imperfect(n, m, a, b) - implicit none - integer i, j, n, m - real a(n), b(n,n) - - do i=1, n - a(i) = i - do j=1, m - b(i,j) = i*j - end do - end do - end subroutine - """ - - t_unit = lp.parse_fortran(fortran_src) - # If n > 0 and m == 0, a single domain would be empty, - # leading (incorrectly) to no assignments to 'a'. - assert len(t_unit["imperfect"].domains) > 1 - - -def test_division_in_shapes(ctx_factory: cl.CtxFactory): - fortran_src = """ - subroutine halve(m, a) - implicit none - integer m, i, j - real*8 a(m/2,m/2) - do i = 1,m/2 - do j = 1,m/2 - a(i, j) = 2*a(i, j) - end do - end do - end subroutine - """ - t_unit = lp.parse_fortran(fortran_src) - ref_t_unit = t_unit - - print(t_unit) - - ctx = ctx_factory() - lp.auto_test_vs_ref(ref_t_unit, ctx, t_unit, parameters={"m": 128}) - - -if __name__ == "__main__": - import sys - if len(sys.argv) > 1: - exec(sys.argv[1]) - else: - from pytest import main - main([__file__]) - -# vim: foldmethod=marker diff --git a/test/test_numa_diff.py b/test/test_numa_diff.py deleted file mode 100644 index 21cac2525..000000000 --- a/test/test_numa_diff.py +++ /dev/null @@ -1,270 +0,0 @@ -"""gNUMA differentiation kernel, wrapped up as a test.""" - -__copyright__ = "Copyright (C) 2015 Andreas Kloeckner, Lucas Wilcox" - -__license__ = """ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -import logging - -import pytest - -import pyopencl as cl -from pyopencl.tools import ( # noqa: F401 - pytest_generate_tests_for_pyopencl as pytest_generate_tests, -) - -import loopy as lp -from loopy.version import LOOPY_USE_LANGUAGE_VERSION_2018_2 # noqa: F401 - - -logger = logging.getLogger(__name__) - - -@pytest.mark.parametrize("ilp_multiple", [1, 2]) -@pytest.mark.parametrize("Nq", [7]) -@pytest.mark.parametrize("opt_level", [11]) -def test_gnuma_horiz_kernel(ctx_factory: cl.CtxFactory, ilp_multiple, Nq, opt_level): # noqa: N803 - pytest.importorskip("fparser") - ctx = ctx_factory() - - import os - - filename = os.path.join(os.path.dirname(__file__), "strongVolumeKernels.f90") - with open(filename) as sourcef: - source = sourcef.read() - - source = source.replace("datafloat", "real*4") - - program = lp.parse_fortran(source, filename, seq_dependencies=False) - - hsv_r, hsv_s = program["strongVolumeKernelR"], program["strongVolumeKernelS"] - - hsv_r = lp.tag_instructions(hsv_r, "rknl") - hsv_s = lp.tag_instructions(hsv_s, "sknl") - hsv = lp.fuse_kernels([hsv_r, hsv_s], ["_r", "_s"]) - # hsv = hsv_s - hsv = lp.add_nosync(hsv, "any", "writes:rhsQ", "writes:rhsQ", force=True) - - from .gnuma_loopy_transforms import ( - fix_euler_parameters, - set_D_storage_format, - set_q_storage_format, - ) - - hsv = lp.fix_parameters(hsv, Nq=Nq) - hsv = lp.prioritize_loops(hsv, "e,k,j,i") - hsv = lp.tag_inames(hsv, {"e": "g.0", "j": "l.1", "i": "l.0"}) - hsv = lp.assume(hsv, "elements >= 1") - - hsv = fix_euler_parameters(hsv, p_p0=1, p_Gamma=1.4, p_R=1) - from loopy.frontend.fortran.translator import specialize_fortran_division - hsv = specialize_fortran_division(hsv) - - for name in ["Q", "rhsQ"]: - hsv = set_q_storage_format(hsv, name) - - hsv = set_D_storage_format(hsv) - # hsv = lp.add_prefetch(hsv, "volumeGeometricFactors") - - ref_hsv = hsv - - if opt_level == 0: - tap_hsv = hsv - - hsv = lp.add_prefetch(hsv, "D[:,:]", fetch_outer_inames="e", - default_tag="l.auto") - - if opt_level == 1: - tap_hsv = hsv - - # turn the first reads into subst rules - local_prep_var_names = set() - for insn in lp.find_instructions(hsv, "tag:local_prep"): - assignee, = insn.assignee_var_names() - local_prep_var_names.add(assignee) - hsv = lp.assignment_to_subst(hsv, assignee) - - # precompute fluxes - hsv = lp.assignment_to_subst(hsv, "JinvD_r") - hsv = lp.assignment_to_subst(hsv, "JinvD_s") - - r_fluxes = lp.find_instructions(hsv, "tag:compute_fluxes and tag:rknl") - s_fluxes = lp.find_instructions(hsv, "tag:compute_fluxes and tag:sknl") - - if ilp_multiple > 1: - hsv = lp.split_iname(hsv, "k", 2, inner_tag="ilp") - ilp_inames = ("k_inner",) - flux_ilp_inames = ("kk",) - else: - ilp_inames = () - flux_ilp_inames = () - - rtmps = [] - stmps = [] - - flux_store_idx = 0 - - for rflux_insn, sflux_insn in zip(r_fluxes, s_fluxes, strict=True): - for knl_tag, insn, flux_inames, tmps, flux_precomp_inames in [ - ("rknl", rflux_insn, ("j", "n",), rtmps, ("jj", "ii",)), - ("sknl", sflux_insn, ("i", "n",), stmps, ("ii", "jj",)), - ]: - flux_var, = insn.assignee_var_names() - print(insn) - - reader, = lp.find_instructions(hsv, - f"tag:{knl_tag} and reads:{flux_var}") - - hsv = lp.assignment_to_subst(hsv, flux_var) - - flux_store_name = "flux_store_%d" % flux_store_idx - flux_store_idx += 1 - tmps.append(flux_store_name) - - hsv = lp.precompute(hsv, flux_var+"_subst", flux_inames + ilp_inames, - temporary_name=flux_store_name, - precompute_inames=flux_precomp_inames + flux_ilp_inames, - default_tag=None) - if flux_var.endswith("_s"): - hsv = lp.tag_array_axes(hsv, flux_store_name, "N0,N1,N2?") - else: - hsv = lp.tag_array_axes(hsv, flux_store_name, "N1,N0,N2?") - - n_iname = "n_"+flux_var.replace("_r", "").replace("_s", "") - n_iname = n_iname.removesuffix("_0") - hsv = lp.rename_iname(hsv, "n", n_iname, within="id:"+reader.id, - existing_ok=True) - - hsv = lp.tag_inames(hsv, {"ii": "l.0", "jj": "l.1"}) - for iname in flux_ilp_inames: - hsv = lp.tag_inames(hsv, {iname: "ilp"}) - - hsv = lp.alias_temporaries(hsv, rtmps) - hsv = lp.alias_temporaries(hsv, stmps) - - if opt_level == 2: - tap_hsv = hsv - - for prep_var_name in local_prep_var_names: - if prep_var_name.startswith("Jinv") or "_s" in prep_var_name: - continue - hsv = lp.precompute(hsv, - lp.find_one_rule_matching(hsv, prep_var_name+"_*subst*"), - default_tag="l.auto") - - if opt_level == 3: - tap_hsv = hsv - - hsv = lp.add_prefetch(hsv, "Q[ii,jj,k,:,:,e]", sweep_inames=ilp_inames, - default_tag="l.auto") - - if opt_level == 4: - tap_hsv = hsv - tap_hsv = lp.tag_inames(tap_hsv, { - "Q_dim_field_inner": "unr", - "Q_dim_field_outer": "unr"}) - - hsv = lp.buffer_array(hsv, "rhsQ", ilp_inames, - fetch_bounding_box=True, default_tag="for", - init_expression="0", store_expression="base + buffer") - - if opt_level == 5: - tap_hsv = hsv - tap_hsv = lp.tag_inames(tap_hsv, { - "rhsQ_init_field_inner": "unr", "rhsQ_store_field_inner": "unr", - "rhsQ_init_field_outer": "unr", "rhsQ_store_field_outer": "unr", - "Q_dim_field_inner": "unr", - "Q_dim_field_outer": "unr"}) - - # buffer axes need to be vectorized in order for this to work - hsv = lp.tag_array_axes(hsv, "rhsQ_buf", "c?,vec,c") - hsv = lp.tag_array_axes(hsv, "Q_fetch", "c?,vec,c") - hsv = lp.tag_array_axes(hsv, "D_fetch", "f,f") - hsv = lp.tag_inames(hsv, - {"Q_dim_k": "unr", "rhsQ_init_k": "unr", "rhsQ_store_k": "unr"}, - ignore_nonexistent=True) - - if opt_level == 6: - tap_hsv = hsv - tap_hsv = lp.tag_inames(tap_hsv, { - "rhsQ_init_field_inner": "unr", "rhsQ_store_field_inner": "unr", - "rhsQ_init_field_outer": "unr", "rhsQ_store_field_outer": "unr", - "Q_dim_field_inner": "unr", - "Q_dim_field_outer": "unr"}) - - hsv = lp.tag_inames(hsv, { - "rhsQ_init_field_inner": "vec", "rhsQ_store_field_inner": "vec", - "rhsQ_init_field_outer": "unr", "rhsQ_store_field_outer": "unr", - "Q_dim_field_inner": "vec", - "Q_dim_field_outer": "unr"}) - - if opt_level == 7: - tap_hsv = hsv - - hsv = lp.collect_common_factors_on_increment(hsv, "rhsQ_buf", - vary_by_axes=(0,) if ilp_multiple > 1 else ()) - - if opt_level >= 8: - tap_hsv = hsv - - hsv = tap_hsv - - hsv = lp.preprocess_kernel(hsv) - hsv = lp.allocate_temporaries_for_base_storage(hsv) - - hsv = lp.set_options(hsv, - build_options=[ - "-cl-denorms-are-zero", - "-cl-fast-relaxed-math", - "-cl-finite-math-only", - "-cl-mad-enable", - "-cl-no-signed-zeros"]) - - if 1: - print("OPS") - op_map = lp.get_op_map(hsv, subgroup_size=32) - print(op_map) - - print("MEM") - gmem_map = lp.get_mem_access_map(hsv, subgroup_size=32).to_bytes() - print(gmem_map) - - # FIXME: renaming's a bit tricky in this program model. - # add a simple transformation for it - # hsv = hsv.copy(name="horizontalStrongVolumeKernel") - - results = lp.auto_test_vs_ref(ref_hsv, ctx, hsv, parameters={"elements": 300}, - quiet=True) - - elapsed = results["elapsed_wall"] - - print("elapsed", elapsed) - - -if __name__ == "__main__": - import sys - if len(sys.argv) > 1: - exec(sys.argv[1]) - else: - from pytest import main - main([__file__]) - -# vim: foldmethod=marker