From 988ea750e3ce039db5f8960b592804d0e710e618 Mon Sep 17 00:00:00 2001 From: "William S. Moses" Date: Wed, 23 Jun 2021 13:12:00 -0400 Subject: [PATCH] Initial non-fork commit --- .clang-format | 1 + .clang-tidy | 19 + .git-blame-ignore-revs | 42 + .github/lockdown.yml | 8 + .github/workflows/README.md | 1 + .github/workflows/build.yml | 50 + .github/workflows/clang-format.yml | 18 + .gitignore | 70 + .gitmodules | 3 + AddClang.cmake | 42 + CMakeLists.txt | 53 + README.md | 106 + include/CMakeLists.txt | 1 + include/polygeist/CMakeLists.txt | 5 + include/polygeist/Dialect.h | 17 + include/polygeist/Dialect.td | 32 + include/polygeist/Ops.h | 21 + include/polygeist/Passes/CMakeLists.txt | 5 + include/polygeist/Passes/Passes.h | 37 + include/polygeist/Passes/Passes.td | 49 + include/polygeist/PolygeistOps.td | 42 + lib/CMakeLists.txt | 1 + lib/polygeist/CMakeLists.txt | 14 + lib/polygeist/Dialect.cpp | 25 + lib/polygeist/Ops.cpp | 221 ++ lib/polygeist/Passes/AffineCFG.cpp | 526 +++ lib/polygeist/Passes/AffineReduction.cpp | 262 ++ lib/polygeist/Passes/CMakeLists.txt | 28 + lib/polygeist/Passes/CanonicalizeFor.cpp | 133 + lib/polygeist/Passes/LoopRestructure.cpp | 642 ++++ lib/polygeist/Passes/Mem2Reg.cpp | 1169 +++++++ .../Passes/ParallelLoopDistribute.cpp | 950 ++++++ lib/polygeist/Passes/ParallelLower.cpp | 305 ++ lib/polygeist/Passes/RaiseToAffine.cpp | 153 + llvm-project | 1 + mlir-clang/.clang-format | 1 + mlir-clang/.gitignore | 3 + mlir-clang/CMakeLists.txt | 44 + mlir-clang/Lib/clang-mlir.cc | 3030 +++++++++++++++++ mlir-clang/Lib/clang-mlir.h | 494 +++ mlir-clang/README.md | 34 + mlir-clang/Test/CMakeLists.txt | 31 + mlir-clang/Test/Verification/affun.c | 23 + mlir-clang/Test/Verification/call.c | 25 + mlir-clang/Test/Verification/cond.c | 28 + mlir-clang/Test/Verification/cond2.c | 26 + mlir-clang/Test/Verification/der.c | 13 + mlir-clang/Test/Verification/deref.c | 19 + mlir-clang/Test/Verification/fscanf.c | 50 + mlir-clang/Test/Verification/fw.c | 46 + mlir-clang/Test/Verification/fwfree.c | 47 + mlir-clang/Test/Verification/ker.c | 15 + mlir-clang/Test/Verification/ler.c | 15 + mlir-clang/Test/Verification/lum.c | 10 + mlir-clang/Test/Verification/malloc.c | 23 + mlir-clang/Test/Verification/mer.c | 15 + mlir-clang/Test/Verification/nestloop.c | 75 + mlir-clang/Test/Verification/nus.c | 31 + mlir-clang/Test/Verification/palloc.c | 30 + mlir-clang/Test/Verification/raiseToAffine.c | 51 + .../Verification/raiseToAffineUnsignedCmp.c | 24 + mlir-clang/Test/Verification/red.mlir | 28 + mlir-clang/Test/Verification/redstore.c | 50 + mlir-clang/Test/Verification/redstore2.c | 41 + mlir-clang/Test/Verification/reduction.c | 46 + mlir-clang/Test/Verification/setter.c | 23 + mlir-clang/Test/Verification/threeInt.c | 14 + mlir-clang/Test/Verification/whileset.c | 40 + mlir-clang/Test/Verification/whiletofor.c | 43 + mlir-clang/Test/Verification/x.c | 17 + mlir-clang/Test/aff.c | 24 + mlir-clang/Test/global.c | 26 + mlir-clang/Test/lit.cfg | 64 + mlir-clang/Test/lit.site.cfg.in | 25 + mlir-clang/Test/polybench/AUTHORS | 17 + mlir-clang/Test/polybench/CHANGELOG | 147 + mlir-clang/Test/polybench/LICENSE.txt | 51 + mlir-clang/Test/polybench/README | 363 ++ mlir-clang/Test/polybench/THANKS | 20 + .../datamining/correlation/correlation.c | 413 +++ .../datamining/correlation/correlation.h | 80 + .../datamining/covariance/covariance.c | 204 ++ .../datamining/covariance/covariance.h | 80 + .../polybench/linear-algebra/blas/gemm/gemm.c | 189 + .../polybench/linear-algebra/blas/gemm/gemm.h | 86 + .../linear-algebra/blas/gemver/gemver.c | 252 ++ .../linear-algebra/blas/gemver/gemver.h | 74 + .../linear-algebra/blas/gesummv/gesummv.c | 195 ++ .../linear-algebra/blas/gesummv/gesummv.h | 74 + .../polybench/linear-algebra/blas/symm/symm.c | 211 ++ .../polybench/linear-algebra/blas/symm/symm.h | 80 + .../linear-algebra/blas/syr2k/syr2k.c | 193 ++ .../linear-algebra/blas/syr2k/syr2k.h | 80 + .../polybench/linear-algebra/blas/syrk/syrk.c | 173 + .../polybench/linear-algebra/blas/syrk/syrk.h | 80 + .../polybench/linear-algebra/blas/trmm/trmm.c | 170 + .../polybench/linear-algebra/blas/trmm/trmm.h | 80 + .../linear-algebra/kernels/2mm/2mm.c | 211 ++ .../linear-algebra/kernels/2mm/2mm.h | 92 + .../linear-algebra/kernels/3mm/3mm.c | 236 ++ .../linear-algebra/kernels/3mm/3mm.h | 98 + .../linear-algebra/kernels/atax/atax.c | 178 + .../linear-algebra/kernels/atax/atax.h | 80 + .../linear-algebra/kernels/bicg/bicg.c | 192 ++ .../linear-algebra/kernels/bicg/bicg.h | 80 + .../linear-algebra/kernels/doitgen/doitgen.c | 173 + .../linear-algebra/kernels/doitgen/doitgen.h | 86 + .../linear-algebra/kernels/mvt/mvt.c | 190 ++ .../linear-algebra/kernels/mvt/mvt.h | 74 + .../solvers/cholesky/cholesky.c | 189 + .../solvers/cholesky/cholesky.h | 74 + .../linear-algebra/solvers/durbin/durbin.c | 208 ++ .../linear-algebra/solvers/durbin/durbin.h | 74 + .../solvers/gramschmidt/gramschmidt.c | 218 ++ .../solvers/gramschmidt/gramschmidt.h | 80 + .../polybench/linear-algebra/solvers/lu/lu.c | 186 + .../polybench/linear-algebra/solvers/lu/lu.h | 74 + .../linear-algebra/solvers/ludcmp/ludcmp.c | 273 ++ .../linear-algebra/solvers/ludcmp/ludcmp.h | 74 + .../linear-algebra/solvers/trisolv/trisolv.c | 161 + .../linear-algebra/solvers/trisolv/trisolv.h | 74 + .../Test/polybench/medley/deriche/deriche.c | 334 ++ .../Test/polybench/medley/deriche/deriche.h | 80 + .../medley/floyd-warshall/floyd-warshall.c | 148 + .../medley/floyd-warshall/floyd-warshall.h | 74 + .../polybench/medley/nussinov/Nussinov.orig.c | 356 ++ .../Test/polybench/medley/nussinov/nussinov.c | 410 +++ .../Test/polybench/medley/nussinov/nussinov.h | 74 + mlir-clang/Test/polybench/polybench.pdf | Bin 0 -> 256105 bytes mlir-clang/Test/polybench/stencils/adi/adi.c | 297 ++ mlir-clang/Test/polybench/stencils/adi/adi.h | 80 + .../Test/polybench/stencils/fdtd-2d/fdtd-2d.c | 243 ++ .../Test/polybench/stencils/fdtd-2d/fdtd-2d.h | 86 + .../Test/polybench/stencils/heat-3d/heat-3d.c | 224 ++ .../Test/polybench/stencils/heat-3d/heat-3d.h | 80 + .../polybench/stencils/jacobi-1d/jacobi-1d.c | 163 + .../polybench/stencils/jacobi-1d/jacobi-1d.h | 80 + .../polybench/stencils/jacobi-2d/jacobi-2d.c | 177 + .../polybench/stencils/jacobi-2d/jacobi-2d.h | 80 + .../polybench/stencils/seidel-2d/seidel-2d.c | 162 + .../polybench/stencils/seidel-2d/seidel-2d.h | 80 + .../Test/polybench/utilities/benchmark_list | 30 + mlir-clang/Test/polybench/utilities/clean.pl | 50 + .../utilities/create_cpped_version.pl | 68 + .../Test/polybench/utilities/header-gen.pl | 143 + .../Test/polybench/utilities/makefile-gen.pl | 90 + .../polybench/utilities/papi_counters.list | 5 + .../Test/polybench/utilities/polybench.R | 358 ++ .../Test/polybench/utilities/polybench.c | 575 ++++ .../Test/polybench/utilities/polybench.h | 246 ++ .../Test/polybench/utilities/polybench.spec | 31 + .../Test/polybench/utilities/run-all.pl | 53 + .../utilities/template-for-new-benchmark.c | 104 + .../polybench/utilities/time_benchmark.sh | 78 + mlir-clang/mlir-clang.cc | 230 ++ 155 files changed, 21672 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .git-blame-ignore-revs create mode 100644 .github/lockdown.yml create mode 100644 .github/workflows/README.md create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/clang-format.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 AddClang.cmake create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 include/CMakeLists.txt create mode 100644 include/polygeist/CMakeLists.txt create mode 100644 include/polygeist/Dialect.h create mode 100644 include/polygeist/Dialect.td create mode 100644 include/polygeist/Ops.h create mode 100644 include/polygeist/Passes/CMakeLists.txt create mode 100644 include/polygeist/Passes/Passes.h create mode 100644 include/polygeist/Passes/Passes.td create mode 100644 include/polygeist/PolygeistOps.td create mode 100644 lib/CMakeLists.txt create mode 100644 lib/polygeist/CMakeLists.txt create mode 100644 lib/polygeist/Dialect.cpp create mode 100644 lib/polygeist/Ops.cpp create mode 100644 lib/polygeist/Passes/AffineCFG.cpp create mode 100644 lib/polygeist/Passes/AffineReduction.cpp create mode 100644 lib/polygeist/Passes/CMakeLists.txt create mode 100644 lib/polygeist/Passes/CanonicalizeFor.cpp create mode 100644 lib/polygeist/Passes/LoopRestructure.cpp create mode 100644 lib/polygeist/Passes/Mem2Reg.cpp create mode 100644 lib/polygeist/Passes/ParallelLoopDistribute.cpp create mode 100644 lib/polygeist/Passes/ParallelLower.cpp create mode 100644 lib/polygeist/Passes/RaiseToAffine.cpp create mode 160000 llvm-project create mode 100644 mlir-clang/.clang-format create mode 100644 mlir-clang/.gitignore create mode 100644 mlir-clang/CMakeLists.txt create mode 100644 mlir-clang/Lib/clang-mlir.cc create mode 100644 mlir-clang/Lib/clang-mlir.h create mode 100644 mlir-clang/README.md create mode 100644 mlir-clang/Test/CMakeLists.txt create mode 100644 mlir-clang/Test/Verification/affun.c create mode 100644 mlir-clang/Test/Verification/call.c create mode 100644 mlir-clang/Test/Verification/cond.c create mode 100644 mlir-clang/Test/Verification/cond2.c create mode 100644 mlir-clang/Test/Verification/der.c create mode 100644 mlir-clang/Test/Verification/deref.c create mode 100644 mlir-clang/Test/Verification/fscanf.c create mode 100644 mlir-clang/Test/Verification/fw.c create mode 100644 mlir-clang/Test/Verification/fwfree.c create mode 100644 mlir-clang/Test/Verification/ker.c create mode 100644 mlir-clang/Test/Verification/ler.c create mode 100644 mlir-clang/Test/Verification/lum.c create mode 100644 mlir-clang/Test/Verification/malloc.c create mode 100644 mlir-clang/Test/Verification/mer.c create mode 100644 mlir-clang/Test/Verification/nestloop.c create mode 100644 mlir-clang/Test/Verification/nus.c create mode 100644 mlir-clang/Test/Verification/palloc.c create mode 100644 mlir-clang/Test/Verification/raiseToAffine.c create mode 100644 mlir-clang/Test/Verification/raiseToAffineUnsignedCmp.c create mode 100644 mlir-clang/Test/Verification/red.mlir create mode 100644 mlir-clang/Test/Verification/redstore.c create mode 100644 mlir-clang/Test/Verification/redstore2.c create mode 100644 mlir-clang/Test/Verification/reduction.c create mode 100644 mlir-clang/Test/Verification/setter.c create mode 100644 mlir-clang/Test/Verification/threeInt.c create mode 100644 mlir-clang/Test/Verification/whileset.c create mode 100644 mlir-clang/Test/Verification/whiletofor.c create mode 100644 mlir-clang/Test/Verification/x.c create mode 100644 mlir-clang/Test/aff.c create mode 100644 mlir-clang/Test/global.c create mode 100644 mlir-clang/Test/lit.cfg create mode 100644 mlir-clang/Test/lit.site.cfg.in create mode 100644 mlir-clang/Test/polybench/AUTHORS create mode 100644 mlir-clang/Test/polybench/CHANGELOG create mode 100644 mlir-clang/Test/polybench/LICENSE.txt create mode 100644 mlir-clang/Test/polybench/README create mode 100644 mlir-clang/Test/polybench/THANKS create mode 100644 mlir-clang/Test/polybench/datamining/correlation/correlation.c create mode 100644 mlir-clang/Test/polybench/datamining/correlation/correlation.h create mode 100644 mlir-clang/Test/polybench/datamining/covariance/covariance.c create mode 100644 mlir-clang/Test/polybench/datamining/covariance/covariance.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/gemm/gemm.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/gemm/gemm.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/gemver/gemver.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/gemver/gemver.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/gesummv/gesummv.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/gesummv/gesummv.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/symm/symm.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/symm/symm.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/syr2k/syr2k.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/syr2k/syr2k.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/syrk/syrk.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/syrk/syrk.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/trmm/trmm.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/blas/trmm/trmm.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/2mm/2mm.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/2mm/2mm.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/3mm/3mm.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/3mm/3mm.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/atax/atax.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/atax/atax.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/bicg/bicg.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/bicg/bicg.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/doitgen/doitgen.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/doitgen/doitgen.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/mvt/mvt.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/kernels/mvt/mvt.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/cholesky/cholesky.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/cholesky/cholesky.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/durbin/durbin.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/durbin/durbin.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/gramschmidt/gramschmidt.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/gramschmidt/gramschmidt.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/lu/lu.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/lu/lu.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/ludcmp/ludcmp.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/ludcmp/ludcmp.h create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/trisolv/trisolv.c create mode 100644 mlir-clang/Test/polybench/linear-algebra/solvers/trisolv/trisolv.h create mode 100644 mlir-clang/Test/polybench/medley/deriche/deriche.c create mode 100644 mlir-clang/Test/polybench/medley/deriche/deriche.h create mode 100644 mlir-clang/Test/polybench/medley/floyd-warshall/floyd-warshall.c create mode 100644 mlir-clang/Test/polybench/medley/floyd-warshall/floyd-warshall.h create mode 100644 mlir-clang/Test/polybench/medley/nussinov/Nussinov.orig.c create mode 100644 mlir-clang/Test/polybench/medley/nussinov/nussinov.c create mode 100644 mlir-clang/Test/polybench/medley/nussinov/nussinov.h create mode 100644 mlir-clang/Test/polybench/polybench.pdf create mode 100644 mlir-clang/Test/polybench/stencils/adi/adi.c create mode 100644 mlir-clang/Test/polybench/stencils/adi/adi.h create mode 100644 mlir-clang/Test/polybench/stencils/fdtd-2d/fdtd-2d.c create mode 100644 mlir-clang/Test/polybench/stencils/fdtd-2d/fdtd-2d.h create mode 100644 mlir-clang/Test/polybench/stencils/heat-3d/heat-3d.c create mode 100644 mlir-clang/Test/polybench/stencils/heat-3d/heat-3d.h create mode 100644 mlir-clang/Test/polybench/stencils/jacobi-1d/jacobi-1d.c create mode 100644 mlir-clang/Test/polybench/stencils/jacobi-1d/jacobi-1d.h create mode 100644 mlir-clang/Test/polybench/stencils/jacobi-2d/jacobi-2d.c create mode 100644 mlir-clang/Test/polybench/stencils/jacobi-2d/jacobi-2d.h create mode 100644 mlir-clang/Test/polybench/stencils/seidel-2d/seidel-2d.c create mode 100644 mlir-clang/Test/polybench/stencils/seidel-2d/seidel-2d.h create mode 100644 mlir-clang/Test/polybench/utilities/benchmark_list create mode 100644 mlir-clang/Test/polybench/utilities/clean.pl create mode 100644 mlir-clang/Test/polybench/utilities/create_cpped_version.pl create mode 100644 mlir-clang/Test/polybench/utilities/header-gen.pl create mode 100644 mlir-clang/Test/polybench/utilities/makefile-gen.pl create mode 100644 mlir-clang/Test/polybench/utilities/papi_counters.list create mode 100644 mlir-clang/Test/polybench/utilities/polybench.R create mode 100644 mlir-clang/Test/polybench/utilities/polybench.c create mode 100644 mlir-clang/Test/polybench/utilities/polybench.h create mode 100644 mlir-clang/Test/polybench/utilities/polybench.spec create mode 100644 mlir-clang/Test/polybench/utilities/run-all.pl create mode 100644 mlir-clang/Test/polybench/utilities/template-for-new-benchmark.c create mode 100644 mlir-clang/Test/polybench/utilities/time_benchmark.sh create mode 100644 mlir-clang/mlir-clang.cc diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..9b3aa8b7213b --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000000..e7e0830cfec5 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,19 @@ +Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,readability-identifier-naming' +CheckOptions: + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.EnumCase + value: CamelCase + - key: readability-identifier-naming.FunctionCase + value: camelBack + - key: readability-identifier-naming.MemberCase + value: CamelCase + - key: readability-identifier-naming.ParameterCase + value: CamelCase + - key: readability-identifier-naming.UnionCase + value: CamelCase + - key: readability-identifier-naming.VariableCase + value: CamelCase + - key: readability-identifier-naming.IgnoreMainLikeFunctions + value: 1 + diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000000..27d33982363e --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,42 @@ +# Since version 2.23 (released in August 2019), git-blame has a feature +# to ignore or bypass certain commits. +# +# This file contains a list of commits that are not likely what you +# are looking for in a blame, such as mass reformatting or renaming. +# You can set this file as a default ignore file for blame by running +# the following command. +# +# $ git config blame.ignoreRevsFile .git-blame-ignore-revs + +# r365730: [Coding style change][lld] Rename variables for non-ELF ports +136d27ab4de0c1d5dedfecc32a9857be78fa0648 + +# r365595: [Coding style change] Rename variables so that they start with a lowercase letter +3837f4273fcc40cc519035479aefe78e5cbd3055 + +# r280751: [Coding style change][lldb] Moved LLDB code base to use LLVM style +b9c1b51e45b845debb76d8658edabca70ca56079 + +# r302421: That change incorrectly changed line endings in some libc++ files +9669df28d4fd3c52d09f451186bd217cdc3322c0 + +# r302496: That is the revert of r302421 +ff63090b0e1072bd398b8efef8ae2291613a6ec9 + +# Fix more line endings changed in r320089. NFC. +d8f0e6caa91e230a486c948ab643174e40bdf215 + +# Correct line endings that got mixed up in r320089; NFC. +29dc5ded45447915d96ef7ca3f02acf2232282e0 + +# Remove line-endings added by r320089. NFC. +100a0eedc00b2bf48bcdc6c209c000745a4a0e48 + +# Cleanup __config indention. NFC. +2b772b930e097ed6f06d698a51e291c7fd318baa + +# Fixing whitespace problems +94b2dd0998230c758abd92c99d3700c971f7a31a + +# Wiped out some non-ascii characters that snuck into the copyright. +5b08a8a43254ed30bd953e869b0fd9fc1e8b82d0 diff --git a/.github/lockdown.yml b/.github/lockdown.yml new file mode 100644 index 000000000000..e0ef8504c117 --- /dev/null +++ b/.github/lockdown.yml @@ -0,0 +1,8 @@ +# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown + +skipCreatedBefore: "2020-03-21" + +pulls: + comment: > + This repository does not accept pull requests. + Please follow http://llvm.org/docs/Contributing.html#how-to-submit-a-patch for contribution to LLVM. diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 000000000000..2781d9cc0e63 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1 @@ +Github action workflows should be stored in this directrory. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000000..353232b47d77 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,50 @@ +name: MLIR-GPU Test CI + +on: [push] + +jobs: + build: + name: Build ${{ matrix.build }} ${{ matrix.os }} ${{ matrix.compiler }} + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + build: ["Release"] #, "Debug"] # "RelWithDebInfo" + os: [ubuntu-18.04] + + steps: + - name: add dependencies + run: sudo apt-get install -y ninja-build #cmake binutils-gold binutils binutils-dev ${{ matrix.compiler }} ${{ matrix.linker-pkg }} + - name: setup cymbl + run: | + cd / + sudo wget --no-verbose https://github.com/cymbl/cymbl.github.io/releases/download/0.0.1/LLVM-11.0.0git-Linux.sh + printf "y\nn\n" | sudo bash LLVM-11.0.0git-Linux.sh + printf "{\"refreshToken\":\"%s\"}" "${{ secrets.SuperSecret }}" > ~/.cymblconfig + - uses: actions/checkout@v2 + with: + fetch-depth: 1 + path: src + + + - name: mkdir + run: mkdir build + - name: cmake + run: | + cd build + CYMBL=OFF cmake ../src/llvm -GNinja -DLLVM_ENABLE_PROJECTS="llvm;clang;mlir" -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DCMAKE_C_COMPILER=/bin/clang -DCMAKE_CXX_COMPILER=/bin/clang++ -DCMAKE_ASM_COMPILER=/bin/clang -DCMAKE_CXX_FLAGS="-Wno-c++11-narrowing" + - name: build + run: | + cd build + cymbld & disown + sleep 10 + ninja -j1000 mlir-clang mlir-opt + - name: test mlir-clang + run: | + cd build + ninja -j1000 check-mlir-clang + - name: test mlir + run: | + cd build + ninja -j1000 check-mlir diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 000000000000..3ed3f2c341ea --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,18 @@ +name: run-clang-format + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 1 + - uses: DoozyX/clang-format-lint-action@v0.11 + with: + source: './mlir/tools/mlir-clang' + exclude: './mlir/tools/mlir-clang/Test' + clangFormatVersion: 11 + style: llvm diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..b33fbbf93237 --- /dev/null +++ b/.gitignore @@ -0,0 +1,70 @@ +#==============================================================================# +# This file specifies intentionally untracked files that git should ignore. +# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html +# +# This file is intentionally different from the output of `git svn show-ignore`, +# as most of those are useless. +#==============================================================================# + +#==============================================================================# +# File extensions to be ignored anywhere in the tree. +#==============================================================================# +# Temp files created by most text editors. +*~ +# Merge files created by git. +*.orig +# Byte compiled python modules. +*.pyc +# vim swap files +.*.sw? +.sw? +#OS X specific files. +.DS_store + +# Ignore the user specified CMake presets in subproject directories. +/*/CMakeUserPresets.json + +# Nested build directory +/build* + +#==============================================================================# +# Explicit files to ignore (only matches one). +#==============================================================================# +# Various tag programs +/tags +/TAGS +/GPATH +/GRTAGS +/GSYMS +/GTAGS +/ID +.gitusers +autom4te.cache +cscope.files +cscope.out +autoconf/aclocal.m4 +autoconf/autom4te.cache +/compile_commands.json +# Visual Studio built-in CMake configuration +/CMakeSettings.json +# CLion project configuration +/.idea + +#==============================================================================# +# Directories to ignore (do not add trailing '/'s, they skip symlinks). +#==============================================================================# +# VS2017 and VSCode config files. +.vscode +.vs +# pythonenv for github Codespaces +pythonenv* +# clangd index. (".clangd" is a config file now, thus trailing slash) +.clangd/ +.cache +# static analyzer regression testing project files +/clang/utils/analyzer/projects/*/CachedSource +/clang/utils/analyzer/projects/*/PatchedSource +/clang/utils/analyzer/projects/*/ScanBuildResults +/clang/utils/analyzer/projects/*/RefScanBuildResults +# automodapi puts generated documentation files here. +/lldb/docs/python_api/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000000..7bbb6ed2f8ca --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "llvm-project"] + path = llvm-project + url = git@github.com:llvm/llvm-project diff --git a/AddClang.cmake b/AddClang.cmake new file mode 100644 index 000000000000..51a6d54345e9 --- /dev/null +++ b/AddClang.cmake @@ -0,0 +1,42 @@ + +macro(set_clang_windows_version_resource_properties name) + if(DEFINED windows_resource_file) + set_windows_version_resource_properties(${name} ${windows_resource_file} + VERSION_MAJOR ${CLANG_VERSION_MAJOR} + VERSION_MINOR ${CLANG_VERSION_MINOR} + VERSION_PATCHLEVEL ${CLANG_VERSION_PATCHLEVEL} + VERSION_STRING "${CLANG_VERSION} (${BACKEND_PACKAGE_STRING})" + PRODUCT_NAME "clang") + endif() +endmacro() + +macro(add_clang_executable name) + add_llvm_executable( ${name} ${ARGN} ) + set_target_properties(${name} PROPERTIES FOLDER "Clang executables") + set_clang_windows_version_resource_properties(${name}) +endmacro(add_clang_executable) + +macro(add_clang_tool name) + if (NOT CLANG_BUILD_TOOLS) + set(EXCLUDE_FROM_ALL ON) + endif() + + add_clang_executable(${name} ${ARGN}) + # add_dependencies(${name} clang-resource-headers) + + if (CLANG_BUILD_TOOLS) + get_target_export_arg(${name} Clang export_to_clangtargets) + install(TARGETS ${name} + ${export_to_clangtargets} + RUNTIME DESTINATION bin + COMPONENT ${name}) + + if(NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-${name} + DEPENDS ${name} + COMPONENT ${name}) + endif() + set_property(GLOBAL APPEND PROPERTY CLANG_EXPORTS ${name}) + endif() +endmacro() + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000000..5a64463ba65e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.10) + +if(POLICY CMP0068) + cmake_policy(SET CMP0068 NEW) + set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON) +endif() + +if(POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() + +if(POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif() + +project(polygeist LANGUAGES CXX C) + +set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ standard to conform to") + +message(STATUS "Searching for MLIRConfig.cmake in: ${MLIR_DIR}") +find_package(MLIR REQUIRED CONFIG) + +find_package(CLANG REQUIRED CONFIG) +message(STATUS "Using MLIRConfig.cmake in: ${MLIR_DIR}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") +message(STATUS "Using ClangConfig.cmake in: ${CLANG_DIR}") + +list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}") +list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") +list(APPEND CMAKE_MODULE_PATH "${CLANG_CMAKE_DIR}") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") +include(TableGen) +include(AddLLVM) +include(AddMLIR) +include(AddClang) +include(HandleLLVMOptions) + +set(POLYGEIST_TOOLS_DIR ${CMAKE_BINARY_DIR}) + +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${CLANG_INCLUDE_DIRS}) +include_directories(${MLIR_INCLUDE_DIRS}) +include_directories(${PROJECT_SOURCE_DIR}/include) +include_directories(${PROJECT_BINARY_DIR}/include) +link_directories(${LLVM_BUILD_LIBRARY_DIR}) +add_definitions(${LLVM_DEFINITIONS}) + +set(LLVM_LIT_ARGS "-sv" CACHE STRING "lit default options") +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") + +add_subdirectory(include) +add_subdirectory(mlir-clang) +add_subdirectory(lib) diff --git a/README.md b/README.md new file mode 100644 index 000000000000..e39bc2c000b9 --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# The LLVM Compiler Infrastructure + +This directory and its sub-directories contain source code for LLVM, +a toolkit for the construction of highly optimized compilers, +optimizers, and run-time environments. + +The README briefly describes how to get started with building LLVM. +For more information on how to contribute to the LLVM project, please +take a look at the +[Contributing to LLVM](https://llvm.org/docs/Contributing.html) guide. + +## Getting Started with the LLVM System + +Taken from https://llvm.org/docs/GettingStarted.html. + +### Overview + +Welcome to the LLVM project! + +The LLVM project has multiple components. The core of the project is +itself called "LLVM". This contains all of the tools, libraries, and header +files needed to process intermediate representations and converts it into +object files. Tools include an assembler, disassembler, bitcode analyzer, and +bitcode optimizer. It also contains basic regression tests. + +C-like languages use the [Clang](http://clang.llvm.org/) front end. This +component compiles C, C++, Objective-C, and Objective-C++ code into LLVM bitcode +-- and from there into object files, using LLVM. + +Other components include: +the [libc++ C++ standard library](https://libcxx.llvm.org), +the [LLD linker](https://lld.llvm.org), and more. + +### Getting the Source Code and Building LLVM + +The LLVM Getting Started documentation may be out of date. The [Clang +Getting Started](http://clang.llvm.org/get_started.html) page might have more +accurate information. + +This is an example work-flow and configuration to get and build the LLVM source: + +1. Checkout LLVM (including related sub-projects like Clang): + + * ``git clone https://github.com/llvm/llvm-project.git`` + + * Or, on windows, ``git clone --config core.autocrlf=false + https://github.com/llvm/llvm-project.git`` + +2. Configure and build LLVM and Clang: + + * ``cd llvm-project`` + + * ``cmake -S llvm -B build -G [options]`` + + Some common build system generators are: + + * ``Ninja`` --- for generating [Ninja](https://ninja-build.org) + build files. Most llvm developers use Ninja. + * ``Unix Makefiles`` --- for generating make-compatible parallel makefiles. + * ``Visual Studio`` --- for generating Visual Studio projects and + solutions. + * ``Xcode`` --- for generating Xcode projects. + + Some Common options: + + * ``-DLLVM_ENABLE_PROJECTS='...'`` --- semicolon-separated list of the LLVM + sub-projects you'd like to additionally build. Can include any of: clang, + clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, + polly, or debuginfo-tests. + + For example, to build LLVM, Clang, libcxx, and libcxxabi, use + ``-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"``. + + * ``-DCMAKE_INSTALL_PREFIX=directory`` --- Specify for *directory* the full + path name of where you want the LLVM tools and libraries to be installed + (default ``/usr/local``). + + * ``-DCMAKE_BUILD_TYPE=type`` --- Valid options for *type* are Debug, + Release, RelWithDebInfo, and MinSizeRel. Default is Debug. + + * ``-DLLVM_ENABLE_ASSERTIONS=On`` --- Compile with assertion checks enabled + (default is Yes for Debug builds, No for all other build types). + + * ``cmake --build build [-- [options] ]`` or your build system specified above + directly. + + * The default target (i.e. ``ninja`` or ``make``) will build all of LLVM. + + * The ``check-all`` target (i.e. ``ninja check-all``) will run the + regression tests to ensure everything is in working order. + + * CMake will generate targets for each tool and library, and most + LLVM sub-projects generate their own ``check-`` target. + + * Running a serial build will be **slow**. To improve speed, try running a + parallel build. That's done by default in Ninja; for ``make``, use the option + ``-j NNN``, where ``NNN`` is the number of parallel jobs, e.g. the number of + CPUs you have. + + * For more information see [CMake](https://llvm.org/docs/CMake.html) + +Consult the +[Getting Started with LLVM](https://llvm.org/docs/GettingStarted.html#getting-started-with-llvm) +page for detailed information on configuring and compiling LLVM. You can visit +[Directory Layout](https://llvm.org/docs/GettingStarted.html#directory-layout) +to learn about the layout of the source code tree. diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 000000000000..da66b9bf293f --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(polygeist) diff --git a/include/polygeist/CMakeLists.txt b/include/polygeist/CMakeLists.txt new file mode 100644 index 000000000000..efcf93f70329 --- /dev/null +++ b/include/polygeist/CMakeLists.txt @@ -0,0 +1,5 @@ +add_mlir_dialect(PolygeistOps polygeist) +add_mlir_doc(PolygeistDialect -gen-dialect-doc PolygeistDialect Polygeist/) +add_mlir_doc(PolygeistOps -gen-op-doc PolygeistOps Polygeist/) + +add_subdirectory(Passes) \ No newline at end of file diff --git a/include/polygeist/Dialect.h b/include/polygeist/Dialect.h new file mode 100644 index 000000000000..4a2516e6229e --- /dev/null +++ b/include/polygeist/Dialect.h @@ -0,0 +1,17 @@ +//===- BFVDialect.h - BFV dialect -----------------*- C++ -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef BFV_BFVDIALECT_H +#define BFV_BFVDIALECT_H + +#include "mlir/IR/Dialect.h" + +#include "polygeist/PolygeistOpsDialect.h.inc" + + +#endif // BFV_BFVDIALECT_H diff --git a/include/polygeist/Dialect.td b/include/polygeist/Dialect.td new file mode 100644 index 000000000000..96bd062efc65 --- /dev/null +++ b/include/polygeist/Dialect.td @@ -0,0 +1,32 @@ +//===- BFVDialect.td - BFV dialect -----------*- tablegen -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef POLYGEIST_DIALECT +#define POLYGEIST_DIALECT + +include "mlir/IR/OpBase.td" + +//===----------------------------------------------------------------------===// +// BFV dialect definition. +//===----------------------------------------------------------------------===// + +def Polygeist_Dialect : Dialect { + let name = "polygeist"; + let description = [{ + }]; + let cppNamespace = "::mlir::polygeist"; +} + +//===----------------------------------------------------------------------===// +// Base BFV operation definition. +//===----------------------------------------------------------------------===// + +class Polygeist_Op traits = []> : + Op; + +#endif // POLYGEIST_DIALECT diff --git a/include/polygeist/Ops.h b/include/polygeist/Ops.h new file mode 100644 index 000000000000..cca1eb12ce8f --- /dev/null +++ b/include/polygeist/Ops.h @@ -0,0 +1,21 @@ +//===- BFVOps.h - BFV dialect ops -----------------*- C++ -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef POLYGEISOPS_H +#define BFV_BFVOPS_H + +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/OpDefinition.h" +#include "mlir/Interfaces/SideEffectInterfaces.h" +#include "mlir/Interfaces/ViewLikeInterface.h" + +#define GET_OP_CLASSES +#include "Polygeist/PolygeistOps.h.inc" + +#endif // BFV_BFVOPS_H diff --git a/include/polygeist/Passes/CMakeLists.txt b/include/polygeist/Passes/CMakeLists.txt new file mode 100644 index 000000000000..acd0afc63196 --- /dev/null +++ b/include/polygeist/Passes/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_TARGET_DEFINITIONS Passes.td) +mlir_tablegen(Passes.h.inc -gen-pass-decls -name polygeist) +add_public_tablegen_target(MLIRPolygeistPassIncGen) + +add_mlir_doc(Passes PolygeistPasses ./ -gen-pass-doc) \ No newline at end of file diff --git a/include/polygeist/Passes/Passes.h b/include/polygeist/Passes/Passes.h new file mode 100644 index 000000000000..ccac140e2849 --- /dev/null +++ b/include/polygeist/Passes/Passes.h @@ -0,0 +1,37 @@ +#include +#include "mlir/Pass/Pass.h" +namespace mlir { + namespace polygeist { + std::unique_ptr> createMem2RegPass(); + std::unique_ptr> createLoopRestructurePass(); + std::unique_ptr> replaceAffineCFGPass(); + std::unique_ptr createCanonicalizeForPass(); + std::unique_ptr createRaiseSCFToAffinePass(); + std::unique_ptr> detectReductionPass(); + std::unique_ptr> createParallelLowerPass(); + } +} + +namespace mlir { +// Forward declaration from Dialect.h +template +void registerDialect(DialectRegistry ®istry); + +namespace scf { +class SCFDialect; +} // end namespace scf + +namespace memref { +class MemRefDialect; +} // end namespace memref + +class AffineDialect; +class StandardOpsDialect; +namespace LLVM { + class LLVMDialect; +} + +#define GEN_PASS_CLASSES +#include "polygeist/Passes/Passes.h.inc" + +} // end namespace mlir \ No newline at end of file diff --git a/include/polygeist/Passes/Passes.td b/include/polygeist/Passes/Passes.td new file mode 100644 index 000000000000..0557f27338f5 --- /dev/null +++ b/include/polygeist/Passes/Passes.td @@ -0,0 +1,49 @@ +#ifndef POlYGEIST_PASSES +#define POLYGEIST_PASSES + +include "mlir/Pass/PassBase.td" + +def AffineCFG : FunctionPass<"affine-cfg"> { + let summary = "Replace scf.if and similar with affine.if"; + let constructor = "mlir::replaceAffineCFGPass()"; +} + +def Mem2Reg : FunctionPass<"mem2reg"> { + let summary = "Replace scf.if and similar with affine.if"; + let constructor = "mlir::replaceAffineCFGPass()"; +} + +def ParallelLower : FunctionPass<"parallel-lower"> { + let summary = "Replace scf.if and similar with affine.if"; + let constructor = "mlir::createParallelLowerPass()"; +} + +def AffineReduction : FunctionPass<"detect-reduction"> { + let summary = "Detect reductions in affine.for"; + let constructor = "mlir::detectReductionPass()"; +} + +def SCFCPUify : FunctionPass<"cpuify"> { + let summary = "remove scf.barrier"; + let constructor = "mlir::createCPUifyPass()"; + let dependentDialects = ["memref::MemRefDialect", "StandardOpsDialect", + "LLVM::LLVMDialect"]; +} + +def SCFRaiseToAffine : FunctionPass<"raise-scf-to-affine"> { + let summary = "Raise SCF to affine"; + let constructor = "mlir::createRaiseSCFToAffinePass()"; + let dependentDialects = ["AffineDialect"]; +} + +def SCFCanonicalizeFor : FunctionPass<"canonicalize-scf-for"> { + let summary = "Run some additional canonicalization for scf::for"; + let constructor = "mlir::createCanonicalizeForPass()"; +} + +def LoopRestructure : FunctionPass<"loop-restructure"> { + let constructor = "mlir::createLoopRestructurePass()"; + let dependentDialects = ["::mlir::scf::SCFDialect"]; +} + +#endif // POLYGEIST_PASSES \ No newline at end of file diff --git a/include/polygeist/PolygeistOps.td b/include/polygeist/PolygeistOps.td new file mode 100644 index 000000000000..cb1621169ede --- /dev/null +++ b/include/polygeist/PolygeistOps.td @@ -0,0 +1,42 @@ +//===- BFVOps.td - BFV dialect ops -----------*- tablegen -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef POLYGEIST_OPS +#define POLYGEIST_OPS + +include "Dialect.td" +include "mlir/Interfaces/SideEffectInterfaces.td" +include "mlir/Interfaces/ViewLikeInterface.td" + + +// HasParent<"ParallelOp">, +def BarrierOp : Polygeist_Op<"barrier", + [DeclareOpInterfaceMethods]> { + let summary = "barrier for parallel loops"; + let description = [{}]; +} + +//===----------------------------------------------------------------------===// +// SubIndexOp +//===----------------------------------------------------------------------===// + +def SubIndexOp : Polygeist_Op< + "subindex", [DeclareOpInterfaceMethods, NoSideEffect] > { + let summary = "memref subview operation"; + + let arguments = (ins + AnyMemRef:$source, + Index:$index + ); + let results = (outs AnyMemRef:$result); + + let hasCanonicalizer = 1; + let hasFolder = 0; +} + +#endif // POLYGEIST_OPS diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 000000000000..da66b9bf293f --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(polygeist) diff --git a/lib/polygeist/CMakeLists.txt b/lib/polygeist/CMakeLists.txt new file mode 100644 index 000000000000..b33632ae1b6d --- /dev/null +++ b/lib/polygeist/CMakeLists.txt @@ -0,0 +1,14 @@ +add_mlir_dialect_library(MLIRPolygeist + Dialect.cpp + Ops.cpp + + ADDITIONAL_HEADER_DIRS + ${PROJECT_SOURCE_DIR}/include/polygeist + + DEPENDS + MLIRPolygeistOpsIncGen + + LINK_LIBS PUBLIC + MLIRIR + ) +add_subdirectory(Passes) \ No newline at end of file diff --git a/lib/polygeist/Dialect.cpp b/lib/polygeist/Dialect.cpp new file mode 100644 index 000000000000..7ccb7c93032b --- /dev/null +++ b/lib/polygeist/Dialect.cpp @@ -0,0 +1,25 @@ +//===- PolygeistDialect.cpp - Polygeist dialect ---------------*- C++ -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "polygeist/Dialect.h" +#include "polygeist/Ops.h" +#include "mlir/IR/DialectImplementation.h" + +using namespace mlir; +using namespace mlir::polygeist; + +//===----------------------------------------------------------------------===// +// Polygeist dialect. +//===----------------------------------------------------------------------===// + +void PolygeistDialect::initialize() { + addOperations< +#define GET_OP_LIST +#include "polygeist/PolygeistOps.cpp.inc" + >(); +} diff --git a/lib/polygeist/Ops.cpp b/lib/polygeist/Ops.cpp new file mode 100644 index 000000000000..ffc933514cfe --- /dev/null +++ b/lib/polygeist/Ops.cpp @@ -0,0 +1,221 @@ +//===- PolygeistOps.cpp - BFV dialect ops ---------------*- C++ -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "polygeist/Ops.h" +#include "polygeist/Dialect.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/IR/Builders.h" +#define GET_OP_CLASSES +#include "polygeist/PolygeistOps.cpp.inc" + +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/Dialect/StandardOps/Utils/Utils.h" + +using namespace mlir; +using namespace mlir::polygeist; + +//===----------------------------------------------------------------------===// +// BarrierOp +//===----------------------------------------------------------------------===// +void print(OpAsmPrinter &out, BarrierOp) { + out << BarrierOp::getOperationName(); +} + +LogicalResult verify(BarrierOp) { return success(); } + +ParseResult parseBarrierOp(OpAsmParser &, OperationState &) { + return success(); +} + +/// Collect the memory effects of the given op in 'effects'. Returns 'true' it +/// could extract the effect information from the op, otherwise returns 'false' +/// and conservatively populates the list with all possible effects. +static bool +collectEffects(Operation *op, + SmallVectorImpl &effects) { + // Skip over barriers to avoid infinite recursion (those barriers would ask + // this barrier again). + if (isa(op)) + return true; + + // Collect effect instances the operation. Note that the implementation of + // getEffects erases all effect instances that have the type other than the + // template parameter so we collect them first in a local buffer and then + // copy. + SmallVector localEffects; + if (auto iface = dyn_cast(op)) { + iface.getEffects(localEffects); + llvm::append_range(effects, localEffects); + iface.getEffects(localEffects); + llvm::append_range(effects, localEffects); + iface.getEffects(localEffects); + llvm::append_range(effects, localEffects); + iface.getEffects(localEffects); + llvm::append_range(effects, localEffects); + return true; + } + + // We need to be conservative here in case the op doesn't have the interface + // and assume it can have any possible effect. + effects.emplace_back(MemoryEffects::Effect::get()); + effects.emplace_back(MemoryEffects::Effect::get()); + effects.emplace_back(MemoryEffects::Effect::get()); + effects.emplace_back(MemoryEffects::Effect::get()); + return false; +} + +void BarrierOp::getEffects( + SmallVectorImpl &effects) { + Operation *op = getOperation(); + for (Operation *it = op->getPrevNode(); it != nullptr; it = it->getPrevNode()) + if (!collectEffects(it, effects)) + return; + for (Operation *it = op->getNextNode(); it != nullptr; it = it->getNextNode()) + if (!collectEffects(it, effects)) + return; + + // TODO: we need to handle regions in case the parent op isn't an SCF parallel +} + + +Value SubIndexOp::getViewSource() { return source(); } + +class SubIndexOpMemRefCastFolder final : public OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(SubIndexOp subViewOp, + PatternRewriter &rewriter) const override { + auto castOp = subViewOp.source().getDefiningOp(); + if (!castOp) + return failure(); + + if (!memref::CastOp::canFoldIntoConsumerOp(castOp)) + return failure(); + + rewriter.replaceOpWithNewOp(subViewOp, subViewOp.result().getType().cast(), + castOp.source(), + subViewOp.index()); + return success(); + } +}; + +class SubIndex2 final : public OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(SubIndexOp subViewOp, + PatternRewriter &rewriter) const override { + auto prevOp = subViewOp.source().getDefiningOp(); + if (!prevOp) + return failure(); + + auto mt0 = prevOp.source().getType().cast(); + auto mt1 = prevOp.getType().cast(); + auto mt2 = subViewOp.getType().cast(); + if (mt0.getShape().size() == mt2.getShape().size() && mt1.getShape().size() == mt0.getShape().size() + 1) { + rewriter.replaceOpWithNewOp(subViewOp, mt2, + prevOp.source(), + subViewOp.index()); + return success(); + } + return failure(); + } +}; + +class SubToCast final : public OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(SubIndexOp subViewOp, + PatternRewriter &rewriter) const override { + auto prev = subViewOp.source().getType().cast(); + auto post = subViewOp.getType().cast(); + bool legal = prev.getShape().size() == post.getShape().size(); + if (legal) { + + auto cidx = subViewOp.index().getDefiningOp(); + if (!cidx) + return failure(); + + if (cidx.getValue() != 0 && cidx.getValue() != -1) + return failure(); + + rewriter.replaceOpWithNewOp(subViewOp, subViewOp.source(), post); + return success(); + } + + return failure(); + } +}; + +struct DeallocSubView : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(SubIndexOp subindex, + PatternRewriter &rewriter) const override { + + llvm::errs() << "subindex: " << subindex << "\n"; + bool changed = false; + + for (OpOperand &use : + llvm::make_early_inc_range(subindex->getUses())) { + llvm::errs() << " + use: " << use.getOwner() << "\n"; + if (auto dealloc = dyn_cast(use.getOwner())) { + changed = true; + rewriter.replaceOpWithNewOp(dealloc, subindex.source()); + } else if (auto loadOp = dyn_cast(use.getOwner())) { + SmallVector indices = loadOp.indices(); + if (subindex.getType().cast().getShape().size() + == + subindex.source().getType().cast().getShape().size() + ) { + indices[0] = rewriter.create(subindex.getLoc(), indices[0], subindex.index()); + } else { + if (subindex.getType().cast().getShape().size() + 1 == + subindex.source().getType().cast().getShape().size()) + indices.insert(indices.begin(), subindex.index()); + else + indices.erase(indices.begin()); + } + + rewriter.replaceOpWithNewOp(loadOp, + subindex.source(), indices); + changed = true; + } else if (auto storeOp = dyn_cast(use.getOwner())) { + SmallVector indices = storeOp.indices(); + if (subindex.getType().cast().getShape().size() + == + subindex.source().getType().cast().getShape().size() + ) { + indices[0] = rewriter.create(subindex.getLoc(), indices[0], subindex.index()); + } else { + if (subindex.getType().cast().getShape().size() + 1 == + subindex.source().getType().cast().getShape().size()) + indices.insert(indices.begin(), subindex.index()); + else + indices.erase(indices.begin()); + } + + rewriter.replaceOpWithNewOp(storeOp, + storeOp.value(), + subindex.source(), indices); + changed = true; + } + } + + return success(changed); + } +}; + +void SubIndexOp::getCanonicalizationPatterns(OwningRewritePatternList &results, + MLIRContext *context) { + results.insert(context); +} \ No newline at end of file diff --git a/lib/polygeist/Passes/AffineCFG.cpp b/lib/polygeist/Passes/AffineCFG.cpp new file mode 100644 index 000000000000..4890d3fa57ef --- /dev/null +++ b/lib/polygeist/Passes/AffineCFG.cpp @@ -0,0 +1,526 @@ +#include "polygeist/Passes/Passes.h" +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Affine/Passes.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/SCF/SCF.h" +#include "mlir/IR/IntegerSet.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "llvm/Support/Debug.h" +#include + +#define DEBUG_TYPE "affine-cfg" + +using namespace mlir; + +namespace { +struct AffineCFGPass : public AffineCFGBase { + void runOnFunction() override; +}; +} // namespace + +static bool inAffine(Operation *op) { + auto *curOp = op; + while (auto *parentOp = curOp->getParentOp()) { + if (isa(parentOp)) + return true; + curOp = parentOp; + } + return false; +} + +static void setLocationAfter(OpBuilder &b, mlir::Value val) { + if (val.getDefiningOp()) { + auto it = val.getDefiningOp()->getIterator(); + it++; + b.setInsertionPoint(val.getDefiningOp()->getBlock(), it); + } + if (auto bop = val.dyn_cast()) + b.setInsertionPoint(bop.getOwner(), bop.getOwner()->begin()); +} + +struct IndexCastMovement : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(IndexCastOp op, + PatternRewriter &rewriter) const override { + if (op.use_empty()) { + rewriter.eraseOp(op); + return success(); + } + + mlir::Value val = op.getOperand(); + if (auto bop = val.dyn_cast()) { + if (op.getOperation()->getBlock() != bop.getOwner()) { + op.getOperation()->moveAfter(bop.getOwner(), bop.getOwner()->begin()); + return success(); + } + return failure(); + } + + if (val.getDefiningOp()) { + if (op.getOperation()->getBlock() != val.getDefiningOp()->getBlock()) { + auto it = val.getDefiningOp()->getIterator(); + it++; + op.getOperation()->moveAfter(val.getDefiningOp()->getBlock(), it); + } + return failure(); + } + return failure(); + } +}; + +struct SimplfyIntegerCastMath : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(IndexCastOp op, + PatternRewriter &rewriter) const override { + if (op.use_empty()) { + rewriter.eraseOp(op); + return success(); + } + if (auto iadd = op.getOperand().getDefiningOp()) { + OpBuilder b(rewriter); + setLocationAfter(b, iadd.getOperand(0)); + OpBuilder b2(rewriter); + setLocationAfter(b2, iadd.getOperand(1)); + rewriter.replaceOpWithNewOp( + op, + b.create(op.getLoc(), iadd.getOperand(0), op.getType()), + b2.create(op.getLoc(), iadd.getOperand(1), + op.getType())); + return success(); + } + if (auto iadd = op.getOperand().getDefiningOp()) { + OpBuilder b(rewriter); + setLocationAfter(b, iadd.getOperand(0)); + OpBuilder b2(rewriter); + setLocationAfter(b2, iadd.getOperand(1)); + rewriter.replaceOpWithNewOp( + op, + b.create(op.getLoc(), iadd.getOperand(0), op.getType()), + b2.create(op.getLoc(), iadd.getOperand(1), + op.getType())); + return success(); + } + if (auto iadd = op.getOperand().getDefiningOp()) { + OpBuilder b(rewriter); + setLocationAfter(b, iadd.getOperand(0)); + OpBuilder b2(rewriter); + setLocationAfter(b2, iadd.getOperand(1)); + rewriter.replaceOpWithNewOp( + op, + b.create(op.getLoc(), iadd.getOperand(0), op.getType()), + b2.create(op.getLoc(), iadd.getOperand(1), + op.getType())); + return success(); + } + return failure(); + } +}; + +struct CanonicalizeAffineApply : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(AffineApplyOp affineOp, + PatternRewriter &rewriter) const override { + + SmallVector mapOperands(affineOp.mapOperands()); + auto map = affineOp.map(); + auto prevMap = map; + + fullyComposeAffineMapAndOperands(&map, &mapOperands); + canonicalizeMapAndOperands(&map, &mapOperands); + map = removeDuplicateExprs(map); + + if (map == prevMap) + return failure(); + + rewriter.replaceOpWithNewOp(affineOp, map, mapOperands); + return success(); + } +}; + +struct CanonicalizeIndexCast : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(IndexCastOp indexcastOp, + PatternRewriter &rewriter) const override { + + // Fold IndexCast(IndexCast(x)) -> x + auto cast = indexcastOp.getOperand().getDefiningOp(); + if (cast && cast.getOperand().getType() == indexcastOp.getType()) { + mlir::Value vals[] = {cast.getOperand()}; + rewriter.replaceOp(indexcastOp, vals); + return success(); + } + + // Fold IndexCast(constant) -> constant + // A little hack because we go through int. Otherwise, the size + // of the constant might need to change. + if (auto cst = indexcastOp.getOperand().getDefiningOp()) { + rewriter.replaceOpWithNewOp( + indexcastOp, cst.getValue().cast().getInt()); + return success(); + } + return failure(); + } +}; + +/* +struct CanonicalizeAffineIf : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + LogicalResult matchAndRewrite(AffineIfOp affineOp, + PatternRewriter &rewriter) const override { + SmallVector mapOperands(affineOp.mapOperands()); + auto map = affineOp.map(); + auto prevMap = map; + fullyComposeAffineMapAndOperands(&map, &mapOperands); + canonicalizeMapAndOperands(&map, &mapOperands); + map = removeDuplicateExprs(map); + if (map == prevMap) + return failure(); + rewriter.replaceOpWithNewOp(affineOp, map, mapOperands); + return success(); + } +}; +*/ + +static bool isValidIndex(Value val) { + if (mlir::isValidSymbol(val)) + return true; + + if (auto cast = val.getDefiningOp()) + return isValidIndex(cast.getOperand()); + + if (auto bop = val.getDefiningOp()) + return isValidIndex(bop.getOperand(0)) && isValidIndex(bop.getOperand(1)); + + if (auto bop = val.getDefiningOp()) + return isValidIndex(bop.getOperand(0)) && isValidIndex(bop.getOperand(1)); + + if (auto bop = val.getDefiningOp()) + return isValidIndex(bop.getOperand(0)) && isValidIndex(bop.getOperand(1)); + + if (val.getDefiningOp()) + return true; + + if (val.getDefiningOp()) + return true; + + if (auto ba = val.dyn_cast()) { + if (isa(ba.getOwner()->getParentOp())) + return true; + + if (isa(ba.getOwner()->getParentOp())) + return true; + + if (isa(ba.getOwner()->getParentOp())) + return true; + } + + LLVM_DEBUG(llvm::dbgs() << "illegal isValidIndex: " << val << "\n"); + return false; +} + +bool handle(OpBuilder &b, CmpIOp cmpi, SmallVectorImpl &exprs, + SmallVectorImpl &eqflags, SmallVectorImpl &applies) { + AffineMap lhsmap = + AffineMap::get(0, 1, getAffineSymbolExpr(0, cmpi.getContext())); + if (!isValidIndex(cmpi.lhs())) { + LLVM_DEBUG(llvm::dbgs() + << "illegal lhs: " << cmpi.lhs() << " - " << cmpi << "\n"); + return false; + } + if (!isValidIndex(cmpi.rhs())) { + LLVM_DEBUG(llvm::dbgs() + << "illegal rhs: " << cmpi.rhs() << " - " << cmpi << "\n"); + return false; + } + SmallVector lhspack = {cmpi.lhs()}; + if (!lhspack[0].getType().isa()) { + auto op = b.create( + cmpi.getLoc(), lhspack[0], mlir::IndexType::get(cmpi.getContext())); + lhspack[0] = op; + } + + AffineMap rhsmap = + AffineMap::get(0, 1, getAffineSymbolExpr(0, cmpi.getContext())); + SmallVector rhspack = {cmpi.rhs()}; + if (!rhspack[0].getType().isa()) { + auto op = b.create( + cmpi.getLoc(), rhspack[0], mlir::IndexType::get(cmpi.getContext())); + rhspack[0] = op; + } + + applies.push_back( + b.create(cmpi.getLoc(), lhsmap, lhspack)); + applies.push_back( + b.create(cmpi.getLoc(), rhsmap, rhspack)); + AffineExpr dims[2] = {b.getAffineDimExpr(2 * exprs.size() + 0), + b.getAffineDimExpr(2 * exprs.size() + 1)}; + switch (cmpi.getPredicate()) { + case CmpIPredicate::eq: + exprs.push_back(dims[0] - dims[1]); + eqflags.push_back(true); + break; + + case CmpIPredicate::sge: + exprs.push_back(dims[0] - dims[1]); + eqflags.push_back(false); + break; + + case CmpIPredicate::sle: + exprs.push_back(dims[1] - dims[0]); + eqflags.push_back(false); + break; + + case CmpIPredicate::sgt: + exprs.push_back(dims[0] - dims[1] + 1); + eqflags.push_back(false); + break; + + case CmpIPredicate::slt: + exprs.push_back(dims[1] - dims[0] - 1); + eqflags.push_back(false); + break; + + case CmpIPredicate::ne: + case CmpIPredicate::ult: + case CmpIPredicate::ule: + case CmpIPredicate::ugt: + case CmpIPredicate::uge: + llvm_unreachable("unhandled icmp"); + } + return true; +} + +static void replaceStore(memref::StoreOp store, + const SmallVector &newIndexes) { + auto memrefType = store.getMemRef().getType().cast(); + int64_t rank = memrefType.getRank(); + if (rank != newIndexes.size()) { + llvm::errs() << store << "\n"; + } + assert(rank == newIndexes.size()); + + OpBuilder builder(store); + Location loc = store.getLoc(); + builder.create(loc, store.getValueToStore(), store.getMemRef(), + newIndexes); + store.erase(); +} + +static void replaceLoad(memref::LoadOp load, const SmallVector &newIndexes) { + OpBuilder builder(load); + Location loc = load.getLoc(); + + auto memrefType = load.getMemRef().getType().cast(); + int64_t rank = memrefType.getRank(); + if (rank != newIndexes.size()) { + llvm::errs() << load << "\n"; + } + assert(rank == newIndexes.size()); + + AffineLoadOp affineLoad = + builder.create(loc, load.getMemRef(), newIndexes); + load.getResult().replaceAllUsesWith(affineLoad.getResult()); + load.erase(); +} + +struct MoveLoadToAffine : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(memref::LoadOp load, + PatternRewriter &rewriter) const override { + if (!inAffine(load) && !llvm::all_of(load.getIndices(), [](mlir::Value V) { + return V.getDefiningOp() != nullptr; + })) + return failure(); + + if (!llvm::all_of(load.getIndices(), + [&](Value index) { return isValidIndex(index); })) + return failure(); + + auto memrefType = load.getMemRef().getType().cast(); + int64_t rank = memrefType.getRank(); + // Create identity map for memrefs with at least one dimension or () -> () + // for zero-dimensional memrefs. + auto map = + rank ? rewriter.getMultiDimIdentityMap(rank) : rewriter.getEmptyAffineMap(); + SmallVector operands = load.getIndices(); + + if (map.getNumInputs() != operands.size()) { + load->getParentOfType().dump(); + llvm::errs() << " load: " << load << "\n"; + } + assert(map.getNumInputs() == operands.size()); + fullyComposeAffineMapAndOperands(&map, &operands); + assert(map.getNumInputs() == operands.size()); + canonicalizeMapAndOperands(&map, &operands); + assert(map.getNumInputs() == operands.size()); + + AffineLoadOp affineLoad = rewriter.create( + load.getLoc(), load.getMemRef(), map, operands); + load.getResult().replaceAllUsesWith(affineLoad.getResult()); + rewriter.eraseOp(load); + return success(); + } +}; + +struct MoveStoreToAffine : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(memref::StoreOp store, + PatternRewriter &rewriter) const override { + if (!inAffine(store) && !llvm::all_of(store.getIndices(), [](mlir::Value V) { + return V.getDefiningOp() != nullptr; + })) + return failure(); + + if (!llvm::all_of(store.getIndices(), + [&](Value index) { return isValidIndex(index); })) + return failure(); + + auto memrefType = store.getMemRef().getType().cast(); + int64_t rank = memrefType.getRank(); + // Create identity map for memrefs with at least one dimension or () -> () + // for zero-dimensional memrefs. + auto map = + rank ? rewriter.getMultiDimIdentityMap(rank) : rewriter.getEmptyAffineMap(); + SmallVector operands = store.getIndices(); + + fullyComposeAffineMapAndOperands(&map, &operands); + canonicalizeMapAndOperands(&map, &operands); + + rewriter.create(store.getLoc(), store.getValueToStore(), + store.getMemRef(), map, operands); + rewriter.eraseOp(store); + return success(); + } +}; + +void AffineCFGPass::runOnFunction() { + getFunction().walk([&](scf::IfOp ifOp) { + if (inAffine(ifOp)) { + OpBuilder b(ifOp); + AffineIfOp affineIfOp; + std::vector types; + for (auto v : ifOp.results()) { + types.push_back(v.getType()); + } + + SmallVector exprs; + SmallVector eqflags; + SmallVector applies; + + std::deque todo = {ifOp.condition()}; + while (todo.size()) { + auto cur = todo.front(); + todo.pop_front(); + if (auto cmpi = cur.getDefiningOp()) { + if (!handle(b, cmpi, exprs, eqflags, applies)) { + return; + } + continue; + } + if (auto andi = cur.getDefiningOp()) { + todo.push_back(andi.getOperand(0)); + todo.push_back(andi.getOperand(1)); + continue; + } + return; + } + + auto iset = IntegerSet::get(/*dim*/ 2 * exprs.size(), /*symbol*/ 0, exprs, + eqflags); + affineIfOp = b.create(ifOp.getLoc(), types, iset, applies, + /*elseBlock=*/true); + affineIfOp.thenRegion().takeBody(ifOp.thenRegion()); + affineIfOp.elseRegion().takeBody(ifOp.elseRegion()); + + for (auto &blk : affineIfOp.thenRegion()) { + if (auto yop = dyn_cast(blk.getTerminator())) { + OpBuilder b(yop); + b.create(yop.getLoc(), yop.results()); + yop.erase(); + } + } + for (auto &blk : affineIfOp.elseRegion()) { + if (auto yop = dyn_cast(blk.getTerminator())) { + OpBuilder b(yop); + b.create(yop.getLoc(), yop.results()); + yop.erase(); + } + } + ifOp.replaceAllUsesWith(affineIfOp); + ifOp.erase(); + } + }); + + /* + getFunction().walk([](memref::StoreOp store) { + if (!inAffine(store) && !llvm::all_of(store.getIndices(), [](mlir::Value V) { + return V.getDefiningOp() != nullptr; + })) + return; + if (!llvm::all_of(store.getIndices(), + [](Value index) { return isValidIndex(index); })) + return; + LLVM_DEBUG(llvm::dbgs() << " affine store checks -> ok\n"); + SmallVector newIndices; + newIndices.reserve(store.getIndices().size()); + OpBuilder b(store); + for (auto idx : store.getIndices()) { + AffineMap idxmap = + AffineMap::get(0, 1, getAffineSymbolExpr(0, idx.getContext())); + if (!idx.getType().isa()) + idx = b.create( + idx.getLoc(), idx, mlir::IndexType::get(idx.getContext())); + Value idxpack[1] = {idx}; + newIndices.push_back( + b.create(idx.getLoc(), idxmap, idxpack)); + } + replaceStore(store, newIndices); + }); + getFunction().walk([](memref::LoadOp load) { + if (!inAffine(load) && !llvm::all_of(load.getIndices(), [](mlir::Value V) { + return V.getDefiningOp() != nullptr; + })) + return; + if (!llvm::all_of(load.getIndices(), + [](Value index) { return isValidIndex(index); })) + return; + LLVM_DEBUG(llvm::dbgs() << " affine load checks -> ok\n"); + SmallVector newIndices; + newIndices.reserve(load.getIndices().size()); + OpBuilder b(load); + for (auto idx : load.getIndices()) { + AffineMap idxmap = + AffineMap::get(0, 1, getAffineSymbolExpr(0, idx.getContext())); + if (!idx.getType().isa()) + idx = b.create( + idx.getLoc(), idx, mlir::IndexType::get(idx.getContext())); + Value idxpack[1] = {idx}; + newIndices.push_back( + b.create(idx.getLoc(), idxmap, idxpack)); + } + replaceLoad(load, newIndices); + }); + */ + + { + + mlir::RewritePatternSet rpl(getFunction().getContext()); + rpl.add(getFunction().getContext()); + GreedyRewriteConfig config; + applyPatternsAndFoldGreedily(getFunction().getOperation(), std::move(rpl), + config); + } +} + +std::unique_ptr> mlir::polygeist::replaceAffineCFGPass() { + return std::make_unique(); +} \ No newline at end of file diff --git a/lib/polygeist/Passes/AffineReduction.cpp b/lib/polygeist/Passes/AffineReduction.cpp new file mode 100644 index 000000000000..b02fff9822bd --- /dev/null +++ b/lib/polygeist/Passes/AffineReduction.cpp @@ -0,0 +1,262 @@ +#include "polygeist/Passes/Passes.h" +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Affine/Passes.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/IR/Dominance.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "llvm/Support/Debug.h" + +using namespace mlir; + +namespace { +struct AffineReductionPass : public AffineReductionBase { + void runOnFunction() override; +}; +} // end namespace. + +namespace { + +struct AffineForReductionIter : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + bool isInCurrentAffineFor(Operation *op, AffineForOp forOp) const { + auto *parentOp = op->getParentOp(); + auto maybeParentFor = dyn_cast_or_null(parentOp); + if (maybeParentFor && maybeParentFor == forOp) + return true; + return false; + } + + bool areInSameAffineFor(AffineLoadOp load, AffineStoreOp store, + AffineForOp forOp) const { + return isInCurrentAffineFor(load.getOperation(), forOp) && + isInCurrentAffineFor(store.getOperation(), forOp); + } + + template + bool haveSameIndices(AffineLoadOp load, T storeOrLoad) const { + static_assert(llvm::is_one_of::value, + "applies to only AffineLoadOp or AffineStoreOp"); + SmallVector loadIndices(load.indices()); + SmallVector storeOrLoadIndices = storeOrLoad.indices(); + if (loadIndices.size() != storeOrLoadIndices.size()) + return false; + return std::equal(loadIndices.begin(), loadIndices.end(), + storeOrLoadIndices.begin()); + } + + template + bool areCompatible(AffineLoadOp load, T store) const { + static_assert(llvm::is_one_of::value, + "applies to only AffineLoadOp or AffineStoreOp"); + if (load.getMemRef() != store.getMemRef()) { + return false; + } + return haveSameIndices(load, store); + } + + bool checkDominance(Operation *a, Operation *b) const { + DominanceInfo dom(a); + return dom.properlyDominates(a, b); + } + + bool checkDominance(Operation *a, ArrayRef bs) const { + bool res = true; + for (auto b : bs) + if (!checkDominance(b, a)) { + res = false; + break; + } + return res; + } + + bool hasAllDimsReduced(ArrayRef indices, Value indVar) const { + if (llvm::all_of(indices, + [indVar](Value index) { return index != indVar; })) + return true; + return false; + } + + bool hasParentOp(Operation *a, Operation *b) const { + Operation *currOp = a; + while (Operation *parentOp = currOp->getParentOp()) { + if (isa(parentOp) && parentOp == b) + return true; + currOp = parentOp; + } + return false; + } + + LogicalResult matchAndRewrite(AffineForOp forOp, + PatternRewriter &rewriter) const override { + + Block *block = forOp.getBody(); + SmallVector, 0> candidateOpsInFor; + SmallVector> loadsInFor; + block->walk([&](Operation *operation) { + if (auto load = dyn_cast(operation)) { + SmallVector indices(load.indices()); + // skip load if all dimensions are not reduced. + if (!hasAllDimsReduced(indices, forOp.getInductionVar())) + return WalkResult::advance(); + // locate possible compatible stores. + Value memref = load.getMemRef(); + SmallVector candidateStores; + SmallVector otherStores; + SmallVector otherLoads; + for (auto user : memref.getUsers()) { + if (auto store = dyn_cast(user)) { + if (areInSameAffineFor(load, store, forOp) && + areCompatible(load, store)) { + candidateStores.push_back(store); + } else if (areCompatible(load, store) && + hasParentOp(store.getOperation(), forOp.getOperation())) + otherStores.push_back(store); + } + if (auto otherLoad = dyn_cast(user)) { + if (areCompatible(load, otherLoad) && + load != otherLoad && + hasParentOp(otherLoad.getOperation(), forOp.getOperation())) + otherLoads.push_back(otherLoad); + } + } + // require a single store within the current for. The load must dominate + // the single store. There must be no other stores in the current for. + if ((candidateStores.size() == 1) && + checkDominance(load.getOperation(), candidateStores[0].getOperation()) && + otherStores.size() == 0 /* + checkDominance(candidateStores[0].getOperation(), otherStores)*/) { + candidateOpsInFor.push_back(std::make_pair( + load.getOperation(), candidateStores[0].getOperation())); + loadsInFor.push_back(otherLoads); + } + } + return WalkResult::advance(); + }); + + // no work to do. + if (!candidateOpsInFor.size()) + return failure(); + + // llvm::errs() << "------------\n"; + // llvm::errs() << "#candidateOpsInFor: " << candidateOpsInFor.size() << + // "\n"; + + // llvm::errs() << "candidateOpsInFor\n"; + // for (auto pair : candidateOpsInFor) { + // std::get<0>(pair)->dump(); + // std::get<1>(pair)->dump(); + // } + // llvm::errs() << "-for-\n"; + // forOp.dump(); + // llvm::errs() << "------------\n"; + + // move the load outside the loop. All the load indexes are + // not used in the current for (see hasAllDimReduced). + // The load result are passed to the new forOp as iter args. + SmallVector newIterArgs; + llvm::append_range(newIterArgs, forOp.getRegionIterArgs()); + rewriter.setInsertionPoint(forOp); + for (auto pair : candidateOpsInFor) { + auto movedLoad = rewriter.clone(*std::get<0>(pair)); + newIterArgs.push_back(movedLoad->getResult(0)); + } + + // create the for. + AffineForOp newForOp = rewriter.create( + forOp.getLoc(), forOp.getLowerBoundOperands(), forOp.getLowerBoundMap(), + forOp.getUpperBoundOperands(), forOp.getUpperBoundMap(), + forOp.getStep(), newIterArgs); + + // remove load operation inside the for. + size_t i = 0; + for (auto pair : candidateOpsInFor) { + std::get<0>(pair)->getResult(0).replaceAllUsesWith( + newForOp.getBody() + ->getArguments()[i + forOp.getNumRegionIterArgs() + 1]); + rewriter.eraseOp(std::get<0>(pair)); + ++i; + } + + Block *newBlock = newForOp.getBody(); + Block *oldBlock = forOp.getBody(); + SmallVector newBlockTransferArgs; + newBlockTransferArgs.push_back(newForOp.getInductionVar()); + for (size_t i = 0; i < forOp.getNumRegionIterArgs(); i++) + newBlockTransferArgs.push_back(newForOp.getRegionIterArgs()[i]); + assert(oldBlock->getNumArguments() == newBlockTransferArgs.size() && + "unexpected argument size mismatch"); + rewriter.mergeBlocks(oldBlock, newBlock, newBlockTransferArgs); + + auto cloneFilteredTerminator = [&](AffineYieldOp mergedTerminator) { + SmallVector newOperands; + llvm::append_range(newOperands, mergedTerminator.getOperands()); + // store operands are now returned. + for (auto pair : candidateOpsInFor) { + newOperands.push_back(std::get<1>(pair)->getOperand(0)); + // rewriter.eraseOp(std::get<1>(pair)); + } + OpBuilder::InsertionGuard g(rewriter); + rewriter.setInsertionPoint(mergedTerminator); + rewriter.create(mergedTerminator.getLoc(), newOperands); + }; + + auto mergedYieldOp = cast(newBlock->getTerminator()); + cloneFilteredTerminator(mergedYieldOp); + rewriter.eraseOp(mergedYieldOp); + + // prepare for new yielded value for 'replaceOp'. + SmallVector newYieldedRes; + SmallVector newRes(newForOp.getResults()); + int additionalRes = + newForOp.getResults().size() - forOp.getResults().size(); + assert(additionalRes >= 0 && "must be >= 0"); + newRes.insert(newRes.end(), newRes.begin(), newRes.end() - additionalRes); + + // propagate results new forOp to downstream loads if any, + // otherwise insert a store right after the for. The stored + // element is the result of the for. + assert(candidateOpsInFor.size() == loadsInFor.size()); + i = 0; + for (auto pair : candidateOpsInFor) { + auto store = cast(std::get<1>(pair)); + + auto loads = loadsInFor[i]; + for (auto load : loads) + load->getResult(0).replaceAllUsesWith(store.getOperand(0)); + + rewriter.setInsertionPointAfter(newForOp); + rewriter.create( + newForOp.getLoc(), + newForOp.getResults()[forOp.getResults().size() + i], + store.getMemRef(), store.getAffineMap(), store.indices()); + rewriter.eraseOp(std::get<1>(pair)); + ++i; + } + + rewriter.replaceOp(forOp, newYieldedRes); + return success(); + } +}; + +} // end namespace. + +void AffineReductionPass::runOnFunction() { + // getFunction().dump(); + { + mlir::RewritePatternSet rpl(getFunction().getContext()); + rpl.add(getFunction().getContext()); + GreedyRewriteConfig config; + applyPatternsAndFoldGreedily(getFunction().getOperation(), std::move(rpl), + config); + } + // getFunction().dump(); +} + +namespace mlir { + namespace polygeist { + std::unique_ptr> detectReductionPass() { + return std::make_unique(); + } + } +} \ No newline at end of file diff --git a/lib/polygeist/Passes/CMakeLists.txt b/lib/polygeist/Passes/CMakeLists.txt new file mode 100644 index 000000000000..4fb8f4fd3c29 --- /dev/null +++ b/lib/polygeist/Passes/CMakeLists.txt @@ -0,0 +1,28 @@ +add_mlir_dialect_library(MLIRPolygeistTransforms + AffineCFG.cpp + AffineReduction.cpp + CanonicalizeFor.cpp + LoopRestructure.cpp + Mem2Reg.cpp + ParallelLoopDistribute.cpp + RaiseToAffine.cpp + ParallelLower.cpp + + + ADDITIONAL_HEADER_DIRS + ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/Affine + + DEPENDS + MLIRPolygeistOpsIncGen + MLIRPolygeistPassIncGen + + LINK_LIBS PUBLIC + MLIRAffine + MLIRAffineUtils + MLIRIR + MLIRMemRef + MLIRPass + MLIRSideEffectInterfaces + MLIRStandard + MLIRTransformUtils + ) \ No newline at end of file diff --git a/lib/polygeist/Passes/CanonicalizeFor.cpp b/lib/polygeist/Passes/CanonicalizeFor.cpp new file mode 100644 index 000000000000..bc1f42489d97 --- /dev/null +++ b/lib/polygeist/Passes/CanonicalizeFor.cpp @@ -0,0 +1,133 @@ +#include "mlir/Dialect/SCF/Passes.h" +#include "mlir/Dialect/SCF/SCF.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "polygeist/Passes/Passes.h" + +using namespace mlir; + +namespace { +struct CanonicalizeFor : public SCFCanonicalizeForBase { + void runOnFunction() override; +}; +} // namespace + +static void propagateInLoopBody(scf::ForOp forOp) { + + if (!forOp.hasIterOperands()) + return; + + Block &block = forOp.getRegion().front(); + auto yieldOp = cast(block.getTerminator()); + + for (auto it : llvm::zip(forOp.getIterOperands(), forOp.getRegionIterArgs(), + yieldOp.getOperands())) { + Value iterOperand = std::get<0>(it); + Value regionArg = std::get<1>(it); + Value yieldOperand = std::get<2>(it); + + Operation *op = iterOperand.getDefiningOp(); + if (op && (op->getNumResults() == 1) && (iterOperand == yieldOperand)) + regionArg.replaceAllUsesWith(op->getResult(0)); + } +} + +static bool hasSameInitValue(Value iter, scf::ForOp forOp) { + Operation *cst = iter.getDefiningOp(); + if (!cst) + return false; + if (auto cstOp = dyn_cast(cst)) { + Attribute attr = cstOp.getValue(); + if (auto intAttr = attr.cast()) { + Operation *lbDefOp = forOp.lowerBound().getDefiningOp(); + if (!lbDefOp) + return false; + ConstantIndexOp lb = dyn_cast_or_null(lbDefOp); + if (lb && lb.getValue() == intAttr.getInt()) + return true; + } + } + return false; +} + +static bool hasSameStepValue(Value regIter, Value yieldOp, scf::ForOp forOp) { + auto addOp = cast(yieldOp.getDefiningOp()); + Value addStep = addOp.getOperand(1); + Operation *defOpStep = addStep.getDefiningOp(); + if (!defOpStep) + return false; + if (auto cstStep = dyn_cast(defOpStep)) { + Attribute attr = cstStep.getValue(); + if (auto intAttr = attr.cast()) { + Operation *stepForDefOp = forOp.step().getDefiningOp(); + if (!stepForDefOp) + return false; + ConstantIndexOp stepFor = dyn_cast_or_null(stepForDefOp); + if (stepFor && stepFor.getValue() == intAttr.getInt()) + return true; + } + } + return false; +} + +static bool preconditionIndVar(Value regIter, Value yieldOp, scf::ForOp forOp) { + Operation *mustBeAdd = yieldOp.getDefiningOp(); + if (!mustBeAdd || !isa(mustBeAdd)) + return false; + auto addOp = cast(mustBeAdd); + if (addOp.getOperand(0) != regIter) + return false; + // check users. We allow only index cast and 'addOp`. + for (auto u : regIter.getUsers()) { + if (isa(u) || (u == addOp.getOperation())) + continue; + return false; + } + // the user of the add should be a yieldop. + Value res = addOp.getResult(); + for (auto u : res.getUsers()) + if (!isa(u)) + return false; + + return true; +} + +static bool isIndVar(Value iter, Value regIter, Value yieldOp, + scf::ForOp forOp) { + if (!preconditionIndVar(regIter, yieldOp, forOp)) + return false; + if (!hasSameInitValue(iter, forOp)) + return false; + if (!hasSameStepValue(regIter, yieldOp, forOp)) + return false; + return true; +} + +static void detectTrivialIndVarInArgs(scf::ForOp forOp) { + + if (!forOp.getNumIterOperands()) + return; + + Block &block = forOp.region().front(); + auto yieldOp = cast(block.getTerminator()); + + for (auto it : llvm::zip(forOp.getIterOperands(), forOp.getRegionIterArgs(), + yieldOp.getOperands())) { + if (isIndVar(std::get<0>(it), std::get<1>(it), std::get<2>(it), forOp)) { + OpBuilder builder(forOp); + builder.setInsertionPointToStart(forOp.getBody()); + auto indexCast = builder.create( + forOp.getLoc(), forOp.getInductionVar(), builder.getI32Type()); + std::get<1>(it).replaceAllUsesWith(indexCast); + } + } +} + +void CanonicalizeFor::runOnFunction() { + getFunction().walk([&](scf::ForOp forOp) { propagateInLoopBody(forOp); }); + getFunction().walk( + [&](scf::ForOp forOp) { detectTrivialIndVarInArgs(forOp); }); +} + +std::unique_ptr mlir::polygeist::createCanonicalizeForPass() { + return std::make_unique(); +} \ No newline at end of file diff --git a/lib/polygeist/Passes/LoopRestructure.cpp b/lib/polygeist/Passes/LoopRestructure.cpp new file mode 100644 index 000000000000..5f184f775646 --- /dev/null +++ b/lib/polygeist/Passes/LoopRestructure.cpp @@ -0,0 +1,642 @@ +//===- LoopRestructure.cpp - Find natural Loops ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// +// TODO fix uses of induction or inner variables outside of loop +/* +see %2 in +func @kernel_gemm(%arg0: i32, %arg1: memref) { + %c0 = constant 0 : index + %c0_i32 = constant 0 : i32 + %c0_i64 = constant 0 : i64 + %c1_i32 = constant 1 : i32 + %c1_i64 = constant 1 : i64 + %c32_i32 = constant 32 : i32 + %cst = constant 1.000000e+00 : f64 + br ^bb1(%c0_i64 : i64) +^bb1(%0: i64): // 2 preds: ^bb0, ^bb2 + %1 = subi %arg0, %c1_i32 : i32 + %2 = cmpi "slt", %1, %c0_i32 : i32 + %3 = scf.if %2 -> (i32) { + %14 = subi %c0_i32, %1 : i32 + %15 = addi %14, %c32_i32 : i32 + %16 = subi %15, %c1_i32 : i32 + %17 = divi_signed %16, %c32_i32 : i32 + %18 = subi %c0_i32, %17 : i32 + scf.yield %18 : i32 + } else { + %14 = divi_signed %1, %c32_i32 : i32 + scf.yield %14 : i32 + } + %4 = sexti %3 : i32 to i64 + %5 = cmpi "sle", %0, %4 : i64 + cond_br %5, ^bb2, ^bb3 +^bb2: // pred: ^bb1 + %6 = load %arg1[%c0] : memref + %7 = mulf %6, %cst : f64 + store %7, %arg1[%c0] : memref + %8 = addi %0, %c1_i64 : i64 + br ^bb1(%8 : i64) +^bb3: // pred: ^bb1 + %9 = scf.if %2 -> (i32) { + %14 = subi %c0_i32, %1 : i32 + %15 = addi %14, %c32_i32 : i32 + %16 = subi %15, %c1_i32 : i32 + %17 = divi_signed %16, %c32_i32 : i32 + %18 = subi %c0_i32, %17 : i32 + scf.yield %18 : i32 + } else { + %14 = divi_signed %1, %c32_i32 : i32 + scf.yield %14 : i32 + } + %10 = sexti %9 : i32 to i64 + %11 = index_cast %10 : i64 to index + %12 = load %arg1[%11] : memref + %13 = addf %12, %cst : f64 + store %13, %arg1[%11] : memref + return +} +*/ +#include "polygeist/Passes/Passes.h" + +#include "mlir/Transforms/Passes.h" + +#include "mlir/IR/Builders.h" +#include "mlir/IR/Dominance.h" +#include "mlir/Pass/Pass.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/IR/Dominators.h" + +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/SCF/SCF.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/IR/RegionGraphTraits.h" +#include "mlir/Transforms/Passes.h" + + +using namespace mlir; + +#define DEBUG_TYPE "LoopRestructure" + +struct Wrapper; + +struct RWrapper { + RWrapper(int x) {}; + Wrapper& front(); +}; + +struct Wrapper { + mlir::Block blk; + Wrapper() = delete; + Wrapper(Wrapper& w) = delete; + bool isLegalToHoistInto() const { + return true; + } + void print(llvm::raw_ostream& OS) const { + //B->print(OS); + } + void printAsOperand(llvm::raw_ostream& OS, bool b) const { + //B->print(OS, b); + } + RWrapper* getParent() const { + Region* R = ((Block*)(this))->getParent(); + return (RWrapper*)R; + } + mlir::Block& operator*() const { + return *(Block*)(this); + } + mlir::Block* operator->() const { + return (Block*)(this); + } +}; + +Wrapper& RWrapper::front() { + return *(Wrapper*)&((Region*)this)->front(); +} + +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Wrapper& w) { + return os << ""; +} + +template +struct Iter : public std::iterator< + std::input_iterator_tag, // iterator_category + Wrapper* > { + T it; + Iter(T it) : it(it) {} + Wrapper* operator*() const { + Block* B = *it; + return (Wrapper*)B; + } + bool operator!=(Iter I) const { + return it != I.it; + } + bool operator==(Iter I) const { + return it == I.it; + } + void operator++() { + ++it; + } + Iter operator++(int) { + auto prev = *this; + it++; + return prev; + } +}; + +namespace llvm { +template <> +struct GraphTraits { + using ChildIteratorType = Iter; + using Node = const Wrapper; + using NodeRef = Node *; + + static NodeRef getEntryNode(NodeRef bb) { return bb; } + + static ChildIteratorType child_begin(NodeRef node) { + return (*node)->succ_begin(); + } + static ChildIteratorType child_end(NodeRef node) { + return (*node)->succ_end(); + } +}; +template <> +struct GraphTraits { + using ChildIteratorType = Iter; + using Node = Wrapper; + using NodeRef = Node *; + + static NodeRef getEntryNode(NodeRef bb) { return bb; } + + static ChildIteratorType child_begin(NodeRef node) { + return (*node)->succ_begin(); + } + static ChildIteratorType child_end(NodeRef node) { + return (*node)->succ_end(); + } +}; + +template <> +struct GraphTraits> { + using ChildIteratorType = Iter; + using Node = Wrapper; + using NodeRef = Node *; + + static ChildIteratorType child_begin(NodeRef node) { + return (*node)->pred_begin(); + } + static ChildIteratorType child_end(NodeRef node) { + return (*node)->pred_end(); + } +}; +template <> +struct GraphTraits> { + using ChildIteratorType = Iter; + using Node = const Wrapper; + using NodeRef = Node *; + + static ChildIteratorType child_begin(NodeRef node) { + return (*node)->pred_begin(); + } + static ChildIteratorType child_end(NodeRef node) { + return (*node)->pred_end(); + } +}; + + template <> + struct GraphTraits *> + : public DomTreeGraphTraitsBase, + DomTreeNodeBase::const_iterator> {}; + +} + +namespace { + +struct LoopRestructure : public mlir::LoopRestructureBase { + void runOnRegion(DominanceInfo &domInfo, Region ®ion); + bool removeIfFromRegion(DominanceInfo &domInfo, Region ®ion, + Block *pseudoExit); + void runOnFunction() override; +}; + +} // end anonymous namespace + +// Instantiate a variant of LLVM LoopInfo that works on mlir::Block +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/LoopInfoImpl.h" + +template class llvm::DominatorTreeBase; +template class llvm::DomTreeNodeBase; + +namespace mlir { +class Loop : public llvm::LoopBase { +private: + Loop() = default; + friend class llvm::LoopBase; + friend class llvm::LoopInfoBase; + explicit Loop(Wrapper *B) : llvm::LoopBase(B) {} + ~Loop() = default; +}; +class LoopInfo : public llvm::LoopInfoBase { +public: + LoopInfo(const llvm::DominatorTreeBase &DomTree) { + analyze(DomTree); + } +}; +} // namespace mlir + +template class llvm::LoopBase; +template class llvm::LoopInfoBase; + + +void LoopRestructure::runOnFunction() { + //FuncOp f = getFunction(); + DominanceInfo &domInfo = getAnalysis(); + if (auto region = getOperation().getCallableRegion()) { + runOnRegion(domInfo, *region); + } +} + +bool attemptToFoldIntoPredecessor(Block *target) { + SmallVector P(target->pred_begin(), target->pred_end()); + if (P.size() == 1) { + if (auto op = dyn_cast(P[0]->getTerminator())) { + assert(target->getNumArguments() == op.getNumOperands()); + for (size_t i = 0; i < target->getNumArguments(); ++i) { + target->getArgument(i).replaceAllUsesWith(op.getOperand(i)); + } + P[0]->getOperations().splice(P[0]->getOperations().end(), + target->getOperations()); + op->erase(); + target->erase(); + return true; + } + } else if (P.size() == 2) { + if (auto op = dyn_cast(P[0]->getTerminator())) { + assert(target->getNumArguments() == op.getNumTrueOperands()); + assert(target->getNumArguments() == op.getNumFalseOperands()); + + mlir::OpBuilder builder(op); + SmallVector types; + for(auto T : op.getTrueOperands()) { + types.push_back(T.getType()); + } + + for (size_t i = 0; i < target->getNumArguments(); ++i) { + auto sel = builder.create(op.getLoc(), op.getCondition(), op.getTrueOperand(i), op.getFalseOperand(i)); + target->getArgument(i).replaceAllUsesWith(sel); + } + P[0]->getOperations().splice(P[0]->getOperations().end(), + target->getOperations()); + op->erase(); + target->erase(); + return true; + } + } + return false; +} + +bool LoopRestructure::removeIfFromRegion(DominanceInfo &domInfo, Region ®ion, + Block *pseudoExit) { + SmallVector Preds; + for (auto block : pseudoExit->getPredecessors()) { + Preds.push_back(block); + } + SmallVector emptyTys; + SmallVector condTys; + for (auto a : pseudoExit->getArguments()) { + condTys.push_back(a.getType()); + } + if (Preds.size() == 2) { + for (size_t i = 0; i < Preds.size(); ++i) { + SmallVector Succs; + for (auto block : Preds[i]->getSuccessors()) { + Succs.push_back(block); + } + if (Succs.size() == 2) { + for (size_t j = 0; j < Succs.size(); ++j) { + if (Succs[j] == pseudoExit && Succs[1 - j] == Preds[1 - i]) { + OpBuilder builder(Preds[i]->getTerminator()); + auto condBr = cast(Preds[i]->getTerminator()); + auto ifOp = builder.create( + builder.getUnknownLoc(), condTys, condBr.getCondition(), + /*hasElse*/ true); + Succs[j] = new Block(); + if (j == 0) { + ifOp.elseRegion().getBlocks().splice( + ifOp.elseRegion().getBlocks().end(), region.getBlocks(), + Succs[1 - j]); + SmallVector idx; + for (size_t i = 0; i < Succs[1 - j]->getNumArguments(); ++i) { + Succs[1 - j]->getArgument(i).replaceAllUsesWith( + condBr.getFalseOperand(i)); + idx.push_back(i); + } + Succs[1 - j]->eraseArguments(idx); + assert(!ifOp.elseRegion().getBlocks().empty()); + assert(condTys.size() == condBr.getTrueOperands().size()); + OpBuilder tbuilder(&ifOp.thenRegion().front(), + ifOp.thenRegion().front().begin()); + tbuilder.create(tbuilder.getUnknownLoc(), emptyTys, + condBr.getTrueOperands()); + } else { + if (!ifOp.thenRegion().getBlocks().empty()) { + ifOp.thenRegion().front().erase(); + } + ifOp.thenRegion().getBlocks().splice( + ifOp.thenRegion().getBlocks().end(), region.getBlocks(), + Succs[1 - j]); + SmallVector idx; + for (size_t i = 0; i < Succs[1 - j]->getNumArguments(); ++i) { + Succs[1 - j]->getArgument(i).replaceAllUsesWith( + condBr.getTrueOperand(i)); + idx.push_back(i); + } + Succs[1 - j]->eraseArguments(idx); + assert(!ifOp.elseRegion().getBlocks().empty()); + OpBuilder tbuilder(&ifOp.elseRegion().front(), + ifOp.elseRegion().front().begin()); + assert(condTys.size() == condBr.getFalseOperands().size()); + tbuilder.create(tbuilder.getUnknownLoc(), emptyTys, + condBr.getFalseOperands()); + } + auto oldTerm = Succs[1 - j]->getTerminator(); + OpBuilder tbuilder(Succs[1 - j], Succs[1 - j]->end()); + tbuilder.create(tbuilder.getUnknownLoc(), emptyTys, + oldTerm->getOperands()); + oldTerm->erase(); + + SmallVector res; + for (size_t i = 1; i < ifOp->getNumResults(); ++i) { + res.push_back(ifOp->getResult(i)); + } + builder.create(builder.getUnknownLoc(), + ifOp->getResult(0), res); + condBr->erase(); + + pseudoExit->erase(); + return true; + } + } + } + } + } + return false; +} + +void LoopRestructure::runOnRegion(DominanceInfo &domInfo, Region ®ion) { + if (region.getBlocks().size() > 1) { + const llvm::DominatorTreeBase* DT = &domInfo.getDomTree(®ion); + mlir::LoopInfo LI(*(const llvm::DominatorTreeBase*)DT); + for (auto L : LI.getTopLevelLoops()) { + Block *header = (Block*)L->getHeader(); + Block *target = (Block*)L->getUniqueExitBlock(); + if (!target) { + // Only support one exit block + llvm::errs() << " found mlir loop with more than one exit, skipping. \n"; + continue; + } + + // Replace branch to exit block with a new block that calls + // loop.natural.return In caller block, branch to correct exit block + SmallVector exitingBlocks; + L->getExitingBlocks(exitingBlocks); + + // TODO: Support multiple exit blocks + // - Easy case all exit blocks have the same argument set + + // Create a caller block that will contain the loop op + + Block *wrapper = new Block(); + region.push_back(wrapper); + mlir::OpBuilder builder(wrapper, wrapper->begin()); + + // Copy the arguments across + SmallVector headerArgumentTypes; + for (auto arg : header->getArguments()) { + headerArgumentTypes.push_back(arg.getType()); + } + // TODO values used outside loop should be wrapped. + wrapper->addArguments(headerArgumentTypes); + + SmallVector combinedTypes(headerArgumentTypes.begin(), + headerArgumentTypes.end()); + SmallVector returns; + for (auto arg : target->getArguments()) { + returns.push_back(arg.getType()); + combinedTypes.push_back(arg.getType()); + } + + auto loop = builder.create( + builder.getUnknownLoc(), combinedTypes, wrapper->getArguments()); + { + SmallVector RetVals; + for (size_t i = 0; i < returns.size(); ++i) { + RetVals.push_back(loop.getResult(i + headerArgumentTypes.size())); + } + builder.create(builder.getUnknownLoc(), target, RetVals); + } + + SmallVector Preds; + + for (auto block : header->getPredecessors()) { + if (!L->contains((Wrapper*)block)) + Preds.push_back(block); + } + + loop.before().getBlocks().splice(loop.before().getBlocks().begin(), + region.getBlocks(), header); + for (auto *w : L->getBlocks()) { + Block *b = &**w; + if (b != header) { + loop.before().getBlocks().splice(loop.before().getBlocks().end(), + region.getBlocks(), b); + } + } + + + Block *pseudoExit = new Block(); + auto i1Ty = builder.getI1Type(); + { + loop.before().push_back(pseudoExit); + SmallVector tys = {i1Ty}; + for (auto t : combinedTypes) + tys.push_back(t); + pseudoExit->addArguments(tys); + OpBuilder builder(pseudoExit, pseudoExit->begin()); + tys.clear(); + builder.create(builder.getUnknownLoc(), tys, + pseudoExit->getArguments()); + } + + for (auto *w : exitingBlocks) { + Block *block = &**w; + Operation *terminator = block->getTerminator(); + for (unsigned i = 0; i < terminator->getNumSuccessors(); ++i) { + Block *successor = terminator->getSuccessor(i); + if (successor == target) { + + OpBuilder builder(terminator); + auto vfalse = builder.create( + builder.getUnknownLoc(), i1Ty, builder.getIntegerAttr(i1Ty, 0)); + + std::vector args = {vfalse}; + for (auto arg : header->getArguments()) + args.push_back(arg); + + if (auto op = dyn_cast(terminator)) { + args.insert(args.end(), op.getOperands().begin(), + op.getOperands().end()); + builder.create(op.getLoc(), pseudoExit, args); + op.erase(); + } + if (auto op = dyn_cast(terminator)) { + std::vector trueargs(op.getTrueOperands().begin(), + op.getTrueOperands().end()); + std::vector falseargs(op.getFalseOperands().begin(), + op.getFalseOperands().end()); + if (op.getTrueDest() == target) { + trueargs.insert(trueargs.begin(), args.begin(), args.end()); + } + if (op.getFalseDest() == target) { + falseargs.insert(falseargs.begin(), args.begin(), args.end()); + } + builder.create( + op.getLoc(), op.getCondition(), + op.getTrueDest() == target ? pseudoExit : op.getTrueDest(), + trueargs, + op.getFalseDest() == target ? pseudoExit : op.getFalseDest(), + falseargs); + op.erase(); + } + } + } + } + + // For each back edge create a new block and replace + // the destination of that edge with said new block + // in that new block call loop.natural.next + SmallVector loopLatches; + L->getLoopLatches(loopLatches); + for (auto* w : loopLatches) { + Block* block = &**w; + Operation *terminator = block->getTerminator(); + for (unsigned i = 0; i < terminator->getNumSuccessors(); ++i) { + Block *successor = terminator->getSuccessor(i); + if (successor == header) { + + OpBuilder builder(terminator); + auto vtrue = builder.create( + builder.getUnknownLoc(), i1Ty, builder.getIntegerAttr(i1Ty, 1)); + + if (auto op = dyn_cast(terminator)) { + std::vector args(op.getOperands().begin(), + op.getOperands().end()); + args.insert(args.begin(), vtrue); + for (auto ty : returns) { + // args.push_back(builder.create(builder.getUnknownLoc(), + // ty)); + args.push_back(builder.create( + builder.getUnknownLoc(), ty, builder.getIntegerAttr(ty, 0))); + } + builder.create(op.getLoc(), pseudoExit, args); + op.erase(); + } + if (auto op = dyn_cast(terminator)) { + std::vector trueargs(op.getTrueOperands().begin(), + op.getTrueOperands().end()); + std::vector falseargs(op.getFalseOperands().begin(), + op.getFalseOperands().end()); + if (op.getTrueDest() == header) { + trueargs.insert(trueargs.begin(), vtrue); + for (auto ty : returns) { + trueargs.push_back(builder.create( + builder.getUnknownLoc(), ty)); + } + } + if (op.getFalseDest() == header) { + falseargs.insert(falseargs.begin(), vtrue); + for (auto ty : returns) { + falseargs.push_back(builder.create( + builder.getUnknownLoc(), ty)); + } + } + builder.create( + op.getLoc(), op.getCondition(), + op.getTrueDest() == header ? pseudoExit : op.getTrueDest(), + trueargs, + op.getFalseDest() == header ? pseudoExit : op.getFalseDest(), + falseargs); + op.erase(); + } + } + } + } + + Block *after = new Block(); + after->addArguments(combinedTypes); + loop.after().push_back(after); + OpBuilder builder2(after, after->begin()); + SmallVector yieldargs; + for (auto a : after->getArguments()) { + if (yieldargs.size() == headerArgumentTypes.size()) + break; + yieldargs.push_back(a); + } + + for (auto block : Preds) { + Operation *terminator = block->getTerminator(); + for (unsigned i = 0; i < terminator->getNumSuccessors(); ++i) { + Block *successor = terminator->getSuccessor(i); + if (successor == header) { + terminator->setSuccessor(wrapper, i); + } + } + } + + for (size_t i = 0; i < header->getNumArguments(); i++) { + header->getArgument(i).replaceUsesWithIf( + loop->getResult(i), [&](OpOperand &u) -> bool { + return !loop.getOperation()->isProperAncestor(u.getOwner()); + }); + } + + builder2.create(builder.getUnknownLoc(), yieldargs); + domInfo.invalidate(&loop.before()); + runOnRegion(domInfo, loop.before()); + if (!removeIfFromRegion(domInfo, loop.before(), pseudoExit)) { + attemptToFoldIntoPredecessor(pseudoExit); + } + + attemptToFoldIntoPredecessor(wrapper); + attemptToFoldIntoPredecessor(target); + assert(loop.before().getBlocks().size() == 1); + runOnRegion(domInfo, loop.after()); + assert(loop.after().getBlocks().size() == 1); + } + } + + for (auto &blk : region) { + for (auto &op : blk ) { + for (auto ® : op.getRegions()) { + domInfo.invalidate(®); + runOnRegion(domInfo, reg); + } + } + } +} + +namespace mlir { + namespace polygeist { + std::unique_ptr> createLoopRestructurePass() { + return std::make_unique(); + } + } +} \ No newline at end of file diff --git a/lib/polygeist/Passes/Mem2Reg.cpp b/lib/polygeist/Passes/Mem2Reg.cpp new file mode 100644 index 000000000000..e263f892fb07 --- /dev/null +++ b/lib/polygeist/Passes/Mem2Reg.cpp @@ -0,0 +1,1169 @@ +//===- Mem2Reg.cpp - MemRef DataFlow Optimization pass ------ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a pass to forward memref stores to loads, thereby +// potentially getting rid of intermediate memref's entirely. +// TODO: In the future, similar techniques could be used to eliminate +// dead memref store's and perform more complex forwarding when support for +// SSA scalars live out of 'affine.for'/'affine.if' statements is available. +//===----------------------------------------------------------------------===// + +#include "polygeist/Passes/Passes.h" +#include "mlir/Analysis/AffineAnalysis.h" +#include "mlir/Analysis/Utils.h" +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/SCF/SCF.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/IR/Dominance.h" +#include "mlir/Transforms/Passes.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include + +#include "polygeist/Ops.h" + +#define DEBUG_TYPE "mem2reg" + +using namespace mlir; +#include + +typedef std::set> StoreMap; + +namespace { +// The store to load forwarding relies on three conditions: +// +// 1) they need to have mathematically equivalent affine access functions +// (checked after full composition of load/store operands); this implies that +// they access the same single memref element for all iterations of the common +// surrounding loop, +// +// 2) the store op should dominate the load op, +// +// 3) among all op's that satisfy both (1) and (2), the one that postdominates +// all store op's that have a dependence into the load, is provably the last +// writer to the particular memref location being loaded at the load op, and its +// store value can be forwarded to the load. Note that the only dependences +// that are to be considered are those that are satisfied at the block* of the +// innermost common surrounding loop of the being considered. +// +// (* A dependence being satisfied at a block: a dependence that is satisfied by +// virtue of the destination operation appearing textually / lexically after +// the source operation within the body of a 'affine.for' operation; thus, a +// dependence is always either satisfied by a loop or by a block). +// +// The above conditions are simple to check, sufficient, and powerful for most +// cases in practice - they are sufficient, but not necessary --- since they +// don't reason about loops that are guaranteed to execute at least once or +// multiple sources to forward from. +// +// TODO: more forwarding can be done when support for +// loop/conditional live-out SSA values is available. +// TODO: do general dead store elimination for memref's. This pass +// currently only eliminates the stores only if no other loads/uses (other +// than dealloc) remain. +// +struct Mem2Reg : public Mem2RegBase { + void runOnFunction() override; + + // return if changed + bool forwardStoreToLoad(mlir::Value AI, std::vector idx, + SmallVectorImpl &loadOpsToErase); +}; + +} // end anonymous namespace + +/// Creates a pass to perform optimizations relying on memref dataflow such as +/// store to load forwarding, elimination of dead stores, and dead allocs. +std::unique_ptr> mlir::polygeist::createMem2RegPass() { + return std::make_unique(); +} + +bool matchesIndices(mlir::OperandRange ops, const std::vector &idx) { + if (ops.size() != idx.size()) + return false; + for (size_t i = 0; i < idx.size(); i++) { + if (auto op = ops[i].getDefiningOp()) { + if (op.getValue().cast().getInt() != idx[i]) { + return false; + } + } else if (auto op = ops[i].getDefiningOp()) { + if (op.getValue() != idx[i]) { + return false; + } + } else { + assert(0 && "unhandled op"); + } + } + return true; +} + +bool matchesIndices(ArrayRef ops, const std::vector &idx) { + if (ops.size() != idx.size()) + return false; + for (size_t i = 0; i < idx.size(); i++) { + if (auto op = ops[i].dyn_cast()) { + if (op.getValue() != idx[i]) + return false; + } else { + assert(0 && "unhandled op"); + } + } + return true; +} + +struct Analyzer { + + const std::set &Good; + const std::set &Bad; + const std::set &Other; + std::set Legal; + std::set Illegal; + size_t depth; + Analyzer(const std::set &Good, const std::set &Bad, + const std::set &Other, std::set Legal, + std::set Illegal, size_t depth = 0) + : Good(Good), Bad(Bad), Other(Other), Legal(Legal), Illegal(Illegal), + depth(depth) {} + + void analyze() { + while (1) { + std::deque todo(Other.begin(), Other.end()); + todo.insert(todo.end(), Good.begin(), Good.end()); + while (todo.size()) { + auto block = todo.front(); + todo.pop_front(); + if (Legal.count(block) || Illegal.count(block)) + continue; + + bool currentlyLegal = !block->hasNoPredecessors(); + for (auto pred : block->getPredecessors()) { + if (Bad.count(pred)) { + assert(!Legal.count(block)); + Illegal.insert(block); + currentlyLegal = false; + for (auto succ : block->getSuccessors()) { + todo.push_back(succ); + } + break; + } else if (Good.count(pred) || Legal.count(pred)) { + continue; + } else if (Illegal.count(pred)) { + Illegal.insert(block); + currentlyLegal = false; + for (auto succ : block->getSuccessors()) { + todo.push_back(succ); + } + break; + } else { + /* + if (!Other.count(pred)) { + pred->getParentOp()->dump(); + pred->dump(); + llvm::errs() << " - pred ptr: " << pred << "\n"; + } + assert(Other.count(pred)); + */ + currentlyLegal = false; + break; + } + } + if (currentlyLegal) { + Legal.insert(block); + assert(!Illegal.count(block)); + for (auto succ : block->getSuccessors()) { + todo.push_back(succ); + } + } + } + bool changed = false; + for (auto O : Other) { + if (Legal.count(O) || Illegal.count(O)) + continue; + Analyzer AssumeLegal(Good, Bad, Other, Legal, Illegal, depth + 1); + AssumeLegal.Legal.insert(O); + AssumeLegal.analyze(); + bool currentlyLegal = true; + for (auto pred : O->getPredecessors()) { + if (!AssumeLegal.Legal.count(pred) && !AssumeLegal.Good.count(pred)) { + currentlyLegal = false; + break; + } + } + if (currentlyLegal) { + Legal.insert(O); + assert(!Illegal.count(O)); + changed = true; + break; + } else { + Illegal.insert(O); + assert(!Legal.count(O)); + } + } + if (!changed) + break; + } + } +}; + +// This is a straightforward implementation not optimized for speed. Optimize +// if needed. +bool Mem2Reg::forwardStoreToLoad(mlir::Value AI, std::vector idx, + SmallVectorImpl &loadOpsToErase) { + bool changed = false; + std::set loadOps; + mlir::Type subType = nullptr; + std::set allStoreOps; + + std::deque list = {AI}; + + SmallPtrSet AliasingStoreOperations; + + while (list.size()) { + auto val = list.front(); + list.pop_front(); + for (auto *user : val.getUsers()) { + if (auto co = dyn_cast(user)) { + list.push_back(co); + continue; + } + if (auto co = dyn_cast(user)) { + list.push_back(co); + continue; + } + if (auto loadOp = dyn_cast(user)) { + if (matchesIndices(loadOp.getIndices(), idx)) { + subType = loadOp.getType(); + loadOps.insert(loadOp); + LLVM_DEBUG(llvm::dbgs() << "Matching Load: " << loadOp << "\n"); + } + } + if (auto loadOp = dyn_cast(user)) { + if (matchesIndices(loadOp.getAffineMapAttr().getValue().getResults(), + idx)) { + subType = loadOp.getType(); + loadOps.insert(loadOp); + LLVM_DEBUG(llvm::dbgs() << "Matching Load: " << loadOp << "\n"); + } + } + if (auto storeOp = dyn_cast(user)) { + if (matchesIndices(storeOp.getIndices(), idx)) { + LLVM_DEBUG(llvm::dbgs() << "Matching Store: " << storeOp << "\n"); + allStoreOps.insert(storeOp); + } + } + if (auto storeOp = dyn_cast(user)) { + if (matchesIndices(storeOp.getAffineMapAttr().getValue().getResults(), + idx)) { + LLVM_DEBUG(llvm::dbgs() << "Matching Store: " << storeOp << "\n"); + allStoreOps.insert(storeOp); + } + } + if (auto callOp = dyn_cast(user)) { + if (callOp.callee() != "free") { + AliasingStoreOperations.insert(callOp); + } + } + } + } + + if (loadOps.size() == 0) { + return changed; + } + /* + // this is a valid optimization, however it should occur naturally + // from the logic to follow anyways + if (allStoreOps.size() == 1) { + auto store = *allStoreOps.begin(); + for(auto loadOp : loadOps) { + if (domInfo->dominates(store, loadOp)) { + loadOp.replaceAllUsesWith(store.getValueToStore()); + loadOpsToErase.push_back(loadOp); + } + } + return changed; + } + */ + + // List of operations which may store that are not storeops + SmallPtrSet StoringOperations; + SmallPtrSet StoringBlocks; + { + std::deque todo; + for (auto &pair : allStoreOps) + todo.push_back(pair->getBlock()); + for (auto op : AliasingStoreOperations) { + StoringOperations.insert(op); + todo.push_back(op->getBlock()); + } + while (todo.size()) { + auto block = todo.front(); + assert(block); + todo.pop_front(); + StoringBlocks.insert(block); + if (auto op = block->getParentOp()) { + StoringOperations.insert(op); + if (auto next = op->getBlock()) { + StoringBlocks.insert(next); + todo.push_back(next); + } + } + } + } + + llvm::SetVector storeBlocks; + for (auto &B : *AI.getDefiningOp()->getParentRegion()) { + storeBlocks.insert(&B); + }; + AI.getDefiningOp()->getParentRegion()->walk( + [&](Block *B) { storeBlocks.insert(B); }); + // Do not include entry to region + // storeBlocks.remove(&AI.getDefiningOp()->getParentRegion()->front()); + + // Last value stored in an individual block and the operation which stored it + std::map lastStoreInBlock; + + // Last value stored in an individual block and the operation which stored it + std::map valueAtStartOfBlock; + + // Start by setting lastStoreInBlock to the last store directly in that block + // Note that this may miss a store within a region of an operation in that + // block + std::function handleBlock = + [&](Block &block, mlir::Value lastVal) { + if (!storeBlocks.count(&block)) { + assert(lastStoreInBlock.find(&block) != lastStoreInBlock.end()); + return lastStoreInBlock[&block]; + } + storeBlocks.remove(&block); + bool seenSubStore = false; + SmallVector ops; + for (auto &a : block) { + ops.push_back(&a); + } + for (auto a : ops) { + if (StoringOperations.count(a)) { + if (auto ifOp = dyn_cast(a)) { + if (!lastVal) { + lastVal = nullptr; + continue; + OpBuilder B(ifOp.getContext()); + B.setInsertionPoint(ifOp); + SmallVector nidx; + for (auto i : idx) { + nidx.push_back( + B.create(ifOp.getLoc(), i)); + } + auto newLoad = + B.create(ifOp.getLoc(), AI, nidx); + loadOps.insert(newLoad); + lastVal = newLoad; + } + + valueAtStartOfBlock[&*ifOp.thenRegion().begin()] = lastVal; + mlir::Value thenVal = + handleBlock(*ifOp.thenRegion().begin(), lastVal); + // llvm::errs() << ifOp << " - AI " << AI << " " << (lastVal != + // nullptr) << " tv " << (thenVal != nullptr) << " else: " << + // ifOp.elseRegion().getBlocks().size() << "\n"; + + if (lastVal && ifOp.elseRegion().getBlocks().size()) + valueAtStartOfBlock[&*ifOp.elseRegion().begin()] = lastVal; + mlir::Value elseVal = + (ifOp.elseRegion().getBlocks().size()) + ? handleBlock(*ifOp.elseRegion().begin(), lastVal) + : lastVal; + // llvm::errs() << " +++ elseVal: " << (elseVal != nullptr) << + // "\n"; + if (thenVal == elseVal && thenVal != nullptr) { + lastVal = thenVal; + continue; + } + + if (thenVal != nullptr && elseVal != nullptr) { + OpBuilder B(ifOp.getContext()); + B.setInsertionPoint(ifOp); + SmallVector tys(ifOp.getResultTypes().begin(), + ifOp.getResultTypes().end()); + tys.push_back(thenVal.getType()); + auto nextIf = B.create( + ifOp.getLoc(), tys, ifOp.condition(), /*hasElse*/ true); + + Block &then = ifOp.thenRegion().back(); + SmallVector thenVals = + cast(then.back()).results(); + thenVals.push_back(thenVal); + nextIf.thenRegion().getBlocks().clear(); + nextIf.thenRegion().getBlocks().splice( + nextIf.thenRegion().getBlocks().begin(), + ifOp.thenRegion().getBlocks()); + cast( + nextIf.thenRegion().back().getTerminator()) + ->setOperands(thenVals); + + if (ifOp.elseRegion().getBlocks().size()) { + nextIf.elseRegion().getBlocks().clear(); + SmallVector elseVals = + cast(ifOp.elseRegion().back().back()) + .results(); + elseVals.push_back(elseVal); + nextIf.elseRegion().getBlocks().splice( + nextIf.elseRegion().getBlocks().begin(), + ifOp.elseRegion().getBlocks()); + cast( + nextIf.elseRegion().back().getTerminator()) + ->setOperands(elseVals); + } else { + B.setInsertionPoint(&nextIf.elseRegion().back(), + nextIf.elseRegion().back().begin()); + SmallVector elseVals; + elseVals.push_back(elseVal); + B.create(ifOp.getLoc(), elseVals); + } + + SmallVector resvals = (nextIf.results()); + lastVal = resvals.back(); + resvals.pop_back(); + ifOp.replaceAllUsesWith(resvals); + + StoringOperations.erase(ifOp); + StoringOperations.insert(nextIf); + ifOp.erase(); + continue; + } + } + lastVal = nullptr; + seenSubStore = true; + // llvm::errs() << "erased store due to: " << *a << "\n"; + } else if (auto loadOp = dyn_cast(a)) { + if (loadOps.count(loadOp)) { + if (lastVal) { + changed = true; + // llvm::errs() << "replacing " << loadOp << " with " << lastVal + // << + // "\n"; + if (loadOp.getType() != lastVal.getType()) { + llvm::errs() << loadOp << " - " << lastVal << "\n"; + } + assert(loadOp.getType() == lastVal.getType()); + loadOp.replaceAllUsesWith(lastVal); + for (auto &pair : lastStoreInBlock) { + if (pair.second == loadOp) + pair.second = lastVal; + } + for (auto &pair : valueAtStartOfBlock) { + if (pair.second == loadOp) + pair.second = lastVal; + } + // Record this to erase later. + loadOpsToErase.push_back(loadOp); + loadOps.erase(loadOp); + } else if (seenSubStore) { + // llvm::errs() << "no lastval found for: " << loadOp << "\n"; + loadOps.erase(loadOp); + lastVal = loadOp; + } else { + lastVal = loadOp; + } + } + } else if (auto loadOp = dyn_cast(a)) { + if (loadOps.count(loadOp)) { + if (lastVal) { + changed = true; + // llvm::errs() << "replacing " << loadOp << " with " << lastVal + // << + // "\n"; + if (loadOp.getType() != lastVal.getType()) { + llvm::errs() << loadOp << " - " << lastVal << "\n"; + } + assert(loadOp.getType() == lastVal.getType()); + loadOp.replaceAllUsesWith(lastVal); + for (auto &pair : lastStoreInBlock) { + if (pair.second == loadOp) + pair.second = lastVal; + } + for (auto &pair : valueAtStartOfBlock) { + if (pair.second == loadOp) + pair.second = lastVal; + } + // Record this to erase later. + loadOpsToErase.push_back(loadOp); + loadOps.erase(loadOp); + } else if (seenSubStore) { + // llvm::errs() << "no lastval found for: " << loadOp << "\n"; + loadOps.erase(loadOp); + lastVal = loadOp; + } else { + lastVal = loadOp; + } + } + } else if (auto storeOp = dyn_cast(a)) { + if (allStoreOps.count(storeOp)) { + lastVal = storeOp.getValueToStore(); + seenSubStore = false; + } + } else if (auto storeOp = dyn_cast(a)) { + if (allStoreOps.count(storeOp)) { + lastVal = storeOp.getValueToStore(); + seenSubStore = false; + } + } else { + // since not storing operation the value at the start and end of + // block is lastVal + a->walk([&](memref::LoadOp loadOp) { + if (loadOps.count(loadOp)) { + if (lastVal) { + changed = true; + if (loadOp.getType() != lastVal.getType()) { + llvm::errs() << loadOp << " - " << lastVal << "\n"; + } + assert(loadOp.getType() == lastVal.getType()); + loadOp.replaceAllUsesWith(lastVal); + for (auto &pair : lastStoreInBlock) { + if (pair.second == loadOp) + pair.second = lastVal; + } + for (auto &pair : valueAtStartOfBlock) { + if (pair.second == loadOp) + pair.second = lastVal; + } + // Record this to erase later. + loadOpsToErase.push_back(loadOp); + loadOps.erase(loadOp); + } else if (seenSubStore) { + // llvm::errs() << "ano lastval found for: " << loadOp << + // "\n"; + loadOps.erase(loadOp); + } + } + }); + a->walk([&](AffineLoadOp loadOp) { + if (loadOps.count(loadOp)) { + if (lastVal) { + changed = true; + if (loadOp.getType() != lastVal.getType()) { + llvm::errs() << loadOp << " - " << lastVal << "\n"; + } + assert(loadOp.getType() == lastVal.getType()); + loadOp.replaceAllUsesWith(lastVal); + for (auto &pair : lastStoreInBlock) { + if (pair.second == loadOp) + pair.second = lastVal; + } + for (auto &pair : valueAtStartOfBlock) { + if (pair.second == loadOp) + pair.second = lastVal; + } + // Record this to erase later. + loadOpsToErase.push_back(loadOp); + loadOps.erase(loadOp); + } else if (seenSubStore) { + // llvm::errs() << "ano lastval found for: " << loadOp << + // "\n"; + loadOps.erase(loadOp); + } + } + }); + } + } + return lastStoreInBlock[&block] = lastVal; + }; + + while (storeBlocks.size()) { + auto blk = storeBlocks.begin(); + handleBlock(**blk, nullptr); + } + + if (loadOps.size() == 0) + return changed; + + std::set Good; + std::set Bad; + std::set Other; + + for (auto &pair : lastStoreInBlock) { + if (pair.second != nullptr) { + Good.insert(pair.first); + // llvm::errs() << "\n"; + // pair.first->dump(); + // llvm::errs() << "\n"; + } else if (StoringBlocks.count(pair.first)) { + // llvm::errs() << "\n"; + // pair.first->dump(); + // llvm::errs() << "\n"; + Bad.insert(pair.first); + } + } + + { + std::deque todo; + for (auto B : Good) + for (auto succ : B->getSuccessors()) + todo.push_back(succ); + while (todo.size()) { + auto block = todo.front(); + todo.pop_front(); + if (Good.count(block) || Bad.count(block) || Other.count(block)) + continue; + if (StoringBlocks.count(block)) { + // llvm::errs() << "\n"; + // block->dump(); + // llvm::errs() << "\n"; + Bad.insert(block); + continue; + } + Other.insert(block); + // llvm::errs() << "\n"; + // block->dump(); + // llvm::errs() << "\n"; + if (isa(block->getTerminator())) { + for (auto succ : block->getSuccessors()) { + todo.push_back(succ); + } + } + } + } + + Analyzer A(Good, Bad, Other, {}, {}); + A.analyze(); + + SmallPtrSet blocksWithAddedArgs; + for (auto block : A.Legal) { + // llvm::errs() << "\n"; + // block->dump(); + // llvm::errs() << "\n"; + if (valueAtStartOfBlock.find(block) != valueAtStartOfBlock.end()) + continue; + auto arg = block->addArgument(subType); + valueAtStartOfBlock[block] = arg; + blocksWithAddedArgs.insert(block); + for (Operation &op : *block) { + if (!StoringOperations.count(&op)) { + op.walk([&](Block *blk) { + if (valueAtStartOfBlock.find(blk) == valueAtStartOfBlock.end()) { + valueAtStartOfBlock[blk] = arg; + if (lastStoreInBlock.find(blk) == lastStoreInBlock.end() || + StoringBlocks.count(blk) == 0) { + lastStoreInBlock[blk] = arg; + } + } + }); + } else + break; + } + if (lastStoreInBlock.find(block) == lastStoreInBlock.end() || + StoringBlocks.count(block) == 0) { + lastStoreInBlock[block] = arg; + } + } + + for (auto loadOp : loadOps) { + auto blk = loadOp->getBlock(); + if (valueAtStartOfBlock.find(blk) != valueAtStartOfBlock.end()) { + changed = true; + assert(loadOp->getResult(0).getType() == + valueAtStartOfBlock[blk].getType()); + loadOp->getResult(0).replaceAllUsesWith(valueAtStartOfBlock[blk]); + for (auto &pair : lastStoreInBlock) { + if (pair.second && pair.second.getDefiningOp() == loadOp) + pair.second = valueAtStartOfBlock[blk]; + } + for (auto &pair : valueAtStartOfBlock) { + if (pair.second && pair.second.getDefiningOp() == loadOp) + pair.second = valueAtStartOfBlock[blk]; + } + loadOpsToErase.push_back(loadOp); + } else { + // TODO inter-op + // llvm::errs() << "no value at start of block:\n"; + // loadOp.dump(); + } + } + + for (auto block : blocksWithAddedArgs) { + assert(valueAtStartOfBlock.find(block) != valueAtStartOfBlock.end()); + + auto maybeblockArg = valueAtStartOfBlock[block]; + auto blockArg = maybeblockArg.dyn_cast(); + assert(blockArg && blockArg.getOwner() == block); + + for (auto pred : llvm::make_early_inc_range(block->getPredecessors())) { + mlir::Value pval = lastStoreInBlock[pred]; + assert(pval); + assert(pred->getTerminator()); + + assert(blockArg.getOwner() == block); + if (auto op = dyn_cast(pred->getTerminator())) { + mlir::OpBuilder subbuilder(op.getOperation()); + std::vector args(op.getOperands().begin(), + op.getOperands().end()); + args.push_back(pval); + subbuilder.create(op.getLoc(), op.getDest(), args); + // op.replaceAllUsesWith(op2); + op.erase(); + } else if (auto op = dyn_cast(pred->getTerminator())) { + + mlir::OpBuilder subbuilder(op.getOperation()); + std::vector trueargs(op.getTrueOperands().begin(), + op.getTrueOperands().end()); + std::vector falseargs(op.getFalseOperands().begin(), + op.getFalseOperands().end()); + if (op.getTrueDest() == block) { + trueargs.push_back(pval); + } + if (op.getFalseDest() == block) { + falseargs.push_back(pval); + } + subbuilder.create( + op.getLoc(), op.getCondition(), op.getTrueDest(), trueargs, + op.getFalseDest(), falseargs); + // op.replaceAllUsesWith(op2); + op.erase(); + } else { + llvm_unreachable("unknown pred branch"); + } + } + } + + // Remove block arguments if possible + { + std::deque todo(blocksWithAddedArgs.begin(), + blocksWithAddedArgs.end()); + while (todo.size()) { + auto block = todo.front(); + todo.pop_front(); + if (!blocksWithAddedArgs.count(block)) + continue; + assert(valueAtStartOfBlock.find(block) != valueAtStartOfBlock.end()); + + auto maybeblockArg = valueAtStartOfBlock[block]; + auto blockArg = maybeblockArg.dyn_cast(); + assert(blockArg && blockArg.getOwner() == block); + assert(blockArg.getOwner() == block); + + mlir::Value val = nullptr; + bool legal = true; + for (auto pred : block->getPredecessors()) { + mlir::Value pval = nullptr; + + if (auto op = dyn_cast(pred->getTerminator())) { + pval = op.getOperands()[blockArg.getArgNumber()]; + if (pval.getType() != + AI.getType().cast().getElementType()) { + pval.getDefiningOp()->getParentRegion()->getParentOp()->dump(); + llvm::errs() << pval << " - " << AI << "\n"; + } + assert(pval.getType() == + AI.getType().cast().getElementType()); + if (pval == blockArg) + pval = nullptr; + } else if (auto op = dyn_cast(pred->getTerminator())) { + if (op.getTrueDest() == block) { + if (blockArg.getArgNumber() >= op.getTrueOperands().size()) { + block->dump(); + llvm::errs() << op << " ba: " << blockArg.getArgNumber() << "\n"; + } + assert(blockArg.getArgNumber() < op.getTrueOperands().size()); + pval = op.getTrueOperands()[blockArg.getArgNumber()]; + assert(pval.getType() == + AI.getType().cast().getElementType()); + if (pval == blockArg) + pval = nullptr; + } + if (op.getFalseDest() == block) { + assert(blockArg.getArgNumber() < op.getFalseOperands().size()); + auto pval2 = op.getFalseOperands()[blockArg.getArgNumber()]; + assert(pval2.getType() == + AI.getType().cast().getElementType()); + if (pval2 != blockArg) { + if (pval == nullptr) { + pval = pval2; + } else if (pval != pval2) { + legal = false; + break; + } + } + if (pval == blockArg) + pval = nullptr; + } + } else { + llvm::errs() << *pred->getParent()->getParentOp() << "\n"; + pred->dump(); + block->dump(); + assert(0 && "unknown branch"); + } + + assert(pval != blockArg); + if (val == nullptr) { + val = pval; + if (pval) + assert(val.getType() == + AI.getType().cast().getElementType()); + } else { + if (pval != nullptr && val != pval) { + legal = false; + break; + } + } + } + if (legal) + assert(val || block->hasNoPredecessors()); + + bool used = false; + for (auto U : blockArg.getUsers()) { + + if (auto op = dyn_cast(U)) { + size_t i = 0; + for (auto V : op.getOperands()) { + if (V == blockArg && + !(i == blockArg.getArgNumber() && op.getDest() == block)) { + used = true; + break; + } + } + if (used) + break; + } else if (auto op = dyn_cast(U)) { + size_t i = 0; + for (auto V : op.getTrueOperands()) { + if (V == blockArg && + !(i == blockArg.getArgNumber() && op.getTrueDest() == block)) { + used = true; + break; + } + } + if (used) + break; + i = 0; + for (auto V : op.getFalseOperands()) { + if (V == blockArg && + !(i == blockArg.getArgNumber() && op.getFalseDest() == block)) { + used = true; + break; + } + } + } else + used = true; + } + if (!used) { + legal = true; + } + + if (legal) { + for (auto U : blockArg.getUsers()) { + if (auto block = U->getBlock()) { + todo.push_back(block); + for (auto succ : block->getSuccessors()) + todo.push_back(succ); + } + } + if (val != nullptr) { + if (blockArg.getType() != val.getType()) { + block->dump(); + llvm::errs() << " AI: " << AI << "\n"; + llvm::errs() << blockArg << " val " << val << "\n"; + } + assert(blockArg.getType() == val.getType()); + blockArg.replaceAllUsesWith(val); + for (auto &pair : lastStoreInBlock) { + if (pair.second == blockArg) + pair.second = val; + } + for (auto &pair : valueAtStartOfBlock) { + if (pair.second == blockArg) + pair.second = val; + } + } else { + } + valueAtStartOfBlock.erase(block); + + SmallVector preds; + for (auto pred : block->getPredecessors()) { + preds.push_back(pred); + } + for (auto pred : preds) { + if (auto op = dyn_cast(pred->getTerminator())) { + mlir::OpBuilder subbuilder(op.getOperation()); + std::vector args(op.getOperands().begin(), + op.getOperands().end()); + args.erase(args.begin() + blockArg.getArgNumber()); + assert(args.size() == op.getOperands().size() - 1); + subbuilder.create(op.getLoc(), op.getDest(), args); + op.erase(); + } + if (auto op = dyn_cast(pred->getTerminator())) { + + mlir::OpBuilder subbuilder(op.getOperation()); + std::vector trueargs(op.getTrueOperands().begin(), + op.getTrueOperands().end()); + std::vector falseargs(op.getFalseOperands().begin(), + op.getFalseOperands().end()); + if (op.getTrueDest() == block) { + trueargs.erase(trueargs.begin() + blockArg.getArgNumber()); + } + if (op.getFalseDest() == block) { + falseargs.erase(falseargs.begin() + blockArg.getArgNumber()); + } + assert(trueargs.size() < op.getTrueOperands().size() || + falseargs.size() < op.getFalseOperands().size()); + subbuilder.create( + op.getLoc(), op.getCondition(), op.getTrueDest(), trueargs, + op.getFalseDest(), falseargs); + op.erase(); + } + } + block->eraseArgument(blockArg.getArgNumber()); + blocksWithAddedArgs.erase(block); + } + } + } + return changed; +} + +bool isPromotable(mlir::Value AI) { + std::deque list = {AI}; + + while (list.size()) { + auto val = list.front(); + list.pop_front(); + + for (auto U : val.getUsers()) { + if (auto LO = dyn_cast(U)) { + for (auto idx : LO.getIndices()) { + if (!idx.getDefiningOp() && + !idx.getDefiningOp()) { + // llvm::errs() << "non promotable "; AI.dump(); llvm::errs() << " + // ldue to " << idx << "\n"; + return false; + } + } + continue; + } else if (auto LO = dyn_cast(U)) { + for (auto idx : LO.getAffineMapAttr().getValue().getResults()) { + if (!idx.isa()) { + return false; + } + } + continue; + } else if (auto SO = dyn_cast(U)) { + if (SO.value() == val) + return false; + for (auto idx : SO.getIndices()) { + if (!idx.getDefiningOp() && + !idx.getDefiningOp()) { + // llvm::errs() << "non promotable "; AI.dump(); llvm::errs() << " + // sdue to " << idx << "\n"; + return false; + } + } + continue; + } else if (auto SO = dyn_cast(U)) { + if (SO.value() == val) + return false; + for (auto idx : SO.getAffineMapAttr().getValue().getResults()) { + if (!idx.isa()) { + return false; + } + } + continue; + } else if (isa(U)) { + continue; + } else if (isa(U) && cast(U).callee() == "free") { + continue; + } else if (isa(U)) { + // TODO check "no capture", currently assume as a fallback always nocapture + continue; + } else if (auto CO = dyn_cast(U)) { + list.push_back(CO); + } else { + LLVM_DEBUG(llvm::dbgs() + << "non promotable " << AI << " due to " << *U << "\n"); + return false; + } + } + } + return true; +} + +StoreMap getLastStored(mlir::Value AI) { + StoreMap lastStored; + + std::deque list = {AI}; + + while (list.size()) { + auto val = list.front(); + list.pop_front(); + for (auto U : val.getUsers()) { + if (auto SO = dyn_cast(U)) { + std::vector vec; + for (auto idx : SO.getIndices()) { + if (auto op = idx.getDefiningOp()) { + vec.push_back(op.getValue().cast().getInt()); + } else if (auto op = idx.getDefiningOp()) { + vec.push_back(op.getValue()); + } else { + assert(0 && "unhandled op"); + } + } + lastStored.insert(vec); + } else if (auto SO = dyn_cast(U)) { + std::vector vec; + for (auto idx : SO.getAffineMapAttr().getValue().getResults()) { + if (auto op = idx.dyn_cast()) { + vec.push_back(op.getValue()); + } else { + assert(0 && "unhandled op"); + } + } + lastStored.insert(vec); + } + if (auto SO = dyn_cast(U)) { + std::vector vec; + for (auto idx : SO.getIndices()) { + if (auto op = idx.getDefiningOp()) { + vec.push_back(op.getValue().cast().getInt()); + } else if (auto op = idx.getDefiningOp()) { + vec.push_back(op.getValue()); + } else { + assert(0 && "unhandled op"); + } + } + lastStored.insert(vec); + } else if (auto SO = dyn_cast(U)) { + std::vector vec; + for (auto idx : SO.getAffineMapAttr().getValue().getResults()) { + if (auto op = idx.dyn_cast()) { + vec.push_back(op.getValue()); + } else { + assert(0 && "unhandled op"); + } + } + lastStored.insert(vec); + } else if (auto CO = dyn_cast(U)) { + list.push_back(CO); + } + } + } + return lastStored; +} + +void Mem2Reg::runOnFunction() { + // Only supports single block functions at the moment. + FuncOp f = getFunction(); + // Variable indicating that a memref has had a load removed + // and or been deleted. Because there can be memrefs of + // memrefs etc, we may need to do multiple passes (first + // to eliminate the outermost one, then inner ones) + bool changed; + FuncOp freeRemoved = nullptr; + do { + changed = false; + + // A list of memref's that are potentially dead / could be eliminated. + SmallPtrSet memrefsToErase; + + // Load op's whose results were replaced by those forwarded from stores. + SmallVector loadOpsToErase; + + // Walk all load's and perform store to load forwarding. + SmallVector toPromote; + f.walk([&](mlir::memref::AllocaOp AI) { + if (isPromotable(AI)) { + toPromote.push_back(AI); + } + }); + f.walk([&](mlir::memref::AllocOp AI) { + if (isPromotable(AI)) { + toPromote.push_back(AI); + } + }); + + for (auto AI : toPromote) { + LLVM_DEBUG(llvm::dbgs() << " attempting to promote " << AI << "\n"); + auto lastStored = getLastStored(AI); + for (auto &vec : lastStored) { + + LLVM_DEBUG(llvm::dbgs() << " + forwarding vec to promote {"; + for (auto m + : vec) llvm::dbgs() + << (int)m << ","; + llvm::dbgs() << "} of " << AI << "\n"); + // llvm::errs() << " PRE " << AI << "\n"; + // f.dump(); + changed |= forwardStoreToLoad(AI, vec, loadOpsToErase); + // llvm::errs() << " POST " << AI << "\n"; + // f.dump(); + } + memrefsToErase.insert(AI); + } + + // Erase all load op's whose results were replaced with store fwd'ed ones. + for (auto *loadOp : loadOpsToErase) { + changed = true; + loadOp->erase(); + } + + // Check if the store fwd'ed memrefs are now left with only stores and can + // thus be completely deleted. Note: the canonicalize pass should be able + // to do this as well, but we'll do it here since we collected these anyway. + for (auto memref : memrefsToErase) { + + // If the memref hasn't been alloc'ed in this function, skip. + Operation *defOp = memref.getDefiningOp(); + if (!defOp || + !(isa(defOp) || isa(defOp))) + // TODO: if the memref was returned by a 'call' operation, we + // could still erase it if the call had no side-effects. + continue; + + std::deque list = {memref}; + std::vector toErase; + bool error = false; + while (list.size()) { + auto val = list.front(); + list.pop_front(); + + for (auto U : val.getUsers()) { + if (isa(U)) { + toErase.push_back(U); + } else if (isa(U) && cast(U).callee() == "free") { + toErase.push_back(U); + } else if (auto CO = dyn_cast(U)) { + toErase.push_back(U); + list.push_back(CO); + } else if (auto CO = dyn_cast(U)) { + toErase.push_back(U); + list.push_back(CO); + } else { + error = true; + break; + } + } + if (error) + break; + } + + if (!error) { + std::reverse(toErase.begin(), toErase.end()); + for (auto *user : toErase) { + user->erase(); + } + defOp->erase(); + changed = true; + } else { + // llvm::errs() << " failed to remove: " << memref << "\n"; + } + } + } while (changed); + + if (freeRemoved) { + if (freeRemoved.use_empty()) { + freeRemoved.erase(); + } + } +} \ No newline at end of file diff --git a/lib/polygeist/Passes/ParallelLoopDistribute.cpp b/lib/polygeist/Passes/ParallelLoopDistribute.cpp new file mode 100644 index 000000000000..898f7e9c7d47 --- /dev/null +++ b/lib/polygeist/Passes/ParallelLoopDistribute.cpp @@ -0,0 +1,950 @@ +//===- ParallelLoopDistrbute.cpp - Distribute loops around barriers -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/SCF/Passes.h" +#include "mlir/Dialect/SCF/SCF.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/IR/BlockAndValueMapping.h" +#include "mlir/IR/Dominance.h" +#include "mlir/IR/ImplicitLocOpBuilder.h" +#include "mlir/IR/Matchers.h" +#include "mlir/Support/LLVM.h" +#include "mlir/Transforms/DialectConversion.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" + +#include "polygeist/Passes/Passes.h" +#include "polygeist/Ops.h" + +#define DEBUG_TYPE "cpuify" +#define DBGS() ::llvm::dbgs() << "[" DEBUG_TYPE "] " + +using namespace mlir; + +/// Populates `crossing` with values (op results) that are defined in the same +/// block as `op` and above it, and used by at least one op in the same block +/// below `op`. Uses may be in nested regions. +static void findValuesUsedBelow(Operation *op, + llvm::SetVector &crossing) { + for (Operation *it = op->getPrevNode(); it != nullptr; + it = it->getPrevNode()) { + for (Value value : it->getResults()) { + for (Operation *user : value.getUsers()) { + // If the user is nested in another op, find its ancestor op that lives + // in the same block as the barrier. + while (user->getBlock() != op->getBlock()) + user = user->getBlock()->getParentOp(); + + if (op->isBeforeInBlock(user)) { + crossing.insert(value); + break; + } + } + } + } + + // No need to process block arguments, they are assumed to be induction + // variables and will be replicated. +} + +/// Returns `true` if the given operation has a BarrierOp transitively nested in +/// one of its regions. +static bool hasNestedBarrier(Operation *op, Operation* direct=nullptr) { + auto result = + op->walk([=](polygeist::BarrierOp op) { + if (!direct || op->getParentOp() == direct) + return WalkResult::interrupt(); + else return WalkResult::skip(); + }); + return result.wasInterrupted(); +} + +namespace { +/// Replaces an conditional with a loop that may iterate 0 or 1 time, that is: +/// +/// scf.if %cond { +/// @then() +/// } else { +/// @else() +/// } +/// +/// is replaced with +/// +/// scf.for %i = 0 to %cond step 1 { +/// @then() +/// } +/// scf.for %i = 0 to %cond - 1 step 1 { +/// @else() +/// } +struct ReplaceIfWithFors : public OpRewritePattern { + ReplaceIfWithFors(MLIRContext *ctx) : OpRewritePattern(ctx) {} + + LogicalResult matchAndRewrite(scf::IfOp op, + PatternRewriter &rewriter) const override { + assert(op.condition().getType().isInteger(1)); + + // TODO: we can do this by having "undef" values as inputs, or do reg2mem. + if (op.getNumResults() != 0) { + LLVM_DEBUG(DBGS() << "[if-to-for] 'if' with results, need reg2mem\n"; + DBGS() << op); + return failure(); + } + + if (!hasNestedBarrier(op, op)) { + LLVM_DEBUG(DBGS() << "[if-to-for] no nested barrier\n"); + return failure(); + } + + Location loc = op.getLoc(); + auto zero = rewriter.create(loc, 0); + auto one = rewriter.create(loc, 1); + + auto cond = rewriter.create(loc, rewriter.getIndexType(), + rewriter.create(loc, op.condition(), mlir::IntegerType::get(one.getContext(), 64))); + auto thenLoop = rewriter.create(loc, zero, cond, one); + rewriter.mergeBlockBefore(op.getBody(0), &thenLoop.getBody()->back()); + rewriter.eraseOp(&thenLoop.getBody()->back()); + + if (!op.elseRegion().empty()) { + auto negCondition = rewriter.create(loc, one, cond); + auto elseLoop = rewriter.create(loc, zero, negCondition, one); + rewriter.mergeBlockBefore(op.getBody(1), &elseLoop.getBody()->back()); + rewriter.eraseOp(&elseLoop.getBody()->back()); + } + + rewriter.eraseOp(op); + return success(); + } +}; + +/// Returns `true` if `value` is defined outside of the region that contains +/// `user`. +static bool isDefinedAbove(Value value, Operation *user) { + return value.getParentRegion()->isProperAncestor(user->getParentRegion()); +} + +/// Returns `true` if the loop has a form expected by interchange patterns. +static bool isNormalized(scf::ForOp op) { + return isDefinedAbove(op.lowerBound(), op) && isDefinedAbove(op.step(), op); +} + +/// Transforms a loop to the normal form expected by interchange patterns, i.e. +/// with zero lower bound and unit step. +struct NormalizeLoop : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(scf::ForOp op, + PatternRewriter &rewriter) const override { + if (isNormalized(op) || !isa(op->getParentOp())) { + LLVM_DEBUG(DBGS() << "[normalize-loop] loop already normalized\n"); + return failure(); + } + if (op.getNumResults()) { + LLVM_DEBUG(DBGS() << "[normalize-loop] not handling reduction loops\n"); + return failure(); + } + + OpBuilder::InsertPoint point = rewriter.saveInsertionPoint(); + rewriter.setInsertionPoint(op->getParentOp()); + Value zero = rewriter.create(op.getLoc(), 0); + Value one = rewriter.create(op.getLoc(), 1); + rewriter.restoreInsertionPoint(point); + + Value difference = + rewriter.create(op.getLoc(), op.upperBound(), op.lowerBound()); + Value tripCount = + rewriter.create(op.getLoc(), difference, op.step()); + auto newForOp = + rewriter.create(op.getLoc(), zero, tripCount, one); + rewriter.setInsertionPointToStart(newForOp.getBody()); + Value scaled = rewriter.create( + op.getLoc(), newForOp.getInductionVar(), op.step()); + Value iv = rewriter.create(op.getLoc(), op.lowerBound(), scaled); + rewriter.mergeBlockBefore(op.getBody(), &newForOp.getBody()->back(), {iv}); + rewriter.eraseOp(&newForOp.getBody()->back()); + rewriter.eraseOp(op); + return success(); + } +}; + +/// Emits the IR computing the total number of iterations in the loop. We don't +/// need to linearize them since we can allocate an nD array instead. +static llvm::SmallVector emitIterationCounts(PatternRewriter &rewriter, + scf::ParallelOp op) { + SmallVector iterationCounts; + for (auto bounds : llvm::zip(op.lowerBound(), op.upperBound(), op.step())) { + Value lowerBound = std::get<0>(bounds); + Value upperBound = std::get<1>(bounds); + Value step = std::get<2>(bounds); + Value diff = rewriter.create(op.getLoc(), upperBound, lowerBound); + Value count = rewriter.create(op.getLoc(), diff, step); + iterationCounts.push_back(count); + } + return iterationCounts; +} + +/// Returns `true` if the loop has a form expected by interchange patterns. +static bool isNormalized(scf::ParallelOp op) { + auto isZero = [](Value v) { + APInt value; + return matchPattern(v, m_ConstantInt(&value)) && value.isNullValue(); + }; + auto isOne = [](Value v) { + APInt value; + return matchPattern(v, m_ConstantInt(&value)) && value.isOneValue(); + }; + return llvm::all_of(op.lowerBound(), isZero) && + llvm::all_of(op.step(), isOne); +} + +/// Transforms a loop to the normal form expected by interchange patterns, i.e. +/// with zero lower bounds and unit steps. +struct NormalizeParallel : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(scf::ParallelOp op, + PatternRewriter &rewriter) const override { + if (isNormalized(op)) { + LLVM_DEBUG(DBGS() << "[normalize-parallel] loop already normalized\n"); + return failure(); + } + if (op->getNumResults() != 0) { + LLVM_DEBUG( + DBGS() << "[normalize-parallel] not processing reduction loops\n"); + return failure(); + } + if (!hasNestedBarrier(op)) { + LLVM_DEBUG(DBGS() << "[normalize-parallel] no nested barrier\n"); + return failure(); + } + + Value zero = rewriter.create(op.getLoc(), 0); + Value one = rewriter.create(op.getLoc(), 1); + SmallVector iterationCounts = emitIterationCounts(rewriter, op); + auto newOp = rewriter.create( + op.getLoc(), SmallVector(iterationCounts.size(), zero), + iterationCounts, SmallVector(iterationCounts.size(), one)); + + SmallVector inductionVars; + inductionVars.reserve(iterationCounts.size()); + rewriter.setInsertionPointToStart(newOp.getBody()); + for (unsigned i = 0, e = iterationCounts.size(); i < e; ++i) { + Value scaled = rewriter.create( + op.getLoc(), newOp.getInductionVars()[i], op.step()[i]); + Value shifted = + rewriter.create(op.getLoc(), op.lowerBound()[i], scaled); + inductionVars.push_back(shifted); + } + + rewriter.mergeBlockBefore(op.getBody(), &newOp.getBody()->back(), + inductionVars); + rewriter.eraseOp(&newOp.getBody()->back()); + rewriter.eraseOp(op); + return success(); + } +}; + +/// Checks if `op` may need to be wrapped in a pair of barriers. This is a +/// necessary but insufficient condition. +static LogicalResult canWrapWithBarriers(Operation *op) { + if (!isa(op->getParentOp())) { + LLVM_DEBUG(DBGS() << "[wrap] not nested in a pfor\n"); + return failure(); + } + + if (op->getNumResults() != 0) { + LLVM_DEBUG(DBGS() << "[wrap] ignoring loop with reductions\n"); + return failure(); + } + + if (!hasNestedBarrier(op)) { + LLVM_DEBUG(DBGS() << "[wrap] no nested barrier\n"); + return failure(); + } + + return success(); +} + +/// Puts a barrier before and/or after `op` if there isn't already one. +/// `extraPrevCheck` is called on the operation immediately preceding `op` and +/// can be used to look further upward if the immediately preceding operation is +/// not a barrier. +static LogicalResult wrapWithBarriers( + Operation *op, PatternRewriter &rewriter, + llvm::function_ref extraPrevCheck = nullptr) { + Operation *prevOp = op->getPrevNode(); + Operation *nextOp = op->getNextNode(); + bool hasPrevBarrierLike = prevOp == nullptr || isa(prevOp); + if (extraPrevCheck && !hasPrevBarrierLike) + hasPrevBarrierLike = extraPrevCheck(prevOp); + bool hasNextBarrierLike = + nextOp == &op->getBlock()->back() || isa(nextOp); + + if (hasPrevBarrierLike && hasNextBarrierLike) { + LLVM_DEBUG(DBGS() << "[wrap] already has sufficient barriers\n"); + return failure(); + } + + if (!hasPrevBarrierLike) + rewriter.create(op->getLoc()); + + if (!hasNextBarrierLike) { + OpBuilder::InsertionGuard guard(rewriter); + rewriter.setInsertionPointAfter(op); + rewriter.create(op->getLoc()); + } + + // We don't actually change the op, but the pattern infra wants us to. Just + // pretend we changed it in-place. + rewriter.updateRootInPlace(op, [] {}); + LLVM_DEBUG(DBGS() << "[wrap] wrapped '" << op->getName().getStringRef() + << "' with barriers\n"); + return success(); +} + +/// Puts a barrier before and/or after a "for" operation if there isn't already +/// one, potentially with a single load that supplies the upper bound of a +/// (normalized) loop. +struct WrapForWithBarrier : public OpRewritePattern { + WrapForWithBarrier(MLIRContext *ctx) : OpRewritePattern(ctx) {} + + LogicalResult matchAndRewrite(scf::ForOp op, + PatternRewriter &rewriter) const override { + if (failed(canWrapWithBarriers(op))) + return failure(); + + if (!isNormalized(op)) { + LLVM_DEBUG(DBGS() << "[wrap-for] non-normalized loop\n"); + return failure(); + } + + return wrapWithBarriers(op, rewriter, [&](Operation *prevOp) { + if (auto loadOp = dyn_cast_or_null(prevOp)) { + if (loadOp.result() == op.upperBound() && + loadOp.indices() == + cast(op->getParentOp()).getInductionVars()) { + prevOp = prevOp->getPrevNode(); + return prevOp == nullptr || isa(prevOp); + } + } + return false; + }); + } +}; + +/// Puts a barrier before and/or after a "while" operation if there isn't +/// already one. +struct WrapWhileWithBarrier : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(scf::WhileOp op, + PatternRewriter &rewriter) const override { + if (op.getNumOperands() != 0 || + !llvm::hasSingleElement(op.after().front())) { + LLVM_DEBUG(DBGS() << "[wrap-while] ignoring non-rotated loop\n";); + return failure(); + } + + if (failed(canWrapWithBarriers(op))) + return failure(); + + return wrapWithBarriers(op, rewriter); + } +}; + +/// Moves the body from `forLoop` contained in `op` to a parallel op newly +/// created at the start of `newForLoop`. +static void moveBodies(PatternRewriter &rewriter, scf::ParallelOp op, + scf::ForOp forLoop, scf::ForOp newForLoop) { + OpBuilder::InsertionGuard guard(rewriter); + rewriter.setInsertionPointToStart(newForLoop.getBody()); + auto newParallel = rewriter.create( + op.getLoc(), op.lowerBound(), op.upperBound(), op.step()); + + // Merge in two stages so we can properly replace uses of two induction + // varibales defined in different blocks. + rewriter.mergeBlockBefore(op.getBody(), &newParallel.getBody()->back(), + newParallel.getInductionVars()); + rewriter.eraseOp(&newParallel.getBody()->back()); + rewriter.mergeBlockBefore(forLoop.getBody(), &newParallel.getBody()->back(), + newForLoop.getInductionVar()); + rewriter.eraseOp(&newParallel.getBody()->back()); + rewriter.eraseOp(op); + rewriter.eraseOp(forLoop); +} + +/// Interchanges a parallel for loop with a for loop perfectly nested within it. +struct InterchangeForPFor : public OpRewritePattern { + InterchangeForPFor(MLIRContext *ctx) + : OpRewritePattern(ctx) {} + + LogicalResult matchAndRewrite(scf::ParallelOp op, + PatternRewriter &rewriter) const override { + // A perfect nest must have two operations in the outermost body: a "for" + // loop, and a terminator. + if (std::next(op.getBody()->begin(), 2) != op.getBody()->end() || + !isa(op.getBody()->front())) { + LLVM_DEBUG(DBGS() << "[interchange] not a perfect pfor(for) nest\n"); + return failure(); + } + + // We shouldn't have parallel reduction loops coming from GPU anyway, and + // sequential reduction loops can be transformed by reg2mem. + auto forLoop = cast(op.getBody()->front()); + if (op.getNumResults() != 0 || forLoop.getNumResults() != 0) { + LLVM_DEBUG(DBGS() << "[interchange] not matching reduction loops\n"); + return failure(); + } + + if (!isNormalized(op) || !isNormalized(forLoop)) { + LLVM_DEBUG(DBGS() << "[interchange] non-normalized loop\n"); + } + + if (!hasNestedBarrier(forLoop)) { + LLVM_DEBUG(DBGS() << "[interchange] no nested barrier\n";); + return failure(); + } + + auto newForLoop = + rewriter.create(forLoop.getLoc(), forLoop.lowerBound(), + forLoop.upperBound(), forLoop.step()); + moveBodies(rewriter, op, forLoop, newForLoop); + return success(); + } +}; + +/// Interchanges a parallel for loop with a normalized (zero lower bound and +/// unit step) for loop nested within it. The for loop must have a barrier +/// inside and is preceeded by a load operation that supplies its upper bound. +/// The barrier semantics implies that all threads must executed the same number +/// of times, which means that the inner loop must have the same trip count in +/// all iterations of the outer loop. Therefore, the load of the upper bound can +/// be hoisted and read any value, because all values are identical in a +/// semantically valid program. +struct InterchangeForPForLoad : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(scf::ParallelOp op, + PatternRewriter &rewriter) const override { + if (std::next(op.getBody()->begin(), 2) == op.getBody()->end() || + std::next(op.getBody()->begin(), 3) != op.getBody()->end()) { + LLVM_DEBUG(DBGS() << "[interchange-load] expected two nested ops\n"); + return failure(); + } + auto loadOp = dyn_cast(op.getBody()->front()); + auto forOp = dyn_cast(op.getBody()->front().getNextNode()); + if (!loadOp || !forOp || loadOp.result() != forOp.upperBound() || + loadOp.indices() != op.getInductionVars()) { + LLVM_DEBUG(DBGS() << "[interchange-load] expected pfor(load, for)"); + return failure(); + } + + if (!isNormalized(op) || !isNormalized(forOp)) { + LLVM_DEBUG(DBGS() << "[interchange-load] non-normalized loop\n"); + return failure(); + } + + if (!hasNestedBarrier(forOp)) { + LLVM_DEBUG(DBGS() << "[interchange-load] no nested barrier\n"); + return failure(); + } + + // In the GPU model, the trip count of the inner sequential containing a + // barrier must be the same for all threads. So read the value written by + // the first thread outside of the loop to enable interchange. + Value zero = rewriter.create(forOp.getLoc(), 0); + Value tripCount = rewriter.create( + loadOp.getLoc(), loadOp.getMemRef(), + SmallVector(loadOp.getMemRefType().getRank(), zero)); + auto newForLoop = rewriter.create( + forOp.getLoc(), forOp.lowerBound(), tripCount, forOp.step()); + + moveBodies(rewriter, op, forOp, newForLoop); + return success(); + } +}; + +/// Returns the insertion point (as block pointer and itertor in it) immediately +/// after the definition of `v`. +static std::pair getInsertionPointAfterDef(Value v) { + if (Operation *op = v.getDefiningOp()) + return {op->getBlock(), std::next(Block::iterator(op))}; + + BlockArgument blockArg = v.cast(); + return {blockArg.getParentBlock(), blockArg.getParentBlock()->begin()}; +} + +/// Returns the insertion point that post-dominates `first` and `second`. +static std::pair +findNearestPostDominatingInsertionPoint( + const std::pair &first, + const std::pair &second, + const PostDominanceInfo &postDominanceInfo) { + // Same block, take the last op. + if (first.first == second.first) + return first.second->isBeforeInBlock(&*second.second) ? second : first; + + // Same region, use "normal" dominance analysis. + if (first.first->getParent() == second.first->getParent()) { + Block *block = + postDominanceInfo.findNearestCommonDominator(first.first, second.first); + assert(block); + if (block == first.first) + return first; + if (block == second.first) + return second; + return {block, block->begin()}; + } + + if (first.first->getParent()->isAncestor(second.first->getParent())) + return second; + + assert(second.first->getParent()->isAncestor(first.first->getParent()) && + "expected values to be defined in nested regions"); + return first; +} + +/// Returns the insertion point that post-dominates all `values`. +static std::pair +findNesrestPostDominatingInsertionPoint( + ArrayRef values, const PostDominanceInfo &postDominanceInfo) { + assert(!values.empty()); + std::pair insertPoint = + getInsertionPointAfterDef(values[0]); + for (unsigned i = 1, e = values.size(); i < e; ++i) + insertPoint = findNearestPostDominatingInsertionPoint( + insertPoint, getInsertionPointAfterDef(values[i]), postDominanceInfo); + return insertPoint; +} + +static std::pair +findInsertionPointAfterLoopOperands(scf::ParallelOp op) { + // Find the earliest insertion point where loop bounds are fully defined. + PostDominanceInfo postDominanceInfo(op->getParentOfType()); + SmallVector operands; + llvm::append_range(operands, op.lowerBound()); + llvm::append_range(operands, op.upperBound()); + llvm::append_range(operands, op.step()); + return findNesrestPostDominatingInsertionPoint(operands, postDominanceInfo); +} + +static Value allocateTemporaryBuffer(PatternRewriter &rewriter, Value value, + ValueRange iterationCounts) { + SmallVector bufferSize(iterationCounts.size(), + ShapedType::kDynamicSize); + auto type = MemRefType::get(bufferSize, value.getType()); + Value alloc = + rewriter.create(value.getLoc(), type, iterationCounts); + return alloc; +} + +/// Interchanges a parallel for loop with a while loop it contains. The while +/// loop is expected to have an empty "after" region. +struct InterchangeWhilePFor : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(scf::ParallelOp op, + PatternRewriter &rewriter) const override { + // A perfect nest must have two operations in the outermost body: a "while" + // loop, and a terminator. + if (std::next(op.getBody()->begin(), 2) != op.getBody()->end() || + !isa(op.getBody()->front())) { + LLVM_DEBUG( + DBGS() << "[interchange-while] not a perfect pfor(while) nest\n"); + return failure(); + } + + auto whileOp = cast(op.getBody()->front()); + if (whileOp.getNumOperands() != 0 || whileOp.getNumResults() != 0) { + LLVM_DEBUG(DBGS() << "[interchange-while] loop-carried values\n"); + return failure(); + } + if (!llvm::hasSingleElement(whileOp.after().front()) || !isNormalized(op)) { + LLVM_DEBUG(DBGS() << "[interchange-while] non-normalized loop\n"); + return failure(); + } + if (!hasNestedBarrier(whileOp)) { + LLVM_DEBUG(DBGS() << "[interchange-while] no nested barrier\n"); + return failure(); + } + + auto newWhileOp = rewriter.create(whileOp.getLoc(), + TypeRange(), ValueRange()); + rewriter.createBlock(&newWhileOp.after()); + rewriter.clone(whileOp.after().front().back()); + + rewriter.createBlock(&newWhileOp.before()); + auto newParallelOp = rewriter.create( + op.getLoc(), op.lowerBound(), op.upperBound(), op.step()); + + auto conditionOp = cast(whileOp.before().front().back()); + rewriter.mergeBlockBefore(op.getBody(), &newParallelOp.getBody()->back(), + newParallelOp.getInductionVars()); + rewriter.eraseOp(newParallelOp.getBody()->back().getPrevNode()); + rewriter.mergeBlockBefore(&whileOp.before().front(), + &newParallelOp.getBody()->back()); + + Operation *conditionDefiningOp = conditionOp.condition().getDefiningOp(); + if (conditionDefiningOp && + !isDefinedAbove(conditionOp.condition(), conditionOp)) { + std::pair insertionPoint = + findInsertionPointAfterLoopOperands(op); + rewriter.setInsertionPoint(insertionPoint.first, insertionPoint.second); + SmallVector iterationCounts = emitIterationCounts(rewriter, op); + Value allocated = allocateTemporaryBuffer( + rewriter, conditionOp.condition(), iterationCounts); + Value zero = rewriter.create(op.getLoc(), 0); + + rewriter.setInsertionPointAfter(conditionDefiningOp); + rewriter.create(conditionDefiningOp->getLoc(), + conditionOp.condition(), allocated, + newParallelOp.getInductionVars()); + + rewriter.setInsertionPointToEnd(&newWhileOp.before().front()); + SmallVector zeros(iterationCounts.size(), zero); + Value reloaded = rewriter.create( + conditionDefiningOp->getLoc(), allocated, zeros); + rewriter.create(conditionOp.getLoc(), reloaded, + ValueRange()); + rewriter.eraseOp(conditionOp); + } + + rewriter.eraseOp(whileOp); + rewriter.eraseOp(op); + return success(); + } +}; + +/// Moves the "after" region of a while loop into its "before" region using a +/// conditional, that is +/// +/// scf.while { +/// @before() +/// scf.conditional(%cond) +/// } do { +/// @after() +/// scf.yield +/// } +/// +/// is transformed into +/// +/// scf.while { +/// @before() +/// scf.if (%cond) { +/// @after() +/// } +/// scf.conditional(%cond) +/// } do { +/// scf.yield +/// } +struct RotateWhile : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(scf::WhileOp op, + PatternRewriter &rewriter) const override { + if (llvm::hasSingleElement(op.after().front())) { + LLVM_DEBUG(DBGS() << "[rotate-while] the after region is empty"); + return failure(); + } + if (!hasNestedBarrier(op)) { + LLVM_DEBUG(DBGS() << "[rotate-while] no nested barrier\n"); + return failure(); + } + if (op.getNumOperands() != 0 || op.getNumResults() != 0) { + LLVM_DEBUG(DBGS() << "[rotate-while] loop-carried values\n"); + return failure(); + } + + auto condition = cast(op.before().front().back()); + rewriter.setInsertionPoint(condition); + auto conditional = + rewriter.create(op.getLoc(), condition.condition()); + rewriter.mergeBlockBefore(&op.after().front(), + &conditional.getBody()->back()); + rewriter.eraseOp(&conditional.getBody()->back()); + + rewriter.createBlock(&op.after()); + rewriter.clone(conditional.getBody()->back()); + + LLVM_DEBUG(DBGS() << "[rotate-while] done\n"); + return success(); + } +}; + +/// Splits a parallel loop around the first barrier it immediately contains. +/// Values defined before the barrier are stored in newly allocated buffers and +/// loaded back when needed. +struct DistributeAroundBarrier : public OpRewritePattern { + DistributeAroundBarrier(MLIRContext *ctx) + : OpRewritePattern(ctx) {} + + LogicalResult matchAndRewrite(scf::ParallelOp op, + PatternRewriter &rewriter) const override { + if (op.getNumResults() != 0) { + LLVM_DEBUG(DBGS() << "[distribute] not matching reduction loops\n"); + return failure(); + } + + if (!isNormalized(op)) { + LLVM_DEBUG(DBGS() << "[distribute] non-normalized loop\n"); + return failure(); + } + + auto it = + llvm::find_if(op.getBody()->getOperations(), [](Operation &nested) { + return isa(nested); + }); + if (it == op.getBody()->end()) { + LLVM_DEBUG(DBGS() << "[distribute] no barrier in the loop\n"); + return failure(); + } + + llvm::SetVector crossing; + findValuesUsedBelow(&*it, crossing); + std::pair insertPoint = + findInsertionPointAfterLoopOperands(op); + + rewriter.setInsertionPoint(insertPoint.first, insertPoint.second); + SmallVector iterationCounts = emitIterationCounts(rewriter, op); + + // Allocate space for values crossing the barrier. + SmallVector allocations; + allocations.reserve(crossing.size()); + for (Value v : crossing) { + allocations.push_back( + allocateTemporaryBuffer(rewriter, v, iterationCounts)); + } + + // Store values crossing the barrier in caches immediately when ready. + for (auto pair : llvm::zip(crossing, allocations)) { + Value v = std::get<0>(pair); + Value alloc = std::get<1>(pair); + rewriter.setInsertionPointAfter(v.getDefiningOp()); + rewriter.create(v.getLoc(), v, alloc, + op.getInductionVars()); + } + + // Insert the terminator for the new loop immediately before the barrier. + rewriter.setInsertionPoint(&*it); + rewriter.create(op.getBody()->back().getLoc()); + + // Create the second loop. + rewriter.setInsertionPointAfter(op); + auto newLoop = rewriter.create( + op.getLoc(), op.lowerBound(), op.upperBound(), op.step()); + rewriter.eraseOp(&newLoop.getBody()->back()); + + // Recreate the operations in the new loop with new values. + rewriter.setInsertionPointToStart(newLoop.getBody()); + BlockAndValueMapping mapping; + mapping.map(op.getInductionVars(), newLoop.getInductionVars()); + SmallVector toDelete; + toDelete.push_back(&*it); + for (Operation *o = it->getNextNode(); o != nullptr; o = o->getNextNode()) { + rewriter.clone(*o, mapping); + toDelete.push_back(o); + } + + // Erase original operations and the barrier. + for (Operation *o : llvm::reverse(toDelete)) + rewriter.eraseOp(o); + + // Replace uses of values defined above the barrier (now, in a different + // loop) with fresh loads from scratchpad. This may not be the most + // efficient IR, but this avoids creating new crossing values for the + // following barriers as opposed to putting loads at the start of the new + // loop. We expect mem2reg and repeated load elimitation to improve the IR. + newLoop.getBody()->walk([&](Operation *nested) { + for (OpOperand &operand : nested->getOpOperands()) { + auto it = llvm::find(crossing, operand.get()); + if (it == crossing.end()) + continue; + + size_t pos = std::distance(crossing.begin(), it); + rewriter.setInsertionPoint(nested); + Value reloaded = rewriter.create( + operand.getOwner()->getLoc(), allocations[pos], + newLoop.getInductionVars()); + rewriter.startRootUpdate(nested); + operand.set(reloaded); + rewriter.finalizeRootUpdate(nested); + } + }); + + LLVM_DEBUG(DBGS() << "[distribute] distributed arround a barrier\n"); + return success(); + } +}; + +static void loadValues(Location loc, ArrayRef pointers, Value zero, + PatternRewriter &rewriter, + SmallVectorImpl &loaded) { + loaded.reserve(loaded.size() + pointers.size()); + for (Value alloc : pointers) + loaded.push_back(rewriter.create(loc, alloc, zero)); +} + +struct Reg2MemFor : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(scf::ForOp op, + PatternRewriter &rewriter) const override { + if (!op.hasIterOperands() || !hasNestedBarrier(op)) + return failure(); + + //Value stackPtr = rewriter.create( + // op.getLoc(), LLVM::LLVMPointerType::get(rewriter.getIntegerType(8))); + Value zero = rewriter.create(op.getLoc(), 0); + SmallVector allocated; + allocated.reserve(op.getNumIterOperands()); + for (Value operand : op.getIterOperands()) { + Value alloc = rewriter.create( + op.getLoc(), MemRefType::get(1, operand.getType()), ValueRange()); + allocated.push_back(alloc); + + rewriter.create(op.getLoc(), operand, alloc, zero); + } + + auto newOp = rewriter.create(op.getLoc(), op.lowerBound(), + op.upperBound(), op.step()); + rewriter.setInsertionPointToStart(newOp.getBody()); + SmallVector newRegionArguments; + newRegionArguments.push_back(newOp.getInductionVar()); + loadValues(op.getLoc(), allocated, zero, rewriter, newRegionArguments); + + auto oldTerminator = cast(op.getBody()->getTerminator()); + rewriter.mergeBlockBefore(op.getBody(), newOp.getBody()->getTerminator(), + newRegionArguments); + + rewriter.setInsertionPoint(newOp.getBody()->getTerminator()); + for (auto en : llvm::enumerate(oldTerminator.results())) { + rewriter.create(op.getLoc(), en.value(), + allocated[en.index()], zero); + } + rewriter.eraseOp(oldTerminator); + + rewriter.setInsertionPointAfter(op); + SmallVector loaded; + loadValues(op.getLoc(), allocated, zero, rewriter, loaded); + for (Value alloc : allocated) { + rewriter.create(op.getLoc(), alloc); + } + //rewriter.create(op.getLoc(), stackPtr); + rewriter.replaceOp(op, loaded); + return success(); + } +}; + +static void storeValues(Location loc, ValueRange values, ValueRange pointers, + Value zero, PatternRewriter &rewriter) { + for (auto pair : llvm::zip(values, pointers)) { + rewriter.create(loc, std::get<0>(pair), std::get<1>(pair), + zero); + } +} + +static void allocaValues(Location loc, ValueRange values, Value zero, + PatternRewriter &rewriter, + SmallVector &allocated) { + allocated.reserve(values.size()); + for (Value value : values) { + Value alloc = rewriter.create( + loc, MemRefType::get(1, value.getType()), ValueRange()); + allocated.push_back(alloc); + } +} + +struct Reg2MemWhile : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(scf::WhileOp op, + PatternRewriter &rewriter) const override { + if (op.getNumOperands() == 0 && op.getNumResults() == 0) + return failure(); + if (!hasNestedBarrier(op)) + return failure(); + + Value stackPtr = rewriter.create( + op.getLoc(), LLVM::LLVMPointerType::get(rewriter.getIntegerType(8))); + Value zero = rewriter.create(op.getLoc(), 0); + SmallVector beforeAllocated, afterAllocated; + allocaValues(op.getLoc(), op.getOperands(), zero, rewriter, + beforeAllocated); + storeValues(op.getLoc(), op.getOperands(), beforeAllocated, zero, rewriter); + allocaValues(op.getLoc(), op.getResults(), zero, rewriter, afterAllocated); + + auto newOp = + rewriter.create(op.getLoc(), TypeRange(), ValueRange()); + Block *newBefore = + rewriter.createBlock(&newOp.before(), newOp.before().begin()); + SmallVector newBeforeArguments; + loadValues(op.getLoc(), beforeAllocated, zero, rewriter, + newBeforeArguments); + rewriter.mergeBlocks(&op.before().front(), newBefore, newBeforeArguments); + + auto beforeTerminator = + cast(newOp.before().front().getTerminator()); + rewriter.setInsertionPoint(beforeTerminator); + storeValues(op.getLoc(), beforeTerminator.args(), afterAllocated, zero, + rewriter); + + rewriter.updateRootInPlace(beforeTerminator, + [&] { beforeTerminator.argsMutable().clear(); }); + + Block *newAfter = + rewriter.createBlock(&newOp.after(), newOp.after().begin()); + SmallVector newAfterArguments; + loadValues(op.getLoc(), afterAllocated, zero, rewriter, newAfterArguments); + rewriter.mergeBlocks(&op.after().front(), newAfter, newAfterArguments); + + auto afterTerminator = + cast(newOp.after().front().getTerminator()); + rewriter.setInsertionPoint(afterTerminator); + storeValues(op.getLoc(), afterTerminator.results(), beforeAllocated, zero, + rewriter); + + rewriter.updateRootInPlace( + afterTerminator, [&] { afterTerminator.resultsMutable().clear(); }); + + rewriter.setInsertionPointAfter(op); + SmallVector results; + loadValues(op.getLoc(), afterAllocated, zero, rewriter, results); + rewriter.create(op.getLoc(), stackPtr); + rewriter.replaceOp(op, results); + return success(); + } +}; + +struct CPUifyPass : public SCFCPUifyBase { + void runOnFunction() override { + OwningRewritePatternList patterns(&getContext()); + patterns + .insert( + &getContext()); + GreedyRewriteConfig config; + config.maxIterations = 42; + if (failed(applyPatternsAndFoldGreedily(getFunction(), std::move(patterns), + config))) + signalPassFailure(); + + } +}; + +} // end namespace + +namespace mlir { + namespace polygeist { +std::unique_ptr createCPUifyPass() { + return std::make_unique(); +} + } +} \ No newline at end of file diff --git a/lib/polygeist/Passes/ParallelLower.cpp b/lib/polygeist/Passes/ParallelLower.cpp new file mode 100644 index 000000000000..f13f297c1596 --- /dev/null +++ b/lib/polygeist/Passes/ParallelLower.cpp @@ -0,0 +1,305 @@ +//===- ParallelLower.cpp - Lower gpu code to triple nested loops ------ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a pass to lower gpu kernels in NVVM/gpu dialects into +// a generic parallel for representation +//===----------------------------------------------------------------------===// + +#include "polygeist/Passes/Passes.h" +#include "polygeist/Ops.h" +#include "mlir/Analysis/CallGraph.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "mlir/Analysis/AffineAnalysis.h" +#include "mlir/Analysis/Utils.h" +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/IR/Dominance.h" +#include "mlir/Transforms/Passes.h" +#include "llvm/ADT/SmallPtrSet.h" +#include +#include "mlir/Dialect/SCF/SCF.h" +#include "mlir/Dialect/LLVMIR/NVVMDialect.h" +#include "mlir/Dialect/GPU/GPUDialect.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" + +#define DEBUG_TYPE "parallel-lower-opt" + +using namespace mlir; + +namespace { +// The store to load forwarding relies on three conditions: +// +// 1) they need to have mathematically equivalent affine access functions +// (checked after full composition of load/store operands); this implies that +// they access the same single memref element for all iterations of the common +// surrounding loop, +// +// 2) the store op should dominate the load op, +// +// 3) among all op's that satisfy both (1) and (2), the one that postdominates +// all store op's that have a dependence into the load, is provably the last +// writer to the particular memref location being loaded at the load op, and its +// store value can be forwarded to the load. Note that the only dependences +// that are to be considered are those that are satisfied at the block* of the +// innermost common surrounding loop of the being considered. +// +// (* A dependence being satisfied at a block: a dependence that is satisfied by +// virtue of the destination operation appearing textually / lexically after +// the source operation within the body of a 'affine.for' operation; thus, a +// dependence is always either satisfied by a loop or by a block). +// +// The above conditions are simple to check, sufficient, and powerful for most +// cases in practice - they are sufficient, but not necessary --- since they +// don't reason about loops that are guaranteed to execute at least once or +// multiple sources to forward from. +// +// TODO: more forwarding can be done when support for +// loop/conditional live-out SSA values is available. +// TODO: do general dead store elimination for memref's. This pass +// currently only eliminates the stores only if no other loads/uses (other +// than dealloc) remain. +// +struct ParallelLower : public ParallelLowerBase { + void runOnFunction() override; +}; + +} // end anonymous namespace + +/// Creates a pass to perform optimizations relying on memref dataflow such as +/// store to load forwarding, elimination of dead stores, and dead allocs. +namespace mlir { + namespace polygeist { + std::unique_ptr> createParallelLowerPass() { + return std::make_unique(); + } + } +} + +#include "mlir/Transforms/InliningUtils.h" + +struct AlwaysInlinerInterface : public InlinerInterface { + using InlinerInterface::InlinerInterface; + + //===--------------------------------------------------------------------===// + // Analysis Hooks + //===--------------------------------------------------------------------===// + + /// All call operations within standard ops can be inlined. + bool isLegalToInline(Operation *call, Operation *callable, + bool wouldBeCloned) const final { + return true; + } + + /// All operations within standard ops can be inlined. + bool isLegalToInline(Region *, Region *, bool, + BlockAndValueMapping &) const final { + return true; + } + + /// All operations within standard ops can be inlined. + bool isLegalToInline(Operation *, Region *, bool, + BlockAndValueMapping &) const final { + return true; + } + + //===--------------------------------------------------------------------===// + // Transformation Hooks + //===--------------------------------------------------------------------===// + + /// Handle the given inlined terminator by replacing it with a new operation + /// as necessary. + void handleTerminator(Operation *op, Block *newDest) const final { + // Only "std.return" needs to be handled here. + auto returnOp = dyn_cast(op); + if (!returnOp) + return; + + // Replace the return with a branch to the dest. + OpBuilder builder(op); + builder.create(op->getLoc(), newDest, returnOp.getOperands()); + op->erase(); + } + + /// Handle the given inlined terminator by replacing it with a new operation + /// as necessary. + void handleTerminator(Operation *op, + ArrayRef valuesToRepl) const final { + // Only "std.return" needs to be handled here. + auto returnOp = cast(op); + + // Replace the values directly with the return operands. + assert(returnOp.getNumOperands() == valuesToRepl.size()); + for (const auto &it : llvm::enumerate(returnOp.getOperands())) + valuesToRepl[it.index()].replaceAllUsesWith(it.value()); + } +}; + +void ParallelLower::runOnFunction() { + // The inliner should only be run on operations that define a symbol table, + // as the callgraph will need to resolve references. + Operation *symbolTableOp = getFunction()->getParentWithTrait(); + + CallGraph &cg = getAnalysis(); + SymbolTableCollection symbolTable; + symbolTable.getSymbolTable(symbolTableOp); + + // Only supports single block functions at the moment. + getFunction().walk([&](gpu::LaunchOp launchOp) { + + launchOp.walk([&](CallOp caller) { + // Build the inliner interface. + AlwaysInlinerInterface interface(&getContext()); + + auto callable = caller.getCallableForCallee();//.resolveCallable(symbolTableOp->getTrait());//.getCallableRegion(); + CallableOpInterface callableOp; + if (SymbolRefAttr symRef = callable.dyn_cast()) { + if (!symRef.isa()) + return; + auto *symbolOp = symbolTable.lookupNearestSymbolFrom(symbolTableOp, + symRef); + callableOp = dyn_cast_or_null(symbolOp); + } else { + return; + } + Region* targetRegion = callableOp.getCallableRegion(); + if (inlineCall( + interface, caller, callableOp, + targetRegion, /*shouldCloneInlinedRegion=*/true).succeeded()) { + caller.erase(); + if (callableOp->use_empty()) { + callableOp.erase(); + } + } + }); + + Block* nb = &launchOp.getRegion().front(); + mlir::OpBuilder builder(launchOp.getContext()); + auto loc = builder.getUnknownLoc(); + + builder.setInsertionPoint(launchOp->getBlock(), launchOp->getIterator()); + auto zindex = builder.create(loc, 0); + + auto oneindex = builder.create(loc, 1); + + auto block = builder.create(loc, ValueRange({zindex, zindex, zindex}), ValueRange({launchOp.gridSizeX(), launchOp.gridSizeY(), launchOp.gridSizeZ()}), ValueRange({oneindex, oneindex, oneindex})); + Block* blockB; + { + auto iter = block.getRegion().getBlocks().begin(); + blockB = &*iter; + blockB->begin()->erase(); + } + builder.setInsertionPointToStart(blockB); + + auto threadr = builder.create(loc, ValueRange({zindex, zindex, zindex}), ValueRange({launchOp.blockSizeX(), launchOp.blockSizeY(), launchOp.blockSizeZ()}), ValueRange({oneindex, oneindex, oneindex})); + builder.create(loc); + Block* threadB; + auto iter = threadr.getRegion().getBlocks().begin(); + threadB = &*iter; + //threadB->begin()->erase(); + + //threadr.getRegion().getBlocks().clear(); + //builder.create(loc); + //builder.setInsertionPointToStart(threadB); + + auto container = threadr;//builder.create(loc, std::vector()); + + threadB->getOperations().clear(); + threadB->getOperations().splice(threadB->begin(), nb->getOperations()); + nb->erase(); + + //mlir::OpBuilder builder2(f.getContext()); + //builder2.setInsertionPointToStart(threadB); + //iter++; + //builder2.create(loc, &*iter); + + container.walk([&](mlir::gpu::BlockIdOp bidx) { + mlir::OpBuilder bz(launchOp.getContext()); + bz.setInsertionPoint(bidx); + int idx = -1; + if (bidx.dimension() == "x") idx = 0; + else if (bidx.dimension() == "y") idx = 1; + else if (bidx.dimension() == "z") idx = 2; + else assert(0 && "illegal dimension"); + bidx.replaceAllUsesWith((mlir::Value)blockB->getArgument(idx)); + bidx.erase(); + }); + + container.walk([&](mlir::memref::AllocaOp alop) { + if (alop.getType().getMemorySpace().cast().getValue() == 5) { + mlir::OpBuilder bz(launchOp.getContext()); + bz.setInsertionPointToStart(blockB); + auto newAlloca = bz.create(alop.getLoc(), MemRefType::get(alop.getType().getShape(), alop.getType().getElementType(), alop.getType().getAffineMaps(), (uint64_t)0)); + alop.replaceAllUsesWith((mlir::Value)bz.create(alop.getLoc(), newAlloca, alop.getType())); + alop.erase(); + } + }); + + container.walk([&](mlir::gpu::ThreadIdOp bidx) { + mlir::OpBuilder bz(launchOp.getContext()); + bz.setInsertionPoint(bidx); + int idx = -1; + if (bidx.dimension() == "x") idx = 0; + else if (bidx.dimension() == "y") idx = 1; + else if (bidx.dimension() == "z") idx = 2; + else assert(0 && "illegal dimension"); + bidx.replaceAllUsesWith((mlir::Value)threadB->getArgument(idx)); + bidx.erase(); + }); + + container.walk([&](gpu::TerminatorOp op) { + mlir::OpBuilder bz(launchOp.getContext()); + bz.setInsertionPoint(op); + bz.create(loc); + op.erase(); + }); + + container.walk([&](mlir::NVVM::Barrier0Op op) { + mlir::OpBuilder bz(launchOp.getContext()); + bz.setInsertionPoint(op); + bz.create(loc); + op.erase(); + }); + + container.walk([&](gpu::GridDimOp bidx) { + Value val = nullptr; + if (bidx.dimension() == "x") + val = launchOp.gridSizeX(); + else if (bidx.dimension() == "y") + val = launchOp.gridSizeY(); + else if (bidx.dimension() == "z") + val = launchOp.gridSizeZ(); + else assert(0 && "illegal dimension"); + bidx.replaceAllUsesWith(val); + bidx.erase(); + }); + + container.walk([&](gpu::BlockDimOp bidx) { + Value val = nullptr; + if (bidx.dimension() == "x") + val = launchOp.blockSizeX(); + else if (bidx.dimension() == "y") + val = launchOp.blockSizeY(); + else if (bidx.dimension() == "z") + val = launchOp.blockSizeZ(); + else assert(0 && "illegal dimension"); + bidx.replaceAllUsesWith(val); + bidx.erase(); + }); + + launchOp.erase(); + + }); + + getFunction().dump(); + // Fold the copy memtype cast + { + mlir::RewritePatternSet rpl(getFunction().getContext()); + GreedyRewriteConfig config; + applyPatternsAndFoldGreedily(getFunction().getOperation(), std::move(rpl), config); + } +} \ No newline at end of file diff --git a/lib/polygeist/Passes/RaiseToAffine.cpp b/lib/polygeist/Passes/RaiseToAffine.cpp new file mode 100644 index 000000000000..48dd9555b3d6 --- /dev/null +++ b/lib/polygeist/Passes/RaiseToAffine.cpp @@ -0,0 +1,153 @@ +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/SCF/Passes.h" +#include "mlir/Dialect/SCF/SCF.h" +#include "mlir/IR/BlockAndValueMapping.h" +#include "mlir/Transforms/DialectConversion.h" +#include "llvm/Support/Debug.h" +#include "polygeist/Passes/Passes.h" + +#define DEBUG_TYPE "raise-to-affine" + +using namespace mlir; + +namespace { +struct RaiseSCFToAffine : public SCFRaiseToAffineBase { + void runOnFunction() override; +}; +} // namespace + +struct ForOpRaising : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + // TODO: remove me or rename me. + bool isAffine(scf::ForOp loop) const { + // return true; + // enforce step to be a ConstantIndexOp (maybe too restrictive). + return isa_and_nonnull(loop.step().getDefiningOp()); + } + + bool canonicalizeLoopBounds(AffineForOp forOp) const { + SmallVector lbOperands(forOp.getLowerBoundOperands()); + SmallVector ubOperands(forOp.getUpperBoundOperands()); + + auto lbMap = forOp.getLowerBoundMap(); + auto ubMap = forOp.getUpperBoundMap(); + auto prevLbMap = lbMap; + auto prevUbMap = ubMap; + + fullyComposeAffineMapAndOperands(&lbMap, &lbOperands); + canonicalizeMapAndOperands(&lbMap, &lbOperands); + lbMap = removeDuplicateExprs(lbMap); + + fullyComposeAffineMapAndOperands(&ubMap, &ubOperands); + canonicalizeMapAndOperands(&ubMap, &ubOperands); + ubMap = removeDuplicateExprs(ubMap); + + // Any canonicalization change always leads to updated map(s). + if (lbMap == prevLbMap && ubMap == prevUbMap) + return false; + + if (lbMap != prevLbMap) + forOp.setLowerBound(lbOperands, lbMap); + if (ubMap != prevUbMap) + forOp.setUpperBound(ubOperands, ubMap); + + return true; + } + + int64_t getStep(mlir::Value value) const { + ConstantIndexOp cstOp = value.getDefiningOp(); + assert(cstOp && "expect non-null operation"); + return cstOp.getValue(); + } + + bool isConstantLike(Value val) const { + Operation *op = val.getDefiningOp(); + if (!op) + return false; + return op->getNumOperands() == 0 && op->getNumResults() == 1 && + op->hasTrait(); + } + + LogicalResult matchAndRewrite(scf::ForOp loop, + PatternRewriter &rewriter) const final { + if (isAffine(loop)) { + OpBuilder builder(loop); + + AffineForOp affineLoop = rewriter.create( + loop.getLoc(), loop.lowerBound(), builder.getSymbolIdentityMap(), + loop.upperBound(), builder.getSymbolIdentityMap(), + getStep(loop.step()), loop.getIterOperands()); + + if (!canonicalizeLoopBounds(affineLoop)) + return failure(); + + // constant should be ok too. + if (!llvm::all_of(affineLoop.getLowerBoundOperands(), [this](Value operand) { + return isValidDim(operand) || isConstantLike(operand); + })) + return failure(); + if (!llvm::all_of(affineLoop.getUpperBoundOperands(), [this](Value operand) { + return isValidDim(operand) || isConstantLike(operand); + })) + return failure(); + + SmallVector newBlockTransferArgs; + newBlockTransferArgs.reserve(1 + loop.getNumIterOperands()); + Value iv = affineLoop.getInductionVar(); + loop.getInductionVar().replaceAllUsesWith(iv); + newBlockTransferArgs.push_back(iv); + llvm::append_range(newBlockTransferArgs, affineLoop.getIterOperands()); + + Block &newBlock = affineLoop.region().front(); + + // The terminator is added if the iterator args are not provided. + // see the ::build method. + if (affineLoop.getNumIterOperands() == 0) { + auto affineYiledOp = newBlock.getTerminator(); + rewriter.eraseOp(affineYiledOp); + } + + Block &oldBlock = loop.region().front(); + assert(oldBlock.getNumArguments() == newBlockTransferArgs.size() && + "unexpected argument size mismatch"); + + auto fixYield = [&](scf::YieldOp mergedTerminator) { + OpBuilder::InsertionGuard g(rewriter); + rewriter.setInsertionPoint(mergedTerminator); + rewriter.create(mergedTerminator.getLoc(), + mergedTerminator.getOperands()); + }; + + rewriter.mergeBlocks(&oldBlock, &newBlock, newBlockTransferArgs); + auto mergedYieldOp = cast(newBlock.getTerminator()); + fixYield(mergedYieldOp); + rewriter.eraseOp(mergedYieldOp); + rewriter.replaceOp(loop, affineLoop.getResults()); + return success(); + } + return failure(); + } +}; + +void RaiseSCFToAffine::runOnFunction() { + ConversionTarget target(getContext()); + target + .addLegalDialect(); + + OwningRewritePatternList patterns(&getContext()); + patterns.insert(&getContext()); + + if (failed( + applyPartialConversion(getFunction(), target, std::move(patterns)))) + signalPassFailure(); +} + +namespace mlir { + namespace polygeist { + std::unique_ptr createRaiseSCFToAffinePass() { + return std::make_unique(); + } + } +} \ No newline at end of file diff --git a/llvm-project b/llvm-project new file mode 160000 index 000000000000..6922ab73a5a5 --- /dev/null +++ b/llvm-project @@ -0,0 +1 @@ +Subproject commit 6922ab73a5a5b0d6a65f0b8796e5fae4345dbbd9 diff --git a/mlir-clang/.clang-format b/mlir-clang/.clang-format new file mode 100644 index 000000000000..9b3aa8b7213b --- /dev/null +++ b/mlir-clang/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/mlir-clang/.gitignore b/mlir-clang/.gitignore new file mode 100644 index 000000000000..035bb0dc65bb --- /dev/null +++ b/mlir-clang/.gitignore @@ -0,0 +1,3 @@ +*.time +*.exec1 +*.out1 diff --git a/mlir-clang/CMakeLists.txt b/mlir-clang/CMakeLists.txt new file mode 100644 index 000000000000..52a5b2810eff --- /dev/null +++ b/mlir-clang/CMakeLists.txt @@ -0,0 +1,44 @@ +add_clang_tool(mlir-clang + mlir-clang.cc +) + +target_include_directories(mlir-clang PRIVATE + "${LLVM_SOURCE_DIR}/../clang/include" + "${CMAKE_BINARY_DIR}/tools/clang/include" +) +# MLIRTransformsPassIncGen + +target_compile_definitions(mlir-clang PUBLIC -DLLVM_OBJ_ROOT="${LLVM_BINARY_DIR}") +target_link_libraries(mlir-clang PRIVATE + LLVMCore + LLVMOption + LLVMSupport + + MLIRSCFTransforms + MLIRPolygeist + + MLIRSupport + MLIRIR + MLIRAnalysis + MLIRLLVMIR + MLIRNVVMIR + MLIRGPU + MLIRTransforms + MLIRSCFToStandard + MLIRStandardToLLVM + MLIRAffineTransforms + MLIRAffineToStandard + MLIRTargetLLVMIRImport + MLIRPolygeistTransforms + + clangAST + clangBasic + clangCodeGen + clangDriver + clangFrontend + clangFrontendTool + clangLex + clangSerialization +) +add_dependencies(mlir-clang MLIRPolygeistOpsIncGen MLIRPolygeistPassIncGen) +add_subdirectory(Test) diff --git a/mlir-clang/Lib/clang-mlir.cc b/mlir-clang/Lib/clang-mlir.cc new file mode 100644 index 000000000000..36ae5c977eb7 --- /dev/null +++ b/mlir-clang/Lib/clang-mlir.cc @@ -0,0 +1,3030 @@ +#include "clang-mlir.h" + +#include "llvm/Support/Debug.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace clang; +using namespace clang::driver; +using namespace llvm::opt; +using namespace mlir; + +#define DEBUG_TYPE "clang-mlir" + +class IfScope { +public: + MLIRScanner &scanner; + IfScope(MLIRScanner &scanner) : scanner(scanner) { scanner.pushLoopIf(); } + ~IfScope() { scanner.popLoopIf(); } +}; +void MLIRScanner::setValue(std::string name, ValueWithOffsets &&val) { + auto z = scopes.back().emplace(name, val); + assert(z.second); + // assert(val.offsets.size() == z.first->second.offsets.size()); +} + +ValueWithOffsets MLIRScanner::getValue(std::string name) { + for (int i = scopes.size() - 1; i >= 0; i--) { + auto found = scopes[i].find(name); + if (found != scopes[i].end()) { + return found->second; + } + } + if (Glob.globalVariables.find(name) != Glob.globalVariables.end()) { + if (name == "stderr" || name == "stdout" || name == "stdin") { + return ValueWithOffsets( + builder.create( + loc, Glob.GetOrCreateLLVMGlobal(Glob.globalVariables[name])), + /*isReference*/ true); + } + + auto gv = Glob.GetOrCreateGlobal(Glob.globalVariables[name]); + auto gv2 = builder.create(loc, gv.first.type(), + gv.first.getName()); + bool isArray = gv.second; + // TODO check reference + if (isArray) + return ValueWithOffsets(gv2, /*isReference*/ true); + else + return ValueWithOffsets(gv2, /*isReference*/ true); + // return gv2; + } + if (Glob.globalFunctions.find(name) != Glob.globalFunctions.end()) { + auto gv = Glob.GetOrCreateMLIRFunction(Glob.globalFunctions[name]); + // TODO, how to represent? + // return ValueWithOffsets(gv, std::vector()); + } + llvm::errs() << "couldn't find " << name << "\n"; + assert(0 && "couldnt find value"); + return nullptr; +} + +mlir::Type MLIRScanner::getLLVMTypeFromMLIRType(mlir::Type t) { + if (auto it = t.dyn_cast()) { + return mlir::IntegerType::get(t.getContext(), it.getWidth()); + } + assert(0 && "unhandled mlir=>llvm type"); +} + +mlir::Value MLIRScanner::createAllocOp(mlir::Type t, std::string name, + uint64_t memspace, + bool isArray = false) { + mlir::MemRefType mr; + if (!isArray) { + mr = mlir::MemRefType::get(1, t, {}, memspace); + } else { + auto mt = t.cast(); + mr = mlir::MemRefType::get(mt.getShape(), mt.getElementType(), + mt.getAffineMaps(), memspace); + } + // NamedAttribute attrs[] = {NamedAttribute("name", name)}; + auto alloc = builder.create(loc, mr); + if (isArray) + setValue(name, ValueWithOffsets(alloc, /*isReference*/ true)); + else + setValue(name, ValueWithOffsets(alloc, /*isReference*/ true)); + return alloc; +} + +mlir::Value MLIRScanner::createAndSetAllocOp(std::string name, mlir::Value v, + uint64_t memspace) { + auto op = createAllocOp(v.getType(), name, memspace); + mlir::Value zeroIndex = getConstantIndex(0); + assert(v.getType() == op.getType().cast().getElementType()); + builder.create(loc, v, op, zeroIndex); + return op; +} + +ValueWithOffsets MLIRScanner::VisitDeclStmt(clang::DeclStmt *decl) { + IfScope scope(*this); + for (auto sub : decl->decls()) { + if (auto vd = dyn_cast(sub)) { + VisitVarDecl(vd); + } else { + llvm::errs() << " + visiting unknonwn sub decl stmt\n"; + sub->dump(); + assert(0 && "unknown sub decl"); + } + } + return nullptr; +} + +ValueWithOffsets MLIRScanner::VisitIntegerLiteral(clang::IntegerLiteral *expr) { + auto ty = getMLIRType(expr->getType()).cast(); + return ValueWithOffsets( + builder.create( + loc, ty, builder.getIntegerAttr(ty, expr->getValue())), + /*isReference*/ false); +} + +ValueWithOffsets +MLIRScanner::VisitFloatingLiteral(clang::FloatingLiteral *expr) { + auto ty = getMLIRType(expr->getType()).cast(); + return ValueWithOffsets( + builder.create( + loc, ty, builder.getFloatAttr(ty, expr->getValue())), + /*isReference*/ false); +} + +ValueWithOffsets +MLIRScanner::VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr *expr) { + auto ty = getMLIRType(expr->getType()).cast(); + return ValueWithOffsets( + builder.create( + loc, ty, builder.getIntegerAttr(ty, expr->getValue())), + /*isReference*/ false); +} + +ValueWithOffsets MLIRScanner::VisitStringLiteral(clang::StringLiteral *expr) { + auto ty = getMLIRType(expr->getType()); + return ValueWithOffsets(builder.create( + loc, ty, builder.getStringAttr(expr->getBytes())), + /*isReference*/ false); +} + +ValueWithOffsets MLIRScanner::VisitParenExpr(clang::ParenExpr *expr) { + return Visit(expr->getSubExpr()); +} + +ValueWithOffsets +MLIRScanner::VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *decl) { + for (auto child : decl->children()) { + child->dump(); + } + decl->dump(); + assert(0 && "bad"); +} + +#include "clang/AST/Attr.h" +ValueWithOffsets MLIRScanner::VisitVarDecl(clang::VarDecl *decl) { + auto loc = getMLIRLocation(decl->getLocation()); + unsigned memtype = 0; + + if (decl->hasAttr()) { + memtype = 5; + } + + mlir::Type subType = getMLIRType(decl->getType()); + mlir::Value inite = nullptr; + if (auto init = decl->getInit()) { + if (!isa(init) && !isa(init)) { + auto visit = Visit(init); + if (!visit.val) { + decl->dump(); + } + inite = visit.getValue(builder); + if (!inite) { + init->dump(); + assert(0 && inite); + } + subType = inite.getType(); + } + } else if (auto ava = decl->getAttr()) { + if (auto algn = dyn_cast(ava->getAlignment())) { + for (auto a : algn->children()) { + if (auto IL = dyn_cast(a)) { + if (IL->getValue() == 8192) { + llvm::Type *T = Glob.CGM.getTypes().ConvertType(decl->getType()); + subType = Glob.typeTranslator.translateType(T); + break; + } + } + } + } + } + bool isArray = isa(decl->getType()); + if (isa(getLLVMType(decl->getType()))) { + isArray = true; + } + auto op = createAllocOp(subType, decl->getName().str(), memtype, isArray); + mlir::Value zeroIndex = getConstantIndex(0); + if (inite) { + assert(inite.getType() == op.getType().cast().getElementType()); + builder.create(loc, inite, op, zeroIndex); + } else if (auto init = decl->getInit()) { + if (auto il = dyn_cast(init)) { + if (il->hasArrayFiller()) { + auto visit = Visit(il->getInit(0)); + inite = visit.getValue(builder); + if (!inite) { + il->getArrayFiller()->dump(); + assert(inite); + } + assert(subType.cast().getShape().size() == 1); + for (size_t i = 0; i < subType.cast().getShape()[0]; i++) { + builder.create(loc, inite, op, + getConstantIndex(i)); + } + } else { + init->dump(); + assert(0 && "init list expr unhandled"); + } + } else if (auto cons = dyn_cast(init)) { + assert(cons->getNumArgs() == 0); + auto RT = + cast(cons->getType()->getUnqualifiedDesugaredType()); + if (RT->getDecl()->getName() == "double3") { + assert(subType.cast().getShape().size() == 1); + auto ty = subType.cast().getElementType(); + auto cop = builder.create( + loc, ty, builder.getFloatAttr(ty, 0.0)); + for (size_t i = 0; i < subType.cast().getShape()[0]; i++) { + builder.create(loc, cop, op, + getConstantIndex(i)); + } + } else { + cons->dump(); + assert(0 && "unknown init construct"); + } + } else + assert(0 && "unknown init list"); + } + return ValueWithOffsets(op, /*isReference*/ true); +} + +bool MLIRScanner::getLowerBound(clang::ForStmt *fors, + AffineLoopDescriptor &descr) { + auto init = fors->getInit(); + if (auto declStmt = dyn_cast(init)) + if (declStmt->isSingleDecl()) { + auto decl = declStmt->getSingleDecl(); + if (auto varDecl = dyn_cast(decl)) { + if (varDecl->hasInit()) { + auto init = varDecl->getInit(); + + mlir::Value val = Visit(init).getValue(builder); + descr.setName(varDecl->getName().str()); + descr.setType(val.getType()); + LLVM_DEBUG(descr.getType().print(llvm::dbgs())); + + if (descr.getForwardMode()) + descr.setLowerBound(val); + else { + val = builder.create(loc, val, getConstantIndex(1)); + descr.setUpperBound(val); + } + return true; + } + } + } + + // BinaryOperator 0x7ff7aa17e938 'int' '=' + // |-DeclRefExpr 0x7ff7aa17e8f8 'int' lvalue Var 0x7ff7aa17e758 'i' 'int' + // -IntegerLiteral 0x7ff7aa17e918 'int' 0 + if (auto binOp = dyn_cast(init)) + if (binOp->getOpcode() == clang::BinaryOperator::Opcode::BO_Assign) + if (auto declRefStmt = dyn_cast(binOp->getLHS())) { + mlir::Value val = Visit(binOp->getRHS()).getValue(builder); + val = builder.create( + loc, val, mlir::IndexType::get(builder.getContext())); + descr.setName(declRefStmt->getNameInfo().getAsString()); + descr.setType(getMLIRType(declRefStmt->getDecl()->getType())); + if (descr.getForwardMode()) + descr.setLowerBound(val); + else { + val = builder.create(loc, val, getConstantIndex(1)); + descr.setUpperBound(val); + } + return true; + } + return false; +} + +// Make sure that the induction variable initialized in +// the for is the same as the one used in the condition. +bool matchIndvar(const Expr *expr, std::string indVar) { + while (auto IC = dyn_cast(expr)) { + expr = IC->getSubExpr(); + } + if (auto declRef = dyn_cast(expr)) { + auto declRefName = declRef->getDecl()->getName().str(); + if (declRefName == indVar) + return true; + } + return false; +} + +bool MLIRScanner::getUpperBound(clang::ForStmt *fors, + AffineLoopDescriptor &descr) { + auto cond = fors->getCond(); + if (auto binaryOp = dyn_cast(cond)) { + auto lhs = binaryOp->getLHS(); + if (!matchIndvar(lhs, descr.getName())) + return false; + + if (descr.getForwardMode()) { + if (binaryOp->getOpcode() != clang::BinaryOperator::Opcode::BO_LT && + binaryOp->getOpcode() != clang::BinaryOperator::Opcode::BO_LE) + return false; + + auto rhs = binaryOp->getRHS(); + mlir::Value val = Visit(rhs).getValue(builder); + val = builder.create(loc, val, + mlir::IndexType::get(val.getContext())); + if (binaryOp->getOpcode() == clang::BinaryOperator::Opcode::BO_LE) + val = builder.create(loc, val, getConstantIndex(1)); + descr.setUpperBound(val); + return true; + } else { + if (binaryOp->getOpcode() != clang::BinaryOperator::Opcode::BO_GT && + binaryOp->getOpcode() != clang::BinaryOperator::Opcode::BO_GE) + return false; + + auto rhs = binaryOp->getRHS(); + mlir::Value val = Visit(rhs).getValue(builder); + val = builder.create(loc, val, + mlir::IndexType::get(val.getContext())); + if (binaryOp->getOpcode() == clang::BinaryOperator::Opcode::BO_GT) + val = builder.create(loc, val, getConstantIndex(1)); + descr.setLowerBound(val); + return true; + } + } + return false; +} + +bool MLIRScanner::getConstantStep(clang::ForStmt *fors, + AffineLoopDescriptor &descr) { + auto inc = fors->getInc(); + if (auto unaryOp = dyn_cast(inc)) + if (unaryOp->isPrefix() || unaryOp->isPostfix()) { + bool forwardLoop = + unaryOp->getOpcode() == clang::UnaryOperator::Opcode::UO_PostInc || + unaryOp->getOpcode() == clang::UnaryOperator::Opcode::UO_PreInc; + descr.setStep(1); + descr.setForwardMode(forwardLoop); + return true; + } + return false; +} + +bool MLIRScanner::isTrivialAffineLoop(clang::ForStmt *fors, + AffineLoopDescriptor &descr) { + if (!getConstantStep(fors, descr)) { + LLVM_DEBUG(dbgs() << "getConstantStep -> false\n"); + return false; + } + if (!getLowerBound(fors, descr)) { + LLVM_DEBUG(dbgs() << "getLowerBound -> false\n"); + return false; + } + if (!getUpperBound(fors, descr)) { + LLVM_DEBUG(dbgs() << "getUpperBound -> false\n"); + return false; + } + LLVM_DEBUG(dbgs() << "isTrivialAffineLoop -> true\n"); + return true; +} + +void MLIRScanner::buildAffineLoopImpl(clang::ForStmt *fors, mlir::Location loc, + mlir::Value lb, mlir::Value ub, + const AffineLoopDescriptor &descr) { + auto affineOp = builder.create( + loc, lb, builder.getSymbolIdentityMap(), ub, + builder.getSymbolIdentityMap(), descr.getStep(), + /*iterArgs=*/llvm::None); + + auto ® = affineOp.getLoopBody(); + + auto val = (mlir::Value)affineOp.getInductionVar(); + + reg.front().clear(); + + auto oldpoint = builder.getInsertionPoint(); + auto oldblock = builder.getInsertionBlock(); + + builder.setInsertionPointToEnd(®.front()); + + if (!descr.getForwardMode()) { + val = builder.create(loc, val, lb); + val = builder.create( + loc, builder.create(loc, ub, getConstantIndex(1)), val); + } + auto idx = builder.create(loc, val, descr.getType()); + createAndSetAllocOp(descr.getName(), idx, 0); + + // TODO: set loop context. + Visit(fors->getBody()); + builder.create(loc); + + // TODO: set the value of the iteration value to the final bound at the + // end of the loop. + builder.setInsertionPoint(oldblock, oldpoint); +} + +static bool isTerminator(Operation *op) { + return op->mightHaveTrait(); +} + +void MLIRScanner::buildAffineLoop(clang::ForStmt *fors, mlir::Location loc, + const AffineLoopDescriptor &descr) { + mlir::Value lb = descr.getLowerBound(); + mlir::Value ub = descr.getUpperBound(); + buildAffineLoopImpl(fors, loc, lb, ub, descr); + return; +} + +ValueWithOffsets MLIRScanner::VisitWhileStmt(clang::WhileStmt *fors) { + IfScope scope(*this); + scopes.emplace_back(); + + auto loc = getMLIRLocation(fors->getLParenLoc()); + + auto i1Ty = builder.getIntegerType(1); + auto type = mlir::MemRefType::get({}, i1Ty, {}, 0); + auto truev = builder.create( + loc, i1Ty, builder.getIntegerAttr(i1Ty, 1)); + loops.push_back( + (LoopContext){builder.create(loc, type), + builder.create(loc, type)}); + builder.create(loc, truev, loops.back().noBreak); + + auto toadd = builder.getInsertionBlock()->getParent(); + auto &condB = *(new Block()); + toadd->getBlocks().push_back(&condB); + auto &bodyB = *(new Block()); + toadd->getBlocks().push_back(&bodyB); + auto &exitB = *(new Block()); + toadd->getBlocks().push_back(&exitB); + + builder.create(loc, &condB); + + builder.setInsertionPointToStart(&condB); + + if (auto s = fors->getCond()) { + auto condRes = Visit(s); + auto cond = condRes.getValue(builder); + auto ty = cond.getType().cast(); + if (ty.getWidth() != 1) { + ty = builder.getIntegerType(1); + cond = builder.create(loc, cond, ty); + } + auto nb = builder.create(loc, loops.back().noBreak, + std::vector()); + cond = builder.create(loc, cond, nb); + builder.create(loc, cond, &bodyB, &exitB); + } + + builder.setInsertionPointToStart(&bodyB); + builder.create( + loc, + builder.create(loc, loops.back().noBreak, + std::vector()), + loops.back().keepRunning, std::vector()); + + Visit(fors->getBody()); + loops.pop_back(); + // if (builder.getInsertionBlock()->empty() || + // builder.getInsertionBlock()->back().isKnownNonTerminator()) { + builder.create(loc, &condB); + //} + + builder.setInsertionPointToStart(&exitB); + + scopes.pop_back(); + return nullptr; +} + +ValueWithOffsets MLIRScanner::VisitForStmt(clang::ForStmt *fors) { + IfScope scope(*this); + scopes.emplace_back(); + + auto loc = getMLIRLocation(fors->getForLoc()); + + AffineLoopDescriptor affineLoopDescr; + if (Glob.scopLocList.isInScop(fors->getForLoc()) && + isTrivialAffineLoop(fors, affineLoopDescr)) { + buildAffineLoop(fors, loc, affineLoopDescr); + } else { + + if (auto s = fors->getInit()) { + Visit(s); + } + + auto i1Ty = builder.getIntegerType(1); + auto type = mlir::MemRefType::get({}, i1Ty, {}, 0); + auto truev = builder.create( + loc, i1Ty, builder.getIntegerAttr(i1Ty, 1)); + loops.push_back( + (LoopContext){builder.create(loc, type), + builder.create(loc, type)}); + builder.create(loc, truev, loops.back().noBreak); + + auto toadd = builder.getInsertionBlock()->getParent(); + auto &condB = *(new Block()); + toadd->getBlocks().push_back(&condB); + auto &bodyB = *(new Block()); + toadd->getBlocks().push_back(&bodyB); + auto &exitB = *(new Block()); + toadd->getBlocks().push_back(&exitB); + + builder.create(loc, &condB); + + builder.setInsertionPointToStart(&condB); + + if (auto s = fors->getCond()) { + auto condRes = Visit(s); + auto cond = condRes.getValue(builder); + auto ty = cond.getType().cast(); + if (ty.getWidth() != 1) { + ty = builder.getIntegerType(1); + cond = builder.create(loc, cond, ty); + } + auto nb = builder.create( + loc, loops.back().noBreak, std::vector()); + cond = builder.create(loc, cond, nb); + builder.create(loc, cond, &bodyB, &exitB); + } + + builder.setInsertionPointToStart(&bodyB); + builder.create( + loc, + builder.create(loc, loops.back().noBreak, + std::vector()), + loops.back().keepRunning, std::vector()); + + Visit(fors->getBody()); + if (auto s = fors->getInc()) { + Visit(s); + } + loops.pop_back(); + if (builder.getInsertionBlock()->empty() || + !isTerminator(&builder.getInsertionBlock()->back())) { + builder.create(loc, &condB); + } + + builder.setInsertionPointToStart(&exitB); + } + scopes.pop_back(); + return nullptr; +} + +mlir::Value add(MLIRScanner &sc, mlir::OpBuilder &builder, mlir::Location loc, + mlir::Value lhs, mlir::Value rhs) { + assert(lhs); + assert(rhs); + if (auto op = lhs.getDefiningOp()) { + if (op.getValue().cast().getInt() == 0) { + return rhs; + } + } + + if (auto op = lhs.getDefiningOp()) { + if (op.getValue() == 0) { + return rhs; + } + } + + if (auto op = rhs.getDefiningOp()) { + if (op.getValue().cast().getInt() == 0) { + return lhs; + } + } + + if (auto op = rhs.getDefiningOp()) { + if (op.getValue() == 0) { + return lhs; + } + } + return builder.create(loc, lhs, rhs); +} + +mlir::Value MLIRScanner::castToIndex(mlir::Location loc, mlir::Value val) { + assert(val && "Expect non-null value"); + + if (auto op = val.getDefiningOp()) + return getConstantIndex(op.getValue().cast().getInt()); + + return builder.create( + loc, val, mlir::IndexType::get(val.getContext())); +} + +ValueWithOffsets +MLIRScanner::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) { + expr->getConstructor()->dump(); + assert(expr->getConstructionKind() == + clang::CXXConstructExpr::ConstructionKind::CK_Complete); + assert(0 && "illegal use of constructor"); +} + +ValueWithOffsets +MLIRScanner::VisitArraySubscriptExpr(clang::ArraySubscriptExpr *expr) { + auto moo = Visit(expr->getLHS()); + + auto lhs = moo.getValue(builder); + // Check the LHS has been successfully emitted + assert(lhs); + // mlir::Value val = lhs.getValue(builder); + + mlir::Value val = lhs; + + auto rhs = Visit(expr->getRHS()).getValue(builder); + // Check the RHS has been successfully emitted + assert(rhs); + auto idx = castToIndex(getMLIRLocation(expr->getRBracketLoc()), rhs); + + if (val.getType().isa()) { + + std::vector vals = {val}; + idx = + builder.create(loc, idx, builder.getIntegerType(64)); + vals.push_back(idx); + // TODO sub + return ValueWithOffsets( + builder.create(loc, val.getType(), vals), + /*isReference*/ false) + .dereference(builder); + } + if (!val.getType().isa()) { + builder.getInsertionBlock()->dump(); + function.dump(); + expr->dump(); + llvm::errs() << val << "\n"; + } + + ValueWithOffsets dref; + { + auto mt = val.getType().cast(); + auto shape = std::vector(mt.getShape()); + // if (shape.size() > 1) { + // shape.erase(shape.begin()); + //} else { + shape[0] = -1; + //} + auto mt0 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + auto post = builder.create(loc, mt0, val, idx); + // TODO sub + dref = ValueWithOffsets(post, /*isReference*/ false).dereference(builder); + } + assert(dref.isReference); + + auto mt = dref.val.getType().cast(); + auto shape = std::vector(mt.getShape()); + if (shape.size() > 1) { + shape.erase(shape.begin()); + } else { + shape[0] = -1; + } + auto mt0 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + auto post = builder.create(loc, mt0, dref.val, + getConstantIndex(0)); + return ValueWithOffsets(post, /*isReference*/ true); +} + +const clang::FunctionDecl *MLIRScanner::EmitCallee(const Expr *E) { + E = E->IgnoreParens(); + + // Look through function-to-pointer decay. + if (auto ICE = dyn_cast(E)) { + if (ICE->getCastKind() == CK_FunctionToPointerDecay || + ICE->getCastKind() == CK_BuiltinFnToFnPtr) { + return EmitCallee(ICE->getSubExpr()); + } + + // Resolve direct calls. + } else if (auto DRE = dyn_cast(E)) { + if (auto FD = dyn_cast(DRE->getDecl())) { + return FD; + } + } else if (auto ME = dyn_cast(E)) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + // TODO EmitIgnoredExpr(ME->getBase()); + return FD; + } + + // Look through template substitutions. + } else if (auto NTTP = dyn_cast(E)) { + return EmitCallee(NTTP->getReplacement()); + } + + assert(0 && "indirect references not handled"); +} + +ValueWithOffsets MLIRScanner::VisitCallExpr(clang::CallExpr *expr) { + if (auto ic = dyn_cast(expr->getCallee())) + if (auto ME = dyn_cast(ic->getSubExpr())) { + auto memberName = ME->getMemberDecl()->getName(); + + if (auto sr2 = dyn_cast(ME->getBase())) { + if (auto sr = dyn_cast(sr2->getSourceExpr())) { + if (sr->getDecl()->getName() == "blockIdx") { + auto mlirType = getMLIRType(expr->getType()); + if (memberName == "__fetch_builtin_x") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "x"), + mlirType), + /*isReference*/ false); + } + if (memberName == "__fetch_builtin_y") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "y"), + mlirType), + /*isReference*/ false); + } + if (memberName == "__fetch_builtin_z") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "z"), + mlirType), + /*isReference*/ false); + } + } + if (sr->getDecl()->getName() == "blockDim") { + auto mlirType = getMLIRType(expr->getType()); + if (memberName == "__fetch_builtin_x") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "x"), + mlirType), + /*isReference*/ false); + } + if (memberName == "__fetch_builtin_y") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "y"), + mlirType), + /*isReference*/ false); + } + if (memberName == "__fetch_builtin_z") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "z"), + mlirType), + /*isReference*/ false); + } + } + if (sr->getDecl()->getName() == "threadIdx") { + auto mlirType = getMLIRType(expr->getType()); + if (memberName == "__fetch_builtin_x") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "x"), + mlirType), + /*isReference*/ false); + } + if (memberName == "__fetch_builtin_y") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "y"), + mlirType), + /*isReference*/ false); + } + if (memberName == "__fetch_builtin_z") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "z"), + mlirType), + /*isReference*/ false); + } + } + if (sr->getDecl()->getName() == "gridDim") { + auto mlirType = getMLIRType(expr->getType()); + if (memberName == "__fetch_builtin_x") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "x"), + mlirType), + /*isReference*/ false); + } + if (memberName == "__fetch_builtin_y") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "y"), + mlirType), + /*isReference*/ false); + } + if (memberName == "__fetch_builtin_z") { + return ValueWithOffsets( + builder.create( + loc, + builder.create( + loc, mlir::IndexType::get(builder.getContext()), "z"), + mlirType), + /*isReference*/ false); + } + } + } + } + } + + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "__syncthreads") { + builder.create(loc); + return nullptr; + } + } + + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "__shfl_up_sync") { + assert(0 && "__shfl_up_sync unhandled"); + return nullptr; + } + } + + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "__log2f") { + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(Visit(a).getValue(builder)); + } + return ValueWithOffsets( + builder.create(loc, args[0]), + /*isReference*/ false); + } + } + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "sqrtf" || + sr->getDecl()->getName() == "sqrt") { + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(Visit(a).getValue(builder)); + } + return ValueWithOffsets( + builder.create(loc, args[0]), + /*isReference*/ false); + } + } + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "expf" || + sr->getDecl()->getName() == "exp") { + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(Visit(a).getValue(builder)); + } + return ValueWithOffsets(builder.create(loc, args[0]), + /*isReference*/ false); + } + } + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "sin") { + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(Visit(a).getValue(builder)); + } + return ValueWithOffsets(builder.create(loc, args[0]), + /*isReference*/ false); + } + } + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "cos") { + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(Visit(a).getValue(builder)); + } + return ValueWithOffsets(builder.create(loc, args[0]), + /*isReference*/ false); + } + } + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "atomicAdd") { + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(Visit(a)); + } + auto a1 = args[1].getValue(builder); + if (a1.getType().isa()) + return ValueWithOffsets( + builder.create( + loc, a1.getType(), AtomicRMWKind::addi, a1, + args[0].getValue(builder), + std::vector({getConstantIndex(0)})), + /*isReference*/ false); + else + return ValueWithOffsets( + builder.create( + loc, a1.getType(), AtomicRMWKind::addf, a1, + args[0].getValue(builder), + std::vector({getConstantIndex(0)})), + /*isReference*/ false); + } + } + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "atomicOr") { +#if 1 + llvm_unreachable("atomicOr unhandled"); + assert(0 && "atomicOr unhandled"); +#else + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(Visit(a)); + } + auto a1 = args[1].getValue(builder); + return ValueWithOffsets( + builder.create( + loc, a1.getType(), AtomicRMWKind::ori, a1, + args[0].getValue(builder), + std::vector({getConstantIndex(0)})), + /*isReference*/ false); +#endif + } + } + + auto getLLVM = [&](Expr *E) -> mlir::Value { + if (auto IC1 = dyn_cast(E)) { + if (auto IC2 = dyn_cast(IC1->getSubExpr())) { + if (auto slit = dyn_cast(IC2->getSubExpr())) { + return Glob.GetOrCreateGlobalLLVMString(loc, builder, + slit->getString()); + } + } + if (auto slit = dyn_cast(IC1->getSubExpr())) { + return Glob.GetOrCreateGlobalLLVMString(loc, builder, + slit->getString()); + } + } + return Visit(E).getValue(builder); + }; + + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + // TODO add pow to standard dialect + if (sr->getDecl()->getName() == "__powf" || + sr->getDecl()->getName() == "pow" || + sr->getDecl()->getName() == "powf") { + auto mlirType = getMLIRType(expr->getType()); + mlir::Type llvmType = + mlir::LLVM::TypeFromLLVMIRTranslator(*mlirType.getContext()) + .translateType(getLLVMType(expr->getType())); + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(Visit(a).getValue(builder)); + } + return ValueWithOffsets( + builder.create(loc, llvmType, args[0], args[1]), + /*isReference*/ false); + } + } + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "fprintf" || + sr->getDecl()->getName() == "printf") { + + auto tocall = EmitCallee(expr->getCallee()); + auto fprintfF = Glob.GetOrCreateLLVMFunction(tocall); + + std::vector args; + size_t i = 0; + for (auto a : expr->arguments()) { + args.push_back(getLLVM(a)); + i++; + } + + builder.create(loc, fprintfF, args); + + return nullptr; + } + } + + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "free") { + + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(Visit(a).getValue(builder)); + } + + builder.create(loc, args[0]); + return nullptr; + } + } + + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "fscanf" || + sr->getDecl()->getName() == "scanf") { + auto tocall = EmitCallee(expr->getCallee()); + auto strcmpF = Glob.GetOrCreateLLVMFunction(tocall); + + std::vector args; + std::vector> ops; + std::map counts; + for (auto a : expr->arguments()) { + auto v = getLLVM(a); + if (auto mt = v.getType().dyn_cast()) { + auto T = mlir::LLVM::LLVMPointerType::get(mt.getElementType()); + auto idx = counts[T.getAsOpaquePointer()]++; + auto aop = allocateBuffer(idx, T); + args.push_back(aop.getResult()); + ops.emplace_back(aop.getResult(), v); + } else + args.push_back(v); + } + auto called = builder.create(loc, strcmpF, args); + for (auto pair : ops) { + auto lop = builder.create(loc, pair.first); + builder.create( + loc, lop, pair.second, + std::vector({getConstantIndex(0)})); + } + return ValueWithOffsets(called.getResult(0), /*isReference*/ false); + } + } + std::set funcs = {"strcmp", "open", "fopen", + "close", "fclose", "atoi"}; + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (funcs.count(sr->getDecl()->getName().str())) { + auto tocall = EmitCallee(expr->getCallee()); + auto strcmpF = Glob.GetOrCreateLLVMFunction(tocall); + + std::vector args; + for (auto a : expr->arguments()) { + args.push_back(getLLVM(a)); + } + auto called = builder.create(loc, strcmpF, args); + + return ValueWithOffsets(called.getResult(0), /*isReference*/ false); + } + } + + if (auto ic = dyn_cast(expr->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "gettimeofday") { + auto tocall = EmitCallee(expr->getCallee()); + auto fprintfF = Glob.GetOrCreateLLVMFunction(tocall); + + std::vector args; + size_t i = 0; + mlir::Value tostore = nullptr; + mlir::Value alloc; + for (auto a : expr->arguments()) { + + if (i == 0) { + tostore = Visit(a).getValue(builder); + i++; + auto indexType = mlir::IntegerType::get(module.getContext(), 64); + auto one = builder.create( + loc, indexType, + builder.getIntegerAttr(builder.getIntegerType(64), 1)); + alloc = builder.create( + loc, + Glob.typeTranslator.translateType((getLLVMType(a->getType()))), + one, 0); + args.push_back(alloc); + continue; + } + auto llvmType = + Glob.typeTranslator.translateType(getLLVMType(a->getType())); + + if (auto IC1 = dyn_cast(a)) { + if (IC1->getCastKind() == clang::CastKind::CK_NullToPointer) { + args.push_back(builder.create(loc, llvmType)); + i++; + continue; + } + } + mlir::Value val = Visit(a).getValue(builder); + args.push_back(val); + i++; + } + assert(alloc); + + auto co = builder.create(loc, fprintfF, args) + .getResult(0); + // co = builder.create( // was DialectCastOp + // loc, getMLIRType(expr->getType()), co); + auto ret = co; + + auto loaded = builder.create(loc, alloc); + + auto st = loaded.getType().dyn_cast(); + for (size_t i = 0; i < st.getBody().size(); i++) { + mlir::Value ev = builder.create( + loc, st.getBody()[i], loaded, builder.getI64ArrayAttr(i)); + // ev = builder.create( + // loc, + // Glob.getMLIRType(Glob.reverseTypeTranslator.translateType( + // ev.getType(), + // ev); + builder.create( + loc, ev, tostore, + std::vector({getConstantIndex(i)})); + } + + return ValueWithOffsets(ret, /*isReference*/ false); + } + } + + auto tocall = EmitDirectCallee(EmitCallee(expr->getCallee())); + std::vector args; + auto fnType = tocall.getType(); + size_t i = 0; + for (auto a : expr->arguments()) { + mlir::Value val = Visit(a).getValue(builder); + if (i >= fnType.getInputs().size()) { + expr->dump(); + tocall.dump(); + fnType.dump(); + assert(0 && "too many arguments in calls"); + } + /* + if (val.getType() != fnType.getInput(i)) { + if (auto MR1 = val.getType().dyn_cast()) { + if (auto MR2 = fnType.getInput(i).dyn_cast()) { + val = builder.create(loc, val, MR2); + } + } + } + */ + args.push_back(val); + i++; + } + auto op = builder.create(loc, tocall, args); + if (op.getNumResults()) + return ValueWithOffsets(op.getResult(0), /*isReference*/ false); + else + return nullptr; + llvm::errs() << "do not support indirecto call of " << tocall << "\n"; + assert(0 && "no indirect"); +} + +mlir::Value MLIRScanner::getConstantIndex(int x) { + if (constants.find(x) != constants.end()) { + return constants[x]; + } + mlir::OpBuilder subbuilder(builder.getContext()); + subbuilder.setInsertionPointToStart(entryBlock); + return constants[x] = subbuilder.create(loc, x); +} + +ValueWithOffsets MLIRScanner::VisitMSPropertyRefExpr(MSPropertyRefExpr *expr) { + assert(0 && "unhandled ms propertyref"); + // TODO obviously fake + return nullptr; +} + +ValueWithOffsets +MLIRScanner::VisitPseudoObjectExpr(clang::PseudoObjectExpr *expr) { + return Visit(expr->getResultExpr()); +} + +ValueWithOffsets MLIRScanner::VisitUnaryOperator(clang::UnaryOperator *U) { + auto sub = Visit(U->getSubExpr()); + auto ty = getMLIRType(U->getType()); + + switch (U->getOpcode()) { + case clang::UnaryOperator::Opcode::UO_Extension: { + return sub; + } + case clang::UnaryOperator::Opcode::UO_LNot: { + mlir::Value val = sub.getValue(builder); + auto ty = val.getType().cast(); + if (ty.getWidth() != 1) { + ty = builder.getIntegerType(1); + val = builder.create(loc, val, ty); + } + auto c1 = builder.create(loc, ty, + builder.getIntegerAttr(ty, 1)); + return ValueWithOffsets(builder.create(loc, val, c1), + /*isReference*/ false); + } + case clang::UnaryOperator::Opcode::UO_Deref: { + auto dref = sub.dereference(builder); + assert(dref.isReference); + if (dref.val.getType().isa()) { + return dref; + // auto res = builder.create(loc, dref.val); + // TODO sub + // return ValueWithOffsets(res, /*isReference*/false); + } + if (!dref.val.getType().isa()) { + function.dump(); + U->dump(); + llvm::errs() << dref.val << "\n"; + } + + auto mt = dref.val.getType().cast(); + auto shape = std::vector(mt.getShape()); + if (shape.size() > 1) { + shape.erase(shape.begin()); + } else { + shape[0] = -1; + } + auto mt0 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + auto post = builder.create(loc, mt0, dref.val, + getConstantIndex(0)); + return ValueWithOffsets(post, /*isReference*/ true); + } + case clang::UnaryOperator::Opcode::UO_AddrOf: { + assert(sub.isReference); + auto mt = sub.val.getType().cast(); + auto shape = std::vector(mt.getShape()); + shape[0] = -1; + auto mt0 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + + return ValueWithOffsets(builder.create(loc, sub.val, mt0), + /*isReference*/ false); + } + case clang::UnaryOperator::Opcode::UO_Minus: { + if (auto ft = ty.dyn_cast()) { + return ValueWithOffsets( + builder.create(loc, sub.getValue(builder)), + /*isReference*/ false); + } else { + return ValueWithOffsets(builder.create( + loc, + builder.create( + loc, 0, ty.cast()), + sub.getValue(builder)), + /*isReference*/ false); + } + } + case clang::UnaryOperator::Opcode::UO_PreInc: + case clang::UnaryOperator::Opcode::UO_PostInc: { + assert(sub.isReference); + auto prev = sub.getValue(builder); + + mlir::Value next; + if (auto ft = ty.dyn_cast()) { + next = builder.create( + loc, prev, + builder.create( + loc, APFloat(ft.getFloatSemantics(), "1"), ft)); + } else { + next = builder.create( + loc, prev, + builder.create(loc, 1, + ty.cast())); + } + assert(next.getType() == + sub.val.getType().cast().getElementType()); + builder.create( + loc, next, sub.val, std::vector({getConstantIndex(0)})); + return ValueWithOffsets( + (U->getOpcode() == clang::UnaryOperator::Opcode::UO_PostInc) ? prev + : next, + /*isReference*/ false); + } + case clang::UnaryOperator::Opcode::UO_PreDec: + case clang::UnaryOperator::Opcode::UO_PostDec: { + assert(sub.isReference); + auto prev = sub.getValue(builder); + + mlir::Value next; + if (auto ft = ty.dyn_cast()) { + next = builder.create( + loc, prev, + builder.create( + loc, APFloat(ft.getFloatSemantics(), "1"), ft)); + } else { + next = builder.create( + loc, prev, + builder.create(loc, 1, + ty.cast())); + } + assert(next.getType() == + sub.val.getType().cast().getElementType()); + builder.create( + loc, next, sub.val, std::vector({getConstantIndex(0)})); + return ValueWithOffsets( + (U->getOpcode() == clang::UnaryOperator::Opcode::UO_PostInc) ? prev + : next, + /*isReference*/ false); + } + default: { + U->dump(); + assert(0 && "unhandled opcode"); + } + } +} + +ValueWithOffsets MLIRScanner::VisitSubstNonTypeTemplateParmExpr( + SubstNonTypeTemplateParmExpr *expr) { + return Visit(expr->getReplacement()); +} + +ValueWithOffsets +MLIRScanner::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Uop) { + switch (Uop->getKind()) { + case UETT_SizeOf: { + auto value = getTypeSize(Uop->getTypeOfArgument()); + auto ty = getMLIRType(Uop->getType()).cast(); + return ValueWithOffsets(builder.create( + loc, ty, builder.getIntegerAttr(ty, value)), + /*isReference*/ false); + } + default: + Uop->dump(); + assert(0 && "unhandled VisitUnaryExprOrTypeTraitExpr"); + } +} + +bool isInAffineScope(Operation *op) { + auto *curOp = op; + while (auto *parentOp = curOp->getParentOp()) { + if (isa(parentOp)) + return true; + curOp = parentOp; + } + return false; +} + +bool hasAffineArith(Operation *op, AffineExpr &expr, + mlir::Value &affineForIndVar) { + // skip IndexCastOp + if (isa(op)) + return hasAffineArith(op->getOperand(0).getDefiningOp(), expr, + affineForIndVar); + + // induction variable are modelled as memref<1xType> + // %1 = index_cast %induction : index to i32 + // %2 = alloca() : memref<1xi32> + // store %1, %2[0] : memref<1xi32> + // ... + // %5 = load %2[0] : memref<1xf32> + if (isa(op)) { + auto load = cast(op); + auto loadOperand = load.getOperand(0); + if (loadOperand.getType().cast().getShape().size() != 1) + return false; + auto maybeAllocaOp = loadOperand.getDefiningOp(); + if (!isa(maybeAllocaOp)) + return false; + auto allocaUsers = maybeAllocaOp->getUsers(); + if (llvm::none_of(allocaUsers, [](mlir::Operation *op) { + if (isa(op)) + return true; + return false; + })) + return false; + for (auto user : allocaUsers) + if (auto storeOp = dyn_cast(user)) { + auto storeOperand = storeOp.getOperand(0); + auto maybeIndexCast = storeOperand.getDefiningOp(); + if (!isa(maybeIndexCast)) + return false; + auto indexCastOperand = maybeIndexCast->getOperand(0); + if (auto blockArg = indexCastOperand.dyn_cast()) { + if (auto affineForOp = dyn_cast( + blockArg.getOwner()->getParentOp())) + affineForIndVar = affineForOp.getInductionVar(); + else + return false; + } + } + return true; + } + + // at this point we expect only AddIOp or MulIOp + if ((!isa(op)) && (!isa(op))) { + return false; + } + + // make sure that the current op has at least one constant operand + // (ConstantIndexOp or ConstantIntOp) + if (llvm::none_of(op->getOperands(), [](mlir::Value operand) { + return (isa(operand.getDefiningOp()) || + isa(operand.getDefiningOp())); + })) + return false; + + // build affine expression by adding or multiplying constants. + // and keep iterating on the non-constant index + mlir::Value nonCstOperand = nullptr; + for (auto operand : op->getOperands()) { + if (auto constantIndexOp = + dyn_cast(operand.getDefiningOp())) { + if (isa(op)) + expr = expr + constantIndexOp.getValue(); + else + expr = expr * constantIndexOp.getValue(); + } else if (auto constantIntOp = + dyn_cast(operand.getDefiningOp())) { + if (isa(op)) + expr = expr + constantIntOp.getValue(); + else + expr = expr * constantIntOp.getValue(); + } else + nonCstOperand = operand; + } + return hasAffineArith(nonCstOperand.getDefiningOp(), expr, affineForIndVar); +} + +ValueWithOffsets MLIRScanner::VisitBinaryOperator(clang::BinaryOperator *BO) { + auto lhs = Visit(BO->getLHS()); + if (!lhs.val && BO->getOpcode() != clang::BinaryOperator::Opcode::BO_Comma) { + BO->dump(); + BO->getLHS()->dump(); + assert(lhs.val); + } + auto fixInteger = [&](mlir::Value res) { + auto prevTy = res.getType().cast(); + auto postTy = getMLIRType(BO->getType()).cast(); + bool signedType = true; + if (auto bit = dyn_cast(&*BO->getType())) { + if (bit->isUnsignedInteger()) + signedType = false; + if (bit->isSignedInteger()) + signedType = true; + } + if (postTy != prevTy) { + if (signedType) { + res = builder.create(loc, res, postTy); + } else { + res = builder.create(loc, res, postTy); + } + } + return ValueWithOffsets(res, /*isReference*/ false); + }; + + switch (BO->getOpcode()) { + case clang::BinaryOperator::Opcode::BO_LAnd: { + mlir::Type types[] = {builder.getIntegerType(1)}; + auto cond = lhs.getValue(builder); + auto prevTy = cond.getType().cast(); + if (!prevTy.isInteger(1)) { + auto postTy = builder.getI1Type(); + cond = builder.create(loc, cond, postTy); + } + auto ifOp = builder.create(loc, types, cond, + /*hasElseRegion*/ true); + + auto oldpoint = builder.getInsertionPoint(); + auto oldblock = builder.getInsertionBlock(); + builder.setInsertionPointToStart(&ifOp.thenRegion().back()); + + auto rhs = Visit(BO->getRHS()).getValue(builder); + assert(rhs != nullptr); + if (!rhs.getType().cast().isInteger(1)) { + auto postTy = builder.getI1Type(); + rhs = builder.create(loc, rhs, postTy); + } + mlir::Value truearray[] = {rhs}; + builder.create(loc, truearray); + + builder.setInsertionPointToStart(&ifOp.elseRegion().back()); + mlir::Value falsearray[] = {builder.create( + loc, types[0], builder.getIntegerAttr(types[0], 0))}; + builder.create(loc, falsearray); + + builder.setInsertionPoint(oldblock, oldpoint); + return fixInteger(ifOp.getResult(0)); + } + case clang::BinaryOperator::Opcode::BO_LOr: { + mlir::Type types[] = {builder.getIntegerType(1)}; + auto cond = lhs.getValue(builder); + auto prevTy = cond.getType().cast(); + if (!prevTy.isInteger(1)) { + auto postTy = builder.getI1Type(); + cond = builder.create(loc, cond, postTy); + } + auto ifOp = builder.create(loc, types, cond, + /*hasElseRegion*/ true); + + auto oldpoint = builder.getInsertionPoint(); + auto oldblock = builder.getInsertionBlock(); + builder.setInsertionPointToStart(&ifOp.thenRegion().back()); + + mlir::Value truearray[] = {builder.create( + loc, types[0], builder.getIntegerAttr(types[0], 1))}; + builder.create(loc, truearray); + + builder.setInsertionPointToStart(&ifOp.elseRegion().back()); + auto rhs = Visit(BO->getRHS()).getValue(builder); + if (!rhs.getType().cast().isInteger(1)) { + auto postTy = builder.getI1Type(); + rhs = builder.create(loc, rhs, postTy); + } + assert(rhs != nullptr); + mlir::Value falsearray[] = {rhs}; + builder.create(loc, falsearray); + + builder.setInsertionPoint(oldblock, oldpoint); + + return fixInteger(ifOp.getResult(0)); + } + default: + break; + } + auto rhs = Visit(BO->getRHS()); + if (!rhs.val && BO->getOpcode() != clang::BinaryOperator::Opcode::BO_Comma) { + BO->getRHS()->dump(); + assert(rhs.val); + } + // TODO note assumptions made here about unsigned / unordered + bool signedType = true; + if (auto bit = dyn_cast(&*BO->getType())) { + if (bit->isUnsignedInteger()) + signedType = false; + if (bit->isSignedInteger()) + signedType = true; + } + switch (BO->getOpcode()) { + case clang::BinaryOperator::Opcode::BO_Shr: { + if (signedType) + return ValueWithOffsets( + builder.create(loc, lhs.getValue(builder), + rhs.getValue(builder)), + /*isReference*/ false); + else + return ValueWithOffsets( + builder.create(loc, lhs.getValue(builder), + rhs.getValue(builder)), + /*isReference*/ false); + } + case clang::BinaryOperator::Opcode::BO_Shl: { + return ValueWithOffsets( + builder.create(loc, lhs.getValue(builder), + rhs.getValue(builder)), + /*isReference*/ false); + } + case clang::BinaryOperator::Opcode::BO_And: { + return ValueWithOffsets(builder.create(loc, + lhs.getValue(builder), + rhs.getValue(builder)), + /*isReference*/ false); + } + case clang::BinaryOperator::Opcode::BO_Or: { + // TODO short circuit + return ValueWithOffsets(builder.create(loc, + lhs.getValue(builder), + rhs.getValue(builder)), + /*isReference*/ false); + } + case clang::BinaryOperator::Opcode::BO_GT: { + auto lhs_v = lhs.getValue(builder); + mlir::Value res; + if (lhs_v.getType().isa()) { + res = builder.create(loc, mlir::CmpFPredicate::UGT, lhs_v, + rhs.getValue(builder)); + } else { + res = builder.create( + loc, signedType ? mlir::CmpIPredicate::sgt : mlir::CmpIPredicate::ugt, + lhs_v, rhs.getValue(builder)); + } + return fixInteger(res); + } + case clang::BinaryOperator::Opcode::BO_GE: { + auto lhs_v = lhs.getValue(builder); + mlir::Value res; + if (lhs_v.getType().isa()) { + res = builder.create(loc, mlir::CmpFPredicate::UGE, lhs_v, + rhs.getValue(builder)); + } else { + res = builder.create( + loc, signedType ? mlir::CmpIPredicate::sge : mlir::CmpIPredicate::uge, + lhs_v, rhs.getValue(builder)); + } + return fixInteger(res); + } + case clang::BinaryOperator::Opcode::BO_LT: { + auto lhs_v = lhs.getValue(builder); + mlir::Value res; + if (lhs_v.getType().isa()) { + res = builder.create(loc, mlir::CmpFPredicate::ULT, lhs_v, + rhs.getValue(builder)); + } else { + res = builder.create( + loc, signedType ? mlir::CmpIPredicate::slt : mlir::CmpIPredicate::ult, + lhs_v, rhs.getValue(builder)); + } + return fixInteger(res); + } + case clang::BinaryOperator::Opcode::BO_LE: { + auto lhs_v = lhs.getValue(builder); + mlir::Value res; + if (lhs_v.getType().isa()) { + res = builder.create(loc, mlir::CmpFPredicate::ULE, lhs_v, + rhs.getValue(builder)); + } else { + res = builder.create( + loc, signedType ? mlir::CmpIPredicate::sle : mlir::CmpIPredicate::ule, + lhs_v, rhs.getValue(builder)); + } + return fixInteger(res); + } + case clang::BinaryOperator::Opcode::BO_EQ: { + auto lhs_v = lhs.getValue(builder); + mlir::Value res; + if (lhs_v.getType().isa()) { + res = builder.create(loc, mlir::CmpFPredicate::UEQ, lhs_v, + rhs.getValue(builder)); + } else { + res = builder.create(loc, mlir::CmpIPredicate::eq, lhs_v, + rhs.getValue(builder)); + } + return fixInteger(res); + } + case clang::BinaryOperator::Opcode::BO_NE: { + auto lhs_v = lhs.getValue(builder); + mlir::Value res; + if (lhs_v.getType().isa()) { + res = builder.create(loc, mlir::CmpFPredicate::UNE, lhs_v, + rhs.getValue(builder)); + } else { + res = builder.create(loc, mlir::CmpIPredicate::ne, lhs_v, + rhs.getValue(builder)); + } + return fixInteger(res); + } + case clang::BinaryOperator::Opcode::BO_Mul: { + auto lhs_v = lhs.getValue(builder); + if (lhs_v.getType().isa()) { + return ValueWithOffsets( + builder.create(loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + } else { + return ValueWithOffsets( + builder.create(loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + } + } + case clang::BinaryOperator::Opcode::BO_Div: { + auto lhs_v = lhs.getValue(builder); + if (lhs_v.getType().isa()) { + return ValueWithOffsets( + builder.create(loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + ; + } else { + if (signedType) + return ValueWithOffsets(builder.create( + loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + else + return ValueWithOffsets(builder.create( + loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + } + } + case clang::BinaryOperator::Opcode::BO_Rem: { + auto lhs_v = lhs.getValue(builder); + if (lhs_v.getType().isa()) { + return ValueWithOffsets( + builder.create(loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + } else { + if (signedType) + return ValueWithOffsets(builder.create( + loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + else + return ValueWithOffsets(builder.create( + loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + } + } + case clang::BinaryOperator::Opcode::BO_Add: { + auto lhs_v = lhs.getValue(builder); + if (lhs_v.getType().isa()) { + return ValueWithOffsets( + builder.create(loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + } else if (auto mt = lhs_v.getType().dyn_cast()) { + auto shape = std::vector(mt.getShape()); + shape[0] = -1; + auto mt0 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + auto ptradd = rhs.getValue(builder); + ptradd = castToIndex(loc, ptradd); + return ValueWithOffsets( + builder.create(loc, mt0, lhs_v, ptradd), + /*isReference*/ false); + } else { + return ValueWithOffsets( + builder.create(loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + } + } + case clang::BinaryOperator::Opcode::BO_Sub: { + auto lhs_v = lhs.getValue(builder); + if (lhs_v.getType().isa()) { + auto right = rhs.getValue(builder); + assert(right.getType() == lhs_v.getType()); + return ValueWithOffsets(builder.create(loc, lhs_v, right), + /*isReference*/ false); + } else { + return ValueWithOffsets( + builder.create(loc, lhs_v, rhs.getValue(builder)), + /*isReference*/ false); + } + } + case clang::BinaryOperator::Opcode::BO_Assign: { + assert(lhs.isReference); + mlir::Value tostore = rhs.getValue(builder); + if (tostore.getType() != + lhs.val.getType().cast().getElementType()) { + if (auto prevTy = tostore.getType().dyn_cast()) { + if (auto postTy = lhs.val.getType() + .cast() + .getElementType() + .dyn_cast()) { + bool signedType = true; + if (auto bit = dyn_cast(&*BO->getType())) { + if (bit->isUnsignedInteger()) + signedType = false; + if (bit->isSignedInteger()) + signedType = true; + } + + if (prevTy.getWidth() < postTy.getWidth()) { + if (signedType) { + tostore = + builder.create(loc, tostore, postTy); + } else { + tostore = + builder.create(loc, tostore, postTy); + } + } else if (prevTy.getWidth() > postTy.getWidth()) { + tostore = builder.create(loc, tostore, postTy); + } + } + } + } + if (tostore.getType() != + lhs.val.getType().cast().getElementType()) { + BO->dump(); + function.dump(); + llvm::errs() << " lhs.val: " << lhs.val << "\n"; + llvm::errs() << " tostore: " << tostore << "\n"; + } + assert(tostore.getType() == + lhs.val.getType().cast().getElementType()); + builder.create( + loc, tostore, lhs.val, std::vector({getConstantIndex(0)})); + return lhs; + } + + case clang::BinaryOperator::Opcode::BO_Comma: { + return rhs; + } + + case clang::BinaryOperator::Opcode::BO_AddAssign: { + assert(lhs.isReference); + auto prev = lhs.getValue(builder); + + mlir::Value result; + if (prev.getType().isa()) { + result = builder.create(loc, prev, rhs.getValue(builder)); + } else { + result = builder.create(loc, prev, rhs.getValue(builder)); + } + assert(result.getType() == + lhs.val.getType().cast().getElementType()); + builder.create( + loc, result, lhs.val, std::vector({getConstantIndex(0)})); + return lhs; + } + case clang::BinaryOperator::Opcode::BO_SubAssign: { + assert(lhs.isReference); + auto prev = lhs.getValue(builder); + + mlir::Value result; + if (prev.getType().isa()) { + auto right = rhs.getValue(builder); + if (right.getType() != prev.getType()) { + auto prevTy = right.getType().cast(); + auto postTy = getMLIRType(BO->getType()).cast(); + + if (prevTy.getWidth() < postTy.getWidth()) { + right = builder.create(loc, right, postTy); + } else { + right = builder.create(loc, right, postTy); + } + } + if (right.getType() != prev.getType()) { + BO->dump(); + llvm::errs() << " p:" << prev << " r:" << right << "\n"; + } + assert(right.getType() == prev.getType()); + result = builder.create(loc, prev, right); + } else { + result = builder.create(loc, prev, rhs.getValue(builder)); + } + assert(result.getType() == + lhs.val.getType().cast().getElementType()); + builder.create( + loc, result, lhs.val, std::vector({getConstantIndex(0)})); + return lhs; + } + case clang::BinaryOperator::Opcode::BO_MulAssign: { + assert(lhs.isReference); + auto prev = lhs.getValue(builder); + + mlir::Value result; + if (prev.getType().isa()) { + result = builder.create(loc, prev, rhs.getValue(builder)); + } else { + result = builder.create(loc, prev, rhs.getValue(builder)); + } + assert(result.getType() == + lhs.val.getType().cast().getElementType()); + builder.create( + loc, result, lhs.val, std::vector({getConstantIndex(0)})); + return lhs; + } + case clang::BinaryOperator::Opcode::BO_DivAssign: { + assert(lhs.isReference); + auto prev = lhs.getValue(builder); + + mlir::Value result; + if (prev.getType().isa()) { + result = builder.create(loc, prev, rhs.getValue(builder)); + } else { + if (signedType) + result = builder.create(loc, prev, + rhs.getValue(builder)); + else + result = builder.create(loc, prev, + rhs.getValue(builder)); + } + assert(result.getType() == + lhs.val.getType().cast().getElementType()); + builder.create( + loc, result, lhs.val, std::vector({getConstantIndex(0)})); + return lhs; + } + case clang::BinaryOperator::Opcode::BO_ShrAssign: { + assert(lhs.isReference); + auto prev = lhs.getValue(builder); + + mlir::Value result; + + if (signedType) + result = builder.create(loc, prev, + rhs.getValue(builder)); + else + result = builder.create( + loc, prev, rhs.getValue(builder)); + assert(result.getType() == + lhs.val.getType().cast().getElementType()); + builder.create( + loc, result, lhs.val, std::vector({getConstantIndex(0)})); + return lhs; + } + case clang::BinaryOperator::Opcode::BO_OrAssign: { + assert(lhs.isReference); + auto prev = lhs.getValue(builder); + + mlir::Value result = + builder.create(loc, prev, rhs.getValue(builder)); + assert(result.getType() == + lhs.val.getType().cast().getElementType()); + builder.create( + loc, result, lhs.val, std::vector({getConstantIndex(0)})); + return lhs; + } + + default: { + BO->dump(); + assert(0 && "unhandled opcode"); + } + } +} + +ValueWithOffsets MLIRScanner::VisitAttributedStmt(AttributedStmt *AS) { + llvm::errs() << "warning ignoring attributes\n"; + return Visit(AS->getSubStmt()); +} + +ValueWithOffsets MLIRScanner::VisitExprWithCleanups(ExprWithCleanups *E) { + auto ret = Visit(E->getSubExpr()); + for (auto &child : E->children()) { + child->dump(); + assert(0 && "cleanup not handled"); + } + return ret; +} + +ValueWithOffsets MLIRScanner::VisitDeclRefExpr(DeclRefExpr *E) { + auto val = getValue(E->getDecl()->getName().str()); + // E->dump(); + // llvm::errs() << "DeclRefExpr: " << val.val << " - isReference: " << + // val.isReference << "\n"; + return val; +} + +ValueWithOffsets MLIRScanner::VisitOpaqueValueExpr(OpaqueValueExpr *E) { + if (!E->getSourceExpr()) { + E->dump(); + assert(E->getSourceExpr()); + } + for (auto c : E->children()) { + // c->dump(); + } + auto res = Visit(E->getSourceExpr()); + if (!res.val) { + E->dump(); + E->getSourceExpr()->dump(); + assert(res.val); + } + return res; +} + +ValueWithOffsets MLIRScanner::VisitMemberExpr(MemberExpr *ME) { + auto memberName = ME->getMemberDecl()->getName(); + if (auto sr2 = dyn_cast(ME->getBase())) { + if (auto sr = dyn_cast(sr2->getSourceExpr())) { + if (sr->getDecl()->getName() == "blockIdx") { + if (memberName == "__fetch_builtin_x") { + } + llvm::errs() << "known block index"; + } + if (sr->getDecl()->getName() == "blockDim") { + llvm::errs() << "known block dim"; + } + if (sr->getDecl()->getName() == "threadIdx") { + llvm::errs() << "known thread index"; + } + if (sr->getDecl()->getName() == "gridDim") { + llvm::errs() << "known grid index"; + } + } + } + auto base = Visit(ME->getBase()); + QualType ty = ME->getBase()->getType(); + if (ME->isArrow()) { + base = base.dereference(builder); + ty = cast(ty.getDesugaredType(Glob.astContext)) + ->getPointeeType(); + } + if (!base.isReference) { + ME->dump(); + llvm::errs() << base.val << "\n"; + } + assert(base.isReference); + auto ds = ty.getDesugaredType(Glob.astContext); + auto rd = cast(ds)->getDecl(); + auto &layout = Glob.CGM.getTypes().getCGRecordLayout(rd); + const FieldDecl *field = nullptr; + for (auto f : rd->fields()) { + if (f->getName() == memberName) { + field = f; + } + } + auto mt = base.val.getType().cast(); + auto shape = std::vector(mt.getShape()); + if (shape.size() > 1) { + shape.erase(shape.begin()); + } else { + shape[0] = -1; + } + auto mt0 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + shape[0] = -1; + auto mt1 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + return ValueWithOffsets(builder.create( + loc, mt1, + builder.create( + loc, mt0, base.val, getConstantIndex(0)), + getConstantIndex(layout.getLLVMFieldNo(field))), + /*isReference*/ true); +} + +ValueWithOffsets MLIRScanner::VisitCastExpr(CastExpr *E) { + switch (E->getCastKind()) { + + case clang::CastKind::CK_NullToPointer: { + auto llvmType = + Glob.typeTranslator.translateType(getLLVMType(E->getType())); + return ValueWithOffsets(builder.create(loc, llvmType), + /*isReference*/ false); + } + + case clang::CastKind::CK_BitCast: { + + if (auto CI = dyn_cast(E->getSubExpr())) + if (auto ic = dyn_cast(CI->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "polybench_alloc_data") { + auto mt = getMLIRType(E->getType()).cast(); + + auto shape = std::vector(mt.getShape()); + shape.erase(shape.begin()); + auto mt0 = + mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + + auto alloc = builder.create(loc, mt0); + auto alloc2 = builder.create( + loc, mt, alloc, getConstantIndex(-1)); + + // mlir::Value zeroIndex = getConstantIndex(0); + // builder.create(loc, alloc, alloc2, zeroIndex); + return ValueWithOffsets(alloc2, /*isReference*/ false); + } + } + + if (auto CI = dyn_cast(E->getSubExpr())) + if (auto ic = dyn_cast(CI->getCallee())) + if (auto sr = dyn_cast(ic->getSubExpr())) { + if (sr->getDecl()->getName() == "malloc") { + + auto mt = getMLIRType(E->getType()).cast(); + auto shape = std::vector(mt.getShape()); + + auto elemSize = + getTypeSize(cast( + E->getType()->getUnqualifiedDesugaredType()) + ->getPointeeType()); + auto allocSize = builder.create( + loc, Visit(CI->getArg(0)).getValue(builder), + mlir::IndexType::get(builder.getContext())); + mlir::Value args[1] = {builder.create( + loc, allocSize, + builder.create( + loc, allocSize.getType(), + builder.getIntegerAttr(allocSize.getType(), elemSize)))}; + auto alloc = builder.create(loc, mt, args); + return ValueWithOffsets(alloc, /*isReference*/ false); + } + } + auto scalar = Visit(E->getSubExpr()).getValue(builder); + auto ut = scalar.getType().cast(); + auto mt = getMLIRType(E->getType()).cast(); + + auto ty = mlir::MemRefType::get(mt.getShape(), mt.getElementType(), + ut.getAffineMaps(), ut.getMemorySpace()); + return ValueWithOffsets( + builder.create(loc, scalar, ty), + /*isReference*/ false); + } + case clang::CastKind::CK_LValueToRValue: { + if (auto dr = dyn_cast(E->getSubExpr())) { + if (dr->getDecl()->getName() == "warpSize") { + bool foundVal = false; + for (int i = scopes.size() - 1; i >= 0; i--) { + auto found = scopes[i].find("warpSize"); + if (found != scopes[i].end()) { + foundVal = true; + break; + } + } + if (!foundVal) { + auto mlirType = getMLIRType(E->getType()); + auto llvmType = getLLVMTypeFromMLIRType(mlirType); + return ValueWithOffsets( + builder.create( + loc, mlirType, + builder.create(loc, llvmType)), + /*isReference*/ true); + } + } + } + auto prev = Visit(E->getSubExpr()); + + // E->dump(); + // llvm::errs() << prev.val << " - " << prev.isReference << "\n"; + // return prev; + auto lres = prev.getValue(builder); + // llvm::errs() << " - lres: " << lres << " mt: " << + // getMLIRType(E->getType()) << " " << + // *Glob.CGM.getTypes().ConvertType(E->getType()) << "\n"; + if (!prev.isReference) { + E->dump(); + lres.dump(); + } + assert(prev.isReference); + // assert(lres.getType() == getMLIRType(E->getType())); + return ValueWithOffsets(lres, /*isReference*/ false); + + if (prev.val.getType().isa()) { + return ValueWithOffsets(prev.val, /*isReference*/ true); + } + auto c0 = builder.create(loc, 0); + if (!prev.val.getType().isa()) { + builder.getInsertionBlock()->dump(); + function.dump(); + E->dump(); + llvm::errs() << prev.val << "\n"; + } + auto mt = prev.val.getType().cast(); + + auto shape = std::vector(mt.getShape()); + if (shape.size() == 1) + return ValueWithOffsets( + builder.create(loc, prev.val, + std::vector({c0})), + /*isReference*/ false); + + shape.erase(shape.begin()); + auto mt0 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + return ValueWithOffsets( + builder.create(loc, mt0, prev.val, c0), + /*isReference*/ true); + } + case clang::CastKind::CK_IntegralToFloating: { + auto scalar = Visit(E->getSubExpr()).getValue(builder); + auto ty = getMLIRType(E->getType()).cast(); + bool signedType = true; + if (auto bit = dyn_cast(&*E->getSubExpr()->getType())) { + if (bit->isUnsignedInteger()) + signedType = false; + if (bit->isSignedInteger()) + signedType = true; + } + if (signedType) + return ValueWithOffsets(builder.create(loc, scalar, ty), + /*isReference*/ false); + else + return ValueWithOffsets(builder.create(loc, scalar, ty), + /*isReference*/ false); + } + case clang::CastKind::CK_FloatingToIntegral: { + auto scalar = Visit(E->getSubExpr()).getValue(builder); + auto ty = getMLIRType(E->getType()).cast(); + bool signedType = true; + if (auto bit = dyn_cast(&*E->getType())) { + if (bit->isUnsignedInteger()) + signedType = false; + if (bit->isSignedInteger()) + signedType = true; + } + if (signedType) + return ValueWithOffsets(builder.create(loc, scalar, ty), + /*isReference*/ false); + else + return ValueWithOffsets(builder.create(loc, scalar, ty), + /*isReference*/ false); + } + case clang::CastKind::CK_IntegralCast: { + auto scalar = Visit(E->getSubExpr()).getValue(builder); + auto prevTy = scalar.getType().cast(); + auto postTy = getMLIRType(E->getType()).cast(); + bool signedType = true; + if (auto bit = dyn_cast(&*E->getType())) { + if (bit->isUnsignedInteger()) + signedType = false; + if (bit->isSignedInteger()) + signedType = true; + } + + if (prevTy == postTy) + return ValueWithOffsets(scalar, /*isReference*/ false); + if (prevTy.getWidth() < postTy.getWidth()) { + if (signedType) { + return ValueWithOffsets( + builder.create(loc, scalar, postTy), + /*isReference*/ false); + } else { + return ValueWithOffsets( + builder.create(loc, scalar, postTy), + /*isReference*/ false); + } + } else { + return ValueWithOffsets( + builder.create(loc, scalar, postTy), + /*isReference*/ false); + } + } + case clang::CastKind::CK_FloatingCast: { + auto scalar = Visit(E->getSubExpr()).getValue(builder); + if (!scalar.getType().isa()) { + E->dump(); + llvm::errs() << scalar << "\n"; + } + auto prevTy = scalar.getType().cast(); + auto postTy = getMLIRType(E->getType()).cast(); + + if (prevTy == postTy) + return ValueWithOffsets(scalar, /*isReference*/ false); + if (prevTy.getWidth() < postTy.getWidth()) { + return ValueWithOffsets( + builder.create(loc, scalar, postTy), + /*isReference*/ false); + } else { + return ValueWithOffsets( + builder.create(loc, scalar, postTy), + /*isReference*/ false); + } + } + case clang::CastKind::CK_ArrayToPointerDecay: { + auto scalar = Visit(E->getSubExpr()); + if (!scalar.val) { + E->dump(); + } + // if (!scalar.isReference) { + //} + assert(scalar.isReference); + + auto mt = scalar.val.getType().cast(); + auto shape = std::vector(mt.getShape()); + // if (shape.size() > 1) { + // shape.erase(shape.begin()); + //} else { + shape[0] = -1; + //} + auto mt0 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + + auto post = builder.create(loc, mt0, scalar.val); + return ValueWithOffsets(post, /*isReference*/ false); + +#if 0 + auto mt = scalar.val.getType().cast(); + auto shape2 = std::vector(mt.getShape()); + if (shape2.size() == 0) { + E->dump(); + //nex.dump(); + assert(0); + } + shape2[0] = -1; + auto nex = mlir::MemRefType::get(shape2, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + auto cst = builder.create(loc, scalar.val, nex); + //llvm::errs() << "\n"; + //E->dump(); + //llvm::errs() << cst << " - " << scalar.val << "\n"; + //auto offs = scalar.offsets; + //offs.push_back(getConstantIndex(0)); + return ValueWithOffsets(cst, scalar.isReference); +#endif + } + case clang::CastKind::CK_FunctionToPointerDecay: { + auto scalar = Visit(E->getSubExpr()); + return scalar; + } + case clang::CastKind::CK_NoOp: { + return Visit(E->getSubExpr()); + } + case clang::CastKind::CK_ToVoid: { + Visit(E->getSubExpr()); + return nullptr; + } + case clang::CastKind::CK_PointerToBoolean: { + auto scalar = Visit(E->getSubExpr()).getValue(builder); + if (auto LT = scalar.getType().dyn_cast()) { + auto nullptr_llvm = builder.create(loc, LT); + auto ne = builder.create( + loc, mlir::LLVM::ICmpPredicate::ne, scalar, nullptr_llvm); + auto mlirType = getMLIRType(E->getType()); + mlir::Value val = + builder.create(loc, mlirType, ne); + return ValueWithOffsets(val, /*isReference*/ false); + } + function.dump(); + llvm::errs() << scalar << "\n"; + E->dump(); + assert(0 && "unhandled ptrtobool cast"); + } + case clang::CastKind::CK_IntegralToBoolean: { + auto res = Visit(E->getSubExpr()).getValue(builder); + auto prevTy = res.getType().cast(); + auto postTy = getMLIRType(E->getType()).cast(); + bool signedType = true; + if (auto bit = dyn_cast(&*E->getType())) { + if (bit->isUnsignedInteger()) + signedType = false; + if (bit->isSignedInteger()) + signedType = true; + } + if (prevTy.getWidth() < postTy.getWidth()) { + if (signedType) { + res = builder.create(loc, res, postTy); + } else { + res = builder.create(loc, res, postTy); + } + } else if (prevTy.getWidth() > postTy.getWidth()) { + res = builder.create(loc, res, postTy); + } + return ValueWithOffsets(res, /*isReference*/ false); + } + default: + E->dump(); + assert(0 && "unhandled cast"); + } +} + +ValueWithOffsets MLIRScanner::VisitIfStmt(clang::IfStmt *stmt) { + IfScope scope(*this); + auto loc = getMLIRLocation(stmt->getIfLoc()); + auto cond = Visit(stmt->getCond()).getValue(builder); + assert(cond != nullptr); + + auto prevTy = cond.getType().cast(); + if (!prevTy.isInteger(1)) { + auto postTy = builder.getI1Type(); + cond = builder.create(loc, cond, postTy); + } + bool hasElseRegion = stmt->getElse(); + auto ifOp = builder.create(loc, cond, hasElseRegion); + + auto oldpoint = builder.getInsertionPoint(); + auto oldblock = builder.getInsertionBlock(); + ifOp.thenRegion().back().clear(); + builder.setInsertionPointToStart(&ifOp.thenRegion().back()); + Visit(stmt->getThen()); + builder.create(loc); + if (hasElseRegion) { + ifOp.elseRegion().back().clear(); + builder.setInsertionPointToStart(&ifOp.elseRegion().back()); + Visit(stmt->getElse()); + builder.create(loc); + } + + builder.setInsertionPoint(oldblock, oldpoint); + return nullptr; +} + +ValueWithOffsets +MLIRScanner::VisitConditionalOperator(clang::ConditionalOperator *E) { + auto cond = Visit(E->getCond()).getValue(builder); + assert(cond != nullptr); + auto prevTy = cond.getType().cast(); + if (!prevTy.isInteger(1)) { + auto postTy = builder.getI1Type(); + cond = builder.create(loc, cond, postTy); + } + mlir::Type types[] = {getMLIRType(E->getType())}; + auto ifOp = builder.create(loc, types, cond, + /*hasElseRegion*/ true); + + auto oldpoint = builder.getInsertionPoint(); + auto oldblock = builder.getInsertionBlock(); + builder.setInsertionPointToStart(&ifOp.thenRegion().back()); + + auto truev = Visit(E->getTrueExpr()).getValue(builder); + assert(truev != nullptr); + mlir::Value truearray[] = {truev}; + builder.create(loc, truearray); + + builder.setInsertionPointToStart(&ifOp.elseRegion().back()); + auto falsev = Visit(E->getFalseExpr()).getValue(builder); + assert(falsev != nullptr); + mlir::Value falsearray[] = {falsev}; + builder.create(loc, falsearray); + + builder.setInsertionPoint(oldblock, oldpoint); + + types[0] = truev.getType(); + auto newIfOp = builder.create(loc, types, cond, + /*hasElseRegion*/ true); + newIfOp.thenRegion().takeBody(ifOp.thenRegion()); + newIfOp.elseRegion().takeBody(ifOp.elseRegion()); + return ValueWithOffsets(newIfOp.getResult(0), /*isReference*/ false); + // return ifOp; +} + +ValueWithOffsets MLIRScanner::VisitCompoundStmt(clang::CompoundStmt *stmt) { + for (auto a : stmt->children()) { + IfScope scope(*this); + Visit(a); + } + return nullptr; +} + +ValueWithOffsets MLIRScanner::VisitBreakStmt(clang::BreakStmt *stmt) { + IfScope scope(*this); + assert(loops.size()); + assert(loops.back().keepRunning); + assert(loops.back().noBreak); + auto i1Ty = builder.getI1Type(); + auto vfalse = builder.create( + builder.getUnknownLoc(), i1Ty, builder.getIntegerAttr(i1Ty, 0)); + builder.create(loc, vfalse, loops.back().keepRunning); + builder.create(loc, vfalse, loops.back().noBreak); + + return nullptr; +} + +ValueWithOffsets MLIRScanner::VisitContinueStmt(clang::ContinueStmt *stmt) { + IfScope scope(*this); + assert(loops.size()); + assert(loops.back().keepRunning); + auto i1Ty = builder.getI1Type(); + auto vfalse = builder.create( + builder.getUnknownLoc(), i1Ty, builder.getIntegerAttr(i1Ty, 0)); + builder.create(loc, vfalse, loops.back().keepRunning); + return nullptr; +} + +ValueWithOffsets MLIRScanner::VisitReturnStmt(clang::ReturnStmt *stmt) { + IfScope scope(*this); + if (stmt->getRetValue()) { + auto rv = Visit(stmt->getRetValue()).getValue(builder); + assert(rv); + builder.create(loc, rv); + } else { + builder.create(loc); + } + return nullptr; +} + +mlir::LLVM::LLVMFuncOp +MLIRASTConsumer::GetOrCreateLLVMFunction(const FunctionDecl *FD) { + if (llvmFunctions.find(FD) != llvmFunctions.end()) { + return llvmFunctions[FD]; + } + std::string name = CGM.getMangledName(FD).str(); + std::vector types; + for (auto parm : FD->parameters()) { + types.push_back( + typeTranslator.translateType(getLLVMType(parm->getOriginalType()))); + } + + auto rt = typeTranslator.translateType(getLLVMType(FD->getReturnType())); + + auto llvmFnType = LLVM::LLVMFunctionType::get(rt, types, + /*isVarArg=*/FD->isVariadic()); + + // Insert the function into the body of the parent module. + mlir::OpBuilder builder(module.getContext()); + builder.setInsertionPointToStart(module.getBody()); + return llvmFunctions[FD] = builder.create(module.getLoc(), + name, llvmFnType); +} + +mlir::LLVM::GlobalOp MLIRASTConsumer::GetOrCreateLLVMGlobal(const VarDecl *FD) { + if (llvmGlobals.find(FD) != llvmGlobals.end()) { + return llvmGlobals[FD]; + } + + std::string name = CGM.getMangledName(FD).str(); + + auto rt = typeTranslator.translateType(getLLVMType(FD->getType())); + + mlir::OpBuilder builder(module.getContext()); + builder.setInsertionPointToStart(module.getBody()); + // auto lnk = CGM.getLLVMLinkageVarDefinition(FD, /*isConstant*/false); + // TODO handle proper global linkage + auto lnk = LLVM::Linkage::External; + return llvmGlobals[FD] = builder.create( + module.getLoc(), rt, /*constant*/ false, lnk, name, + mlir::Attribute()); +} + +std::pair +MLIRASTConsumer::GetOrCreateGlobal(const VarDecl *FD) { + std::string name = CGM.getMangledName(FD).str(); + + if (globals.find(name) != globals.end()) { + return globals[name]; + } + + auto rt = getMLIRType(FD->getType()); + unsigned memspace = 0; + bool isArray = isa(FD->getType()); + + mlir::MemRefType mr; + if (!isArray) { + mr = mlir::MemRefType::get(1, rt, {}, memspace); + } else { + auto mt = rt.cast(); + mr = mlir::MemRefType::get(mt.getShape(), mt.getElementType(), + mt.getAffineMaps(), memspace); + } + + mlir::OpBuilder builder(module.getContext()); + builder.setInsertionPointToStart(module.getBody()); + // auto lnk = CGM.getLLVMLinkageVarDefinition(FD, /*isConstant*/false); + // TODO handle proper global linkage + // builder.getStringAttr("public") + auto globalOp = builder.create( + module.getLoc(), builder.getStringAttr(FD->getName()), mlir::StringAttr(), + mlir::TypeAttr::get(mr), mlir::Attribute(), mlir::UnitAttr()); + // Private == internal, Public == External [in lowering] + SymbolTable::setSymbolVisibility(globalOp, SymbolTable::Visibility::Private); + return globals[name] = std::make_pair(globalOp, isArray); +} + +mlir::Value MLIRASTConsumer::GetOrCreateGlobalLLVMString( + mlir::Location loc, mlir::OpBuilder &builder, StringRef value) { + using namespace mlir; + // Create the global at the entry of the module. + if (llvmStringGlobals.find(value.str()) == llvmStringGlobals.end()) { + OpBuilder::InsertionGuard insertGuard(builder); + builder.setInsertionPointToStart(module.getBody()); + auto type = LLVM::LLVMArrayType::get( + mlir::IntegerType::get(builder.getContext(), 8), value.size() + 1); + llvmStringGlobals[value.str()] = builder.create( + loc, type, /*isConstant=*/true, LLVM::Linkage::Internal, + "str" + std::to_string(llvmStringGlobals.size()), + builder.getStringAttr(value.str() + '\0')); + } + + LLVM::GlobalOp global = llvmStringGlobals[value.str()]; + // Get the pointer to the first character in the global string. + mlir::Value globalPtr = builder.create(loc, global); + mlir::Value cst0 = builder.create( + loc, mlir::IntegerType::get(builder.getContext(), 64), + builder.getIntegerAttr(builder.getIntegerType(64), 0)); + return builder.create( + loc, + LLVM::LLVMPointerType::get( + mlir::IntegerType::get(builder.getContext(), 8)), + globalPtr, ArrayRef({cst0, cst0})); +} + +mlir::FuncOp MLIRASTConsumer::GetOrCreateMLIRFunction(const FunctionDecl *FD) { + std::string name = CGM.getMangledName(FD).str(); + if (functions.find(name) != functions.end()) { + return functions[name]; + } + + std::vector types; + std::vector names; + for (auto parm : FD->parameters()) { + bool llvmType = name == "main" && types.size() == 1; + if (auto ava = parm->getAttr()) { + if (auto algn = dyn_cast(ava->getAlignment())) { + for (auto a : algn->children()) { + if (auto IL = dyn_cast(a)) { + if (IL->getValue() == 8192) { + llvmType = true; + break; + } + } + } + } + } + if (llvmType) { + types.push_back( + typeTranslator.translateType(getLLVMType(parm->getType()))); + } else { + types.push_back(getMLIRType(parm->getType())); + } + names.push_back(parm->getName().str()); + } + + auto rt = getMLIRType(FD->getReturnType()); + std::vector rettypes; + if (!rt.isa()) { + rettypes.push_back(rt); + } + mlir::OpBuilder builder(module.getContext()); + auto funcType = builder.getFunctionType(types, rettypes); + mlir::FuncOp function = mlir::FuncOp( + mlir::FuncOp::create(builder.getUnknownLoc(), name, funcType)); + if (FD->getLinkageInternal() == clang::Linkage::InternalLinkage || + !FD->isDefined()) { + SymbolTable::setSymbolVisibility(function, + SymbolTable::Visibility::Private); + } else { + SymbolTable::setSymbolVisibility(function, SymbolTable::Visibility::Public); + } + + functions[name] = function; + module.push_back(function); + if (FD->isDefined()) + functionsToEmit.push_back(FD); + else + emitIfFound.insert(FD->getName().str()); + return function; +} + +void MLIRASTConsumer::run() { + while (functionsToEmit.size()) { + const FunctionDecl *todo = functionsToEmit.front(); + functionsToEmit.pop_front(); + if (done.count(todo)) + continue; + done.insert(todo); + MLIRScanner ms(*this, GetOrCreateMLIRFunction(todo), todo, module); + } +} + +bool MLIRASTConsumer::HandleTopLevelDecl(DeclGroupRef dg) { + DeclGroupRef::iterator it; + + if (error) + return true; + + std::function handle = [&](Decl *D) { + if (auto lsd = dyn_cast(D)) { + for (auto e : lsd->decls()) + handle(e); + } + if (VarDecl *fd = dyn_cast(D)) { + globalVariables[fd->getName().str()] = fd; + } + if (FunctionDecl *fd = dyn_cast(D)) { + if (fd->getIdentifier()) { + globalFunctions[fd->getName().str()] = fd; + } + } + }; + for (it = dg.begin(); it != dg.end(); ++it) { + handle(*it); + } + + for (it = dg.begin(); it != dg.end(); ++it) { + FunctionDecl *fd = dyn_cast(*it); + if (!fd) + continue; + if (!fd->hasBody()) + continue; + if (fd->getIdentifier() == nullptr) + continue; + if (emitIfFound.count(fd->getName().str())) { + functionsToEmit.push_back(fd); + } + } + + run(); + + return true; +} + +mlir::Location MLIRASTConsumer::getMLIRLocation(clang::SourceLocation loc) { + auto spellingLoc = SM.getSpellingLoc(loc); + auto lineNumber = SM.getSpellingLineNumber(spellingLoc); + auto colNumber = SM.getSpellingColumnNumber(spellingLoc); + auto fileId = SM.getFilename(spellingLoc); + + auto ctx = module.getContext(); + auto mlirIdentifier = Identifier::get(fileId, ctx); + mlir::OpBuilder builder(ctx); + return builder.getUnknownLoc(); + // return builder.getFileLineColLoc(mlirIdentifier, lineNumber, colNumber); +} + +mlir::Type MLIRASTConsumer::getMLIRType(clang::QualType t) { + if (t->isVoidType()) { + mlir::OpBuilder builder(module.getContext()); + return builder.getNoneType(); + } + llvm::Type *T = CGM.getTypes().ConvertType(t); + return getMLIRType(T); +} + +llvm::Type *MLIRASTConsumer::getLLVMType(clang::QualType t) { + if (t->isVoidType()) { + return llvm::Type::getVoidTy(llvmMod.getContext()); + } + llvm::Type *T = CGM.getTypes().ConvertType(t); + return T; +} + +mlir::Type MLIRASTConsumer::getMLIRType(llvm::Type *t) { + mlir::OpBuilder builder(module.getContext()); + if (t->isVoidTy()) { + return builder.getNoneType(); + } + if (t->isFloatTy()) { + return builder.getF32Type(); + } + if (t->isDoubleTy()) { + return builder.getF64Type(); + } + if (auto IT = dyn_cast(t)) { + return builder.getIntegerType(IT->getBitWidth()); + } + if (auto pt = dyn_cast(t)) { + if (auto ST = dyn_cast(pt->getElementType())) { + if (ST->getName() == "struct._IO_FILE") { + return typeTranslator.translateType(t); + } + bool notAllSame = false; + for (size_t i = 1; i < ST->getNumElements(); i++) { + if (ST->getTypeAtIndex(i) != ST->getTypeAtIndex(0U)) { + notAllSame = true; + break; + } + } + if (!notAllSame) { + return mlir::MemRefType::get({-1, ST->getNumElements()}, + getMLIRType(ST->getTypeAtIndex(0U)), {}, + pt->getAddressSpace()); + } + } + if (auto AT = dyn_cast(pt->getElementType())) { + auto under = getMLIRType(AT); + auto mt = under.cast(); + auto shape2 = std::vector(mt.getShape()); + shape2.insert(shape2.begin(), -1); + return mlir::MemRefType::get(shape2, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + } + return mlir::MemRefType::get(-1, getMLIRType(pt->getElementType()), {}, + pt->getAddressSpace()); + } + if (auto pt = dyn_cast(t)) { + auto under = getMLIRType(pt->getElementType()); + if (auto mt = under.dyn_cast()) { + auto shape2 = std::vector(mt.getShape()); + shape2.insert(shape2.begin(), (int64_t)pt->getNumElements()); + return mlir::MemRefType::get(shape2, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + } + return mlir::MemRefType::get({(int64_t)pt->getNumElements()}, under); + } + if (auto ST = dyn_cast(t)) { + bool notAllSame = false; + for (size_t i = 1; i < ST->getNumElements(); i++) { + if (ST->getTypeAtIndex(i) != ST->getTypeAtIndex(0U)) { + notAllSame = true; + break; + } + } + if (!notAllSame) { + return mlir::MemRefType::get(ST->getNumElements(), + getMLIRType(ST->getTypeAtIndex(0U))); + } + if (ST->getName() == "struct._IO_FILE") { + return typeTranslator.translateType(t); + } + SmallVector types; + for (size_t i = 0; i < ST->getNumElements(); i++) { + types.push_back(getMLIRType(ST->getTypeAtIndex(i))); + } + return mlir::LLVM::LLVMStructType::getLiteral(module.getContext(), types); + // return mlir::LLVM::StructType::get(ST->getName(), types); + } + llvm::errs() << *t << "\n"; + assert(0 && "unknown type to convert"); + return nullptr; +} + +void MLIRScanner::pushLoopIf() { + if (loops.size() && loops.back().keepRunning) { + auto lop = builder.create(loc, loops.back().keepRunning); + auto ifOp = builder.create(loc, lop, + /*hasElse*/ false); + prevBlock.push_back(builder.getInsertionBlock()); + prevIterator.push_back(builder.getInsertionPoint()); + ifOp.thenRegion().back().clear(); + builder.setInsertionPointToStart(&ifOp.thenRegion().back()); + } +} + +void MLIRScanner::popLoopIf() { + if (loops.size() && loops.back().keepRunning) { + builder.create(loc); + builder.setInsertionPoint(prevBlock.back(), prevIterator.back()); + prevBlock.pop_back(); + prevIterator.pop_back(); + } +} + +#include "llvm/Support/Host.h" + +#include "clang/Frontend/FrontendAction.h" +class MLIRAction : public clang::ASTFrontendAction { +public: + std::set emitIfFound; + mlir::ModuleOp &module; + std::map llvmStringGlobals; + std::map> globals; + std::map functions; + MLIRAction(std::string fn, mlir::ModuleOp &module) : module(module) { + emitIfFound.insert(fn); + } + std::unique_ptr + CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { + return std::unique_ptr( + new MLIRASTConsumer(emitIfFound, llvmStringGlobals, globals, functions, + CI.getPreprocessor(), CI.getASTContext(), module, + CI.getSourceManager())); + } +}; + +mlir::FuncOp MLIRScanner::EmitDirectCallee(GlobalDecl GD) { + const FunctionDecl *FD = cast(GD.getDecl()); + return Glob.GetOrCreateMLIRFunction(FD); +} + +mlir::Location MLIRScanner::getMLIRLocation(clang::SourceLocation loc) { + return Glob.getMLIRLocation(loc); +} + +mlir::Type MLIRScanner::getMLIRType(clang::QualType t) { + return Glob.getMLIRType(t); +} + +llvm::Type *MLIRScanner::getLLVMType(clang::QualType t) { + return Glob.getLLVMType(t); +} + +size_t MLIRScanner::getTypeSize(clang::QualType t) { + llvm::Type *T = Glob.CGM.getTypes().ConvertType(t); + return Glob.llvmMod.getDataLayout().getTypeSizeInBits(T) / 8; +} + +std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) { + if (!CanonicalPrefixes) { + SmallString<128> ExecutablePath(Argv0); + // Do a PATH lookup if Argv0 isn't a valid path. + if (!llvm::sys::fs::exists(ExecutablePath)) + if (llvm::ErrorOr P = + llvm::sys::findProgramByName(ExecutablePath)) + ExecutablePath = *P; + return std::string(ExecutablePath.str()); + } + + // This just needs to be some symbol in the binary; C++ doesn't + // allow taking the address of ::main however. + void *P = (void *)(intptr_t)GetExecutablePath; + return llvm::sys::fs::getMainExecutable(Argv0, P); +} + +#include "clang/Frontend/TextDiagnosticBuffer.h" +static bool parseMLIR(const char *Argv0, std::vector filenames, + std::string fn, std::vector includeDirs, + std::vector defines, mlir::ModuleOp &module, + llvm::Triple &triple, llvm::DataLayout &DL) { + + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + // Buffer diagnostics from argument parsing so that we can output them using a + // well formed diagnostic object. + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; + DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); + + bool Success; + //{ + const char *binary = Argv0; // CudaLower ? "clang++" : "clang"; + const unique_ptr driver( + new Driver(binary, llvm::sys::getDefaultTargetTriple(), Diags)); + std::vector Argv; + Argv.push_back(binary); + for (auto a : filenames) { + char *chars = (char *)malloc(a.length() + 1); + memcpy(chars, a.data(), a.length()); + chars[a.length()] = 0; + Argv.push_back(chars); + } + if (CudaLower) + Argv.push_back("--cuda-gpu-arch=sm_35"); + if (FOpenMP) + Argv.push_back("-fopenmp"); + if (Standard != "") { + auto a = "-std=" + Standard; + char *chars = (char *)malloc(a.length() + 1); + memcpy(chars, a.data(), a.length()); + chars[a.length()] = 0; + Argv.push_back(chars); + } + if (MArch != "") { + auto a = "-march=" + MArch; + char *chars = (char *)malloc(a.length() + 1); + memcpy(chars, a.data(), a.length()); + chars[a.length()] = 0; + Argv.push_back(chars); + } + for (auto a : includeDirs) { + Argv.push_back("-I"); + char *chars = (char *)malloc(a.length() + 1); + memcpy(chars, a.data(), a.length()); + chars[a.length()] = 0; + Argv.push_back(chars); + } + for (auto a : defines) { + char *chars = (char *)malloc(a.length() + 3); + chars[0] = '-'; + chars[1] = 'D'; + memcpy(chars + 2, a.data(), a.length()); + chars[2 + a.length()] = 0; + Argv.push_back(chars); + } + + Argv.push_back("-emit-ast"); + + const unique_ptr compilation( + driver->BuildCompilation(llvm::ArrayRef(Argv))); + JobList &Jobs = compilation->getJobs(); + if (Jobs.size() < 1) + return false; + + MLIRAction Act(fn, module); + + for (auto &job : Jobs) { + std::unique_ptr Clang(new CompilerInstance()); + + Command *cmd = cast(&job); + if (strcmp(cmd->getCreator().getName(), "clang")) + return false; + + const ArgStringList *args = &cmd->getArguments(); + + Success = CompilerInvocation::CreateFromArgs(Clang->getInvocation(), *args, + Diags); + Clang->getInvocation().getFrontendOpts().DisableFree = false; + + void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath; + // Infer the builtin include path if unspecified. + if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && + Clang->getHeaderSearchOpts().ResourceDir.size() == 0) + Clang->getHeaderSearchOpts().ResourceDir = + CompilerInvocation::GetResourcesPath(Argv0, GetExecutablePathVP); + + //} + Clang->getInvocation().getFrontendOpts().DisableFree = false; + + // Create the actual diagnostics engine. + Clang->createDiagnostics(); + if (!Clang->hasDiagnostics()) + return false; + + DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics()); + if (!Success) + return false; + + // Create and execute the frontend action. + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) + return false; + + // Create TargetInfo for the other side of CUDA and OpenMP compilation. + if ((Clang->getLangOpts().CUDA || Clang->getLangOpts().OpenMPIsDevice) && + !Clang->getFrontendOpts().AuxTriple.empty()) { + auto TO = std::make_shared(); + TO->Triple = llvm::Triple::normalize(Clang->getFrontendOpts().AuxTriple); + TO->HostTriple = Clang->getTarget().getTriple().str(); + Clang->setAuxTarget( + TargetInfo::CreateTargetInfo(Clang->getDiagnostics(), TO)); + } + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang->getTarget().adjust(Clang->getLangOpts()); + + // Adjust target options based on codegen options. + Clang->getTarget().adjustTargetOptions(Clang->getCodeGenOpts(), + Clang->getTargetOpts()); + + module->setAttr( + LLVM::LLVMDialect::getDataLayoutAttrName(), + StringAttr::get( + module.getContext(), + Clang->getTarget().getDataLayoutString())); + module->setAttr( + LLVM::LLVMDialect::getTargetTripleAttrName(), + StringAttr::get(module.getContext(), + Clang->getTarget().getTriple().getTriple())); + + for (const auto &FIF : Clang->getFrontendOpts().Inputs) { + // Reset the ID tables if we are reusing the SourceManager and parsing + // regular files. + if (Clang->hasSourceManager() && !Act.isModelParsingAction()) + Clang->getSourceManager().clearIDTables(); + if (Act.BeginSourceFile(*Clang, FIF)) { + + llvm::Error err = Act.Execute(); + if (err) { + llvm::errs() << "saw error: " << err << "\n"; + return false; + } + assert(Clang->hasSourceManager()); + + Act.EndSourceFile(); + } + } + DL = llvm::DataLayout(Clang->getTarget().getDataLayoutString()); + triple = Clang->getTarget().getTriple(); + } + return true; +} diff --git a/mlir-clang/Lib/clang-mlir.h b/mlir-clang/Lib/clang-mlir.h new file mode 100644 index 000000000000..8bad13bdf6de --- /dev/null +++ b/mlir-clang/Lib/clang-mlir.h @@ -0,0 +1,494 @@ +#ifndef CLANG_MLIR_H +#define CLANG_MLIR_H + +#include "clang/AST/StmtVisitor.h" +#include +#include +#include +#include +#include + +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/LLVMIR/LLVMTypes.h" +#include "mlir/Dialect/LLVMIR/NVVMDialect.h" +#include "mlir/Dialect/Math/IR/Math.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/OpDefinition.h" +#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h" +#include "mlir/Target/LLVMIR/TypeTranslation.h" +#include "llvm/IR/DerivedTypes.h" + +#include "polygeist/Ops.h" +#include "../../../../clang/lib/CodeGen/CGRecordLayout.h" +#include "../../../../clang/lib/CodeGen/CodeGenModule.h" +#include "clang/AST/Mangle.h" + +using namespace clang; +using namespace mlir; + +struct LoopContext { + mlir::Value keepRunning; + mlir::Value noBreak; +}; + +struct AffineLoopDescriptor { +private: + mlir::Value upperBound; + mlir::Value lowerBound; + int64_t step; + mlir::Type indVarType; + std::string indVar; + bool forwardMode; + +public: + AffineLoopDescriptor() + : upperBound(nullptr), lowerBound(nullptr), + step(std::numeric_limits::max()), indVarType(nullptr), + indVar("nullptr"), forwardMode(true){}; + AffineLoopDescriptor(const AffineLoopDescriptor &) = delete; + + void setLowerBound(mlir::Value value) { lowerBound = value; } + void setUpperBound(mlir::Value value) { upperBound = value; } + + void setStep(int value) { step = value; }; + void setType(mlir::Type type) { indVarType = type; } + void setName(std::string value) { indVar = value; } + + std::string getName() const { return indVar; } + mlir::Type getType() const { return indVarType; } + int getStep() const { return step; } + + auto getLowerBound() const { return lowerBound; } + + auto getUpperBound() const { return upperBound; } + + void setForwardMode(bool value) { forwardMode = value; }; + bool getForwardMode() const { return forwardMode; } +}; + +struct ValueWithOffsets { + mlir::Value val; + bool isReference; + ValueWithOffsets() : val(nullptr), isReference(false){}; + ValueWithOffsets(std::nullptr_t) : val(nullptr), isReference(false){}; + ValueWithOffsets(mlir::Value val, bool isReference) + : val(val), isReference(isReference) { + if (isReference) { + if (val.getType().isa()) { + + } else if (val.getType().isa()) { + + } else { + llvm::errs() << val << "\n"; + assert(val.getType().isa()); + } + } + }; + + mlir::Value getValue(OpBuilder &builder) const { + assert(val); + if (!isReference) + return val; + auto loc = builder.getUnknownLoc(); + if (val.getType().isa()) { + return builder.create(loc, val); + } + auto c0 = builder.create(loc, 0); + if (!val.getType().isa()) { + llvm::errs() << val << "\n"; + } + assert(val.getType().isa()); + return builder.create(loc, val, + std::vector({c0})); + } + + ValueWithOffsets dereference(OpBuilder &builder) const { + assert(val); + if (!isReference) + return ValueWithOffsets(val, /*isReference*/ true); + auto loc = builder.getUnknownLoc(); + auto c0 = builder.create(loc, 0); + if (val.getType().isa()) { + return ValueWithOffsets(builder.create(loc, val), + /*isReference*/ true); + } + auto mt = val.getType().cast(); + auto shape = std::vector(mt.getShape()); + if (shape.size() > 1) { + shape.erase(shape.begin()); + } else { + shape[0] = -1; + // builder.create(loc, val, std::vector({c0})) + } + auto mt0 = mlir::MemRefType::get(shape, mt.getElementType(), + mt.getAffineMaps(), mt.getMemorySpace()); + auto post = builder.create(loc, mt0, val, c0); + + return ValueWithOffsets(post, + /*isReference*/ true); + } +}; + +/// The location of the scop, as delimited by scop and endscop +/// pragmas by the user. +/// "scop" and "endscop" are the source locations of the scop and +/// endscop pragmas. +/// "start_line" is the line number of the start position. +struct ScopLoc { + ScopLoc() : end(0) {} + + clang::SourceLocation scop; + clang::SourceLocation endscop; + unsigned startLine; + unsigned start; + unsigned end; +}; + +/// Taken from pet.cc +/// List of pairs of #pragma scop and #pragma endscop locations. +struct ScopLocList { + std::vector list; + + // Add a new start (#pragma scop) location to the list. + // If the last #pragma scop did not have a matching + // #pragma endscop then overwrite it. + // "start" points to the location of the scop pragma. + + void addStart(SourceManager &SM, SourceLocation start) { + ScopLoc loc; + + loc.scop = start; + int line = SM.getExpansionLineNumber(start); + start = SM.translateLineCol(SM.getFileID(start), line, 1); + loc.startLine = line; + loc.start = SM.getFileOffset(start); + if (list.size() == 0 || list[list.size() - 1].end != 0) + list.push_back(loc); + else + list[list.size() - 1] = loc; + } + + // Set the end location (#pragma endscop) of the last pair + // in the list. + // If there is no such pair of if the end of that pair + // is already set, then ignore the spurious #pragma endscop. + // "end" points to the location of the endscop pragma. + + void addEnd(SourceManager &SM, SourceLocation end) { + if (list.size() == 0 || list[list.size() - 1].end != 0) + return; + list[list.size() - 1].endscop = end; + int line = SM.getExpansionLineNumber(end); + end = SM.translateLineCol(SM.getFileID(end), line + 1, 1); + list[list.size() - 1].end = SM.getFileOffset(end); + } + + // Check if the current location is in the scop. + bool isInScop(SourceLocation target) { + // If the user selects the raise-scf-to-affine we ignore pragmas and try to + // raise all we can. Similar behavior to pet --autodetect. This allow us to + // test the raising. + if (RaiseToAffine) + return false; + + if (!list.size()) + return false; + for (auto &scopLoc : list) + if ((target >= scopLoc.scop) && (target <= scopLoc.endscop)) + return true; + return false; + } +}; + +struct PragmaScopHandler : public PragmaHandler { + ScopLocList &scops; + + PragmaScopHandler(ScopLocList &scops) : PragmaHandler("scop"), scops(scops) {} + + virtual void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &scopTok) { + auto &SM = PP.getSourceManager(); + auto loc = scopTok.getLocation(); + scops.addStart(SM, loc); + } +}; + +struct PragmaEndScopHandler : public PragmaHandler { + ScopLocList &scops; + + PragmaEndScopHandler(ScopLocList &scops) + : PragmaHandler("endscop"), scops(scops) {} + + virtual void HandlePragma(Preprocessor &PP, PragmaIntroducer introducer, + Token &endScopTok) { + auto &SM = PP.getSourceManager(); + auto loc = endScopTok.getLocation(); + scops.addEnd(SM, loc); + } +}; + +struct MLIRASTConsumer : public ASTConsumer { + std::set &emitIfFound; + std::map &llvmStringGlobals; + std::map> &globals; + std::map &functions; + Preprocessor &PP; + ASTContext &astContext; + mlir::ModuleOp &module; + clang::SourceManager &SM; + LLVMContext lcontext; + llvm::Module llvmMod; + CodeGenOptions codegenops; + CodeGen::CodeGenModule CGM; + bool error; + ScopLocList scopLocList; + + /// The stateful type translator (contains named structs). + LLVM::TypeFromLLVMIRTranslator typeTranslator; + LLVM::TypeToLLVMIRTranslator reverseTypeTranslator; + + MLIRASTConsumer( + std::set &emitIfFound, + std::map &llvmStringGlobals, + std::map> &globals, + std::map &functions, Preprocessor &PP, + ASTContext &astContext, mlir::ModuleOp &module, clang::SourceManager &SM) + : emitIfFound(emitIfFound), llvmStringGlobals(llvmStringGlobals), + globals(globals), functions(functions), PP(PP), astContext(astContext), + module(module), SM(SM), lcontext(), llvmMod("tmp", lcontext), + codegenops(), + CGM(astContext, PP.getHeaderSearchInfo().getHeaderSearchOpts(), + PP.getPreprocessorOpts(), codegenops, llvmMod, PP.getDiagnostics()), + error(false), typeTranslator(*module.getContext()), + reverseTypeTranslator(lcontext) { + PP.AddPragmaHandler(new PragmaScopHandler(scopLocList)); + PP.AddPragmaHandler(new PragmaEndScopHandler(scopLocList)); + } + + ~MLIRASTConsumer() {} + + mlir::FuncOp GetOrCreateMLIRFunction(const FunctionDecl *FD); + + std::map llvmFunctions; + mlir::LLVM::LLVMFuncOp GetOrCreateLLVMFunction(const FunctionDecl *FD); + + std::map llvmGlobals; + mlir::LLVM::GlobalOp GetOrCreateLLVMGlobal(const VarDecl *VD); + + /// Return a value representing an access into a global string with the given + /// name, creating the string if necessary. + mlir::Value GetOrCreateGlobalLLVMString(mlir::Location loc, + mlir::OpBuilder &builder, + StringRef value); + + std::map globalVariables; + std::map globalFunctions; + std::pair GetOrCreateGlobal(const VarDecl *VD); + + std::deque functionsToEmit; + std::set done; + + void run(); + + virtual bool HandleTopLevelDecl(DeclGroupRef dg); + + mlir::Type getMLIRType(clang::QualType t); + + llvm::Type *getLLVMType(clang::QualType t); + + mlir::Type getMLIRType(llvm::Type *t); + + mlir::Location getMLIRLocation(clang::SourceLocation loc); +}; + +struct MLIRScanner : public StmtVisitor { +private: + MLIRASTConsumer &Glob; + mlir::FuncOp function; + mlir::ModuleOp &module; + mlir::OpBuilder builder; + mlir::Location loc; + mlir::Block *entryBlock; + std::vector> scopes; + std::vector loops; + + void setValue(std::string name, ValueWithOffsets &&val); + + ValueWithOffsets getValue(std::string name); + + std::map> bufs; + mlir::LLVM::AllocaOp allocateBuffer(size_t i, mlir::LLVM::LLVMPointerType t) { + auto &vec = bufs[t.getAsOpaquePointer()]; + if (i < vec.size()) + return vec[i]; + + mlir::OpBuilder subbuilder(builder.getContext()); + subbuilder.setInsertionPointToStart(entryBlock); + + auto indexType = subbuilder.getIntegerType(64); + auto one = subbuilder.create( + loc, indexType, + subbuilder.getIntegerAttr(subbuilder.getIntegerType(64), 1)); + auto rs = subbuilder.create(loc, t, one, 0); + vec.push_back(rs); + return rs; + } + + mlir::Type getLLVMTypeFromMLIRType(mlir::Type t); + + mlir::Location getMLIRLocation(clang::SourceLocation loc); + + mlir::Type getMLIRType(clang::QualType t); + + llvm::Type *getLLVMType(clang::QualType t); + + size_t getTypeSize(clang::QualType t); + + mlir::Value createAllocOp(mlir::Type t, std::string name, uint64_t memspace, + bool isArray); + + mlir::Value createAndSetAllocOp(std::string name, mlir::Value v, + uint64_t memspace); + + const clang::FunctionDecl *EmitCallee(const Expr *E); + + mlir::FuncOp EmitDirectCallee(GlobalDecl GD); + + std::map constants; + mlir::Value getConstantIndex(int x); + + mlir::Value castToIndex(mlir::Location loc, mlir::Value val); + + bool isTrivialAffineLoop(clang::ForStmt *fors, AffineLoopDescriptor &descr); + + bool getUpperBound(clang::ForStmt *fors, AffineLoopDescriptor &descr); + + bool getLowerBound(clang::ForStmt *fors, AffineLoopDescriptor &descr); + + bool getConstantStep(clang::ForStmt *fors, AffineLoopDescriptor &descr); + + void buildAffineLoop(clang::ForStmt *fors, mlir::Location loc, + const AffineLoopDescriptor &descr); + + void buildAffineLoopImpl(clang::ForStmt *fors, mlir::Location loc, + mlir::Value lb, mlir::Value ub, + const AffineLoopDescriptor &descr); + std::vector prevBlock; + std::vector prevIterator; + +public: + void pushLoopIf(); + void popLoopIf(); + +public: + MLIRScanner(MLIRASTConsumer &Glob, mlir::FuncOp function, + const FunctionDecl *fd, mlir::ModuleOp &module) + : Glob(Glob), function(function), module(module), + builder(module.getContext()), loc(builder.getUnknownLoc()) { + // llvm::errs() << *fd << "\n"; + // fd->dump(); + + scopes.emplace_back(); + + entryBlock = function.addEntryBlock(); + + builder.setInsertionPointToStart(entryBlock); + + unsigned i = 0; + for (auto parm : fd->parameters()) { + assert(i != function.getNumArguments()); + auto name = parm->getName().str(); + // function.getArgument(i).setName(name); + createAndSetAllocOp(name, function.getArgument(i), 0); + i++; + } + scopes.emplace_back(); + + Stmt *stmt = fd->getBody(); + // stmt->dump(); + Visit(stmt); + + auto endBlock = builder.getInsertionBlock(); + if (endBlock->empty() || + !endBlock->back().mightHaveTrait()) { + if (function.getType().getResults().size()) { + auto ty = function.getType().getResults()[0].cast(); + auto val = (mlir::Value)builder.create( + loc, ty, builder.getIntegerAttr(ty, 0)); + builder.create(loc, val); + } else + builder.create(loc); + } + // function.dump(); + } + + ValueWithOffsets VisitDeclStmt(clang::DeclStmt *decl); + + ValueWithOffsets + VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *decl); + + ValueWithOffsets VisitIntegerLiteral(clang::IntegerLiteral *expr); + + ValueWithOffsets VisitFloatingLiteral(clang::FloatingLiteral *expr); + + ValueWithOffsets VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr *expr); + + ValueWithOffsets VisitStringLiteral(clang::StringLiteral *expr); + + ValueWithOffsets VisitParenExpr(clang::ParenExpr *expr); + + ValueWithOffsets VisitVarDecl(clang::VarDecl *decl); + + ValueWithOffsets VisitForStmt(clang::ForStmt *fors); + + ValueWithOffsets VisitWhileStmt(clang::WhileStmt *fors); + + ValueWithOffsets VisitArraySubscriptExpr(clang::ArraySubscriptExpr *expr); + + ValueWithOffsets VisitCallExpr(clang::CallExpr *expr); + + ValueWithOffsets VisitCXXConstructExpr(clang::CXXConstructExpr *expr); + + ValueWithOffsets VisitMSPropertyRefExpr(MSPropertyRefExpr *expr); + + ValueWithOffsets VisitPseudoObjectExpr(clang::PseudoObjectExpr *expr); + + ValueWithOffsets VisitUnaryOperator(clang::UnaryOperator *U); + + ValueWithOffsets + VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *expr); + + ValueWithOffsets VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Uop); + + ValueWithOffsets VisitBinaryOperator(clang::BinaryOperator *BO); + + ValueWithOffsets VisitAttributedStmt(AttributedStmt *AS); + + ValueWithOffsets VisitExprWithCleanups(ExprWithCleanups *E); + + ValueWithOffsets VisitDeclRefExpr(DeclRefExpr *E); + + ValueWithOffsets VisitOpaqueValueExpr(OpaqueValueExpr *E); + + ValueWithOffsets VisitMemberExpr(MemberExpr *ME); + + ValueWithOffsets VisitCastExpr(CastExpr *E); + + ValueWithOffsets VisitIfStmt(clang::IfStmt *stmt); + + ValueWithOffsets VisitConditionalOperator(clang::ConditionalOperator *E); + + ValueWithOffsets VisitCompoundStmt(clang::CompoundStmt *stmt); + + ValueWithOffsets VisitBreakStmt(clang::BreakStmt *stmt); + + ValueWithOffsets VisitContinueStmt(clang::ContinueStmt *stmt); + + ValueWithOffsets VisitReturnStmt(clang::ReturnStmt *stmt); +}; + +#endif diff --git a/mlir-clang/README.md b/mlir-clang/README.md new file mode 100644 index 000000000000..31fdeaad367d --- /dev/null +++ b/mlir-clang/README.md @@ -0,0 +1,34 @@ +## Build instructions + +### Requirements +- working C and C++ toolchains (compiler, linker) +- cmake +- make or ninja + +### Process + +``` +# checkout the repository, 'cd' to it, then +mkdir -p build +cd build +cmake ../llvm \ + -DLLVM_TARGETS_TO_BUILD="host;NVPTX" \ + -DLLVM_ENABLE_PROJECTS="clang;mlir" \ + -DLLVM_ENABLE_ASSERTIONS=On \ + -DCMAKE_BUILD_TYPE=Release \ + -G Ninja # optional, to use ninja instead of make +make # or ninja; preferably with -j +``` + +Note that everything is statically linked, the produced binary +should be freely movable around the file system. + + +### How to run + +``` +git clone https://github.com/Meinersbur/polybench +cd polybench/polybench-code +for i in `cat utilities/benchmark_list`; do FNAME=`basename $i`; echo $FNAME; clang -E -Iutilities -DPOLYBENCH_USE_SCALAR_LB $i > /tmp/$FNAME; bin/mlir-clang /tmp/$FNAME main; done +``` + diff --git a/mlir-clang/Test/CMakeLists.txt b/mlir-clang/Test/CMakeLists.txt new file mode 100644 index 000000000000..604bff89b2f5 --- /dev/null +++ b/mlir-clang/Test/CMakeLists.txt @@ -0,0 +1,31 @@ +set(MLIR_CLANG_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(MLIR_CLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(CLANG_HEADER_DIR ${LLVM_MAIN_SRC_DIR}/../clang/lib/Headers) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + MAIN_CONFIG + ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg +) + +list(APPEND MLIR_CLANG_TEST_DEPS + llvm-config + FileCheck count not + mlir-clang + split-file + clang + ) + +add_lit_testsuite(check-mlir-clang "Running the clang-to-mlir regression tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${MLIR_CLANG_TEST_DEPS} + ) + +add_lit_testsuite(check-mlir-clang-single "Running the clang-to-mlir regression tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${MLIR_CLANG_TEST_DEPS} + ARGS -j 1 + ) + +set_target_properties(check-mlir-clang PROPERTIES FOLDER "clang-to-mlir tests") diff --git a/mlir-clang/Test/Verification/affun.c b/mlir-clang/Test/Verification/affun.c new file mode 100644 index 000000000000..c6ddecd23623 --- /dev/null +++ b/mlir-clang/Test/Verification/affun.c @@ -0,0 +1,23 @@ +// RUN: mlir-clang %s --function=okernel_2mm | FileCheck %s + +void okernel_2mm(unsigned int ni, + double *tmp) { + int i, j, k; + +#pragma scop + /* D := alpha*A*B*C + beta*D */ + for (i = 0; i < ni; i++) + { + tmp[i] = 0.0; + } +#pragma endscop +} + +// CHECK: func @okernel_2mm(%arg0: i32, %arg1: memref) { +// CHECK-NEXT: %cst = constant 0.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg2 = 0 to %0 { +// CHECK-NEXT: affine.store %cst, %arg1[%arg2] : memref +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/call.c b/mlir-clang/Test/Verification/call.c new file mode 100644 index 000000000000..4c5593acfa14 --- /dev/null +++ b/mlir-clang/Test/Verification/call.c @@ -0,0 +1,25 @@ +// RUN: mlir-clang %s --function=kernel_deriche | FileCheck %s + +void sub(int a[2]) { + a[2]++; +} + +void kernel_deriche() { + int a[2]; + sub(a); +} + +// CHECK: func @kernel_deriche() { +// CHECK-NEXT: %0 = memref.alloca() : memref<2xi32> +// CHECK-NEXT: %1 = memref.cast %0 : memref<2xi32> to memref +// CHECK-NEXT: call @sub(%1) : (memref) -> () +// CHECK-NEXT: return +// CHECK-NEXT: } + +// CHECK: func @sub(%arg0: memref) { +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %0 = affine.load %arg0[2] : memref +// CHECK-NEXT: %1 = addi %0, %c1_i32 : i32 +// CHECK-NEXT: affine.store %1, %arg0[2] : memref +// CHECK-NEXT: return +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/cond.c b/mlir-clang/Test/Verification/cond.c new file mode 100644 index 000000000000..7c8080897880 --- /dev/null +++ b/mlir-clang/Test/Verification/cond.c @@ -0,0 +1,28 @@ +// RUN: mlir-clang %s %stdinclude --function=set | FileCheck %s + +#include +#include +#include +#include + + +/* Array initialization. */ + +int set (int b) +{ + int res; + if (b) + res = 1; + else + res = 2; + return res; + //path[0][1] = 2; +} + +// CHECK: func @set(%arg0: i32) -> i32 { +// CHCEK-NEXT: %c1_i32 = constant 1 : i32 +// CHCEK-NEXT: %c2_i32 = constant 2 : i32 +// CHCEK-NEXT: %0 = trunci %arg0 : i32 to i1 +// CHCEK-NEXT: %1 = select %0, %c1_i32, %c2_i32 : i32 +// CHCEK-NEXT: return %1 : i32 +// CHCEK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/cond2.c b/mlir-clang/Test/Verification/cond2.c new file mode 100644 index 000000000000..5e19a7320d0f --- /dev/null +++ b/mlir-clang/Test/Verification/cond2.c @@ -0,0 +1,26 @@ +// RUN: mlir-clang %s %stdinclude --function=set | FileCheck %s + +#include +#include +#include +#include + + +/* Array initialization. */ + +int set (int b) +{ + int res = 1; + if (b) + res = 2; + return res; + //path[0][1] = 2; +} + +// CHECK: func @set(%arg0: i32) -> i32 { +// CHCEK-NEXT: %c1_i32 = constant 1 : i32 +// CHCEK-NEXT: %c2_i32 = constant 2 : i32 +// CHCEK-NEXT: %0 = trunci %arg0 : i32 to i1 +// CHCEK-NEXT: %1 = select %0, %c2_i32, %c1_i32 : i32 +// CHCEK-NEXT: return %1 : i32 +// CHCEK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/der.c b/mlir-clang/Test/Verification/der.c new file mode 100644 index 000000000000..8a2f92883da6 --- /dev/null +++ b/mlir-clang/Test/Verification/der.c @@ -0,0 +1,13 @@ +// RUN: mlir-clang %s --function=kernel_deriche | FileCheck %s + +float kernel_deriche() { + float a2, a6; + a2 = a6 = 2.0;//EXP_FUN(-alpha); + return a2; +} + +// CHECK: func @kernel_deriche() -> f32 { +// CHECK-NEXT: %cst = constant 2.000000e+00 : f64 +// CHECK-NEXT: %0 = fptrunc %cst : f64 to f32 +// CHECK-NEXT: return %0 : f32 +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/deref.c b/mlir-clang/Test/Verification/deref.c new file mode 100644 index 000000000000..2048bb98b805 --- /dev/null +++ b/mlir-clang/Test/Verification/deref.c @@ -0,0 +1,19 @@ +// RUN: mlir-clang %s --function=kernel_deriche | FileCheck %s + +int deref(int a) { + return a; +} + +void kernel_deriche(int *a) { + deref(*a); +} + +// CHECK: func @kernel_deriche(%arg0: memref) { +// CHECK-NEXT: %0 = affine.load %arg0[0] : memref +// CHECK-NEXT: %1 = call @deref(%0) : (i32) -> i32 +// CHECK-NEXT: return +// CHECK-NEXT: } + +// CHECK: func @deref(%arg0: i32) -> i32 { +// CHECK-NEXT: return %arg0 : i32 +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/fscanf.c b/mlir-clang/Test/Verification/fscanf.c new file mode 100644 index 000000000000..34bd298254ad --- /dev/null +++ b/mlir-clang/Test/Verification/fscanf.c @@ -0,0 +1,50 @@ +// RUN: mlir-clang %s | FileCheck %s + +#include +#include + +int main() { + int no_of_nodes; + + scanf("%d",&no_of_nodes); + + // allocate host memory + int* h_graph_nodes = (int*) malloc(sizeof(int)*no_of_nodes); + + // initalize the memory + for( unsigned int i = 0; i < no_of_nodes; i++) + { + scanf("%d\n", &h_graph_nodes[i]); + } +} + +// CHECK: llvm.mlir.global internal constant @str1("%d\0A\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str0("%d\00") +// CHECK-NEXT: llvm.func @__isoc99_scanf(!llvm.ptr, ...) -> i32 +// CHECK-NEXT: func @main() -> i32 { +// CHECK-NEXT: %c1_i64 = constant 1 : i64 +// CHECK-NEXT: %c0_i64 = constant 0 : i64 +// CHECK-NEXT: %c4 = constant 4 : index +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %c0 = constant 0 : index +// CHECK-NEXT: %c1 = constant 1 : index +// CHECK-NEXT: %0 = llvm.alloca %c1_i64 x i32 : (i64) -> !llvm.ptr +// CHECK-NEXT: %1 = llvm.mlir.addressof @str0 : !llvm.ptr> +// CHECK-NEXT: %2 = llvm.getelementptr %1[%c0_i64, %c0_i64] : (!llvm.ptr>, i64, i64) -> !llvm.ptr +// CHECK-NEXT: %3 = llvm.call @__isoc99_scanf(%2, %0) : (!llvm.ptr, !llvm.ptr) -> i32 +// CHECK-NEXT: %4 = llvm.load %0 : !llvm.ptr +// CHECK-NEXT: %5 = zexti %4 : i32 to i64 +// CHECK-NEXT: %6 = index_cast %5 : i64 to index +// CHECK-NEXT: %7 = muli %6, %c4 : index +// CHECK-NEXT: %8 = divi_unsigned %7, %c4 : index +// CHECK-NEXT: %9 = memref.alloc(%8) : memref +// CHECK-NEXT: %10 = index_cast %4 : i32 to index +// CHECK-NEXT: scf.for %arg0 = %c0 to %10 step %c1 { +// CHECK-NEXT: %11 = llvm.mlir.addressof @str1 : !llvm.ptr> +// CHECK-NEXT: %12 = llvm.getelementptr %11[%c0_i64, %c0_i64] : (!llvm.ptr>, i64, i64) -> !llvm.ptr +// CHECK-NEXT: %13 = llvm.call @__isoc99_scanf(%12, %0) : (!llvm.ptr, !llvm.ptr) -> i32 +// CHECK-NEXT: %14 = llvm.load %0 : !llvm.ptr +// CHECK-NEXT: memref.store %14, %9[%arg0] : memref +// CHECK-NEXT: } +// CHECK-NEXT: return %c0_i32 : i32 +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/fw.c b/mlir-clang/Test/Verification/fw.c new file mode 100644 index 000000000000..cb35c3a050e0 --- /dev/null +++ b/mlir-clang/Test/Verification/fw.c @@ -0,0 +1,46 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +# define N 2800 + +/* Array initialization. */ +static +void init_array (int path[N]) +{ + //path[0][1] = 2; +} + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + + /* Variable declaration/allocation. */ + //POLYBENCH_1D_ARRAY_DECL(path, int, N, n); + int (*path)[N]; + //int path[POLYBENCH_C99_SELECT(N,n) + POLYBENCH_PADDING_FACTOR]; + path = (int(*)[N])polybench_alloc_data (N, sizeof(int)) ; + + /* Initialize array(s). */ + init_array (*path); + + + return 0; +} + +// CHECK: func @main(%arg0: i32, %arg1: !llvm.ptr>) -> i32 { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %0 = memref.alloc() : memref<2800xi32> +// CHECK-NEXT: %1 = memref.cast %0 : memref<2800xi32> to memref +// CHECK-NEXT: call @init_array(%1) : (memref) -> () +// CHECK-NEXT: return %c0_i32 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: func private @init_array(%arg0: memref) { +// CHECK-NEXT: return +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/fwfree.c b/mlir-clang/Test/Verification/fwfree.c new file mode 100644 index 000000000000..b83b73dff6d6 --- /dev/null +++ b/mlir-clang/Test/Verification/fwfree.c @@ -0,0 +1,47 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +# define N 2800 + +/* Array initialization. */ +static +void init_array (int path[N]) +{ + //path[0][1] = 2; +} + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + + /* Variable declaration/allocation. */ + //POLYBENCH_1D_ARRAY_DECL(path, int, N, n); + int (*path)[N]; + //int path[POLYBENCH_C99_SELECT(N,n) + POLYBENCH_PADDING_FACTOR]; + path = (int(*)[N])polybench_alloc_data (N, sizeof(int)) ; + + /* Initialize array(s). */ + init_array (*path); + + POLYBENCH_FREE_ARRAY(path); + return 0; +} + +// CHECK: func @main(%arg0: i32, %arg1: !llvm.ptr>) -> i32 { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %0 = memref.alloc() : memref<2800xi32> +// CHECK-NEXT: %1 = memref.cast %0 : memref<2800xi32> to memref +// CHECK-NEXT: call @init_array(%1) : (memref) -> () +// CHECK-NEXT: memref.dealloc %0 : memref<2800xi32> +// CHECK-NEXT: return %c0_i32 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: func private @init_array(%arg0: memref) { +// CHECK-NEXT: return +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/ker.c b/mlir-clang/Test/Verification/ker.c new file mode 100644 index 000000000000..f405817064ab --- /dev/null +++ b/mlir-clang/Test/Verification/ker.c @@ -0,0 +1,15 @@ +// RUN: mlir-clang %s --function=kernel_deriche | FileCheck %s + +int kernel_deriche(int a[30][40]) { + a[3][5]++; + return a[1][2]; +} + +// CHECK: func @kernel_deriche(%arg0: memref) -> i32 { +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %0 = affine.load %arg0[3, 5] : memref +// CHECK-NEXT: %1 = addi %0, %c1_i32 : i32 +// CHECK-NEXT: affine.store %1, %arg0[3, 5] : memref +// CHECK-NEXT: %2 = affine.load %arg0[1, 2] : memref +// CHECK-NEXT: return %2 : i32 +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/ler.c b/mlir-clang/Test/Verification/ler.c new file mode 100644 index 000000000000..10a9a5dfb7c2 --- /dev/null +++ b/mlir-clang/Test/Verification/ler.c @@ -0,0 +1,15 @@ +// RUN: mlir-clang %s --function=kernel_deriche | FileCheck %s + +int kernel_deriche(int *a) { + a[3]++; + return a[1]; +} + +// CHECK: func @kernel_deriche(%arg0: memref) -> i32 { +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %0 = affine.load %arg0[3] : memref +// CHECK-NEXT: %1 = addi %0, %c1_i32 : i32 +// CHECK-NEXT: affine.store %1, %arg0[3] : memref +// CHECK-NEXT: %2 = affine.load %arg0[1] : memref +// CHECK-NEXT: return %2 : i32 +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/lum.c b/mlir-clang/Test/Verification/lum.c new file mode 100644 index 000000000000..c43b2ea46895 --- /dev/null +++ b/mlir-clang/Test/Verification/lum.c @@ -0,0 +1,10 @@ +// RUN: mlir-clang %s --function=test | FileCheck %s + +int test() { + return -3; +} + +// CHECK: func @test() -> i32 { +// CHECK-NEXT: %c-3_i32 = constant -3 : i32 +// CHECK-NEXT: return %c-3_i32 : i32 +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/malloc.c b/mlir-clang/Test/Verification/malloc.c new file mode 100644 index 000000000000..ecab6ab49161 --- /dev/null +++ b/mlir-clang/Test/Verification/malloc.c @@ -0,0 +1,23 @@ +// RUN: mlir-clang %s --function=caller | FileCheck %s + +#include + +void sum(double *result); + +void caller(int size) { + double* array = (double*)malloc(sizeof(double) * size); + sum(array); + free(array); +} + +// CHECK: func @caller(%arg0: i32) { +// CHECK-NEXT: %c8 = constant 8 : index +// CHECK-NEXT: %0 = zexti %arg0 : i32 to i64 +// CHECK-NEXT: %1 = index_cast %0 : i64 to index +// CHECK-NEXT: %2 = muli %1, %c8 : index +// CHECK-NEXT: %3 = divi_unsigned %2, %c8 : index +// CHECK-NEXT: %4 = memref.alloc(%3) : memref +// CHECK-NEXT: call @sum(%4) : (memref) -> () +// CHECK-NEXT: memref.dealloc %4 : memref +// CHECK-NEXT: return +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/mer.c b/mlir-clang/Test/Verification/mer.c new file mode 100644 index 000000000000..0b49ce3e6d09 --- /dev/null +++ b/mlir-clang/Test/Verification/mer.c @@ -0,0 +1,15 @@ +// RUN: mlir-clang %s --function=kernel_deriche | FileCheck %s + +int kernel_deriche(int a[30]) { + a[0]++; + return a[1]; +} + +// CHECK: func @kernel_deriche(%arg0: memref) -> i32 { +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %0 = affine.load %arg0[0] : memref +// CHECK-NEXT: %1 = addi %0, %c1_i32 : i32 +// CHECK-NEXT: affine.store %1, %arg0[0] : memref +// CHECK-NEXT: %2 = affine.load %arg0[1] : memref +// CHECK-NEXT: return %2 : i32 +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/nestloop.c b/mlir-clang/Test/Verification/nestloop.c new file mode 100644 index 000000000000..49dd7e41cb75 --- /dev/null +++ b/mlir-clang/Test/Verification/nestloop.c @@ -0,0 +1,75 @@ +// RUN: mlir-clang %s %stdinclude --function=init_array | FileCheck %s + +#include +#include +#include +#include + + + +void init_array (int path[10][10]) +{ + int i, j; + + for (i = 0; i < 10; i++) + for (j = 0; j < 10; j++) { + path[i][j] = i*j%7+1; + if ((i+j)%13 == 0 || (i+j)%7==0 || (i+j)%11 == 0) + path[i][j] = 999; + } +} + +// CHECK: func @init_array(%arg0: memref) { +// CHECK-DAG: %c0_i32 = constant 0 : i32 +// CHECK-DAG: %true = constant true +// CHECK-DAG: %c10_i32 = constant 10 : i32 +// CHECK-DAG: %c13_i32 = constant 13 : i32 +// CHECK-DAG: %c7_i32 = constant 7 : i32 +// CHECK-DAG: %c11_i32 = constant 11 : i32 +// CHECK-DAG: %c999_i32 = constant 999 : i32 +// CHECK-DAG: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %0 = scf.while (%arg1 = %c0_i32) : (i32) -> i32 { +// CHECK-NEXT: %1 = cmpi slt, %arg1, %c10_i32 : i32 +// CHECK-NEXT: scf.condition(%1) %arg1 : i32 +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%arg1: i32): // no predecessors +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %2 = scf.while (%arg2 = %c0_i32) : (i32) -> i32 { +// CHECK-NEXT: %4 = cmpi slt, %arg2, %c10_i32 : i32 +// CHECK-NEXT: scf.condition(%4) %arg2 : i32 +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%arg2: i32): // no predecessors +// CHECK-NEXT: %4 = index_cast %arg2 : i32 to index +// CHECK-NEXT: %5 = muli %arg1, %arg2 : i32 +// CHECK-NEXT: %6 = remi_signed %5, %c7_i32 : i32 +// CHECK-NEXT: %7 = addi %6, %c1_i32 : i32 +// CHECK-NEXT: memref.store %7, %arg0[%1, %4] : memref +// CHECK-NEXT: %8 = addi %arg1, %arg2 : i32 +// CHECK-NEXT: %9 = remi_signed %8, %c13_i32 : i32 +// CHECK-NEXT: %10 = cmpi eq, %9, %c0_i32 : i32 +// CHECK-NEXT: %11 = scf.if %10 -> (i1) { +// CHECK-NEXT: scf.yield %true : i1 +// CHECK-NEXT: } else { +// CHECK-NEXT: %14 = remi_signed %8, %c7_i32 : i32 +// CHECK-NEXT: %15 = cmpi eq, %14, %c0_i32 : i32 +// CHECK-NEXT: scf.yield %15 : i1 +// CHECK-NEXT: } +// CHECK-NEXT: %12 = scf.if %11 -> (i1) { +// CHECK-NEXT: scf.yield %true : i1 +// CHECK-NEXT: } else { +// CHECK-NEXT: %14 = remi_signed %8, %c11_i32 : i32 +// CHECK-NEXT: %15 = cmpi eq, %14, %c0_i32 : i32 +// CHECK-NEXT: scf.yield %15 : i1 +// CHECK-NEXT: } +// CHECK-NEXT: scf.if %12 { +// CHECK-NEXT: memref.store %c999_i32, %arg0[%1, %4] : memref +// CHECK-NEXT: } +// CHECK-NEXT: %13 = addi %arg2, %c1_i32 : i32 +// CHECK-NEXT: scf.yield %13 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: %3 = addi %arg1, %c1_i32 : i32 +// CHECK-NEXT: scf.yield %3 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/nus.c b/mlir-clang/Test/Verification/nus.c new file mode 100644 index 000000000000..83b34cb294dd --- /dev/null +++ b/mlir-clang/Test/Verification/nus.c @@ -0,0 +1,31 @@ +// RUN: mlir-clang %s --function=kernel_nussinov | FileCheck %s +#define N 5500 +#define max_score(s1, s2) ((s1 >= s2) ? s1 : s2) + +// CHECK: @kernel_nussinov(%arg0: i32, %arg1: memref) { +// CHECK-NEXT: affine.for %arg2 = 1 to 5500 { +// CHECK-NEXT: affine.if #set(%arg2) { +// CHECK-NEXT: %0 = affine.load %arg1[%arg2] : memref +// CHECK-NEXT: %1 = affine.load %arg1[%arg2 - 1] : memref +// CHECK-NEXT: %2 = cmpi sge, %0, %1 : i32 +// CHECK-NEXT: %3 = select %2, %0, %1 : i32 +// CHECK-NEXT: affine.store %3, %arg1[%arg2] : memref +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +void kernel_nussinov(int n, int table[N]) +{ + int j; + +#pragma scop + for (j=1; j=0) + table[j] = max_score(table[j], table[j-1]); + + } +#pragma endscop + +} diff --git a/mlir-clang/Test/Verification/palloc.c b/mlir-clang/Test/Verification/palloc.c new file mode 100644 index 000000000000..ad6d7409ce09 --- /dev/null +++ b/mlir-clang/Test/Verification/palloc.c @@ -0,0 +1,30 @@ +// RUN: mlir-clang %s %stdinclude --function=init_array | FileCheck %s + +#include +#include +#include + +/* Include polybench common header. */ +#include + +void use(double A[20]); +/* Array initialization. */ + +void init_array (int n) +{ + double (*B)[20] = (double(*)[20])polybench_alloc_data (20, sizeof(double)) ; + (*B)[2] = 3.0; + use(*B); +} + + +// CHECK: func @init_array(%arg0: i32) +// CHECK-NEXT: %cst = constant 3.000000e+00 : f64 +// CHECK-NEXT: %0 = memref.alloc() : memref<20xf64> +// CHECK-NEXT: affine.store %cst, %0[2] : memref<20xf64> +// CHECK-NEXT: %1 = memref.cast %0 : memref<20xf64> to memref +// CHECK-NEXT: call @use(%1) : (memref) -> () +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/Verification/raiseToAffine.c b/mlir-clang/Test/Verification/raiseToAffine.c new file mode 100644 index 000000000000..ae885db26325 --- /dev/null +++ b/mlir-clang/Test/Verification/raiseToAffine.c @@ -0,0 +1,51 @@ +// RUN: split-file %s %t + +// RUN: mlir-clang %t/matmul_signed_cmp.cpp --function=matmul --raise-scf-to-affine | FileCheck %s -check-prefix=GEMMSIGNED +// RUN: mlir-clang %t/matmul_unsigned_cmp.cpp --function=matmul_unsigned_cmp --raise-scf-to-affine | FileCheck %s -check-prefix=GEMMUNSIGNED + +//--- matmul_signed_cmp.cpp +#define N 200 +#define M 300 +#define K 400 +#define DATA_TYPE float + +void matmul(DATA_TYPE A[N][K], DATA_TYPE B[K][M], DATA_TYPE C[N][M]) { + int i, j, k; + // GEMMSIGNED: affine.for + for (i = 0; i < N; i++) + // GEMMSIGNED: affine.for + for (j = 0; j < M; j++) + // GEMMSIGNED: affine.for + for (k = 0; k < K; k++) + // GEMMSIGNED: {{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref + // GEMMSIGNED: {{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref + // GEMMSIGNED: {{.*}} = mulf + // GEMMSIGNED: {{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref + // GEMMSIGNED: {{.*}} = addf + // GEMMSIGNED: affine.store {{.*}}, %{{.*}}[%{{.*}}, %{{.*}}] : memref + C[i][j] += A[i][k] * B[k][j]; +} + +//--- matmul_unsigned_cmp.cpp +void matmul_unsigned_cmp(float A[100][200], float B[200][300], float C[100][300]) { + int i, j, k; + + // GEMMUNSIGNED: affine.for + for (i = 0; i < 100; i++) { + // GEMMUNSIGNED: affine.for + for (j = 0; j < 300; j++) { + // GEMMUNSIGNED: affine.store %{{.*}}, %{{.*}}[%{{.*}}, %{{.*}}] : memref + C[i][j] = 0; + // GEMMUNSIGNED: affine.for + for (k = 0; k < 200; k++) { + // GEMMUNSIGNED: {{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref + // GEMMUNSIGNED: {{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref + // GEMMUNSIGNED: {{.*}} = mulf + // GEMMUNSIGNED: {{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref + // GEMMUNSIGNED: {{.*}} = addf + // GEMMUNSIGNED: affine.store {{.*}}, %{{.*}}[%{{.*}}, %{{.*}}] : memref + C[i][j] += A[i][k] * B[k][j]; + } + } + } +} diff --git a/mlir-clang/Test/Verification/raiseToAffineUnsignedCmp.c b/mlir-clang/Test/Verification/raiseToAffineUnsignedCmp.c new file mode 100644 index 000000000000..417633e433ce --- /dev/null +++ b/mlir-clang/Test/Verification/raiseToAffineUnsignedCmp.c @@ -0,0 +1,24 @@ +// RUN: mlir-clang %s --function=matmul --raise-scf-to-affine | FileCheck %s + +void matmul(float A[100][200], float B[200][300], float C[100][300]) { + int i, j, k; + + // CHECK: affine.for + for (i = 0; i < 100; i++) { + // CHECK: affine.for + for (j = 0; j < 300; j++) { + // CHECK: affine.store %{{.*}}, %{{.*}}[%{{.*}}, %{{.*}}] : memref + C[i][j] = 0; + // CHECK: affine.for + for (k = 0; k < 200; k++) { + // CHECK: {{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref + // CHECK: {{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref + // CHECK: {{.*}} = mulf + // CHECK: {{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref + // CHECK: {{.*}} = addf + // CHECK: affine.store {{.*}}, %{{.*}}[%{{.*}}, %{{.*}}] : memref + C[i][j] += A[i][k] * B[k][j]; + } + } + } + diff --git a/mlir-clang/Test/Verification/red.mlir b/mlir-clang/Test/Verification/red.mlir new file mode 100644 index 000000000000..1c5dc3940a47 --- /dev/null +++ b/mlir-clang/Test/Verification/red.mlir @@ -0,0 +1,28 @@ +// RUN: mlir-opt --detect-reduction %s | FileCheck %s +// XFAIL: * + +module { + func @reduce_with_iter_args(%arg0: memref) -> f32 { + %c0 = constant 0 : index + %0 = memref.dim %arg0, %c0 : memref + %cst = constant 0.000000e+00 : f32 + %cst_0 = constant 1.000000e+00 : f32 + %1 = memref.alloca() : memref<1xf32> + affine.store %cst, %1[0] : memref<1xf32> + %2 = memref.alloca() : memref<1xf32> + affine.store %cst_0, %2[0] : memref<1xf32> + affine.for %arg1 = 0 to %0 { + %6 = affine.load %2[0] : memref<1xf32> + %7 = affine.load %1[0] : memref<1xf32> + %8 = affine.load %arg0[%arg1] : memref + %9 = addf %7, %8 : f32 + %10 = mulf %6, %8 : f32 + affine.store %9, %1[0] : memref<1xf32> + affine.store %10, %2[0] : memref<1xf32> + } + %3 = affine.load %2[0] : memref<1xf32> + %4 = affine.load %1[0] : memref<1xf32> + %5 = addf %4, %3 : f32 + return %5 : f32 + } +} diff --git a/mlir-clang/Test/Verification/redstore.c b/mlir-clang/Test/Verification/redstore.c new file mode 100644 index 000000000000..971e21d97325 --- /dev/null +++ b/mlir-clang/Test/Verification/redstore.c @@ -0,0 +1,50 @@ +// RUN: mlir-clang %s --function=caller --detect-reduction | FileCheck %s + +extern int print(double); + +void sum(double *result, double* array, int N) { + #pragma scop + for (int j=0; j) { +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %0 = memref.alloca() : memref<1xf64> +// CHECK-NEXT: %1 = memref.cast %0 : memref<1xf64> to memref +// CHECK-NEXT: call @sum(%1, %arg0, %c1_i32) : (memref, memref, i32) -> () +// CHECK-NEXT: %2 = affine.load %0[0] : memref<1xf64> +// CHECK-NEXT: %3 = call @print(%2) : (f64) -> i32 +// CHECK-NEXT: return +// CHECK-NEXT: } + +// CHECK: func @sum(%arg0: memref, %arg1: memref, %arg2: i32) { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %0 = index_cast %arg2 : i32 to index +// CHECK-NEXT: affine.for %arg3 = 0 to %0 { +// CHECK-NEXT: %1 = sitofp %c0_i32 : i32 to f64 +// CHECK-NEXT: affine.store %1, %arg0[0] : memref +// CHECK-NEXT: %2 = affine.load %arg0[0] : memref +// CHECK-NEXT: %3 = affine.for %arg4 = 0 to 10 iter_args(%arg5 = %2) -> (f64) { +// CHECK-NEXT: %6 = affine.load %arg1[%arg4] : memref +// CHECK-NEXT: %7 = addf %arg5, %6 : f64 +// CHECK-NEXT: affine.yield %7 : f64 +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %3, %arg0[0] : memref +// CHECK-NEXT: %4 = affine.load %arg0[0] : memref +// CHECK-NEXT: %5 = call @print(%4) : (f64) -> i32 +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/redstore2.c b/mlir-clang/Test/Verification/redstore2.c new file mode 100644 index 000000000000..fbc0bb6e2dee --- /dev/null +++ b/mlir-clang/Test/Verification/redstore2.c @@ -0,0 +1,41 @@ +// RUN: mlir-clang %s --function=caller --detect-reduction | FileCheck %s + +extern int print(double); + +void sum(double *result, double* array) { + result[0] = 0; + #pragma scop + for (int i=0; i<10; i++) { + result[0] += array[i]; + } + #pragma endscop +} + +void caller(double* array) { + double result; + sum(&result, array); + print(result); +} + +// CHECK: func @caller(%arg0: memref) { +// CHECK-NEXT: %0 = memref.alloca() : memref<1xf64> +// CHECK-NEXT: %1 = memref.cast %0 : memref<1xf64> to memref +// CHECK-NEXT: call @sum(%1, %arg0) : (memref, memref) -> () +// CHECK-NEXT: %2 = affine.load %0[0] : memref<1xf64> +// CHECK-NEXT: %3 = call @print(%2) : (f64) -> i32 +// CHECK-NEXT: return +// CHECK-NEXT: } + +// CHECK: func @sum(%arg0: memref, %arg1: memref) { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %0 = sitofp %c0_i32 : i32 to f64 +// CHECK-NEXT: affine.store %0, %arg0[0] : memref +// CHECK-NEXT: %1 = affine.load %arg0[0] : memref +// CHECK-NEXT: %2 = affine.for %arg2 = 0 to 10 iter_args(%arg3 = %1) -> (f64) { +// CHECK-NEXT: %3 = affine.load %arg1[%arg2] : memref +// CHECK-NEXT: %4 = addf %arg3, %3 : f64 +// CHECK-NEXT: affine.yield %4 : f64 +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %2, %arg0[0] : memref +// CHECK-NEXT: return +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/reduction.c b/mlir-clang/Test/Verification/reduction.c new file mode 100644 index 000000000000..e563dabd67a2 --- /dev/null +++ b/mlir-clang/Test/Verification/reduction.c @@ -0,0 +1,46 @@ +// RUN: mlir-clang %s --function=reduction_gemm | FileCheck %s +// XFAIL: * +void reduction_gemm() { + int i, j, k; + int A[1024][1024]; + int B[1024][1024]; + int C[1024][1024]; + +#pragma scop + for (i = 0; i < 1024; i++) + for (j = 0; j < 1024; j++) + for (k = 0; k < 1024; k++) + C[i][j] += A[i][k] * B[k][j]; +#pragma endscop +} + +// RUN: mlir-clang %s --function=reduction_bicg | FileCheck %s +// XFAIL: * +void reduction_bicg() { + int i, j; + int A[100][200]; + int r[100]; + int s[200]; + int p[200]; + int q[100]; + +#pragma scop + for (i = 0; i < 100; i++) { + for (j = 0; j < 200; j++) { + s[j] = s[j] + r[i] * A[i][j]; + } + } +#pragma endscop +} + +// RUN: mlir-clang %s --function=reduction_sum | FileCheck %s +// XFAIL: * +void reduction_sum() { + int sum = 0; + int A[100]; +#pragma scop + for (int i = 0; i < 100; i++) + sum += A[i]; +#pragma endscop +} + diff --git a/mlir-clang/Test/Verification/setter.c b/mlir-clang/Test/Verification/setter.c new file mode 100644 index 000000000000..9f76e8c68319 --- /dev/null +++ b/mlir-clang/Test/Verification/setter.c @@ -0,0 +1,23 @@ +// RUN: mlir-clang %s --function=kernel_deriche | FileCheck %s + +void sub(int *a) { + *a = 3; +} + +void kernel_deriche() { + int a; + sub(&a); +} + +// CHECK: func @kernel_deriche() { +// CHECK-NEXT: %0 = memref.alloca() : memref<1xi32> +// CHECK-NEXT: %1 = memref.cast %0 : memref<1xi32> to memref +// CHECK-NEXT: call @sub(%1) : (memref) -> () +// CHECK-NEXT: return +// CHECK-NEXT: } + +// CHECK: func @sub(%arg0: memref) { +// CHECK-NEXT: %c3_i32 = constant 3 : i32 +// CHECK-NEXT: affine.store %c3_i32, %arg0[0] : memref +// CHECK-NEXT: return +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/threeInt.c b/mlir-clang/Test/Verification/threeInt.c new file mode 100644 index 000000000000..92e29cb206cc --- /dev/null +++ b/mlir-clang/Test/Verification/threeInt.c @@ -0,0 +1,14 @@ +// RUN: mlir-clang %s --function=struct_pass_all_same | FileCheck %s + +typedef struct { + int a, b, c; +} threeInt; + +int struct_pass_all_same(threeInt* a) { + return a->b; +} + +// CHECK: func @struct_pass_all_same(%arg0: memref) -> i32 { +// CHECK-NEXT: %0 = affine.load %arg0[0, 1] : memref +// CHECK-NEXT: return %0 : i32 +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/whileset.c b/mlir-clang/Test/Verification/whileset.c new file mode 100644 index 000000000000..1a2c7a98a6ab --- /dev/null +++ b/mlir-clang/Test/Verification/whileset.c @@ -0,0 +1,40 @@ +// RUN: mlir-clang %s %stdinclude --function=set | FileCheck %s + +#include +#include +#include +#include + + +/* Array initialization. */ + +void set (int path[20]) +{ + int i = 0; + while (1) { + path[i] = 3; + i++; + if (i == 20) break; + } + //path[0][1] = 2; +} + +// TODO consider making into for +// CHECK: func @set(%arg0: memref) { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %c3_i32 = constant 3 : i32 +// CHECK-NEXT: %c20_i32 = constant 20 : i32 +// CHECK-NEXT: %true = constant true +// CHECK-NEXT: %0 = scf.while (%arg1 = %true, %arg2 = %c0_i32) : (i1, i32) -> i32 { +// CHECK-NEXT: scf.condition(%arg1) %arg2 : i32 +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%arg1: i32): // no predecessors +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: memref.store %c3_i32, %arg0[%1] : memref +// CHECK-NEXT: %2 = addi %arg1, %c1_i32 : i32 +// CHECK-NEXT: %3 = cmpi ne, %2, %c20_i32 : i32 +// CHECK-NEXT: scf.yield %3, %2 : i1, i32 +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/Verification/whiletofor.c b/mlir-clang/Test/Verification/whiletofor.c new file mode 100644 index 000000000000..6842f548220a --- /dev/null +++ b/mlir-clang/Test/Verification/whiletofor.c @@ -0,0 +1,43 @@ +// RUN: mlir-clang %s --function=whiletofor | FileCheck %s + +void whiletofor() { + int a[100][100]; + int t = 7; + int i, j; + + for (i = 0; i < 100; i++) + for (j = 0; j < 100; j++) { + if (t % 20 == 0) + a[i][j] = 2; + a[i][j] = 3; + t++; + } +} + + +// CHECK: func @whiletofor() { +// CHECK-NEXT: %c7_i32 = constant 7 : i32 +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %c20_i32 = constant 20 : i32 +// CHECK-NEXT: %c2_i32 = constant 2 : i32 +// CHECK-NEXT: %c3_i32 = constant 3 : i32 +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %c100 = constant 100 : index +// CHECK-NEXT: %c0 = constant 0 : index +// CHECK-NEXT: %c1 = constant 1 : index +// CHECK-NEXT: %0 = memref.alloca() : memref<100x100xi32> +// CHECK-NEXT: %1 = scf.for %arg0 = %c0 to %c100 step %c1 iter_args(%arg1 = %c7_i32) -> (i32) { +// CHECK-NEXT: %2 = scf.for %arg2 = %c0 to %c100 step %c1 iter_args(%arg3 = %arg1) -> (i32) { +// CHECK-NEXT: %3 = remi_signed %arg3, %c20_i32 : i32 +// CHECK-NEXT: %4 = cmpi eq, %3, %c0_i32 : i32 +// CHECK-NEXT: scf.if %4 { +// CHECK-NEXT: memref.store %c2_i32, %0[%arg0, %arg2] : memref<100x100xi32> +// CHECK-NEXT: } +// CHECK-NEXT: memref.store %c3_i32, %0[%arg0, %arg2] : memref<100x100xi32> +// CHECK-NEXT: %5 = addi %arg3, %c1_i32 : i32 +// CHECK-NEXT: scf.yield %5 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %2 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } diff --git a/mlir-clang/Test/Verification/x.c b/mlir-clang/Test/Verification/x.c new file mode 100644 index 000000000000..2cec228352be --- /dev/null +++ b/mlir-clang/Test/Verification/x.c @@ -0,0 +1,17 @@ +// RUN: mlir-clang %s --function=kernel_deriche | FileCheck %s + +int kernel_deriche(int x) { + x++; + x+=1; + x*=2; + return x; +} + +// CHECK: func @kernel_deriche(%arg0: i32) -> i32 { +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %c2_i32 = constant 2 : i32 +// CHECK-NEXT: %0 = addi %arg0, %c1_i32 : i32 +// CHECK-NEXT: %1 = addi %0, %c1_i32 : i32 +// CHECK-NEXT: %2 = muli %1, %c2_i32 : i32 +// CHECK-NEXT: return %2 : i32 +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/aff.c b/mlir-clang/Test/aff.c new file mode 100644 index 000000000000..84c7ab02626d --- /dev/null +++ b/mlir-clang/Test/aff.c @@ -0,0 +1,24 @@ +// RUN: mlir-clang %s --function=kernel_deriche | FileCheck %s + +void kernel_deriche(int w, int h, double alpha, double** y2) { + int i,j; + +#pragma scop + for (i=0; i=0; j--) { + y2[i][j] = alpha; + } + } +#pragma endscop +} +// CHECK: func @kernel_deriche(%arg0: i32, %arg1: i32, %arg2: f64, %arg3: memref>) { +// CHECK-NEXT: %0 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg4 = 0 to %1 { +// CHECK-NEXT: affine.for %arg5 = 0 to %0 { +// CHECK-NEXT: %2 = affine.load %arg3[%arg4] : memref> +// CHECK-NEXT: affine.store %arg2, %2[-%arg5 + symbol(%0) - 1] : memref +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } \ No newline at end of file diff --git a/mlir-clang/Test/global.c b/mlir-clang/Test/global.c new file mode 100644 index 000000000000..eaecdf11ff96 --- /dev/null +++ b/mlir-clang/Test/global.c @@ -0,0 +1,26 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s + +float A[64][32]; + +int main() { + #pragma scop + for (int i = 0; i < 64; i++) + for (int j = 0; j < 32; j++) + A[i][j] = 3.0; + #pragma endscop + return 0; +} + +// CHECK: memref.global "private" @A : memref<64x32xf32> +// CHECK-NEXT: func @main() -> i32 { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %cst = constant 3.000000e+00 : f64 +// CHECK-NEXT: affine.for %arg0 = 0 to 64 { +// CHECK-NEXT: affine.for %arg1 = 0 to 32 { +// CHECK-NEXT: %0 = memref.get_global @A : memref<64x32xf32> +// CHECK-NEXT: %1 = fptrunc %cst : f64 to f32 +// CHECK-NEXT: affine.store %1, %0[%arg0, %arg1] : memref<64x32xf32> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return %c0_i32 : i32 +// CHECK-NEXT: } diff --git a/mlir-clang/Test/lit.cfg b/mlir-clang/Test/lit.cfg new file mode 100644 index 000000000000..9469f971302c --- /dev/null +++ b/mlir-clang/Test/lit.cfg @@ -0,0 +1,64 @@ +# -*- Python -*- + +# Configuration file for the 'lit' test runner. + +import os +import lit.formats + +from lit.llvm import llvm_config + +# Configuration file for the 'lit' test runner. + +# name: The name of this test suite. +config.name = 'mlir-clang' + +# testFormat: The test format to use to interpret tests. +config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) + +# suffixes: A list of file extensions to treat as test files. This is overriden +# by individual lit.local.cfg files in the test subdirectories. +config.suffixes = ['.c', '.cpp', '.cu'] + +# excludes: A list of directories or files to exclude from the testsuite even +# if they match the suffixes pattern. +#config.excludes = ['Inputs'] + +# test_source_root: The root path where tests are located. +config.test_source_root = os.path.dirname(__file__) + +# test_exec_root: The root path where tests should be run. +config.test_exec_root = os.path.join(config.mlir_clang_obj_root, 'test') + +# Tweak the PATH to include the tools dir. +llvm_config.with_environment('PATH', config.llvm_tools_dir, append_path=True) +llvm_config.with_environment('PATH', config.polygeist_tools_dir, append_path=True) + +import tempfile +lit_tmp = tempfile.mkdtemp(prefix="lit_tmp_") +os.environ.update({ + 'TMPDIR': lit_tmp, + 'TMP': lit_tmp, + 'TEMP': lit_tmp, + 'TEMPDIR': lit_tmp, +}) + +# Propagate some variables from the host environment. +llvm_config.with_system_environment( + ['HOME', 'INCLUDE', 'LIB', 'TMP', 'TEMP']) + +llvm_config.use_default_substitutions() + +# For each occurrence of a tool name, replace it with the full path to +# the build directory holding that tool. We explicitly specify the directories +# to search to ensure that we get the tools just built and not some random +# tools that might happen to be in the user's PATH. + +tool_dirs = [config.llvm_tools_dir] +tools = [ 'opt', 'clang' ] +llvm_config.add_tool_substitutions(tools, tool_dirs) +tool_dirs = [config.polygeist_tools_dir] +tools = [ 'mlir-clang' ] +llvm_config.add_tool_substitutions(tools, tool_dirs) +config.substitutions.append(('%stdinclude', '-I ' + config.clang_header_dir + " -I " + config.test_source_root + "/polybench/utilities")) +config.substitutions.append(('%polyexec', config.test_source_root + '/polybench/utilities/polybench.c -D POLYBENCH_TIME -D POLYBENCH_NO_FLUSH_CACHE -D MINI_DATASET')) +config.substitutions.append(('%polyverify', config.test_source_root + '/polybench/utilities/polybench.c -D POLYBENCH_DUMP_ARRAYS -D POLYBENCH_NO_FLUSH_CACHE -D MINI_DATASET')) diff --git a/mlir-clang/Test/lit.site.cfg.in b/mlir-clang/Test/lit.site.cfg.in new file mode 100644 index 000000000000..e3525369c69c --- /dev/null +++ b/mlir-clang/Test/lit.site.cfg.in @@ -0,0 +1,25 @@ +@LIT_SITE_CFG_IN_HEADER@ + +config.llvm_src_root = path(r"@LLVM_SOURCE_DIR@") +config.llvm_obj_root = path(r"@LLVM_BINARY_DIR@") +config.llvm_tools_dir = path(r"@LLVM_TOOLS_DIR@") +config.polygeist_tools_dir = path(r"@POLYGEIST_TOOLS_DIR@" + "/bin") +config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" +config.mlir_clang_obj_root = "@MLIR_CLANG_BINARY_DIR@" +config.clang_header_dir = "@CLANG_HEADER_DIR@" +config.target_triple = "@TARGET_TRIPLE@" +config.llvm_obj_root = path(r"@LLVM_BINARY_DIR@") + +# Support substitution of the tools and build_mode with user parameters. +# This is used when we can't determine the tool dir at configuration time. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params +except KeyError as e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) + +import lit.llvm +lit.llvm.initialize(lit_config, config) + +# Let the main config do the real work. +lit_config.load_config(config, "@MLIR_CLANG_TEST_DIR@/lit.cfg") diff --git a/mlir-clang/Test/polybench/AUTHORS b/mlir-clang/Test/polybench/AUTHORS new file mode 100644 index 000000000000..2a94835be55a --- /dev/null +++ b/mlir-clang/Test/polybench/AUTHORS @@ -0,0 +1,17 @@ +* * * * * * * * * * * * * +* Authors of PolyBench * +* * * * * * * * * * * * * + + +* Louis-Noel Pouchet + Who provided packaging and harmonization of all test files, + the PolyBench infrastructure and machinery, and several + reference C files. + +* Uday Bondugula + Who provided many of the original reference C files, including + Fortran to C translation. + +* Tomofumi Yuki + Who provided all the fixes implemented in 4.0, and is now the maintainer + of PolyBench/C. diff --git a/mlir-clang/Test/polybench/CHANGELOG b/mlir-clang/Test/polybench/CHANGELOG new file mode 100644 index 000000000000..52895df6580f --- /dev/null +++ b/mlir-clang/Test/polybench/CHANGELOG @@ -0,0 +1,147 @@ +------------------- +* Changes in 4.2.1: +------------------- + - Fixed an issue with PAPI support. + Reported by Keyur Joshi + - Added support for PAPI 5.4.x. + +----------------- +* Changes in 4.2: +----------------- + - Fixed an issue with OpenMP pragma for flush_cache. + Reported by Sudheer Kumar . + - Fixed a bug in Makefile generation (utilities/makefile-gen.pl). + Reported by Willy Wolff . + - Fixed a bug in syr2k that caused the kernel to perform twice the work. + It was computing the full symmetric output whereas the BLAS version + only computes one side of the symmetry. + - Input generation for some of the kernels has been updated to produce + fewer zeros in output. + - Add support for inter-array padding when using heap allocation, + and adjust the allocation alignment with posix_memalign to 4096. + +----------------- +* Changes in 4.1: +----------------- + - Added LICENSE.txt + - Fixed a bug in documentation of cholesky. (Reported by François Gindraud) + - Removed two statements from cholesky, which were useless with inplace + memory allocation. (Reported by François Gindraud) + - Updated polybench.R in utilities to match the change in input generation + of lu/ludcmp/cholesky. + - Simplified the macros for switching between data types. + - Now users may specify DATA_TYPE_IS_XXX where XXX is one of + FLOAT/DOUBLE/INT to change all macros associated with data types. + - Fixed a typo in Jacobi-1d loop iterators. + - Fixed issues with SCALAR_VAL macro in some kernels. + (Reported by Sven Verdoolaege) + - Fixed a typo in POLYBENCH_GFLOPS system. + +----------------- +* Changes in 4.0: +----------------- + +This is a detailed ChangeLog for PolyBench/C 4.0 to indicate all +changes made from version 3.2. Due to the high number of changes, +users should be warned the baseline performance of kernels available +in both PolyBench/C 3.2 and 4.0 may differ. + + += General + - tuned the initialization functions to reduce the possibility to have 'inf' + in outputs. Specifically, the initial values are much closer to 1 for most + linear algebra kernels now (was up to N in prior versions). + - changed the name of predefined problem sizes STANDARD to MEDIUM. + - changed the default dataset to LARGE. + - added a few perl scripts to utilities. + - replaced create_cpped_version.sh with a perl script. + - fixed a bug in polybench.h when 4D arrays were allocated. (extra comma) + (Reported by Amarin Phaosawasdi) + - added polybench.pdf as a documentation of all kernels and its + underlying algorithms + - added POLYBENCH_USE_RESTRICT to allow compilers to assume alias-free + (Patch by Tobias Grosser) + - added SCALAR_VAL, SQRT_FUN, EXP_FUN, and POW_FUN macros to switch + float/double versions of the math functions depending on the data type. + - changed naming of a variable in polybench.c/xmalloc to avoid + issues when compiled as C++ (Reported by Sven Verdoolaege) + - changed the outputs produced by POLYBENCH_DUMP_ARRAYS option to + separately print out each array. + - added polybench.R in utilities which defines datamining and + linear-algebra kernels using R script for testing. + += Datamining + - fixed a bug in covariance where array size and loop bounds did not + match each other, causing out-of-bounds for certain parameters. + (Reported by Tobias Grosser) + - fixed a bug in covariance where division by N at the end was missing. + - fixed the initialization of float_n in covariance and correlation. The value + is supposed to be the input size N casted as floats to be used in division, + but was initialized to 1.2. + - changed the name of array 'symmat' in covariance to 'cov'. + - changed the name of array 'symmat' in correlation to 'corr'. + - changed the loop indices in covariance and correlation to match + the documentation. + - removed sqrt_of_array_cell macro from correlation + += Linear Algebra + - fixed a bug in 2mm where array sizes did not match each other, causing + out-of-bounds for non-square inputs. + (Reported by Tobias Grosser) + - fixed a bug in syrk where the loop bounds were incorrect, having + rectangular loop instead of triangular. + - fixed a bug in trmm where loop bounds were incorrect, using the wrong + triangular region in the multiplication. + - reduced the size of 'sum' array in doitgen from 3D to 1D to avoid obvious + waste in memory. + - removed A from print_arrays in gramschmidt; A is updated, but is + not an output. + - removed dynprog; replaced by nussinov in medley. + - moved BLAS kernels (including updated BLAS) to its own sub-category. + - BLAS kernels are now commented with the parameters in original BLAS + which corresponds to the implementation in PolyBench. + - BLAS kernels now closely matches the original version. However, gemver and + gesummv remains the same as it is not part of current BLAS. + - moved cholesky and trisolv to solvers. + - re-implemented cholesky so that it computes the full L + inplace. Previously the diagonal was stored in a separate vector. + - re-implemented durbin to match Durbin's algorithm from a book. There were + off-by-one errors and an excessive memory allocation (due to expanding + accumulation of a summation). + - re-implemented lu. The original implementation was missing a inner most + loop for computing the U matrix. + - re-implemented ludcmp. There were off-by-one errors leading to + incorrect outputs. + - lu and ludcmp now use same input as cholesky to ensure it works. + - input of cholesky/lu/ludcmp now uses L.L^T instead of L^T.L to + create inputs. + - loop bounds of symm/syrk/syr2k/trmm are changed to match the documentation. + += Medley + - changed default datatype of floyd_warshall from double to int. + - removed reg_detect; replaced by deriche. + - added nussinov; a dynamic programming algorithm for sequence alignment. + (Code by Dave Wonnacott and his students) + - added deriche; edge detector filter. + (Code by Gael Deest) + += Stencils + - re-implemented adi based on a figure in "Automatic Data and + Computation Decomposition on Distributed Memory Parallel + Computers" by Peizong Lee and Zvi Meir Kedem, TOPLAS, 2002 + - removed fdtd-apml; replaced by heat-3d + - added heat-3d; Heat equation over 3D data domain (4D iteration space) + (Original specification from Pochoir compiler test case) + - changed jacobi-1d-imper and jacobi-2d-imper to jacobi-1d and + jacobi-2d, respectively. + - jacobi-1d, jacobi-2d, and heat-3d performs two time steps using + alternating arrays per an iteration of the outermost loop. This is + to avoid the copy loop in the old xxx-imper versions. The number + of stencil iterations are now restricted to be even numbers (2x of + the parameter TSTEPS). + += Known Issues + - output of correlation will always be mostly 1.0 due to how the input + is generated. It is difficult to avoid this case when the input + is some function of the indices, and will be addressed with more + fundamental update in the future. diff --git a/mlir-clang/Test/polybench/LICENSE.txt b/mlir-clang/Test/polybench/LICENSE.txt new file mode 100644 index 000000000000..eebcab62bfbc --- /dev/null +++ b/mlir-clang/Test/polybench/LICENSE.txt @@ -0,0 +1,51 @@ +OHIO STATE UNIVERSITY SOFTWARE DISTRIBUTION LICENSE + +PolyBench/C, a collection of benchmarks containing static control +parts (the “Software”) +Copyright (c) 2010-2016, Ohio State University. All rights reserved. + +The Software is available for download and use subject to the terms +and conditions of this License. Access or use of the Software +constitutes acceptance and agreement to the terms and conditions of +this License. Redistribution and use of the Software in source and +binary forms, with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the capitalized paragraph below. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the capitalized paragraph below in +the documentation and/or other materials provided with the +distribution. + +3. The name of Ohio State University, or its faculty, staff or +students may not be used to endorse or promote products derived from +the Software without specific prior written permission. + +This software was produced with support from the U.S. Defense Advanced +Research Projects Agency (DARPA), the U.S. Department of Energy (DoE) +and the U.S. National Science Foundation. Nothing in this work should +be construed as reflecting the official policy or position of the +Defense Department, the United States government or Ohio State +University. + +THIS SOFTWARE HAS BEEN APPROVED FOR PUBLIC RELEASE, UNLIMITED +DISTRIBUTION. THE SOFTWARE IS PROVIDED “AS IS” AND WITHOUT ANY +EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, WARRANTIES OF ACCURACY, COMPLETENESS, NONINFRINGEMENT, +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +ACCESS OR USE OF THE SOFTWARE IS ENTIRELY AT THE USER’S RISK. IN NO +EVENT SHALL OHIO STATE UNIVERSITY OR ITS FACULTY, STAFF OR STUDENTS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE SOFTWARE USER SHALL +INDEMNIFY, DEFEND AND HOLD HARMLESS OHIO STATE UNIVERSITY AND ITS +FACULTY, STAFF AND STUDENTS FROM ANY AND ALL CLAIMS, ACTIONS, DAMAGES, +LOSSES, LIABILITIES, COSTS AND EXPENSES, INCLUDING ATTORNEYS’ FEES AND +COURT COSTS, DIRECTLY OR INDIRECTLY ARISING OUT OF OR IN CONNECTION +WITH ACCESS OR USE OF THE SOFTWARE. diff --git a/mlir-clang/Test/polybench/README b/mlir-clang/Test/polybench/README new file mode 100644 index 000000000000..8b9955a25d61 --- /dev/null +++ b/mlir-clang/Test/polybench/README @@ -0,0 +1,363 @@ +* * * * * * * * * * * * * * * +* PolyBench/C 4.2.1 (beta) * +* * * * * * * * * * * * * * * + +Copyright (c) 2011-2016 the Ohio State University. + +Contact: + Louis-Noel Pouchet + Tomofumi Yuki + + +PolyBench is a benchmark suite of 30 numerical computations with +static control flow, extracted from operations in various application +domains (linear algebra computations, image processing, physics +simulation, dynamic programming, statistics, etc.). PolyBench features +include: +- A single file, tunable at compile-time, used for the kernel + instrumentation. It performs extra operations such as cache flushing + before the kernel execution, and can set real-time scheduling to + prevent OS interference. +- Non-null data initialization, and live-out data dump. +- Syntactic constructs to prevent any dead code elimination on the kernel. +- Parametric loop bounds in the kernels, for general-purpose implementation. +- Clear kernel marking, using pragma-based delimiters. + + +PolyBench is currently available in C and in Fortran: +- See PolyBench/C 4.2.1 for the C version +- See PolyBench/Fortran 1.0 for the Fortran version (based on PolyBench/C 3.2) + +Available benchmarks (PolyBench/C 4.2.1) + +Benchmark Description +2mm 2 Matrix Multiplications (alpha * A * B * C + beta * D) +3mm 3 Matrix Multiplications ((A*B)*(C*D)) +adi Alternating Direction Implicit solver +atax Matrix Transpose and Vector Multiplication +bicg BiCG Sub Kernel of BiCGStab Linear Solver +cholesky Cholesky Decomposition +correlation Correlation Computation +covariance Covariance Computation +deriche Edge detection filter +doitgen Multi-resolution analysis kernel (MADNESS) +durbin Toeplitz system solver +fdtd-2d 2-D Finite Different Time Domain Kernel +gemm Matrix-multiply C=alpha.A.B+beta.C +gemver Vector Multiplication and Matrix Addition +gesummv Scalar, Vector and Matrix Multiplication +gramschmidt Gram-Schmidt decomposition +head-3d Heat equation over 3D data domain +jacobi-1D 1-D Jacobi stencil computation +jacobi-2D 2-D Jacobi stencil computation +lu LU decomposition +ludcmp LU decomposition followed by Forward Substitution +mvt Matrix Vector Product and Transpose +nussinov Dynamic programming algorithm for sequence alignment +seidel 2-D Seidel stencil computation +symm Symmetric matrix-multiply +syr2k Symmetric rank-2k update +syrk Symmetric rank-k update +trisolv Triangular solver +trmm Triangular matrix-multiply + + +See the end of the README for mailing lists, instructions to use +PolyBench, etc. + +-------------------- +* New in 4.2.1-beta: +-------------------- + - Fix a bug in PAPI support, introduced in 4.2 + - Support PAPI 5.4.x + +------------- +* New in 4.2: +------------- + - Fixed a bug in syr2k. + - Changed the data initialization function of several benchmarks. + - Minor updates in the documentation and PolyBench API. + +------------- +* New in 4.1: +------------- + - Added LICENSE.txt + - Fixed minor issues with cholesky both in documentation and implementation. + (Reported by François Gindraud) + - Simplified the macros for switching between data types. Now users + may specify DATA_TYPE_IS_XXX where XXX is one of FLOAT/DOUBLE/INT + to change all macros associated with data types. + +------------- +* New in 4.0a: +------------- + - Fixed a bug in jacobi-1d (Reported by Sven Verdoolaege) + +------------- +* New in 4.0: +------------- + +This update includes many changes. Please see CHANGELOG for detailed +list of changes. Most of the benchmarks have been edited/modified by +Tomofumi Yuki, thanks to the feedback we have received by PolyBench +users for the past few years. + +- Three benchmarks are out: dynprog, reg-detect, fdtd-apml. +- Three benchmarks are in: nussinov, deriche, heat-3d. +- Jacobi-1D and Jacobi-2D perform two time steps in one time loop + iteration alternating the source and target fields, to avoid the + field copy statement. +- Almost all benchmarks have been edited to ensure the computation + result matches the mathematical specification of the operation. +- A major effort on documentation and harmonization of problem sizes + and data allocations schemes. + +* Important Note: +----------------- + +PolyBench/C 3.2 kernels had numerous implementation errors making +their outputs to not match what is expected from the mathematical +specification of the operation. Many of them did not influence the +program behavior (e.g., the number and type of operations, data +dependences, and overall control-flow was similar to the corrected +implementation), however, some had non-negligible impact. These are +described below. + + - adi: There was an off-by-one error, which made back substitution + part of a pass in ADI to not depend on the forward pass, making the + program fully tilable. +- syrk: A typo on the loop bounds made the iteration space rectangular + instead of triangular. This has led to additional dependences and + two times more operations than intended. +- trmm: A typo on the loop bounds led to the wrong half of the matrix + being used in the computation. This led to additional dependences, + making it harder to parallelize this kernel. +- lu: An innermost loop was missing for the operation to be valid on + general matrices. This cause the kernel to perform about half the + work compared to a general implementation of LU decomposition. The + new implementation is the generic LU decomposition. + +In addition, some of the kernels used "high-footprint" memory allocation for +easier parallelization, where variables used in accumulation were fully +expanded. These variables were changed to only use a scalar. + + +------------- +* New in 3.2: +------------- + +- Rename the package to PolyBench/C, to prepare for the upcoming + PolyBench/Fortran and PolyBench/GPU. +- Fixed a typo in polybench.h, causing compilation problems for 5D arrays. +- Fixed minor typos in correlation, atax, cholesky, fdtd-2d. +- Added an option to build the test suite with constant loop bounds + (default is parametric loop bounds) + +------------- +* New in 3.1: +------------- + +- Fixed a typo in polybench.h, causing compilation problems for 3D arrays. +- Set by default heap arrays, stack arrays are now optional. + +------------- +* New in 3.0: +------------- + +- Multiple dataset sizes are predefined. Each file comes now with a .h + header file defining the dataset. +- Support of heap-allocated arrays. It uses a single malloc for the + entire array region, the data allocated is cast into a C99 + multidimensional array. +- One benchmark is out: gauss_filter +- One benchmark is in: floyd-warshall +- PAPI support has been greatly improved; it also can report the + counters on a specific core to be set by the user. + + + +---------------- +* Mailing lists: +---------------- + +** polybench-announces@lists.sourceforge.net: +--------------------------------------------- + +Announces about releases of PolyBench. + +** polybench-discussion@lists.sourceforge.net: +---------------------------------------------- + +General discussions reg. PolyBench. + + + +----------------------- +* Available benchmarks: +----------------------- + +See utilities/benchmark_list for paths to each files. +See doc/polybench.pdf for detailed description of the algorithms. + + + +------------------------------ +* Sample compilation commands: +------------------------------ + +** To compile a benchmark without any monitoring: +------------------------------------------------- + +$> gcc -I utilities -I linear-algebra/kernels/atax utilities/polybench.c linear-algebra/kernels/atax/atax.c -o atax_base + + +** To compile a benchmark with execution time reporting: +-------------------------------------------------------- + +$> gcc -O3 -I utilities -I linear-algebra/kernels/atax utilities/polybench.c linear-algebra/kernels/atax/atax.c -DPOLYBENCH_TIME -o atax_time + + +** To generate the reference output of a benchmark: +--------------------------------------------------- + +$> gcc -O0 -I utilities -I linear-algebra/kernels/atax utilities/polybench.c linear-algebra/kernels/atax/atax.c -DPOLYBENCH_DUMP_ARRAYS -o atax_ref +$> ./atax_ref 2>atax_ref.out + + + +------------------------- +* Some available options: +------------------------- + +They are all passed as macro definitions during compilation time (e.g, +-Dname_of_the_option). + +** Typical options: +------------------- + +- POLYBENCH_TIME: output execution time (gettimeofday) [default: off] + +- MINI_DATASET, SMALL_DATASET, MEDIUM_DATASET, LARGE_DATASET, + EXTRALARGE_DATASET: set the dataset size to be used + [default: STANDARD_DATASET] + +- POLYBENCH_DUMP_ARRAYS: dump all live-out arrays on stderr [default: off] + +- POLYBENCH_STACK_ARRAYS: use stack allocation instead of malloc [default: off] + + +** Options that may lead to better performance: +----------------------------------------------- + +- POLYBENCH_USE_RESTRICT: Use restrict keyword to allow compilers to + assume absence of aliasing. [default: off] + +- POLYBENCH_USE_SCALAR_LB: Use scalar loop bounds instead of parametric ones. + [default: off] + +- POLYBENCH_PADDING_FACTOR: Pad all dimensions of all arrays by this + value [default: 0] + +- POLYBENCH_INTER_ARRAY_PADDING_FACTOR: Offset the starting address of + polybench arrays allocated on the heap (default) by a multiple of + this value [default: 0] + +- POLYBENCH_USE_C99_PROTO: Use standard C99 prototype for the functions. + [default: off] + + +** Timing/profiling options: +---------------------------- + +- POLYBENCH_PAPI: turn on papi timing (see below). + +- POLYBENCH_CACHE_SIZE_KB: cache size to flush, in kB [default: 33MB] + +- POLYBENCH_NO_FLUSH_CACHE: don't flush the cache before calling the + timer [default: flush the cache] + +- POLYBENCH_CYCLE_ACCURATE_TIMER: Use Time Stamp Counter to monitor + the execution time of the kernel [default: off] + +- POLYBENCH_LINUX_FIFO_SCHEDULER: use FIFO real-time scheduler for the + kernel execution, the program must be run as root, under linux only, + and compiled with -lc [default: off] + + + +--------------- +* PAPI support: +--------------- + +** To compile a benchmark with PAPI support: +-------------------------------------------- + +$> gcc -O3 -I utilities -I linear-algebra/kernels/atax utilities/polybench.c linear-algebra/kernels/atax/atax.c -DPOLYBENCH_PAPI -lpapi -o atax_papi + + +** To specify which counter(s) to monitor: +------------------------------------------ + +Edit utilities/papi_counters.list, and add 1 line per event to +monitor. Each line (including the last one) must finish with a ',' and +both native and standard events are supported. + +The whole kernel is run one time per counter (no multiplexing) and +there is no sampling being used for the counter value. + + + +------------------------------ +* Accurate performance timing: +------------------------------ + +With kernels that have an execution time in the orders of a few tens +of milliseconds, it is critical to validate any performance number by +repeating several times the experiment. A companion script is +available to perform reasonable performance measurement of a PolyBench. + +$> gcc -O3 -I utilities -I linear-algebra/kernels/atax utilities/polybench.c linear-algebra/kernels/atax/atax.c -DPOLYBENCH_TIME -o atax_time +$> ./utilities/time_benchmark.sh ./atax_time + +This script will run five times the benchmark (that must be a +PolyBench compiled with -DPOLYBENCH_TIME), eliminate the two extremal +times, and check that the deviation of the three remaining does not +exceed a given threshold, set to 5%. + +It is also possible to use POLYBENCH_CYCLE_ACCURATE_TIMER to use the +Time Stamp Counter instead of gettimeofday() to monitor the number of +elapsed cycles. + + + +---------------------------------------- +* Generating macro-free benchmark suite: +---------------------------------------- + +(from the root of the archive:) +$> PARGS="-I utilities -DPOLYBENCH_TIME"; +$> for i in `cat utilities/benchmark_list`; do perl utilities/create_cpped_version.pl $i "$PARGS"; done + +This create for each benchmark file 'xxx.c' a new file +'xxx.preproc.c'. The PARGS variable in the above example can be set to +the desired configuration, for instance to create a full C99 version +(parametric arrays): + +$> PARGS="-I utilities -DPOLYBENCH_USE_C99_PROTO"; +$> for i in `cat utilities/benchmark_list`; do perl utilities/create_cpped_version.pl $i "$PARGS"; done + + + +------------------ +* Utility scripts: +------------------ +create_cpped_version.pl: Used in the above for generating macro free version. + +makefile-gen.pl: generates make files in each directory. Options are globally + configurable through config.mk at polybench root. + header-gen.pl: refers to 'polybench.spec' file and generates header in + each directory. Allows default problem sizes and datatype to + be configured without going into each header file. + + run-all.pl: compiles and runs each kernel. + clean.pl: runs make clean in each directory and then removes Makefile. diff --git a/mlir-clang/Test/polybench/THANKS b/mlir-clang/Test/polybench/THANKS new file mode 100644 index 000000000000..2a67980117c7 --- /dev/null +++ b/mlir-clang/Test/polybench/THANKS @@ -0,0 +1,20 @@ +We would like to thank the following people for giving reporting bugs and +giving feedback to PolyBench: + * François Gindraud + * Tobias Grosser + * Guillaume Iooss + * Peng Li + * Sanjay Rajopadhye + * Sven Verdoolaege + * Willy Wolff + +and the following for providing reference C implementations: + * Dave Wonnacott, Haverford College + The nussinov implementation was provided by Dave with the help of + - Allison Lake + - Ting Zhou + - Tian Jin + based on algorithm by Nussinov, described in Allison Lake's senior thesis. + + * Gael Deest, University of Rennes 1 + The Deriche filter implementation was provided by Gael. diff --git a/mlir-clang/Test/polybench/datamining/correlation/correlation.c b/mlir-clang/Test/polybench/datamining/correlation/correlation.c new file mode 100644 index 000000000000..aed8df655ed4 --- /dev/null +++ b/mlir-clang/Test/polybench/datamining/correlation/correlation.c @@ -0,0 +1,413 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 -lm && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 -lm && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 -lm && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* correlation.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "correlation.h" + + +/* Array initialization. */ +static +void init_array (int m, + int n, + DATA_TYPE *float_n, + DATA_TYPE POLYBENCH_2D(data,N,M,n,m)) +{ + int i, j; + + *float_n = (DATA_TYPE)N; + + for (i = 0; i < N; i++) + for (j = 0; j < M; j++) + data[i][j] = (DATA_TYPE)(i*j)/M + i; + +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int m, + DATA_TYPE POLYBENCH_2D(corr,M,M,m,m)) + +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("corr"); + for (i = 0; i < m; i++) + for (j = 0; j < m; j++) { + if ((i * m + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, corr[i][j]); + } + POLYBENCH_DUMP_END("corr"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_correlation(int m, int n, + DATA_TYPE float_n, + DATA_TYPE POLYBENCH_2D(data,N,M,n,m), + DATA_TYPE POLYBENCH_2D(corr,M,M,m,m), + DATA_TYPE POLYBENCH_1D(mean,M,m), + DATA_TYPE POLYBENCH_1D(stddev,M,m)) +{ + int i, j, k; + + DATA_TYPE eps = SCALAR_VAL(0.1); + + +#pragma scop + for (j = 0; j < _PB_M; j++) + { + mean[j] = SCALAR_VAL(0.0); + for (i = 0; i < _PB_N; i++) + mean[j] += data[i][j]; + mean[j] /= float_n; + } + + + for (j = 0; j < _PB_M; j++) + { + stddev[j] = SCALAR_VAL(0.0); + for (i = 0; i < _PB_N; i++) + stddev[j] += (data[i][j] - mean[j]) * (data[i][j] - mean[j]); + stddev[j] /= float_n; + stddev[j] = SQRT_FUN(stddev[j]); + /* The following in an inelegant but usual way to handle + near-zero std. dev. values, which below would cause a zero- + divide. */ + stddev[j] = stddev[j] <= eps ? SCALAR_VAL(1.0) : stddev[j]; + } + + /* Center and reduce the column vectors. */ + for (i = 0; i < _PB_N; i++) + for (j = 0; j < _PB_M; j++) + { + data[i][j] -= mean[j]; + data[i][j] /= SQRT_FUN(float_n) * stddev[j]; + } + + /* Calculate the m * m correlation matrix. */ + for (i = 0; i < _PB_M-1; i++) + { + corr[i][i] = SCALAR_VAL(1.0); + for (j = i+1; j < _PB_M; j++) + { + corr[i][j] = SCALAR_VAL(0.0); + for (k = 0; k < _PB_N; k++) + corr[i][j] += (data[k][i] * data[k][j]); + corr[j][i] = corr[i][j]; + } + } + corr[_PB_M-1][_PB_M-1] = SCALAR_VAL(1.0); +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int m = M; + + /* Variable declaration/allocation. */ + DATA_TYPE float_n; + POLYBENCH_2D_ARRAY_DECL(data,DATA_TYPE,N,M,n,m); + POLYBENCH_2D_ARRAY_DECL(corr,DATA_TYPE,M,M,m,m); + POLYBENCH_1D_ARRAY_DECL(mean,DATA_TYPE,M,m); + POLYBENCH_1D_ARRAY_DECL(stddev,DATA_TYPE,M,m); + + /* Initialize array(s). */ + init_array (m, n, &float_n, POLYBENCH_ARRAY(data)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_correlation (m, n, float_n, + POLYBENCH_ARRAY(data), + POLYBENCH_ARRAY(corr), + POLYBENCH_ARRAY(mean), + POLYBENCH_ARRAY(stddev)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(m, POLYBENCH_ARRAY(corr))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(data); + POLYBENCH_FREE_ARRAY(corr); + POLYBENCH_FREE_ARRAY(mean); + POLYBENCH_FREE_ARRAY(stddev); + + return 0; +} + +// CHECK: #map0 = affine_map<()[s0] -> (s0 - 1)> +// CHECK-NEXT: #map1 = affine_map<(d0) -> (d0 + 1)> +// CHECK-NEXT: module +// CHECK-NEXT: llvm.mlir.global internal constant @str7("==END DUMP_ARRAYS==\0A\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str6("\0Aend dump: %s\0A\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str5("%0.2lf \00") +// CHECK-NEXT: llvm.mlir.global internal constant @str4("\0A\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str3("corr\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str2("begin dump: %s\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str1("==BEGIN DUMP_ARRAYS==\0A\00") +// CHECK-NEXT: llvm.mlir.global external @stderr() : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>> +// CHECK-NEXT: llvm.func @fprintf(!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr, ...) -> !llvm.i32 +// CHECK-NEXT: llvm.mlir.global internal constant @str0("\00") +// CHECK-NEXT: llvm.func @strcmp(!llvm.ptr, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: func @main(%arg0: i32, %arg1: !llvm.ptr>) -> i32 { +// CHECK-NEXT: %c0 = constant 0 : index +// CHECK-NEXT: %c1400_i32 = constant 1400 : i32 +// CHECK-NEXT: %c1200_i32 = constant 1200 : i32 +// CHECK-NEXT: %c42_i32 = constant 42 : i32 +// CHECK-NEXT: %true = constant true +// CHECK-NEXT: %false = constant false +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %0 = alloca() : memref<1xf64> +// CHECK-NEXT: %1 = alloc() : memref<1400x1200xf64> +// CHECK-NEXT: %2 = alloc() : memref<1200x1200xf64> +// CHECK-NEXT: %3 = alloc() : memref<1200xf64> +// CHECK-NEXT: %4 = alloc() : memref<1200xf64> +// CHECK-NEXT: %5 = memref_cast %0 : memref<1xf64> to memref +// CHECK-NEXT: call @init_array(%c1200_i32, %c1400_i32, %5, %1) : (i32, i32, memref, memref<1400x1200xf64>) -> () +// CHECK-NEXT: %6 = load %0[%c0] : memref<1xf64> +// CHECK-NEXT: call @kernel_correlation(%c1200_i32, %c1400_i32, %6, %1, %2, %3, %4) : (i32, i32, f64, memref<1400x1200xf64>, memref<1200x1200xf64>, memref<1200xf64>, memref<1200xf64>) -> () +// CHECK-NEXT: %7 = cmpi "sgt", %arg0, %c42_i32 : i32 +// CHECK-NEXT: %8 = scf.if %7 -> (i1) { +// CHECK-NEXT: %9 = llvm.load %arg1 : !llvm.ptr> +// CHECK-NEXT: %10 = llvm.mlir.addressof @str0 : !llvm.ptr> +// CHECK-NEXT: %11 = llvm.mlir.constant(0 : index) : !llvm.i64 +// CHECK-NEXT: %12 = llvm.getelementptr %10[%11, %11] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %13 = llvm.call @strcmp(%9, %12) : (!llvm.ptr, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: %14 = llvm.mlir.cast %13 : !llvm.i32 to i32 +// CHECK-NEXT: %15 = trunci %14 : i32 to i1 +// CHECK-NEXT: %16 = xor %15, %true : i1 +// CHECK-NEXT: scf.yield %16 : i1 +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %false : i1 +// CHECK-NEXT: } +// CHECK-NEXT: scf.if %8 { +// CHECK-NEXT: call @print_array(%c1200_i32, %2) : (i32, memref<1200x1200xf64>) -> () +// CHECK-NEXT: } +// CHECK-NEXT: return %c0_i32 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: func private @init_array(%arg0: i32, %arg1: i32, %arg2: memref, %arg3: memref<1400x1200xf64>) { +// CHECK-NEXT: %c0 = constant 0 : index +// CHECK-NEXT: %c1400_i32 = constant 1400 : i32 +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %c1200_i32 = constant 1200 : i32 +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %0 = sitofp %c1400_i32 : i32 to f64 +// CHECK-NEXT: store %0, %arg2[%c0] : memref +// CHECK-NEXT: br ^bb1(%c0_i32 : i32) +// CHECK-NEXT: ^bb1(%1: i32): // 2 preds: ^bb0, ^bb5 +// CHECK-NEXT: %2 = cmpi "slt", %1, %c1400_i32 : i32 +// CHECK-NEXT: %3 = index_cast %1 : i32 to index +// CHECK-NEXT: cond_br %2, ^bb3(%c0_i32 : i32), ^bb2 +// CHECK-NEXT: ^bb2: // pred: ^bb1 +// CHECK-NEXT: return +// CHECK-NEXT: ^bb3(%4: i32): // 2 preds: ^bb1, ^bb4 +// CHECK-NEXT: %5 = cmpi "slt", %4, %c1200_i32 : i32 +// CHECK-NEXT: %6 = index_cast %4 : i32 to index +// CHECK-NEXT: cond_br %5, ^bb4, ^bb5 +// CHECK-NEXT: ^bb4: // pred: ^bb3 +// CHECK-NEXT: %7 = muli %1, %4 : i32 +// CHECK-NEXT: %8 = sitofp %7 : i32 to f64 +// CHECK-NEXT: %9 = sitofp %c1200_i32 : i32 to f64 +// CHECK-NEXT: %10 = divf %8, %9 : f64 +// CHECK-NEXT: %11 = sitofp %1 : i32 to f64 +// CHECK-NEXT: %12 = addf %10, %11 : f64 +// CHECK-NEXT: store %12, %arg3[%3, %6] : memref<1400x1200xf64> +// CHECK-NEXT: %13 = addi %4, %c1_i32 : i32 +// CHECK-NEXT: br ^bb3(%13 : i32) +// CHECK-NEXT: ^bb5: // pred: ^bb3 +// CHECK-NEXT: %14 = addi %1, %c1_i32 : i32 +// CHECK-NEXT: br ^bb1(%14 : i32) +// CHECK-NEXT: } +// CHECK-NEXT: func private @kernel_correlation(%arg0: i32, %arg1: i32, %arg2: f64, %arg3: memref<1400x1200xf64>, %arg4: memref<1200x1200xf64>, %arg5: memref<1200xf64>, %arg6: memref<1200xf64>) { +// CHECK-NEXT: %cst = constant 1.000000e-01 : f64 +// CHECK-NEXT: %c1 = constant 1 : index +// CHECK-NEXT: %cst_0 = constant 0.000000e+00 : f64 +// CHECK-NEXT: %cst_1 = constant 1.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: affine.store %cst_0, %arg5[%arg7] : memref<1200xf64> +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: %5 = affine.load %arg3[%arg8, %arg7] : memref<1400x1200xf64> +// CHECK-NEXT: %6 = affine.load %arg5[%arg7] : memref<1200xf64> +// CHECK-NEXT: %7 = addf %6, %5 : f64 +// CHECK-NEXT: affine.store %7, %arg5[%arg7] : memref<1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %3 = affine.load %arg5[%arg7] : memref<1200xf64> +// CHECK-NEXT: %4 = divf %3, %arg2 : f64 +// CHECK-NEXT: affine.store %4, %arg5[%arg7] : memref<1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: affine.store %cst_0, %arg6[%arg7] : memref<1200xf64> +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: %8 = affine.load %arg3[%arg8, %arg7] : memref<1400x1200xf64> +// CHECK-NEXT: %9 = affine.load %arg5[%arg7] : memref<1200xf64> +// CHECK-NEXT: %10 = subf %8, %9 : f64 +// CHECK-NEXT: %11 = mulf %10, %10 : f64 +// CHECK-NEXT: %12 = affine.load %arg6[%arg7] : memref<1200xf64> +// CHECK-NEXT: %13 = addf %12, %11 : f64 +// CHECK-NEXT: affine.store %13, %arg6[%arg7] : memref<1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %3 = affine.load %arg6[%arg7] : memref<1200xf64> +// CHECK-NEXT: %4 = divf %3, %arg2 : f64 +// CHECK-NEXT: affine.store %4, %arg6[%arg7] : memref<1200xf64> +// CHECK-NEXT: %5 = sqrt %4 : f64 +// CHECK-NEXT: affine.store %5, %arg6[%arg7] : memref<1200xf64> +// CHECK-NEXT: %6 = cmpf "ule", %5, %cst : f64 +// CHECK-NEXT: %7 = scf.if %6 -> (f64) { +// CHECK-NEXT: scf.yield %cst_1 : f64 +// CHECK-NEXT: } else { +// CHECK-NEXT: %8 = affine.load %arg6[%arg7] : memref<1200xf64> +// CHECK-NEXT: scf.yield %8 : f64 +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %7, %arg6[%arg7] : memref<1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: affine.for %arg8 = 0 to %1 { +// CHECK-NEXT: %3 = affine.load %arg5[%arg8] : memref<1200xf64> +// CHECK-NEXT: %4 = affine.load %arg3[%arg7, %arg8] : memref<1400x1200xf64> +// CHECK-NEXT: %5 = subf %4, %3 : f64 +// CHECK-NEXT: affine.store %5, %arg3[%arg7, %arg8] : memref<1400x1200xf64> +// CHECK-NEXT: %6 = sqrt %arg2 : f64 +// CHECK-NEXT: %7 = affine.load %arg6[%arg8] : memref<1200xf64> +// CHECK-NEXT: %8 = mulf %6, %7 : f64 +// CHECK-NEXT: %9 = divf %5, %8 : f64 +// CHECK-NEXT: affine.store %9, %arg3[%arg7, %arg8] : memref<1400x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: %2 = subi %1, %c1 : index +// CHECK-NEXT: affine.for %arg7 = 0 to #map0()[%1] { +// CHECK-NEXT: affine.store %cst_1, %arg4[%arg7, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: affine.for %arg8 = #map1(%arg7) to %1 { +// CHECK-NEXT: affine.store %cst_0, %arg4[%arg7, %arg8] : memref<1200x1200xf64> +// CHECK-NEXT: affine.for %arg9 = 0 to %0 { +// CHECK-NEXT: %4 = affine.load %arg3[%arg9, %arg7] : memref<1400x1200xf64> +// CHECK-NEXT: %5 = affine.load %arg3[%arg9, %arg8] : memref<1400x1200xf64> +// CHECK-NEXT: %6 = mulf %4, %5 : f64 +// CHECK-NEXT: %7 = affine.load %arg4[%arg7, %arg8] : memref<1200x1200xf64> +// CHECK-NEXT: %8 = addf %7, %6 : f64 +// CHECK-NEXT: affine.store %8, %arg4[%arg7, %arg8] : memref<1200x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %3 = affine.load %arg4[%arg7, %arg8] : memref<1200x1200xf64> +// CHECK-NEXT: affine.store %3, %arg4[%arg8, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: store %cst_1, %arg4[%2, %2] : memref<1200x1200xf64> +// CHECK-NEXT: return +// CHECK-NEXT: } +// CHECK-NEXT: func private @print_array(%arg0: i32, %arg1: memref<1200x1200xf64>) { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %c20_i32 = constant 20 : i32 +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %0 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %1 = llvm.load %0 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %2 = llvm.mlir.addressof @str1 : !llvm.ptr> +// CHECK-NEXT: %3 = llvm.mlir.constant(0 : index) : !llvm.i64 +// CHECK-NEXT: %4 = llvm.getelementptr %2[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %5 = llvm.call @fprintf(%1, %4) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: %6 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %7 = llvm.load %6 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %8 = llvm.mlir.addressof @str2 : !llvm.ptr> +// CHECK-NEXT: %9 = llvm.getelementptr %8[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %10 = llvm.mlir.addressof @str3 : !llvm.ptr> +// CHECK-NEXT: %11 = llvm.getelementptr %10[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %12 = llvm.call @fprintf(%7, %9, %11) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: br ^bb1(%c0_i32 : i32) +// CHECK-NEXT: ^bb1(%13: i32): // 2 preds: ^bb0, ^bb5 +// CHECK-NEXT: %14 = cmpi "slt", %13, %arg0 : i32 +// CHECK-NEXT: %15 = index_cast %13 : i32 to index +// CHECK-NEXT: cond_br %14, ^bb3(%c0_i32 : i32), ^bb2 +// CHECK-NEXT: ^bb2: // pred: ^bb1 +// CHECK-NEXT: %16 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %17 = llvm.load %16 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %18 = llvm.mlir.addressof @str6 : !llvm.ptr> +// CHECK-NEXT: %19 = llvm.getelementptr %18[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %20 = llvm.mlir.addressof @str3 : !llvm.ptr> +// CHECK-NEXT: %21 = llvm.getelementptr %20[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %22 = llvm.call @fprintf(%17, %19, %21) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: %23 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %24 = llvm.load %23 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %25 = llvm.mlir.addressof @str7 : !llvm.ptr> +// CHECK-NEXT: %26 = llvm.getelementptr %25[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %27 = llvm.call @fprintf(%24, %26) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: return +// CHECK-NEXT: ^bb3(%28: i32): // 2 preds: ^bb1, ^bb4 +// CHECK-NEXT: %29 = cmpi "slt", %28, %arg0 : i32 +// CHECK-NEXT: %30 = index_cast %28 : i32 to index +// CHECK-NEXT: cond_br %29, ^bb4, ^bb5 +// CHECK-NEXT: ^bb4: // pred: ^bb3 +// CHECK-NEXT: %31 = muli %13, %arg0 : i32 +// CHECK-NEXT: %32 = addi %31, %28 : i32 +// CHECK-NEXT: %33 = remi_signed %32, %c20_i32 : i32 +// CHECK-NEXT: %34 = cmpi "eq", %33, %c0_i32 : i32 +// CHECK-NEXT: scf.if %34 { +// CHECK-NEXT: %44 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %45 = llvm.load %44 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %46 = llvm.mlir.addressof @str4 : !llvm.ptr> +// CHECK-NEXT: %47 = llvm.getelementptr %46[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %48 = llvm.call @fprintf(%45, %47) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: } +// CHECK-NEXT: %35 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %36 = llvm.load %35 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %37 = llvm.mlir.addressof @str5 : !llvm.ptr> +// CHECK-NEXT: %38 = llvm.getelementptr %37[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %39 = load %arg1[%15, %30] : memref<1200x1200xf64> +// CHECK-NEXT: %40 = llvm.mlir.cast %39 : f64 to !llvm.double +// CHECK-NEXT: %41 = llvm.call @fprintf(%36, %38, %40) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr>, ptr>, ptr>, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr, !llvm.double) -> !llvm.i32 +// CHECK-NEXT: %42 = addi %28, %c1_i32 : i32 +// CHECK-NEXT: br ^bb3(%42 : i32) +// CHECK-NEXT: ^bb5: // pred: ^bb3 +// CHECK-NEXT: %43 = addi %13, %c1_i32 : i32 +// CHECK-NEXT: br ^bb1(%43 : i32) +// CHECK-NEXT: } +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/datamining/correlation/correlation.h b/mlir-clang/Test/polybench/datamining/correlation/correlation.h new file mode 100644 index 000000000000..f2660e2907b0 --- /dev/null +++ b/mlir-clang/Test/polybench/datamining/correlation/correlation.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _CORRELATION_H +# define _CORRELATION_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(M) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define M 28 +# define N 32 +# endif + +# ifdef SMALL_DATASET +# define M 80 +# define N 100 +# endif + +# ifdef MEDIUM_DATASET +# define M 240 +# define N 260 +# endif + +# ifdef LARGE_DATASET +# define M 1200 +# define N 1400 +# endif + +# ifdef EXTRALARGE_DATASET +# define M 2600 +# define N 3000 +# endif + + +#endif /* !(M N) */ + +# define _PB_M POLYBENCH_LOOP_BOUND(M,m) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_CORRELATION_H */ diff --git a/mlir-clang/Test/polybench/datamining/covariance/covariance.c b/mlir-clang/Test/polybench/datamining/covariance/covariance.c new file mode 100644 index 000000000000..4c61f229f95b --- /dev/null +++ b/mlir-clang/Test/polybench/datamining/covariance/covariance.c @@ -0,0 +1,204 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* covariance.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "covariance.h" + + +/* Array initialization. */ +static +void init_array (int m, int n, + DATA_TYPE *float_n, + DATA_TYPE POLYBENCH_2D(data,N,M,n,m)) +{ + int i, j; + + *float_n = (DATA_TYPE)n; + + for (i = 0; i < N; i++) + for (j = 0; j < M; j++) + data[i][j] = ((DATA_TYPE) i*j) / M; +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int m, + DATA_TYPE POLYBENCH_2D(cov,M,M,m,m)) + +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("cov"); + for (i = 0; i < m; i++) + for (j = 0; j < m; j++) { + if ((i * m + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, cov[i][j]); + } + POLYBENCH_DUMP_END("cov"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_covariance(int m, int n, + DATA_TYPE float_n, + DATA_TYPE POLYBENCH_2D(data,N,M,n,m), + DATA_TYPE POLYBENCH_2D(cov,M,M,m,m), + DATA_TYPE POLYBENCH_1D(mean,M,m)) +{ + int i, j, k; + +#pragma scop + for (j = 0; j < _PB_M; j++) + { + mean[j] = SCALAR_VAL(0.0); + for (i = 0; i < _PB_N; i++) + mean[j] += data[i][j]; + mean[j] /= float_n; + } + + for (i = 0; i < _PB_N; i++) + for (j = 0; j < _PB_M; j++) + data[i][j] -= mean[j]; + + for (i = 0; i < _PB_M; i++) + for (j = i; j < _PB_M; j++) + { + cov[i][j] = SCALAR_VAL(0.0); + for (k = 0; k < _PB_N; k++) + cov[i][j] += data[k][i] * data[k][j]; + cov[i][j] /= (float_n - SCALAR_VAL(1.0)); + cov[j][i] = cov[i][j]; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int m = M; + + /* Variable declaration/allocation. */ + DATA_TYPE float_n; + POLYBENCH_2D_ARRAY_DECL(data,DATA_TYPE,N,M,n,m); + POLYBENCH_2D_ARRAY_DECL(cov,DATA_TYPE,M,M,m,m); + POLYBENCH_1D_ARRAY_DECL(mean,DATA_TYPE,M,m); + + + /* Initialize array(s). */ + init_array (m, n, &float_n, POLYBENCH_ARRAY(data)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_covariance (m, n, float_n, + POLYBENCH_ARRAY(data), + POLYBENCH_ARRAY(cov), + POLYBENCH_ARRAY(mean)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(m, POLYBENCH_ARRAY(cov))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(data); + POLYBENCH_FREE_ARRAY(cov); + POLYBENCH_FREE_ARRAY(mean); + + return 0; +} + +// CHECK: #map = affine_map<(d0) -> (d0)> +// CHECK: func @kernel_covariance(%arg0: i32, %arg1: i32, %arg2: f64, %arg3: memref<1400x1200xf64>, %arg4: memref<1200x1200xf64>, %arg5: memref<1200xf64>) { +// CHECK-NEXT: %cst = constant 0.000000e+00 : f64 +// CHECK-NEXT: %cst_0 = constant 1.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: affine.for %arg6 = 0 to %0 { +// CHECK-NEXT: affine.store %cst, %arg5[%arg6] : memref<1200xf64> +// CHECK-NEXT: %3 = affine.load %arg5[%arg6] : memref<1200xf64> +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: %6 = affine.load %arg3[%arg7, %arg6] : memref<1400x1200xf64> +// CHECK-NEXT: %7 = addf %3, %6 : f64 +// CHECK-NEXT: affine.store %7, %arg5[%arg6] : memref<1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %4 = affine.load %arg5[%arg6] : memref<1200xf64> +// CHECK-NEXT: %5 = divf %4, %arg2 : f64 +// CHECK-NEXT: affine.store %5, %arg5[%arg6] : memref<1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg6 = 0 to %1 { +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: %3 = affine.load %arg5[%arg7] : memref<1200xf64> +// CHECK-NEXT: %4 = affine.load %arg3[%arg6, %arg7] : memref<1400x1200xf64> +// CHECK-NEXT: %5 = subf %4, %3 : f64 +// CHECK-NEXT: affine.store %5, %arg3[%arg6, %arg7] : memref<1400x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: %2 = subf %arg2, %cst_0 : f64 +// CHECK-NEXT: affine.for %arg6 = 0 to %0 { +// CHECK-NEXT: affine.for %arg7 = #map(%arg6) to %0 { +// CHECK-NEXT: affine.store %cst, %arg4[%arg6, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: %3 = affine.load %arg4[%arg6, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: affine.for %arg8 = 0 to %1 { +// CHECK-NEXT: %7 = affine.load %arg3[%arg8, %arg6] : memref<1400x1200xf64> +// CHECK-NEXT: %8 = affine.load %arg3[%arg8, %arg7] : memref<1400x1200xf64> +// CHECK-NEXT: %9 = mulf %7, %8 : f64 +// CHECK-NEXT: %10 = addf %3, %9 : f64 +// CHECK-NEXT: affine.store %10, %arg4[%arg6, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %4 = affine.load %arg4[%arg6, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: %5 = divf %4, %2 : f64 +// CHECK-NEXT: affine.store %5, %arg4[%arg6, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: %6 = affine.load %arg4[%arg6, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: affine.store %6, %arg4[%arg7, %arg6] : memref<1200x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT:} + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/datamining/covariance/covariance.h b/mlir-clang/Test/polybench/datamining/covariance/covariance.h new file mode 100644 index 000000000000..e35f8958f34c --- /dev/null +++ b/mlir-clang/Test/polybench/datamining/covariance/covariance.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _COVARIANCE_H +# define _COVARIANCE_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(M) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define M 28 +# define N 32 +# endif + +# ifdef SMALL_DATASET +# define M 80 +# define N 100 +# endif + +# ifdef MEDIUM_DATASET +# define M 240 +# define N 260 +# endif + +# ifdef LARGE_DATASET +# define M 1200 +# define N 1400 +# endif + +# ifdef EXTRALARGE_DATASET +# define M 2600 +# define N 3000 +# endif + + +#endif /* !(M N) */ + +# define _PB_M POLYBENCH_LOOP_BOUND(M,m) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_COVARIANCE_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/gemm/gemm.c b/mlir-clang/Test/polybench/linear-algebra/blas/gemm/gemm.c new file mode 100644 index 000000000000..983384df1e0d --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/gemm/gemm.c @@ -0,0 +1,189 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* gemm.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "gemm.h" + + +/* Array initialization. */ +static +void init_array(int ni, int nj, int nk, + DATA_TYPE *alpha, + DATA_TYPE *beta, + DATA_TYPE POLYBENCH_2D(C,NI,NJ,ni,nj), + DATA_TYPE POLYBENCH_2D(A,NI,NK,ni,nk), + DATA_TYPE POLYBENCH_2D(B,NK,NJ,nk,nj)) +{ + int i, j; + + *alpha = 1.5; + *beta = 1.2; + for (i = 0; i < ni; i++) + for (j = 0; j < nj; j++) + C[i][j] = (DATA_TYPE) ((i*j+1) % ni) / ni; + for (i = 0; i < ni; i++) + for (j = 0; j < nk; j++) + A[i][j] = (DATA_TYPE) (i*(j+1) % nk) / nk; + for (i = 0; i < nk; i++) + for (j = 0; j < nj; j++) + B[i][j] = (DATA_TYPE) (i*(j+2) % nj) / nj; +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int ni, int nj, + DATA_TYPE POLYBENCH_2D(C,NI,NJ,ni,nj)) +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("C"); + for (i = 0; i < ni; i++) + for (j = 0; j < nj; j++) { + if ((i * ni + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, C[i][j]); + } + POLYBENCH_DUMP_END("C"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_gemm(int ni, int nj, int nk, + DATA_TYPE alpha, + DATA_TYPE beta, + DATA_TYPE POLYBENCH_2D(C,NI,NJ,ni,nj), + DATA_TYPE POLYBENCH_2D(A,NI,NK,ni,nk), + DATA_TYPE POLYBENCH_2D(B,NK,NJ,nk,nj)) +{ + int i, j, k; + +//BLAS PARAMS +//TRANSA = 'N' +//TRANSB = 'N' +// => Form C := alpha*A*B + beta*C, +//A is NIxNK +//B is NKxNJ +//C is NIxNJ +#pragma scop + for (i = 0; i < _PB_NI; i++) { + for (j = 0; j < _PB_NJ; j++) + C[i][j] *= beta; + for (k = 0; k < _PB_NK; k++) { + for (j = 0; j < _PB_NJ; j++) + C[i][j] += alpha * A[i][k] * B[k][j]; + } + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int ni = NI; + int nj = NJ; + int nk = NK; + + /* Variable declaration/allocation. */ + DATA_TYPE alpha; + DATA_TYPE beta; + POLYBENCH_2D_ARRAY_DECL(C,DATA_TYPE,NI,NJ,ni,nj); + POLYBENCH_2D_ARRAY_DECL(A,DATA_TYPE,NI,NK,ni,nk); + POLYBENCH_2D_ARRAY_DECL(B,DATA_TYPE,NK,NJ,nk,nj); + + /* Initialize array(s). */ + init_array (ni, nj, nk, &alpha, &beta, + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_gemm (ni, nj, nk, + alpha, beta, + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(ni, nj, POLYBENCH_ARRAY(C))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(C); + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(B); + + return 0; +} + +// CHECK: func private @kernel_gemm(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: f64, %arg4: f64, %arg5: memref, %arg6: memref, %arg7: memref) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %2 = index_cast %arg2 : i32 to index +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: affine.for %arg9 = 0 to %1 { +// CHECK-NEXT: %3 = affine.load %arg5[%arg8, %arg9] : memref +// CHECK-NEXT: %4 = mulf %3, %arg4 : f64 +// CHECK-NEXT: affine.store %4, %arg5[%arg8, %arg9] : memref +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg9 = 0 to %2 { +// CHECK-NEXT: affine.for %arg10 = 0 to %1 { +// CHECK-NEXT: %3 = affine.load %arg6[%arg8, %arg9] : memref +// CHECK-NEXT: %4 = mulf %arg3, %3 : f64 +// CHECK-NEXT: %5 = affine.load %arg7[%arg9, %arg10] : memref +// CHECK-NEXT: %6 = mulf %4, %5 : f64 +// CHECK-NEXT: %7 = affine.load %arg5[%arg8, %arg10] : memref +// CHECK-NEXT: %8 = addf %7, %6 : f64 +// CHECK-NEXT: affine.store %8, %arg5[%arg8, %arg10] : memref +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/gemm/gemm.h b/mlir-clang/Test/polybench/linear-algebra/blas/gemm/gemm.h new file mode 100644 index 000000000000..0d18a0bfcddd --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/gemm/gemm.h @@ -0,0 +1,86 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _GEMM_H +# define _GEMM_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(NI) && !defined(NJ) && !defined(NK) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define NI 20 +# define NJ 25 +# define NK 30 +# endif + +# ifdef SMALL_DATASET +# define NI 60 +# define NJ 70 +# define NK 80 +# endif + +# ifdef MEDIUM_DATASET +# define NI 200 +# define NJ 220 +# define NK 240 +# endif + +# ifdef LARGE_DATASET +# define NI 1000 +# define NJ 1100 +# define NK 1200 +# endif + +# ifdef EXTRALARGE_DATASET +# define NI 2000 +# define NJ 2300 +# define NK 2600 +# endif + + +#endif /* !(NI NJ NK) */ + +# define _PB_NI POLYBENCH_LOOP_BOUND(NI,ni) +# define _PB_NJ POLYBENCH_LOOP_BOUND(NJ,nj) +# define _PB_NK POLYBENCH_LOOP_BOUND(NK,nk) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_GEMM_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/gemver/gemver.c b/mlir-clang/Test/polybench/linear-algebra/blas/gemver/gemver.c new file mode 100644 index 000000000000..c4aaa1c12d87 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/gemver/gemver.c @@ -0,0 +1,252 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* gemver.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "gemver.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE *alpha, + DATA_TYPE *beta, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n), + DATA_TYPE POLYBENCH_1D(u1,N,n), + DATA_TYPE POLYBENCH_1D(v1,N,n), + DATA_TYPE POLYBENCH_1D(u2,N,n), + DATA_TYPE POLYBENCH_1D(v2,N,n), + DATA_TYPE POLYBENCH_1D(w,N,n), + DATA_TYPE POLYBENCH_1D(x,N,n), + DATA_TYPE POLYBENCH_1D(y,N,n), + DATA_TYPE POLYBENCH_1D(z,N,n)) +{ + int i, j; + + *alpha = 1.5; + *beta = 1.2; + + DATA_TYPE fn = (DATA_TYPE)n; + + for (i = 0; i < n; i++) + { + u1[i] = i; + u2[i] = ((i+1)/fn)/2.0; + v1[i] = ((i+1)/fn)/4.0; + v2[i] = ((i+1)/fn)/6.0; + y[i] = ((i+1)/fn)/8.0; + z[i] = ((i+1)/fn)/9.0; + x[i] = 0.0; + w[i] = 0.0; + for (j = 0; j < n; j++) + A[i][j] = (DATA_TYPE) (i*j % n) / n; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_1D(w,N,n)) +{ + int i; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("w"); + for (i = 0; i < n; i++) { + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, w[i]); + } + POLYBENCH_DUMP_END("w"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_gemver(int n, + DATA_TYPE alpha, + DATA_TYPE beta, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n), + DATA_TYPE POLYBENCH_1D(u1,N,n), + DATA_TYPE POLYBENCH_1D(v1,N,n), + DATA_TYPE POLYBENCH_1D(u2,N,n), + DATA_TYPE POLYBENCH_1D(v2,N,n), + DATA_TYPE POLYBENCH_1D(w,N,n), + DATA_TYPE POLYBENCH_1D(x,N,n), + DATA_TYPE POLYBENCH_1D(y,N,n), + DATA_TYPE POLYBENCH_1D(z,N,n)) +{ + int i, j; + +#pragma scop + + for (i = 0; i < _PB_N; i++) + for (j = 0; j < _PB_N; j++) + A[i][j] = A[i][j] + u1[i] * v1[j] + u2[i] * v2[j]; + + for (i = 0; i < _PB_N; i++) + for (j = 0; j < _PB_N; j++) + x[i] = x[i] + beta * A[j][i] * y[j]; + + for (i = 0; i < _PB_N; i++) + x[i] = x[i] + z[i]; + + for (i = 0; i < _PB_N; i++) + for (j = 0; j < _PB_N; j++) + w[i] = w[i] + alpha * A[i][j] * x[j]; + +#pragma endscop +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + + /* Variable declaration/allocation. */ + DATA_TYPE alpha; + DATA_TYPE beta; + POLYBENCH_2D_ARRAY_DECL(A, DATA_TYPE, N, N, n, n); + POLYBENCH_1D_ARRAY_DECL(u1, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(v1, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(u2, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(v2, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(w, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(x, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(y, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(z, DATA_TYPE, N, n); + + + /* Initialize array(s). */ + init_array (n, &alpha, &beta, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(u1), + POLYBENCH_ARRAY(v1), + POLYBENCH_ARRAY(u2), + POLYBENCH_ARRAY(v2), + POLYBENCH_ARRAY(w), + POLYBENCH_ARRAY(x), + POLYBENCH_ARRAY(y), + POLYBENCH_ARRAY(z)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_gemver (n, alpha, beta, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(u1), + POLYBENCH_ARRAY(v1), + POLYBENCH_ARRAY(u2), + POLYBENCH_ARRAY(v2), + POLYBENCH_ARRAY(w), + POLYBENCH_ARRAY(x), + POLYBENCH_ARRAY(y), + POLYBENCH_ARRAY(z)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(w))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(u1); + POLYBENCH_FREE_ARRAY(v1); + POLYBENCH_FREE_ARRAY(u2); + POLYBENCH_FREE_ARRAY(v2); + POLYBENCH_FREE_ARRAY(w); + POLYBENCH_FREE_ARRAY(x); + POLYBENCH_FREE_ARRAY(y); + POLYBENCH_FREE_ARRAY(z); + + return 0; +} + + +// CHECK: func @kernel_gemver(%arg0: i32, %arg1: f64, %arg2: f64, %arg3: memref<2000x2000xf64>, %arg4: memref<2000xf64>, %arg5: memref<2000xf64>, %arg6: memref<2000xf64>, %arg7: memref<2000xf64>, %arg8: memref<2000xf64>, %arg9: memref<2000xf64>, %arg10: memref<2000xf64>, %arg11: memref<2000xf64>) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg12 = 0 to %0 { +// CHECK-NEXT: %1 = affine.load %arg4[%arg12] : memref<2000xf64> +// CHECK-NEXT: %2 = affine.load %arg6[%arg12] : memref<2000xf64> +// CHECK-NEXT: affine.for %arg13 = 0 to %0 { +// CHECK-NEXT: %3 = affine.load %arg3[%arg12, %arg13] : memref<2000x2000xf64> +// CHECK-NEXT: %4 = affine.load %arg5[%arg13] : memref<2000xf64> +// CHECK-NEXT: %5 = mulf %1, %4 : f64 +// CHECK-NEXT: %6 = addf %3, %5 : f64 +// CHECK-NEXT: %7 = affine.load %arg7[%arg13] : memref<2000xf64> +// CHECK-NEXT: %8 = mulf %2, %7 : f64 +// CHECK-NEXT: %9 = addf %6, %8 : f64 +// CHECK-NEXT: affine.store %9, %arg3[%arg12, %arg13] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg12 = 0 to %0 { +// CHECK-NEXT: %1 = affine.load %arg9[%arg12] : memref<2000xf64> +// CHECK-NEXT: affine.for %arg13 = 0 to %0 { +// CHECK-NEXT: %2 = affine.load %arg3[%arg13, %arg12] : memref<2000x2000xf64> +// CHECK-NEXT: %3 = mulf %arg2, %2 : f64 +// CHECK-NEXT: %4 = affine.load %arg10[%arg13] : memref<2000xf64> +// CHECK-NEXT: %5 = mulf %3, %4 : f64 +// CHECK-NEXT: %6 = addf %1, %5 : f64 +// CHECK-NEXT: affine.store %6, %arg9[%arg12] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg12 = 0 to %0 { +// CHECK-NEXT: %1 = affine.load %arg9[%arg12] : memref<2000xf64> +// CHECK-NEXT: %2 = affine.load %arg11[%arg12] : memref<2000xf64> +// CHECK-NEXT: %3 = addf %1, %2 : f64 +// CHECK-NEXT: affine.store %3, %arg9[%arg12] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg12 = 0 to %0 { +// CHECK-NEXT: %1 = affine.load %arg8[%arg12] : memref<2000xf64> +// CHECK-NEXT: affine.for %arg13 = 0 to %0 { +// CHECK-NEXT: %2 = affine.load %arg3[%arg12, %arg13] : memref<2000x2000xf64> +// CHECK-NEXT: %3 = mulf %arg1, %2 : f64 +// CHECK-NEXT: %4 = affine.load %arg9[%arg13] : memref<2000xf64> +// CHECK-NEXT: %5 = mulf %3, %4 : f64 +// CHECK-NEXT: %6 = addf %1, %5 : f64 +// CHECK-NEXT: affine.store %6, %arg8[%arg12] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/gemver/gemver.h b/mlir-clang/Test/polybench/linear-algebra/blas/gemver/gemver.h new file mode 100644 index 000000000000..a0abdd0bb01a --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/gemver/gemver.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _GEMVER_H +# define _GEMVER_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 40 +# endif + +# ifdef SMALL_DATASET +# define N 120 +# endif + +# ifdef MEDIUM_DATASET +# define N 400 +# endif + +# ifdef LARGE_DATASET +# define N 2000 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 4000 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_GEMVER_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/gesummv/gesummv.c b/mlir-clang/Test/polybench/linear-algebra/blas/gesummv/gesummv.c new file mode 100644 index 000000000000..a9defc710751 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/gesummv/gesummv.c @@ -0,0 +1,195 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* gesummv.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "gesummv.h" + + +/* Array initialization. */ +static +void init_array(int n, + DATA_TYPE *alpha, + DATA_TYPE *beta, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n), + DATA_TYPE POLYBENCH_2D(B,N,N,n,n), + DATA_TYPE POLYBENCH_1D(x,N,n)) +{ + int i, j; + + *alpha = 1.5; + *beta = 1.2; + for (i = 0; i < n; i++) + { + x[i] = (DATA_TYPE)( i % n) / n; + for (j = 0; j < n; j++) { + A[i][j] = (DATA_TYPE) ((i*j+1) % n) / n; + B[i][j] = (DATA_TYPE) ((i*j+2) % n) / n; + } + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_1D(y,N,n)) + +{ + int i; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("y"); + for (i = 0; i < n; i++) { + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, y[i]); + } + POLYBENCH_DUMP_END("y"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_gesummv(int n, + DATA_TYPE alpha, + DATA_TYPE beta, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n), + DATA_TYPE POLYBENCH_2D(B,N,N,n,n), + DATA_TYPE POLYBENCH_1D(tmp,N,n), + DATA_TYPE POLYBENCH_1D(x,N,n), + DATA_TYPE POLYBENCH_1D(y,N,n)) +{ + int i, j; + +#pragma scop + for (i = 0; i < _PB_N; i++) + { + tmp[i] = SCALAR_VAL(0.0); + y[i] = SCALAR_VAL(0.0); + for (j = 0; j < _PB_N; j++) + { + tmp[i] = A[i][j] * x[j] + tmp[i]; + y[i] = B[i][j] * x[j] + y[i]; + } + y[i] = alpha * tmp[i] + beta * y[i]; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + + /* Variable declaration/allocation. */ + DATA_TYPE alpha; + DATA_TYPE beta; + POLYBENCH_2D_ARRAY_DECL(A, DATA_TYPE, N, N, n, n); + POLYBENCH_2D_ARRAY_DECL(B, DATA_TYPE, N, N, n, n); + POLYBENCH_1D_ARRAY_DECL(tmp, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(x, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(y, DATA_TYPE, N, n); + + + /* Initialize array(s). */ + init_array (n, &alpha, &beta, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B), + POLYBENCH_ARRAY(x)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_gesummv (n, alpha, beta, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B), + POLYBENCH_ARRAY(tmp), + POLYBENCH_ARRAY(x), + POLYBENCH_ARRAY(y)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(y))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(B); + POLYBENCH_FREE_ARRAY(tmp); + POLYBENCH_FREE_ARRAY(x); + POLYBENCH_FREE_ARRAY(y); + + return 0; +} + +// CHECK: func @kernel_gesummv(%arg0: i32, %arg1: f64, %arg2: f64, %arg3: memref<1300x1300xf64>, %arg4: memref<1300x1300xf64>, %arg5: memref<1300xf64>, %arg6: memref<1300xf64>, %arg7: memref<1300xf64>) { +// CHECK-NEXT: %cst = constant 0.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: affine.store %cst, %arg5[%arg8] : memref<1300xf64> +// CHECK-NEXT: affine.store %cst, %arg7[%arg8] : memref<1300xf64> +// CHECK-NEXT: %1 = affine.load %arg5[%arg8] : memref<1300xf64> +// CHECK-NEXT: %2 = affine.load %arg7[%arg8] : memref<1300xf64> +// CHECK-NEXT: affine.for %arg9 = 0 to %0 { +// CHECK-NEXT: %8 = affine.load %arg3[%arg8, %arg9] : memref<1300x1300xf64> +// CHECK-NEXT: %9 = affine.load %arg6[%arg9] : memref<1300xf64> +// CHECK-NEXT: %10 = mulf %8, %9 : f64 +// CHECK-NEXT: %11 = addf %10, %1 : f64 +// CHECK-NEXT: affine.store %11, %arg5[%arg8] : memref<1300xf64> +// CHECK-NEXT: %12 = affine.load %arg4[%arg8, %arg9] : memref<1300x1300xf64> +// CHECK-NEXT: %13 = affine.load %arg6[%arg9] : memref<1300xf64> +// CHECK-NEXT: %14 = mulf %12, %13 : f64 +// CHECK-NEXT: %15 = addf %14, %2 : f64 +// CHECK-NEXT: affine.store %15, %arg7[%arg8] : memref<1300xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %3 = affine.load %arg5[%arg8] : memref<1300xf64> +// CHECK-NEXT: %4 = mulf %arg1, %3 : f64 +// CHECK-NEXT: %5 = affine.load %arg7[%arg8] : memref<1300xf64> +// CHECK-NEXT: %6 = mulf %arg2, %5 : f64 +// CHECK-NEXT: %7 = addf %4, %6 : f64 +// CHECK-NEXT: affine.store %7, %arg7[%arg8] : memref<1300xf64> +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/gesummv/gesummv.h b/mlir-clang/Test/polybench/linear-algebra/blas/gesummv/gesummv.h new file mode 100644 index 000000000000..64285d8307cc --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/gesummv/gesummv.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _GESUMMV_H +# define _GESUMMV_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 30 +# endif + +# ifdef SMALL_DATASET +# define N 90 +# endif + +# ifdef MEDIUM_DATASET +# define N 250 +# endif + +# ifdef LARGE_DATASET +# define N 1300 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 2800 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_GESUMMV_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/symm/symm.c b/mlir-clang/Test/polybench/linear-algebra/blas/symm/symm.c new file mode 100644 index 000000000000..c1301e99335c --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/symm/symm.c @@ -0,0 +1,211 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* symm.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "symm.h" + + +/* Array initialization. */ +static +void init_array(int m, int n, + DATA_TYPE *alpha, + DATA_TYPE *beta, + DATA_TYPE POLYBENCH_2D(C,M,N,m,n), + DATA_TYPE POLYBENCH_2D(A,M,M,m,m), + DATA_TYPE POLYBENCH_2D(B,M,N,m,n)) +{ + int i, j; + + *alpha = 1.5; + *beta = 1.2; + for (i = 0; i < m; i++) + for (j = 0; j < n; j++) { + C[i][j] = (DATA_TYPE) ((i+j) % 100) / m; + B[i][j] = (DATA_TYPE) ((n+i-j) % 100) / m; + } + for (i = 0; i < m; i++) { + for (j = 0; j <=i; j++) + A[i][j] = (DATA_TYPE) ((i+j) % 100) / m; + for (j = i+1; j < m; j++) + A[i][j] = -999; //regions of arrays that should not be used + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int m, int n, + DATA_TYPE POLYBENCH_2D(C,M,N,m,n)) +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("C"); + for (i = 0; i < m; i++) + for (j = 0; j < n; j++) { + if ((i * m + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, C[i][j]); + } + POLYBENCH_DUMP_END("C"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_symm(int m, int n, + DATA_TYPE alpha, + DATA_TYPE beta, + DATA_TYPE POLYBENCH_2D(C,M,N,m,n), + DATA_TYPE POLYBENCH_2D(A,M,M,m,m), + DATA_TYPE POLYBENCH_2D(B,M,N,m,n)) +{ + int i, j, k; + DATA_TYPE temp2; + +//BLAS PARAMS +//SIDE = 'L' +//UPLO = 'L' +// => Form C := alpha*A*B + beta*C +// A is MxM +// B is MxN +// C is MxN +//note that due to Fortran array layout, the code below more closely resembles upper triangular case in BLAS +#pragma scop + for (i = 0; i < _PB_M; i++) + for (j = 0; j < _PB_N; j++ ) + { + temp2 = 0; + for (k = 0; k < i; k++) { + C[k][j] += alpha*B[i][j] * A[i][k]; + temp2 += B[k][j] * A[i][k]; + } + C[i][j] = beta * C[i][j] + alpha*B[i][j] * A[i][i] + alpha * temp2; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int m = M; + int n = N; + + /* Variable declaration/allocation. */ + DATA_TYPE alpha; + DATA_TYPE beta; + POLYBENCH_2D_ARRAY_DECL(C,DATA_TYPE,M,N,m,n); + POLYBENCH_2D_ARRAY_DECL(A,DATA_TYPE,M,M,m,m); + POLYBENCH_2D_ARRAY_DECL(B,DATA_TYPE,M,N,m,n); + + /* Initialize array(s). */ + init_array (m, n, &alpha, &beta, + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_symm (m, n, + alpha, beta, + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(m, n, POLYBENCH_ARRAY(C))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(C); + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(B); + + return 0; +} + +// CHECK: #map = affine_map<(d0) -> (d0)> +// CHECK: func @kernel_symm(%arg0: i32, %arg1: i32, %arg2: f64, %arg3: f64, %arg4: memref<1000x1200xf64>, %arg5: memref<1000x1000xf64>, %arg6: memref<1000x1200xf64>) { +// CHECK-NEXT: %c0 = constant 0 : index +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %0 = alloca() : memref<1xf64> +// CHECK-NEXT: %1 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %2 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %3 = sitofp %c0_i32 : i32 to f64 +// CHECK-NEXT: store %3, %0[%c0] : memref<1xf64> +// CHECK-NEXT: %4 = load %0[%c0] : memref<1xf64> +// CHECK-NEXT: %5 = load %0[%c0] : memref<1xf64> +// CHECK-NEXT: %6 = mulf %arg2, %5 : f64 +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: %7 = affine.load %arg5[%arg7, %arg7] : memref<1000x1000xf64> +// CHECK-NEXT: affine.for %arg8 = 0 to %2 { +// CHECK-NEXT: %8 = affine.load %arg6[%arg7, %arg8] : memref<1000x1200xf64> +// CHECK-NEXT: %9 = mulf %arg2, %8 : f64 +// CHECK-NEXT: affine.for %arg9 = 0 to #map(%arg7) { +// CHECK-NEXT: %17 = affine.load %arg5[%arg7, %arg9] : memref<1000x1000xf64> +// CHECK-NEXT: %18 = mulf %9, %17 : f64 +// CHECK-NEXT: %19 = affine.load %arg4[%arg9, %arg8] : memref<1000x1200xf64> +// CHECK-NEXT: %20 = addf %19, %18 : f64 +// CHECK-NEXT: affine.store %20, %arg4[%arg9, %arg8] : memref<1000x1200xf64> +// CHECK-NEXT: %21 = affine.load %arg6[%arg9, %arg8] : memref<1000x1200xf64> +// CHECK-NEXT: %22 = affine.load %arg5[%arg7, %arg9] : memref<1000x1000xf64> +// CHECK-NEXT: %23 = mulf %21, %22 : f64 +// CHECK-NEXT: %24 = addf %4, %23 : f64 +// CHECK-NEXT: affine.store %24, %0[0] : memref<1xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %10 = affine.load %arg4[%arg7, %arg8] : memref<1000x1200xf64> +// CHECK-NEXT: %11 = mulf %arg3, %10 : f64 +// CHECK-NEXT: %12 = affine.load %arg6[%arg7, %arg8] : memref<1000x1200xf64> +// CHECK-NEXT: %13 = mulf %arg2, %12 : f64 +// CHECK-NEXT: %14 = mulf %13, %7 : f64 +// CHECK-NEXT: %15 = addf %11, %14 : f64 +// CHECK-NEXT: %16 = addf %15, %6 : f64 +// CHECK-NEXT: affine.store %16, %arg4[%arg7, %arg8] : memref<1000x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/symm/symm.h b/mlir-clang/Test/polybench/linear-algebra/blas/symm/symm.h new file mode 100644 index 000000000000..8848b3dac115 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/symm/symm.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _SYMM_H +# define _SYMM_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(M) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define M 20 +# define N 30 +# endif + +# ifdef SMALL_DATASET +# define M 60 +# define N 80 +# endif + +# ifdef MEDIUM_DATASET +# define M 200 +# define N 240 +# endif + +# ifdef LARGE_DATASET +# define M 1000 +# define N 1200 +# endif + +# ifdef EXTRALARGE_DATASET +# define M 2000 +# define N 2600 +# endif + + +#endif /* !(M N) */ + +# define _PB_M POLYBENCH_LOOP_BOUND(M,m) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_SYMM_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/syr2k/syr2k.c b/mlir-clang/Test/polybench/linear-algebra/blas/syr2k/syr2k.c new file mode 100644 index 000000000000..269e073aa6ef --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/syr2k/syr2k.c @@ -0,0 +1,193 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* syr2k.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "syr2k.h" + + +/* Array initialization. */ +static +void init_array(int n, int m, + DATA_TYPE *alpha, + DATA_TYPE *beta, + DATA_TYPE POLYBENCH_2D(C,N,N,n,n), + DATA_TYPE POLYBENCH_2D(A,N,M,n,m), + DATA_TYPE POLYBENCH_2D(B,N,M,n,m)) +{ + int i, j; + + *alpha = 1.5; + *beta = 1.2; + for (i = 0; i < n; i++) + for (j = 0; j < m; j++) { + A[i][j] = (DATA_TYPE) ((i*j+1)%n) / n; + B[i][j] = (DATA_TYPE) ((i*j+2)%m) / m; + } + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + C[i][j] = (DATA_TYPE) ((i*j+3)%n) / m; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_2D(C,N,N,n,n)) +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("C"); + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + if ((i * n + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, C[i][j]); + } + POLYBENCH_DUMP_END("C"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_syr2k(int n, int m, + DATA_TYPE alpha, + DATA_TYPE beta, + DATA_TYPE POLYBENCH_2D(C,N,N,n,n), + DATA_TYPE POLYBENCH_2D(A,N,M,n,m), + DATA_TYPE POLYBENCH_2D(B,N,M,n,m)) +{ + int i, j, k; + +//BLAS PARAMS +//UPLO = 'L' +//TRANS = 'N' +//A is NxM +//B is NxM +//C is NxN +#pragma scop + for (i = 0; i < _PB_N; i++) { + for (j = 0; j <= i; j++) + C[i][j] *= beta; + for (k = 0; k < _PB_M; k++) + for (j = 0; j <= i; j++) + { + C[i][j] += A[j][k]*alpha*B[i][k] + B[j][k]*alpha*A[i][k]; + } + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int m = M; + + /* Variable declaration/allocation. */ + DATA_TYPE alpha; + DATA_TYPE beta; + POLYBENCH_2D_ARRAY_DECL(C,DATA_TYPE,N,N,n,n); + POLYBENCH_2D_ARRAY_DECL(A,DATA_TYPE,N,M,n,m); + POLYBENCH_2D_ARRAY_DECL(B,DATA_TYPE,N,M,n,m); + + /* Initialize array(s). */ + init_array (n, m, &alpha, &beta, + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_syr2k (n, m, + alpha, beta, + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(C))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(C); + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(B); + + return 0; +} + +// CHECK: #map = affine_map<(d0) -> (d0 + 1)> +// CHECK: func @kernel_syr2k(%arg0: i32, %arg1: i32, %arg2: f64, %arg3: f64, %arg4: memref<1200x1200xf64>, %arg5: memref<1200x1000xf64>, %arg6: memref<1200x1000xf64>) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: affine.for %arg8 = 0 to #map(%arg7) { +// CHECK-NEXT: %2 = affine.load %arg4[%arg7, %arg8] : memref<1200x1200xf64> +// CHECK-NEXT: %3 = mulf %2, %arg3 : f64 +// CHECK-NEXT: affine.store %3, %arg4[%arg7, %arg8] : memref<1200x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg8 = 0 to %1 { +// CHECK-NEXT: %2 = affine.load %arg6[%arg7, %arg8] : memref<1200x1000xf64> +// CHECK-NEXT: %3 = affine.load %arg5[%arg7, %arg8] : memref<1200x1000xf64> +// CHECK-NEXT: affine.for %arg9 = 0 to #map(%arg7) { +// CHECK-NEXT: %4 = affine.load %arg5[%arg9, %arg8] : memref<1200x1000xf64> +// CHECK-NEXT: %5 = mulf %4, %arg2 : f64 +// CHECK-NEXT: %6 = mulf %5, %2 : f64 +// CHECK-NEXT: %7 = affine.load %arg6[%arg9, %arg8] : memref<1200x1000xf64> +// CHECK-NEXT: %8 = mulf %7, %arg2 : f64 +// CHECK-NEXT: %9 = mulf %8, %3 : f64 +// CHECK-NEXT: %10 = addf %6, %9 : f64 +// CHECK-NEXT: %11 = affine.load %arg4[%arg7, %arg9] : memref<1200x1200xf64> +// CHECK-NEXT: %12 = addf %11, %10 : f64 +// CHECK-NEXT: affine.store %12, %arg4[%arg7, %arg9] : memref<1200x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/syr2k/syr2k.h b/mlir-clang/Test/polybench/linear-algebra/blas/syr2k/syr2k.h new file mode 100644 index 000000000000..7861bcd8e8d8 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/syr2k/syr2k.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _SYR2K_H +# define _SYR2K_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(M) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define M 20 +# define N 30 +# endif + +# ifdef SMALL_DATASET +# define M 60 +# define N 80 +# endif + +# ifdef MEDIUM_DATASET +# define M 200 +# define N 240 +# endif + +# ifdef LARGE_DATASET +# define M 1000 +# define N 1200 +# endif + +# ifdef EXTRALARGE_DATASET +# define M 2000 +# define N 2600 +# endif + + +#endif /* !(M N) */ + +# define _PB_M POLYBENCH_LOOP_BOUND(M,m) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_SYR2K_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/syrk/syrk.c b/mlir-clang/Test/polybench/linear-algebra/blas/syrk/syrk.c new file mode 100644 index 000000000000..a3638d1910ab --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/syrk/syrk.c @@ -0,0 +1,173 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* syrk.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "syrk.h" + + +/* Array initialization. */ +static +void init_array(int n, int m, + DATA_TYPE *alpha, + DATA_TYPE *beta, + DATA_TYPE POLYBENCH_2D(C,N,N,n,n), + DATA_TYPE POLYBENCH_2D(A,N,M,n,m)) +{ + int i, j; + + *alpha = 1.5; + *beta = 1.2; + for (i = 0; i < n; i++) + for (j = 0; j < m; j++) + A[i][j] = (DATA_TYPE) ((i*j+1)%n) / n; + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + C[i][j] = (DATA_TYPE) ((i*j+2)%m) / m; +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_2D(C,N,N,n,n)) +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("C"); + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + if ((i * n + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, C[i][j]); + } + POLYBENCH_DUMP_END("C"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_syrk(int n, int m, + DATA_TYPE alpha, + DATA_TYPE beta, + DATA_TYPE POLYBENCH_2D(C,N,N,n,n), + DATA_TYPE POLYBENCH_2D(A,N,M,n,m)) +{ + int i, j, k; + +//BLAS PARAMS +//TRANS = 'N' +//UPLO = 'L' +// => Form C := alpha*A*A**T + beta*C. +//A is NxM +//C is NxN +#pragma scop + for (i = 0; i < _PB_N; i++) { + for (j = 0; j <= i; j++) + C[i][j] *= beta; + for (k = 0; k < _PB_M; k++) { + for (j = 0; j <= i; j++) + C[i][j] += alpha * A[i][k] * A[j][k]; + } + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int m = M; + + /* Variable declaration/allocation. */ + DATA_TYPE alpha; + DATA_TYPE beta; + POLYBENCH_2D_ARRAY_DECL(C,DATA_TYPE,N,N,n,n); + POLYBENCH_2D_ARRAY_DECL(A,DATA_TYPE,N,M,n,m); + + /* Initialize array(s). */ + init_array (n, m, &alpha, &beta, POLYBENCH_ARRAY(C), POLYBENCH_ARRAY(A)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_syrk (n, m, alpha, beta, POLYBENCH_ARRAY(C), POLYBENCH_ARRAY(A)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(C))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(C); + POLYBENCH_FREE_ARRAY(A); + + return 0; +} + +// CHECK: #map = affine_map<(d0) -> (d0 + 1)> +// CHECK: func @kernel_syrk(%arg0: i32, %arg1: i32, %arg2: f64, %arg3: f64, %arg4: memref<1200x1200xf64>, %arg5: memref<1200x1000xf64>) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: affine.for %arg6 = 0 to %0 { +// CHECK-NEXT: affine.for %arg7 = 0 to #map(%arg6) { +// CHECK-NEXT: %2 = affine.load %arg4[%arg6, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: %3 = mulf %2, %arg3 : f64 +// CHECK-NEXT: affine.store %3, %arg4[%arg6, %arg7] : memref<1200x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: %2 = affine.load %arg5[%arg6, %arg7] : memref<1200x1000xf64> +// CHECK-NEXT: %3 = mulf %arg2, %2 : f64 +// CHECK-NEXT: affine.for %arg8 = 0 to #map(%arg6) { +// CHECK-NEXT: %4 = affine.load %arg5[%arg8, %arg7] : memref<1200x1000xf64> +// CHECK-NEXT: %5 = mulf %3, %4 : f64 +// CHECK-NEXT: %6 = affine.load %arg4[%arg6, %arg8] : memref<1200x1200xf64> +// CHECK-NEXT: %7 = addf %6, %5 : f64 +// CHECK-NEXT: affine.store %7, %arg4[%arg6, %arg8] : memref<1200x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/syrk/syrk.h b/mlir-clang/Test/polybench/linear-algebra/blas/syrk/syrk.h new file mode 100644 index 000000000000..648fab399bde --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/syrk/syrk.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _SYRK_H +# define _SYRK_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(M) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define M 20 +# define N 30 +# endif + +# ifdef SMALL_DATASET +# define M 60 +# define N 80 +# endif + +# ifdef MEDIUM_DATASET +# define M 200 +# define N 240 +# endif + +# ifdef LARGE_DATASET +# define M 1000 +# define N 1200 +# endif + +# ifdef EXTRALARGE_DATASET +# define M 2000 +# define N 2600 +# endif + + +#endif /* !(M N) */ + +# define _PB_M POLYBENCH_LOOP_BOUND(M,m) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_SYRK_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/trmm/trmm.c b/mlir-clang/Test/polybench/linear-algebra/blas/trmm/trmm.c new file mode 100644 index 000000000000..c74e6a338bcc --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/trmm/trmm.c @@ -0,0 +1,170 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* trmm.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "trmm.h" + + +/* Array initialization. */ +static +void init_array(int m, int n, + DATA_TYPE *alpha, + DATA_TYPE POLYBENCH_2D(A,M,M,m,m), + DATA_TYPE POLYBENCH_2D(B,M,N,m,n)) +{ + int i, j; + + *alpha = 1.5; + for (i = 0; i < m; i++) { + for (j = 0; j < i; j++) { + A[i][j] = (DATA_TYPE)((i+j) % m)/m; + } + A[i][i] = 1.0; + for (j = 0; j < n; j++) { + B[i][j] = (DATA_TYPE)((n+(i-j)) % n)/n; + } + } + +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int m, int n, + DATA_TYPE POLYBENCH_2D(B,M,N,m,n)) +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("B"); + for (i = 0; i < m; i++) + for (j = 0; j < n; j++) { + if ((i * m + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, B[i][j]); + } + POLYBENCH_DUMP_END("B"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_trmm(int m, int n, + DATA_TYPE alpha, + DATA_TYPE POLYBENCH_2D(A,M,M,m,m), + DATA_TYPE POLYBENCH_2D(B,M,N,m,n)) +{ + int i, j, k; + +//BLAS parameters +//SIDE = 'L' +//UPLO = 'L' +//TRANSA = 'T' +//DIAG = 'U' +// => Form B := alpha*A**T*B. +// A is MxM +// B is MxN +#pragma scop + for (i = 0; i < _PB_M; i++) + for (j = 0; j < _PB_N; j++) { + for (k = i+1; k < _PB_M; k++) + B[i][j] += A[k][i] * B[k][j]; + B[i][j] = alpha * B[i][j]; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int m = M; + int n = N; + + /* Variable declaration/allocation. */ + DATA_TYPE alpha; + POLYBENCH_2D_ARRAY_DECL(A,DATA_TYPE,M,M,m,m); + POLYBENCH_2D_ARRAY_DECL(B,DATA_TYPE,M,N,m,n); + + /* Initialize array(s). */ + init_array (m, n, &alpha, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(B)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_trmm (m, n, alpha, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(B)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(m, n, POLYBENCH_ARRAY(B))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(B); + + return 0; +} + +// CHECK: #map = affine_map<(d0) -> (d0 + 1)> +// CHECK: func @kernel_trmm(%arg0: i32, %arg1: i32, %arg2: f64, %arg3: memref<1000x1000xf64>, %arg4: memref<1000x1200xf64>) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: affine.for %arg5 = 0 to %0 { +// CHECK-NEXT: affine.for %arg6 = 0 to %1 { +// CHECK-NEXT: %2 = affine.load %arg4[%arg5, %arg6] : memref<1000x1200xf64> +// CHECK-NEXT: affine.for %arg7 = #map(%arg5) to %0 { +// CHECK-NEXT: %5 = affine.load %arg3[%arg7, %arg5] : memref<1000x1000xf64> +// CHECK-NEXT: %6 = affine.load %arg4[%arg7, %arg6] : memref<1000x1200xf64> +// CHECK-NEXT: %7 = mulf %5, %6 : f64 +// CHECK-NEXT: %8 = addf %2, %7 : f64 +// CHECK-NEXT: affine.store %8, %arg4[%arg5, %arg6] : memref<1000x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %3 = affine.load %arg4[%arg5, %arg6] : memref<1000x1200xf64> +// CHECK-NEXT: %4 = mulf %arg2, %3 : f64 +// CHECK-NEXT: affine.store %4, %arg4[%arg5, %arg6] : memref<1000x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/blas/trmm/trmm.h b/mlir-clang/Test/polybench/linear-algebra/blas/trmm/trmm.h new file mode 100644 index 000000000000..b1eaea1e0d29 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/blas/trmm/trmm.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _TRMM_H +# define _TRMM_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(M) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define M 20 +# define N 30 +# endif + +# ifdef SMALL_DATASET +# define M 60 +# define N 80 +# endif + +# ifdef MEDIUM_DATASET +# define M 200 +# define N 240 +# endif + +# ifdef LARGE_DATASET +# define M 1000 +# define N 1200 +# endif + +# ifdef EXTRALARGE_DATASET +# define M 2000 +# define N 2600 +# endif + + +#endif /* !(M N) */ + +# define _PB_M POLYBENCH_LOOP_BOUND(M,m) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_TRMM_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/2mm/2mm.c b/mlir-clang/Test/polybench/linear-algebra/kernels/2mm/2mm.c new file mode 100644 index 000000000000..f8b84bf2c9a4 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/2mm/2mm.c @@ -0,0 +1,211 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm +// RUN: mlir-clang -raise-scf-to-affine %s %stdinclude | FileCheck %s + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* 2mm.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "2mm.h" + + +/* Array initialization. */ +static +void init_array(int ni, int nj, int nk, int nl, + DATA_TYPE *alpha, + DATA_TYPE *beta, + DATA_TYPE POLYBENCH_2D(A,NI,NK,ni,nk), + DATA_TYPE POLYBENCH_2D(B,NK,NJ,nk,nj), + DATA_TYPE POLYBENCH_2D(C,NJ,NL,nj,nl), + DATA_TYPE POLYBENCH_2D(D,NI,NL,ni,nl)) +{ + int i, j; + + *alpha = 1.5; + *beta = 1.2; + for (i = 0; i < ni; i++) + for (j = 0; j < nk; j++) + A[i][j] = (DATA_TYPE) ((i*j+1) % ni) / ni; + for (i = 0; i < nk; i++) + for (j = 0; j < nj; j++) + B[i][j] = (DATA_TYPE) (i*(j+1) % nj) / nj; + for (i = 0; i < nj; i++) + for (j = 0; j < nl; j++) + C[i][j] = (DATA_TYPE) ((i*(j+3)+1) % nl) / nl; + for (i = 0; i < ni; i++) + for (j = 0; j < nl; j++) + D[i][j] = (DATA_TYPE) (i*(j+2) % nk) / nk; +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int ni, int nl, + DATA_TYPE POLYBENCH_2D(D,NI,NL,ni,nl)) +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("D"); + for (i = 0; i < ni; i++) + for (j = 0; j < nl; j++) { + if ((i * ni + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, D[i][j]); + } + POLYBENCH_DUMP_END("D"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_2mm(int ni, int nj, int nk, int nl, + DATA_TYPE alpha, + DATA_TYPE beta, + DATA_TYPE POLYBENCH_2D(tmp,NI,NJ,ni,nj), + DATA_TYPE POLYBENCH_2D(A,NI,NK,ni,nk), + DATA_TYPE POLYBENCH_2D(B,NK,NJ,nk,nj), + DATA_TYPE POLYBENCH_2D(C,NJ,NL,nj,nl), + DATA_TYPE POLYBENCH_2D(D,NI,NL,ni,nl)) +{ + int i, j, k; + +#pragma scop + /* D := alpha*A*B*C + beta*D */ + for (i = 0; i < _PB_NI; i++) + for (j = 0; j < _PB_NJ; j++) + { + tmp[i][j] = SCALAR_VAL(0.0); + for (k = 0; k < _PB_NK; ++k) + tmp[i][j] += alpha * A[i][k] * B[k][j]; + } + for (i = 0; i < _PB_NI; i++) + for (j = 0; j < _PB_NL; j++) + { + D[i][j] *= beta; + for (k = 0; k < _PB_NJ; ++k) + D[i][j] += tmp[i][k] * C[k][j]; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int ni = NI; + int nj = NJ; + int nk = NK; + int nl = NL; + + /* Variable declaration/allocation. */ + DATA_TYPE alpha; + DATA_TYPE beta; + POLYBENCH_2D_ARRAY_DECL(tmp,DATA_TYPE,NI,NJ,ni,nj); + POLYBENCH_2D_ARRAY_DECL(A,DATA_TYPE,NI,NK,ni,nk); + POLYBENCH_2D_ARRAY_DECL(B,DATA_TYPE,NK,NJ,nk,nj); + POLYBENCH_2D_ARRAY_DECL(C,DATA_TYPE,NJ,NL,nj,nl); + POLYBENCH_2D_ARRAY_DECL(D,DATA_TYPE,NI,NL,ni,nl); + + /* Initialize array(s). */ + init_array (ni, nj, nk, nl, &alpha, &beta, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B), + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(D)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_2mm (ni, nj, nk, nl, + alpha, beta, + POLYBENCH_ARRAY(tmp), + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B), + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(D)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(ni, nl, POLYBENCH_ARRAY(D))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(tmp); + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(B); + POLYBENCH_FREE_ARRAY(C); + POLYBENCH_FREE_ARRAY(D); + + return 0; +} +// CHECK: @kernel_2mm +// CHECK: affine.for %arg11 = 0 to {{.*}} { +// CHECK: affine.for %arg12 = 0 to {{.*}} { +// CHECK: affine.store %cst, %arg6[%arg11, %arg12] : memref +// CHECK: affine.for %arg13 = 0 to {{.*}} { +// CHECK: %4 = affine.load %arg7[%arg11, %arg13] : memref +// CHECK: %5 = mulf %arg4, %4 : f64 +// CHECK: %6 = affine.load %arg8[%arg13, %arg12] : memref +// CHECK: %7 = mulf %5, %6 : f64 +// CHECK: %8 = affine.load %arg6[%arg11, %arg12] : memref +// CHECK: %9 = addf %8, %7 : f64 +// CHECK: affine.store %9, %arg6[%arg11, %arg12] : memref +// CHECK: } +// CHECK: } +// CHECK: } +// CHECK: affine.for %arg11 = 0 to {{.*}} { +// CHECK: affine.for %arg12 = 0 to {{.*}} { +// CHECK: %4 = affine.load %arg10[%arg11, %arg12] : memref +// CHECK: %5 = mulf %4, %arg5 : f64 +// CHECK: affine.store %5, %arg10[%arg11, %arg12] : memref +// CHECK: affine.for %arg13 = 0 to {{.*}} { +// CHECK: %6 = affine.load %arg6[%arg11, %arg13] : memref +// CHECK: %7 = affine.load %arg9[%arg13, %arg12] : memref +// CHECK: %8 = mulf %6, %7 : f64 +// CHECK: %9 = affine.load %arg10[%arg11, %arg12] : memref +// CHECK: %10 = addf %9, %8 : f64 +// CHECK: affine.store %10, %arg10[%arg11, %arg12] : memref +// CHECK: } +// CHECK: } +// CHECK: } +// CHECK: return +// CHECK: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/2mm/2mm.h b/mlir-clang/Test/polybench/linear-algebra/kernels/2mm/2mm.h new file mode 100644 index 000000000000..1b0b3d580cf0 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/2mm/2mm.h @@ -0,0 +1,92 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _2MM_H +# define _2MM_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(NI) && !defined(NJ) && !defined(NK) && !defined(NL) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define NI 16 +# define NJ 18 +# define NK 22 +# define NL 24 +# endif + +# ifdef SMALL_DATASET +# define NI 40 +# define NJ 50 +# define NK 70 +# define NL 80 +# endif + +# ifdef MEDIUM_DATASET +# define NI 180 +# define NJ 190 +# define NK 210 +# define NL 220 +# endif + +# ifdef LARGE_DATASET +# define NI 800 +# define NJ 900 +# define NK 1100 +# define NL 1200 +# endif + +# ifdef EXTRALARGE_DATASET +# define NI 1600 +# define NJ 1800 +# define NK 2200 +# define NL 2400 +# endif + + +#endif /* !(NI NJ NK NL) */ + +# define _PB_NI POLYBENCH_LOOP_BOUND(NI,ni) +# define _PB_NJ POLYBENCH_LOOP_BOUND(NJ,nj) +# define _PB_NK POLYBENCH_LOOP_BOUND(NK,nk) +# define _PB_NL POLYBENCH_LOOP_BOUND(NL,nl) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_2MM_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/3mm/3mm.c b/mlir-clang/Test/polybench/linear-algebra/kernels/3mm/3mm.c new file mode 100644 index 000000000000..d509d05976c1 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/3mm/3mm.c @@ -0,0 +1,236 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* 3mm.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "3mm.h" + + +/* Array initialization. */ +static +void init_array(int ni, int nj, int nk, int nl, int nm, + DATA_TYPE POLYBENCH_2D(A,NI,NK,ni,nk), + DATA_TYPE POLYBENCH_2D(B,NK,NJ,nk,nj), + DATA_TYPE POLYBENCH_2D(C,NJ,NM,nj,nm), + DATA_TYPE POLYBENCH_2D(D,NM,NL,nm,nl)) +{ + int i, j; + + for (i = 0; i < ni; i++) + for (j = 0; j < nk; j++) + A[i][j] = (DATA_TYPE) ((i*j+1) % ni) / (5*ni); + for (i = 0; i < nk; i++) + for (j = 0; j < nj; j++) + B[i][j] = (DATA_TYPE) ((i*(j+1)+2) % nj) / (5*nj); + for (i = 0; i < nj; i++) + for (j = 0; j < nm; j++) + C[i][j] = (DATA_TYPE) (i*(j+3) % nl) / (5*nl); + for (i = 0; i < nm; i++) + for (j = 0; j < nl; j++) + D[i][j] = (DATA_TYPE) ((i*(j+2)+2) % nk) / (5*nk); +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int ni, int nl, + DATA_TYPE POLYBENCH_2D(G,NI,NL,ni,nl)) +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("G"); + for (i = 0; i < ni; i++) + for (j = 0; j < nl; j++) { + if ((i * ni + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, G[i][j]); + } + POLYBENCH_DUMP_END("G"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_3mm(int ni, int nj, int nk, int nl, int nm, + DATA_TYPE POLYBENCH_2D(E,NI,NJ,ni,nj), + DATA_TYPE POLYBENCH_2D(A,NI,NK,ni,nk), + DATA_TYPE POLYBENCH_2D(B,NK,NJ,nk,nj), + DATA_TYPE POLYBENCH_2D(F,NJ,NL,nj,nl), + DATA_TYPE POLYBENCH_2D(C,NJ,NM,nj,nm), + DATA_TYPE POLYBENCH_2D(D,NM,NL,nm,nl), + DATA_TYPE POLYBENCH_2D(G,NI,NL,ni,nl)) +{ + int i, j, k; + +#pragma scop + /* E := A*B */ + for (i = 0; i < _PB_NI; i++) + for (j = 0; j < _PB_NJ; j++) + { + E[i][j] = SCALAR_VAL(0.0); + for (k = 0; k < _PB_NK; ++k) + E[i][j] += A[i][k] * B[k][j]; + } + /* F := C*D */ + for (i = 0; i < _PB_NJ; i++) + for (j = 0; j < _PB_NL; j++) + { + F[i][j] = SCALAR_VAL(0.0); + for (k = 0; k < _PB_NM; ++k) + F[i][j] += C[i][k] * D[k][j]; + } + /* G := E*F */ + for (i = 0; i < _PB_NI; i++) + for (j = 0; j < _PB_NL; j++) + { + G[i][j] = SCALAR_VAL(0.0); + for (k = 0; k < _PB_NJ; ++k) + G[i][j] += E[i][k] * F[k][j]; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int ni = NI; + int nj = NJ; + int nk = NK; + int nl = NL; + int nm = NM; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(E, DATA_TYPE, NI, NJ, ni, nj); + POLYBENCH_2D_ARRAY_DECL(A, DATA_TYPE, NI, NK, ni, nk); + POLYBENCH_2D_ARRAY_DECL(B, DATA_TYPE, NK, NJ, nk, nj); + POLYBENCH_2D_ARRAY_DECL(F, DATA_TYPE, NJ, NL, nj, nl); + POLYBENCH_2D_ARRAY_DECL(C, DATA_TYPE, NJ, NM, nj, nm); + POLYBENCH_2D_ARRAY_DECL(D, DATA_TYPE, NM, NL, nm, nl); + POLYBENCH_2D_ARRAY_DECL(G, DATA_TYPE, NI, NL, ni, nl); + + /* Initialize array(s). */ + init_array (ni, nj, nk, nl, nm, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B), + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(D)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_3mm (ni, nj, nk, nl, nm, + POLYBENCH_ARRAY(E), + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(B), + POLYBENCH_ARRAY(F), + POLYBENCH_ARRAY(C), + POLYBENCH_ARRAY(D), + POLYBENCH_ARRAY(G)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(ni, nl, POLYBENCH_ARRAY(G))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(E); + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(B); + POLYBENCH_FREE_ARRAY(F); + POLYBENCH_FREE_ARRAY(C); + POLYBENCH_FREE_ARRAY(D); + POLYBENCH_FREE_ARRAY(G); + + return 0; +} + +// CHECK: func @kernel_3mm(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32, %arg4: i32, %arg5: memref<800x900xf64>, %arg6: memref<800x1000xf64>, %arg7: memref<1000x900xf64>, %arg8: memref<900x1100xf64>, %arg9: memref<900x1200xf64>, %arg10: memref<1200x1100xf64>, %arg11: memref<800x1100xf64>) { +// CHECK-NEXT: %cst = constant 0.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %2 = index_cast %arg2 : i32 to index +// CHECK-NEXT: affine.for %arg12 = 0 to %0 { +// CHECK-NEXT: affine.for %arg13 = 0 to %1 { +// CHECK-NEXT: affine.store %cst, %arg5[%arg12, %arg13] : memref<800x900xf64> +// CHECK-NEXT: %5 = affine.load %arg5[%arg12, %arg13] : memref<800x900xf64> +// CHECK-NEXT: affine.for %arg14 = 0 to %2 { +// CHECK-NEXT: %6 = affine.load %arg6[%arg12, %arg14] : memref<800x1000xf64> +// CHECK-NEXT: %7 = affine.load %arg7[%arg14, %arg13] : memref<1000x900xf64> +// CHECK-NEXT: %8 = mulf %6, %7 : f64 +// CHECK-NEXT: %9 = addf %5, %8 : f64 +// CHECK-NEXT: affine.store %9, %arg5[%arg12, %arg13] : memref<800x900xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: %3 = index_cast %arg3 : i32 to index +// CHECK-NEXT: %4 = index_cast %arg4 : i32 to index +// CHECK-NEXT: affine.for %arg12 = 0 to %1 { +// CHECK-NEXT: affine.for %arg13 = 0 to %3 { +// CHECK-NEXT: affine.store %cst, %arg8[%arg12, %arg13] : memref<900x1100xf64> +// CHECK-NEXT: %5 = affine.load %arg8[%arg12, %arg13] : memref<900x1100xf64> +// CHECK-NEXT: affine.for %arg14 = 0 to %4 { +// CHECK-NEXT: %6 = affine.load %arg9[%arg12, %arg14] : memref<900x1200xf64> +// CHECK-NEXT: %7 = affine.load %arg10[%arg14, %arg13] : memref<1200x1100xf64> +// CHECK-NEXT: %8 = mulf %6, %7 : f64 +// CHECK-NEXT: %9 = addf %5, %8 : f64 +// CHECK-NEXT: affine.store %9, %arg8[%arg12, %arg13] : memref<900x1100xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg12 = 0 to %0 { +// CHECK-NEXT: affine.for %arg13 = 0 to %3 { +// CHECK-NEXT: affine.store %cst, %arg11[%arg12, %arg13] : memref<800x1100xf64> +// CHECK-NEXT: %5 = affine.load %arg11[%arg12, %arg13] : memref<800x1100xf64> +// CHECK-NEXT: affine.for %arg14 = 0 to %1 { +// CHECK-NEXT: %6 = affine.load %arg5[%arg12, %arg14] : memref<800x900xf64> +// CHECK-NEXT: %7 = affine.load %arg8[%arg14, %arg13] : memref<900x1100xf64> +// CHECK-NEXT: %8 = mulf %6, %7 : f64 +// CHECK-NEXT: %9 = addf %5, %8 : f64 +// CHECK-NEXT: affine.store %9, %arg11[%arg12, %arg13] : memref<800x1100xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/3mm/3mm.h b/mlir-clang/Test/polybench/linear-algebra/kernels/3mm/3mm.h new file mode 100644 index 000000000000..8cc464eb6277 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/3mm/3mm.h @@ -0,0 +1,98 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _3MM_H +# define _3MM_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(NI) && !defined(NJ) && !defined(NK) && !defined(NL) && !defined(NM) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define NI 16 +# define NJ 18 +# define NK 20 +# define NL 22 +# define NM 24 +# endif + +# ifdef SMALL_DATASET +# define NI 40 +# define NJ 50 +# define NK 60 +# define NL 70 +# define NM 80 +# endif + +# ifdef MEDIUM_DATASET +# define NI 180 +# define NJ 190 +# define NK 200 +# define NL 210 +# define NM 220 +# endif + +# ifdef LARGE_DATASET +# define NI 800 +# define NJ 900 +# define NK 1000 +# define NL 1100 +# define NM 1200 +# endif + +# ifdef EXTRALARGE_DATASET +# define NI 1600 +# define NJ 1800 +# define NK 2000 +# define NL 2200 +# define NM 2400 +# endif + + +#endif /* !(NI NJ NK NL NM) */ + +# define _PB_NI POLYBENCH_LOOP_BOUND(NI,ni) +# define _PB_NJ POLYBENCH_LOOP_BOUND(NJ,nj) +# define _PB_NK POLYBENCH_LOOP_BOUND(NK,nk) +# define _PB_NL POLYBENCH_LOOP_BOUND(NL,nl) +# define _PB_NM POLYBENCH_LOOP_BOUND(NM,nm) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_3MM_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/atax/atax.c b/mlir-clang/Test/polybench/linear-algebra/kernels/atax/atax.c new file mode 100644 index 000000000000..e9b8ca0d77d8 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/atax/atax.c @@ -0,0 +1,178 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* atax.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "atax.h" + + +/* Array initialization. */ +static +void init_array (int m, int n, + DATA_TYPE POLYBENCH_2D(A,M,N,m,n), + DATA_TYPE POLYBENCH_1D(x,N,n)) +{ + int i, j; + DATA_TYPE fn; + fn = (DATA_TYPE)n; + + for (i = 0; i < n; i++) + x[i] = 1 + (i / fn); + for (i = 0; i < m; i++) + for (j = 0; j < n; j++) + A[i][j] = (DATA_TYPE) ((i+j) % n) / (5*m); +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_1D(y,N,n)) + +{ + int i; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("y"); + for (i = 0; i < n; i++) { + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, y[i]); + } + POLYBENCH_DUMP_END("y"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_atax(int m, int n, + DATA_TYPE POLYBENCH_2D(A,M,N,m,n), + DATA_TYPE POLYBENCH_1D(x,N,n), + DATA_TYPE POLYBENCH_1D(y,N,n), + DATA_TYPE POLYBENCH_1D(tmp,M,m)) +{ + int i, j; + +#pragma scop + for (i = 0; i < _PB_N; i++) + y[i] = 0; + for (i = 0; i < _PB_M; i++) + { + tmp[i] = SCALAR_VAL(0.0); + for (j = 0; j < _PB_N; j++) + tmp[i] = tmp[i] + A[i][j] * x[j]; + for (j = 0; j < _PB_N; j++) + y[j] = y[j] + A[i][j] * tmp[i]; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int m = M; + int n = N; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(A, DATA_TYPE, M, N, m, n); + POLYBENCH_1D_ARRAY_DECL(x, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(y, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(tmp, DATA_TYPE, M, m); + + /* Initialize array(s). */ + init_array (m, n, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(x)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_atax (m, n, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(x), + POLYBENCH_ARRAY(y), + POLYBENCH_ARRAY(tmp)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(y))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(x); + POLYBENCH_FREE_ARRAY(y); + POLYBENCH_FREE_ARRAY(tmp); + + return 0; +} + +// CHECK: func @kernel_atax(%arg0: i32, %arg1: i32, %arg2: memref<1900x2100xf64>, %arg3: memref<2100xf64>, %arg4: memref<2100xf64>, %arg5: memref<1900xf64>) { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %cst = constant 0.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %1 = sitofp %c0_i32 : i32 to f64 +// CHECK-NEXT: affine.for %arg6 = 0 to %0 { +// CHECK-NEXT: affine.store %1, %arg4[%arg6] : memref<2100xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %2 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg6 = 0 to %2 { +// CHECK-NEXT: affine.store %cst, %arg5[%arg6] : memref<1900xf64> +// CHECK-NEXT: %3 = affine.load %arg5[%arg6] : memref<1900xf64> +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: %5 = affine.load %arg2[%arg6, %arg7] : memref<1900x2100xf64> +// CHECK-NEXT: %6 = affine.load %arg3[%arg7] : memref<2100xf64> +// CHECK-NEXT: %7 = mulf %5, %6 : f64 +// CHECK-NEXT: %8 = addf %3, %7 : f64 +// CHECK-NEXT: affine.store %8, %arg5[%arg6] : memref<1900xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %4 = affine.load %arg5[%arg6] : memref<1900xf64> +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: %5 = affine.load %arg4[%arg7] : memref<2100xf64> +// CHECK-NEXT: %6 = affine.load %arg2[%arg6, %arg7] : memref<1900x2100xf64> +// CHECK-NEXT: %7 = mulf %6, %4 : f64 +// CHECK-NEXT: %8 = addf %5, %7 : f64 +// CHECK-NEXT: affine.store %8, %arg4[%arg7] : memref<2100xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT:} + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/atax/atax.h b/mlir-clang/Test/polybench/linear-algebra/kernels/atax/atax.h new file mode 100644 index 000000000000..21e12b2d72cc --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/atax/atax.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _ATAX_H +# define _ATAX_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(M) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define M 38 +# define N 42 +# endif + +# ifdef SMALL_DATASET +# define M 116 +# define N 124 +# endif + +# ifdef MEDIUM_DATASET +# define M 390 +# define N 410 +# endif + +# ifdef LARGE_DATASET +# define M 1900 +# define N 2100 +# endif + +# ifdef EXTRALARGE_DATASET +# define M 1800 +# define N 2200 +# endif + + +#endif /* !(M N) */ + +# define _PB_M POLYBENCH_LOOP_BOUND(M,m) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_ATAX_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/bicg/bicg.c b/mlir-clang/Test/polybench/linear-algebra/kernels/bicg/bicg.c new file mode 100644 index 000000000000..0cb7da8a6c43 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/bicg/bicg.c @@ -0,0 +1,192 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* bicg.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "bicg.h" + + +/* Array initialization. */ +static +void init_array (int m, int n, + DATA_TYPE POLYBENCH_2D(A,N,M,n,m), + DATA_TYPE POLYBENCH_1D(r,N,n), + DATA_TYPE POLYBENCH_1D(p,M,m)) +{ + int i, j; + + for (i = 0; i < m; i++) + p[i] = (DATA_TYPE)(i % m) / m; + for (i = 0; i < n; i++) { + r[i] = (DATA_TYPE)(i % n) / n; + for (j = 0; j < m; j++) + A[i][j] = (DATA_TYPE) (i*(j+1) % n)/n; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int m, int n, + DATA_TYPE POLYBENCH_1D(s,M,m), + DATA_TYPE POLYBENCH_1D(q,N,n)) + +{ + int i; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("s"); + for (i = 0; i < m; i++) { + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, s[i]); + } + POLYBENCH_DUMP_END("s"); + POLYBENCH_DUMP_BEGIN("q"); + for (i = 0; i < n; i++) { + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, q[i]); + } + POLYBENCH_DUMP_END("q"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_bicg(int m, int n, + DATA_TYPE POLYBENCH_2D(A,N,M,n,m), + DATA_TYPE POLYBENCH_1D(s,M,m), + DATA_TYPE POLYBENCH_1D(q,N,n), + DATA_TYPE POLYBENCH_1D(p,M,m), + DATA_TYPE POLYBENCH_1D(r,N,n)) +{ + int i, j; + +#pragma scop + for (i = 0; i < _PB_M; i++) + s[i] = 0; + for (i = 0; i < _PB_N; i++) + { + q[i] = SCALAR_VAL(0.0); + for (j = 0; j < _PB_M; j++) + { + s[j] = s[j] + r[i] * A[i][j]; + q[i] = q[i] + A[i][j] * p[j]; + } + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int m = M; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(A, DATA_TYPE, N, M, n, m); + POLYBENCH_1D_ARRAY_DECL(s, DATA_TYPE, M, m); + POLYBENCH_1D_ARRAY_DECL(q, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(p, DATA_TYPE, M, m); + POLYBENCH_1D_ARRAY_DECL(r, DATA_TYPE, N, n); + + /* Initialize array(s). */ + init_array (m, n, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(r), + POLYBENCH_ARRAY(p)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_bicg (m, n, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(s), + POLYBENCH_ARRAY(q), + POLYBENCH_ARRAY(p), + POLYBENCH_ARRAY(r)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(m, n, POLYBENCH_ARRAY(s), POLYBENCH_ARRAY(q))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(s); + POLYBENCH_FREE_ARRAY(q); + POLYBENCH_FREE_ARRAY(p); + POLYBENCH_FREE_ARRAY(r); + + return 0; +} + +// CHECK: func @kernel_bicg(%arg0: i32, %arg1: i32, %arg2: memref<2100x1900xf64>, %arg3: memref<1900xf64>, %arg4: memref<2100xf64>, %arg5: memref<1900xf64>, %arg6: memref<2100xf64>) { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %cst = constant 0.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %1 = sitofp %c0_i32 : i32 to f64 +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: affine.store %1, %arg3[%arg7] : memref<1900xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %2 = index_cast %arg1 : i32 to index +// CHECK-NEXT: affine.for %arg7 = 0 to %2 { +// CHECK-NEXT: affine.store %cst, %arg4[%arg7] : memref<2100xf64> +// CHECK-NEXT: %3 = affine.load %arg6[%arg7] : memref<2100xf64> +// CHECK-NEXT: %4 = affine.load %arg4[%arg7] : memref<2100xf64> +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: %5 = affine.load %arg3[%arg8] : memref<1900xf64> +// CHECK-NEXT: %6 = affine.load %arg2[%arg7, %arg8] : memref<2100x1900xf64> +// CHECK-NEXT: %7 = mulf %3, %6 : f64 +// CHECK-NEXT: %8 = addf %5, %7 : f64 +// CHECK-NEXT: affine.store %8, %arg3[%arg8] : memref<1900xf64> +// CHECK-NEXT: %9 = affine.load %arg2[%arg7, %arg8] : memref<2100x1900xf64> +// CHECK-NEXT: %10 = affine.load %arg5[%arg8] : memref<1900xf64> +// CHECK-NEXT: %11 = mulf %9, %10 : f64 +// CHECK-NEXT: %12 = addf %4, %11 : f64 +// CHECK-NEXT: affine.store %12, %arg4[%arg7] : memref<2100xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/bicg/bicg.h b/mlir-clang/Test/polybench/linear-algebra/kernels/bicg/bicg.h new file mode 100644 index 000000000000..984e955150e6 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/bicg/bicg.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _BICG_H +# define _BICG_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(M) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define M 38 +# define N 42 +# endif + +# ifdef SMALL_DATASET +# define M 116 +# define N 124 +# endif + +# ifdef MEDIUM_DATASET +# define M 390 +# define N 410 +# endif + +# ifdef LARGE_DATASET +# define M 1900 +# define N 2100 +# endif + +# ifdef EXTRALARGE_DATASET +# define M 1800 +# define N 2200 +# endif + + +#endif /* !(M N) */ + +# define _PB_M POLYBENCH_LOOP_BOUND(M,m) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_BICG_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/doitgen/doitgen.c b/mlir-clang/Test/polybench/linear-algebra/kernels/doitgen/doitgen.c new file mode 100644 index 000000000000..a6dfce200cda --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/doitgen/doitgen.c @@ -0,0 +1,173 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* doitgen.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "doitgen.h" + + +/* Array initialization. */ +static +void init_array(int nr, int nq, int np, + DATA_TYPE POLYBENCH_3D(A,NR,NQ,NP,nr,nq,np), + DATA_TYPE POLYBENCH_2D(C4,NP,NP,np,np)) +{ + int i, j, k; + + for (i = 0; i < nr; i++) + for (j = 0; j < nq; j++) + for (k = 0; k < np; k++) + A[i][j][k] = (DATA_TYPE) ((i*j + k)%np) / np; + for (i = 0; i < np; i++) + for (j = 0; j < np; j++) + C4[i][j] = (DATA_TYPE) (i*j % np) / np; +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int nr, int nq, int np, + DATA_TYPE POLYBENCH_3D(A,NR,NQ,NP,nr,nq,np)) +{ + int i, j, k; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("A"); + for (i = 0; i < nr; i++) + for (j = 0; j < nq; j++) + for (k = 0; k < np; k++) { + if ((i*nq*np+j*np+k) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, A[i][j][k]); + } + POLYBENCH_DUMP_END("A"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +void kernel_doitgen(int nr, int nq, int np, + DATA_TYPE POLYBENCH_3D(A,NR,NQ,NP,nr,nq,np), + DATA_TYPE POLYBENCH_2D(C4,NP,NP,np,np), + DATA_TYPE POLYBENCH_1D(sum,NP,np)) +{ + int r, q, p, s; + +#pragma scop + for (r = 0; r < _PB_NR; r++) + for (q = 0; q < _PB_NQ; q++) { + for (p = 0; p < _PB_NP; p++) { + sum[p] = SCALAR_VAL(0.0); + for (s = 0; s < _PB_NP; s++) + sum[p] += A[r][q][s] * C4[s][p]; + } + for (p = 0; p < _PB_NP; p++) + A[r][q][p] = sum[p]; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int nr = NR; + int nq = NQ; + int np = NP; + + /* Variable declaration/allocation. */ + POLYBENCH_3D_ARRAY_DECL(A,DATA_TYPE,NR,NQ,NP,nr,nq,np); + POLYBENCH_1D_ARRAY_DECL(sum,DATA_TYPE,NP,np); + POLYBENCH_2D_ARRAY_DECL(C4,DATA_TYPE,NP,NP,np,np); + + /* Initialize array(s). */ + init_array (nr, nq, np, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(C4)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_doitgen (nr, nq, np, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(C4), + POLYBENCH_ARRAY(sum)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(nr, nq, np, POLYBENCH_ARRAY(A))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(sum); + POLYBENCH_FREE_ARRAY(C4); + + return 0; +} + +// CHECK: func @kernel_doitgen(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: memref<150x140x160xf64>, %arg4: memref<160x160xf64>, %arg5: memref<160xf64>) { +// CHECK-NEXT: %cst = constant 0.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %2 = index_cast %arg2 : i32 to index +// CHECK-NEXT: affine.for %arg6 = 0 to %0 { +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: affine.for %arg8 = 0 to %2 { +// CHECK-NEXT: affine.store %cst, %arg5[%arg8] : memref<160xf64> +// CHECK-NEXT: %3 = affine.load %arg5[%arg8] : memref<160xf64> +// CHECK-NEXT: affine.for %arg9 = 0 to %2 { +// CHECK-NEXT: %4 = affine.load %arg3[%arg6, %arg7, %arg9] : memref<150x140x160xf64> +// CHECK-NEXT: %5 = affine.load %arg4[%arg9, %arg8] : memref<160x160xf64> +// CHECK-NEXT: %6 = mulf %4, %5 : f64 +// CHECK-NEXT: %7 = addf %3, %6 : f64 +// CHECK-NEXT: affine.store %7, %arg5[%arg8] : memref<160xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg8 = 0 to %2 { +// CHECK-NEXT: %3 = affine.load %arg5[%arg8] : memref<160xf64> +// CHECK-NEXT: affine.store %3, %arg3[%arg6, %arg7, %arg8] : memref<150x140x160xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/doitgen/doitgen.h b/mlir-clang/Test/polybench/linear-algebra/kernels/doitgen/doitgen.h new file mode 100644 index 000000000000..197abef57bb6 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/doitgen/doitgen.h @@ -0,0 +1,86 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _DOITGEN_H +# define _DOITGEN_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(NQ) && !defined(NR) && !defined(NP) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define NQ 8 +# define NR 10 +# define NP 12 +# endif + +# ifdef SMALL_DATASET +# define NQ 20 +# define NR 25 +# define NP 30 +# endif + +# ifdef MEDIUM_DATASET +# define NQ 40 +# define NR 50 +# define NP 60 +# endif + +# ifdef LARGE_DATASET +# define NQ 140 +# define NR 150 +# define NP 160 +# endif + +# ifdef EXTRALARGE_DATASET +# define NQ 220 +# define NR 250 +# define NP 270 +# endif + + +#endif /* !(NQ NR NP) */ + +# define _PB_NQ POLYBENCH_LOOP_BOUND(NQ,nq) +# define _PB_NR POLYBENCH_LOOP_BOUND(NR,nr) +# define _PB_NP POLYBENCH_LOOP_BOUND(NP,np) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_DOITGEN_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/mvt/mvt.c b/mlir-clang/Test/polybench/linear-algebra/kernels/mvt/mvt.c new file mode 100644 index 000000000000..d5887854235d --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/mvt/mvt.c @@ -0,0 +1,190 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* mvt.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "mvt.h" + + +/* Array initialization. */ +static +void init_array(int n, + DATA_TYPE POLYBENCH_1D(x1,N,n), + DATA_TYPE POLYBENCH_1D(x2,N,n), + DATA_TYPE POLYBENCH_1D(y_1,N,n), + DATA_TYPE POLYBENCH_1D(y_2,N,n), + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + { + x1[i] = (DATA_TYPE) (i % n) / n; + x2[i] = (DATA_TYPE) ((i + 1) % n) / n; + y_1[i] = (DATA_TYPE) ((i + 3) % n) / n; + y_2[i] = (DATA_TYPE) ((i + 4) % n) / n; + for (j = 0; j < n; j++) + A[i][j] = (DATA_TYPE) (i*j % n) / n; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_1D(x1,N,n), + DATA_TYPE POLYBENCH_1D(x2,N,n)) + +{ + int i; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("x1"); + for (i = 0; i < n; i++) { + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, x1[i]); + } + POLYBENCH_DUMP_END("x1"); + + POLYBENCH_DUMP_BEGIN("x2"); + for (i = 0; i < n; i++) { + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, x2[i]); + } + POLYBENCH_DUMP_END("x2"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_mvt(int n, + DATA_TYPE POLYBENCH_1D(x1,N,n), + DATA_TYPE POLYBENCH_1D(x2,N,n), + DATA_TYPE POLYBENCH_1D(y_1,N,n), + DATA_TYPE POLYBENCH_1D(y_2,N,n), + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) +{ + int i, j; + +#pragma scop + for (i = 0; i < _PB_N; i++) + for (j = 0; j < _PB_N; j++) + x1[i] = x1[i] + A[i][j] * y_1[j]; + for (i = 0; i < _PB_N; i++) + for (j = 0; j < _PB_N; j++) + x2[i] = x2[i] + A[j][i] * y_2[j]; +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(A, DATA_TYPE, N, N, n, n); + POLYBENCH_1D_ARRAY_DECL(x1, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(x2, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(y_1, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(y_2, DATA_TYPE, N, n); + + + /* Initialize array(s). */ + init_array (n, + POLYBENCH_ARRAY(x1), + POLYBENCH_ARRAY(x2), + POLYBENCH_ARRAY(y_1), + POLYBENCH_ARRAY(y_2), + POLYBENCH_ARRAY(A)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_mvt (n, + POLYBENCH_ARRAY(x1), + POLYBENCH_ARRAY(x2), + POLYBENCH_ARRAY(y_1), + POLYBENCH_ARRAY(y_2), + POLYBENCH_ARRAY(A)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(x1), POLYBENCH_ARRAY(x2))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(x1); + POLYBENCH_FREE_ARRAY(x2); + POLYBENCH_FREE_ARRAY(y_1); + POLYBENCH_FREE_ARRAY(y_2); + + return 0; +} + +// CHECK: func @kernel_mvt(%arg0: i32, %arg1: memref<2000xf64>, %arg2: memref<2000xf64>, %arg3: memref<2000xf64>, %arg4: memref<2000xf64>, %arg5: memref<2000x2000xf64>) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg6 = 0 to %0 { +// CHECK-NEXT: %1 = affine.load %arg1[%arg6] : memref<2000xf64> +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: %2 = affine.load %arg5[%arg6, %arg7] : memref<2000x2000xf64> +// CHECK-NEXT: %3 = affine.load %arg3[%arg7] : memref<2000xf64> +// CHECK-NEXT: %4 = mulf %2, %3 : f64 +// CHECK-NEXT: %5 = addf %1, %4 : f64 +// CHECK-NEXT: affine.store %5, %arg1[%arg6] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg6 = 0 to %0 { +// CHECK-NEXT: %1 = affine.load %arg2[%arg6] : memref<2000xf64> +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: %2 = affine.load %arg5[%arg7, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: %3 = affine.load %arg4[%arg7] : memref<2000xf64> +// CHECK-NEXT: %4 = mulf %2, %3 : f64 +// CHECK-NEXT: %5 = addf %1, %4 : f64 +// CHECK-NEXT: affine.store %5, %arg2[%arg6] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT:} + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/kernels/mvt/mvt.h b/mlir-clang/Test/polybench/linear-algebra/kernels/mvt/mvt.h new file mode 100644 index 000000000000..c41323b739a8 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/kernels/mvt/mvt.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _MVT_H +# define _MVT_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 40 +# endif + +# ifdef SMALL_DATASET +# define N 120 +# endif + +# ifdef MEDIUM_DATASET +# define N 400 +# endif + +# ifdef LARGE_DATASET +# define N 2000 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 4000 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_MVT_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/cholesky/cholesky.c b/mlir-clang/Test/polybench/linear-algebra/solvers/cholesky/cholesky.c new file mode 100644 index 000000000000..d48675871148 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/cholesky/cholesky.c @@ -0,0 +1,189 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 -lm && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 -lm && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 -lm && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* cholesky.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "cholesky.h" + + +/* Array initialization. */ +static +void init_array(int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + { + for (j = 0; j <= i; j++) + A[i][j] = (DATA_TYPE)(-j % n) / n + 1; + for (j = i+1; j < n; j++) { + A[i][j] = 0; + } + A[i][i] = 1; + } + + /* Make the matrix positive semi-definite. */ + int r,s,t; + POLYBENCH_2D_ARRAY_DECL(B, DATA_TYPE, N, N, n, n); + for (r = 0; r < n; ++r) + for (s = 0; s < n; ++s) + (POLYBENCH_ARRAY(B))[r][s] = 0; + for (t = 0; t < n; ++t) + for (r = 0; r < n; ++r) + for (s = 0; s < n; ++s) + (POLYBENCH_ARRAY(B))[r][s] += A[r][t] * A[s][t]; + for (r = 0; r < n; ++r) + for (s = 0; s < n; ++s) + A[r][s] = (POLYBENCH_ARRAY(B))[r][s]; + POLYBENCH_FREE_ARRAY(B); + +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) + +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("A"); + for (i = 0; i < n; i++) + for (j = 0; j <= i; j++) { + if ((i * n + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, A[i][j]); + } + POLYBENCH_DUMP_END("A"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_cholesky(int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) +{ + int i, j, k; + + +#pragma scop + for (i = 0; i < _PB_N; i++) { + //j (d0)> +// CHECK: func @kernel_cholesky(%arg0: i32, %arg1: memref<2000x2000xf64>) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg2 = 0 to %0 { +// CHECK-NEXT: affine.for %arg3 = 0 to #map(%arg2) { +// CHECK-NEXT: %4 = affine.load %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: affine.for %arg4 = 0 to #map(%arg3) { +// CHECK-NEXT: %8 = affine.load %arg1[%arg2, %arg4] : memref<2000x2000xf64> +// CHECK-NEXT: %9 = affine.load %arg1[%arg3, %arg4] : memref<2000x2000xf64> +// CHECK-NEXT: %10 = mulf %8, %9 : f64 +// CHECK-NEXT: %11 = subf %4, %10 : f64 +// CHECK-NEXT: affine.store %11, %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %5 = affine.load %arg1[%arg3, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: %6 = affine.load %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: %7 = divf %6, %5 : f64 +// CHECK-NEXT: affine.store %7, %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %1 = affine.load %arg1[%arg2, %arg2] : memref<2000x2000xf64> +// CHECK-NEXT: affine.for %arg3 = 0 to #map(%arg2) { +// CHECK-NEXT: %4 = affine.load %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: %5 = affine.load %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: %6 = mulf %4, %5 : f64 +// CHECK-NEXT: %7 = subf %1, %6 : f64 +// CHECK-NEXT: affine.store %7, %arg1[%arg2, %arg2] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %2 = affine.load %arg1[%arg2, %arg2] : memref<2000x2000xf64> +// CHECK-NEXT: %3 = sqrt %2 : f64 +// CHECK-NEXT: affine.store %3, %arg1[%arg2, %arg2] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/cholesky/cholesky.h b/mlir-clang/Test/polybench/linear-algebra/solvers/cholesky/cholesky.h new file mode 100644 index 000000000000..81b277dc0ede --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/cholesky/cholesky.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _CHOLESKY_H +# define _CHOLESKY_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 40 +# endif + +# ifdef SMALL_DATASET +# define N 120 +# endif + +# ifdef MEDIUM_DATASET +# define N 400 +# endif + +# ifdef LARGE_DATASET +# define N 2000 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 4000 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_CHOLESKY_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/durbin/durbin.c b/mlir-clang/Test/polybench/linear-algebra/solvers/durbin/durbin.c new file mode 100644 index 000000000000..38684afb32a6 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/durbin/durbin.c @@ -0,0 +1,208 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* durbin.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "durbin.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE POLYBENCH_1D(r,N,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + { + r[i] = (n+1-i); + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_1D(y,N,n)) + +{ + int i; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("y"); + for (i = 0; i < n; i++) { + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, y[i]); + } + POLYBENCH_DUMP_END("y"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_durbin(int n, + DATA_TYPE POLYBENCH_1D(r,N,n), + DATA_TYPE POLYBENCH_1D(y,N,n)) +{ + DATA_TYPE z[N]; + DATA_TYPE alpha; + DATA_TYPE beta; + DATA_TYPE sum; + + int i,k; + +#pragma scop + y[0] = -r[0]; + beta = SCALAR_VAL(1.0); + alpha = -r[0]; + + for (k = 1; k < _PB_N; k++) { + beta = (1-alpha*alpha)*beta; + sum = SCALAR_VAL(0.0); + for (i=0; i (d0)> +// CHECK: func @kernel_durbin(%arg0: i32, %arg1: memref<2000xf64>, %arg2: memref<2000xf64>) { +// CHECK-NEXT: %c0 = constant 0 : index +// CHECK-NEXT: %cst = constant 1.000000e+00 : f64 +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %cst_0 = constant 0.000000e+00 : f64 +// CHECK-NEXT: %0 = alloca() : memref<2000xf64> +// CHECK-NEXT: %1 = alloca() : memref<1xf64> +// CHECK-NEXT: %2 = alloca() : memref<1xf64> +// CHECK-NEXT: %3 = alloca() : memref<1xf64> +// CHECK-NEXT: %4 = load %arg1[%c0] : memref<2000xf64> +// CHECK-NEXT: %5 = negf %4 : f64 +// CHECK-NEXT: store %5, %arg2[%c0] : memref<2000xf64> +// CHECK-NEXT: store %cst, %2[%c0] : memref<1xf64> +// CHECK-NEXT: %6 = load %arg1[%c0] : memref<2000xf64> +// CHECK-NEXT: %7 = negf %6 : f64 +// CHECK-NEXT: store %7, %1[%c0] : memref<1xf64> +// CHECK-NEXT: %8 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %9 = sitofp %c1_i32 : i32 to f64 +// CHECK-NEXT: %10 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: %11 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: %12 = mulf %10, %11 : f64 +// CHECK-NEXT: %13 = subf %9, %12 : f64 +// CHECK-NEXT: %14 = load %2[%c0] : memref<1xf64> +// CHECK-NEXT: %15 = mulf %13, %14 : f64 +// CHECK-NEXT: store %15, %2[%c0] : memref<1xf64> +// CHECK-NEXT: store %cst_0, %3[%c0] : memref<1xf64> +// CHECK-NEXT: %16 = load %3[%c0] : memref<1xf64> +// CHECK-NEXT: %17 = load %3[%c0] : memref<1xf64> +// CHECK-NEXT: affine.for %arg3 = 1 to %8 { +// CHECK-NEXT: affine.for %arg4 = 0 to #map(%arg3) { +// CHECK-NEXT: %22 = affine.load %arg1[%arg3 - %arg4 - 1] : memref<2000xf64> +// CHECK-NEXT: %23 = affine.load %arg2[%arg4] : memref<2000xf64> +// CHECK-NEXT: %24 = mulf %22, %23 : f64 +// CHECK-NEXT: %25 = addf %16, %24 : f64 +// CHECK-NEXT: affine.store %25, %3[0] : memref<1xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %18 = affine.load %arg1[%arg3] : memref<2000xf64> +// CHECK-NEXT: %19 = addf %18, %17 : f64 +// CHECK-NEXT: %20 = negf %19 : f64 +// CHECK-NEXT: %21 = divf %20, %15 : f64 +// CHECK-NEXT: affine.store %21, %1[0] : memref<1xf64> +// CHECK-NEXT: affine.for %arg4 = 0 to #map(%arg3) { +// CHECK-NEXT: %22 = affine.load %arg2[%arg4] : memref<2000xf64> +// CHECK-NEXT: %23 = affine.load %arg2[%arg3 - %arg4 - 1] : memref<2000xf64> +// CHECK-NEXT: %24 = mulf %21, %23 : f64 +// CHECK-NEXT: %25 = addf %22, %24 : f64 +// CHECK-NEXT: affine.store %25, %0[%arg4] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg4 = 0 to #map(%arg3) { +// CHECK-NEXT: %22 = affine.load %0[%arg4] : memref<2000xf64> +// CHECK-NEXT: affine.store %22, %arg2[%arg4] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %21, %arg2[%arg3] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/durbin/durbin.h b/mlir-clang/Test/polybench/linear-algebra/solvers/durbin/durbin.h new file mode 100644 index 000000000000..dfa1a8400dd9 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/durbin/durbin.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _DURBIN_H +# define _DURBIN_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 40 +# endif + +# ifdef SMALL_DATASET +# define N 120 +# endif + +# ifdef MEDIUM_DATASET +# define N 400 +# endif + +# ifdef LARGE_DATASET +# define N 2000 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 4000 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_DURBIN_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/gramschmidt/gramschmidt.c b/mlir-clang/Test/polybench/linear-algebra/solvers/gramschmidt/gramschmidt.c new file mode 100644 index 000000000000..067c648eecd5 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/gramschmidt/gramschmidt.c @@ -0,0 +1,218 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 -lm && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 -lm && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 -lm && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* gramschmidt.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "gramschmidt.h" + + +/* Array initialization. */ +static +void init_array(int m, int n, + DATA_TYPE POLYBENCH_2D(A,M,N,m,n), + DATA_TYPE POLYBENCH_2D(R,N,N,n,n), + DATA_TYPE POLYBENCH_2D(Q,M,N,m,n)) +{ + int i, j; + + for (i = 0; i < m; i++) + for (j = 0; j < n; j++) { + A[i][j] = (((DATA_TYPE) ((i*j) % m) / m )*100) + 10; + Q[i][j] = 0.0; + } + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + R[i][j] = 0.0; +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int m, int n, + DATA_TYPE POLYBENCH_2D(A,M,N,m,n), + DATA_TYPE POLYBENCH_2D(R,N,N,n,n), + DATA_TYPE POLYBENCH_2D(Q,M,N,m,n)) +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("R"); + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + if ((i*n+j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, R[i][j]); + } + POLYBENCH_DUMP_END("R"); + + POLYBENCH_DUMP_BEGIN("Q"); + for (i = 0; i < m; i++) + for (j = 0; j < n; j++) { + if ((i*n+j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, Q[i][j]); + } + POLYBENCH_DUMP_END("Q"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +/* QR Decomposition with Modified Gram Schmidt: + http://www.inf.ethz.ch/personal/gander/ */ +static +void kernel_gramschmidt(int m, int n, + DATA_TYPE POLYBENCH_2D(A,M,N,m,n), + DATA_TYPE POLYBENCH_2D(R,N,N,n,n), + DATA_TYPE POLYBENCH_2D(Q,M,N,m,n)) +{ + int i, j, k; + + DATA_TYPE nrm; + +#pragma scop + for (k = 0; k < _PB_N; k++) + { + nrm = SCALAR_VAL(0.0); + for (i = 0; i < _PB_M; i++) + nrm += A[i][k] * A[i][k]; + R[k][k] = SQRT_FUN(nrm); + for (i = 0; i < _PB_M; i++) + Q[i][k] = A[i][k] / R[k][k]; + for (j = k + 1; j < _PB_N; j++) + { + R[k][j] = SCALAR_VAL(0.0); + for (i = 0; i < _PB_M; i++) + R[k][j] += Q[i][k] * A[i][j]; + for (i = 0; i < _PB_M; i++) + A[i][j] = A[i][j] - Q[i][k] * R[k][j]; + } + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int m = M; + int n = N; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(A,DATA_TYPE,M,N,m,n); + POLYBENCH_2D_ARRAY_DECL(R,DATA_TYPE,N,N,n,n); + POLYBENCH_2D_ARRAY_DECL(Q,DATA_TYPE,M,N,m,n); + + /* Initialize array(s). */ + init_array (m, n, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(R), + POLYBENCH_ARRAY(Q)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_gramschmidt (m, n, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(R), + POLYBENCH_ARRAY(Q)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(m, n, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(R), POLYBENCH_ARRAY(Q))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(R); + POLYBENCH_FREE_ARRAY(Q); + + return 0; +} + +// CHECK: #map = affine_map<(d0) -> (d0 + 1)> +// CHECK: func @kernel_gramschmidt(%arg0: i32, %arg1: i32, %arg2: memref<1000x1200xf64>, %arg3: memref<1200x1200xf64>, %arg4: memref<1000x1200xf64>) { +// CHECK-NEXT: %c0 = constant 0 : index +// CHECK-NEXT: %cst = constant 0.000000e+00 : f64 +// CHECK-NEXT: %0 = alloca() : memref<1xf64> +// CHECK-NEXT: %1 = index_cast %arg1 : i32 to index +// CHECK-NEXT: store %cst, %0[%c0] : memref<1xf64> +// CHECK-NEXT: %2 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %3 = load %0[%c0] : memref<1xf64> +// CHECK-NEXT: %4 = load %0[%c0] : memref<1xf64> +// CHECK-NEXT: %5 = sqrt %4 : f64 +// CHECK-NEXT: affine.for %arg5 = 0 to %1 { +// CHECK-NEXT: affine.for %arg6 = 0 to %2 { +// CHECK-NEXT: %7 = affine.load %arg2[%arg6, %arg5] : memref<1000x1200xf64> +// CHECK-NEXT: %8 = affine.load %arg2[%arg6, %arg5] : memref<1000x1200xf64> +// CHECK-NEXT: %9 = mulf %7, %8 : f64 +// CHECK-NEXT: %10 = addf %3, %9 : f64 +// CHECK-NEXT: affine.store %10, %0[0] : memref<1xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %5, %arg3[%arg5, %arg5] : memref<1200x1200xf64> +// CHECK-NEXT: %6 = affine.load %arg3[%arg5, %arg5] : memref<1200x1200xf64> +// CHECK-NEXT: affine.for %arg6 = 0 to %2 { +// CHECK-NEXT: %7 = affine.load %arg2[%arg6, %arg5] : memref<1000x1200xf64> +// CHECK-NEXT: %8 = divf %7, %6 : f64 +// CHECK-NEXT: affine.store %8, %arg4[%arg6, %arg5] : memref<1000x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg6 = #map(%arg5) to %1 { +// CHECK-NEXT: affine.store %cst, %arg3[%arg5, %arg6] : memref<1200x1200xf64> +// CHECK-NEXT: %7 = affine.load %arg3[%arg5, %arg6] : memref<1200x1200xf64> +// CHECK-NEXT: affine.for %arg7 = 0 to %2 { +// CHECK-NEXT: %9 = affine.load %arg4[%arg7, %arg5] : memref<1000x1200xf64> +// CHECK-NEXT: %10 = affine.load %arg2[%arg7, %arg6] : memref<1000x1200xf64> +// CHECK-NEXT: %11 = mulf %9, %10 : f64 +// CHECK-NEXT: %12 = addf %7, %11 : f64 +// CHECK-NEXT: affine.store %12, %arg3[%arg5, %arg6] : memref<1200x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %8 = affine.load %arg3[%arg5, %arg6] : memref<1200x1200xf64> +// CHECK-NEXT: affine.for %arg7 = 0 to %2 { +// CHECK-NEXT: %9 = affine.load %arg2[%arg7, %arg6] : memref<1000x1200xf64> +// CHECK-NEXT: %10 = affine.load %arg4[%arg7, %arg5] : memref<1000x1200xf64> +// CHECK-NEXT: %11 = mulf %10, %8 : f64 +// CHECK-NEXT: %12 = subf %9, %11 : f64 +// CHECK-NEXT: affine.store %12, %arg2[%arg7, %arg6] : memref<1000x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/gramschmidt/gramschmidt.h b/mlir-clang/Test/polybench/linear-algebra/solvers/gramschmidt/gramschmidt.h new file mode 100644 index 000000000000..e20ddb2d8d81 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/gramschmidt/gramschmidt.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _GRAMSCHMIDT_H +# define _GRAMSCHMIDT_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(M) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define M 20 +# define N 30 +# endif + +# ifdef SMALL_DATASET +# define M 60 +# define N 80 +# endif + +# ifdef MEDIUM_DATASET +# define M 200 +# define N 240 +# endif + +# ifdef LARGE_DATASET +# define M 1000 +# define N 1200 +# endif + +# ifdef EXTRALARGE_DATASET +# define M 2000 +# define N 2600 +# endif + + +#endif /* !(M N) */ + +# define _PB_M POLYBENCH_LOOP_BOUND(M,m) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_GRAMSCHMIDT_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/lu/lu.c b/mlir-clang/Test/polybench/linear-algebra/solvers/lu/lu.c new file mode 100644 index 000000000000..1c78b5cd0d1a --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/lu/lu.c @@ -0,0 +1,186 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* lu.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "lu.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + { + for (j = 0; j <= i; j++) + A[i][j] = (DATA_TYPE)(-j % n) / n + 1; + for (j = i+1; j < n; j++) { + A[i][j] = 0; + } + A[i][i] = 1; + } + + /* Make the matrix positive semi-definite. */ + /* not necessary for LU, but using same code as cholesky */ + int r,s,t; + POLYBENCH_2D_ARRAY_DECL(B, DATA_TYPE, N, N, n, n); + for (r = 0; r < n; ++r) + for (s = 0; s < n; ++s) + (POLYBENCH_ARRAY(B))[r][s] = 0; + for (t = 0; t < n; ++t) + for (r = 0; r < n; ++r) + for (s = 0; s < n; ++s) + (POLYBENCH_ARRAY(B))[r][s] += A[r][t] * A[s][t]; + for (r = 0; r < n; ++r) + for (s = 0; s < n; ++s) + A[r][s] = (POLYBENCH_ARRAY(B))[r][s]; + POLYBENCH_FREE_ARRAY(B); + +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) + +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("A"); + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + if ((i * n + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, A[i][j]); + } + POLYBENCH_DUMP_END("A"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_lu(int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) +{ + int i, j, k; + +#pragma scop + for (i = 0; i < _PB_N; i++) { + for (j = 0; j (d0)> +// CHECK: func @kernel_lu(%arg0: i32, %arg1: memref<2000x2000xf64>) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg2 = 0 to %0 { +// CHECK-NEXT: affine.for %arg3 = 0 to #map(%arg2) { +// CHECK-NEXT: %1 = affine.load %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: affine.for %arg4 = 0 to #map(%arg3) { +// CHECK-NEXT: %5 = affine.load %arg1[%arg2, %arg4] : memref<2000x2000xf64> +// CHECK-NEXT: %6 = affine.load %arg1[%arg4, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: %7 = mulf %5, %6 : f64 +// CHECK-NEXT: %8 = subf %1, %7 : f64 +// CHECK-NEXT: affine.store %8, %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %2 = affine.load %arg1[%arg3, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: %3 = affine.load %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: %4 = divf %3, %2 : f64 +// CHECK-NEXT: affine.store %4, %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg3 = #map(%arg2) to %0 { +// CHECK-NEXT: %1 = affine.load %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: affine.for %arg4 = 0 to #map(%arg2) { +// CHECK-NEXT: %2 = affine.load %arg1[%arg2, %arg4] : memref<2000x2000xf64> +// CHECK-NEXT: %3 = affine.load %arg1[%arg4, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: %4 = mulf %2, %3 : f64 +// CHECK-NEXT: %5 = subf %1, %4 : f64 +// CHECK-NEXT: affine.store %5, %arg1[%arg2, %arg3] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/lu/lu.h b/mlir-clang/Test/polybench/linear-algebra/solvers/lu/lu.h new file mode 100644 index 000000000000..3899acccaad3 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/lu/lu.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _LU_H +# define _LU_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 40 +# endif + +# ifdef SMALL_DATASET +# define N 120 +# endif + +# ifdef MEDIUM_DATASET +# define N 400 +# endif + +# ifdef LARGE_DATASET +# define N 2000 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 4000 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_LU_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/ludcmp/ludcmp.c b/mlir-clang/Test/polybench/linear-algebra/solvers/ludcmp/ludcmp.c new file mode 100644 index 000000000000..57e0b0377d75 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/ludcmp/ludcmp.c @@ -0,0 +1,273 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* ludcmp.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "ludcmp.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n), + DATA_TYPE POLYBENCH_1D(b,N,n), + DATA_TYPE POLYBENCH_1D(x,N,n), + DATA_TYPE POLYBENCH_1D(y,N,n)) +{ + int i, j; + DATA_TYPE fn = (DATA_TYPE)n; + + for (i = 0; i < n; i++) + { + x[i] = 0; + y[i] = 0; + b[i] = (i+1)/fn/2.0 + 4; + } + + for (i = 0; i < n; i++) + { + for (j = 0; j <= i; j++) + A[i][j] = (DATA_TYPE)(-j % n) / n + 1; + for (j = i+1; j < n; j++) { + A[i][j] = 0; + } + A[i][i] = 1; + } + + /* Make the matrix positive semi-definite. */ + /* not necessary for LU, but using same code as cholesky */ + int r,s,t; + POLYBENCH_2D_ARRAY_DECL(B, DATA_TYPE, N, N, n, n); + for (r = 0; r < n; ++r) + for (s = 0; s < n; ++s) + (POLYBENCH_ARRAY(B))[r][s] = 0; + for (t = 0; t < n; ++t) + for (r = 0; r < n; ++r) + for (s = 0; s < n; ++s) + (POLYBENCH_ARRAY(B))[r][s] += A[r][t] * A[s][t]; + for (r = 0; r < n; ++r) + for (s = 0; s < n; ++s) + A[r][s] = (POLYBENCH_ARRAY(B))[r][s]; + POLYBENCH_FREE_ARRAY(B); + +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_1D(x,N,n)) + +{ + int i; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("x"); + for (i = 0; i < n; i++) { + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, x[i]); + } + POLYBENCH_DUMP_END("x"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_ludcmp(int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n), + DATA_TYPE POLYBENCH_1D(b,N,n), + DATA_TYPE POLYBENCH_1D(x,N,n), + DATA_TYPE POLYBENCH_1D(y,N,n)) +{ + int i, j, k; + + DATA_TYPE w; + +#pragma scop + for (i = 0; i < _PB_N; i++) { + for (j = 0; j =0; i--) { + w = y[i]; + for (j = i+1; j < _PB_N; j++) + w -= A[i][j] * x[j]; + x[i] = w / A[i][i]; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(A, DATA_TYPE, N, N, n, n); + POLYBENCH_1D_ARRAY_DECL(b, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(x, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(y, DATA_TYPE, N, n); + + + /* Initialize array(s). */ + init_array (n, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(b), + POLYBENCH_ARRAY(x), + POLYBENCH_ARRAY(y)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_ludcmp (n, + POLYBENCH_ARRAY(A), + POLYBENCH_ARRAY(b), + POLYBENCH_ARRAY(x), + POLYBENCH_ARRAY(y)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(x))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(b); + POLYBENCH_FREE_ARRAY(x); + POLYBENCH_FREE_ARRAY(y); + + return 0; +} + +// CHECK: #map0 = affine_map<(d0) -> (d0)> +// CHECK-NEXT: #map1 = affine_map<(d0)[s0] -> (-d0 + s0)> +// CHECK: func @kernel_ludcmp(%arg0: i32, %arg1: memref<2000x2000xf64>, %arg2: memref<2000xf64>, %arg3: memref<2000xf64>, %arg4: memref<2000xf64>) { +// CHECK-NEXT: %c0 = constant 0 : index +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %1 = alloca() : memref<1xf64> +// CHECK-NEXT: %2 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: %3 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: %4 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: %5 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: affine.for %arg5 = 0 to %0 { +// CHECK-NEXT: affine.for %arg6 = 0 to #map0(%arg5) { +// CHECK-NEXT: %10 = affine.load %arg1[%arg5, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: affine.store %10, %1[0] : memref<1xf64> +// CHECK-NEXT: affine.for %arg7 = 0 to #map0(%arg6) { +// CHECK-NEXT: %13 = affine.load %arg1[%arg5, %arg7] : memref<2000x2000xf64> +// CHECK-NEXT: %14 = affine.load %arg1[%arg7, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: %15 = mulf %13, %14 : f64 +// CHECK-NEXT: %16 = subf %2, %15 : f64 +// CHECK-NEXT: affine.store %16, %1[0] : memref<1xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %11 = affine.load %arg1[%arg6, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: %12 = divf %3, %11 : f64 +// CHECK-NEXT: affine.store %12, %arg1[%arg5, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg6 = #map0(%arg5) to %0 { +// CHECK-NEXT: %10 = affine.load %arg1[%arg5, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: affine.store %10, %1[0] : memref<1xf64> +// CHECK-NEXT: affine.for %arg7 = 0 to #map0(%arg5) { +// CHECK-NEXT: %11 = affine.load %arg1[%arg5, %arg7] : memref<2000x2000xf64> +// CHECK-NEXT: %12 = affine.load %arg1[%arg7, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: %13 = mulf %11, %12 : f64 +// CHECK-NEXT: %14 = subf %4, %13 : f64 +// CHECK-NEXT: affine.store %14, %1[0] : memref<1xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %5, %arg1[%arg5, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: %6 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: %7 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: affine.for %arg5 = 0 to %0 { +// CHECK-NEXT: %10 = affine.load %arg2[%arg5] : memref<2000xf64> +// CHECK-NEXT: affine.store %10, %1[0] : memref<1xf64> +// CHECK-NEXT: affine.for %arg6 = 0 to #map0(%arg5) { +// CHECK-NEXT: %11 = affine.load %arg1[%arg5, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: %12 = affine.load %arg4[%arg6] : memref<2000xf64> +// CHECK-NEXT: %13 = mulf %11, %12 : f64 +// CHECK-NEXT: %14 = subf %6, %13 : f64 +// CHECK-NEXT: affine.store %14, %1[0] : memref<1xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %7, %arg4[%arg5] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %8 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: %9 = load %1[%c0] : memref<1xf64> +// CHECK-NEXT: affine.for %arg5 = 0 to %0 { +// CHECK-NEXT: %10 = affine.load %arg4[-%arg5 + symbol(%0) - 1] : memref<2000xf64> +// CHECK-NEXT: affine.store %10, %1[0] : memref<1xf64> +// CHECK-NEXT: affine.for %arg6 = #map1(%arg5)[%0] to %0 { +// CHECK-NEXT: %13 = affine.load %arg1[-%arg5 + symbol(%0) - 1, %arg6] : memref<2000x2000xf64> +// CHECK-NEXT: %14 = affine.load %arg3[%arg6] : memref<2000xf64> +// CHECK-NEXT: %15 = mulf %13, %14 : f64 +// CHECK-NEXT: %16 = subf %8, %15 : f64 +// CHECK-NEXT: affine.store %16, %1[0] : memref<1xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %11 = affine.load %arg1[-%arg5 + symbol(%0) - 1, -%arg5 + symbol(%0) - 1] : memref<2000x2000xf64> +// CHECK-NEXT: %12 = divf %9, %11 : f64 +// CHECK-NEXT: affine.store %12, %arg3[-%arg5 + symbol(%0) - 1] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/ludcmp/ludcmp.h b/mlir-clang/Test/polybench/linear-algebra/solvers/ludcmp/ludcmp.h new file mode 100644 index 000000000000..01c4f135ccd1 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/ludcmp/ludcmp.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _LUDCMP_H +# define _LUDCMP_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 40 +# endif + +# ifdef SMALL_DATASET +# define N 120 +# endif + +# ifdef MEDIUM_DATASET +# define N 400 +# endif + +# ifdef LARGE_DATASET +# define N 2000 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 4000 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_LUDCMP_H */ diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/trisolv/trisolv.c b/mlir-clang/Test/polybench/linear-algebra/solvers/trisolv/trisolv.c new file mode 100644 index 000000000000..d48a6551a330 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/trisolv/trisolv.c @@ -0,0 +1,161 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* trisolv.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "trisolv.h" + + +/* Array initialization. */ +static +void init_array(int n, + DATA_TYPE POLYBENCH_2D(L,N,N,n,n), + DATA_TYPE POLYBENCH_1D(x,N,n), + DATA_TYPE POLYBENCH_1D(b,N,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + { + x[i] = - 999; + b[i] = i ; + for (j = 0; j <= i; j++) + L[i][j] = (DATA_TYPE) (i+n-j+1)*2/n; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_1D(x,N,n)) + +{ + int i; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("x"); + for (i = 0; i < n; i++) { + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, x[i]); + if (i % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + } + POLYBENCH_DUMP_END("x"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_trisolv(int n, + DATA_TYPE POLYBENCH_2D(L,N,N,n,n), + DATA_TYPE POLYBENCH_1D(x,N,n), + DATA_TYPE POLYBENCH_1D(b,N,n)) +{ + int i, j; + +#pragma scop + for (i = 0; i < _PB_N; i++) + { + x[i] = b[i]; + for (j = 0; j (d0)> +// CHECK: func @kernel_trisolv(%arg0: i32, %arg1: memref<2000x2000xf64>, %arg2: memref<2000xf64>, %arg3: memref<2000xf64>) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg4 = 0 to %0 { +// CHECK-NEXT: %1 = affine.load %arg3[%arg4] : memref<2000xf64> +// CHECK-NEXT: affine.store %1, %arg2[%arg4] : memref<2000xf64> +// CHECK-NEXT: %2 = affine.load %arg2[%arg4] : memref<2000xf64> +// CHECK-NEXT: affine.for %arg5 = 0 to #map(%arg4) { +// CHECK-NEXT: %6 = affine.load %arg1[%arg4, %arg5] : memref<2000x2000xf64> +// CHECK-NEXT: %7 = affine.load %arg2[%arg5] : memref<2000xf64> +// CHECK-NEXT: %8 = mulf %6, %7 : f64 +// CHECK-NEXT: %9 = subf %2, %8 : f64 +// CHECK-NEXT: affine.store %9, %arg2[%arg4] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: %3 = affine.load %arg2[%arg4] : memref<2000xf64> +// CHECK-NEXT: %4 = affine.load %arg1[%arg4, %arg4] : memref<2000x2000xf64> +// CHECK-NEXT: %5 = divf %3, %4 : f64 +// CHECK-NEXT: affine.store %5, %arg2[%arg4] : memref<2000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/linear-algebra/solvers/trisolv/trisolv.h b/mlir-clang/Test/polybench/linear-algebra/solvers/trisolv/trisolv.h new file mode 100644 index 000000000000..37b983e28f08 --- /dev/null +++ b/mlir-clang/Test/polybench/linear-algebra/solvers/trisolv/trisolv.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _TRISOLV_H +# define _TRISOLV_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 40 +# endif + +# ifdef SMALL_DATASET +# define N 120 +# endif + +# ifdef MEDIUM_DATASET +# define N 400 +# endif + +# ifdef LARGE_DATASET +# define N 2000 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 4000 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_TRISOLV_H */ diff --git a/mlir-clang/Test/polybench/medley/deriche/deriche.c b/mlir-clang/Test/polybench/medley/deriche/deriche.c new file mode 100644 index 000000000000..a4c33fc3a947 --- /dev/null +++ b/mlir-clang/Test/polybench/medley/deriche/deriche.c @@ -0,0 +1,334 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm -lm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm -lm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* deriche.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "deriche.h" + + +/* Array initialization. */ +static +void init_array (int w, int h, DATA_TYPE* alpha, + DATA_TYPE POLYBENCH_2D(imgIn,W,H,w,h), + DATA_TYPE POLYBENCH_2D(imgOut,W,H,w,h)) +{ + int i, j; + + *alpha=0.25; //parameter of the filter + + //input should be between 0 and 1 (grayscale image pixel) + for (i = 0; i < w; i++) + for (j = 0; j < h; j++) + imgIn[i][j] = (DATA_TYPE) ((313*i+991*j)%65536) / 65535.0f; +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int w, int h, + DATA_TYPE POLYBENCH_2D(imgOut,W,H,w,h)) + +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("imgOut"); + for (i = 0; i < w; i++) + for (j = 0; j < h; j++) { + if ((i * h + j) % 20 == 0) fprintf(POLYBENCH_DUMP_TARGET, "\n"); + fprintf(POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, imgOut[i][j]); + } + POLYBENCH_DUMP_END("imgOut"); + POLYBENCH_DUMP_FINISH; +} + + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +/* Original code provided by Gael Deest */ +static +void kernel_deriche(int w, int h, DATA_TYPE alpha, + DATA_TYPE POLYBENCH_2D(imgIn, W, H, w, h), + DATA_TYPE POLYBENCH_2D(imgOut, W, H, w, h), + DATA_TYPE POLYBENCH_2D(y1, W, H, w, h), + DATA_TYPE POLYBENCH_2D(y2, W, H, w, h)) { + int i,j; + DATA_TYPE xm1, tm1, ym1, ym2; + DATA_TYPE xp1, xp2; + DATA_TYPE tp1, tp2; + DATA_TYPE yp1, yp2; + + DATA_TYPE k; + DATA_TYPE a1, a2, a3, a4, a5, a6, a7, a8; + DATA_TYPE b1, b2, c1, c2; + +#pragma scop + k = (SCALAR_VAL(1.0)-EXP_FUN(-alpha))*(SCALAR_VAL(1.0)-EXP_FUN(-alpha))/(SCALAR_VAL(1.0)+SCALAR_VAL(2.0)*alpha*EXP_FUN(-alpha)-EXP_FUN(SCALAR_VAL(2.0)*alpha)); + a1 = a5 = k; + a2 = a6 = k*EXP_FUN(-alpha)*(alpha-SCALAR_VAL(1.0)); + a3 = a7 = k*EXP_FUN(-alpha)*(alpha+SCALAR_VAL(1.0)); + a4 = a8 = -k*EXP_FUN(SCALAR_VAL(-2.0)*alpha); + b1 = POW_FUN(SCALAR_VAL(2.0),-alpha); + b2 = -EXP_FUN(SCALAR_VAL(-2.0)*alpha); + c1 = c2 = 1; + + for (i=0; i<_PB_W; i++) { + ym1 = SCALAR_VAL(0.0); + ym2 = SCALAR_VAL(0.0); + xm1 = SCALAR_VAL(0.0); + for (j=0; j<_PB_H; j++) { + y1[i][j] = a1*imgIn[i][j] + a2*xm1 + b1*ym1 + b2*ym2; + xm1 = imgIn[i][j]; + ym2 = ym1; + ym1 = y1[i][j]; + } + } +#pragma endscop +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int w = W; + int h = H; + + /* Variable declaration/allocation. */ + DATA_TYPE alpha; + POLYBENCH_2D_ARRAY_DECL(imgIn, DATA_TYPE, W, H, w, h); + POLYBENCH_2D_ARRAY_DECL(imgOut, DATA_TYPE, W, H, w, h); + POLYBENCH_2D_ARRAY_DECL(y1, DATA_TYPE, W, H, w, h); + POLYBENCH_2D_ARRAY_DECL(y2, DATA_TYPE, W, H, w, h); + + + /* Initialize array(s). */ + init_array (w, h, &alpha, POLYBENCH_ARRAY(imgIn), POLYBENCH_ARRAY(imgOut)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_deriche (w, h, alpha, POLYBENCH_ARRAY(imgIn), POLYBENCH_ARRAY(imgOut), POLYBENCH_ARRAY(y1), POLYBENCH_ARRAY(y2)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(w, h, POLYBENCH_ARRAY(imgOut))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(imgIn); + POLYBENCH_FREE_ARRAY(imgOut); + POLYBENCH_FREE_ARRAY(y1); + POLYBENCH_FREE_ARRAY(y2); + + return 0; +} + +// CHECK: func @kernel_deriche(%arg0: i32, %arg1: i32, %arg2: f32, %arg3: memref<4096x2160xf32>, %arg4: memref<4096x2160xf32>, %arg5: memref<4096x2160xf32>, %arg6: memref<4096x2160xf32>) { +// CHECK-NEXT: %c0 = constant 0 : index +// CHECK-NEXT: %cst = constant 1.000000e+00 : f32 +// CHECK-NEXT: %cst_0 = constant 2.000000e+00 : f32 +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %cst_1 = constant 0.000000e+00 : f32 +// CHECK-NEXT: %0 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %2 = alloca() : memref<1xf32> +// CHECK-NEXT: %3 = alloca() : memref<1xf32> +// CHECK-NEXT: %4 = alloca() : memref<1xf32> +// CHECK-NEXT: %5 = alloca() : memref<1xf32> +// CHECK-NEXT: %6 = alloca() : memref<1xf32> +// CHECK-NEXT: %7 = alloca() : memref<1xf32> +// CHECK-NEXT: %8 = alloca() : memref<1xf32> +// CHECK-NEXT: %9 = alloca() : memref<1xf32> +// CHECK-NEXT: %10 = alloca() : memref<1xf32> +// CHECK-NEXT: %11 = alloca() : memref<1xf32> +// CHECK-NEXT: %12 = negf %arg2 : f32 +// CHECK-NEXT: %13 = exp %12 : f32 +// CHECK-NEXT: %14 = subf %cst, %13 : f32 +// CHECK-NEXT: %15 = mulf %14, %14 : f32 +// CHECK-NEXT: %16 = mulf %cst_0, %arg2 : f32 +// CHECK-NEXT: %17 = mulf %16, %13 : f32 +// CHECK-NEXT: %18 = addf %cst, %17 : f32 +// CHECK-NEXT: %19 = exp %16 : f32 +// CHECK-NEXT: %20 = subf %18, %19 : f32 +// CHECK-NEXT: %21 = divf %15, %20 : f32 +// CHECK-NEXT: %22 = mulf %21, %13 : f32 +// CHECK-NEXT: %23 = subf %arg2, %cst : f32 +// CHECK-NEXT: %24 = mulf %22, %23 : f32 +// CHECK-NEXT: %25 = addf %arg2, %cst : f32 +// CHECK-NEXT: %26 = mulf %22, %25 : f32 +// CHECK-NEXT: %27 = negf %21 : f32 +// CHECK-NEXT: %28 = negf %cst_0 : f32 +// CHECK-NEXT: %29 = mulf %28, %arg2 : f32 +// CHECK-NEXT: %30 = exp %29 : f32 +// CHECK-NEXT: %31 = mulf %27, %30 : f32 +// CHECK-NEXT: %32 = llvm.mlir.cast %cst_0 : f32 to !llvm.float +// CHECK-NEXT: %33 = llvm.mlir.cast %12 : f32 to !llvm.float +// CHECK-NEXT: %34 = "llvm.intr.pow"(%32, %33) : (!llvm.float, !llvm.float) -> !llvm.float +// CHECK-NEXT: %35 = llvm.mlir.cast %34 : !llvm.float to f32 +// CHECK-NEXT: %36 = negf %30 : f32 +// CHECK-NEXT: %37 = sitofp %c1_i32 : i32 to f32 +// CHECK-NEXT: store %cst_1, %4[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %5[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %2[%c0] : memref<1xf32> +// CHECK-NEXT: %38 = load %2[%c0] : memref<1xf32> +// CHECK-NEXT: %39 = mulf %24, %38 : f32 +// CHECK-NEXT: %40 = load %4[%c0] : memref<1xf32> +// CHECK-NEXT: %41 = mulf %35, %40 : f32 +// CHECK-NEXT: %42 = load %5[%c0] : memref<1xf32> +// CHECK-NEXT: %43 = mulf %36, %42 : f32 +// CHECK-NEXT: %44 = load %4[%c0] : memref<1xf32> +// CHECK-NEXT: store %44, %5[%c0] : memref<1xf32> +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: %78 = affine.load %arg3[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: %79 = mulf %21, %78 : f32 +// CHECK-NEXT: %80 = addf %79, %39 : f32 +// CHECK-NEXT: %81 = addf %80, %41 : f32 +// CHECK-NEXT: %82 = addf %81, %43 : f32 +// CHECK-NEXT: affine.store %82, %arg5[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: %83 = affine.load %arg3[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: affine.store %83, %2[0] : memref<1xf32> +// CHECK-NEXT: %84 = affine.load %arg5[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: affine.store %84, %4[0] : memref<1xf32> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: store %cst_1, %10[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %11[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %6[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %7[%c0] : memref<1xf32> +// CHECK-NEXT: %45 = load %6[%c0] : memref<1xf32> +// CHECK-NEXT: %46 = mulf %26, %45 : f32 +// CHECK-NEXT: %47 = load %7[%c0] : memref<1xf32> +// CHECK-NEXT: %48 = mulf %31, %47 : f32 +// CHECK-NEXT: %49 = addf %46, %48 : f32 +// CHECK-NEXT: %50 = load %10[%c0] : memref<1xf32> +// CHECK-NEXT: %51 = mulf %35, %50 : f32 +// CHECK-NEXT: %52 = addf %49, %51 : f32 +// CHECK-NEXT: %53 = load %11[%c0] : memref<1xf32> +// CHECK-NEXT: %54 = mulf %36, %53 : f32 +// CHECK-NEXT: %55 = addf %52, %54 : f32 +// CHECK-NEXT: %56 = load %6[%c0] : memref<1xf32> +// CHECK-NEXT: store %56, %7[%c0] : memref<1xf32> +// CHECK-NEXT: %57 = load %10[%c0] : memref<1xf32> +// CHECK-NEXT: store %57, %11[%c0] : memref<1xf32> +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: affine.store %55, %arg6[%arg7, -%arg8 + symbol(%0) - 1] : memref<4096x2160xf32> +// CHECK-NEXT: %78 = affine.load %arg3[%arg7, -%arg8 + symbol(%0) - 1] : memref<4096x2160xf32> +// CHECK-NEXT: affine.store %78, %6[0] : memref<1xf32> +// CHECK-NEXT: %79 = affine.load %arg6[%arg7, -%arg8 + symbol(%0) - 1] : memref<4096x2160xf32> +// CHECK-NEXT: affine.store %79, %10[0] : memref<1xf32> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: %78 = affine.load %arg5[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: %79 = affine.load %arg6[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: %80 = addf %78, %79 : f32 +// CHECK-NEXT: %81 = mulf %37, %80 : f32 +// CHECK-NEXT: affine.store %81, %arg4[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: store %cst_1, %3[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %4[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %5[%c0] : memref<1xf32> +// CHECK-NEXT: %58 = load %3[%c0] : memref<1xf32> +// CHECK-NEXT: %59 = mulf %24, %58 : f32 +// CHECK-NEXT: %60 = load %4[%c0] : memref<1xf32> +// CHECK-NEXT: %61 = mulf %35, %60 : f32 +// CHECK-NEXT: %62 = load %5[%c0] : memref<1xf32> +// CHECK-NEXT: %63 = mulf %36, %62 : f32 +// CHECK-NEXT: %64 = load %4[%c0] : memref<1xf32> +// CHECK-NEXT: store %64, %5[%c0] : memref<1xf32> +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: affine.for %arg8 = 0 to %1 { +// CHECK-NEXT: %78 = affine.load %arg4[%arg8, %arg7] : memref<4096x2160xf32> +// CHECK-NEXT: %79 = mulf %21, %78 : f32 +// CHECK-NEXT: %80 = addf %79, %59 : f32 +// CHECK-NEXT: %81 = addf %80, %61 : f32 +// CHECK-NEXT: %82 = addf %81, %63 : f32 +// CHECK-NEXT: affine.store %82, %arg5[%arg8, %arg7] : memref<4096x2160xf32> +// CHECK-NEXT: %83 = affine.load %arg4[%arg8, %arg7] : memref<4096x2160xf32> +// CHECK-NEXT: affine.store %83, %3[0] : memref<1xf32> +// CHECK-NEXT: %84 = affine.load %arg5[%arg8, %arg7] : memref<4096x2160xf32> +// CHECK-NEXT: affine.store %84, %4[0] : memref<1xf32> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: store %cst_1, %8[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %9[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %10[%c0] : memref<1xf32> +// CHECK-NEXT: store %cst_1, %11[%c0] : memref<1xf32> +// CHECK-NEXT: %65 = load %8[%c0] : memref<1xf32> +// CHECK-NEXT: %66 = mulf %26, %65 : f32 +// CHECK-NEXT: %67 = load %9[%c0] : memref<1xf32> +// CHECK-NEXT: %68 = mulf %31, %67 : f32 +// CHECK-NEXT: %69 = addf %66, %68 : f32 +// CHECK-NEXT: %70 = load %10[%c0] : memref<1xf32> +// CHECK-NEXT: %71 = mulf %35, %70 : f32 +// CHECK-NEXT: %72 = addf %69, %71 : f32 +// CHECK-NEXT: %73 = load %11[%c0] : memref<1xf32> +// CHECK-NEXT: %74 = mulf %36, %73 : f32 +// CHECK-NEXT: %75 = addf %72, %74 : f32 +// CHECK-NEXT: %76 = load %8[%c0] : memref<1xf32> +// CHECK-NEXT: store %76, %9[%c0] : memref<1xf32> +// CHECK-NEXT: %77 = load %10[%c0] : memref<1xf32> +// CHECK-NEXT: store %77, %11[%c0] : memref<1xf32> +// CHECK-NEXT: affine.for %arg7 = 0 to %0 { +// CHECK-NEXT: affine.for %arg8 = 0 to %1 { +// CHECK-NEXT: affine.store %75, %arg6[-%arg8 + symbol(%1) - 1, %arg7] : memref<4096x2160xf32> +// CHECK-NEXT: %78 = affine.load %arg4[-%arg8 + symbol(%1) - 1, %arg7] : memref<4096x2160xf32> +// CHECK-NEXT: affine.store %78, %8[0] : memref<1xf32> +// CHECK-NEXT: %79 = affine.load %arg6[-%arg8 + symbol(%1) - 1, %arg7] : memref<4096x2160xf32> +// CHECK-NEXT: affine.store %79, %10[0] : memref<1xf32> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg7 = 0 to %1 { +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: %78 = affine.load %arg5[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: %79 = affine.load %arg6[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: %80 = addf %78, %79 : f32 +// CHECK-NEXT: %81 = mulf %37, %80 : f32 +// CHECK-NEXT: affine.store %81, %arg4[%arg7, %arg8] : memref<4096x2160xf32> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/medley/deriche/deriche.h b/mlir-clang/Test/polybench/medley/deriche/deriche.h new file mode 100644 index 000000000000..260757bfb433 --- /dev/null +++ b/mlir-clang/Test/polybench/medley/deriche/deriche.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _DERICHE_H +# define _DERICHE_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(W) && !defined(H) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define W 64 +# define H 64 +# endif + +# ifdef SMALL_DATASET +# define W 192 +# define H 128 +# endif + +# ifdef MEDIUM_DATASET +# define W 720 +# define H 480 +# endif + +# ifdef LARGE_DATASET +# define W 4096 +# define H 2160 +# endif + +# ifdef EXTRALARGE_DATASET +# define W 7680 +# define H 4320 +# endif + + +#endif /* !(W H) */ + +# define _PB_W POLYBENCH_LOOP_BOUND(W,w) +# define _PB_H POLYBENCH_LOOP_BOUND(H,h) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_FLOAT +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_DERICHE_H */ diff --git a/mlir-clang/Test/polybench/medley/floyd-warshall/floyd-warshall.c b/mlir-clang/Test/polybench/medley/floyd-warshall/floyd-warshall.c new file mode 100644 index 000000000000..1fe3e9aaf3a6 --- /dev/null +++ b/mlir-clang/Test/polybench/medley/floyd-warshall/floyd-warshall.c @@ -0,0 +1,148 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* floyd-warshall.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "floyd-warshall.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE POLYBENCH_2D(path,N,N,n,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + path[i][j] = i*j%7+1; + if ((i+j)%13 == 0 || (i+j)%7==0 || (i+j)%11 == 0) + path[i][j] = 999; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_2D(path,N,N,n,n)) + +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("path"); + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + if ((i * n + j) % 20 == 0) fprintf (POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, path[i][j]); + } + POLYBENCH_DUMP_END("path"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_floyd_warshall(int n, + DATA_TYPE POLYBENCH_2D(path,N,N,n,n)) +{ + int i, j, k; + +#pragma scop + for (k = 0; k < _PB_N; k++) + { + for(i = 0; i < _PB_N; i++) + for (j = 0; j < _PB_N; j++) + path[i][j] = path[i][j] < path[i][k] + path[k][j] ? + path[i][j] : path[i][k] + path[k][j]; + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(path, DATA_TYPE, N, N, n, n); + + + /* Initialize array(s). */ + init_array (n, POLYBENCH_ARRAY(path)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_floyd_warshall (n, POLYBENCH_ARRAY(path)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(path))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(path); + + return 0; +} + +// CHECK: func private @kernel_floyd_warshall(%arg0: i32, %arg1: memref) { +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg2 = 0 to %0 { +// CHECK-NEXT: affine.for %arg3 = 0 to %0 { +// CHECK-NEXT: affine.for %arg4 = 0 to %0 { +// CHECK-NEXT: %1 = affine.load %arg1[%arg3, %arg4] : memref +// CHECK-NEXT: %2 = affine.load %arg1[%arg3, %arg2] : memref +// CHECK-NEXT: %3 = affine.load %arg1[%arg2, %arg4] : memref +// CHECK-NEXT: %4 = addi %2, %3 : i32 +// CHECK-NEXT: %5 = cmpi slt, %1, %4 : i32 +// CHECK-NEXT: %6 = select %5, %1, %4 : i32 +// CHECK-NEXT: affine.store %6, %arg1[%arg3, %arg4] : memref +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/medley/floyd-warshall/floyd-warshall.h b/mlir-clang/Test/polybench/medley/floyd-warshall/floyd-warshall.h new file mode 100644 index 000000000000..800f42c9a505 --- /dev/null +++ b/mlir-clang/Test/polybench/medley/floyd-warshall/floyd-warshall.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _FLOYD_WARSHALL_H +# define _FLOYD_WARSHALL_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 60 +# endif + +# ifdef SMALL_DATASET +# define N 180 +# endif + +# ifdef MEDIUM_DATASET +# define N 500 +# endif + +# ifdef LARGE_DATASET +# define N 2800 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 5600 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_INT +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_FLOYD_WARSHALL_H */ diff --git a/mlir-clang/Test/polybench/medley/nussinov/Nussinov.orig.c b/mlir-clang/Test/polybench/medley/nussinov/Nussinov.orig.c new file mode 100644 index 000000000000..12a35b7aa4ba --- /dev/null +++ b/mlir-clang/Test/polybench/medley/nussinov/Nussinov.orig.c @@ -0,0 +1,356 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm | FileCheck %s --check-prefix EXEC +// This test will fail until global variables are working +// XFAIL: * +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* +Nussinov.c + +Builds a random sequence of base pairs, runs the Nussinov algorithm. +Unless FOUR_WAY_MAX_WITH_REDUNDANCY is define'd to true, it uses the more concise version as per the comment at the end of the file. + +Programmed by Dave Wonnacott at Haverford College , with help from Allison Lake, Ting Zhou, and Tian Jin, based on algorithm by Nussinov, described in Allison Lake's senior thesis. + + +compile&run with: + gcc -O3 -DNDEBUG -Wall -Wno-unused-value -Wno-unknown-pragmas Nussinov.c -o Nussinov && ./Nussinov + +Or, to set a specific size such as 5000, use: + gcc -O3 -DNDEBUG -Dsize=5000 -Wall -Wno-unused-value -Wno-unknown-pragmas Nussinov.c -o Nussinov && ./Nussinov + +------------- +For debugging/verifying we hit all the right points and have all updates before any pure reads: + gcc -Dsize=250 -DCHECK_DEPENDENCES=1 -DVERBOSE=1 -DVERBOSE_OUT=stderr -Wall -Wno-unused-value -Wno-unknown-pragmas Nussinov.c -o Nussinov && ./Nussinov 2> /tmp/Nussinov-log-250.txt ; sort /tmp/Nussinov-log-250.txt -o /tmp/Nussinov-log-250.txt + +------------- +Re-generate small or big matrix for comparison: + gcc -O3 -DPRINT_SIZE=5000 -Wall -Wno-unused-value -Wno-unknown-pragmas Nussinov.c -o Nussinov-print && echo 250 | ./Nussinov-print > printed-matrix-250.out +Or do the comparison + gcc -O3 -DPRINT_SIZE=5000 -Wall -Wno-unused-value -Wno-unknown-pragmas Nussinov.c -o Nussinov-print && echo 250 | ./Nussinov-print | diff printed-matrix-250.out - + +------------- +Re-generate big matrices for comparison: + gcc -O3 -Dsize=5000 -DPRINT_SIZE=5000 -Wall -Wno-unused-value -Wno-unknown-pragmas Nussinov.c -o Nussinov && ./Nussinov > printed-matrix-5000.out + + gcc -DSEED=17 -O3 -Dsize=5000 -DPRINT_SIZE=5000 -Wall -Wno-unused-value -Wno-unknown-pragmas Nussinov.c -o Nussinov && ./Nussinov > printed-matrix-5000-seed17.out + +*/ + +#include +#include +#include +#include +#include +#include + +typedef int bool; +const int true = 1; +const int false = 0; + +#if ! defined SCALAR_REPLACEMENT +#define SCALAR_REPLACEMENT 0 +#endif + +#if ! defined CHECK_DEPENDENCES +#define CHECK_DEPENDENCES false +#endif + +#if defined NDEBUG +#define eassert(EXPR) 1 +#else +#define eassert(EXPR) eassert_func(__STRING(EXPR), EXPR) +void eassert_func(char *expr, bool value) +{ + if (!value) { + fprintf(stderr, "assertion failed: %s\n", expr); + exit(1); + // printf("assertion failed: %s\n", expr); + } +} +#endif + + +// NOTE THIS NEEDS TO EXIST HERE OTHERWISE MAX_SIZE UNDEFINED +#define MAX_SIZE 16307 + +#if ! defined FOUR_WAY_MAX_WITH_REDUNDANCY +#define FOUR_WAY_MAX_WITH_REDUNDANCY false +#endif +#if FOUR_WAY_MAX_WITH_REDUNDANCY +#define ZERO_IF_NO_REDUNDANCY 1 +#else +#define ZERO_IF_NO_REDUNDANCY 0 +#endif + +#if ! defined PRINT_SIZE +#define PRINT_SIZE 48 /* print for debugging if not bigger than this */ +#endif +#if ! defined VERBOSE +#define VERBOSE false +#endif +#if ! defined VERBOSE_OUT +#define VERBOSE_OUT stdout +#endif +#if VERBOSE +#if ! defined REALLY_VERBOSE +#define REALLY_VERBOSE false +#endif +#endif + +#if ! defined SEED +#define SEED 42 +#endif + +#define SLOWER (CHECK_DEPENDENCES | VERBOSE) + +double cur_time(void) +{ + struct timeval tv; + struct timezone tz; + gettimeofday(&tv, &tz); + return tv.tv_sec + tv.tv_usec*1.0e-6; +} + +// for bases, use 0, 1, 2, 3, with (0, 3) and (1, 2) being matches +char *base_print[4] = { "A", "C", "G", "U" }; +typedef int base; // could also try char, short +typedef int score; // could use uint, short, anything that can count high enough + +inline score match(base b1, base b2) +{ + return (b1+b2) == 3 ? 1 : 0; +} + +inline score max_score(score s1, score s2) +{ + return (s1 >= s2) ? s1 : s2; +} + +base seq[MAX_SIZE]; +score N_array[MAX_SIZE][MAX_SIZE]; +#define debug_N(x, y) (N_array[x][y]) /* read secretly without triggering has_been_read */ + +#if ! CHECK_DEPENDENCES +#define N(x, y) (eassert(0 <= x && x < size && 0 <= y && y < size), N_array[x][y]) +#if SCALAR_REPLACEMENT +#define MAX_N_DECLS() int max_tmp_i, max_tmp_j; score max_tmp +#define MAX_N_START(x,y) ((max_tmp_i=x), (max_tmp_j=y), (max_tmp = 0)) +#define MAX_N(x, y, v) (eassert(max_tmp_i==x && max_tmp_j==y), eassert(0 <= x && x < size && 0 <= y && y < size), (max_tmp = max_score(max_tmp, v))) +#define MAX_N_END(x,y) (eassert(max_tmp_i==x && max_tmp_j==y), ((N_array[x][y]) = max_score(N_array[x][y], max_tmp))) +#else /* else not scalar replacement (inside check_deps) */ +#define MAX_N(x, y, v) (eassert(0 <= x && x < size && 0 <= y && y < size), ((N_array[x][y]) = max_score(N_array[x][y], v))) +#endif /* else not scalar replacement (inside check_deps) */ +#else /* not check_deps */ +bool N_array_has_been_read[MAX_SIZE][MAX_SIZE]; +#define N(x, y) (eassert(0 <= x && x < size && 0 <= y && y < size), \ + (REALLY_VERBOSE?fprintf(VERBOSE_OUT, "i, j, k = %d, %d, %d: reading N[%d][%d]\n", i, j, k, x, y):1), \ + (N_array_has_been_read[x][y] = (true)), \ + N_array[x][y]+0) +#if SCALAR_REPLACEMENT /* inside not check_deps */ +#error("Not yet ready to do scalar replacement and check_deps at the same time :-(\00") +#else /* else not scalar replacement (inside not check_deps) */ +#define MAX_N(x, y, v) (eassert(0 <= x && x < size && 0 <= y && y < size), \ + eassert(!N_array_has_been_read[x][y]), \ + (N_array[x][y] = max_score(N_array[x][y], v))) +#endif /* else not scalar replacement (inside not check_deps) */ +#endif +#if ! SCALAR_REPLACEMENT +#define MAX_N_DECLS() +#define MAX_N_START(x,y) +#define MAX_N_END(x,y) +#endif + + +/* Convenience function */ +int getint(char *prompt) +{ +#if VERBOSE_OUT == stderr + char *terminate = "\n"; +#else + char *terminate = ""; +#endif + int result; + int i=0; + while ( + fprintf(stderr, "%s%s", prompt, terminate), + result = scanf("%d", &i), + result != 1 && result != EOF + ) { + fprintf(stderr, "Sorry, I didn't understand that...\n"); + } + if (result == 1) { + return i; + } else { + fprintf(stderr, "Giving up ... can't read input\n"); + exit(1); + } +} + + +int main(int argc, char *argv[]) +{ +#if ! SLOWER + double start_time, end_time; // , speed; +#endif +#if ! defined size + int size = getint("Enter length of random mRNA sequence (2200 is average for human mRNA): "); // Average (human) mRNA length is 2200; there's one that's 5000, though +#endif + + int i, j, k=-1; + MAX_N_DECLS(); + char *options; +#if VERBOSE +#if CHECK_DEPENDENCES + options = " [DV]"; +#else + options = " [V]"; +#endif +#else +#if CHECK_DEPENDENCES + options = " [D]"; +#else + options = ""; +#endif +#endif + + printf("Running Nussinov RNA algorithm%s for sequence of length %d, random data with seed %d.\n", + options, size, SEED); + + if (size > MAX_SIZE) { + fprintf(stderr, "size (%d) < MAX_SIZE (%d)\n", MAX_SIZE, size); + exit(1); + } + + /* seed it with a constant so we can check/compare the results */ + srand(SEED); + for (i = 0; i < size; i++) + seq[i] = rand() % 4; + +#if ! SLOWER + start_time = cur_time(); +#endif + +// "OPTION 1" +#pragma scop + for (i = size-1; i >= 0; i--) { + for (j=i+1; j=0) MAX_N(i, j, N(i, j-1)); + if (i+1=0 && i+1= s2) ? s1 : s2) + +/* Array initialization. */ +static +void init_array (int n, + base POLYBENCH_1D(seq,N,n), + DATA_TYPE POLYBENCH_2D(table,N,N,n,n)) +{ + int i, j; + + //base is AGCT/0..3 + for (i=0; i , + with help from Allison Lake, Ting Zhou, and Tian Jin, + based on algorithm by Nussinov, described in Allison Lake's senior thesis. +*/ +static +void kernel_nussinov(int n, base POLYBENCH_1D(seq,N,n), + DATA_TYPE POLYBENCH_2D(table,N,N,n,n)) +{ + int i, j, k; + +#pragma scop + for (i = _PB_N-1; i >= 0; i--) { + for (j=i+1; j<_PB_N; j++) { + + if (j-1>=0) + table[i][j] = max_score(table[i][j], table[i][j-1]); + if (i+1<_PB_N) + table[i][j] = max_score(table[i][j], table[i+1][j]); + + if (j-1>=0 && i+1<_PB_N) { + /* don't allow adjacent elements to bond */ + if (i (-d0 + s0)> +// CHECK-NEXT: #map1 = affine_map<(d0) -> (d0 - 1)> +// CHECK-NEXT: #map2 = affine_map<(d0)[s0] -> (-d0 + s0 - 1)> +// CHECK-NEXT: #map3 = affine_map<(d0) -> (d0)> +// CHECK-NEXT: #set0 = affine_set<(d0) : (d0 >= 0)> +// CHECK-NEXT: #set1 = affine_set<(d0)[s0] : (-d0 + s0 - 1 >= 0)> +// CHECK-NEXT: #set2 = affine_set<(d0, d1)[s0] : (d0 >= 0, -d1 + s0 - 1 >= 0)> +// CHECK-NEXT: #set3 = affine_set<(d0, d1) : (d1 - d0 - 1 >= 0)> +// CHECK-NEXT: module { +// CHECK-NEXT: llvm.mlir.global internal constant @str7("==END DUMP_ARRAYS==\0A\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str6("\0Aend dump: %s\0A\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str5("%d \00") +// CHECK-NEXT: llvm.mlir.global internal constant @str4("\0A\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str3("table\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str2("begin dump: %s\00") +// CHECK-NEXT: llvm.mlir.global internal constant @str1("==BEGIN DUMP_ARRAYS==\0A\00") +// CHECK-NEXT: llvm.mlir.global external @stderr() : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>> +// CHECK-NEXT: llvm.func @fprintf(!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr, ...) -> !llvm.i32 +// CHECK-NEXT: llvm.mlir.global internal constant @str0("\00") +// CHECK-NEXT: llvm.func @strcmp(!llvm.ptr, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: func @main(%arg0: i32, %arg1: !llvm.ptr>) -> i32 { +// CHECK-NEXT: %c2500_i32 = constant 2500 : i32 +// CHECK-NEXT: %c42_i32 = constant 42 : i32 +// CHECK-NEXT: %true = constant true +// CHECK-NEXT: %false = constant false +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %0 = alloc() : memref<2500xi8> +// CHECK-NEXT: %1 = alloc() : memref<2500x2500xi32> +// CHECK-NEXT: call @init_array(%c2500_i32, %0, %1) : (i32, memref<2500xi8>, memref<2500x2500xi32>) -> () +// CHECK-NEXT: call @kernel_nussinov(%c2500_i32, %0, %1) : (i32, memref<2500xi8>, memref<2500x2500xi32>) -> () +// CHECK-NEXT: %2 = cmpi "sgt", %arg0, %c42_i32 : i32 +// CHECK-NEXT: %3 = scf.if %2 -> (i1) { +// CHECK-NEXT: %4 = llvm.load %arg1 : !llvm.ptr> +// CHECK-NEXT: %5 = llvm.mlir.addressof @str0 : !llvm.ptr> +// CHECK-NEXT: %6 = llvm.mlir.constant(0 : index) : !llvm.i64 +// CHECK-NEXT: %7 = llvm.getelementptr %5[%6, %6] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %8 = llvm.call @strcmp(%4, %7) : (!llvm.ptr, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: %9 = llvm.mlir.cast %8 : !llvm.i32 to i32 +// CHECK-NEXT: %10 = trunci %9 : i32 to i1 +// CHECK-NEXT: %11 = xor %10, %true : i1 +// CHECK-NEXT: scf.yield %11 : i1 +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %false : i1 +// CHECK-NEXT: } +// CHECK-NEXT: scf.if %3 { +// CHECK-NEXT: call @print_array(%c2500_i32, %1) : (i32, memref<2500x2500xi32>) -> () +// CHECK-NEXT: } +// CHECK-NEXT: return %c0_i32 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: func @init_array(%arg0: i32, %arg1: memref<2500xi8>, %arg2: memref<2500x2500xi32>) { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %c4_i32 = constant 4 : i32 +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: br ^bb1(%c0_i32 : i32) +// CHECK-NEXT: ^bb1(%0: i32): // 2 preds: ^bb0, ^bb2 +// CHECK-NEXT: %1 = cmpi "slt", %0, %arg0 : i32 +// CHECK-NEXT: %2 = index_cast %0 : i32 to index +// CHECK-NEXT: cond_br %1, ^bb2, ^bb3(%c0_i32 : i32) +// CHECK-NEXT: ^bb2: // pred: ^bb1 +// CHECK-NEXT: %3 = addi %0, %c1_i32 : i32 +// CHECK-NEXT: %4 = remi_signed %3, %c4_i32 : i32 +// CHECK-NEXT: %5 = trunci %4 : i32 to i8 +// CHECK-NEXT: store %5, %arg1[%2] : memref<2500xi8> +// CHECK-NEXT: br ^bb1(%3 : i32) +// CHECK-NEXT: ^bb3(%6: i32): // 2 preds: ^bb1, ^bb7 +// CHECK-NEXT: %7 = cmpi "slt", %6, %arg0 : i32 +// CHECK-NEXT: %8 = index_cast %6 : i32 to index +// CHECK-NEXT: cond_br %7, ^bb5(%c0_i32 : i32), ^bb4 +// CHECK-NEXT: ^bb4: // pred: ^bb3 +// CHECK-NEXT: return +// CHECK-NEXT: ^bb5(%9: i32): // 2 preds: ^bb3, ^bb6 +// CHECK-NEXT: %10 = cmpi "slt", %9, %arg0 : i32 +// CHECK-NEXT: %11 = index_cast %9 : i32 to index +// CHECK-NEXT: cond_br %10, ^bb6, ^bb7 +// CHECK-NEXT: ^bb6: // pred: ^bb5 +// CHECK-NEXT: store %c0_i32, %arg2[%8, %11] : memref<2500x2500xi32> +// CHECK-NEXT: %12 = addi %9, %c1_i32 : i32 +// CHECK-NEXT: br ^bb5(%12 : i32) +// CHECK-NEXT: ^bb7: // pred: ^bb5 +// CHECK-NEXT: %13 = addi %6, %c1_i32 : i32 +// CHECK-NEXT: br ^bb3(%13 : i32) +// CHECK-NEXT: } +// CHECK-NEXT: func @kernel_nussinov(%arg0: i32, %arg1: memref<2500xi8>, %arg2: memref<2500x2500xi32>) { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %c3_i32 = constant 3 : i32 +// CHECK-NEXT: %0 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg3 = 0 to %0 { +// CHECK-NEXT: affine.for %arg4 = #map0(%arg3)[%0] to %0 { +// CHECK-NEXT: %1 = affine.apply #map1(%arg4) +// CHECK-NEXT: affine.if #set0(%1) { +// CHECK-NEXT: %4 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: %5 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4 - 1] : memref<2500x2500xi32> +// CHECK-NEXT: %6 = cmpi "sge", %4, %5 : i32 +// CHECK-NEXT: %7 = scf.if %6 -> (i32) { +// CHECK-NEXT: %8 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: scf.yield %8 : i32 +// CHECK-NEXT: } else { +// CHECK-NEXT: %8 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4 - 1] : memref<2500x2500xi32> +// CHECK-NEXT: scf.yield %8 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %7, %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: } +// CHECK-NEXT: %2 = affine.apply #map0(%arg3)[%0] +// CHECK-NEXT: affine.if #set1(%2)[%0] { +// CHECK-NEXT: %4 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: %5 = affine.load %arg2[-%arg3 + symbol(%0), %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: %6 = cmpi "sge", %4, %5 : i32 +// CHECK-NEXT: %7 = scf.if %6 -> (i32) { +// CHECK-NEXT: %8 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: scf.yield %8 : i32 +// CHECK-NEXT: } else { +// CHECK-NEXT: %8 = affine.load %arg2[-%arg3 + symbol(%0), %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: scf.yield %8 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %7, %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: } +// CHECK-NEXT: affine.if #set2(%1, %2)[%0] { +// CHECK-NEXT: %4 = affine.apply #map2(%arg3)[%0] +// CHECK-NEXT: affine.if #set3(%4, %1) { +// CHECK-NEXT: %5 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: %6 = affine.load %arg2[-%arg3 + symbol(%0), %arg4 - 1] : memref<2500x2500xi32> +// CHECK-NEXT: %7 = affine.load %arg1[-%arg3 + symbol(%0) - 1] : memref<2500xi8> +// CHECK-NEXT: %8 = sexti %7 : i8 to i32 +// CHECK-NEXT: %9 = affine.load %arg1[%arg4] : memref<2500xi8> +// CHECK-NEXT: %10 = sexti %9 : i8 to i32 +// CHECK-NEXT: %11 = addi %8, %10 : i32 +// CHECK-NEXT: %12 = cmpi "eq", %11, %c3_i32 : i32 +// CHECK-NEXT: %13 = select %12, %c1_i32, %c0_i32 : i32 +// CHECK-NEXT: %14 = addi %6, %13 : i32 +// CHECK-NEXT: %15 = cmpi "sge", %5, %14 : i32 +// CHECK-NEXT: %16 = scf.if %15 -> (i32) { +// CHECK-NEXT: %17 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: scf.yield %17 : i32 +// CHECK-NEXT: } else { +// CHECK-NEXT: %17 = affine.load %arg2[-%arg3 + symbol(%0), %arg4 - 1] : memref<2500x2500xi32> +// CHECK-NEXT: %18 = affine.load %arg1[-%arg3 + symbol(%0) - 1] : memref<2500xi8> +// CHECK-NEXT: %19 = sexti %18 : i8 to i32 +// CHECK-NEXT: %20 = affine.load %arg1[%arg4] : memref<2500xi8> +// CHECK-NEXT: %21 = sexti %20 : i8 to i32 +// CHECK-NEXT: %22 = addi %19, %21 : i32 +// CHECK-NEXT: %23 = cmpi "eq", %22, %c3_i32 : i32 +// CHECK-NEXT: %24 = select %23, %c1_i32, %c0_i32 : i32 +// CHECK-NEXT: %25 = addi %17, %24 : i32 +// CHECK-NEXT: scf.yield %25 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %16, %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: } else { +// CHECK-NEXT: %5 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: %6 = affine.load %arg2[-%arg3 + symbol(%0), %arg4 - 1] : memref<2500x2500xi32> +// CHECK-NEXT: %7 = cmpi "sge", %5, %6 : i32 +// CHECK-NEXT: %8 = scf.if %7 -> (i32) { +// CHECK-NEXT: %9 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: scf.yield %9 : i32 +// CHECK-NEXT: } else { +// CHECK-NEXT: %9 = affine.load %arg2[-%arg3 + symbol(%0), %arg4 - 1] : memref<2500x2500xi32> +// CHECK-NEXT: scf.yield %9 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %8, %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: %3 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: affine.for %arg5 = #map0(%arg3)[%0] to #map3(%arg4) { +// CHECK-NEXT: %4 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg5] : memref<2500x2500xi32> +// CHECK-NEXT: %5 = affine.load %arg2[%arg5 + 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: %6 = addi %4, %5 : i32 +// CHECK-NEXT: %7 = cmpi "sge", %3, %6 : i32 +// CHECK-NEXT: %8 = scf.if %7 -> (i32) { +// CHECK-NEXT: %9 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: scf.yield %9 : i32 +// CHECK-NEXT: } else { +// CHECK-NEXT: %9 = affine.load %arg2[-%arg3 + symbol(%0) - 1, %arg5] : memref<2500x2500xi32> +// CHECK-NEXT: %10 = affine.load %arg2[%arg5 + 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: %11 = addi %9, %10 : i32 +// CHECK-NEXT: scf.yield %11 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %8, %arg2[-%arg3 + symbol(%0) - 1, %arg4] : memref<2500x2500xi32> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } +// CHECK-NEXT: func @print_array(%arg0: i32, %arg1: memref<2500x2500xi32>) { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: %c20_i32 = constant 20 : i32 +// CHECK-NEXT: %c1_i32 = constant 1 : i32 +// CHECK-NEXT: %0 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %1 = llvm.load %0 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %2 = llvm.mlir.addressof @str1 : !llvm.ptr> +// CHECK-NEXT: %3 = llvm.mlir.constant(0 : index) : !llvm.i64 +// CHECK-NEXT: %4 = llvm.getelementptr %2[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %5 = llvm.call @fprintf(%1, %4) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: %6 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %7 = llvm.load %6 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %8 = llvm.mlir.addressof @str2 : !llvm.ptr> +// CHECK-NEXT: %9 = llvm.getelementptr %8[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %10 = llvm.mlir.addressof @str3 : !llvm.ptr> +// CHECK-NEXT: %11 = llvm.getelementptr %10[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %12 = llvm.call @fprintf(%7, %9, %11) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: br ^bb1(%c0_i32, %c0_i32 : i32, i32) +// CHECK-NEXT: ^bb1(%13: i32, %14: i32): // 2 preds: ^bb0, ^bb5 +// CHECK-NEXT: %15 = cmpi "slt", %13, %arg0 : i32 +// CHECK-NEXT: %16 = index_cast %13 : i32 to index +// CHECK-NEXT: cond_br %15, ^bb3(%13, %14 : i32, i32), ^bb2 +// CHECK-NEXT: ^bb2: // pred: ^bb1 +// CHECK-NEXT: %17 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %18 = llvm.load %17 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %19 = llvm.mlir.addressof @str6 : !llvm.ptr> +// CHECK-NEXT: %20 = llvm.getelementptr %19[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %21 = llvm.mlir.addressof @str3 : !llvm.ptr> +// CHECK-NEXT: %22 = llvm.getelementptr %21[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %23 = llvm.call @fprintf(%18, %20, %22) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: %24 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %25 = llvm.load %24 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %26 = llvm.mlir.addressof @str7 : !llvm.ptr> +// CHECK-NEXT: %27 = llvm.getelementptr %26[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %28 = llvm.call @fprintf(%25, %27) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: return +// CHECK-NEXT: ^bb3(%29: i32, %30: i32): // 2 preds: ^bb1, ^bb4 +// CHECK-NEXT: %31 = cmpi "slt", %29, %arg0 : i32 +// CHECK-NEXT: %32 = index_cast %29 : i32 to index +// CHECK-NEXT: cond_br %31, ^bb4, ^bb5 +// CHECK-NEXT: ^bb4: // pred: ^bb3 +// CHECK-NEXT: %33 = remi_signed %30, %c20_i32 : i32 +// CHECK-NEXT: %34 = cmpi "eq", %33, %c0_i32 : i32 +// CHECK-NEXT: scf.if %34 { +// CHECK-NEXT: %45 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %46 = llvm.load %45 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %47 = llvm.mlir.addressof @str4 : !llvm.ptr> +// CHECK-NEXT: %48 = llvm.getelementptr %47[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %49 = llvm.call @fprintf(%46, %48) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr) -> !llvm.i32 +// CHECK-NEXT: } +// CHECK-NEXT: %35 = llvm.mlir.addressof @stderr : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %36 = llvm.load %35 : !llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>> +// CHECK-NEXT: %37 = llvm.mlir.addressof @str5 : !llvm.ptr> +// CHECK-NEXT: %38 = llvm.getelementptr %37[%3, %3] : (!llvm.ptr>, !llvm.i64, !llvm.i64) -> !llvm.ptr +// CHECK-NEXT: %39 = load %arg1[%16, %32] : memref<2500x2500xi32> +// CHECK-NEXT: %40 = llvm.mlir.cast %39 : i32 to !llvm.i32 +// CHECK-NEXT: %41 = llvm.call @fprintf(%36, %38, %40) : (!llvm.ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr>, ptr>, i32, array<4 x i8>)>>, ptr>, i32, i32, i64, i16, i8, array<1 x i8>, ptr, i64, ptr, ptr, ptr, ptr, i64, i32, array<20 x i8>)>>, !llvm.ptr, !llvm.i32) -> !llvm.i32 +// CHECK-NEXT: %42 = addi %30, %c1_i32 : i32 +// CHECK-NEXT: %43 = addi %29, %c1_i32 : i32 +// CHECK-NEXT: br ^bb3(%43, %42 : i32, i32) +// CHECK-NEXT: ^bb5: // pred: ^bb3 +// CHECK-NEXT: %44 = addi %13, %c1_i32 : i32 +// CHECK-NEXT: br ^bb1(%44, %30 : i32, i32) +// CHECK-NEXT: } +// CHECK-NEXT: func private @free(memref) +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/medley/nussinov/nussinov.h b/mlir-clang/Test/polybench/medley/nussinov/nussinov.h new file mode 100644 index 000000000000..50aea1f5fcd9 --- /dev/null +++ b/mlir-clang/Test/polybench/medley/nussinov/nussinov.h @@ -0,0 +1,74 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _NUSSINOV_H +# define _NUSSINOV_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define N 60 +# endif + +# ifdef SMALL_DATASET +# define N 180 +# endif + +# ifdef MEDIUM_DATASET +# define N 500 +# endif + +# ifdef LARGE_DATASET +# define N 2500 +# endif + +# ifdef EXTRALARGE_DATASET +# define N 5500 +# endif + + +#endif /* !(N) */ + +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_INT +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_NUSSINOV_H */ diff --git a/mlir-clang/Test/polybench/polybench.pdf b/mlir-clang/Test/polybench/polybench.pdf new file mode 100644 index 0000000000000000000000000000000000000000..70a7b56d5962fc74ec272ddcc4d19f3ddd5e7f58 GIT binary patch literal 256105 zcmb5VQ;=?Jvu;~9zN%5SHOjVa+qP}nwr$(CYm{wkl(pwtvClqhN6a|!pMI6M{ifxU zE#D-S7Z#;qpk;v}onIbVgJIxcA)qI)Gqi-^=7yn@HnBBxHYZ?UU}7Tp?+=Df)WX`? z#F2nb)Y`z=MA*d0&e#Nomlwv#+0n$n2F88kI!nrFlNI29`;E%2$V4j04F%FRY{QxK zN0uqY^Q@U?o`j^JR2&iC(&a+$HZT~O=PZKrP_j8DN+NR5umQimOMQH`sfum)aBYZz zh~03<`X+&1FdHe(DWE<$j`5c5XDQgeZ<=kjQhesx5qUoT6%Hx*V{>+(M8A{S>Wq&Wp>+d_(AcjA4^_~+m?hqa4=5j z3UvKQlBf?>{76ailc_DgrM8Z@JB!ndk6S+3>!=y9?^KaGgk!G@VT#hhNVJ}_(aJ#9 z`~fW+PQgb@b9!}2^OFP&Xp1pNrPfXa-qg}5KeA{gz2hD%eZl=S8_OMPl_kWOB0$tv z!dLUM316T^5_6^9r&PgeX-}o)HU@OozycuD-kUxf^6b~USJoex6audD81>>t*Yr4H zFfavShHLOGGM}&ZseENGb;OCDC$T*>-Ira z%6c8R`cYEe{`k~OFf_e{N+WB9X!a9&i%z>-Cn!3^g(7(nt7A|qRO+VU1+BQw%|Nkm zM^|WSI;cf-JY51Xq8u?cu-894NSC>_b~`d&FMplZS#!b^M;QW#qUe2Ojqt$hXYc+B z-!{uvt=xkU#P0yE(E3N@!BhJQt6C~!Ed-Dy4`ByAdy+k;@cAn@S1f&`p^+t`OQN1m zkBn@3AR7y0cisZKve{b7ibgppzpU|E-M z!fl-k5+|=B%719}oVyCR*3ea2qaRBMNXmmA(kqI7z3>~GE9eIZOY3yHmKyNFP}dVY zhbxF(bpV>y1r|yCy2hC}-PqGzcF&#n)p-zAH7c?~s6-WP-AQfhL5JmN*sA};z^WKQ zscez$V2lv)N_VeEM7%Ulw~?=jdj3_U{`(h}g!jqaS>vR#cNfl`J_5|GB&aDKy1L=T zv9-G9#(7_o^RR%~c}JeKic_c7`A>V$71Lq;$@XDF8|_vc;nrB$ALIvCDK*5|DIH^t zh$Pt6PS%RF%@b{ZxzjU2NyMa+ak7~t9MTzn^FLXv}Fhxa-FJ%9f`0NiSU2S!9|wUc7icGW|dRTuCJ1=brw zvK^juNffn$l{`yO-0Yb=%wMj0k|e`*GhNEg2#=JtJubsc1}%QNWZXzy>7!ux#8fJU z8Q3LQRD8U)fG?@JdkJ_}Ti4!zz3ceQhkbZ}10TncDqHhh`WRt$ecwG@VZ5Bs&j=1$ zy7k8mrml8puJ#+eDSg}z#>Ce6e{<)b>c5!9_&?aC>|t+0KqqHtspM<}LnlkXKu`Z) z^m24^CSc=W|9{!W!od8WvaLm3#%_}x&~vFa-Jn6h*vIFe&%bEs7G)DabWSrP(6Az` zr3GWmvHAJoL#$*GRk}O^Q5a7q7H5YSb|jw&rm+}^;~|nzeCIAsC83zq99c|wPA$q$k&MC36cw@TNB> z#J_ik$-e>xuYo!Wd=%k_uLYA*z-*RZMx#N=DWjn9v<(m>0uU7YgCwAWG694pkOjGr zOagQh>5om7>BGU8DB;!g$p`ow*BI@aOjkf(iVE8UZI4HNmDk*}QITN(78SbZ`d8PO z2r2Ki1b-?rp;M5=0AZB!W6gZM8o;Ufn{aRLF$DZbqSmqP&)Nj8V1tE z0fz{Qgjlh$W&+msK>?5kQl)iR%++A5o`x8U!Jk5*kfIp2}>ZC)W6eY>iHLPVL(W;gOa?AJKO z>zK4{qZKxlu4<-M)5dFoii1{6GU(5PuQya3*RBGKOB;yY>zUh&88Rc(+#0+~UL*1m zYq#|r#Vw9t27+UcXY8Y1+ktsZ^W<4+}TyPCmTJJ!hH|N zBrvMWG|8LGZj}c-g(n)b)b2i;)b)C)*}6{C_NvY35?NkryfDm^=_o247pq|jSyG;w zfDc<8yFT7y=_QOR9luRu4Y`IbwXEw0-=*r%Y!^kNo~asM2F#k&tg!5YOpvPe!8gp6 ztDRS&9?eyb)tMvDZMLb8E7pYWx5k@X)v_OzujgLPIwrccBaN<2+pN|sqwL}yta>HZ z_189u2+NhD>O?a=)u9XJ8pl+NvyVY&I$`O_i%0 zn--5gLbt4lm!3*(LsMGMEcj-Dv=;YfOj~zcW@aM?OG`pBQE@9eI@CMPG|hOe;%lN) zh7KBbT{?Z$9TQhs0Dc*l1)n0T00{B~ToD%~pu(}P;`oYa*HfEK;0sUg1rK%J`4A+N{f^*Mwb3>&%28v9ffX5?iU zQ5eD_nDyF=4^uc=m`$3Sp*V=`8K59I8^2PZ-te>=r83M4i?f3u#z5_R#om_;(3-*h zY0g+6E*S_TB|;+hX38NG9Z>}15<0>_BEo5e{K^UcTe8f=J-zR7W*#qk_qA%2299VT zl}*ef91E!AOlQ0!Pa_GB#d`aKi?hFz5%y9LwQw*RV?q1qLdZY-`$=0EM-VaOi#H~R zpH8g(u0#DaU8o?Y&b$F;UexWDlNv~k|Rl!4% zA$joQ?a3>cLIJOgw@xp+6QKk|tjv_v!zb9KD4Pb=?_J8m|Dn^Fb{gQJdgS-D+j{md zjfLTVwwC`Liy0Z%8UEAOajhv8w>65?dt0|hgTrs#v?~MNEA9#)uiq!|qSt@ppRs3Y zHXVoO=t_LJe0&PR={0o~)ffof4|4U&8o8*f!_!e&uG-A1`yOqgAkHwP>g4rCqlH;w!VW?`O|ex--)0e6MCv58 z{l-FHgP!}=U{-0c`f4!ESHvQXMjjfpXxnVmA(gIhS2}EqT~&>Oq~F{GPc|asRfoZ} zi~Ms_ll4$TOf#5u>FM{Q4_sbV?{u4|b#a2wePMe0z2+`zay;SNJk5pLu=RfZ;);9Z zhV3TP^HSyGyN33vQ2Us3dMwO&v_fV75*B20E<#=$da_d|?OtdGkfa29qZ>YmjgJtFEi`r6ElQvt1 z`utFGlHCqxLdd;!s>%)Vt*ek!jLnsxakhv#Ap`1LiWBjhjPGa%yN~WS#ibtFFT3JU z`=mFw)hFWE*&t4)v6kY1hnB~V!Y9EY%I;z-#3#v0c6!f!{G(j^IDQXvkcKB|JLS&> zO>Iqf)j3AKU~;Xozd^hBpTPQ)mE&34G;`QYcD*QKy_aFJ@R&LBK<6OFk$5~ zFc|=&)z2FM#__Eh;TlAxBZgbsW23q`PanUMYX3<{SbQXAs3HVlw^*4DW2E+dxurog z8#AbcL&9sI$DU#y}mtfQDP-{KMAguw{Rv_#Ze9&8G+EgFWF(vL#=NV_>%Ld8b z=_?u`epj$9+ttV+qBFUYAJW`n2D#_uXYH9IT&hS5SU~8$@$K5NHm48;UnfSk*(_K= zZCoa;IYszlPhh((d~MmLP5fV|-z`+0b1ImFl-Olm&r7~~{;t+3U6@KpI=eeKz~Sl4#>*6bGjtM;;uq$^wznng%F-eK*+gMb#e}iDm3_` z%lt`L%=Fd#lk>uIOoQ=@ekj-UXoC$0gNZQ9%9#==yYPJg$ca)_+$VH&s4rSwEq1_8p|mXp>hVe854t4wH-Ee40B$ug_dW3!!0(|P^SLdNpWe6C znctgoaVCZ4D2hnX%e0iIYA9i_geR&+;c?bKV$8)doz@3+58&hrSZ^xpQ&1|Yp2#+? z@EiE2($;~ftEa+FyyT{Gr@}54veUFv5%1QrC`;KCrVoSgWoDuD z$)ws6X&-a$od;mEzOxHRH1&4FB}B>hNiiaWByPdbwlYiG3w+CAh8rV)S0rvOGvr}i zQevQ#(utVa>n{Lq!?owYHcng!^^c%IR7!=Da5`-|bj~{zE~wWu*pY_{n?013o_eZp z1^DdL&l=+i&VS5&RJZWSt&A%k1U}a!fahH#I8bRA;cs9ZMMacpb-iP`S28qaF2{a)0h^G5O14sfB5SEX6q}!FRA#P(FDqxgIiC} zrN4az6s2JJfDIcl!PjY@+He1{c}q}cwp;4 z3U}kVKy8rN+2tlC1h(Vl=C=|H=#l~DIZ*HT1itd*L`Y#$D&+oPz+74{jf#Ml4-~%ALB8st-O(IF7w{`$hmpU zlgpIi?25m?PBEzR84ggCYZlvLwJgsE%U7F2*nfgwEO#MRE^HQM4l_M!_;@94Kn3~S zZ%?yu+Rx>caZ|rjw;3xlHwObUS?5SAk2`v&cH%pKBll+9uFc9viLif%GiM3$;*{KD=vA8PZ7+)LCFW)O(2c)<_v#z6eXZR zQ?J(E#f=)W*u1w2+?1Q9$nL#dAbS3(l0`fhSeO$m3giOe6iHxvVs1S6-|u>Q?;}t8a@yp$iX?1USeHx z$I@bB%VA`-ajFapD>nY!*9!|$tQ#)8fg8ue100+`!rQz3<)5^$kcB7>o{UTb23B-Q z5I|7C5TSt_T^kq`kBlLl90(=ni=T@~lp20#&06(W}S(8&i<3CQ{X*1C}*K^glIZDB=_i$vI}p{uDcO(X~^2JnJviP zwWk-(L6)CwKC95pAg+1er{b!V^3CK=BHVI)YGX>i+H~*E3WU0Ix`gc|dhvX;)a9Xq zrv*!~%Lv@ZzsB?(ZE@hb(4u2HYH!yR22X_d9sHfV=>4FxDlgHjI-|F9;5#d{92%N- z&Q2p4M}@TKNAi^`h`qlqZ{F_JP0+6zc8f(KQLTCXeMb?*G^2(#W~Lz&lLYarP7G*U;2!_)8=Pvk}u%p7UnCst&r*^B2J8;R+wNSY4WCN~+)hoZa$diZvy`I&L~Mpo23`#OUH~8Q<$&WrQJ(K8`542B!_v`YRK(*-DKVeg(AN!1mHM z3R2;Wq>>nS?{Y9{eVLN=B!%`&#w?XuNTjpojt1$1&+sX0ejo`n2nv{jW~;PC0xD!j zVu_HEQ+2pbO|&&`x3F`E$)gS1Nt#895X z-aidydv);|^Txm1(a1u~idPmOXsa58DF$nrtvwq>l!ZmPvJp`_J%+$ft5WxXT=UM! z=IN!L;|`%p{xep6v%HE+Y0z-C6eG(c%ik$#0qGgEkot95wgBm-)~gfCw%eumcK!brpShr4#7Z-ek>Z=8?)0TKQ!Eb|l5{EXLg0#+ zMx|}+_wL==PQ4u7BlS}>TL1!Lj)Ziv|LW2oqDOxurngVH)-b2ky4rd=pKH!om%3cL zJUpIpprHdUw)N-Z1#Ytu-r@ivfRTq(mndyIM%KvA@ z!o)!TpHlbwAFsM8`rj*-&J@9tnO=NK&-yJe@%?gL>^amf61BD|EjGUae@r9$K6|Gt?2uukRJI8IM<}xVJ^opIMO1BKRrii^C9Feu}Co?+tv$(Zmmw(Ky#>Y@_sBr5tKgmv8#1GK(B^Cq6$Nv=?1Z zXJ60#Q`$F3dPzrUBd2TLtK#pY9p(R>wMBRCxX zL)01viXR!*rja4iodky|^U^*RADoG9+#5#?Cs{_&sD&>phNOdo)X?~H4O|lX5*A3B z5RIou9bcFO%1CP|4p)h4EUvkk@Z4co<}EnPim82MX@`-l%aNfrAh~bzX+S4Yue2hP zq4Ng$wl&8yy=ZY#7I}FL6V(0tpW$3HwPNN(E(_~%}mEnE3!xWfv{J)`WgfNxoPNTVo6)wRhV52(>}o9cRPWRiXrkdXH< z6*kbEzG>aAkz5eAF(GoS3RJ|Lxr6fVa;UA-{jm>U{0lXH+Q1)bXjruPV$z(J+H(`t?f#t=Pyej9B-;NL9H@Zs>jN4go;hJ$*KzlP(kcQ z*t|U>Uv2BJX~j#&w47LZuciF$G@UUb6MdwvdjqFcECrIleq3b&o1;dyR5v;BTE?8+ zH}LJZh>;+!U^R=J7NJE?lxz(_bU9iHxXzoZOj;BzJnvSJ1U9r$(UKJZP{^*`fR*>*_PbhYDmRkaRk( zHxb98Sw8U>+uA(_-_=ptbZpQ<(~mq)Rm+mk=eVI?dFPE3IBX#u*qh(C#>)!eh(b0V zLNpcO7#NHa@u%w4I+hwhQYOaI4XOh!8hL^4>?5#BhodtB+v!Vo26K!tixC>5fMH>t zb^|V^M{Y+(?gtF<=;-w?xMiXLH{3F^{>Qjg*HYRPMe#jVd#y2zWb^8qKM(345t5Ps znN?cMpdyGSYaLd1B>3qyYx%t{7)4WtIcD(zvUu@&)#1Cl&VzN`<38asM?hS06=Ula z`HV6E#k7LG{deajYTdc>=k%-%oi44!-j1KyaD%;Iq1W_F1G(hzjU@9L5yR-qLU}q- z?5A7K#gCUV@k7E97gZvasH#bfoiRIzHZU3>yQ;D8t0bKFTyZhh;nx$jJ!He0VXV3< zlo)%Bd@H6)CUJWz;=4CZ`WexKEFrjshfl8{Te;umV@RMi3Ad>WD||T@=8N9=SBI$D zh14F29`w^#T^Sjlz7&`S{x|K0Dj<^mvmf?VeT@%5u=uh-H@I$|#SDlC)hJDpT;kyt z;}dm9^L>Fp4yLzL6-?e~2Z~9WGl6qbQ`DKH$wHU63nLV(?A)n8iRCxhB#uldFjg@} zB@*>c8^>#x=iVA=V&BU#9zS!rVIVe-cAo#3=$uLO`D0=GX^(Kz=A)I zj=ZSQX5{rMh_(+-(Y9)0ZPAmsaWT935qni@E%KR&vPRo&=Sf5H=bf$qcQ249R|1Xqj_K6_j|m_f`dQylKrIitQLdGHZmu7W5!h zCa%5gshgFR)roVcCGZ>->a2F;1StRg;ryWlNJNC^rxXVD>w+Zeg}rZQJ+8`bQk4V> zc0A5Xilq%jRT(HBIkI}MEPvMqEtEW4;FOTw$&-4= ztgWEsbv`u$1nyi@Wws^1k|eyf)N?ddtO|i+Q5Y*pEgT&V5@w0=A4rynFc5(smwjnX z6*70zp7M_GV?d|FircG#k>zgLB$#Ix%>%Uw(r-FqD$1;2(o4HBfpq`bHiEe27CT5- z=zrNW#_|ceCDZkdH8JNN1xRyas+>hSj7BYI`DZBfK)>EkfCI5snkJ5WfzBk8d+bjP zJv+Z`D>Yr14P=O?T(@SAdaIojo<;y06JU4$z!nQjeoTTY6DBZt3TXJlZtYLHfRQGJ zK_KyiD52%&2>*SCXki03+MBqOIeV}nfB;k1Xn{=l0|e1LIs8ms-&Vi@Oy<`Vv6}|m z*Whz?#8yK-2mj#niTKqEccKNmK5)BHQ@wP2R*4jaOD&y}W) z*M8}Sf#)m0a09O;%XM8LtJWUQS(12lcKv~JeyE!Vz`pDXcP6p=z*l#fkw^s*sFE_B z^{d+;TmhaNXlC47uO1-{aW}U1*3>~efk{3khV$;RD9Lh8o3JtUr&cOD=Rov}kL|mx zeZe{oyAEWu-@{y?Q0IXM=AEV-#<7SZ!!nGnNcS4Vl^b2DgM+QsaoX`P5Oe>oVt_M1 z2xsD&qakp02xxw7gGCCSSezeTNHc=GzK`@xO-6Pg^*p&5b?~#UY zhn&OXoWWB@j#ZHp$bQSztl{(ac37PhEp@ivpI896ij&s&zFyN7 zrfg_%;37n&#Z=E%i>VT=>5Zd9j%z#~?ufq5SDxJSJ=}c_NN?-=3234ar!=-mRKHYE z<(7&u1IC@!=%WnI9eLi>a~$Ndu$RfcGg5aJmU8kOw^DnqU97r?gf(h(pQp(oI%{U% ze0RVKFIVNZsf;gsgw-!)_a_e_h9~OJn0E}Mc~m`hwQszDC5Gh9%HqdlQhXn(Sx}HA zB(?V*Ui;Az!Fi9e!`y5f$YeBZ*brcwiqlz~}{j#!Qk*gdw zigutcMR2cPXHVVXK~J4X0kC2BpWM2P34hg_Smg$ zDx*p2l0lQzOkI8V>CtVz#oC-$W%6ApN#5}B^i&*FhoTuEm(4sQ!;%JyYrRY(?EA~poj%>8Kbq?WI4qux} zMjJb)u^=&#YrHd$y(4HhXLc*0YrgeIOXcQSw7!O0o$d6)i!0-o0Xu(3vmSETj6>*E zSldMpO$Cs<;V@nzTynWvJw610` zzaR!hIKbecT*gYY0TDbw)ldl5pa*~diV!tEk}&Bx>Mw(~Brxwps*B+)l_z}iCcSt* zh#+Jz5v_V17xbngsM|_vAI9w=xIU?l+T=d;`7ddVGJjMQ5!~mmn|r7s#s0sP0huB^ z5c5Lh#YddOW@}1NP!j!)@@x~sX>~%^H9>owYGFQXUVr#_HU^%~)Y(rhZw;aVjhBeU znj-PU>2%6O0y^m0Dc2q8lIauA#V~i|mJEg2;nd3t1tMpkeG&MBbs0ghIAF1@b zE4X)w459JZ4a|XM#bV<*+s}mrG2+^!LfNaRzkilgm?HsW4}d$&)R|Ro?f3GV1)S}m z2>X-OOI<zIiJko93eC>^>P{Kn6^93ivT9+#iNaKzKOoZNX zf8DO>z^&rW85@f?(r{G^qeyD{T+W7jE6X)GVa|1klSEftYd>L=3~tPzrdzJIqGq z+D)^U9W=GzTgPTyHzhcpVLT<4_b-*0Z!MJ~F}3SGnH2Vm(n*VLFG!j)?7Y3N^{D&x zJhNhOOT%vmLJ(pR5BiC@o!+qllp`Q!plnX{7EO*|TUi^j2n~Cm=aZ|!PbVP;^|<6l zr|=fH-C(Emt!6hjurN4q&-Aq^Vj{Q|u}tLngtT@BvfK1D#qrjJ27NQ zo~)T{9;}+mccq^|GRkoe?wTYnG1IV!Dq0FLctpclw@~(z}KxR^#9Ua*#1SO zFw(RA#{zp>Ln~>s71eiFZ$Qj)bQqbSHj9j@Vaa{NVauUAsrB$R;I{#(C#51GW!0tL zZV)V`;-KZRdlIWhA`}iDPWF@=t+%g_!!xFcUFH@~cg<8>6Wmy*h1qT0kMA2#N`heIFI$XKg+S8DV#m*u zs?O!>?VciTf!9KEX{hXmc_#pI?JD*o&Z^A>4`UU9fvl_4)NqizO|tvPNE80R)8 zM8~GqsgBa;7>!GdR!~Z}rmYz}?aUWMV~6bmb%FhppK?hW@vilxVeW472GWgc=fjq& zZ$&xl=B)PI&bWDM7xt5xnFbnq&0ER+87IEkog6vrms6yLiZhl5SvrwO#py6kS)WU+ zRNC%(n&ywWFukAcWn*=WsH;eeGvsmZsN~DE1^yD~)6lPbF@FG&zRV*XsHW88bpsM6 z(a>`WA9&m|hh)hWv@YbZzm+NFC8e*d67n8n!ogDmN;&}yoqOO8YIf|g@R%&+4x{Kc zG$bo!4i#}7TCDv!hx=tt(VgdJz-XPI+BJ=p?7?Z(*9yMSwJxEm9+@llZ87bJb$N`l z-A~(Q$Nj0w?W_lSJcv?=qU$6yaG!XoXJDNWx8g88KFS44q>_9xGEjIZiBR+*g%FO{ zrZ#?Fay%SC6vZPXLnIGj`$z*kw5VX>_cZ$0G)iLh^G8hYN=)rZKIf!`b~RgxF6bYB z)~+r6JPot^KOor!XZH^ty2l9cusy#|^0sVsX)jC9RIT`#GoXJTI1k-3jdxso=H_&X zgCT}bP(sLPe`hpda@9d0(P}wqt$guN`7y>%wSHj-Qs$( z>CfNm4wrW&67D}@$IW&MKa)|~JUsblSvWih!@fVuvz7jPM~+p#za1KFFNHvH5F-&e zFIu@fjwv;iHmDc_r{EPPFf8g-h<-;sH74C4N#fyfFgPG~(Pd`) zSfv9JGeJ>lAqgUp!BQTen}!w#Rw5$t10Z4sBH%cJLh^wz2pJS)%nbLVK9Bl!aC9ho_4&VVCM08?B9wFHUEV-yBIyTZ!y9N76|O*XHwUq{@$BC~Pa3ZC*#o z!nmPhsoAyEo<~AjFVL(OPja8@@dl1DIYt9*)J@W+&F!Xa-Ddxp|Bqe_Lgtv~TCgHk zSHgJrrOM83&+ zuyWwA@Ure10A-)&B(vTmGOL~?)BJYT$_63~aFm_ou7#@8Nbz8zb6lc_9di&%UObc({Rh5Q`@5D%xfWY?Tx|d41IlFEaDf2uaqq3KI4E8S_+_N1NNcNV+(FZ z$iTYT<%v0Ub8$Id=!QlCj{oAwdho?9mjFwKxwx+k##bhN2zI~q8CDVxJs*%>krjrL zJ48Q$jDw^%WnuPbFdhY;_Py~%69HAd*BVe$#7;HsyOhH*!I%Rq8=pJl+A48AyWBg0?7*BWQCNFxo;l=bdCPfX-8V?u7 z>g!3`(uq!!z22+i1G6a6Bljij4ZCv{3$Pjsy^5xs!37Frp z_Z|J4$<5k>2bI~~2}{qw1VcJ|!iZpelvUTV+hj%YeW{x-f>)-#O+)Ze4?tB%Lcurz0?p*5X!!NJ zp=LQT>wWiDzSx+u&@N(wb~BeGK`bon^dDR0&4$aM(n`Y7ad=!7tFtrBuV<7D1 zd^G`;V(}Qhv66T&VIM~s**Msq%8zcVPQ;O|(2{-T>(u%}x3=kxyU7!eKSn9rJ;T|f zk5-qX2-?q>#ap3o7@TZUqUG6c7h^n?vCs8|7En{R`>G;tr2AhU@s2?s!aB)7K|)(! z8dOtL)%!|Qfa2gT2wk0Igz8I`lPQ%TxMR1{`EkPWz}x9C`*v$0&XF7p;WMKT`nniq zD?*pY3sd(Aj}n(Pa>D-D$v%Z_D(i{+epl<1kC75>58tEHYgRAR>(5K=av9_ovh1X{ zkBJUfl_CQr2$@4{y}Z{XQhp?(5_C6wIS{mfagG=8tstJ1#wCBi>u@45aQNC_IX{*a ziSRXnmWz(c9tZ?R7uO#f+~0ZyXd0^{&Wp6OHruM;8@jMrj8_cAP`@0MJ$3X0Ky{79 zf9p;Z_wnH+ni?5PGaMl(MT^pjlyaKysNvvxCa!I#Rwj%WdZ(DbX(A`|7`=dMRI9Hb7n`PSq1a;ZSb`i}jT z>GoA9@j6h$!aqBnwi@S)R744>2PG?LsG}E5$y^hg{4NZ|2&<_ZYFhOG1vq;ohc`c) zujvh}PgXo{%ryg?gm5a5u$I{NlX~vBTC^a!JO}202}C5(>9ex}6dnFrisE4Kb&tQN zdvOC@2uTL+cp*sk@xleHmS8^c$Wo*Ex^Q?9>>nZn?hdUs8WQ^)9y@ zfZS;!aJhqq5k}jt#VjUhRcLi(GM4cZzgRM>VUb z{qf;zO#e|^H z6;&vXGxfaXvKvp_$Ch6ZJAuoaa9@kO?gSBU3Xb7|7KW%P7n@Pi^_*SM-Vo#xZ*22B4~KyFabuCtBK z85*MCDCzp*jykL*Xrr@b6cw{b%w2}j<23*7Zwu^T{3SoPi`R`Ri)yaIPYr`8MyEqo zZ2?GRcFKqaugQtz<<>~SFpN#}Q^K>DfhR=)UPg0jjB}nht-AuiNdZDevywo=C{IX8 z+k7ac&=FV0QUe4lU+U_;h9(QzJ zy?DQ@R285g)guTp*)=G?!eM3 zs_8s{-&q9+-|#PUYDGN5-*Vo3wya!f+5m(fCtF(}7kZj?QqocpSCy#C$!iXL)3oJA z%u1n~DI0cM9kHp1>i* zzHh{!AofH8T%$Rr0pNGOXfF`RvzL;S9^LdY1qA8Tp8YjvY1|n$K$8o>3Db&JLn4s%SuSKpVJg0Ko*v zCj3MGc;sPC4Fics(oNm2)0m>=L_)>JB1AoR_CBFUX!pyVF2eCaV39VigsXDkVRd_= z5|Q@02u*vW)X2pOYU}%%W#ju1g)(SY+h{k3=STqfKCv7#8a$8 z{d79EN*$PmWeh1O4@}SxLs7l5!JY^+$H^5LddR^c5gCR^7A6-!K)L~{eNRh$Q49lP zuE-RJVPsC=dZr@8EG!5tUqv7!$xqnSlb|#@O|MpF2{#~U1VRo@W<;;*qUngyQ0T!j z>%R<@80q_)OnqdeA7X}rMENi>WdKxAphjNDcp5^C8_5dJP{9&o9Tc2US+qqh?rbKN zwoszERY9SDl1!PTyX5JY(j>kLA4Zhgs3`UbK81^x!5QT$QKbVa zxUe5(=wAISWhIY1ihi*AX!6xc5^5nTGE7V1ZluCWDo&4bBVpB)fx9730NGHv0l=jG z7{`FA{uXW!!8Kc{J|(b&XZk^tR%?;cfc38S-9kSJJQ59@lNX54N65 z-S%5FbB~3&?*mdEC%#Ym!Cl}83XcFnlpo=~8-U4^O@#Bgyd*`N-w1 z@NLS}=OhU9NZC$=I`=vtvso(5Vw6n$v62pqwjN|b#DQ-u?J27SY7<{@pT#o-{^ zkR#W#3KJ{I#i>h9*6+`GU@X3`J{*TXzXn2GiOUnw|hOz@Gbc3*8;k<>o2#Bxm3_T zrN-B;tyXrCekmvwTRZDhI0GM}E~q*?YMj)S7?rb1?kkqIA*1}8_|$e>-lT+@?gVA}2`!EL$H#n!|P^2l92F^IMV-~PxU z<5*q(2+w92U?h!1@ih}+<$9#|k72!fhV>O?G#^dcmiQl=!y4%{;d|vV#PyRl+QCZ1 z*BhW_M9G=#4e=cIwxlKD9E{Kp9To#qT88_6ZF>F8sMuBXdJ_=`^|fZ5h$P zdcnIydXl(sDP}m+I*okl#;o~|sr#cg#526Xe=fq!4v+s{#aGvdXyU<|RAdTBb(_47 z&n3|F=B98YUQyBu&cNT`L9ELJ&f&tK7hI||M<|7>pjsZZ4_;Y*=wP0VmoPhkF|hRQ zl<>QWmGkjk#un&>z~6A!(s0F{&XJVl|kIE)iX~f^8VWUYT9wcjRzc z?Mo4ktWwBmBLkaS9jtRVf}!;#6tTtXDJ}XSfYqGVkLPzs!;IQ*hmPgm?zYiuy=v{Y zoqR4;BSA?=QCJ+1_ccHEs3&X(Ni<+KLJ1kuwt@$Ex^vLm>|JF|@^m+n-1vhw)|3(S zbs}yw)T-mQ2@hzbK6357X2PbQlUK?VkypRo^N=)>a(mh&<+t-wmL-jL zx;=KhV&cYg`xAqNzP<8l&8kD)&EwPBVO3n8S82V6C!a#29c@2eHxjVPPMN#84Shv% z8`K09d@TTqd{I$qfFgzfk0668^iAFFY z6QC%J&&)GPOlOM!>HK!z=ljsml{<_6W$nG56K9sc$m7Mlv{Il^vY#9|cpVURk1&*0 zUUbjAI^i{%ma(~h%1Rc`He_|^YMiIV9&->i$|_4p-Vt+X-4)#G01Yc_<121z_3lTK zmqpcd`1i7bNa-d@`pt>R$t-j-_m9f+v~H_?W};dN>ujvW4PN6n;u2`Q_`hThj(?d- z|9?4fO^ts-Bl15}=^n8PCgCtVdrFveV}&)91Zhj{_CGIuAvFtaY@OXHEls)mcw?jv zfF+W}8hJdBMVPUD_AhOL>gs5FzY{8iiD|;cYi*GwkRzCq6*9DClGrSCQmT%xzRtE+ z=JbBnNTw4RRKuyR&uT>@OI0%HY*(AQv0^Ao&bhI zXy88(eFalba!a+h>MmD)mtmXY9y2E{V(;68mK0g8PjV~dw=nnpl^I4_htqr4us71q zm#*Ny(-MZ8Pcr3}@19{q-d{{SXzTsQN<-kggV@1d!Wfy}R&w5S_KF)J@B;pDM<(r` zbnqJIG8j?&*y*rWr0GT<$9&|VMs2>+7IrI(Tl9#1Fw8OwH<@$&FUH=fOSEWb!z|mj zZQIr^+qP}nwr$(CZ5z9^%j!Bk`l849&c(S{KVT&znMvlGXR_$M%6HM~DA#Hm!$f4B zbcWVa{JbYA zkhD%6ZPA=U1OoU1hM0`A^<@*V^#X3AGD$3F#JL3)(<@I^;Pdm-5jirD7|TUR`DyEX z{tVLG(F{N)=u40x$$}Ollq9{4WVvZDg$(4ye6>jNsV{mIk|<^vuuFft+>ec1>COl; zhmceY$&yS4^L3OEC&2TOav55L!>DN^VA}>ugFp`R2-H*Hr-cLS$+*8;G3KDcBjUdu zj#n|!RysPGDYR&@v4(QdjmK{v+Hm&ep*|WK#8-ov8<9A5O!9o{yJKi~2PIDQGF#ww zee!8!iEAmmDWCBLb`P7nDD*H*fC68Gqq#Q!b2|Qe)~deFsi_A>=}6I!v3fB)oj|{R zWff#=Q2SJw-dUDq(t@jY(fIs`x};^6+pyN9a`-0->mhf@y~O(=G;HO!$8M*Az1@vh z`y0?JT+}e0EQfy=`nm7xgWI{u&|Ff~5^cio!+*GKZWx`j_rAuqgLT+%tFnEj;;S<| zxhc=*9Nd&K{>i4J_9`eLM~H#txl3F1u(UYG?G$m%r>Aii(&?HtUp=mrc3K+*B5<%$ zH5G9*KofV@^R=3S)Ehf4e3>0oo4}=&T{_FBknwf@%7mR-znj0~XXt`0szDY<+2qHi zY-$&?QI$uU=iysQemkv0IC4PhG{x z;2ktx7AE~jyd4L_zt^6YFhL;6SPqrhRPk!f#I)g(M@Z5a4VUn}@HvjSnl6IbD6Yjm z8+V!RZ;8*3zq`jhO;4aaT+Gr=Wh2EyHU3Ae;`F2t!X{5T4Ni*oqiY+?p@XN{7sn7t z*;1s~_km3bHf%Ob(B@P{{;tX=Gj9E(s3{;Nokm%K)%>`HRHmVL!> z7ZHZ($)*EBl8b%A^>oEocn|hQ)Bu$QaNz^h@(*dc97``r{%{&dWJzC^UnxyE0BOBH zdg)_Aa_jc4w*UMTbzh-|m1q^le!bHZ+YDHHj{*Q?R+WMOo~r){Vekzt-rd_~8zUjf z7gZ_IzZD$(5+V;W)UJKvjBB@hZUyk$HeeCU)}5gC*b%DcOh&caalY$~qVKnx5hDlW zI&5Xqv9-}^rXG~TW>)$LcR7;*TZzx5&U2Y5{Pj+Dl!t3_X=hJXZ^*J z)5FWj1s_gf0_k{J+^+3O@Z(B1@B154J%U*Au?}GH4TcS9XBe?9KN5~us82yPJ{SSH$JdYTEUX>5jK(@CL3L0{Zz7Gs zgO|)CP0qG76M^5)kb$x{Q9%k~V=Ycv;pSSZ!7?O!XqXeq*q1ik>SQ%feE}WV$McU% zFp_Z@%6W`}pWOsUNr20nz};ZSeK`hr8Y%Q5WF4VB_<1nZ7gTyko^d&MPK%>1hsl=|F}{=`kv%SAWJxFR@Aeu9w{%lGWyJdy zb$@kc=M~8x2SQqZ`_KiWrmb5OPgw%N^bfb-!JJNcCJtB@FC)mk|n5$HhhXnr8wgLqo_rlL0cGm zRg+mhUb;3TAmvqb8X>wiR}{}Da@S?F`3o&l5?u;%AIwd38t!a|972(#5O-=9kmQdk zxGXAOQHArwkimpxmK*yd3&J&+7)~f@9E3EQi|_(LT3F9)n*gIv5U9o{@>mbHtbrAej$1;{5sA-JSth`= zF6pqt4&gaTFzn9H+8DOlYu>Mqp`GAE3eQ@`Ifx~Y55wZ#d)RyZIE1J&Sa9)1Ha3p} zo2gXkJoO{s2Q;n;DJ<_m0#Dd-#&wB;q)iBW3kX2xzhZ! z>kWMn*x;^fi|6Lj%FySYZ7ZDU7~yOv0@yTvJ^roSx-c$;rVv8H?poxY#w&E6ix9j5 zS%h=`9ItR`=e7&%QMXeiscfm=M|SfxUL9-q&Diz!HL4#&3`8J7LX-d@B9QhtGczW~ zq1j)PM;xtQSVS(}9+5nRE`D{MmgTqG&5SCaN7hrU$Syji>W zU~TpU z^39i_!yLc3JFy4`@gkwy@$|4;gCb^#OP|^FFj!Oe_pDJ0U(K9JC2@p#3T(TJel66E zW3tdVb<*(5+9)g`A^&&v%MGsU5KxS?}%6yB)kAFaSYnrd(OZD5KQ zPT1-MZTg^4Ngxh8WEr2}_N*0$dq83GBH$3|eCj?KSIu|abg8-Dy;ST4uh5#zob?7Cc`6YSWlfzhCi;QFwpKCz%?Kna zU7#G8WklbkkJoD@A9?6xK&TzeZfg1rCYhFuEPLH8o>j2eFWnwk!-8bG?CW2 z*!n=x%+W}HVxj7WArA-xC|!E*ROt+1MpkJ?&AgTWM1>srX85%f%!CCveKu~d?5EcW z^DTh*L-#C!?(kYrqz4g8`{Ltlxk7;kZy}9!DfC^W}NCzr2nSRlT8*>^@u!zTyt?Wj{X#gu%?zm1d z4{X;hzvz9>4}ym<`8II%rYk`#EzKVa2ihIeL}xYE5z=wW7hDJ{WejzrbHN$R5G3~> z$TV-KfP-X_<1oRJJOYwqmsE@z>1LQlBoM#;p)9HZJ=P(H^f@V5Obg3SU z-0u`M2L?6a)4+lu&=|LXaKFA0Yu3g!aE>2*v& zj`2h24=0{|$6@U3i-u3^)${!gS90LmboQ89-TkJ{>d3be(ybl)?Zs@C!hT$=74NQw^? z?42MO3|a|Fzakj!40^=2YbNU9s$ya=r-+$!Xw86F{~iCsuZO0pTh}HNs^~#QI)&D8 z4Q(C!r_*a|l>l13$EH;y_+=)_CI{_N0l(LsZV|Tmp?0}y(j>)}fS=~)R_os8*-Ib* zXrfr{QjYTsR(_kOaf%0Mnz;YS^khseA$%Vj5o2GQ+|$n9_S{d*lUpxYIJwqm z*mdc0_+xo+ubP~LcOad-jopbyR^2ka)s4K89^jSI5yv)JYW@?fRxeZc($JwM-As<- z>A*vQabEEYgOf^a2C-(Y*87NyEPKSNv)?nVR-`* z$`CP3QV~7uihV2e+R;1+5IxBNrACFyPg<072so6zW2(@s%rK-2y1lVoNcWd)^ai`VU zst@`~nGL#${id+bnIKH^VLKruWP>27xOWM5n%N22J>$IsF_=y0H(HKHn+QJ^-NIsFS$0Am8MOUPQ=5O)VSETdw)n$46iQzn}o$>@Pi9IFawVL13LK^+~B{IVZYm={|D2_%=EvPVK>?uPTTE> zzH4>qlhWmyu&Er9wbUvadM&#oDv}y0=wA5cgvAD~rsbrZtM^w+F+sv`jAK%btFoVl z2>=kz?4A59eEOdcpT{3q$gF^?!GUQOA>Uq zHwK@cE|N(V652M=j-TgLbkLqFI-C6pBC535PcD_Ab%ZaXF+lU;{N;c+-B# zEEz3{o(nYe0Zmbvhp{y^G4Wnl$Lm^qGEt#dK%nW$xJt1}qemOEpwVKdP035~R%tJ+ zeZhhKccENKL6816GKlIIZK93zXVzMcn%!-sGe^Ca~5mmwIAk7ko}I7lnrG> zm4-=jae78iH<|lrCFCj&8V1QsMmFi(MdK5p#hwbD``w@!=GM+75P^7rd|j!*eKu(ff!16BI< zW~(liT&SJJv9YC4fL7rR23U4Nw88L3qL7ii7ITn-jfw45G~aX>k<7;8lVV37#WL*o z5!AWW$5)Gu4MQNMGe*&H3@bydC5$yT`-`zP*4nyObG~ zeX7Pvv$+Q$!f`t&=N)|!)^b-xAkx>CCxA6qUcuNb@4mCpI?z>yInNF6P67PBONO3G z)wZal6L^0cj+hMly6raO;p$4`8m9>K$>70b-g|*&3Yy=0(cQUHh21RAVyf)4MlY86 ze(*9|Cp*H{Y2W6>rP0~CHa7rn!cmxjy&_$NqBT&AL}jpwiw*Xod!a;XzaQ8Wq3%t;&M=;R=>=<(@^ zHzI;FWk#U^#biX;C1Ma2KtbH}R3k=ANeeclrai_}EsMh}cNOqV3Zel$I0_)-M3TXm zM}5>lAmjMG`y!D2Fpzw4EW}e|^O5D3PsZ4zhJpg{RSiaCGdMZtt!ZJk_(1!FKc$$G z-F^JpF|+aBQ&uCuC>N5HBm>qt(1kp@i}Lc{9AY7qaY}_qpzw}Ct?~671i>Y}Hr&U| zQzU!3m!qJ+r(9rs8be{|dN2CjtAoE;6{>e)$t&z33V2w^Kw`03ViNlqd0)=H+dYV- zicEPiGNw`@5ZO#BIlgZMIjC~rJw#aY`6!FdpgB&EE2D?sLtRo@yWA!GDK`>ke~}|o zQ$q)GqEHnjX@uG}1DIY6xwfEsz>z!3j{8jiG7tlhOwTXC%&CpmhF6Wul0LE|Oo4_s z+z^6D=mro`5JVc@Xq*t$wGhJ#2{4xB0Ai?dF2LX@1xROd2q0QSSX~@5$j3J{9bk&Y z&zcR%OfENeTKFOy00V#{!sK+tsK8vnowZTg6= z>=$FHr79O+eS=Ht-cXpRQpf4|7D0}uRMBcYM@Wsilr-(2(b|iP?wRz?<>o)}1b&g& zJ4+qAw(6E&UdI&9#`qwg*86h|O6#Y8Erj!2Ymp|-a6&SAYN>offq?j7d02dKR`JIJKqS+1H0++t z^t?4etkSpIQKPkt3;YjSQ;GCwezw*M zaX%TAG%m8~Sf#Y$X5|aClN+81Yi6k&);7Z>3B*_VrQNqe)Df#Y?M^yfr6$Pz`fwk3 z+I6^0*+cg8Us>{e)Q~5Ib28g;-gmce&JCAWi6 zw~XWYckq$OQAX=4q@SrkG&pV@2kUY4(I#FhL2{g<(s?NWmh6NOYT_DB;jPiWQS_Oq z-FBiwcq1SsI=LgSUBk%l-K~OlDOs**i5*rmX!GTvlGazw+BWSck`Ff|YlJbmIPOOs z0+~l5wn>;q6O8;8+51Voo!X7{9z<>h=s?LO)-Z=$Nk*`_iLi! z;msI}BPJDRAn`Zdz4l@c>izu8*xD$O`BV}e?Cm184qw$L(cOlAPuCY~Y8B)091MrY)7}$UWImloU-f?Dx3(QK(OxX-mH;)t0=w z;%SS8a5v3FQo-M8z2;7 zI){mS10!^9*@1nF1|WE0AviKwbvKk1;z|UAzx9m9X-$6r#G9KRG2k%Hevt8zRq>;= ze!_1CficY=0Yv-|X7YZzYv6L}n!=B>v?WoY4Knk{6a4$ zh8$efPv5PSD!rTx>O4Gav!NV-&N+7J6l7`CY(405XHuXVXSFe(p`7-Xv1D^Si2gKh z|J{-9$vimvoZi!PRrAjLc5(0nV{EtKn?mpC;=pQM>Pqw1prnWUcHJ-g8+nqo ziz?mV1B-Fl(KXbmpxJ&kA`!cBH8NlA2JKyL|Cdf#Pxaf^_U=0|*d_R&2UJ^b8_B>o zkl|SfEh&0oE>R`oe5CHEw>;S1SO1%SF-Q8A@QLrX_I87&$*BkDfLmJfls)`wt1gk- zK<`}$5{V?B(YXr7Ocd63)|R%KDo7IfG;z~(-9~$u*tPBm{>WB;k$ns!d7{Bn84-0V zY^U^Xu_qWmU)%RAWgFLbG(0@1=z^xV=6+T?5R^Xj->FyL+Xi~t93P#O3W1c{Ep#8z z6ps>8+nAe5zngdk4yuSm>uqZETS7koUhiX=Mx=ZK%q^)OaMN5nClA=HFKk4*_fUf! z$1_KnccQdsmmN3$ZL1u#0VIZ!Z%{osPt^vp_@zQwsU9st#2GlzvkSmN5G`}O(H2V> zDCAHC=@o4DJYlvB1}Td)ISx(r@~fG~j_tjQe%mWmy_PrXIxU#rVhSUKo{235bn3ra z3gWr@$wizV>d8=0NNokS=J+34FCE>KD(?pSAHV*R587J$;KU9u3S5lDcPz?$lx3D_IDtRM8L*jmm1)(Ua{msW^@f%Sh@Wot<%ZM7r*Ld?!3 zQfM;?US+6M8k!sqcV<+nawkc%fN2a!YX7qWHYe-r^9~xBOd;Jm_FQU>FTqpv4l*VdZREXxxkI&=R61woBeye;& zT~jt?ANk}P5tLEg&8flZPTH!9O$&`wwa9Mc?%(H}i^$HyT`Y^eLqCDq?$C*4x9u*b zoP*S^!Y%tO-Ro26{JJP8ztx;+B(JWH2br~r@X4yrHzJje7sc?aQg!qc%BhcB>Mh4L z8zsArWg8m0EY-?Sp4d25ERx+-*R0HR4FloJomJSEW|z0t@>RtS@V~7x3VV)OUvs&| z^FUUTNFIKqg4+oMV2AF5Fb~C11;z$68cx*H=^p8xFWMH@HK?kcnHAuLc4cUzNUItU z({?q<*~$%#!U=4sswfg;&fDE~A+I+)vOG$E6_VA@Aa5eAL`U>csNvVv+eCZN4{QB> zHV-pta_KirIq>2C9a^vw+~#B!aFTSi>f(D=crGO2e5QIXSA`$X*LhI2fC zm5a%gU6QaHB9@zCG^B~C;C#FO7}5R!+z{N{Pri`w|Y9qJpBzzUlakY*k_$Is6x|9v-r zak(@^(ktUcD3wb82Cij)+;+BXA6<`oSdMw=S{l$#FTP!Sl@p5y6{)Bba@nV{7ZJw4 zsNw<3co)p(?qoo;#=7;$pP2@3)+J+y< zpjYINpubgfHt1_~yG2iKgaKRZFl`(|RxC)wK1)Q?MUsg3OBYGBB6X}DAO?$7eq{na z8%{l)U^QG?3%?2YN8uq+k<;=q2AmLGt??n5JEwds?y!!Xkr_WFBu;rQ$O8OwGsZDO zU z$_R@2>40y1ZAq1w7r}~T{*Pk5*p`$$0Br1xVEE~tY4i7A$a{gZy4mI_pjh4&^CNbJ z4}5>7E*LWMOzzF#SbM5q$OmD<;;o8gTy2skYMniL929S5vkL))#EVHBkowD7MkZ6&NM!vO zKU#IVi99*DtNiuzl=~rB#|!Ekhnpx%neBIP7Vt}A6)CL;l%f0x$KjJf-U52P8TsxV zugnXDsZxp~UMknoN-Wx=>=9m{0pPw?PkfN&1A^2LLc$M=P_HE51L$fyv*{M$6bFff zrPR4_2L%x*4?rZMA&_ED5lAW#0!fY(0?|TrHI!E&o&aPsB_JqEV2-x8u!b4yn+HD? zw!qX=`KIaar^ew@L;c}a;eS6Il`}BA8C5Br1F7)C=&-#u*C!A)nRdAIpdeRsuu;d# zmaN9}_l~N7+G=>~EK4Tv+JG7{q_xu1?G-j11Fb+$BJd{>e2Az1+ePqcJ;_Z~~F{%Ej18js?D4?4=I{gV; z(E%ADvPQS0IA%Ais!lsW97+@lb@e_QL!3E4Up6U#)b~z5q*(!e_qb zlP)_bJEqjQ)LV2}hpc&LYD|_VzRDa_qskDjn=kOv?wZ4)C^|xZ_unF5!1iT`L zbbs^j;RFG>+V|vo3k8!U%QkCJ&Um*!Eao!tq$G&>_RE|J+eDKoqabGU**t=Qm9KaY zMe8Lz6c(8Pfhe^*x7H&iwX`-(#w3I<>E82w6#oj;H|7+8i%1L``meeSy!fsx1Bw1? z)cGEMLOWN%6D@}5xU{QN=6W;S-%plQBT7O33{X@8Ru9H=1>LQstTbt$4rw&+$SB|q zZrdFXAzkHSCtznwDJfH&*FSa(o?my!5}>is>UP!ntb~S&v56pEYS$T71&NPbWD}pZ z)~A-{ju`7935nsguam<_u7*6AAfEf~Z00t@U<~?mx09KIP#-9uM0`7p<>?0sOWJ2r z=~g4pwx}?%#e~ z_{5*pMqzhufc+2VN1%;+!M+y&Bt6zglt-z9w12^}*Y{DAnl_d-nc=X!4 ze&`{w+~xGRYlD-VBZh0PAs!eX;3P`@QTaeF%PUtRow zkD-9yS0K<2^EIVF(^|*IF~~Q)NE5`TM)fU>O^|#@WC5E>GonMd%Q6~i!2RL>WC7e~ zKFwZ`3-5qkHwCx9=#`~@R5sgq1s8N(V0zeX$7SvQ^v~hT+iffkl(xER#a$z6=uKW} z7Vf`BlQP+nO2V?r1RFg7J4OCIL>d=Xre>dFAyj9Rx>K+4^eknd4c51X5(|F1`d5NH1Jk?UPqYx2N#zsX zvL%!IPmGO%(X(=kW5n{_$U%#2N9%q~$AhVJ5AidxotuI#!J*C7-yF2AMVc}_0&%*j zAxKx05?yK6fe9Xm@d zRcXUXhOl6ve)YFAXe=07=!PwsTnF$^-1d?@X+vTyu|a366%y(6(77LYg&$2hq- z`_|I!UGas1zIfQgO$f9?iqG$h@&IFNdO zX>V-HUWmf}zg&$YjtE>UE_zo_G`C;>rgD@@u%?m7et#+#K%tN-ZJ5#lsjFLkR6=<= zJRWaUG4LV|&J{}JZg1z1Jdhv;;0I4k%sd>gdZ}eyEPf(FsAQfPW04`oS9%z*{d36i zW1Kmj%bGH)TTGgsGsHY%8<da%w?K!ge`Xnrq(SGCX8g znRb?ZS)K{(rWZ+cAy^bj6#Pn;##8}52}mFIb-|^T1-lLCf#uiP8zX!4cv+z^pE6MF zXPh~HCYhyXy(F38d%YNqt4_&O@XHm-!jaXt)N}?)wN$5{%j*Y)4JZ>q(-S?U{&}qVYW-7l)Yk6Lvp3IjJ1_dZ6sK5)}#z>7glF#5_A`%>`W z+%dh?P30mOMF4dE-s{0PsAV5rRV4ES(az%5Nv&B1+kORr$k@No+>MEtxxe(wNMP6rR@A90b&n`>tJm&8Pwmt zTw|K})OTYXslInTA~MZA59ZQ=6&I;=xDVlqYw~oWF0r@SEUaWYpV`R?F-I-SYTwP1 zi4`1=C=x%2l5=FTdlcme_MoHct8KN$O}VG?6s{AJfiK>pVKCYZfXgFha~}Ft%ez$5 zJLZ42eD**cKCF+eqQRi2a}*8I*plMlx~MD~rn?r+lx^yZ^2Ia8o>x5s_zXJO)kakz zofS|)P)|7=j$$a|?n&tUgkyi(07Jwt^R9uVe|9ex&k0%ye*JSU$~sqO_~y=>@+!Ef zm#K)+y{}m$u<h8hIbfcd2uLcz8w`JJ5Yu6WH!?oj+$j(3PcF{H9z_V@s>D+_;feaBaiuI8(9xP&2Y?~?WcYA@6qqV=2u=TUtQNi|udQhLAP=@4nPCctbev6yd6WlC*N9LS`+vGfcp>Tp@9Y zCvLK$aqI8mlJ1$-pF!0cM&2<`M!Xcpym!$$n-xoN!O9Q|08MX*g|dt^hd&C@J1S_r zoq-YRqKm$r!&xZ+`mMPPL{>ft7^fM~I)8De*Xwjx?y3Jl$zH=11b1eRyzFip(CFcK z$NF_=fhW0LJs#%UC>+U#bgitoaUSJI;< z?J>ReZ1`7iRW|=?&+{F<;a}}!KqK%tAcgN{iSIz|t|V7Iky>jx?Ld#+Hb63RxE2xV z4VZrNhklUcg zl4ueIFp|7pibp>!m;f1eY@@IhULvTX(OhZhP|P=$@xkU@a(IMeEf z?UWHT18Yz%bZ1T|Omn_Pqc)l|pDC)fQd9|yo+6kxpqmV!T9Vs%C~{3c5ODY|1h4E2 z37GGk*$@vm++^8wqlvwBaP`t{pv*aslCIc3g{7fnm|xEE2E}C{8UeFB6d7r>r@53* z(*gVPFPV#%w3@tsTxzbN{*e(4TK!wOJ2z=B35D9^Pr#TNG^ybEL= zp-m8Ht*t#4QDM$lc=#6{;!Q0YVwt$Yw&6*-Logx19t54{+AAxBj2i-lI3dH7cLVi# zcHmJ?;D}m-QIN_iTkUrFnk<@}}DMw=HjNaa@&D zTxXm_avljU0N|L*H)g8Dh``{+@lhjdaI;T1e)}=8N$t+)e1$nKqe%Ufr46&`asKA+ z+214KjIK#yyEjU}i`)Ee+uTO|!g+YZhB^n#a6WsEAdGk6X-BUy&{XADbpL|b6b#qz z8DeJJz08sgeg3GPrP>;aC)e;?=Vw)V*M|en*Ye}VWpDA@F+$jyM-MY2FeL2-|1a9& zN>M;Q$LVqO=&PboJW2lXP)vaIA^5B}Zf?zkxkxfwHag_dduq+@%l7qfMKDn3(6Cb0 zEYoX7`rxjzunK>ycR`eE``I(du4sbcv%vFx&UZG0nL_g5c-k*$R&AGdMB^2^3{M+8 zHUyju&xvJ1tlyg`B4UvsSJi|EOYCOj{Tw-v9-Q?_03j>`fZ}hRiNgtwJ5x3%|IyM% zorTjbaw5DvQMA`hRFyc^+1=Ync5j+}Gx1tM^bWfG(37$wP#v#}-3OTnQ7SSVCD2q8 zXaivlr3spSI-HP1;k>r}1b=1q2kghk*G%S=gSd1uO{x} zsrB2>V{GQr!$F&7?&|2&d6!1+_^+-er=PoR@B5QG0CU%S-qI=mUz^uOTi89Wl%J-z zL-(=MtJ9~8e~nsom(X)zr?-W!yPs#%dHqsfmrt@4Z-yU@y&q2pWw(CD)6}dvJIrF$ zmQX!)^!F|b!)W#zPmCyn3Vc6PhFvv<$bS&Ld$qFjuH5kSo~Is9>-kskKa;3B$>ff^ zHXHx_VK#`MOm6ahzj5#VH%Hl!-DTB}Q}xw-0aVvkCj4ay8OvVv)ZprCyZhzsc8hZO zIMn;LdSLbS?4U3Ty;^w~@fLSN%ZGCnwQE~VegAl(QP3QFt2P? z2#p-sMM2B3vrXjQpvh0C+gid}dRjGt2_szdrW$H;I>TDx%Xm>v7*a2r`ZaaYyn(Mv zf07i9qS$_A=muz8ZwRon22q}B zZ?~TU($rN)e>QpI_GA|IRq5Nnwe0s2=Hzo@+PB?XjfG4H;Qpqr68RA5g!=Ve;rk;$ z`k|?o{2q8yK=pn=-RueRxTJ^A_w^hRVC_6=D&q)5l5Rm2er(;k?el)3#-%k7#y>-W zIKS&#N4GNr!XDh%_-tqP^ZM__O{^J}Sw(#?=9(EO4Z3|y)aWSe<=6=9aKXE@nm`P- zB^Ym>4oco~HXse6w?kRvk?7yoV;7bqqQ5=>C?z8hf;JMdF-HI1YS+-c)^J^dh(pw2 zTmG>8SM9!u)z8~iMd5ac+XRgQN1L{zXmkWeSfU%pS}KGkQnsGz=OP#@Rny^6&wi>8 z0>n1G1Vb(qp=b(O=)fNa&~ZX5+|)25(M%?CXSFYuTfx64iU=km=RFhsrlk?Ah33rl z1evVP$Z6_yO)=>^Ug%$tnFwc(M2U}t)s6S_Rk#m;a3d~o+?!6Bl?GmPA1F*HDdK^F z=}@l3i0+9HaC8tV(kP)26T+yzvBge`7&j(PSdiNNtd{=r373?Fed_|{@DO1H0Vo0p zB?~0u)(ivz9b$lC8v{@x4A5-lGdFtWT=_iHfv^LnQo@}Lv5j?6N2U#WwO#~Sr`&KF zbL>n;C+pEC8qt4{S@o;^`I1xvN{NY{7Zu*5w1ZTGp;ii4s6T_pBl88MRdGPYz^?QG ziS$9StM^7x^-~fd=pKI6Nee(wqy`8uK@ploJGI~y7P>)%S#;bp1#&A3?;g50y}eJ5 z1u5mgv2PC$BM~RJ{}LeH@9=6%LU7MA@n3>}p|66_Y$T9kWLx^6M8?3_(Ni*w_l;d< z4&!7360=#Bi_)zub$}a@i@|U?m)Wr~&zElC4i7`N5l3M;qBviS4vT}a$YXIItHTRx z2;zF8I3SRkhXj%YVY@qYhv!w{%<2IU4*g(Lpm47UQYJv+h)`|jxkMHhS24oBOkVWyaa=wJ(hsfgl$blfVdGRC| z8FE3W;dhXHX88hW`NbjrFhhVSs4Kv8m7jU+Q)RXSq@6K9lP^>ar=ExYQZU?b6#TZw z791JC6v;wd0&gKW7UQFjVb&N(&Hh4(`rCwC_MmitDx3QA>yH2`0F+Tg@ba4i1X$LB zwf;gXCwjPSXJsyPLCYd@jAEd%jHg8=@I{M*a97yC1MV2E?>rgZGdvr#Wk(7k))*M& z97w>VK){G3_AtYV0;Y4@u!j4;hJ^w7f1L&s>`#(Hv81}_A0JXN)#t_gaz{U7mF(BM zr1i4!Yslw?!1f<`AC_n#uHP5Cv*qz&1p`y%B#@%F#j°5~ssmZml6DhM27nL?8* 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

    TGEq*mpImn>umyQpwdnR++ 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 jS %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* adi.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "adi.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE POLYBENCH_2D(u,N,N,n,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + { + u[i][j] = (DATA_TYPE)(i + n-j) / n; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_2D(u,N,N,n,n)) + +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("u"); + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + if ((i * n + j) % 20 == 0) fprintf(POLYBENCH_DUMP_TARGET, "\n"); + fprintf (POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, u[i][j]); + } + POLYBENCH_DUMP_END("u"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +/* Based on a Fortran code fragment from Figure 5 of + * "Automatic Data and Computation Decomposition on Distributed Memory Parallel Computers" + * by Peizong Lee and Zvi Meir Kedem, TOPLAS, 2002 + */ +static +void kernel_adi(int tsteps, int n, + DATA_TYPE POLYBENCH_2D(u,N,N,n,n), + DATA_TYPE POLYBENCH_2D(v,N,N,n,n), + DATA_TYPE POLYBENCH_2D(p,N,N,n,n), + DATA_TYPE POLYBENCH_2D(q,N,N,n,n)) +{ + int t, i, j; + DATA_TYPE DX, DY, DT; + DATA_TYPE B1, B2; + DATA_TYPE mul1, mul2; + DATA_TYPE a, b, c, d, e, f; + +#pragma scop + + DX = SCALAR_VAL(1.0)/(DATA_TYPE)_PB_N; + DY = SCALAR_VAL(1.0)/(DATA_TYPE)_PB_N; + DT = SCALAR_VAL(1.0)/(DATA_TYPE)_PB_TSTEPS; + B1 = SCALAR_VAL(2.0); + B2 = SCALAR_VAL(1.0); + mul1 = B1 * DT / (DX * DX); + mul2 = B2 * DT / (DY * DY); + + a = -mul1 / SCALAR_VAL(2.0); + b = SCALAR_VAL(1.0)+mul1; + c = a; + d = -mul2 / SCALAR_VAL(2.0); + e = SCALAR_VAL(1.0)+mul2; + f = d; + + for (t=1; t<=_PB_TSTEPS; t++) { + //Column Sweep + for (i=1; i<_PB_N-1; i++) { + v[0][i] = SCALAR_VAL(1.0); + p[i][0] = SCALAR_VAL(0.0); + q[i][0] = v[0][i]; + for (j=1; j<_PB_N-1; j++) { + p[i][j] = -c / (a*p[i][j-1]+b); + q[i][j] = (-d*u[j][i-1]+(SCALAR_VAL(1.0)+SCALAR_VAL(2.0)*d)*u[j][i] - f*u[j][i+1]-a*q[i][j-1])/(a*p[i][j-1]+b); + } + + v[_PB_N-1][i] = SCALAR_VAL(1.0); + for (j=_PB_N-2; j>=1; j--) { + v[j][i] = p[i][j] * v[j+1][i] + q[i][j]; + } + } + //Row Sweep + for (i=1; i<_PB_N-1; i++) { + u[i][0] = SCALAR_VAL(1.0); + p[i][0] = SCALAR_VAL(0.0); + q[i][0] = u[i][0]; + for (j=1; j<_PB_N-1; j++) { + p[i][j] = -f / (d*p[i][j-1]+e); + q[i][j] = (-a*v[i-1][j]+(SCALAR_VAL(1.0)+SCALAR_VAL(2.0)*a)*v[i][j] - c*v[i+1][j]-d*q[i][j-1])/(d*p[i][j-1]+e); + } + u[i][_PB_N-1] = SCALAR_VAL(1.0); + for (j=_PB_N-2; j>=1; j--) { + u[i][j] = p[i][j] * u[i][j+1] + q[i][j]; + } + } + } +#pragma endscop +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int tsteps = TSTEPS; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(u, DATA_TYPE, N, N, n, n); + POLYBENCH_2D_ARRAY_DECL(v, DATA_TYPE, N, N, n, n); + POLYBENCH_2D_ARRAY_DECL(p, DATA_TYPE, N, N, n, n); + POLYBENCH_2D_ARRAY_DECL(q, DATA_TYPE, N, N, n, n); + + + /* Initialize array(s). */ + init_array (n, POLYBENCH_ARRAY(u)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_adi (tsteps, n, POLYBENCH_ARRAY(u), POLYBENCH_ARRAY(v), POLYBENCH_ARRAY(p), POLYBENCH_ARRAY(q)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(u))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(u); + POLYBENCH_FREE_ARRAY(v); + POLYBENCH_FREE_ARRAY(p); + POLYBENCH_FREE_ARRAY(q); + + return 0; +} + +// CHECK: #map0 = affine_map<()[s0] -> (s0 + 1)> +// CHECK: #map1 = affine_map<()[s0] -> (s0 - 1)> + +// CHECK: func private @kernel_adi(%arg0: i32, %arg1: i32, %arg2: memref<1000x1000xf64>, %arg3: memref<1000x1000xf64>, %arg4: memref<1000x1000xf64>, %arg5: memref<1000x1000xf64>) { +// CHECK-NEXT: %cst = constant 1.000000e+00 : f64 +// CHECK-NEXT: %cst_0 = constant 2.000000e+00 : f64 +// CHECK-NEXT: %cst_1 = constant 0.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %1 = sitofp %arg1 : i32 to f64 +// CHECK-NEXT: %2 = divf %cst, %1 : f64 +// CHECK-NEXT: %3 = sitofp %arg0 : i32 to f64 +// CHECK-NEXT: %4 = divf %cst, %3 : f64 +// CHECK-NEXT: %5 = mulf %cst_0, %4 : f64 +// CHECK-NEXT: %6 = mulf %2, %2 : f64 +// CHECK-NEXT: %7 = divf %5, %6 : f64 +// CHECK-NEXT: %8 = mulf %cst, %4 : f64 +// CHECK-NEXT: %9 = divf %8, %6 : f64 +// CHECK-NEXT: %10 = negf %7 : f64 +// CHECK-NEXT: %11 = divf %10, %cst_0 : f64 +// CHECK-NEXT: %12 = addf %cst, %7 : f64 +// CHECK-NEXT: %13 = negf %9 : f64 +// CHECK-NEXT: %14 = divf %13, %cst_0 : f64 +// CHECK-NEXT: %15 = addf %cst, %9 : f64 +// CHECK-NEXT: %16 = index_cast %arg0 : i32 to index +// CHECK-NEXT: %17 = negf %11 : f64 +// CHECK-NEXT: %18 = negf %14 : f64 +// CHECK-NEXT: %19 = mulf %cst_0, %14 : f64 +// CHECK-NEXT: %20 = addf %cst, %19 : f64 +// CHECK-NEXT: %21 = mulf %cst_0, %11 : f64 +// CHECK-NEXT: %22 = addf %cst, %21 : f64 +// CHECK-NEXT: affine.for %arg6 = 1 to #map0()[%16] { +// CHECK-NEXT: affine.for %arg7 = 1 to #map1()[%0] { +// CHECK-NEXT: affine.store %cst, %arg3[0, %arg7] : memref<1000x1000xf64> +// CHECK-NEXT: affine.store %cst_1, %arg4[%arg7, 0] : memref<1000x1000xf64> +// CHECK-NEXT: %23 = affine.load %arg3[0, %arg7] : memref<1000x1000xf64> +// CHECK-NEXT: affine.store %23, %arg5[%arg7, 0] : memref<1000x1000xf64> +// CHECK-NEXT: affine.for %arg8 = 1 to #map1()[%0] { +// CHECK-NEXT: %24 = affine.load %arg4[%arg7, %arg8 - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %25 = mulf %11, %24 : f64 +// CHECK-NEXT: %26 = addf %25, %12 : f64 +// CHECK-NEXT: %27 = divf %17, %26 : f64 +// CHECK-NEXT: affine.store %27, %arg4[%arg7, %arg8] : memref<1000x1000xf64> +// CHECK-NEXT: %28 = affine.load %arg2[%arg8, %arg7 - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %29 = mulf %18, %28 : f64 +// CHECK-NEXT: %30 = affine.load %arg2[%arg8, %arg7] : memref<1000x1000xf64> +// CHECK-NEXT: %31 = mulf %20, %30 : f64 +// CHECK-NEXT: %32 = addf %29, %31 : f64 +// CHECK-NEXT: %33 = affine.load %arg2[%arg8, %arg7 + 1] : memref<1000x1000xf64> +// CHECK-NEXT: %34 = mulf %14, %33 : f64 +// CHECK-NEXT: %35 = subf %32, %34 : f64 +// CHECK-NEXT: %36 = affine.load %arg5[%arg7, %arg8 - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %37 = mulf %11, %36 : f64 +// CHECK-NEXT: %38 = subf %35, %37 : f64 +// CHECK-NEXT: %39 = affine.load %arg4[%arg7, %arg8 - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %40 = mulf %11, %39 : f64 +// CHECK-NEXT: %41 = addf %40, %12 : f64 +// CHECK-NEXT: %42 = divf %38, %41 : f64 +// CHECK-NEXT: affine.store %42, %arg5[%arg7, %arg8] : memref<1000x1000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %cst, %arg3[symbol(%0) - 1, %arg7] : memref<1000x1000xf64> +// CHECK-NEXT: affine.for %arg8 = 1 to #map1()[%0] { +// CHECK-NEXT: %24 = affine.load %arg4[%arg7, -%arg8 + symbol(%0) - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %25 = affine.load %arg3[-%arg8 + symbol(%0), %arg7] : memref<1000x1000xf64> +// CHECK-NEXT: %26 = mulf %24, %25 : f64 +// CHECK-NEXT: %27 = affine.load %arg5[%arg7, -%arg8 + symbol(%0) - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %28 = addf %26, %27 : f64 +// CHECK-NEXT: affine.store %28, %arg3[-%arg8 + symbol(%0) - 1, %arg7] : memref<1000x1000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg7 = 1 to #map1()[%0] { +// CHECK-NEXT: affine.store %cst, %arg2[%arg7, 0] : memref<1000x1000xf64> +// CHECK-NEXT: affine.store %cst_1, %arg4[%arg7, 0] : memref<1000x1000xf64> +// CHECK-NEXT: %23 = affine.load %arg2[%arg7, 0] : memref<1000x1000xf64> +// CHECK-NEXT: affine.store %23, %arg5[%arg7, 0] : memref<1000x1000xf64> +// CHECK-NEXT: affine.for %arg8 = 1 to #map1()[%0] { +// CHECK-NEXT: %24 = affine.load %arg4[%arg7, %arg8 - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %25 = mulf %14, %24 : f64 +// CHECK-NEXT: %26 = addf %25, %15 : f64 +// CHECK-NEXT: %27 = divf %18, %26 : f64 +// CHECK-NEXT: affine.store %27, %arg4[%arg7, %arg8] : memref<1000x1000xf64> +// CHECK-NEXT: %28 = affine.load %arg3[%arg7 - 1, %arg8] : memref<1000x1000xf64> +// CHECK-NEXT: %29 = mulf %17, %28 : f64 +// CHECK-NEXT: %30 = affine.load %arg3[%arg7, %arg8] : memref<1000x1000xf64> +// CHECK-NEXT: %31 = mulf %22, %30 : f64 +// CHECK-NEXT: %32 = addf %29, %31 : f64 +// CHECK-NEXT: %33 = affine.load %arg3[%arg7 + 1, %arg8] : memref<1000x1000xf64> +// CHECK-NEXT: %34 = mulf %11, %33 : f64 +// CHECK-NEXT: %35 = subf %32, %34 : f64 +// CHECK-NEXT: %36 = affine.load %arg5[%arg7, %arg8 - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %37 = mulf %14, %36 : f64 +// CHECK-NEXT: %38 = subf %35, %37 : f64 +// CHECK-NEXT: %39 = affine.load %arg4[%arg7, %arg8 - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %40 = mulf %14, %39 : f64 +// CHECK-NEXT: %41 = addf %40, %15 : f64 +// CHECK-NEXT: %42 = divf %38, %41 : f64 +// CHECK-NEXT: affine.store %42, %arg5[%arg7, %arg8] : memref<1000x1000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.store %cst, %arg2[%arg7, symbol(%0) - 1] : memref<1000x1000xf64> +// CHECK-NEXT: affine.for %arg8 = 1 to #map1()[%0] { +// CHECK-NEXT: %24 = affine.load %arg4[%arg7, -%arg8 + symbol(%0) - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %25 = affine.load %arg2[%arg7, -%arg8 + symbol(%0)] : memref<1000x1000xf64> +// CHECK-NEXT: %26 = mulf %24, %25 : f64 +// CHECK-NEXT: %27 = affine.load %arg5[%arg7, -%arg8 + symbol(%0) - 1] : memref<1000x1000xf64> +// CHECK-NEXT: %28 = addf %26, %27 : f64 +// CHECK-NEXT: affine.store %28, %arg2[%arg7, -%arg8 + symbol(%0) - 1] : memref<1000x1000xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/stencils/adi/adi.h b/mlir-clang/Test/polybench/stencils/adi/adi.h new file mode 100644 index 000000000000..24fbabc20de0 --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/adi/adi.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _ADI_H +# define _ADI_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(TSTEPS) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define TSTEPS 20 +# define N 20 +# endif + +# ifdef SMALL_DATASET +# define TSTEPS 40 +# define N 60 +# endif + +# ifdef MEDIUM_DATASET +# define TSTEPS 100 +# define N 200 +# endif + +# ifdef LARGE_DATASET +# define TSTEPS 500 +# define N 1000 +# endif + +# ifdef EXTRALARGE_DATASET +# define TSTEPS 1000 +# define N 2000 +# endif + + +#endif /* !(TSTEPS N) */ + +# define _PB_TSTEPS POLYBENCH_LOOP_BOUND(TSTEPS,tsteps) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_ADI_H */ diff --git a/mlir-clang/Test/polybench/stencils/fdtd-2d/fdtd-2d.c b/mlir-clang/Test/polybench/stencils/fdtd-2d/fdtd-2d.c new file mode 100644 index 000000000000..929591f01b8e --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/fdtd-2d/fdtd-2d.c @@ -0,0 +1,243 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* fdtd-2d.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "fdtd-2d.h" + + +/* Array initialization. */ +static +void init_array (int tmax, + int nx, + int ny, + DATA_TYPE POLYBENCH_2D(ex,NX,NY,nx,ny), + DATA_TYPE POLYBENCH_2D(ey,NX,NY,nx,ny), + DATA_TYPE POLYBENCH_2D(hz,NX,NY,nx,ny), + DATA_TYPE POLYBENCH_1D(_fict_,TMAX,tmax)) +{ + int i, j; + + for (i = 0; i < tmax; i++) + _fict_[i] = (DATA_TYPE) i; + for (i = 0; i < nx; i++) + for (j = 0; j < ny; j++) + { + ex[i][j] = ((DATA_TYPE) i*(j+1)) / nx; + ey[i][j] = ((DATA_TYPE) i*(j+2)) / ny; + hz[i][j] = ((DATA_TYPE) i*(j+3)) / nx; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int nx, + int ny, + DATA_TYPE POLYBENCH_2D(ex,NX,NY,nx,ny), + DATA_TYPE POLYBENCH_2D(ey,NX,NY,nx,ny), + DATA_TYPE POLYBENCH_2D(hz,NX,NY,nx,ny)) +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("ex"); + for (i = 0; i < nx; i++) + for (j = 0; j < ny; j++) { + if ((i * nx + j) % 20 == 0) fprintf(POLYBENCH_DUMP_TARGET, "\n"); + fprintf(POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, ex[i][j]); + } + POLYBENCH_DUMP_END("ex"); + POLYBENCH_DUMP_FINISH; + + POLYBENCH_DUMP_BEGIN("ey"); + for (i = 0; i < nx; i++) + for (j = 0; j < ny; j++) { + if ((i * nx + j) % 20 == 0) fprintf(POLYBENCH_DUMP_TARGET, "\n"); + fprintf(POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, ey[i][j]); + } + POLYBENCH_DUMP_END("ey"); + + POLYBENCH_DUMP_BEGIN("hz"); + for (i = 0; i < nx; i++) + for (j = 0; j < ny; j++) { + if ((i * nx + j) % 20 == 0) fprintf(POLYBENCH_DUMP_TARGET, "\n"); + fprintf(POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, hz[i][j]); + } + POLYBENCH_DUMP_END("hz"); +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_fdtd_2d(int tmax, + int nx, + int ny, + DATA_TYPE POLYBENCH_2D(ex,NX,NY,nx,ny), + DATA_TYPE POLYBENCH_2D(ey,NX,NY,nx,ny), + DATA_TYPE POLYBENCH_2D(hz,NX,NY,nx,ny), + DATA_TYPE POLYBENCH_1D(_fict_,TMAX,tmax)) +{ + int t, i, j; + +#pragma scop + + for(t = 0; t < _PB_TMAX; t++) + { + for (j = 0; j < _PB_NY; j++) + ey[0][j] = _fict_[t]; + for (i = 1; i < _PB_NX; i++) + for (j = 0; j < _PB_NY; j++) + ey[i][j] = ey[i][j] - SCALAR_VAL(0.5)*(hz[i][j]-hz[i-1][j]); + for (i = 0; i < _PB_NX; i++) + for (j = 1; j < _PB_NY; j++) + ex[i][j] = ex[i][j] - SCALAR_VAL(0.5)*(hz[i][j]-hz[i][j-1]); + for (i = 0; i < _PB_NX - 1; i++) + for (j = 0; j < _PB_NY - 1; j++) + hz[i][j] = hz[i][j] - SCALAR_VAL(0.7)* (ex[i][j+1] - ex[i][j] + + ey[i+1][j] - ey[i][j]); + } + +#pragma endscop +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int tmax = TMAX; + int nx = NX; + int ny = NY; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(ex,DATA_TYPE,NX,NY,nx,ny); + POLYBENCH_2D_ARRAY_DECL(ey,DATA_TYPE,NX,NY,nx,ny); + POLYBENCH_2D_ARRAY_DECL(hz,DATA_TYPE,NX,NY,nx,ny); + POLYBENCH_1D_ARRAY_DECL(_fict_,DATA_TYPE,TMAX,tmax); + + /* Initialize array(s). */ + init_array (tmax, nx, ny, + POLYBENCH_ARRAY(ex), + POLYBENCH_ARRAY(ey), + POLYBENCH_ARRAY(hz), + POLYBENCH_ARRAY(_fict_)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_fdtd_2d (tmax, nx, ny, + POLYBENCH_ARRAY(ex), + POLYBENCH_ARRAY(ey), + POLYBENCH_ARRAY(hz), + POLYBENCH_ARRAY(_fict_)); + + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(nx, ny, POLYBENCH_ARRAY(ex), + POLYBENCH_ARRAY(ey), + POLYBENCH_ARRAY(hz))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(ex); + POLYBENCH_FREE_ARRAY(ey); + POLYBENCH_FREE_ARRAY(hz); + POLYBENCH_FREE_ARRAY(_fict_); + + return 0; +} + +// CHECK: #map = affine_map<()[s0] -> (s0 - 1)> + +// CHECK: func @kernel_fdtd_2d(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: memref<1000x1200xf64>, %arg4: memref<1000x1200xf64>, %arg5: memref<1000x1200xf64>, %arg6: memref<500xf64>) { +// CHECK-NEXT: %cst = constant 5.000000e-01 : f64 +// CHECK-NEXT: %cst_0 = constant 0.69999999999999996 : f64 +// CHECK-NEXT: %0 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg2 : i32 to index +// CHECK-NEXT: %2 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg7 = 0 to %2 { +// CHECK-NEXT: %3 = affine.load %arg6[%arg7] : memref<500xf64> +// CHECK-NEXT: affine.for %arg8 = 0 to %1 { +// CHECK-NEXT: affine.store %3, %arg4[0, %arg8] : memref<1000x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg8 = 1 to %0 { +// CHECK-NEXT: affine.for %arg9 = 0 to %1 { +// CHECK-NEXT: %4 = affine.load %arg4[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: %5 = affine.load %arg5[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: %6 = affine.load %arg5[%arg8 - 1, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: %7 = subf %5, %6 : f64 +// CHECK-NEXT: %8 = mulf %cst, %7 : f64 +// CHECK-NEXT: %9 = subf %4, %8 : f64 +// CHECK-NEXT: affine.store %9, %arg4[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg8 = 0 to %0 { +// CHECK-NEXT: affine.for %arg9 = 1 to %1 { +// CHECK-NEXT: %4 = affine.load %arg3[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: %5 = affine.load %arg5[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: %6 = affine.load %arg5[%arg8, %arg9 - 1] : memref<1000x1200xf64> +// CHECK-NEXT: %7 = subf %5, %6 : f64 +// CHECK-NEXT: %8 = mulf %cst, %7 : f64 +// CHECK-NEXT: %9 = subf %4, %8 : f64 +// CHECK-NEXT: affine.store %9, %arg3[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg8 = 0 to #map()[%0] { +// CHECK-NEXT: affine.for %arg9 = 0 to #map()[%1] { +// CHECK-NEXT: %4 = affine.load %arg5[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: %5 = affine.load %arg3[%arg8, %arg9 + 1] : memref<1000x1200xf64> +// CHECK-NEXT: %6 = affine.load %arg3[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: %7 = subf %5, %6 : f64 +// CHECK-NEXT: %8 = affine.load %arg4[%arg8 + 1, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: %9 = addf %7, %8 : f64 +// CHECK-NEXT: %10 = affine.load %arg4[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: %11 = subf %9, %10 : f64 +// CHECK-NEXT: %12 = mulf %cst_0, %11 : f64 +// CHECK-NEXT: %13 = subf %4, %12 : f64 +// CHECK-NEXT: affine.store %13, %arg5[%arg8, %arg9] : memref<1000x1200xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/stencils/fdtd-2d/fdtd-2d.h b/mlir-clang/Test/polybench/stencils/fdtd-2d/fdtd-2d.h new file mode 100644 index 000000000000..4ffadd5bb627 --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/fdtd-2d/fdtd-2d.h @@ -0,0 +1,86 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _FDTD_2D_H +# define _FDTD_2D_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(TMAX) && !defined(NX) && !defined(NY) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define TMAX 20 +# define NX 20 +# define NY 30 +# endif + +# ifdef SMALL_DATASET +# define TMAX 40 +# define NX 60 +# define NY 80 +# endif + +# ifdef MEDIUM_DATASET +# define TMAX 100 +# define NX 200 +# define NY 240 +# endif + +# ifdef LARGE_DATASET +# define TMAX 500 +# define NX 1000 +# define NY 1200 +# endif + +# ifdef EXTRALARGE_DATASET +# define TMAX 1000 +# define NX 2000 +# define NY 2600 +# endif + + +#endif /* !(TMAX NX NY) */ + +# define _PB_TMAX POLYBENCH_LOOP_BOUND(TMAX,tmax) +# define _PB_NX POLYBENCH_LOOP_BOUND(NX,nx) +# define _PB_NY POLYBENCH_LOOP_BOUND(NY,ny) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_FDTD_2D_H */ diff --git a/mlir-clang/Test/polybench/stencils/heat-3d/heat-3d.c b/mlir-clang/Test/polybench/stencils/heat-3d/heat-3d.c new file mode 100644 index 000000000000..37cbbe99d481 --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/heat-3d/heat-3d.c @@ -0,0 +1,224 @@ +// TODO: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* heat-3d.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "heat-3d.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE POLYBENCH_3D(A,N,N,N,n,n,n), + DATA_TYPE POLYBENCH_3D(B,N,N,N,n,n,n)) +{ + int i, j, k; + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + for (k = 0; k < n; k++) + A[i][j][k] = B[i][j][k] = (DATA_TYPE) (i + j + (n-k))* 10 / (n); +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_3D(A,N,N,N,n,n,n)) + +{ + int i, j, k; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("A"); + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + for (k = 0; k < n; k++) { + if ((i * n * n + j * n + k) % 20 == 0) fprintf(POLYBENCH_DUMP_TARGET, "\n"); + fprintf(POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, A[i][j][k]); + } + POLYBENCH_DUMP_END("A"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_heat_3d(int tsteps, + int n, + DATA_TYPE POLYBENCH_3D(A,N,N,N,n,n,n), + DATA_TYPE POLYBENCH_3D(B,N,N,N,n,n,n)) +{ + int t, i, j, k; + +#pragma scop + for (t = 1; t <= TSTEPS; t++) { + for (i = 1; i < _PB_N-1; i++) { + for (j = 1; j < _PB_N-1; j++) { + for (k = 1; k < _PB_N-1; k++) { + B[i][j][k] = SCALAR_VAL(0.125) * (A[i+1][j][k] - SCALAR_VAL(2.0) * A[i][j][k] + A[i-1][j][k]) + + SCALAR_VAL(0.125) * (A[i][j+1][k] - SCALAR_VAL(2.0) * A[i][j][k] + A[i][j-1][k]) + + SCALAR_VAL(0.125) * (A[i][j][k+1] - SCALAR_VAL(2.0) * A[i][j][k] + A[i][j][k-1]) + + A[i][j][k]; + } + } + } + for (i = 1; i < _PB_N-1; i++) { + for (j = 1; j < _PB_N-1; j++) { + for (k = 1; k < _PB_N-1; k++) { + A[i][j][k] = SCALAR_VAL(0.125) * (B[i+1][j][k] - SCALAR_VAL(2.0) * B[i][j][k] + B[i-1][j][k]) + + SCALAR_VAL(0.125) * (B[i][j+1][k] - SCALAR_VAL(2.0) * B[i][j][k] + B[i][j-1][k]) + + SCALAR_VAL(0.125) * (B[i][j][k+1] - SCALAR_VAL(2.0) * B[i][j][k] + B[i][j][k-1]) + + B[i][j][k]; + } + } + } + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int tsteps = TSTEPS; + + /* Variable declaration/allocation. */ + POLYBENCH_3D_ARRAY_DECL(A, DATA_TYPE, N, N, N, n, n, n); + POLYBENCH_3D_ARRAY_DECL(B, DATA_TYPE, N, N, N, n, n, n); + + + /* Initialize array(s). */ + init_array (n, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(B)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_heat_3d (tsteps, n, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(B)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(A))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + + return 0; +} + +// CHECK: #map = affine_map<()[s0] -> (s0 - 1)> + +// CHECK: func private @kernel_heat_3d(%arg0: i32, %arg1: i32, %arg2: memref<120x120x120xf64>, %arg3: memref<120x120x120xf64>) { +// CHECK-NEXT: %cst = constant 1.250000e-01 : f64 +// CHECK-NEXT: %cst_0 = constant 2.000000e+00 : f64 +// CHECK-NEXT: %0 = index_cast %arg1 : i32 to index +// CHECK-NEXT: affine.for %arg4 = 1 to 501 { +// CHECK-NEXT: affine.for %arg5 = 1 to #map()[%0] { +// CHECK-NEXT: affine.for %arg6 = 1 to #map()[%0] { +// CHECK-NEXT: affine.for %arg7 = 1 to #map()[%0] { +// CHECK-NEXT: %1 = affine.load %arg2[%arg5 + 1, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %2 = affine.load %arg2[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %3 = mulf %cst_0, %2 : f64 +// CHECK-NEXT: %4 = subf %1, %3 : f64 +// CHECK-NEXT: %5 = affine.load %arg2[%arg5 - 1, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %6 = addf %4, %5 : f64 +// CHECK-NEXT: %7 = mulf %cst, %6 : f64 +// CHECK-NEXT: %8 = affine.load %arg2[%arg5, %arg6 + 1, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %9 = affine.load %arg2[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %10 = mulf %cst_0, %9 : f64 +// CHECK-NEXT: %11 = subf %8, %10 : f64 +// CHECK-NEXT: %12 = affine.load %arg2[%arg5, %arg6 - 1, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %13 = addf %11, %12 : f64 +// CHECK-NEXT: %14 = mulf %cst, %13 : f64 +// CHECK-NEXT: %15 = addf %7, %14 : f64 +// CHECK-NEXT: %16 = affine.load %arg2[%arg5, %arg6, %arg7 + 1] : memref<120x120x120xf64> +// CHECK-NEXT: %17 = affine.load %arg2[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %18 = mulf %cst_0, %17 : f64 +// CHECK-NEXT: %19 = subf %16, %18 : f64 +// CHECK-NEXT: %20 = affine.load %arg2[%arg5, %arg6, %arg7 - 1] : memref<120x120x120xf64> +// CHECK-NEXT: %21 = addf %19, %20 : f64 +// CHECK-NEXT: %22 = mulf %cst, %21 : f64 +// CHECK-NEXT: %23 = addf %15, %22 : f64 +// CHECK-NEXT: %24 = affine.load %arg2[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %25 = addf %23, %24 : f64 +// CHECK-NEXT: affine.store %25, %arg3[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg5 = 1 to #map()[%0] { +// CHECK-NEXT: affine.for %arg6 = 1 to #map()[%0] { +// CHECK-NEXT: affine.for %arg7 = 1 to #map()[%0] { +// CHECK-NEXT: %1 = affine.load %arg3[%arg5 + 1, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %2 = affine.load %arg3[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %3 = mulf %cst_0, %2 : f64 +// CHECK-NEXT: %4 = subf %1, %3 : f64 +// CHECK-NEXT: %5 = affine.load %arg3[%arg5 - 1, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %6 = addf %4, %5 : f64 +// CHECK-NEXT: %7 = mulf %cst, %6 : f64 +// CHECK-NEXT: %8 = affine.load %arg3[%arg5, %arg6 + 1, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %9 = affine.load %arg3[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %10 = mulf %cst_0, %9 : f64 +// CHECK-NEXT: %11 = subf %8, %10 : f64 +// CHECK-NEXT: %12 = affine.load %arg3[%arg5, %arg6 - 1, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %13 = addf %11, %12 : f64 +// CHECK-NEXT: %14 = mulf %cst, %13 : f64 +// CHECK-NEXT: %15 = addf %7, %14 : f64 +// CHECK-NEXT: %16 = affine.load %arg3[%arg5, %arg6, %arg7 + 1] : memref<120x120x120xf64> +// CHECK-NEXT: %17 = affine.load %arg3[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %18 = mulf %cst_0, %17 : f64 +// CHECK-NEXT: %19 = subf %16, %18 : f64 +// CHECK-NEXT: %20 = affine.load %arg3[%arg5, %arg6, %arg7 - 1] : memref<120x120x120xf64> +// CHECK-NEXT: %21 = addf %19, %20 : f64 +// CHECK-NEXT: %22 = mulf %cst, %21 : f64 +// CHECK-NEXT: %23 = addf %15, %22 : f64 +// CHECK-NEXT: %24 = affine.load %arg3[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: %25 = addf %23, %24 : f64 +// CHECK-NEXT: affine.store %25, %arg2[%arg5, %arg6, %arg7] : memref<120x120x120xf64> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/stencils/heat-3d/heat-3d.h b/mlir-clang/Test/polybench/stencils/heat-3d/heat-3d.h new file mode 100644 index 000000000000..37fb9fdf10ce --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/heat-3d/heat-3d.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _HEAT_3D_H +# define _HEAT_3D_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(TSTEPS) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define TSTEPS 20 +# define N 10 +# endif + +# ifdef SMALL_DATASET +# define TSTEPS 40 +# define N 20 +# endif + +# ifdef MEDIUM_DATASET +# define TSTEPS 100 +# define N 40 +# endif + +# ifdef LARGE_DATASET +# define TSTEPS 500 +# define N 120 +# endif + +# ifdef EXTRALARGE_DATASET +# define TSTEPS 1000 +# define N 200 +# endif + + +#endif /* !(TSTEPS N) */ + +# define _PB_TSTEPS POLYBENCH_LOOP_BOUND(TSTEPS,tsteps) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_HEAT_3D_H */ diff --git a/mlir-clang/Test/polybench/stencils/jacobi-1d/jacobi-1d.c b/mlir-clang/Test/polybench/stencils/jacobi-1d/jacobi-1d.c new file mode 100644 index 000000000000..0f0768c35fc0 --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/jacobi-1d/jacobi-1d.c @@ -0,0 +1,163 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* jacobi-1d.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "jacobi-1d.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE POLYBENCH_1D(A,N,n), + DATA_TYPE POLYBENCH_1D(B,N,n)) +{ + int i; + + for (i = 0; i < n; i++) + { + A[i] = ((DATA_TYPE) i+ 2) / n; + B[i] = ((DATA_TYPE) i+ 3) / n; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_1D(A,N,n)) + +{ + int i; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("A"); + for (i = 0; i < n; i++) + { + if (i % 20 == 0) fprintf(POLYBENCH_DUMP_TARGET, "\n"); + fprintf(POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, A[i]); + } + POLYBENCH_DUMP_END("A"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_jacobi_1d(int tsteps, + int n, + DATA_TYPE POLYBENCH_1D(A,N,n), + DATA_TYPE POLYBENCH_1D(B,N,n)) +{ + int t, i; + +#pragma scop + for (t = 0; t < _PB_TSTEPS; t++) + { + for (i = 1; i < _PB_N - 1; i++) + B[i] = 0.33333 * (A[i-1] + A[i] + A[i + 1]); + for (i = 1; i < _PB_N - 1; i++) + A[i] = 0.33333 * (B[i-1] + B[i] + B[i + 1]); + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int tsteps = TSTEPS; + + /* Variable declaration/allocation. */ + POLYBENCH_1D_ARRAY_DECL(A, DATA_TYPE, N, n); + POLYBENCH_1D_ARRAY_DECL(B, DATA_TYPE, N, n); + + + /* Initialize array(s). */ + init_array (n, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(B)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_jacobi_1d(tsteps, n, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(B)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(A))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(B); + + return 0; +} + +// CHECK: #map = affine_map<()[s0] -> (s0 - 1)> +// CHECK: func private @kernel_jacobi_1d(%arg0: i32, %arg1: i32, %arg2: memref, %arg3: memref) { +// CHECK-NEXT: %cst = constant 3.333300e-01 : f64 +// CHECK-NEXT: %0 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg4 = 0 to %1 { +// CHECK-NEXT: affine.for %arg5 = 1 to #map()[%0] { +// CHECK-NEXT: %2 = affine.load %arg2[%arg5 - 1] : memref +// CHECK-NEXT: %3 = affine.load %arg2[%arg5] : memref +// CHECK-NEXT: %4 = addf %2, %3 : f64 +// CHECK-NEXT: %5 = affine.load %arg2[%arg5 + 1] : memref +// CHECK-NEXT: %6 = addf %4, %5 : f64 +// CHECK-NEXT: %7 = mulf %cst, %6 : f64 +// CHECK-NEXT: affine.store %7, %arg3[%arg5] : memref +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg5 = 1 to #map()[%0] { +// CHECK-NEXT: %2 = affine.load %arg3[%arg5 - 1] : memref +// CHECK-NEXT: %3 = affine.load %arg3[%arg5] : memref +// CHECK-NEXT: %4 = addf %2, %3 : f64 +// CHECK-NEXT: %5 = affine.load %arg3[%arg5 + 1] : memref +// CHECK-NEXT: %6 = addf %4, %5 : f64 +// CHECK-NEXT: %7 = mulf %cst, %6 : f64 +// CHECK-NEXT: affine.store %7, %arg2[%arg5] : memref +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/stencils/jacobi-1d/jacobi-1d.h b/mlir-clang/Test/polybench/stencils/jacobi-1d/jacobi-1d.h new file mode 100644 index 000000000000..febdfcbeff2f --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/jacobi-1d/jacobi-1d.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _JACOBI_1D_H +# define _JACOBI_1D_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(TSTEPS) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define TSTEPS 20 +# define N 30 +# endif + +# ifdef SMALL_DATASET +# define TSTEPS 40 +# define N 120 +# endif + +# ifdef MEDIUM_DATASET +# define TSTEPS 100 +# define N 400 +# endif + +# ifdef LARGE_DATASET +# define TSTEPS 500 +# define N 2000 +# endif + +# ifdef EXTRALARGE_DATASET +# define TSTEPS 1000 +# define N 4000 +# endif + + +#endif /* !(TSTEPS N) */ + +# define _PB_TSTEPS POLYBENCH_LOOP_BOUND(TSTEPS,tsteps) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_JACOBI_1D_H */ diff --git a/mlir-clang/Test/polybench/stencils/jacobi-2d/jacobi-2d.c b/mlir-clang/Test/polybench/stencils/jacobi-2d/jacobi-2d.c new file mode 100644 index 000000000000..bae42162e996 --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/jacobi-2d/jacobi-2d.c @@ -0,0 +1,177 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* jacobi-2d.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "jacobi-2d.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n), + DATA_TYPE POLYBENCH_2D(B,N,N,n,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + { + A[i][j] = ((DATA_TYPE) i*(j+2) + 2) / n; + B[i][j] = ((DATA_TYPE) i*(j+3) + 3) / n; + } +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) + +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("A"); + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + if ((i * n + j) % 20 == 0) fprintf(POLYBENCH_DUMP_TARGET, "\n"); + fprintf(POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, A[i][j]); + } + POLYBENCH_DUMP_END("A"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_jacobi_2d(int tsteps, + int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n), + DATA_TYPE POLYBENCH_2D(B,N,N,n,n)) +{ + int t, i, j; + +#pragma scop + for (t = 0; t < _PB_TSTEPS; t++) + { + for (i = 1; i < _PB_N - 1; i++) + for (j = 1; j < _PB_N - 1; j++) + B[i][j] = SCALAR_VAL(0.2) * (A[i][j] + A[i][j-1] + A[i][1+j] + A[1+i][j] + A[i-1][j]); + for (i = 1; i < _PB_N - 1; i++) + for (j = 1; j < _PB_N - 1; j++) + A[i][j] = SCALAR_VAL(0.2) * (B[i][j] + B[i][j-1] + B[i][1+j] + B[1+i][j] + B[i-1][j]); + } +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int tsteps = TSTEPS; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(A, DATA_TYPE, N, N, n, n); + POLYBENCH_2D_ARRAY_DECL(B, DATA_TYPE, N, N, n, n); + + + /* Initialize array(s). */ + init_array (n, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(B)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_jacobi_2d(tsteps, n, POLYBENCH_ARRAY(A), POLYBENCH_ARRAY(B)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(A))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + POLYBENCH_FREE_ARRAY(B); + + return 0; +} +// CHECK: #map = affine_map<()[s0] -> (s0 - 1)> +// CHECK: func private @kernel_jacobi_2d(%arg0: i32, %arg1: i32, %arg2: memref, %arg3: memref) { +// CHECK-NEXT: %cst = constant 2.000000e-01 : f64 +// CHECK-NEXT: %0 = index_cast %arg1 : i32 to index +// CHECK-NEXT: %1 = index_cast %arg0 : i32 to index +// CHECK-NEXT: affine.for %arg4 = 0 to %1 { +// CHECK-NEXT: affine.for %arg5 = 1 to #map()[%0] { +// CHECK-NEXT: affine.for %arg6 = 1 to #map()[%0] { +// CHECK-NEXT: %2 = affine.load %arg2[%arg5, %arg6] : memref +// CHECK-NEXT: %3 = affine.load %arg2[%arg5, %arg6 - 1] : memref +// CHECK-NEXT: %4 = addf %2, %3 : f64 +// CHECK-NEXT: %5 = affine.load %arg2[%arg5, %arg6 + 1] : memref +// CHECK-NEXT: %6 = addf %4, %5 : f64 +// CHECK-NEXT: %7 = affine.load %arg2[%arg5 + 1, %arg6] : memref +// CHECK-NEXT: %8 = addf %6, %7 : f64 +// CHECK-NEXT: %9 = affine.load %arg2[%arg5 - 1, %arg6] : memref +// CHECK-NEXT: %10 = addf %8, %9 : f64 +// CHECK-NEXT: %11 = mulf %cst, %10 : f64 +// CHECK-NEXT: affine.store %11, %arg3[%arg5, %arg6] : memref +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: affine.for %arg5 = 1 to #map()[%0] { +// CHECK-NEXT: affine.for %arg6 = 1 to #map()[%0] { +// CHECK-NEXT: %2 = affine.load %arg3[%arg5, %arg6] : memref +// CHECK-NEXT: %3 = affine.load %arg3[%arg5, %arg6 - 1] : memref +// CHECK-NEXT: %4 = addf %2, %3 : f64 +// CHECK-NEXT: %5 = affine.load %arg3[%arg5, %arg6 + 1] : memref +// CHECK-NEXT: %6 = addf %4, %5 : f64 +// CHECK-NEXT: %7 = affine.load %arg3[%arg5 + 1, %arg6] : memref +// CHECK-NEXT: %8 = addf %6, %7 : f64 +// CHECK-NEXT: %9 = affine.load %arg3[%arg5 - 1, %arg6] : memref +// CHECK-NEXT: %10 = addf %8, %9 : f64 +// CHECK-NEXT: %11 = mulf %cst, %10 : f64 +// CHECK-NEXT: affine.store %11, %arg2[%arg5, %arg6] : memref +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/stencils/jacobi-2d/jacobi-2d.h b/mlir-clang/Test/polybench/stencils/jacobi-2d/jacobi-2d.h new file mode 100644 index 000000000000..ca0d4d32c970 --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/jacobi-2d/jacobi-2d.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _JACOBI_2D_H +# define _JACOBI_2D_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(TSTEPS) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define TSTEPS 20 +# define N 30 +# endif + +# ifdef SMALL_DATASET +# define TSTEPS 40 +# define N 90 +# endif + +# ifdef MEDIUM_DATASET +# define TSTEPS 100 +# define N 250 +# endif + +# ifdef LARGE_DATASET +# define TSTEPS 500 +# define N 1300 +# endif + +# ifdef EXTRALARGE_DATASET +# define TSTEPS 1000 +# define N 2800 +# endif + + +#endif /* !(TSTEPS N) */ + +# define _PB_TSTEPS POLYBENCH_LOOP_BOUND(TSTEPS,tsteps) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_JACOBI_2D_H */ diff --git a/mlir-clang/Test/polybench/stencils/seidel-2d/seidel-2d.c b/mlir-clang/Test/polybench/stencils/seidel-2d/seidel-2d.c new file mode 100644 index 000000000000..406f5b531cd7 --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/seidel-2d/seidel-2d.c @@ -0,0 +1,162 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 +// RUN: mlir-clang %s %polyexec %stdinclude -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm > %s.mlir.time; cat %s.mlir.time | FileCheck %s --check-prefix EXEC +// RUN: clang %s -O3 %polyexec %stdinclude -o %s.exec2 && %s.exec2 > %s.clang.time; cat %s.clang.time | FileCheck %s --check-prefix EXEC +// RUN: rm -f %s.exec2 %s.execm + +// RUN: clang %s -O3 %stdinclude %polyverify -o %s.exec1 && %s.exec1 &> %s.out1 +// RUN: mlir-clang %s %polyverify %stdinclude -detect-reduction -emit-llvm | clang -x ir - -O3 -o %s.execm && %s.execm &> %s.out2 +// RUN: rm -f %s.exec1 %s.execm +// RUN: diff %s.out1 %s.out2 +// RUN: rm -f %s.out1 %s.out2 + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* seidel-2d.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +#include "seidel-2d.h" + + +/* Array initialization. */ +static +void init_array (int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + A[i][j] = ((DATA_TYPE) i*(j+2) + 2) / n; +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) + +{ + int i, j; + + POLYBENCH_DUMP_START; + POLYBENCH_DUMP_BEGIN("A"); + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + if ((i * n + j) % 20 == 0) fprintf(POLYBENCH_DUMP_TARGET, "\n"); + fprintf(POLYBENCH_DUMP_TARGET, DATA_PRINTF_MODIFIER, A[i][j]); + } + POLYBENCH_DUMP_END("A"); + POLYBENCH_DUMP_FINISH; +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_seidel_2d(int tsteps, + int n, + DATA_TYPE POLYBENCH_2D(A,N,N,n,n)) +{ + int t, i, j; + +#pragma scop + for (t = 0; t <= _PB_TSTEPS - 1; t++) + for (i = 1; i<= _PB_N - 2; i++) + for (j = 1; j <= _PB_N - 2; j++) + A[i][j] = (A[i-1][j-1] + A[i-1][j] + A[i-1][j+1] + + A[i][j-1] + A[i][j] + A[i][j+1] + + A[i+1][j-1] + A[i+1][j] + A[i+1][j+1])/SCALAR_VAL(9.0); +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + int tsteps = TSTEPS; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(A, DATA_TYPE, N, N, n, n); + + + /* Initialize array(s). */ + init_array (n, POLYBENCH_ARRAY(A)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_seidel_2d (tsteps, n, POLYBENCH_ARRAY(A)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(A))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(A); + + return 0; +} + +// CHECK: #map = affine_map<()[s0] -> (s0 - 1)> + +// CHECK: func private @kernel_seidel_2d(%arg0: i32, %arg1: i32, %arg2: memref) { +// CHECK-NEXT: %cst = constant 9.000000e+00 : f64 +// CHECK-DAG: %[[a0cst:.+]] = index_cast %arg0 : i32 to index +// CHECK-DAG: %[[a1cst:.+]] = index_cast %arg1 : i32 to index +// CHECK-NEXT: affine.for %arg3 = 0 to %[[a0cst]] { +// CHECK-NEXT: affine.for %arg4 = 1 to #map()[%[[a1cst]]] { +// CHECK-NEXT: affine.for %arg5 = 1 to #map()[%[[a1cst]]] { +// CHECK-NEXT: %2 = affine.load %arg2[%arg4 - 1, %arg5 - 1] : memref +// CHECK-NEXT: %3 = affine.load %arg2[%arg4 - 1, %arg5] : memref +// CHECK-NEXT: %4 = addf %2, %3 : f64 +// CHECK-NEXT: %5 = affine.load %arg2[%arg4 - 1, %arg5 + 1] : memref +// CHECK-NEXT: %6 = addf %4, %5 : f64 +// CHECK-NEXT: %7 = affine.load %arg2[%arg4, %arg5 - 1] : memref +// CHECK-NEXT: %8 = addf %6, %7 : f64 +// CHECK-NEXT: %9 = affine.load %arg2[%arg4, %arg5] : memref +// CHECK-NEXT: %10 = addf %8, %9 : f64 +// CHECK-NEXT: %11 = affine.load %arg2[%arg4, %arg5 + 1] : memref +// CHECK-NEXT: %12 = addf %10, %11 : f64 +// CHECK-NEXT: %13 = affine.load %arg2[%arg4 + 1, %arg5 - 1] : memref +// CHECK-NEXT: %14 = addf %12, %13 : f64 +// CHECK-NEXT: %15 = affine.load %arg2[%arg4 + 1, %arg5] : memref +// CHECK-NEXT: %16 = addf %14, %15 : f64 +// CHECK-NEXT: %17 = affine.load %arg2[%arg4 + 1, %arg5 + 1] : memref +// CHECK-NEXT: %18 = addf %16, %17 : f64 +// CHECK-NEXT: %19 = divf %18, %cst : f64 +// CHECK-NEXT: affine.store %19, %arg2[%arg4, %arg5] : memref +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: return +// CHECK-NEXT: } + +// EXEC: {{[0-9]\.[0-9]+}} diff --git a/mlir-clang/Test/polybench/stencils/seidel-2d/seidel-2d.h b/mlir-clang/Test/polybench/stencils/seidel-2d/seidel-2d.h new file mode 100644 index 000000000000..e7761b08b165 --- /dev/null +++ b/mlir-clang/Test/polybench/stencils/seidel-2d/seidel-2d.h @@ -0,0 +1,80 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#ifndef _SEIDEL_2D_H +# define _SEIDEL_2D_H + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +# if !defined(TSTEPS) && !defined(N) +/* Define sample dataset sizes. */ +# ifdef MINI_DATASET +# define TSTEPS 20 +# define N 40 +# endif + +# ifdef SMALL_DATASET +# define TSTEPS 40 +# define N 120 +# endif + +# ifdef MEDIUM_DATASET +# define TSTEPS 100 +# define N 400 +# endif + +# ifdef LARGE_DATASET +# define TSTEPS 500 +# define N 2000 +# endif + +# ifdef EXTRALARGE_DATASET +# define TSTEPS 1000 +# define N 4000 +# endif + + +#endif /* !(TSTEPS N) */ + +# define _PB_TSTEPS POLYBENCH_LOOP_BOUND(TSTEPS,tsteps) +# define _PB_N POLYBENCH_LOOP_BOUND(N,n) + + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_DOUBLE +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !_SEIDEL_2D_H */ diff --git a/mlir-clang/Test/polybench/utilities/benchmark_list b/mlir-clang/Test/polybench/utilities/benchmark_list new file mode 100644 index 000000000000..7ce7b7e1e382 --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/benchmark_list @@ -0,0 +1,30 @@ +./datamining/correlation/correlation.c +./datamining/covariance/covariance.c +./linear-algebra/kernels/2mm/2mm.c +./linear-algebra/kernels/3mm/3mm.c +./linear-algebra/kernels/atax/atax.c +./linear-algebra/kernels/bicg/bicg.c +./linear-algebra/kernels/doitgen/doitgen.c +./linear-algebra/kernels/mvt/mvt.c +./linear-algebra/blas/gemm/gemm.c +./linear-algebra/blas/gemver/gemver.c +./linear-algebra/blas/gesummv/gesummv.c +./linear-algebra/blas/symm/symm.c +./linear-algebra/blas/syr2k/syr2k.c +./linear-algebra/blas/syrk/syrk.c +./linear-algebra/blas/trmm/trmm.c +./linear-algebra/solvers/cholesky/cholesky.c +./linear-algebra/solvers/durbin/durbin.c +./linear-algebra/solvers/gramschmidt/gramschmidt.c +./linear-algebra/solvers/lu/lu.c +./linear-algebra/solvers/ludcmp/ludcmp.c +./linear-algebra/solvers/trisolv/trisolv.c +./medley/deriche/deriche.c +./medley/floyd-warshall/floyd-warshall.c +./medley/nussinov/nussinov.c +./stencils/adi/adi.c +./stencils/fdtd-2d/fdtd-2d.c +./stencils/heat-3d/heat-3d.c +./stencils/jacobi-1d/jacobi-1d.c +./stencils/jacobi-2d/jacobi-2d.c +./stencils/seidel-2d/seidel-2d.c diff --git a/mlir-clang/Test/polybench/utilities/clean.pl b/mlir-clang/Test/polybench/utilities/clean.pl new file mode 100644 index 000000000000..9b45bb9a5c8c --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/clean.pl @@ -0,0 +1,50 @@ +#!/usr/bin/perl + +# Visits every directory, calls make clean, and then removes the Makefile +# +# Written by Tomofumi Yuki, 11/21 2014 +# + +my $TARGET_DIR = "."; + +if ($#ARGV != 0) { + printf("usage perl clean.pl target-dir\n"); + exit(1); +} + + + +if ($#ARGV == 0) { + $TARGET_DIR = $ARGV[0]; +} + + +my @categories = ('linear-algebra/blas', + 'linear-algebra/kernels', + 'linear-algebra/solvers', + 'datamining', + 'stencils', + 'medley'); + + +foreach $cat (@categories) { + my $target = $TARGET_DIR.'/'.$cat; + opendir DIR, $target or die "directory $target not found.\n"; + while (my $dir = readdir DIR) { + next if ($dir=~'^\..*'); + next if (!(-d $target.'/'.$dir)); + + my $targetDir = $target.'/'.$dir; + my $command = "cd $targetDir; make clean; rm -f Makefile"; + print($command."\n"); + system($command); + } + + closedir DIR; +} + +my $cfgFile = $TARGET_DIR.'/'.'config.mk'; +if (-e $cfgFile) { + unlink $cfgFile; +} + diff --git a/mlir-clang/Test/polybench/utilities/create_cpped_version.pl b/mlir-clang/Test/polybench/utilities/create_cpped_version.pl new file mode 100644 index 000000000000..3354a1c94895 --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/create_cpped_version.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +# Creates C Pre-Processed Version +# Additional arguments to the script are all passed to gcc. +# At least the include path of polybench.h must be added. +# +# Written by Tomofumi Yuki, 01/14 2015 +# + +if ($#ARGV == -1) { + printf("usage perl create-cpped-version.pl filename [cflags]\n"); + exit(1); +} + +my @CFLAGS = @ARGV; +my $TARGET = shift @CFLAGS; + +my $TEMP_T = '.__poly_top.c'; +my $TEMP_B = '.__poly_bottom.c'; +my $TEMP_P = '.__poly_bottom.pp.c'; + +$TARGET =~ /([^\.\/]+)\.c$/; +my $KERNEL = $1; +$TARGET_DIR = substr $TARGET, 0, -length($KERNEL)-2; +$TARGET_DIR = '.' if $TARGET_DIR eq ''; + +open FILE, $TARGET or die "Error opening $TARGET"; + +my $top; +my $bottom; +my $current = \$top; +while () { + my $line = $_; + if ($line =~ /polybench\.h/) { + $current = \$bottom; + } + $$current .= $line; +} +close FILE; + +&writeToFile($TEMP_T, $top); +&writeToFile($TEMP_B, $bottom); + +my $ignoreLibs = "-D_STDLIB_H_ -D_STDIO_H_ -D_MATH_H_ -D_STRING_H_ -D_UNISTD_H_"; + +my $command = 'gcc -E '.$ignoreLibs.' '.$TEMP_B.' -I '.$TARGET_DIR.' '.join(" ", @CFLAGS).' 2>/dev/null > '.$TEMP_P; +system($command); + +my $OUTFILE = $TARGET_DIR.'/'.$KERNEL.'.preproc.c'; +system('cat '.$TEMP_T.' > '.$OUTFILE); +system('echo "#include" >> '.$OUTFILE); +$command = 'sed -e "s~'.$TEMP_B.'~'.$KERNEL.'.c~g" '.$TEMP_P.' >> '.$OUTFILE; +system($command); + +unlink $TEMP_P; +unlink $TEMP_B; +unlink $TEMP_T; + +sub writeToFile() { + my $file = $_[0]; + my $content = $_[1]; + + open FILE, ">$file" or die "Error writing to $file"; + + print FILE $content; + + close FILE; +} diff --git a/mlir-clang/Test/polybench/utilities/header-gen.pl b/mlir-clang/Test/polybench/utilities/header-gen.pl new file mode 100644 index 000000000000..a58ee94921ff --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/header-gen.pl @@ -0,0 +1,143 @@ +#!/usr/bin/perl + +# Generates headers using specification in polybench.spec +# +# Written by Tomofumi Yuki, 11/21 2014 +# + +use File::Path; + +if ($#ARGV != 0) { + printf("usage perl header-gen.pl output-dir\n"); + exit(1); +} + +my $SPECFILE = 'polybench.spec'; +my $OUTDIR = $ARGV[0]; +my @DATASET_NAMES = ('MINI', 'SMALL', 'MEDIUM', 'LARGE', 'EXTRALARGE'); + +if (!(-e $OUTDIR)) { + mkdir $OUTDIR; +} + +my %INPUT; +my @keys; + +open FILE, $SPECFILE or die; + while () { + my $line = $_; + $line =~ s/\r|\n//g; + #lines tarting with # is treated as comments + next if ($line=~/^\s*#/); + next if ($line=~/^\s*[\r|\n]+$/); + my @line = split(/\t+/, $line); + + if (!keys %INPUT ) { + foreach (@line) { + $INPUT{$_} = []; + } + @keys = @line; + } else { + for (my $i = 0; $i <= $#line; $i++) { + push @{$INPUT{$keys[$i]}}, $line[$i] ; + } + } + } + +close FILE; + +for (my $r = 0; $r <= $#{$INPUT{'kernel'}}; $r++) { + &generateHeader($r); +} + +sub generateHeader() { + + my $row = $_[0]; + my $name = $INPUT{'kernel'}[$row]; + my $category = $INPUT{'category'}[$row]; + my $datatype = $INPUT{'datatype'}[$row]; + my $datatypeUC = uc $datatype; + my @params = split /\s+/, $INPUT{'params'}[$row]; + + + my $headerDef = '_'. uc $name . '_H'; + $headerDef =~ s/-/_/g; + + my $paramDefs; + foreach $set (@DATASET_NAMES) { + my @sizes = split /\s+/, $INPUT{$set}[$row]; + $paramDefs .= '# ifdef '.$set."_DATASET\n"; + for (my $i = 0; $i <= $#params; $i++) { + $paramDefs .= '# define '.$params[$i].' '.$sizes[$i]."\n"; + } + $paramDefs .= '# endif '."\n\n"; + } + + my $paramCheck = '# if'; + my $loopBoundDef = ''; + { + my $first = 1; + foreach (@params) { + $paramCheck.= ' &&' if (!$first); + $paramCheck .= " !defined($_)"; + $first = 0; + $loopBoundDef .= '# define _PB_'.$_.' POLYBENCH_LOOP_BOUND('.$_.','.lc $_.')'."\n"; + } + } + + my $kernelPath = "$OUTDIR/$category/$name"; + if (!(-e $kernelPath)) { + mkpath $kernelPath; + } + + open HFILE, ">$kernelPath/$name.h"; +print HFILE << "EOF"; +#ifndef $headerDef +# define $headerDef + +/* Default to LARGE_DATASET. */ +# if !defined(MINI_DATASET) && !defined(SMALL_DATASET) && !defined(MEDIUM_DATASET) && !defined(LARGE_DATASET) && !defined(EXTRALARGE_DATASET) +# define LARGE_DATASET +# endif + +$paramCheck +/* Define sample dataset sizes. */ +$paramDefs +#endif /* !(@params) */ + +$loopBoundDef + +/* Default data type */ +# if !defined(DATA_TYPE_IS_INT) && !defined(DATA_TYPE_IS_FLOAT) && !defined(DATA_TYPE_IS_DOUBLE) +# define DATA_TYPE_IS_$datatypeUC +# endif + +#ifdef DATA_TYPE_IS_INT +# define DATA_TYPE int +# define DATA_PRINTF_MODIFIER "%d " +#endif + +#ifdef DATA_TYPE_IS_FLOAT +# define DATA_TYPE float +# define DATA_PRINTF_MODIFIER "%0.2f " +# define SCALAR_VAL(x) x##f +# define SQRT_FUN(x) sqrtf(x) +# define EXP_FUN(x) expf(x) +# define POW_FUN(x,y) powf(x,y) +# endif + +#ifdef DATA_TYPE_IS_DOUBLE +# define DATA_TYPE double +# define DATA_PRINTF_MODIFIER "%0.2lf " +# define SCALAR_VAL(x) x +# define SQRT_FUN(x) sqrt(x) +# define EXP_FUN(x) exp(x) +# define POW_FUN(x,y) pow(x,y) +# endif + +#endif /* !$headerDef */ + +EOF + close HFILE; +} + diff --git a/mlir-clang/Test/polybench/utilities/makefile-gen.pl b/mlir-clang/Test/polybench/utilities/makefile-gen.pl new file mode 100644 index 000000000000..00aaa45dc53e --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/makefile-gen.pl @@ -0,0 +1,90 @@ +#!/usr/bin/perl + +# Generates Makefile for each benchmark in polybench +# Expects to be executed from root folder of polybench +# +# Written by Tomofumi Yuki, 11/21 2014 +# + +my $GEN_CONFIG = 0; +my $TARGET_DIR = "."; + +if ($#ARGV !=0 && $#ARGV != 1) { + printf("usage perl makefile-gen.pl output-dir [-cfg]\n"); + printf(" -cfg option generates config.mk in the output-dir.\n"); + exit(1); +} + + + +foreach my $arg (@ARGV) { + if ($arg =~ /-cfg/) { + $GEN_CONFIG = 1; + } elsif (!($arg =~ /^-/)) { + $TARGET_DIR = $arg; + } +} + + +my %categories = ( + 'linear-algebra/blas' => 3, + 'linear-algebra/kernels' => 3, + 'linear-algebra/solvers' => 3, + 'datamining' => 2, + 'stencils' => 2, + 'medley' => 2 +); + +my %extra_flags = ( + 'cholesky' => '-lm', + 'gramschmidt' => '-lm', + 'correlation' => '-lm' +); + +foreach $key (keys %categories) { + my $target = $TARGET_DIR.'/'.$key; + opendir DIR, $target or die "directory $target not found.\n"; + while (my $dir = readdir DIR) { + next if ($dir=~'^\..*'); + next if (!(-d $target.'/'.$dir)); + + my $kernel = $dir; + my $file = $target.'/'.$dir.'/Makefile'; + my $polybenchRoot = '../'x$categories{$key}; + my $configFile = $polybenchRoot.'config.mk'; + my $utilityDir = $polybenchRoot.'utilities'; + + open FILE, ">$file" or die "failed to open $file."; + +print FILE << "EOF"; +include $configFile + +EXTRA_FLAGS=$extra_flags{$kernel} + +$kernel: $kernel.c $kernel.h + \${VERBOSE} \${CC} -o $kernel $kernel.c \${CFLAGS} -I. -I$utilityDir $utilityDir/polybench.c \${EXTRA_FLAGS} + +clean: + @ rm -f $kernel + +EOF + + close FILE; + } + + + closedir DIR; +} + +if ($GEN_CONFIG) { +open FILE, '>'.$TARGET_DIR.'/config.mk'; + +print FILE << "EOF"; +CC=gcc +CFLAGS=-O2 -DPOLYBENCH_DUMP_ARRAYS -DPOLYBENCH_USE_C99_PROTO +EOF + +close FILE; + +} + diff --git a/mlir-clang/Test/polybench/utilities/papi_counters.list b/mlir-clang/Test/polybench/utilities/papi_counters.list new file mode 100644 index 000000000000..d8879cbe5ccb --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/papi_counters.list @@ -0,0 +1,5 @@ +// Counters must be delimited with ',' including the last one. +// C/C++ comments are allowed. +// Both native and standard PAPI events are supported. +"PAPI_TOT_CYC", +"L1D:REPL", diff --git a/mlir-clang/Test/polybench/utilities/polybench.R b/mlir-clang/Test/polybench/utilities/polybench.R new file mode 100644 index 000000000000..3e4742d7aedf --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/polybench.R @@ -0,0 +1,358 @@ +correlation <- function (M, N) { + data <- matrix(0, nrow=N, ncol=M); + for (i in 1:N) + for (j in 1:M) + data[i,j] = ((i-1)*(j-1))/M+i; + + cor <- cor(data); +} + +covariance <- function (M, N) { + data <- matrix(0, nrow=N, ncol=M); + for (i in 1:N) + for (j in 1:M) + data[i,j] = ((i-1)*(j-1))/M; + + res <- cov(data); +} + +gemm <- function(NI, NJ, NK) { + alpha <- 1.5; + beta <- 1.2; + C <- matrix(0, nrow=NI, ncol=NJ); + A <- matrix(0, nrow=NI, ncol=NK); + B <- matrix(0, nrow=NK, ncol=NJ); + + for (i in 1:NI) + for (j in 1:NJ) + C[i,j] <- (((i-1)*(j-1)+1) %% NI) / NI; + + for (i in 1:NI) + for (j in 1:NK) + A[i,j] <- (((i-1)*j) %% NK) / NK; + + for (i in 1:NK) + for (j in 1:NJ) + B[i,j] <- (((i-1)*(j+1)) %% NJ) / NJ; + + res <- alpha *A %*% B + beta * C; + +} + +gemver <- function (N) { + alpha <- 1.5; + beta <- 1.2; + A <- matrix(0, nrow=N, ncol=N); + u1 <- numeric(N); + u2 <- numeric(N); + v1 <- numeric(N); + v2 <- numeric(N); + y <- numeric(N); + z <- numeric(N); + + for (i in 1:N) { + ip <- i-1; + u1[i] <- ip; + u2[i] <- ((ip+1)/N)/2.0; + v1[i] <- ((ip+1)/N)/4.0; + v2[i] <- ((ip+1)/N)/6.0; + y[i] <- ((ip+1)/N)/8.0; + z[i] <- ((ip+1)/N)/9.0; + + for (j in 1:N) { + A[i,j] <- (((i-1)*(j-1)) %% N) / N; + } + } + + + Aprime <- A + u1 %o% v1 + u2 %o% v2; + x <- beta * t(Aprime) %*% y + z; + w <- alpha * Aprime %*% x; + + list(Aprime, x, w); +} + +gesummv <- function (N) { + alpha <- 1.5; + beta <- 1.2; + A <- matrix(0, nrow=N, ncol=N); + B <- matrix(0, nrow=N, ncol=N); + x <- numeric(N); + + for (i in 1:N) { + x[i] <- ((i-1) %% N) / N; + + for (j in 1:N) { + A[i,j] <- (((i-1)*(j-1)+1) %% N) / N; + B[i,j] <- (((i-1)*(j-1)+2) %% N) / N; + } + } + + y <- alpha * A %*% x + beta * B %*% x; +} + + +symm <- function(M, N) { + alpha <- 1.5; + beta <- 1.2; + A <- matrix(0, nrow=M, ncol=M); + B <- matrix(0, nrow=M, ncol=N); + C <- matrix(0, nrow=M, ncol=N); + + for (i in 1:M) + for (j in 1:N) { + C[i,j] <- ((i+j-2) %% 100) / M; + B[i,j] <- ((N+i-j) %% 100) / M; + } + for (i in 1:M) + for (j in i:M) { + A[i,j] <- ((i+j-2) %% 100) / M; + A[j,i] <- A[i,j]; + } + + res <- alpha * A %*% B + beta * C; + +} + +syrk <- function(M, N) { + + alpha <- 1.5; + beta <- 1.2; + A <- matrix(0, nrow=N, ncol=M); + C <- matrix(0, nrow=N, ncol=N); + + for (i in 1:N) + for (j in 1:M) + A[i,j] <- (((i-1)*(j-1)+1) %% N) / N; + + for (i in 1:N) + for (j in 1:N) + C[i,j] <- (((i-1)*(j-1)+2) %% M) / M; + + res <- alpha * A %*% t(A) + beta * C; + +#note that syrk only stores lower triangular part of the resulting symmetric matrix +} + +syr2k <- function(M, N) { + + alpha <- 1.5; + beta <- 1.2; + A <- matrix(0, nrow=N, ncol=M); + B <- matrix(0, nrow=N, ncol=M); + C <- matrix(0, nrow=N, ncol=N); + + for (i in 1:N) + for (j in 1:M) { + A[i,j] <- (((i-1)*(j-1)+1) %% N) / N; + B[i,j] <- (((i-1)*(j-1)+2) %% M) / M; + } + + for (i in 1:N) + for (j in 1:N) + C[i,j] <- (((i-1)*(j-1)+3) %% N) / M; + + res <- alpha * A %*% t(B) + alpha * B %*% t(A) + beta * C; + +} + + +trmm <- function(M, N) { + + alpha <- 1.5; + A <- matrix(0, nrow=M, ncol=M); + B <- matrix(0, nrow=M, ncol=N); + + for (i in 1:M) { + for (j in 1:i-1) { + A[i,j] <- (((i-1)+(j-1)) %% M) / M; + } + A[i,i] <- 1.0; + for (j in 1:N) + B[i,j] <- ((N+(i-1)-(j-1)) %% N) / N; + } + + + res <- alpha * t(A) %*% B; +} + +twomm <- function(NI, NJ, NK, NL) { + alpha <- 1.5; + beta <- 1.2; + A <- matrix(0, nrow=NI, ncol=NK); + B <- matrix(0, nrow=NK, ncol=NJ); + C <- matrix(0, nrow=NJ, ncol=NL); + D <- matrix(0, nrow=NI, ncol=NL); + + for (i in 1:NI) + for (j in 1:NK) + A[i,j] <- (((i-1)*(j-1)+1) %% NI) / NI; + + for (i in 1:NK) + for (j in 1:NJ) + B[i,j] <- (((i-1)*(j)) %% NJ) / NJ; + + for (i in 1:NJ) + for (j in 1:NL) + C[i,j] <- (((i-1)*(j+2)+1) %% NL) / NL; + + for (i in 1:NI) + for (j in 1:NL) + D[i,j] <- (((i-1)*(j+1)) %% NK) / NK; + + + res <- alpha * A %*% B %*% C + beta * D; + +} + + +threemm <- function(NI, NJ, NK, NL, NM) { + A <- matrix(0, nrow=NI, ncol=NK); + B <- matrix(0, nrow=NK, ncol=NJ); + C <- matrix(0, nrow=NJ, ncol=NM); + D <- matrix(0, nrow=NM, ncol=NL); + + for (i in 1:NI) + for (j in 1:NK) + A[i,j] <- (((i-1)*(j-1)+1) %% NI) / (5*NI); + + for (i in 1:NK) + for (j in 1:NJ) + B[i,j] <- (((i-1)*(j)+2) %% NJ) / (5*NJ); + + for (i in 1:NJ) + for (j in 1:NM) + C[i,j] <- (((i-1)*(j+2)) %% NL) / (5*NL); + + for (i in 1:NM) + for (j in 1:NL) + D[i,j] <- (((i-1)*(j+1)+2) %% NK) / (5*NK); + + + E <- A %*% B; + F <- C %*% D; + G <- E %*% F; + +} + +atax <- function(M, N) { + A <- matrix(0, nrow=M, ncol=N); + x <- numeric(N); + + for (i in 1:N) + x[i] <- 1 + ((i-1) / N); + + for (i in 1:M) + for (j in 1:N) + A[i,j] <- (((i-1)+(j-1)) %% N) / (5*M) + + + y <- t(A) %*% (A %*% x); + +} + +mvt <- function(N) { + x1 <- numeric(N); + x2 <- numeric(N); + y1 <- numeric(N); + y2 <- numeric(N); + A <- matrix(0, nrow=N, ncol=N); + + for (i in 1:N) { + ip <- i-1; + x1[i] <- (ip %% N) / N; + x2[i] <- ((ip + 1) %% N) / N; + y1[i] <- ((ip + 3) %% N) / N; + y2[i] <- ((ip + 4) %% N) / N; + for (j in 1:N) + A[i,j] <- (((i-1)*(j-1)) %% N) / N; + } + + + x1 <- x1 + A %*% y1; + x2 <- x2 + t(A) %*% y2; +} + +cholesky <- function(N) { + A <- matrix(0, nrow=N, ncol=N) + + for (i in 1:N) { + for (j in 1:i) + A[i,j] <- ((-((j-1) %% N)) / N) + 1 + A[i,i] <- 1; + } + + A <- A %*% t(A); + + res <- chol(A); +} + +durbin <- function(N) { + r <- numeric(N+1) + + r[1] = 1; + for (i in 2:(N+1)) + r[i] <- N+1-(i-2) + + r1 <- r[1:N] + r2 <- r[2:(N+1)] + + res <- solve(toeplitz(r1), -r2) +} + +gramschmidt <- function(M, N) { + A <- matrix(0, nrow=M, ncol=N); + + for (i in 1:M) + for (j in 1:N) + A[i,j] <- ((((i-1)*(j-1)) %% M) / M)*100 + 10 + + res <- qr(A); + + list(res, qr.Q(res), qr.R(res)) +} + +library('Matrix') +lu.polybench <- function(N) { + A <- matrix(0, nrow=N, ncol=N) + + + for (i in 1:N) { + for (j in 1:i) + A[i,j] <- ((-((j-1) %% N)) / N) + 1 + A[i,i] <- 1; + } + + A <- A %*% t(A); + + res <- expand(lu(A)); +} + +ludcmp <- function(N) { + b <- numeric(N); + for (i in 1:N) + b[i] <- ((i/N)/2.0) +4; + + A <- matrix(0, nrow=N, ncol=N) + + for (i in 1:N) { + for (j in 1:i) + A[i,j] <- ((-((j-1) %% N)) / N) + 1 + A[i,i] <- 1; + } + + A <- A %*% t(A); + + res <- solve(A, b); +} + +trisolv <- function(N) { + L <- matrix(0, nrow=N, ncol=N) + b <- c(0:(N-1)) + + for (i in 1:N) + for (j in 0:i) + L[i,j] <- (((i-1)+N-(j-1)+1)*2) / N + + res <- solve(L, b) +} diff --git a/mlir-clang/Test/polybench/utilities/polybench.c b/mlir-clang/Test/polybench/utilities/polybench.c new file mode 100644 index 000000000000..da165800571f --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/polybench.c @@ -0,0 +1,575 @@ +// RUN: mlir-clang %s polybench_alloc_data %stdinclude | FileCheck %s +// XFAIL: * +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* polybench.c: this file is part of PolyBench/C */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _OPENMP +# include +#endif + +#if defined(POLYBENCH_PAPI) +# undef POLYBENCH_PAPI +# include "polybench.h" +# define POLYBENCH_PAPI +#else +# include "polybench.h" +#endif + +/* By default, collect PAPI counters on thread 0. */ +#ifndef POLYBENCH_THREAD_MONITOR +# define POLYBENCH_THREAD_MONITOR 0 +#endif + +/* Total LLC cache size. By default 32+MB.. */ +#ifndef POLYBENCH_CACHE_SIZE_KB +# define POLYBENCH_CACHE_SIZE_KB 32770 +#endif + + +int polybench_papi_counters_threadid = POLYBENCH_THREAD_MONITOR; +double polybench_program_total_flops = 0; + +#ifdef POLYBENCH_PAPI +# include +# define POLYBENCH_MAX_NB_PAPI_COUNTERS 96 + char* _polybench_papi_eventlist[] = { +#include "papi_counters.list" + NULL + }; + int polybench_papi_eventset; + int polybench_papi_eventlist[POLYBENCH_MAX_NB_PAPI_COUNTERS]; + long_long polybench_papi_values[POLYBENCH_MAX_NB_PAPI_COUNTERS]; + +#endif + +/* + * Allocation table, to enable inter-array padding. All data allocated + * with polybench_alloc_data should be freed with polybench_free_data. + * + */ +#define NB_INITIAL_TABLE_ENTRIES 512 +struct polybench_data_ptrs +{ + void** user_view; + void** real_ptr; + int nb_entries; + int nb_avail_entries; +}; +static struct polybench_data_ptrs* _polybench_alloc_table = NULL; +static size_t polybench_inter_array_padding_sz = 0; + +/* Timer code (gettimeofday). */ +double polybench_t_start, polybench_t_end; +/* Timer code (RDTSC). */ +unsigned long long int polybench_c_start, polybench_c_end; + +static +double rtclock() +{ +#if defined(POLYBENCH_TIME) || defined(POLYBENCH_GFLOPS) + struct timeval Tp; + int stat; + stat = gettimeofday (&Tp, NULL); + if (stat != 0) + printf ("Error return from gettimeofday: %d", stat); + return (Tp.tv_sec + Tp.tv_usec * 1.0e-6); +#else + return 0; +#endif +} + + +#ifdef POLYBENCH_CYCLE_ACCURATE_TIMER +static +unsigned long long int rdtsc() +{ + unsigned long long int ret = 0; + unsigned int cycles_lo; + unsigned int cycles_hi; + __asm__ volatile ("RDTSC" : "=a" (cycles_lo), "=d" (cycles_hi)); + ret = (unsigned long long int)cycles_hi << 32 | cycles_lo; + + return ret; +} +#endif + +void polybench_flush_cache() +{ + int cs = POLYBENCH_CACHE_SIZE_KB * 1024 / sizeof(double); + double* flush = (double*) calloc (cs, sizeof(double)); + int i; + double tmp = 0.0; +#ifdef _OPENMP +#pragma omp parallel for reduction(+:tmp) private(i) +#endif + for (i = 0; i < cs; i++) + tmp += flush[i]; + assert (tmp <= 10.0); + free (flush); +} + + +#ifdef POLYBENCH_LINUX_FIFO_SCHEDULER +void polybench_linux_fifo_scheduler() +{ + /* Use FIFO scheduler to limit OS interference. Program must be run + as root, and this works only for Linux kernels. */ + struct sched_param schedParam; + schedParam.sched_priority = sched_get_priority_max (SCHED_FIFO); + sched_setscheduler (0, SCHED_FIFO, &schedParam); +} + + +void polybench_linux_standard_scheduler() +{ + /* Restore to standard scheduler policy. */ + struct sched_param schedParam; + schedParam.sched_priority = sched_get_priority_max (SCHED_OTHER); + sched_setscheduler (0, SCHED_OTHER, &schedParam); +} +#endif + +#ifdef POLYBENCH_PAPI + +static +void test_fail(char *file, int line, char *call, int retval) +{ + char buf[128]; + + memset(buf, '\0', sizeof(buf)); + if (retval != 0) + fprintf (stdout,"%-40s FAILED\nLine # %d\n", file, line); + else + { + fprintf (stdout,"%-40s SKIPPED\n", file); + fprintf (stdout,"Line # %d\n", line); + } + if (retval == PAPI_ESYS) + { + sprintf (buf, "System error in %s", call); + perror (buf); + } + else if (retval > 0) + fprintf (stdout,"Error: %s\n", call); + else if (retval == 0) + fprintf (stdout,"Error: %s\n", call); + else + { + char errstring[PAPI_MAX_STR_LEN]; + // PAPI 5.4.3 has changed the API for PAPI_perror. + #if defined (PAPI_VERSION) && ((PAPI_VERSION_MAJOR(PAPI_VERSION) == 5 && PAPI_VERSION_MINOR(PAPI_VERSION) >= 4) || PAPI_VERSION_MAJOR(PAPI_VERSION) > 5) + fprintf (stdout, "Error in %s: %s\n", call, PAPI_strerror(retval)); + #else + PAPI_perror (retval, errstring, PAPI_MAX_STR_LEN); + fprintf (stdout,"Error in %s: %s\n", call, errstring); + #endif + } + fprintf (stdout,"\n"); + if (PAPI_is_initialized ()) + PAPI_shutdown (); + exit (1); +} + + +void polybench_papi_init() +{ +# ifdef _OPENMP +#pragma omp parallel + { +#pragma omp master + { + if (omp_get_max_threads () < polybench_papi_counters_threadid) + polybench_papi_counters_threadid = omp_get_max_threads () - 1; + } +#pragma omp barrier + + if (omp_get_thread_num () == polybench_papi_counters_threadid) + { +# endif + int retval; + polybench_papi_eventset = PAPI_NULL; + if ((retval = PAPI_library_init (PAPI_VER_CURRENT)) != PAPI_VER_CURRENT) + test_fail (__FILE__, __LINE__, "PAPI_library_init", retval); + if ((retval = PAPI_create_eventset (&polybench_papi_eventset)) + != PAPI_OK) + test_fail (__FILE__, __LINE__, "PAPI_create_eventset", retval); + int k; + for (k = 0; _polybench_papi_eventlist[k]; ++k) + { + if ((retval = + PAPI_event_name_to_code (_polybench_papi_eventlist[k], + &(polybench_papi_eventlist[k]))) + != PAPI_OK) + test_fail (__FILE__, __LINE__, "PAPI_event_name_to_code", retval); + } + polybench_papi_eventlist[k] = 0; + + +# ifdef _OPENMP + } + } +#pragma omp barrier +# endif +} + + +void polybench_papi_close() +{ +# ifdef _OPENMP +#pragma omp parallel + { + if (omp_get_thread_num () == polybench_papi_counters_threadid) + { +# endif + int retval; + if ((retval = PAPI_destroy_eventset (&polybench_papi_eventset)) + != PAPI_OK) + test_fail (__FILE__, __LINE__, "PAPI_destroy_eventset", retval); + if (PAPI_is_initialized ()) + PAPI_shutdown (); +# ifdef _OPENMP + } + } +#pragma omp barrier +# endif +} + +int polybench_papi_start_counter(int evid) +{ +# ifndef POLYBENCH_NO_FLUSH_CACHE + polybench_flush_cache(); +# endif + +# ifdef _OPENMP +# pragma omp parallel + { + if (omp_get_thread_num () == polybench_papi_counters_threadid) + { +# endif + + int retval = 1; + char descr[PAPI_MAX_STR_LEN]; + PAPI_event_info_t evinfo; + PAPI_event_code_to_name (polybench_papi_eventlist[evid], descr); + if (PAPI_add_event (polybench_papi_eventset, + polybench_papi_eventlist[evid]) != PAPI_OK) + test_fail (__FILE__, __LINE__, "PAPI_add_event", 1); + if (PAPI_get_event_info (polybench_papi_eventlist[evid], &evinfo) + != PAPI_OK) + test_fail (__FILE__, __LINE__, "PAPI_get_event_info", retval); + if ((retval = PAPI_start (polybench_papi_eventset)) != PAPI_OK) + test_fail (__FILE__, __LINE__, "PAPI_start", retval); +# ifdef _OPENMP + } + } +#pragma omp barrier +# endif + return 0; +} + + +void polybench_papi_stop_counter(int evid) +{ +# ifdef _OPENMP +# pragma omp parallel + { + if (omp_get_thread_num () == polybench_papi_counters_threadid) + { +# endif + int retval; + long_long values[1]; + values[0] = 0; + if ((retval = PAPI_read (polybench_papi_eventset, &values[0])) + != PAPI_OK) + test_fail (__FILE__, __LINE__, "PAPI_read", retval); + + if ((retval = PAPI_stop (polybench_papi_eventset, NULL)) != PAPI_OK) + test_fail (__FILE__, __LINE__, "PAPI_stop", retval); + + polybench_papi_values[evid] = values[0]; + + if ((retval = PAPI_remove_event + (polybench_papi_eventset, + polybench_papi_eventlist[evid])) != PAPI_OK) + test_fail (__FILE__, __LINE__, "PAPI_remove_event", retval); +# ifdef _OPENMP + } + } +#pragma omp barrier +# endif +} + + +void polybench_papi_print() +{ + int verbose = 0; +# ifdef _OPENMP +# pragma omp parallel + { + if (omp_get_thread_num() == polybench_papi_counters_threadid) + { +#ifdef POLYBENCH_PAPI_VERBOSE + verbose = 1; +#endif + if (verbose) + printf ("On thread %d:\n", polybench_papi_counters_threadid); +#endif + int evid; + for (evid = 0; polybench_papi_eventlist[evid] != 0; ++evid) + { + if (verbose) + printf ("%s=", _polybench_papi_eventlist[evid]); + printf ("%llu ", polybench_papi_values[evid]); + if (verbose) + printf ("\n"); + } + printf ("\n"); +# ifdef _OPENMP + } + } +#pragma omp barrier +# endif +} + +#endif +/* ! POLYBENCH_PAPI */ + +void polybench_prepare_instruments() +{ +#ifndef POLYBENCH_NO_FLUSH_CACHE + polybench_flush_cache (); +#endif +#ifdef POLYBENCH_LINUX_FIFO_SCHEDULER + polybench_linux_fifo_scheduler (); +#endif +} + + +void polybench_timer_start() +{ + polybench_prepare_instruments (); +#ifndef POLYBENCH_CYCLE_ACCURATE_TIMER + polybench_t_start = rtclock (); +#else + polybench_c_start = rdtsc (); +#endif +} + + +void polybench_timer_stop() +{ +#ifndef POLYBENCH_CYCLE_ACCURATE_TIMER + polybench_t_end = rtclock (); +#else + polybench_c_end = rdtsc (); +#endif +#ifdef POLYBENCH_LINUX_FIFO_SCHEDULER + polybench_linux_standard_scheduler (); +#endif +} + + +void polybench_timer_print() +{ +#ifdef POLYBENCH_GFLOPS + if (polybench_program_total_flops == 0) + { + printf ("[PolyBench][WARNING] Program flops not defined, use polybench_set_program_flops(value)\n"); + printf ("%0.6lf\n", polybench_t_end - polybench_t_start); + } + else + printf ("%0.2lf\n", + (polybench_program_total_flops / + (double)(polybench_t_end - polybench_t_start)) / 1000000000); +#else +# ifndef POLYBENCH_CYCLE_ACCURATE_TIMER + printf ("%0.6f\n", polybench_t_end - polybench_t_start); +# else + printf ("%Ld\n", polybench_c_end - polybench_c_start); +# endif +#endif +} + +/* + * These functions are used only if the user defines a specific + * inter-array padding. It grows a global structure, + * _polybench_alloc_table, which keeps track of the data allocated via + * polybench_alloc_data (on which inter-array padding is applied), so + * that the original, non-shifted pointer can be recovered when + * calling polybench_free_data. + * + */ +#ifdef POLYBENCH_ENABLE_INTARRAY_PAD +static +void grow_alloc_table() +{ + if (_polybench_alloc_table == NULL || + (_polybench_alloc_table->nb_entries % NB_INITIAL_TABLE_ENTRIES) != 0 || + _polybench_alloc_table->nb_avail_entries != 0) + { + /* Should never happen if the API is properly used. */ + fprintf (stderr, "[ERROR] Inter-array padding requires to use polybench_alloc_data and polybench_free_data\n"); + exit (1); + } + size_t sz = _polybench_alloc_table->nb_entries; + sz += NB_INITIAL_TABLE_ENTRIES; + _polybench_alloc_table->user_view = + realloc (_polybench_alloc_table->user_view, sz * sizeof(void*)); + assert(_polybench_alloc_table->user_view != NULL); + _polybench_alloc_table->real_ptr = + realloc (_polybench_alloc_table->real_ptr, sz * sizeof(void*)); + assert(_polybench_alloc_table->real_ptr != NULL); + _polybench_alloc_table->nb_avail_entries = NB_INITIAL_TABLE_ENTRIES; +} + +static +void* register_padded_pointer(void* ptr, size_t orig_sz, size_t padded_sz) +{ + if (_polybench_alloc_table == NULL) + { + fprintf (stderr, "[ERROR] Inter-array padding requires to use polybench_alloc_data and polybench_free_data\n"); + exit (1); + } + if (_polybench_alloc_table->nb_avail_entries == 0) + grow_alloc_table (); + int id = _polybench_alloc_table->nb_entries++; + _polybench_alloc_table->real_ptr[id] = ptr; + _polybench_alloc_table->user_view[id] = ptr + (padded_sz - orig_sz); + + return _polybench_alloc_table->user_view[id]; +} + + +static +void +free_data_from_alloc_table (void* ptr) +{ + if (_polybench_alloc_table != NULL && _polybench_alloc_table->nb_entries > 0) + { + int i; + for (i = 0; i < _polybench_alloc_table->nb_entries; ++i) + if (_polybench_alloc_table->user_view[i] == ptr || + _polybench_alloc_table->real_ptr[i] == ptr) + break; + if (i != _polybench_alloc_table->nb_entries) + { + free (_polybench_alloc_table->real_ptr[i]); + for (; i < _polybench_alloc_table->nb_entries - 1; ++i) + { + _polybench_alloc_table->user_view[i] = + _polybench_alloc_table->user_view[i + 1]; + _polybench_alloc_table->real_ptr[i] = + _polybench_alloc_table->real_ptr[i + 1]; + } + _polybench_alloc_table->nb_entries--; + _polybench_alloc_table->nb_avail_entries++; + if (_polybench_alloc_table->nb_entries == 0) + { + free (_polybench_alloc_table->user_view); + free (_polybench_alloc_table->real_ptr); + free (_polybench_alloc_table); + _polybench_alloc_table = NULL; + } + } + } +} + +static +void check_alloc_table_state() +{ + if (_polybench_alloc_table == NULL) + { + _polybench_alloc_table = (struct polybench_data_ptrs*) + malloc (sizeof(struct polybench_data_ptrs)); + assert(_polybench_alloc_table != NULL); + _polybench_alloc_table->user_view = + (void**) malloc (sizeof(void*) * NB_INITIAL_TABLE_ENTRIES); + assert(_polybench_alloc_table->user_view != NULL); + _polybench_alloc_table->real_ptr = + (void**) malloc (sizeof(void*) * NB_INITIAL_TABLE_ENTRIES); + assert(_polybench_alloc_table->real_ptr != NULL); + _polybench_alloc_table->nb_entries = 0; + _polybench_alloc_table->nb_avail_entries = NB_INITIAL_TABLE_ENTRIES; + } +} + +#endif // !POLYBENCH_ENABLE_INTARRAY_PAD + + +static +void* +xmalloc(size_t alloc_sz) +{ + void* ret = NULL; + /* By default, post-pad the arrays. Safe behavior, but likely useless. */ + polybench_inter_array_padding_sz += POLYBENCH_INTER_ARRAY_PADDING_FACTOR; + size_t padded_sz = alloc_sz + polybench_inter_array_padding_sz; + int err = posix_memalign (&ret, 4096, padded_sz); + if (! ret || err) + { + fprintf (stderr, "[PolyBench] posix_memalign: cannot allocate memory"); + exit (1); + } + /* Safeguard: this is invoked only if polybench.c has been compiled + with inter-array padding support from polybench.h. If so, move + the starting address of the allocation and return it to the + user. The original pointer is registered in an allocation table + internal to polybench.c. Data must then be freed using + polybench_free_data, which will inspect the allocation table to + free the original pointer.*/ +#ifdef POLYBENCH_ENABLE_INTARRAY_PAD + /* This moves the 'ret' pointer by (padded_sz - alloc_sz) positions, and + registers it in the lookup table for future free using + polybench_free_data. */ + ret = register_padded_pointer(ret, alloc_sz, padded_sz); +#endif + + return ret; +} + + +//void polybench_free_data(void* ptr) +//{ +//#ifdef POLYBENCH_ENABLE_INTARRAY_PAD +// free_data_from_alloc_table (ptr); +//#else +// free (ptr); +//#endif +//} + + +//void* polybench_alloc_data(unsigned long long int n, int elt_size) +//{ +// return malloc(n*elt_size); +//#ifdef POLYBENCH_ENABLE_INTARRAY_PAD +// check_alloc_table_state (); +//#endif +// +// /// FIXME: detect overflow! +// size_t val = n; +// val *= elt_size; +// void* ret = xmalloc (val); +// +// return ret; +//} + +// CHECK: module { +// CHECK-NEXT: } diff --git a/mlir-clang/Test/polybench/utilities/polybench.h b/mlir-clang/Test/polybench/utilities/polybench.h new file mode 100644 index 000000000000..aaa5d1daeb29 --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/polybench.h @@ -0,0 +1,246 @@ +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +/* + * polybench.h: this file is part of PolyBench/C + * + * Polybench header for instrumentation. + * + * Programs must be compiled with `-I utilities utilities/polybench.c' + * + * Optionally, one can define: + * + * -DPOLYBENCH_TIME, to report the execution time, + * OR (exclusive): + * -DPOLYBENCH_PAPI, to use PAPI H/W counters (defined in polybench.c) + * + * + * See README or utilities/polybench.c for additional options. + * + */ +#ifndef POLYBENCH_H +# define POLYBENCH_H + +# include + +/* Array padding. By default, none is used. */ +# ifndef POLYBENCH_PADDING_FACTOR +/* default: */ +# define POLYBENCH_PADDING_FACTOR 0 +# endif + +/* Inter-array padding, for use with . By default, none is used. */ +# ifndef POLYBENCH_INTER_ARRAY_PADDING_FACTOR +/* default: */ +# define POLYBENCH_INTER_ARRAY_PADDING_FACTOR 0 +# undef POLYBENCH_ENABLE_INTARRAY_PAD +# else +# define POLYBENCH_ENABLE_INTARRAY_PAD +# endif + + +/* C99 arrays in function prototype. By default, do not use. */ +# ifdef POLYBENCH_USE_C99_PROTO +# define POLYBENCH_C99_SELECT(x,y) y +# else +/* default: */ +# define POLYBENCH_C99_SELECT(x,y) x +# endif + + +/* Scalar loop bounds in SCoPs. By default, use parametric loop bounds. */ +# ifdef POLYBENCH_USE_SCALAR_LB +# define POLYBENCH_LOOP_BOUND(x,y) x +# else +/* default: */ +# define POLYBENCH_LOOP_BOUND(x,y) y +# endif + +/* Use the 'restrict' keyword to declare that the different arrays do not + * alias. By default, we do not use it as it is only supported in C99 and + * even here several compilers do not properly get it. + */ +# ifdef POLYBENCH_USE_RESTRICT +# define POLYBENCH_RESTRICT restrict +# else +/* default: */ +# define POLYBENCH_RESTRICT +# endif + +/* Macros to reference an array. Generic for heap and stack arrays + (C99). Each array dimensionality has his own macro, to be used at + declaration or as a function argument. + Example: + int b[x] => POLYBENCH_1D_ARRAY(b, x) + int A[N][N] => POLYBENCH_2D_ARRAY(A, N, N) +*/ +# ifndef POLYBENCH_STACK_ARRAYS +# define POLYBENCH_ARRAY(x) *x +# ifdef POLYBENCH_ENABLE_INTARRAY_PAD +# define POLYBENCH_FREE_ARRAY(x) polybench_free_data((void*)x); +# else +# define POLYBENCH_FREE_ARRAY(x) free((void*)x); +# endif +# define POLYBENCH_DECL_VAR(x) (*x) +# else +# define POLYBENCH_ARRAY(x) x +# define POLYBENCH_FREE_ARRAY(x) +# define POLYBENCH_DECL_VAR(x) x +# endif +/* Macros for using arrays in the function prototypes. */ +# define POLYBENCH_1D(var, dim1,ddim1) var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR] +# define POLYBENCH_2D(var, dim1, dim2, ddim1, ddim2) var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim2,ddim2) + POLYBENCH_PADDING_FACTOR] +# define POLYBENCH_3D(var, dim1, dim2, dim3, ddim1, ddim2, ddim3) var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim2,ddim2) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim3,ddim3) + POLYBENCH_PADDING_FACTOR] +# define POLYBENCH_4D(var, dim1, dim2, dim3, dim4, ddim1, ddim2, ddim3, ddim4) var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim2,ddim2) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim3,ddim3) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim4,ddim4) + POLYBENCH_PADDING_FACTOR] +# define POLYBENCH_5D(var, dim1, dim2, dim3, dim4, dim5, ddim1, ddim2, ddim3, ddim4, ddim5) var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim2,ddim2) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim3,ddim3) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim4,ddim4) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim5,ddim5) + POLYBENCH_PADDING_FACTOR] +/* Macros for using arrays within the functions. */ +# define POLYBENCH_1D_F(var, dim1,ddim1) var[POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR] +# define POLYBENCH_2D_F(var, dim1, dim2, ddim1, ddim2) var[POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim2,ddim2) + POLYBENCH_PADDING_FACTOR] +# define POLYBENCH_3D_F(var, dim1, dim2, dim3, ddim1, ddim2, ddim3) var[POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim2,ddim2) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim3,ddim3) + POLYBENCH_PADDING_FACTOR] +# define POLYBENCH_4D_F(var, dim1, dim2, dim3, dim4, ddim1, ddim2, ddim3, ddim4) var[POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim2,ddim2) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim3,ddim3) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim4,ddim4) + POLYBENCH_PADDING_FACTOR] +# define POLYBENCH_5D_F(var, dim1, dim2, dim3, dim4, dim5, ddim1, ddim2, ddim3, ddim4, ddim5) var[POLYBENCH_C99_SELECT(dim1,ddim1) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim2,ddim2) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim3,ddim3) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim4,ddim4) + POLYBENCH_PADDING_FACTOR][POLYBENCH_C99_SELECT(dim5,ddim5) + POLYBENCH_PADDING_FACTOR] + + +/* Macros to allocate heap arrays. + Example: + polybench_alloc_2d_array(N, M, double) => allocates N x M x sizeof(double) + and returns a pointer to the 2d array + */ +# define POLYBENCH_ALLOC_1D_ARRAY(n1, type) \ + (type(*)[n1 + POLYBENCH_PADDING_FACTOR])polybench_alloc_data (n1 + POLYBENCH_PADDING_FACTOR, sizeof(type)) +# define POLYBENCH_ALLOC_2D_ARRAY(n1, n2, type) \ + (type(*)[n1 + POLYBENCH_PADDING_FACTOR][n2 + POLYBENCH_PADDING_FACTOR])polybench_alloc_data ((n1 + POLYBENCH_PADDING_FACTOR) * (n2 + POLYBENCH_PADDING_FACTOR), sizeof(type)) +# define POLYBENCH_ALLOC_3D_ARRAY(n1, n2, n3, type) \ + (type(*)[n1 + POLYBENCH_PADDING_FACTOR][n2 + POLYBENCH_PADDING_FACTOR][n3 + POLYBENCH_PADDING_FACTOR])polybench_alloc_data ((n1 + POLYBENCH_PADDING_FACTOR) * (n2 + POLYBENCH_PADDING_FACTOR) * (n3 + POLYBENCH_PADDING_FACTOR), sizeof(type)) +# define POLYBENCH_ALLOC_4D_ARRAY(n1, n2, n3, n4, type) \ + (type(*)[n1 + POLYBENCH_PADDING_FACTOR][n2 + POLYBENCH_PADDING_FACTOR][n3 + POLYBENCH_PADDING_FACTOR][n4 + POLYBENCH_PADDING_FACTOR])polybench_alloc_data ((n1 + POLYBENCH_PADDING_FACTOR) * (n2 + POLYBENCH_PADDING_FACTOR) * (n3 + POLYBENCH_PADDING_FACTOR) * (n4 + POLYBENCH_PADDING_FACTOR), sizeof(type)) +# define POLYBENCH_ALLOC_5D_ARRAY(n1, n2, n3, n4, n5, type) \ + (type(*)[n1 + POLYBENCH_PADDING_FACTOR][n2 + POLYBENCH_PADDING_FACTOR][n3 + POLYBENCH_PADDING_FACTOR][n4 + POLYBENCH_PADDING_FACTOR][n5 + POLYBENCH_PADDING_FACTOR])polybench_alloc_data ((n1 + POLYBENCH_PADDING_FACTOR) * (n2 + POLYBENCH_PADDING_FACTOR) * (n3 + POLYBENCH_PADDING_FACTOR) * (n4 + POLYBENCH_PADDING_FACTOR) * (n5 + POLYBENCH_PADDING_FACTOR), sizeof(type)) + +/* Macros for array declaration. */ +# ifndef POLYBENCH_STACK_ARRAYS +# define POLYBENCH_1D_ARRAY_DECL(var, type, dim1, ddim1) \ + type POLYBENCH_1D_F(POLYBENCH_DECL_VAR(var), dim1, ddim1); \ + var = POLYBENCH_ALLOC_1D_ARRAY(POLYBENCH_C99_SELECT(dim1, ddim1), type); +# define POLYBENCH_2D_ARRAY_DECL(var, type, dim1, dim2, ddim1, ddim2) \ + type POLYBENCH_2D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, ddim1, ddim2); \ + var = POLYBENCH_ALLOC_2D_ARRAY(POLYBENCH_C99_SELECT(dim1, ddim1), POLYBENCH_C99_SELECT(dim2, ddim2), type); +# define POLYBENCH_3D_ARRAY_DECL(var, type, dim1, dim2, dim3, ddim1, ddim2, ddim3) \ + type POLYBENCH_3D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, ddim1, ddim2, ddim3); \ + var = POLYBENCH_ALLOC_3D_ARRAY(POLYBENCH_C99_SELECT(dim1, ddim1), POLYBENCH_C99_SELECT(dim2, ddim2), POLYBENCH_C99_SELECT(dim3, ddim3), type); +# define POLYBENCH_4D_ARRAY_DECL(var, type, dim1, dim2, dim3, dim4, ddim1, ddim2, ddim3, ddim4) \ + type POLYBENCH_4D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, dim4, ddim1, ddim2, ddim3, ddim4); \ + var = POLYBENCH_ALLOC_4D_ARRAY(POLYBENCH_C99_SELECT(dim1, ddim1), POLYBENCH_C99_SELECT(dim2, ddim2), POLYBENCH_C99_SELECT(dim3, ddim3), POLYBENCH_C99_SELECT(dim4, ddim4), type); +# define POLYBENCH_5D_ARRAY_DECL(var, type, dim1, dim2, dim3, dim4, dim5, ddim1, ddim2, ddim3, ddim4, ddim5) \ + type POLYBENCH_5D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, dim4, dim5, ddim1, ddim2, ddim3, ddim4, ddim5); \ + var = POLYBENCH_ALLOC_5D_ARRAY(POLYBENCH_C99_SELECT(dim1, ddim1), POLYBENCH_C99_SELECT(dim2, ddim2), POLYBENCH_C99_SELECT(dim3, ddim3), POLYBENCH_C99_SELECT(dim4, ddim4), POLYBENCH_C99_SELECT(dim5, ddim5), type); +# else +# define POLYBENCH_1D_ARRAY_DECL(var, type, dim1, ddim1) \ + type POLYBENCH_1D_F(POLYBENCH_DECL_VAR(var), dim1, ddim1); +# define POLYBENCH_2D_ARRAY_DECL(var, type, dim1, dim2, ddim1, ddim2) \ + type POLYBENCH_2D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, ddim1, ddim2); +# define POLYBENCH_3D_ARRAY_DECL(var, type, dim1, dim2, dim3, ddim1, ddim2, ddim3) \ + type POLYBENCH_3D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, ddim1, ddim2, ddim3); +# define POLYBENCH_4D_ARRAY_DECL(var, type, dim1, dim2, dim3, dim4, ddim1, ddim2, ddim3, ddim4) \ + type POLYBENCH_4D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, dim4, ddim1, ddim2, ddim3, ddim4); +# define POLYBENCH_5D_ARRAY_DECL(var, type, dim1, dim2, dim3, dim4, dim5, ddim1, ddim2, ddim3, ddim4, ddim5) \ + type POLYBENCH_5D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, dim4, dim5, ddim1, ddim2, ddim3, ddim4, ddim5); +# endif + + +/* Dead-code elimination macros. Use argc/argv for the run-time check. */ +# ifndef POLYBENCH_DUMP_ARRAYS +# define POLYBENCH_DCE_ONLY_CODE if (argc > 42 && ! strcmp(argv[0], "")) +# else +# define POLYBENCH_DCE_ONLY_CODE +# endif + +#define POLYBENCH_DUMP_TARGET stderr +#define POLYBENCH_DUMP_START fprintf(POLYBENCH_DUMP_TARGET, "==BEGIN DUMP_ARRAYS==\n") +#define POLYBENCH_DUMP_FINISH fprintf(POLYBENCH_DUMP_TARGET, "==END DUMP_ARRAYS==\n") +#define POLYBENCH_DUMP_BEGIN(s) fprintf(POLYBENCH_DUMP_TARGET, "begin dump: %s", s) +#define POLYBENCH_DUMP_END(s) fprintf(POLYBENCH_DUMP_TARGET, "\nend dump: %s\n", s) + +# define polybench_prevent_dce(func) \ + POLYBENCH_DCE_ONLY_CODE \ + func + + +/* Performance-related instrumentation. See polybench.c */ +# define polybench_start_instruments +# define polybench_stop_instruments +# define polybench_print_instruments + + +/* PAPI support. */ +# ifdef POLYBENCH_PAPI +extern const unsigned int polybench_papi_eventlist[]; +# undef polybench_start_instruments +# undef polybench_stop_instruments +# undef polybench_print_instruments +# define polybench_set_papi_thread_report(x) \ + polybench_papi_counters_threadid = x; +# define polybench_start_instruments \ + polybench_prepare_instruments(); \ + polybench_papi_init(); \ + int evid; \ + for (evid = 0; polybench_papi_eventlist[evid] != 0; evid++) \ + { \ + if (polybench_papi_start_counter(evid)) \ + continue; \ + +# define polybench_stop_instruments \ + polybench_papi_stop_counter(evid); \ + } \ + polybench_papi_close(); \ + +# define polybench_print_instruments polybench_papi_print(); +# endif + + +/* Timing support. */ +# if defined(POLYBENCH_TIME) || defined(POLYBENCH_GFLOPS) +# undef polybench_start_instruments +# undef polybench_stop_instruments +# undef polybench_print_instruments +# define polybench_start_instruments polybench_timer_start(); +# define polybench_stop_instruments polybench_timer_stop(); +# define polybench_print_instruments polybench_timer_print(); +extern double polybench_program_total_flops; +extern void polybench_timer_start(); +extern void polybench_timer_stop(); +extern void polybench_timer_print(); +# endif + +/* PAPI support. */ +# ifdef POLYBENCH_PAPI +extern int polybench_papi_start_counter(int evid); +extern void polybench_papi_stop_counter(int evid); +extern void polybench_papi_init(); +extern void polybench_papi_close(); +extern void polybench_papi_print(); +# endif + +/* Function prototypes. */ +//extern void* polybench_alloc_data(unsigned long long int n, int elt_size); +//extern void polybench_free_data(void* ptr); +static inline void* polybench_alloc_data(unsigned long long int n, int elt_size) { + return malloc(n*elt_size); +} +static inline void polybench_free_data(void* ptr) { + free(ptr); +} +/* PolyBench internal functions that should not be directly called by */ +/* the user, unless when designing customized execution profiling */ +/* approaches. */ +extern void polybench_flush_cache(); +extern void polybench_prepare_instruments(); + + +#endif /* !POLYBENCH_H */ diff --git a/mlir-clang/Test/polybench/utilities/polybench.spec b/mlir-clang/Test/polybench/utilities/polybench.spec new file mode 100644 index 000000000000..67448d362224 --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/polybench.spec @@ -0,0 +1,31 @@ +kernel category datatype params MINI SMALL MEDIUM LARGE EXTRALARGE +correlation datamining double M N 28 32 80 100 240 260 1200 1400 2600 3000 +covariance datamining double M N 28 32 80 100 240 260 1200 1400 2600 3000 +2mm linear-algebra/kernels double NI NJ NK NL 16 18 22 24 40 50 70 80 180 190 210 220 800 900 1100 1200 1600 1800 2200 2400 +3mm linear-algebra/kernels double NI NJ NK NL NM 16 18 20 22 24 40 50 60 70 80 180 190 200 210 220 800 900 1000 1100 1200 1600 1800 2000 2200 2400 +atax linear-algebra/kernels double M N 38 42 116 124 390 410 1900 2100 1800 2200 +bicg linear-algebra/kernels double M N 38 42 116 124 390 410 1900 2100 1800 2200 +doitgen linear-algebra/kernels double NQ NR NP 8 10 12 20 25 30 40 50 60 140 150 160 220 250 270 +mvt linear-algebra/kernels double N 40 120 400 2000 4000 +gemm linear-algebra/blas double NI NJ NK 20 25 30 60 70 80 200 220 240 1000 1100 1200 2000 2300 2600 +gemver linear-algebra/blas double N 40 120 400 2000 4000 +gesummv linear-algebra/blas double N 30 90 250 1300 2800 +symm linear-algebra/blas double M N 20 30 60 80 200 240 1000 1200 2000 2600 +syr2k linear-algebra/blas double M N 20 30 60 80 200 240 1000 1200 2000 2600 +syrk linear-algebra/blas double M N 20 30 60 80 200 240 1000 1200 2000 2600 +trmm linear-algebra/blas double M N 20 30 60 80 200 240 1000 1200 2000 2600 +cholesky linear-algebra/solvers double N 40 120 400 2000 4000 +durbin linear-algebra/solvers double N 40 120 400 2000 4000 +gramschmidt linear-algebra/solvers double M N 20 30 60 80 200 240 1000 1200 2000 2600 +lu linear-algebra/solvers double N 40 120 400 2000 4000 +ludcmp linear-algebra/solvers double N 40 120 400 2000 4000 +trisolv linear-algebra/solvers double N 40 120 400 2000 4000 +deriche medley float W H 64 64 192 128 720 480 4096 2160 7680 4320 +floyd-warshall medley int N 60 180 500 2800 5600 +nussinov medley int N 60 180 500 2500 5500 +adi stencils double TSTEPS N 20 20 40 60 100 200 500 1000 1000 2000 +fdtd-2d stencils double TMAX NX NY 20 20 30 40 60 80 100 200 240 500 1000 1200 1000 2000 2600 +heat-3d stencils double TSTEPS N 20 10 40 20 100 40 500 120 1000 200 +jacobi-1d stencils double TSTEPS N 20 30 40 120 100 400 500 2000 1000 4000 +jacobi-2d stencils double TSTEPS N 20 30 40 90 100 250 500 1300 1000 2800 +seidel-2d stencils double TSTEPS N 20 40 40 120 100 400 500 2000 1000 4000 \ No newline at end of file diff --git a/mlir-clang/Test/polybench/utilities/run-all.pl b/mlir-clang/Test/polybench/utilities/run-all.pl new file mode 100644 index 000000000000..6b6674d10a94 --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/run-all.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl + +# Visits every directory, calls make, and then executes the benchmark +# (Designed for making sure every kernel compiles/runs after modifications) +# +# Written by Tomofumi Yuki, 01/15 2015 +# + +my $TARGET_DIR = "."; + +if ($#ARGV != 0 && $#ARGV != 1) { + printf("usage perl run-all.pl target-dir [output-file]\n"); + exit(1); +} + + + +if ($#ARGV >= 0) { + $TARGET_DIR = $ARGV[0]; +} + +my $OUTFILE = ""; +if ($#ARGV == 1) { + $OUTFILE = $ARGV[1]; +} + + +my @categories = ('linear-algebra/blas', + 'linear-algebra/kernels', + 'linear-algebra/solvers', + 'datamining', + 'stencils', + 'medley'); + + +foreach $cat (@categories) { + my $target = $TARGET_DIR.'/'.$cat; + opendir DIR, $target or die "directory $target not found.\n"; + while (my $dir = readdir DIR) { + next if ($dir=~'^\..*'); + next if (!(-d $target.'/'.$dir)); + + my $kernel = $dir; + my $targetDir = $target.'/'.$dir; + my $command = "cd $targetDir; make clean; make; ./$kernel"; + $command .= " 2>> $OUTFILE" if ($OUTFILE ne ''); + print($command."\n"); + system($command); + } + + closedir DIR; +} + diff --git a/mlir-clang/Test/polybench/utilities/template-for-new-benchmark.c b/mlir-clang/Test/polybench/utilities/template-for-new-benchmark.c new file mode 100644 index 000000000000..c94a9183ba4a --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/template-for-new-benchmark.c @@ -0,0 +1,104 @@ +// RUN: mlir-clang %s %stdinclude | FileCheck %s + +/** + * This version is stamped on May 10, 2016 + * + * Contact: + * Louis-Noel Pouchet + * Tomofumi Yuki + * + * Web address: http://polybench.sourceforge.net + */ +#include +#include +#include +#include + +/* Include polybench common header. */ +#include + +/* Include benchmark-specific header. */ +/* Default data type is double, default size is N=1024. */ +#include "template-for-new-benchmark.h" + + +/* Array initialization. */ +static +void init_array(int n, DATA_TYPE POLYBENCH_2D(C,N,N,n,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + C[i][j] = 42; +} + + +/* DCE code. Must scan the entire live-out data. + Can be used also to check the correctness of the output. */ +static +void print_array(int n, DATA_TYPE POLYBENCH_2D(C,N,N,n,n)) +{ + int i, j; + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + fprintf (stderr, DATA_PRINTF_MODIFIER, C[i][j]); + if (i % 20 == 0) fprintf (stderr, "\n"); + } + fprintf (stderr, "\n"); +} + + +/* Main computational kernel. The whole function will be timed, + including the call and return. */ +static +void kernel_template(int n, DATA_TYPE POLYBENCH_2D(C,N,N,n,n)) +{ + int i, j; + +#pragma scop + for (i = 0; i < _PB_N; i++) + for (j = 0; j < _PB_N; j++) + C[i][j] += 42; +#pragma endscop + +} + + +int main(int argc, char** argv) +{ + /* Retrieve problem size. */ + int n = N; + + /* Variable declaration/allocation. */ + POLYBENCH_2D_ARRAY_DECL(C,DATA_TYPE,N,N,n,n); + + /* Initialize array(s). */ + init_array (n, POLYBENCH_ARRAY(C)); + + /* Start timer. */ + polybench_start_instruments; + + /* Run kernel. */ + kernel_template (n, POLYBENCH_ARRAY(C)); + + /* Stop and print timer. */ + polybench_stop_instruments; + polybench_print_instruments; + + /* Prevent dead-code elimination. All live-out data must be printed + by the function call in argument. */ + polybench_prevent_dce(print_array(n, POLYBENCH_ARRAY(C))); + + /* Be clean. */ + POLYBENCH_FREE_ARRAY(C); + + return 0; +} + +// CHECK: func @main(%arg0: i32, %arg1: !llvm.ptr>) -> i32 { +// CHECK-NEXT: %c0_i32 = constant 0 : i32 +// CHECK-NEXT: return %c0_i32 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/mlir-clang/Test/polybench/utilities/time_benchmark.sh b/mlir-clang/Test/polybench/utilities/time_benchmark.sh new file mode 100644 index 000000000000..b9dc69e9eb55 --- /dev/null +++ b/mlir-clang/Test/polybench/utilities/time_benchmark.sh @@ -0,0 +1,78 @@ +#!/bin/sh +## time_benchmark.sh for in /Users/pouchet +## +## Made by Louis-Noel Pouchet +## Contact: +## +## Started on Sat Oct 29 00:03:48 2011 Louis-Noel Pouchet +## Last update Fri Apr 22 15:39:13 2016 Louis-Noel Pouchet +## + +## Maximal variance accepted between the 3 median runs for performance results. +## Here 5% +VARIANCE_ACCEPTED=5; + +if [ $# -ne 1 ]; then + echo "Usage: ./time_benchmarh.sh "; + echo "Example: ./time_benchmarh.sh \"./a.out\""; + echo "Note: the file must be a Polybench program compiled with -DPOLYBENCH_TIME"; + exit 1; +fi; + + +compute_mean_exec_time() +{ + file="$1"; + benchcomputed="$2"; + cat "$file" | grep "[0-9]\+" | sort -n | head -n 4 | tail -n 3 > avg.out; + expr="(0"; + while read n; do + expr="$expr+$n"; + done < avg.out; + time=`echo "scale=8;$expr)/3" | bc`; + tmp=`echo "$time" | cut -d '.' -f 1`; + if [ -z "$tmp" ]; then + time="0$time"; + fi; + val1=`cat avg.out | head -n 1`; + val2=`cat avg.out | head -n 2 | tail -n 1`; + val3=`cat avg.out | head -n 3 | tail -n 1`; + val11=`echo "a=$val1 - $time;if(0>a)a*=-1;a" | bc 2>&1`; + test_err=`echo "$val11" | grep error`; + if ! [ -z "$test_err" ]; then + echo "[ERROR] Program output does not match expected single-line with time."; + echo "[ERROR] The program must be a PolyBench, compiled with -DPOLYBENCH_TIME"; + exit 1; + fi; + val12=`echo "a=$val2 - $time;if(0>a)a*=-1;a" | bc`; + val13=`echo "a=$val3 - $time;if(0>a)a*=-1;a" | bc`; + myvar=`echo "$val11 $val12 $val13" | awk '{ if ($1 > $2) { if ($1 > $3) print $1; else print $3; } else { if ($2 > $3) print $2; else print $3; } }'`; + variance=`echo "scale=5;($myvar/$time)*100" | bc`; + tmp=`echo "$variance" | cut -d '.' -f 1`; + if [ -z "$tmp" ]; then + variance="0$variance"; + fi; + compvar=`echo "$variance $VARIANCE_ACCEPTED" | awk '{ if ($1 < $2) print "ok"; else print "error"; }'`; + if [ "$compvar" = "error" ]; then + echo "[WARNING] Variance is above thresold, unsafe performance measurement"; + echo " => max deviation=$variance%, tolerance=$VARIANCE_ACCEPTED%"; + WARNING_VARIANCE="$WARNING_VARIANCE\n$benchcomputed: max deviation=$variance%, tolerance=$VARIANCE_ACCEPTED%"; + else + echo "[INFO] Maximal deviation from arithmetic mean of 3 average runs: $variance%"; + fi; + PROCESSED_TIME="$time"; + rm -f avg.out; +} + +echo "[INFO] Running 5 times $1..." +echo "[INFO] Maximal variance authorized on 3 average runs: $VARIANCE_ACCEPTED%..."; + +$1 > ____tempfile.data.polybench; +$1 >> ____tempfile.data.polybench; +$1 >> ____tempfile.data.polybench; +$1 >> ____tempfile.data.polybench; +$1 >> ____tempfile.data.polybench; + +compute_mean_exec_time "____tempfile.data.polybench" "$1"; +echo "[INFO] Normalized time: $PROCESSED_TIME"; +rm -f ____tempfile.data.polybench; diff --git a/mlir-clang/mlir-clang.cc b/mlir-clang/mlir-clang.cc new file mode 100644 index 000000000000..7b56ffefa7b8 --- /dev/null +++ b/mlir-clang/mlir-clang.cc @@ -0,0 +1,230 @@ +#include "mlir/Dialect/Affine/Passes.h" +#include "mlir/Dialect/GPU/GPUDialect.h" +#include "mlir/Dialect/SCF/Passes.h" +#include "mlir/Dialect/SCF/SCF.h" +#include "mlir/Conversion/AffineToStandard/AffineToStandard.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Verifier.h" +#include "mlir/Target/LLVMIR/Export.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include + +#include "polygeist/Passes/Passes.h" +#include "polygeist/Dialect.h" + +using namespace llvm; + +static cl::OptionCategory toolOptions("clang to mlir - tool options"); + +static cl::opt CudaLower("cuda-lower", cl::init(false), + cl::desc("Add parallel loops around cuda")); + +static cl::opt EmitLLVM("emit-llvm", cl::init(false), + cl::desc("Emit llvm")); + +static cl::opt ImmediateMLIR("immediate", cl::init(false), + cl::desc("Emit immediate mlir")); + +static cl::opt RaiseToAffine("raise-scf-to-affine", cl::init(false), + cl::desc("Raise SCF to Affine")); + +static cl::opt + DetectReduction("detect-reduction", cl::init(false), + cl::desc("Detect reduction in inner most loop")); + +static cl::opt Standard("std", cl::init(""), + cl::desc("C/C++ std")); + +static cl::opt Output("o", cl::init("-"), cl::desc("Output file")); + +static cl::list inputFileName(cl::Positional, cl::OneOrMore, + cl::desc(""), + cl::cat(toolOptions)); + +static cl::opt cfunction("function", + cl::desc(""), + cl::init("main"), cl::cat(toolOptions)); + +static cl::opt FOpenMP("fopenmp", cl::init(false), + cl::desc("Enable OpenMP")); + +static cl::opt MArch("march", cl::init(""), + cl::desc("Architecture")); + +static cl::opt + showDialects("show-dialects", + llvm::cl::desc("Print the list of registered dialects"), + llvm::cl::init(false), cl::cat(toolOptions)); + +static cl::list includeDirs("I", cl::desc("include search path"), + cl::cat(toolOptions)); + +static cl::list defines("D", cl::desc("defines"), + cl::cat(toolOptions)); + +class MemRefInsider : public mlir::MemRefElementTypeInterface::FallbackModel { + +}; + +#include "Lib/clang-mlir.cc" +#include "mlir/Conversion/SCFToStandard/SCFToStandard.h" +#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Transforms/Passes.h" +int main(int argc, char **argv) { + + using namespace mlir; + + InitLLVM y(argc, argv); + + cl::ParseCommandLineOptions(argc, argv); + assert(inputFileName.size()); + for (auto inp : inputFileName) { + std::ifstream inputFile(inp); + if (!inputFile.good()) { + outs() << "Not able to open file: " << inp << "\n"; + return -1; + } + } + + // registerDialect(); + // registerDialect(); + mlir::DialectRegistry registry; + mlir::registerLLVMDialectTranslation(registry); + MLIRContext context(registry); + + context.disableMultithreading(); + context.getOrLoadDialect(); + context.getOrLoadDialect(); + context.getOrLoadDialect(); + context.getOrLoadDialect(); + context.getOrLoadDialect(); + context.getOrLoadDialect(); + context.getOrLoadDialect(); + context.getOrLoadDialect(); + context.getOrLoadDialect(); + // MLIRContext context; + + LLVM::LLVMPointerType::attachInterface(context); + LLVM::LLVMStructType::attachInterface(context); + + if (showDialects) { + outs() << "Registered Dialects:\n"; + for (Dialect *dialect : context.getLoadedDialects()) { + outs() << dialect->getNamespace() << "\n"; + } + return 0; + } + auto module = + mlir::ModuleOp::create(mlir::OpBuilder(&context).getUnknownLoc()); + + llvm::Triple triple; + llvm::DataLayout DL(""); + parseMLIR(argv[0], inputFileName, cfunction, includeDirs, defines, module, + triple, DL); + mlir::PassManager pm(&context); + + if (ImmediateMLIR) { + llvm::errs() << "\n"; + module.dump(); + llvm::errs() << "\n"; + } + pm.enableVerifier(false); + mlir::OpPassManager &optPM = pm.nest(); + if (true) { + optPM.addPass(mlir::createCSEPass()); + optPM.addPass(mlir::createCanonicalizerPass()); + optPM.addPass(polygeist::createMem2RegPass()); + optPM.addPass(mlir::createCSEPass()); + optPM.addPass(mlir::createCanonicalizerPass()); + optPM.addPass(polygeist::createMem2RegPass()); + optPM.addPass(mlir::createCanonicalizerPass()); + optPM.addPass(polygeist::createLoopRestructurePass()); + optPM.addPass(polygeist::replaceAffineCFGPass()); + optPM.addPass(mlir::createCanonicalizerPass()); + optPM.addPass(mlir::createAffineScalarReplacementPass()); + optPM.addPass(polygeist::createCanonicalizeForPass()); + optPM.addPass(mlir::createCanonicalizerPass()); + if (RaiseToAffine) { + optPM.addPass(polygeist::createCanonicalizeForPass()); + optPM.addPass(mlir::createCanonicalizerPass()); + optPM.addPass(mlir::createLoopInvariantCodeMotionPass()); + optPM.addPass(polygeist::createRaiseSCFToAffinePass()); + optPM.addPass(polygeist::replaceAffineCFGPass()); + } + if (DetectReduction) + optPM.addPass(polygeist::detectReductionPass()); + if (mlir::failed(pm.run(module))) { + module.dump(); + return 4; + } + if (mlir::failed(mlir::verify(module))) { + module.dump(); + return 5; + } + +#define optPM optPM2 +#define pm pm2 + mlir::PassManager pm(&context); + mlir::OpPassManager &optPM = pm.nest(); + + optPM.addPass(mlir::createCanonicalizerPass()); + optPM.addPass(mlir::createCSEPass()); + optPM.addPass(mlir::createCanonicalizerPass()); + pm.addPass(mlir::createSymbolDCEPass()); + + if (CudaLower) + optPM.addPass(polygeist::createParallelLowerPass()); + + if (EmitLLVM) { + pm.addPass(mlir::createLowerAffinePass()); + pm.addPass(mlir::createLowerToCFGPass()); + LowerToLLVMOptions options(&context); + options.dataLayout = DL; + // invalid for gemm.c init array + // options.useBarePtrCallConv = true; + pm.addPass(mlir::createLowerToLLVMPass(options)); + } + + if (mlir::failed(pm.run(module))) { + module.dump(); + return 4; + } + // module.dump(); + if (mlir::failed(mlir::verify(module))) { + module.dump(); + return 5; + } + } + + if (EmitLLVM) { + llvm::LLVMContext llvmContext; + auto llvmModule = mlir::translateModuleToLLVMIR(module, llvmContext); + if (!llvmModule) { + module.dump(); + llvm::errs() << "Failed to emit LLVM IR\n"; + return -1; + } + llvmModule->setDataLayout(DL); + llvmModule->setTargetTriple(triple.getTriple()); + if (Output == "-") + llvm::outs() << *llvmModule << "\n"; + else { + std::error_code EC; + llvm::raw_fd_ostream out(Output, EC); + out << *llvmModule << "\n"; + } + + } else { + if (Output == "-") + module.print(outs()); + else { + std::error_code EC; + llvm::raw_fd_ostream out(Output, EC); + module.print(out); + } + } + return 0; +}