diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b676c7d0ec00..4968da0bc778 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ on: jobs: build: - name: Build ${{ matrix.os }} ${{ matrix.compiler }} polymer=${{ matrix.polymer }} pluto ${{ matrix.pluto-build }} mlir ${{ matrix.mlir-build }} polygeist ${{ matrix.polygeist-build }} + name: Build ${{ matrix.os }} ${{ matrix.compiler }} polymer=${{ matrix.polymer }} pluto ${{ matrix.pluto }} ${{ matrix.pluto-build }} mlir ${{ matrix.mlir-build }} polygeist ${{ matrix.polygeist-build }} runs-on: ${{ matrix.os }} strategy: @@ -17,7 +17,8 @@ jobs: pluto-build: ["Release"] # Only Release supported currently mlir-build: ["Release"] #, "Debug"] # "RelWithDebInfo" polygeist-build: ["Release", "Debug"] #, "Debug"] # "RelWithDebInfo" - polymer: ["0", "1"] + polymer: ["1"] + pluto: ["1", "0"] os: [ubuntu-22.04] compiler: [gcc, clang] cxxcompiler: [g++, clang++] @@ -26,6 +27,15 @@ jobs: cxxcompiler: clang++ - compiler: clang cxxcompiler: g++ + include: + - pluto-build: "Release" + mlir-build: "Release" + polygeist-build: "Release" + polymer: "0" + pluto: "0" + os: ubuntu-22.04 + compiler: clang + cxxcompiler: clang++ timeout-minutes: 360 steps: @@ -35,6 +45,11 @@ jobs: path: src submodules: 'recursive' + - name: define isl 1 + id: defineisl + run: | + ( [ ${{matrix.pluto}} == 0 ] && echo "isl=1" || echo "isl=0" ) >> $GITHUB_OUTPUT + - name: add dependencies run: sudo apt-get install -y llvm-15-tools ninja-build libbison-dev libtool libgmp-dev #libflex-dev cmake binutils-gold binutils binutils-dev ${{ matrix.compiler }} ${{ matrix.linker-pkg }} @@ -46,13 +61,13 @@ jobs: key: ${{ matrix.pluto-build }}-${{ matrix.os }}-pluto-${{ matrix.polymer }} - name: Pluto build - if: steps.cache-pluto.outputs.cache-hit != 'true' && matrix.polymer == '1' + if: steps.cache-pluto.outputs.cache-hit != 'true' && matrix.polymer == '1' && matrix.pluto == '1' run: | mkdir -p pluto-build PATH="/usr/lib/llvm-15/bin/:$PATH" src/tools/polymer/build_polymer_deps.sh $(pwd)/pluto-build sleep 1s - uses: actions/cache/save@v3 - if: steps.cache-pluto.outputs.cache-hit != 'true' + if: steps.cache-pluto.outputs.cache-hit != 'true' && matrix.polymer == '1' && matrix.pluto == '1' with: path: pluto-build key: ${{ matrix.pluto-build }}-${{ matrix.os }}-pluto-${{ matrix.polymer }} @@ -66,14 +81,14 @@ jobs: uses: actions/cache@v3 with: path: mlir-build - key: ${{ matrix.mlir-build }}-${{ matrix.os }}-mlir-${{ steps.getversion.outputs.version }} + key: ${{ matrix.mlir-build }}-${{ matrix.os }}-mlir-${{ steps.getversion.outputs.version }}-polly - name: MLIR build if: steps.cache-mlir.outputs.cache-hit != 'true' run: | mkdir mlir-build cd mlir-build - CYMBL=OFF cmake ../src/llvm-project/llvm -GNinja -DLLVM_ENABLE_PROJECTS="llvm;clang;mlir" -DCMAKE_BUILD_TYPE=${{ matrix.mlir-build }} -DCMAKE_C_COMPILER=/bin/clang -DCMAKE_CXX_COMPILER=/bin/clang++ -DCMAKE_ASM_COMPILER=/bin/clang -DCMAKE_CXX_FLAGS="-Wno-c++11-narrowing" + CYMBL=OFF cmake ../src/llvm-project/llvm -GNinja -DLLVM_ENABLE_PROJECTS="llvm;clang;mlir;polly" -DCMAKE_BUILD_TYPE=${{ matrix.mlir-build }} -DCMAKE_C_COMPILER=/bin/clang -DCMAKE_CXX_COMPILER=/bin/clang++ -DCMAKE_ASM_COMPILER=/bin/clang -DCMAKE_CXX_FLAGS="-Wno-c++11-narrowing" #cymbld & disown sleep 1 CYMBL=OFF ninja @@ -89,7 +104,7 @@ jobs: run: | cd build ls ../mlir-build/lib/cmake/clang - cmake ../src/ -GNinja -DMLIR_DIR=`pwd`/../mlir-build/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=`pwd`/../mlir-build/bin/llvm-lit -DClang_DIR=`pwd`/../mlir-build/lib/cmake/clang -DCMAKE_BUILD_TYPE=${{ matrix.polygeist-build }} -DCMAKE_C_COMPILER=${{matrix.compiler}} -DCMAKE_CXX_COMPILER=${{matrix.cxxcompiler}} -DPOLYGEIST_ENABLE_POLYMER=${{ matrix.polymer }} -DPOLYMER_DEP_DIR=$(pwd)/../pluto-build/ + cmake ../src/ -GNinja -DMLIR_DIR=`pwd`/../mlir-build/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=`pwd`/../mlir-build/bin/llvm-lit -DClang_DIR=`pwd`/../mlir-build/lib/cmake/clang -DCMAKE_BUILD_TYPE=${{ matrix.polygeist-build }} -DCMAKE_C_COMPILER=${{matrix.compiler}} -DCMAKE_CXX_COMPILER=${{matrix.cxxcompiler}} -DPOLYGEIST_ENABLE_POLYMER=${{ matrix.polymer }} -DPOLYMER_DEP_DIR=$(pwd)/../pluto-build/ -DPOLYGEIST_POLYMER_ENABLE_PLUTO=${{matrix.pluto}} -DPOLYGEIST_POLYMER_ENABLE_ISL=${{ steps.defineisl.outputs.isl }} - name: test cgeist run: | @@ -99,7 +114,7 @@ jobs: ninja check-cgeist - name: test polymer - if: matrix.polymer == '1' + if: matrix.polymer == '1' && matrix.pluto == '1' run: | cd build ninja diff --git a/CMakeLists.txt b/CMakeLists.txt index 7dd08cf3e003..b48ad6cad751 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ set(POLYGEIST_ENABLE_CUDA 0 CACHE BOOL "Enable CUDA frontend and backend") set(POLYGEIST_ENABLE_ROCM 0 CACHE BOOL "Enable ROCM backend") set(POLYGEIST_ENABLE_POLYMER 0 CACHE BOOL "Enable Polymer") +set(POLYGEIST_POLYMER_ENABLE_ISL 0 CACHE BOOL "Enable Polymer isl") +set(POLYGEIST_POLYMER_ENABLE_PLUTO 0 CACHE BOOL "Enable Polymer pluto") set(POLYMER_DEP_DIR "" CACHE STRING "Pluto build root directory") diff --git a/README.md b/README.md index 8f8b9140eabc..0ac2df0e9f13 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ -## Build instructions +# Build instructions -### Requirements +## Requirements - Working C and C++ toolchains(compiler, linker) - cmake - make or ninja -### 1. Clone Polygeist +## 1. Clone Polygeist ```sh git clone --recursive https://github.com/llvm/Polygeist cd Polygeist ``` -### 2. Install LLVM, MLIR, Clang, and Polygeist +## 2. Install LLVM, MLIR, Clang, and Polygeist -#### Option 1: Using pre-built LLVM, MLIR, and Clang +### Option 1: Using pre-built LLVM, MLIR, and Clang Polygeist can be built by providing paths to a pre-built MLIR and Clang toolchain. -1. Build LLVM, MLIR, and Clang: +#### 1. Build LLVM, MLIR, and Clang: ```sh mkdir llvm-project/build cd llvm-project/build @@ -34,9 +34,11 @@ To enable compilation to cuda add `-DMLIR_ENABLE_CUDA_RUNNER=1` and remove `-DLL To enable the ROCM backend add `-DMLIR_ENABLE_ROCM_RUNNER=1` and remove `-DLLVM_TARGETS_TO_BUILD="host"` from the cmake arguments. (You may need to specify `-DHIP_CLANG_INCLUDE_PATH`, and/or `ROCM_PATH`) +For ISL-enabled polymer, `polly` must be added to the `LLVM_ENABLE_PROJECTS` variable. + For faster compilation we recommend using `-DLLVM_USE_LINKER=lld`. -2. Build Polygeist: +#### 2. Build Polygeist: ```sh mkdir build cd build @@ -52,23 +54,31 @@ ninja check-polygeist-opt && ninja check-cgeist For faster compilation we recommend using `-DPOLYGEIST_USE_LINKER=lld`. - 1. GPU backends +##### GPU backends To enable the CUDA backend add `-DPOLYGEIST_ENABLE_CUDA=1` To enable the ROCM backend add `-DPOLYGEIST_ENABLE_ROCM=1` - 2. Polymer +##### Polymer To enable polymer, add `-DPOLYGEIST_ENABLE_POLYMER=1` +There are two configurations of polymer that can be built - one with Pluto and one with ISL. + +###### Pluto +Add `-DPOLYGEIST_POLYMER_ENABLE_PLUTO=1` This will cause the cmake invokation to pull and build the dependencies for polymer. To specify a custom directory for the dependencies, specify `-DPOLYMER_DEP_DIR=`. The dependencies will be build using the `tools/polymer/build_polymer_deps.sh`. -To run the polymer tests, use `ninja check-polymer`. +To run the polymer pluto tests, use `ninja check-polymer`. + +###### ISL +Add `-DPOLYGEIST_POLYMER_ENABLE_ISL=1` +This requires an `llvm-project` build with `polly` enabled as a subproject. -#### Option 2: Using unified LLVM, MLIR, Clang, and Polygeist build +### Option 2: Using unified LLVM, MLIR, Clang, and Polygeist build Polygeist can also be built as an external LLVM project using [LLVM_EXTERNAL_PROJECTS](https://llvm.org/docs/CMake.html#llvm-related-variables). @@ -90,7 +100,7 @@ ninja check-polygeist-opt && ninja check-cgeist `ninja check-polygeist-opt` runs the tests in `Polygeist/test/polygeist-opt` `ninja check-cgeist` runs the tests in `Polygeist/tools/cgeist/Test` -## Citing Polygeist +# Citing Polygeist If you use Polygeist, please consider citing the relevant publications: diff --git a/include/polygeist/Passes/Passes.h b/include/polygeist/Passes/Passes.h index 95b10c3972ae..fd4ab057da3d 100644 --- a/include/polygeist/Passes/Passes.h +++ b/include/polygeist/Passes/Passes.h @@ -4,6 +4,7 @@ #include "mlir/Conversion/LLVMCommon/LoweringOptions.h" #include "mlir/Dialect/ControlFlow/IR/ControlFlow.h" #include "mlir/Dialect/OpenMP/OpenMPDialect.h" +#include "mlir/Dialect/Vector/IR/VectorOps.h" #include "mlir/Pass/Pass.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "polygeist/Dialect.h" diff --git a/include/polygeist/Passes/Passes.td b/include/polygeist/Passes/Passes.td index 98c1b89739c6..70081f5502a0 100644 --- a/include/polygeist/Passes/Passes.td +++ b/include/polygeist/Passes/Passes.td @@ -9,8 +9,15 @@ def PolyhedralOpt : Pass<"polyhedral-opt"> { let constructor = "mlir::polygeist::createPolyhedralOptPass()"; let dependentDialects = [ "scf::SCFDialect", + "func::FuncDialect", "arith::ArithDialect", "memref::MemRefDialect", + "affine::AffineDialect", + // This vector dialect is needed because lower-affine needs it. We launch + // a pass manager to use lower-affine from this pass itself, and that + // causes the multi-threaded mlir context to try to load the vector dialect + // which is unsupported. Instead we preload it here. + "vector::VectorDialect", ]; } diff --git a/lib/polygeist/Passes/PolyhedralOpt.cpp b/lib/polygeist/Passes/PolyhedralOpt.cpp index a82cb7c9929b..e8f69127b4bd 100644 --- a/lib/polygeist/Passes/PolyhedralOpt.cpp +++ b/lib/polygeist/Passes/PolyhedralOpt.cpp @@ -1,3 +1,4 @@ +#include "mlir/Conversion/AffineToStandard/AffineToStandard.h" #ifdef POLYGEIST_ENABLE_POLYMER #include "mlir/IR/Visitors.h" @@ -38,17 +39,28 @@ using namespace mlir; using namespace polygeist; +static llvm::cl::opt + UsePolyhedralOptimizerCl("use-polyhedral-optimizer", + llvm::cl::init("pluto"), + llvm::cl::desc("pluto or islexternal")); + namespace { #define POLYGEIST_OUTLINED_AFFINE_ATTR "polygeist.outlined_affine" -static SmallVector findAffineRegions(Operation *root) { - SmallVector affineRegions; +struct Scop { + Operation *begin; + Operation *end; +}; + +static SmallVector findScops(Operation *root) { + SmallVector affineLoops; + SmallVector scops; root->walk([&](Operation *loop) { if (!(isa(loop) || isa(loop))) return; - if (!affineRegions.empty() && affineRegions.back()->isAncestor(loop)) + if (!affineLoops.empty() && affineLoops.back()->isAncestor(loop)) return; auto result = loop->walk([&](Operation *op) { @@ -71,22 +83,34 @@ static SmallVector findAffineRegions(Operation *root) { if (!result.wasInterrupted()) { LLVM_DEBUG(llvm::dbgs() << DEBUG_LABEL << "Found affine region\n" << *loop << "\n"); - affineRegions.push_back(loop); + affineLoops.push_back(loop); } }); - return affineRegions; + size_t size = affineLoops.size(); + for (size_t i = 0; i < size; i++) { + Operation *loop = affineLoops[i]; + Scop scop = {.begin = loop, .end = loop->getNextNode()}; + while (i + 1 < size && scop.end == affineLoops[i + 1]) + scop.end = affineLoops[++i]->getNextNode(); + scops.push_back(scop); + } + return scops; } static FailureOr outlineOp(RewriterBase &rewriter, Location loc, - Operation *op, StringRef funcName, + Scop scop, StringRef funcName, func::CallOp *callOp) { assert(!funcName.empty() && "funcName cannot be empty"); OpBuilder::InsertionGuard g(rewriter); - rewriter.setInsertionPoint(op); + rewriter.setInsertionPoint(scop.begin); auto executeOp = rewriter.create(loc, TypeRange()); rewriter.createBlock(&executeOp.getRegion()); - rewriter.clone(*op); + auto cur = scop.begin; + while (cur != scop.end) { + rewriter.clone(*cur); + cur = cur->getNextNode(); + } rewriter.create(loc); auto ret = outlineSingleBlockRegion(rewriter, loc, executeOp.getRegion(), funcName, callOp); @@ -96,15 +120,20 @@ static FailureOr outlineOp(RewriterBase &rewriter, Location loc, } (*ret)->setAttr(POLYGEIST_OUTLINED_AFFINE_ATTR, rewriter.getUnitAttr()); rewriter.eraseOp(executeOp.getRegion().front().getTerminator()); - rewriter.inlineBlockBefore(&executeOp.getRegion().front(), op); + rewriter.inlineBlockBefore(&executeOp.getRegion().front(), scop.begin); rewriter.eraseOp(executeOp); - rewriter.eraseOp(op); + cur = scop.begin; + while (cur != scop.end) { + auto tmp = cur; + cur = cur->getNextNode(); + rewriter.eraseOp(tmp); + } return ret; } static SmallVector> -outlineAffineRegions(Operation *root) { - auto affineRegions = findAffineRegions(root); +outlineScops(Operation *root) { + auto scops = findScops(root); auto m = isa(root) ? cast(root) : root->getParentOfType(); auto loc = root->getLoc(); @@ -119,9 +148,9 @@ outlineAffineRegions(Operation *root) { }; SmallVector> funcs; IRRewriter rewriter(root->getContext()); - for (Operation *op : affineRegions) { + for (Scop scop : scops) { func::CallOp callOp; - auto ret = outlineOp(rewriter, loc, op, getName(), &callOp); + auto ret = outlineOp(rewriter, loc, scop, getName(), &callOp); if (failed(ret)) { llvm::errs() << "Outlining affine region failed\n" << *root; abort(); @@ -179,11 +208,12 @@ void PolyhedralOptPass::runOnOperation() { ModuleOp m = cast(SymbolTable::getNearestSymbolTable(op)); auto &context = *op->getContext(); - auto funcOps = outlineAffineRegions(op); + auto funcOps = outlineScops(op); mlir::PassManager preTransformPm(&context); preTransformPm.addPass(createCanonicalizerPass()); - AlwaysInlinerInterface interface(&getContext()); + SmallVector toInline; + IRRewriter b(&context); for (auto &pair : funcOps) { mlir::func::FuncOp f = pair.first; @@ -205,18 +235,37 @@ void PolyhedralOptPass::runOnOperation() { return; } mlir::func::FuncOp g = nullptr; - if ((g = polymer::plutoTransform(f, b, ""))) { + if (UsePolyhedralOptimizerCl == "islexternal") { + g = polymer::islexternalTransform(f, b); + } else if (UsePolyhedralOptimizerCl == "pluto") { + g = polymer::plutoTransform(f, b, ""); + } + if (g) { g.setPublic(); g->setAttrs(f->getAttrs()); g.setName(f.getName()); f.erase(); } - if (g && /*options.parallelize=*/true) { - polymer::plutoParallelize(g, b); - } - inlineAll(call); + // if (g && /*options.parallelize=*/true) { + // polymer::plutoParallelize(g, b); + // } + toInline.push_back(call); } + + mlir::PassManager lowerAffine(&context); + lowerAffine.addPass(createLowerAffinePass()); + lowerAffine.addPass(createCanonicalizerPass()); + + // Conversion from ISL emits scf so we need to lower the statements before + // inlining them + if (UsePolyhedralOptimizerCl == "islexternal" && failed(lowerAffine.run(m))) { + signalPassFailure(); + return; + } + + for (auto call : toInline) + inlineAll(call); cleanupTempFuncs(m); } diff --git a/test/lit.cfg.py b/test/lit.cfg.py index 724ad32039dc..14be1508b64b 100644 --- a/test/lit.cfg.py +++ b/test/lit.cfg.py @@ -39,6 +39,8 @@ config.substitutions.append(('%polygeist_src_root', config.polygeist_src_root)) config.substitutions.append(('%polygeist_obj_root', config.polygeist_obj_root)) config.substitutions.append(('%polymer_enabled', config.polymer_enabled)) +config.substitutions.append(('%polymer_pluto_enabled', config.polymer_pluto_enabled)) +config.substitutions.append(('%polymer_isl_enabled', config.polymer_isl_enabled)) llvm_config.with_system_environment(['HOME', 'INCLUDE', 'LIB', 'TMP', 'TEMP']) @@ -48,6 +50,8 @@ # subdirectories contain auxiliary inputs for various tests in their parent # directories. config.excludes = ['Inputs', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt'] +if config.polymer_isl_enabled == "0" or config.polymer_enabled == "0": + config.excludes += ['isl'] # test_source_root: The root path where tests are located. config.test_source_root = os.path.dirname(__file__) diff --git a/test/lit.site.cfg.py.in b/test/lit.site.cfg.py.in index 052f0b8985b3..e37bf01184f5 100644 --- a/test/lit.site.cfg.py.in +++ b/test/lit.site.cfg.py.in @@ -37,6 +37,8 @@ config.polygeist_tools_dir = "@POLYGEIST_TOOLS_DIR@" config.polygeist_shlib_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@" config.verilator_path = "@VERILATOR_PATH@" config.polymer_enabled = "@POLYGEIST_ENABLE_POLYMER@" +config.polymer_pluto_enabled = "@POLYGEIST_POLYMER_ENABLE_PLUTO@" +config.polymer_isl_enabled = "@POLYGEIST_POLYMER_ENABLE_ISL@" # Support substitution of the tools_dir with user parameters. This is # used when we can't determine the tool dir at configuration time. diff --git a/test/polygeist-opt/affine-opt.mlir b/test/polygeist-opt/affine-opt.mlir index 59677272ceeb..d0861d404991 100644 --- a/test/polygeist-opt/affine-opt.mlir +++ b/test/polygeist-opt/affine-opt.mlir @@ -1,4 +1,4 @@ -// RUN: if [ %polymer_enabled == 1 ]; then polygeist-opt --polyhedral-opt %s 2>&1 | FileCheck %s; fi +// RUN: if [ %polymer_pluto_enabled == 1 ]; then polygeist-opt --polyhedral-opt %s 2>&1 | FileCheck %s; fi // CHECK: // CHECK: diff --git a/test/polygeist-opt/affine-opt2.mlir b/test/polygeist-opt/affine-opt2.mlir index 6b1436097bc8..82667bca6074 100644 --- a/test/polygeist-opt/affine-opt2.mlir +++ b/test/polygeist-opt/affine-opt2.mlir @@ -1,5 +1,5 @@ // XFAIL: * -// RUN: polygeist-opt --polyhedral-opt %s | FileCheck %s +// RUN: [ "%polymer_enabled" == 0 ] && false || polygeist-opt --polyhedral-opt %s | FileCheck %s #set = affine_set<(d0, d1, d2, d3)[s0, s1] : (-d0 - d2 * 8 + s0 - 1 >= 0, -d1 - d3 * 32 + s1 - 1 >= 0)> module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<4xi32>>, #dlti.dl_entry, dense<64> : vector<4xi32>>, #dlti.dl_entry, dense<32> : vector<4xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry, dense<32> : vector<4xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry<"dlti.stack_alignment", 128 : i32>, #dlti.dl_entry<"dlti.endianness", "little">>, llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", llvm.target_triple = "x86_64-unknown-linux-gnu", polygeist.gpu_module.llvm.data_layout = "e-i64:64-i128:128-v16:16-v32:32-n16:32:64", polygeist.gpu_module.llvm.target_triple = "nvptx64-nvidia-cuda", "polygeist.target-cpu" = "x86-64", "polygeist.target-features" = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87", "polygeist.tune-cpu" = "generic"} { diff --git a/test/polygeist-opt/isl/const_for_if_then_else.mlir b/test/polygeist-opt/isl/const_for_if_then_else.mlir new file mode 100644 index 000000000000..b26647071e2f --- /dev/null +++ b/test/polygeist-opt/isl/const_for_if_then_else.mlir @@ -0,0 +1,48 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[] : (-d0 + 50 -1 >= 0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref, + %S: index, + %N: index) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + + affine.for %i = 0 to 100 { + affine.if #set1(%i)[] { + %4 = affine.load %B[%i] : memref + affine.store %4, %C[%i] : memref + } else { + %4 = affine.load %A[%i] : memref + affine.store %4, %C[%i] : memref + } + affine.store %beta, %C[%i] : memref + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "{ S1[i0] : 50 <= i0 <= 99; S2[i0] : 0 <= i0 <= 99; S0[i0] : 0 <= i0 <= 49 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: - "{ [i0] -> A1[o0] : o0 = i0 }" +// ISL_OUT: writes: +// ISL_OUT: - "{ [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: - S1: +// ISL_OUT: reads: +// ISL_OUT: - "{ [i0] -> A3[o0] : o0 = i0 }" +// ISL_OUT: writes: +// ISL_OUT: - "{ [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: - S2: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "{ [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: { domain: "{ S1[i0] : 50 <= i0 <= 99; S2[i0] : 0 <= i0 <= 99; S0[i0] : 0 <= i0 <= 49 }", child: { schedule: "L0[{ S1[i0] -> [(i0)]; S2[i0] -> [(i0)]; S0[i0] -> [(i0)] }]", child: { sequence: [ { filter: "{ S0[i0] }" }, { filter: "{ S1[i0] }" }, { filter: "{ S2[i0] }" } ] } } } diff --git a/test/polygeist-opt/isl/for_if_then_else.mlir b/test/polygeist-opt/isl/for_if_then_else.mlir new file mode 100644 index 000000000000..0e8fff37733b --- /dev/null +++ b/test/polygeist-opt/isl/for_if_then_else.mlir @@ -0,0 +1,43 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[s0] : (-d0 + s0 -1 >= 0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref, + %S: index, + %N: index) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + + affine.for %i = 0 to #map()[%N] { + affine.if #set1(%i)[%S] { + %4 = affine.load %B[%i] : memref + affine.store %4, %C[%i] : memref + } else { + %4 = affine.load %A[%i] : memref + affine.store %4, %C[%i] : memref + } + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0, P1] -> { S1[i0] : i0 >= P0 and 0 <= i0 < P1; S0[i0] : 0 <= i0 < P1 and i0 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A1[o0] : o0 = i0 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: - S1: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A3[o0] : o0 = i0 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: { domain: "[P0, P1] -> { S1[i0] : i0 >= P0 and 0 <= i0 < P1; S0[i0] : 0 <= i0 < P1 and i0 < P0 }", child: { schedule: "[P0, P1] -> L0[{ S1[i0] -> [(i0)]; S0[i0] -> [(i0)] }]", child: { sequence: [ { filter: "[P0, P1] -> { S0[i0] }" }, { filter: "[P0, P1] -> { S1[i0] }" } ] } } } diff --git a/test/polygeist-opt/isl/for_if_then_else_store.mlir b/test/polygeist-opt/isl/for_if_then_else_store.mlir new file mode 100644 index 000000000000..17df67f13820 --- /dev/null +++ b/test/polygeist-opt/isl/for_if_then_else_store.mlir @@ -0,0 +1,48 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[s0] : (-d0 + s0 -1 >= 0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref, + %S: index, + %N: index) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + + affine.for %i = 0 to #map()[%N] { + affine.if #set1(%i)[%S] { + %4 = affine.load %B[%i] : memref + affine.store %4, %C[%i] : memref + } else { + %4 = affine.load %A[%i] : memref + affine.store %4, %C[%i] : memref + } + affine.store %beta, %C[%i] : memref + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0, P1] -> { S1[i0] : i0 >= P0 and 0 <= i0 < P1; S2[i0] : 0 <= i0 < P1; S0[i0] : 0 <= i0 < P1 and i0 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A1[o0] : o0 = i0 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: - S1: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A3[o0] : o0 = i0 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: - S2: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: { domain: "[P0, P1] -> { S1[i0] : i0 >= P0 and 0 <= i0 < P1; S2[i0] : 0 <= i0 < P1; S0[i0] : 0 <= i0 < P1 and i0 < P0 }", child: { schedule: "[P0, P1] -> L0[{ S1[i0] -> [(i0)]; S2[i0] -> [(i0)]; S0[i0] -> [(i0)] }]", child: { sequence: [ { filter: "[P0, P1] -> { S0[i0] }" }, { filter: "[P0, P1] -> { S1[i0] }" }, { filter: "[P0, P1] -> { S2[i0] }" } ] } } } diff --git a/test/polygeist-opt/isl/for_step.mlir b/test/polygeist-opt/isl/for_step.mlir new file mode 100644 index 000000000000..83102de9baf9 --- /dev/null +++ b/test/polygeist-opt/isl/for_step.mlir @@ -0,0 +1,28 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[s0] : (-d0 + s0 -1 >= 0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref, + %S: index, + %N: index) { + affine.for %i = 0 to #map()[%N] step 2 { + affine.store %beta, %C[%i] : memref + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0] -> { S0[i0] : (i0) mod 2 = 0 and 0 <= i0 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0] -> A1[o0] : o0 = i0 }" +// ISL_OUT: { domain: "[P0] -> { S0[i0] : (i0) mod 2 = 0 and 0 <= i0 < P0 }", child: { schedule: "[P0] -> L0[{ S0[i0] -> [(i0)] }]" } } diff --git a/test/polygeist-opt/isl/matmul.mlir b/test/polygeist-opt/isl/matmul.mlir new file mode 100644 index 000000000000..2590ff024b00 --- /dev/null +++ b/test/polygeist-opt/isl/matmul.mlir @@ -0,0 +1,54 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed +#map = affine_map<()[s0] -> (s0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + %NI = memref.dim %C, %c0 : memref + %NJ = memref.dim %C, %c1 : memref + %NK = memref.dim %A, %c1 : memref + + affine.for %i = 0 to #map()[%NI] { + affine.for %j = 0 to #map()[%NJ] { + %0 = affine.load %C[%i, %j] : memref + %1 = arith.mulf %0, %beta : f32 + affine.store %1, %C[%i, %j] : memref + } + + affine.for %j = 0 to #map()[%NJ] { + affine.for %k = 0 to #map()[%NK] { + %2 = affine.load %A[%i, %k] : memref + %3 = arith.mulf %alpha, %2 : f32 + %4 = affine.load %B[%k, %j] : memref + %5 = arith.mulf %3, %4 : f32 + %6 = affine.load %C[%i, %j] : memref + %7 = arith.addf %6, %5 : f32 + affine.store %7, %C[%i, %j] : memref + } + } + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0, P1, P2] -> { S0[i0, i1] : 0 <= i0 < P2 and 0 <= i1 < P0; S1[i0, i1, i2] : 0 <= i0 < P2 and 0 <= i1 < P0 and 0 <= i2 < P1 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: - S1: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1, i2] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1, i2] -> A2[o0, o1] : o0 = i0 and o1 = i2 }" +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1, i2] -> A3[o0, o1] : o0 = i2 and o1 = i1 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1, i2] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: { domain: "[P0, P1, P2] -> { S0[i0, i1] : 0 <= i0 < P2 and 0 <= i1 < P0; S1[i0, i1, i2] : 0 <= i0 < P2 and 0 <= i1 < P0 and 0 <= i2 < P1 }", child: { schedule: "[P0, P1, P2] -> L3[{ S0[i0, i1] -> [(i0)]; S1[i0, i1, i2] -> [(i0)] }]", child: { sequence: [ { filter: "[P0, P1, P2] -> { S0[i0, i1] }", child: { schedule: "[P0, P1, P2] -> L0[{ S0[i0, i1] -> [(i1)] }]" } }, { filter: "[P0, P1, P2] -> { S1[i0, i1, i2] }", child: { schedule: "[P0, P1, P2] -> L2[{ S1[i0, i1, i2] -> [(i1)] }]", child: { schedule: "[P0, P1, P2] -> L1[{ S1[i0, i1, i2] -> [(i2)] }]" } } } ] } } } diff --git a/test/polygeist-opt/isl/matmul_if.mlir b/test/polygeist-opt/isl/matmul_if.mlir new file mode 100644 index 000000000000..0e8fff37733b --- /dev/null +++ b/test/polygeist-opt/isl/matmul_if.mlir @@ -0,0 +1,43 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[s0] : (-d0 + s0 -1 >= 0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref, + %S: index, + %N: index) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + + affine.for %i = 0 to #map()[%N] { + affine.if #set1(%i)[%S] { + %4 = affine.load %B[%i] : memref + affine.store %4, %C[%i] : memref + } else { + %4 = affine.load %A[%i] : memref + affine.store %4, %C[%i] : memref + } + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0, P1] -> { S1[i0] : i0 >= P0 and 0 <= i0 < P1; S0[i0] : 0 <= i0 < P1 and i0 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A1[o0] : o0 = i0 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: - S1: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A3[o0] : o0 = i0 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = i0 }" +// ISL_OUT: { domain: "[P0, P1] -> { S1[i0] : i0 >= P0 and 0 <= i0 < P1; S0[i0] : 0 <= i0 < P1 and i0 < P0 }", child: { schedule: "[P0, P1] -> L0[{ S1[i0] -> [(i0)]; S0[i0] -> [(i0)] }]", child: { sequence: [ { filter: "[P0, P1] -> { S0[i0] }" }, { filter: "[P0, P1] -> { S1[i0] }" } ] } } } diff --git a/test/polygeist-opt/isl/matmul_par.mlir b/test/polygeist-opt/isl/matmul_par.mlir new file mode 100644 index 000000000000..b8160a538377 --- /dev/null +++ b/test/polygeist-opt/isl/matmul_par.mlir @@ -0,0 +1,54 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed +#map = affine_map<()[s0] -> (s0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + %NI = memref.dim %C, %c0 : memref + %NJ = memref.dim %C, %c1 : memref + %NK = memref.dim %A, %c1 : memref + + affine.for %i = 0 to #map()[%NI] { + affine.for %j = 0 to #map()[%NJ] { + %0 = affine.load %C[%i, %j] : memref + %1 = arith.mulf %0, %beta : f32 + affine.store %1, %C[%i, %j] : memref + } + + affine.parallel (%j) = (0) to (%NJ) { + affine.for %k = 0 to #map()[%NK] { + %2 = affine.load %A[%i, %k] : memref + %3 = arith.mulf %alpha, %2 : f32 + %4 = affine.load %B[%k, %j] : memref + %5 = arith.mulf %3, %4 : f32 + %6 = affine.load %C[%i, %j] : memref + %7 = arith.addf %6, %5 : f32 + affine.store %7, %C[%i, %j] : memref + } + } + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0, P1, P2] -> { S0[i0, i1] : 0 <= i0 < P2 and 0 <= i1 < P0; S1[i0, i1, i2] : 0 <= i0 < P2 and 0 <= i1 < P0 and 0 <= i2 < P1 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: - S1: +// ISL_OUT: reads: +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1, i2] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1, i2] -> A2[o0, o1] : o0 = i0 and o1 = i2 }" +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1, i2] -> A3[o0, o1] : o0 = i2 and o1 = i1 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0, P1, P2] -> { [i0, i1, i2] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: { domain: "[P0, P1, P2] -> { S0[i0, i1] : 0 <= i0 < P2 and 0 <= i1 < P0; S1[i0, i1, i2] : 0 <= i0 < P2 and 0 <= i1 < P0 and 0 <= i2 < P1 }", child: { schedule: "[P0, P1, P2] -> L3[{ S0[i0, i1] -> [(i0)]; S1[i0, i1, i2] -> [(i0)] }]", child: { sequence: [ { filter: "[P0, P1, P2] -> { S0[i0, i1] }", child: { schedule: "[P0, P1, P2] -> L0[{ S0[i0, i1] -> [(i1)] }]" } }, { filter: "[P0, P1, P2] -> { S1[i0, i1, i2] }", child: { mark: "parallel", child: { schedule: "[P0, P1, P2] -> L2[{ S1[i0, i1, i2] -> [(i1)] }]", permutable: 1, child: { schedule: "[P0, P1, P2] -> L1[{ S1[i0, i1, i2] -> [(i2)] }]" } } } } ] } } } diff --git a/test/polygeist-opt/isl/multi_scop.mlir b/test/polygeist-opt/isl/multi_scop.mlir new file mode 100644 index 000000000000..54e89b00325b --- /dev/null +++ b/test/polygeist-opt/isl/multi_scop.mlir @@ -0,0 +1,68 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-import-schedules=%t/schedules $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[s0] : (-d0 + s0 -1 >= 0)> +module { + func.func private @foo() + func.func @f1(%beta: f32, + %C: memref, + %N: index) { + affine.for %i = 0 to #map()[%N] step 1 { + affine.store %beta, %C[%i + 1] : memref + } + func.call @foo() : () -> () + // TODO these should be the same scop + affine.for %i = 0 to #map()[%N] step 2 { + affine.store %beta, %C[%i + 2] : memref + } + affine.for %i = 0 to #map()[%N] step 3 { + affine.store %beta, %C[%i + 3] : memref + } + return + } + func.func @f2(%beta: f32, + %C: memref, + %N: index) { + affine.for %i = 0 to #map()[%N] step 4{ + affine.store %beta, %C[%i + 4] : memref + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0] -> { S0[i0] : 0 <= i0 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0] -> A1[o0] : o0 = 1 + i0 }" +// ISL_OUT: domain: "[P0] -> { S1[i0] : (i0) mod 2 = 0 and 0 <= i0 < P0; S2[i0] : (i0) mod 3 = 0 and 0 <= i0 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S1: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0] -> A1[o0] : o0 = 2 + i0 }" +// ISL_OUT: - S2: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0] -> A1[o0] : o0 = 3 + i0 }" +// ISL_OUT: domain: "[P0] -> { S3[i0] : (i0) mod 4 = 0 and 0 <= i0 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S3: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0] -> A1[o0] : o0 = 4 + i0 }" +// ISL_OUT: { domain: "[P0] -> { S0[i0] : 0 <= i0 < P0 }", child: { schedule: "[P0] -> L0[{ S0[i0] -> [(i0)] }]" } } +// ISL_OUT: { domain: "[P0] -> { S1[i0] : (i0) mod 2 = 0 and 0 <= i0 < P0; S2[i0] : (i0) mod 3 = 0 and 0 <= i0 < P0 }", child: { sequence: [ { filter: "[P0] -> { S1[i0] }", child: { schedule: "[P0] -> L0[{ S1[i0] -> [(i0)] }]" } }, { filter: "[P0] -> { S2[i0] }", child: { schedule: "[P0] -> L1[{ S2[i0] -> [(i0)] }]" } } ] } } +// ISL_OUT: { domain: "[P0] -> { S3[i0] : (i0) mod 4 = 0 and 0 <= i0 < P0 }", child: { schedule: "[P0] -> L0[{ S3[i0] -> [(i0)] }]" } } + diff --git a/test/polygeist-opt/isl/onlyconst.mlir b/test/polygeist-opt/isl/onlyconst.mlir new file mode 100644 index 000000000000..d244511ab040 --- /dev/null +++ b/test/polygeist-opt/isl/onlyconst.mlir @@ -0,0 +1,30 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[s0] : (-d0 + s0 -1 >= 0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref, + %S: index, + %N: index) { + affine.for %i = 0 to 10 { + affine.for %j = 0 to 20 { + affine.store %beta, %C[0] : memref + } + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "{ S0[i0, i1] : 0 <= i0 <= 9 and 0 <= i1 <= 19 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "{ [i0, i1] -> A1[o0] : o0 = 0 }" +// ISL_OUT: { domain: "{ S0[i0, i1] : 0 <= i0 <= 9 and 0 <= i1 <= 19 }", child: { schedule: "L1[{ S0[i0, i1] -> [(i0)] }]", child: { schedule: "L0[{ S0[i0, i1] -> [(i1)] }]" } } } diff --git a/test/polygeist-opt/isl/reg2mem.mlir b/test/polygeist-opt/isl/reg2mem.mlir new file mode 100644 index 000000000000..af17ab8c8b1f --- /dev/null +++ b/test/polygeist-opt/isl/reg2mem.mlir @@ -0,0 +1,42 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[s0] : (-d0 + s0 -1 >= 0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref, + %S: index, + %N: index) { + affine.for %i = 0 to %N { + %4 = affine.load %A[%i] : memref + affine.if #set1(%i)[%S] { + affine.store %4, %B[%i] : memref + } + affine.store %4, %C[%i] : memref + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0, P1] -> { S1[i0] : 0 <= i0 < P1 and i0 < P0; S2[i0] : 0 <= i0 < P1; S0[i0] : 0 <= i0 < P1 }" +// ISL_OUT-NEXT: accesses: +// ISL_OUT-NEXT: - S0: +// ISL_OUT-NEXT: reads: +// ISL_OUT-NEXT: - "[P0, P1] -> { [i0] -> A1[o0] : o0 = i0 }" +// ISL_OUT-NEXT: writes: +// ISL_OUT-NEXT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = 0 }" +// ISL_OUT-NEXT: - S1: +// ISL_OUT-NEXT: reads: +// ISL_OUT-NEXT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = 0 }" +// ISL_OUT-NEXT: writes: +// ISL_OUT-NEXT: - "[P0, P1] -> { [i0] -> A3[o0] : o0 = i0 }" +// ISL_OUT-NEXT: - S2: +// ISL_OUT-NEXT: reads: +// ISL_OUT-NEXT: - "[P0, P1] -> { [i0] -> A2[o0] : o0 = 0 }" +// ISL_OUT-NEXT: writes: +// ISL_OUT-NEXT: - "[P0, P1] -> { [i0] -> A4[o0] : o0 = i0 }" diff --git a/test/polygeist-opt/isl/test1.mlir b/test/polygeist-opt/isl/test1.mlir new file mode 100644 index 000000000000..9b7eb7973114 --- /dev/null +++ b/test/polygeist-opt/isl/test1.mlir @@ -0,0 +1,28 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed +module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<4xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry, dense<32> : vector<4xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry, dense<64> : vector<4xi32>>, #dlti.dl_entry, dense<32> : vector<4xi32>>, #dlti.dl_entry<"dlti.stack_alignment", 128 : i32>, #dlti.dl_entry<"dlti.endianness", "little">>, llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", llvm.target_triple = "x86_64-unknown-linux-gnu", "polygeist.target-cpu" = "x86-64", "polygeist.target-features" = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87", "polygeist.tune-cpu" = "generic"} { + func.func @f(%arg0: i64, %arg1: memref) attributes {llvm.linkage = #llvm.linkage} { + %0 = arith.index_cast %arg0 : i64 to index + affine.for %arg2 = 1 to %0 { + affine.for %arg3 = 0 to %0 { + %1 = affine.load %arg1[%arg2, %arg3] : memref + %2 = affine.load %arg1[%arg2 - 1, %arg3] : memref + %3 = arith.addf %1, %2 : f64 + affine.store %3, %arg1[%arg2, %arg3] : memref + } + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0] -> { S0[i0, i1] : 0 < i0 < P0 and 0 <= i1 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: - "[P0] -> { [i0, i1] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: - "[P0] -> { [i0, i1] -> A1[o0, o1] : o0 = -1 + i0 and o1 = i1 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0, i1] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: { domain: "[P0] -> { S0[i0, i1] : 0 < i0 < P0 and 0 <= i1 < P0 }", child: { schedule: "[P0] -> L1[{ S0[i0, i1] -> [(i0)] }]", child: { schedule: "[P0] -> L0[{ S0[i0, i1] -> [(i1)] }]" } } } diff --git a/test/polygeist-opt/isl/test2.mlir b/test/polygeist-opt/isl/test2.mlir new file mode 100644 index 000000000000..01127313bd1b --- /dev/null +++ b/test/polygeist-opt/isl/test2.mlir @@ -0,0 +1,28 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed +module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<4xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry, dense<32> : vector<4xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry : vector<2xi32>>, #dlti.dl_entry, dense<64> : vector<4xi32>>, #dlti.dl_entry, dense<32> : vector<4xi32>>, #dlti.dl_entry<"dlti.stack_alignment", 128 : i32>, #dlti.dl_entry<"dlti.endianness", "little">>, llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", llvm.target_triple = "x86_64-unknown-linux-gnu", "polygeist.target-cpu" = "x86-64", "polygeist.target-features" = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87", "polygeist.tune-cpu" = "generic"} { + func.func @f(%arg0: i64, %arg1: memref) attributes {llvm.linkage = #llvm.linkage} { + %0 = arith.index_cast %arg0 : i64 to index + affine.for %arg2 = 1 to %0 { + affine.for %arg3 = 0 to %0 { + %1 = affine.load %arg1[%arg2, %arg3] : memref + %2 = affine.load %arg1[%arg2 - 1, %arg3] : memref + %3 = arith.addf %1, %2 : f64 + affine.store %3, %arg1[%arg2, %arg3 + %0 + 42] : memref + } + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0] -> { S0[i0, i1] : 0 < i0 < P0 and 0 <= i1 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: - "[P0] -> { [i0, i1] -> A1[o0, o1] : o0 = i0 and o1 = i1 }" +// ISL_OUT: - "[P0] -> { [i0, i1] -> A1[o0, o1] : o0 = -1 + i0 and o1 = i1 }" +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0, i1] -> A1[o0, o1] : o0 = i0 and o1 = 42 + P0 + i1 }" +// ISL_OUT: { domain: "[P0] -> { S0[i0, i1] : 0 < i0 < P0 and 0 <= i1 < P0 }", child: { schedule: "[P0] -> L1[{ S0[i0, i1] -> [(i0)] }]", child: { schedule: "[P0] -> L0[{ S0[i0, i1] -> [(i1)] }]" } } } diff --git a/test/polygeist-opt/isl/veccopy.mlir b/test/polygeist-opt/isl/veccopy.mlir new file mode 100644 index 000000000000..b1a8244b2a1f --- /dev/null +++ b/test/polygeist-opt/isl/veccopy.mlir @@ -0,0 +1,28 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[s0] : (-d0 + s0 -1 >= 0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref, + %S: index, + %N: index) { + affine.for %i = 0 to #map()[%N] { + affine.store %beta, %C[%i] : memref + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0] -> { S0[i0] : 0 <= i0 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0] -> A1[o0] : o0 = i0 }" +// ISL_OUT: { domain: "[P0] -> { S0[i0] : 0 <= i0 < P0 }", child: { schedule: "[P0] -> L0[{ S0[i0] -> [(i0)] }]" } } diff --git a/test/polygeist-opt/isl/vecinit.mlir b/test/polygeist-opt/isl/vecinit.mlir new file mode 100644 index 000000000000..af95e270fc01 --- /dev/null +++ b/test/polygeist-opt/isl/vecinit.mlir @@ -0,0 +1,44 @@ +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal $ISL_OPT_PLACEHOLDER %s 2>&1 | FileCheck %s +// CHECK-NOT: isl_ctx not freed + +#map = affine_map<()[s0] -> (s0)> +#set1 = affine_set<(d0)[s0] : (-d0 + s0 -1 >= 0)> +module { + func.func @gemm(%alpha: f32, %beta: f32, + %C: memref, + %A: memref, + %B: memref, + %S: index, + %N: index) { + affine.for %i = 0 to #map()[%N] { + affine.store %beta, %C[%i] : memref + } + affine.for %i = 0 to #map()[%N] step 7 { + affine.store %beta, %A[%i + 1, - 687 * %i + 234 * %N + 42] : memref + } + affine.for %i = 0 to #map()[%N] step 5 { + affine.for %j = 0 to #map()[%N] { + affine.store %beta, %B[%i + %j + 43] : memref + } + } + return + } +} +// RUN: mkdir -p %t/schedules +// RUN: mkdir -p %t/accesses +// RUN: polygeist-opt --polyhedral-opt --use-polyhedral-optimizer=islexternal --islexternal-dump-schedules=%t/schedules --islexternal-dump-accesses=%t/accesses $ISL_OPT_PLACEHOLDER %s && find %t/schedules/ %t/accesses/ -type f -print0 | sort -z | xargs -0r cat | FileCheck --check-prefix=ISL_OUT %s +// ISL_OUT: domain: "[P0] -> { S2[i0, i1] : (i0) mod 5 = 0 and 0 <= i0 < P0 and 0 <= i1 < P0; S1[i0] : (i0) mod 7 = 0 and 0 <= i0 < P0; S0[i0] : 0 <= i0 < P0 }" +// ISL_OUT: accesses: +// ISL_OUT: - S0: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0] -> A1[o0] : o0 = i0 }" +// ISL_OUT: - S1: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0] -> A2[o0, o1] : o0 = 1 + i0 and o1 = 42 + 234P0 - 687i0 }" +// ISL_OUT: - S2: +// ISL_OUT: reads: +// ISL_OUT: writes: +// ISL_OUT: - "[P0] -> { [i0, i1] -> A3[o0] : o0 = 43 + i0 + i1 }" +// ISL_OUT: { domain: "[P0] -> { S2[i0, i1] : (i0) mod 5 = 0 and 0 <= i0 < P0 and 0 <= i1 < P0; S1[i0] : (i0) mod 7 = 0 and 0 <= i0 < P0; S0[i0] : 0 <= i0 < P0 }", child: { sequence: [ { filter: "[P0] -> { S0[i0] }", child: { schedule: "[P0] -> L0[{ S0[i0] -> [(i0)] }]" } }, { filter: "[P0] -> { S1[i0] }", child: { schedule: "[P0] -> L1[{ S1[i0] -> [(i0)] }]" } }, { filter: "[P0] -> { S2[i0, i1] }", child: { schedule: "[P0] -> L3[{ S2[i0, i1] -> [(i0)] }]", child: { schedule: "[P0] -> L2[{ S2[i0, i1] -> [(i1)] }]" } } } ] } } \ No newline at end of file diff --git a/tools/cgeist/Test/CUDA/polybench-cuda/gemm/gemm.cu b/tools/cgeist/Test/CUDA/polybench-cuda/gemm/gemm.cu index 9058a2219a63..0870ade4296c 100644 --- a/tools/cgeist/Test/CUDA/polybench-cuda/gemm/gemm.cu +++ b/tools/cgeist/Test/CUDA/polybench-cuda/gemm/gemm.cu @@ -1,6 +1,6 @@ // clang-format off // RUN: cgeist %s %stdinclude %cudaopts -O3 -o %s.execm && %s.execm 1 10 10 10 -// RUN: cgeist %s %stdinclude %polymer_cudaopts -O3 -o %s.execm && %s.execm 1 10 10 10 +// RUN: [ "%polymer_pluto_enabled" == "" ] || cgeist %s %stdinclude %polymer_pluto_cudaopts -O3 -o %s.execm && %s.execm 1 10 10 10 // clang-format on /** * gemm.c: This file is part of the PolyBench/C 3.2 test suite. diff --git a/tools/cgeist/Test/CUDA/polybench-cuda/lu/lu.cu b/tools/cgeist/Test/CUDA/polybench-cuda/lu/lu.cu index 6edc03b28397..9a35a6f6020e 100644 --- a/tools/cgeist/Test/CUDA/polybench-cuda/lu/lu.cu +++ b/tools/cgeist/Test/CUDA/polybench-cuda/lu/lu.cu @@ -1,6 +1,6 @@ // clang-format off // RUN: cgeist %s %stdinclude %cudaopts -O3 -o %s.execm && %s.execm 1 100 -// RUN: cgeist %s %stdinclude %cudaopts_polymer -O3 -o %s.execm && %s.execm 1 100 +// RUN: [ "%polymer_pluto_enabled" == "" ] || cgeist %s %stdinclude %polymer_pluto_cudaopts -O3 -o %s.execm && %s.execm 1 10 10 10 // clang-format on /** * lu.c: This file is part of the PolyBench/C 3.2 test suite. diff --git a/tools/cgeist/Test/CUDA/polybench-cuda/seidel-2d/seidel-2d.cu b/tools/cgeist/Test/CUDA/polybench-cuda/seidel-2d/seidel-2d.cu index 60e769a075d1..1d373c12e540 100644 --- a/tools/cgeist/Test/CUDA/polybench-cuda/seidel-2d/seidel-2d.cu +++ b/tools/cgeist/Test/CUDA/polybench-cuda/seidel-2d/seidel-2d.cu @@ -1,6 +1,6 @@ // clang-format off // RUN: cgeist %s %stdinclude %cudaopts -O3 -o %s.execm && %s.execm 1 10 2 -// RUN: cgeist %s %stdinclude %polymer_cudaopts -O3 -o %s.execm && %s.execm 1 10 2 +// RUN: [ "%polymer_pluto_enabled" == "" ] || cgeist %s %stdinclude %polymer_pluto_cudaopts -O3 -o %s.execm && %s.execm 1 10 10 10 // clang-format on /** * seidel-2d.c: This file is part of the PolyBench/C 3.2 test suite. diff --git a/tools/cgeist/Test/lit.cfg b/tools/cgeist/Test/lit.cfg index ef2de408b903..c3855b175d61 100644 --- a/tools/cgeist/Test/lit.cfg +++ b/tools/cgeist/Test/lit.cfg @@ -74,4 +74,6 @@ config.substitutions.append(('%resourcedir', '-resource-dir=' + resource_dir)) 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')) config.substitutions.append(('%cudaopts', cudaopts)) -config.substitutions.append(('%polymer_cudaopts', cudaopts + ' --polymer-pluto-opt="" --raise-scf-to-affine')) +config.substitutions.append(('%polymer_pluto_cudaopts', cudaopts + ' --polyhedral-opt --raise-scf-to-affine')) +config.substitutions.append(('%polymer_enabled', config.polymer_enabled)) +config.substitutions.append(('%polymer_pluto_enabled', config.polymer_pluto_enabled)) diff --git a/tools/cgeist/Test/lit.site.cfg.in b/tools/cgeist/Test/lit.site.cfg.in index f3f734d12079..d1669f319406 100644 --- a/tools/cgeist/Test/lit.site.cfg.in +++ b/tools/cgeist/Test/lit.site.cfg.in @@ -11,6 +11,8 @@ config.llvm_obj_root = path(r"@LLVM_BINARY_DIR@") config.polygeist_enable_cuda = "@POLYGEIST_ENABLE_CUDA@" config.cudart_static_path = "@CUDA_cudart_static_LIBRARY@" config.polygeist_enable_rocm = "@POLYGEIST_ENABLE_ROCM@" +config.polymer_enabled = "@POLYGEIST_ENABLE_POLYMER@" +config.polymer_pluto_enabled = "@POLYGEIST_POLYMER_ENABLE_PLUTO@" # 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. diff --git a/tools/polymer/CMakeLists.txt b/tools/polymer/CMakeLists.txt index 09b4432aa9bd..c12633027e91 100644 --- a/tools/polymer/CMakeLists.txt +++ b/tools/polymer/CMakeLists.txt @@ -24,22 +24,38 @@ set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake" # Find whether the GMP package exists. find_package(GMP REQUIRED) -# build pluto, openscop, cloog -execute_process( - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/build_polymer_deps.sh" "${POLYGEIST_PLUTO_DIR}" - COMMAND_ERROR_IS_FATAL ANY -) -# configure the cmake variables for pluto -include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/AddPluto.cmake") -include_directories("${PLUTO_INCLUDE_DIR}") -message(STATUS "Will build and use Polymer-bundled Pluto") +if(POLYGEIST_POLYMER_ENABLE_PLUTO) + # build pluto, openscop, cloog + execute_process( + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/build_polymer_deps.sh" "${POLYGEIST_PLUTO_DIR}" + COMMAND_ERROR_IS_FATAL ANY + ) + # configure the cmake variables for pluto + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/AddPluto.cmake") + include_directories("${PLUTO_INCLUDE_DIR}") + message(STATUS "Will build and use Polymer-bundled Pluto") -include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/OpenScop.cmake") -include_directories("${OSL_INCLUDE_DIR}") + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/OpenScop.cmake") + include_directories("${OSL_INCLUDE_DIR}") -include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CLooG.cmake") -include_directories("${CLOOG_INCLUDE_DIR}") + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CLooG.cmake") + include_directories("${CLOOG_INCLUDE_DIR}") + set(POLYMER_AVAIL_TARGETS + PolymerTargetOpenScop + ) +elseif(POLYGEIST_POLYMER_ENABLE_ISL) + include_directories(${MLIR_MAIN_SRC_DIR}/../polly/lib/External/isl/include) + include_directories(${MLIR_MAIN_SRC_DIR}/../polly/include) + include_directories(${MLIR_MAIN_SRC_DIR}/../polly/lib/External/isl/include) + include_directories(${MLIR_MAIN_SRC_DIR}/../polly/lib/External/isl/include) + include_directories(${LLVM_BINARY_DIR}/tools/polly/lib/External/isl/include) + set(POLYMER_AVAIL_TARGETS + PolymerTargetISL + ) +else() + message(FATAL_ERROR "Need one of POLYGEIST_POLYMER_ENABLE_PLUTO or POLYGEIST_POLYMER_ENABLE_ISL") +endif() # ------------------------------------------------- This project include_directories("${POLYMER_MAIN_INCLUDE_DIR}") diff --git a/tools/polymer/include/polymer/Support/IslScop.h b/tools/polymer/include/polymer/Support/IslScop.h new file mode 100644 index 000000000000..1ad9118d8af9 --- /dev/null +++ b/tools/polymer/include/polymer/Support/IslScop.h @@ -0,0 +1,176 @@ +//===- IslScop.h ------------------------------------------------*- C++ -*-===// +// +// This file declares the C++ wrapper for the Scop struct in OpenScop. +// +//===----------------------------------------------------------------------===// +#ifndef POLYMER_SUPPORT_OSLSCOP_H +#define POLYMER_SUPPORT_OSLSCOP_H + +#include "mlir/Dialect/Affine/Analysis/AffineStructures.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Support/LLVM.h" +#include "polymer/Support/ScatteringUtils.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" + +#include +#include +#include +#include +#include +#include + +struct isl_schedule; +struct isl_union_set; +struct isl_mat; +struct isl_ctx; +struct isl_set; +struct isl_space; +struct isl_basic_set; +struct isl_basic_map; + +#define __isl_keep +#define __isl_give +#define __isl_take + +namespace mlir { +namespace affine { +class AffineValueMap; +class AffineForOp; +class FlatAffineValueConstraints; +} // namespace affine +struct LogicalResult; +class Operation; +class Value; +namespace func { +class FuncOp; +} +} // namespace mlir + +namespace polymer { + +class IslMLIRBuilder; +class ScopStmt; + +/// A wrapper for the osl_scop struct in the openscop library. +class IslScop { +public: + using SymbolTable = llvm::StringMap; + using ValueTable = llvm::DenseMap; + using MemRefToId = llvm::DenseMap; + using ScopStmtMap = std::map; + using ScopStmtNames = std::vector; + + IslScop(); + ~IslScop(); + + /// Simply create a new statement in the linked list scop->statement. + void createStatement(); + + /// Add the relation defined by cst to the context of the current scop. + void addContextRelation(mlir::affine::FlatAffineValueConstraints cst); + /// Add the domain relation. + void addDomainRelation(int stmtId, + mlir::affine::FlatAffineValueConstraints &cst); + /// Add the access relation. + mlir::LogicalResult + addAccessRelation(int stmtId, bool isRead, mlir::Value memref, + mlir::affine::AffineValueMap &vMap, + mlir::affine::FlatAffineValueConstraints &cst); + + /// Initialize the symbol table. + void initializeSymbolTable(mlir::func::FuncOp f, + mlir::affine::FlatAffineValueConstraints *cst); + + /// Get the symbol table object. + /// TODO: maybe not expose the symbol table to the external world like this. + SymbolTable *getSymbolTable(); + ValueTable *getValueTable(); + + /// Get the mapping from memref Value to its id. + MemRefToId *getMemRefIdMap(); + + /// Get the ScopStmtMap. + ScopStmtMap *getScopStmtMap(); + + /// Get the list of stmt names followed by their insertion order + ScopStmtNames *getScopStmtNames(); + + void dumpSchedule(llvm::raw_ostream &os); + void dumpAccesses(llvm::raw_ostream &os); + + void buildSchedule(llvm::SmallVector ops) { + loopId = 0; + schedule = buildSequenceSchedule(ops); + } + + static llvm::SmallVector + getSequenceScheduleOpList(mlir::Operation *begin, mlir::Operation *end); + static llvm::SmallVector + getSequenceScheduleOpList(mlir::Block *block); + + isl_schedule *getSchedule() { return schedule; } + + mlir::func::FuncOp applySchedule(__isl_take isl_schedule *newSchedule, + mlir::func::FuncOp f); + +private: + struct IslStmt { + isl_basic_set *domain; + std::vector readRelations; + std::vector writeRelations; + }; + std::vector islStmts; + isl_schedule *schedule = nullptr; + unsigned loopId = 0; + + template + __isl_give isl_schedule *buildLoopSchedule(T loopOp, unsigned depth); + __isl_give isl_schedule * + buildParallelSchedule(mlir::affine::AffineParallelOp parallelOp, + unsigned depth); + __isl_give isl_schedule *buildForSchedule(mlir::affine::AffineForOp forOp, + unsigned depth); + __isl_give isl_schedule *buildLeafSchedule(mlir::func::CallOp callOp); + __isl_give isl_schedule * + buildSequenceSchedule(llvm::SmallVector ops, + unsigned depth = 0); + + IslStmt &getIslStmt(std::string name); + + __isl_give isl_space * + setupSpace(__isl_take isl_space *space, + mlir::affine::FlatAffineValueConstraints &cst, std::string name); + + __isl_give isl_mat * + createConstraintRows(mlir::affine::FlatAffineValueConstraints &cst, + bool isEq); + + /// Create access relation constraints. + mlir::LogicalResult createAccessRelationConstraints( + mlir::affine::AffineValueMap &vMap, + mlir::affine::FlatAffineValueConstraints &cst, + mlir::affine::FlatAffineValueConstraints &domain); + + /// The internal storage of the Scop. + // osl_scop *scop; + isl_ctx *ctx; + + /// Number of memrefs recorded. + MemRefToId memRefIdMap; + /// Symbol table for MLIR values. + SymbolTable symbolTable; + ValueTable valueTable; + /// + ScopStmtMap scopStmtMap; + + ScopStmtNames scopStmtNames; + + friend class IslMLIRBuilder; +}; + +} // namespace polymer + +#endif diff --git a/tools/polymer/include/polymer/Support/OslScop.h b/tools/polymer/include/polymer/Support/OslScop.h index 7562d7da4416..b83897ae945d 100644 --- a/tools/polymer/include/polymer/Support/OslScop.h +++ b/tools/polymer/include/polymer/Support/OslScop.h @@ -6,8 +6,10 @@ #ifndef POLYMER_SUPPORT_OSLSCOP_H #define POLYMER_SUPPORT_OSLSCOP_H +#include "mlir/Dialect/Affine/Analysis/AffineStructures.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Support/LLVM.h" +#include "polymer/Support/ScatteringUtils.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" @@ -27,6 +29,7 @@ struct osl_generic; namespace mlir { namespace affine { class AffineValueMap; +class AffineForOp; class FlatAffineValueConstraints; } // namespace affine struct LogicalResult; @@ -155,6 +158,7 @@ class OslScop { /// The internal storage of the Scop. osl_scop *scop; + /// The scattering tree maintained. std::unique_ptr scatTreeRoot; /// Number of memrefs recorded. diff --git a/tools/polymer/include/polymer/Support/OslScopStmtOpSet.h b/tools/polymer/include/polymer/Support/OslScopStmtOpSet.h index f226d52ec0cd..15b7d3d9c3af 100644 --- a/tools/polymer/include/polymer/Support/OslScopStmtOpSet.h +++ b/tools/polymer/include/polymer/Support/OslScopStmtOpSet.h @@ -22,13 +22,13 @@ namespace polymer { /// This class contains a set of operations that will correspond to a single /// OpenScop statement body. The underlying data structure is SetVector. -class OslScopStmtOpSet { +class ScopStmtOpSet { public: using Set = llvm::SetVector; using iterator = Set::iterator; using reverse_iterator = Set::reverse_iterator; - OslScopStmtOpSet() {} + ScopStmtOpSet() {} /// The core store op. There should be only one of it. mlir::Operation *getStoreOp() { return storeOp; } diff --git a/tools/polymer/include/polymer/Support/OslSymbolTable.h b/tools/polymer/include/polymer/Support/OslSymbolTable.h index 695dde4f5efd..dafce079ee7e 100644 --- a/tools/polymer/include/polymer/Support/OslSymbolTable.h +++ b/tools/polymer/include/polymer/Support/OslSymbolTable.h @@ -18,11 +18,11 @@ class Value; namespace polymer { -class OslScopStmtOpSet; +class ScopStmtOpSet; -class OslSymbolTable { +class PolymerSymbolTable { public: - using OpSet = OslScopStmtOpSet; + using OpSet = ScopStmtOpSet; using OpSetPtr = std::unique_ptr; enum SymbolType { LoopIV, Memref, StmtOpSet }; diff --git a/tools/polymer/include/polymer/Support/PolymerUtils.h b/tools/polymer/include/polymer/Support/PolymerUtils.h index c3eeccff8292..c421c33342ed 100644 --- a/tools/polymer/include/polymer/Support/PolymerUtils.h +++ b/tools/polymer/include/polymer/Support/PolymerUtils.h @@ -1,7 +1,12 @@ #ifndef POLYMER_SUPPORT_POLYMERUTILS_H_ #define POLYMER_SUPPORT_POLYMERUTILS_H_ +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" + namespace polymer { +mlir::func::FuncOp islexternalTransform(mlir::func::FuncOp f, + mlir::OpBuilder &rewriter); mlir::func::FuncOp plutoTransform(mlir::func::FuncOp f, mlir::OpBuilder &rewriter, std::string dumpClastAfterPluto, diff --git a/tools/polymer/include/polymer/Target/ISL.h b/tools/polymer/include/polymer/Target/ISL.h new file mode 100644 index 000000000000..69ac66820a40 --- /dev/null +++ b/tools/polymer/include/polymer/Target/ISL.h @@ -0,0 +1,49 @@ +//===- OpenScop.h -----------------------------------------------*- C++ -*-===// +// +// This file declares the interfaces for converting OpenScop representation to +// MLIR modules. +// +//===----------------------------------------------------------------------===// + +#ifndef POLYMER_TARGET_ISL_H +#define POLYMER_TARGET_ISL_H + +#include + +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Support/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" + +namespace mlir { +template class OwningOpRef; +class MLIRContext; +class ModuleOp; +namespace func { +class FuncOp; +} +struct LogicalResult; +class Operation; +class Value; +} // namespace mlir + +struct isl_schedule; + +#define __isl_give + +namespace polymer { + +class IslScop; +class PolymerSymbolTable; + +std::unique_ptr createIslFromFuncOp(mlir::func::FuncOp funcOp); + +/// Create a function (FuncOp) from the given OpenScop object in the given +/// module (ModuleOp). +mlir::func::FuncOp createFuncOpFromIsl(std::unique_ptr scop, + mlir::func::FuncOp f, + __isl_give isl_schedule *newSchedule); + +} // namespace polymer + +#endif diff --git a/tools/polymer/include/polymer/Target/OpenScop.h b/tools/polymer/include/polymer/Target/OpenScop.h index 071182b52224..a743561623f7 100644 --- a/tools/polymer/include/polymer/Target/OpenScop.h +++ b/tools/polymer/include/polymer/Target/OpenScop.h @@ -32,17 +32,17 @@ class Value; namespace polymer { class OslScop; -class OslSymbolTable; +class PolymerSymbolTable; std::unique_ptr createOpenScopFromFuncOp(mlir::func::FuncOp funcOp, - OslSymbolTable &symTable); + PolymerSymbolTable &symTable); /// Create a function (FuncOp) from the given OpenScop object in the given /// module (ModuleOp). mlir::Operation * createFuncOpFromOpenScop(std::unique_ptr scop, mlir::ModuleOp module, - OslSymbolTable &symTable, mlir::MLIRContext *context, - PlutoProg *prog = nullptr, + PolymerSymbolTable &symTable, + mlir::MLIRContext *context, PlutoProg *prog = nullptr, const char *dumpClastAfterPluto = nullptr); mlir::OwningOpRef diff --git a/tools/polymer/lib/Support/CMakeLists.txt b/tools/polymer/lib/Support/CMakeLists.txt index 23ba940af488..baa0786ec39e 100644 --- a/tools/polymer/lib/Support/CMakeLists.txt +++ b/tools/polymer/lib/Support/CMakeLists.txt @@ -1,31 +1,52 @@ -add_mlir_library(PolymerSupport - OslScop.cc - OslScopStmtOpSet.cc - OslSymbolTable.cc - ScopStmt.cc - ScatteringUtils.cc - Utils.cc - - DEPENDS - mlir-headers - - ADDITIONAL_HEADERS - ${POLYMER_MAIN_INCLUDE_DIR}/polymer/Support - - LINK_LIBS PUBLIC - MLIRAnalysis - MLIRAffineAnalysis - - # libosl - libcloog - # libisl - libplutoisl - libplutoosl - libplutopip - libplutopolylib - libplutocloog - libplutocandl - libpluto -) +if(POLYGEIST_POLYMER_ENABLE_PLUTO) + add_mlir_library(PolymerSupport + OslScop.cc + OslScopStmtOpSet.cc + OslSymbolTable.cc + ScopStmt.cc + ScatteringUtils.cc + Utils.cc + + DEPENDS + mlir-headers + + ADDITIONAL_HEADERS + ${POLYMER_MAIN_INCLUDE_DIR}/polymer/Support + + LINK_LIBS PUBLIC + MLIRAnalysis + MLIRAffineAnalysis + + # libosl + libcloog + # libisl + libplutoisl + libplutoosl + libplutopip + libplutopolylib + libplutocloog + libplutocandl + libpluto + ) +elseif(POLYGEIST_POLYMER_ENABLE_ISL) + add_mlir_library(PolymerSupport + IslScop.cc + ScopStmt.cc + Utils.cc + + DEPENDS + mlir-headers + + ADDITIONAL_HEADERS + ${POLYMER_MAIN_INCLUDE_DIR}/polymer/Support + + LINK_LIBS PUBLIC + MLIRAnalysis + MLIRAffineAnalysis + + PollyISL + Polly + ) +endif() target_link_libraries(PolymerSupport PUBLIC ${GMP_LIBRARY}) diff --git a/tools/polymer/lib/Support/IslScop.cc b/tools/polymer/lib/Support/IslScop.cc new file mode 100644 index 000000000000..77b1ea49578b --- /dev/null +++ b/tools/polymer/lib/Support/IslScop.cc @@ -0,0 +1,1174 @@ +//===- IslScop.cc -----------------------------------------------*- C++ -*-===// + +#include "polymer/Support/IslScop.h" +#include "mlir/Analysis/Presburger/PresburgerSpace.h" +#include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/SCF/IR/SCF.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Support/LLVM.h" +#include "polymer/Support/ScatteringUtils.h" +#include "polymer/Support/ScopStmt.h" + +#include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h" +#include "mlir/Dialect/Affine/Analysis/AffineStructures.h" +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Affine/IR/AffineValueMap.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/IR/Attributes.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/Operation.h" +#include "mlir/IR/Value.h" +#include "mlir/Support/LogicalResult.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include "polly/CodeGen/IslNodeBuilder.h" +#include "polly/Support/GICHelper.h" + +#include "isl/aff_type.h" +#include "isl/ast.h" +#include "isl/id_to_id.h" +#include "isl/printer.h" +#include "isl/space_type.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace polymer; +using namespace mlir; + +using llvm::dbgs; +using llvm::errs; +using llvm::formatv; + +#define DEBUG_TYPE "islscop" + +IslScop::IslScop() { ctx = isl_ctx_alloc(); } + +IslScop::~IslScop() { + for (auto &stmt : islStmts) { + for (auto &rel : stmt.readRelations) + rel = isl_basic_map_free(rel); + for (auto &rel : stmt.writeRelations) + rel = isl_basic_map_free(rel); + stmt.domain = isl_basic_set_free(stmt.domain); + } + isl_schedule_free(schedule); + isl_ctx_free(ctx); +} + +void IslScop::createStatement() { islStmts.push_back({}); } + +void IslScop::addContextRelation(affine::FlatAffineValueConstraints cst) { + // Project out the dim IDs in the context with only the symbol IDs left. + SmallVector dimValues; + cst.getValues(0, cst.getNumDimVars(), &dimValues); + for (mlir::Value dimValue : dimValues) + cst.projectOut(dimValue); + if (cst.getNumDimAndSymbolVars() > 0) + cst.removeIndependentConstraints(0, cst.getNumDimAndSymbolVars()); +} + +namespace { +struct IslStr { + char *s; + char *str() { return s; } + IslStr(char *s) : s(s) {} + ~IslStr() { free(s); } +}; +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, IslStr s) { + return os << s.str(); +} +} // namespace + +inline void islAssert(const isl_size &size) { assert(size != isl_size_error); } +inline unsigned unsignedFromIslSize(const isl_size &size) { + islAssert(size); + return static_cast(size); +} + +#define ISL_DEBUG(S, X) \ + LLVM_DEBUG({ \ + llvm::dbgs() << S; \ + X; \ + llvm::dbgs() << "\n"; \ + }) + +static __isl_give isl_multi_union_pw_aff * +mapToDimension(__isl_take isl_union_set *uset, unsigned N) { + assert(!isl_union_set_is_empty(uset)); + N += 1; + + isl_union_pw_multi_aff *res = + isl_union_pw_multi_aff_empty(isl_union_set_get_space(uset)); + isl_set_list *bsetlist = isl_union_set_get_set_list(uset); + for (unsigned i = 0; i < unsignedFromIslSize(isl_set_list_size(bsetlist)); + i++) { + isl_set *set = isl_set_list_get_at(bsetlist, i); + unsigned Dim = unsignedFromIslSize(isl_set_dim(set, isl_dim_set)); + assert(Dim >= N); + auto pma = isl_pw_multi_aff_project_out_map(isl_set_get_space(set), + isl_dim_set, N, Dim - N); + isl_set_free(set); + + if (N > 1) + pma = isl_pw_multi_aff_drop_dims(pma, isl_dim_out, 0, N - 1); + res = isl_union_pw_multi_aff_add_pw_multi_aff(res, pma); + } + + isl_set_list_free(bsetlist); + isl_union_set_free(uset); + + return isl_multi_union_pw_aff_from_union_pw_multi_aff(res); +} + +static constexpr char parallelLoopMark[] = "parallel"; +static isl_id *getParallelLoopMark(isl_ctx *ctx) { + isl_id *loopMark = isl_id_alloc(ctx, parallelLoopMark, nullptr); + return loopMark; +} +static bool isParallelLoopMark(isl_id *id) { + return std::string(parallelLoopMark) == isl_id_get_name(id); +} + +isl_schedule * +IslScop::buildParallelSchedule(affine::AffineParallelOp parallelOp, + unsigned depth) { + isl_schedule *schedule = buildLoopSchedule(parallelOp, depth); + isl_schedule_node *node = isl_schedule_get_root(schedule); + schedule = isl_schedule_free(schedule); + node = isl_schedule_node_first_child(node); + node = isl_schedule_node_band_set_permutable(node, 1); + node = isl_schedule_node_insert_mark(node, getParallelLoopMark(ctx)); + schedule = isl_schedule_node_get_schedule(node); + isl_schedule_node_free(node); + return schedule; +} + +template +isl_schedule *IslScop::buildLoopSchedule(T loopOp, unsigned depth) { + SmallVector body = getSequenceScheduleOpList(loopOp.getBody()); + + isl_schedule *child = buildSequenceSchedule(body, depth + 1); + ISL_DEBUG("CHILD:\n", isl_schedule_dump(child)); + isl_union_set *domain = isl_schedule_get_domain(child); + ISL_DEBUG("MUPA dom: ", isl_union_set_dump(domain)); + isl_multi_union_pw_aff *mupa = mapToDimension(domain, depth); + mupa = isl_multi_union_pw_aff_set_tuple_name( + mupa, isl_dim_set, ("L" + std::to_string(loopId++)).c_str()); + ISL_DEBUG("MUPA: ", isl_multi_union_pw_aff_dump(mupa)); + schedule = isl_schedule_insert_partial_schedule(child, mupa); + + ISL_DEBUG("Created for schedule:\n", isl_schedule_dump(schedule)); + + return schedule; +} + +isl_schedule *IslScop::buildForSchedule(affine::AffineForOp forOp, + unsigned depth) { + isl_schedule *schedule = buildLoopSchedule(forOp, depth); + return schedule; +} + +isl_schedule *IslScop::buildLeafSchedule(func::CallOp callOp) { + // TODO check that we are really calling a statement + auto &stmt = getIslStmt(callOp.getCallee().str()); + isl_schedule *schedule = isl_schedule_from_domain( + isl_union_set_from_basic_set(isl_basic_set_copy(stmt.domain))); + LLVM_DEBUG({ + llvm::errs() << "Created leaf schedule:\n"; + isl_schedule_dump(schedule); + llvm::errs() << "\n"; + }); + return schedule; +} + +SmallVector IslScop::getSequenceScheduleOpList(Operation *begin, + Operation *end) { + SmallVector ops; + for (auto op = begin; op != end; op = op->getNextNode()) { + if (auto ifOp = dyn_cast(op)) { + auto thenOps = getSequenceScheduleOpList(ifOp.getThenBlock()); + if (ifOp.hasElse()) { + auto elseOps = getSequenceScheduleOpList(ifOp.getElseBlock()); + ops.insert(ops.end(), elseOps.begin(), elseOps.end()); + } + ops.insert(ops.end(), thenOps.begin(), thenOps.end()); + } else { + ops.push_back(op); + } + } + return ops; +} + +SmallVector IslScop::getSequenceScheduleOpList(Block *block) { + // We cannot be yielding anything + assert(block->back().getNumOperands() == 0); + return getSequenceScheduleOpList(&block->front(), &block->back()); +} + +isl_schedule *IslScop::buildSequenceSchedule(SmallVector ops, + unsigned depth) { + auto buildOpSchedule = [&](Operation *op) { + if (auto forOp = dyn_cast(op)) { + return buildForSchedule(forOp, depth); + } else if (auto parallelOp = dyn_cast(op)) { + return buildParallelSchedule(parallelOp, depth); + } else if (auto callOp = dyn_cast(op)) { + return buildLeafSchedule(callOp); + } else if (auto alloca = dyn_cast(op)) { + return (isl_schedule *)nullptr; + } else { + llvm_unreachable("unhandled op"); + } + }; + + auto len = ops.size(); + if (len == 1) + return buildOpSchedule(ops[0]); + + isl_schedule *schedule = nullptr; + for (auto curOp : ops) { + isl_schedule *child = buildOpSchedule(curOp); + if (!child) + continue; + if (!schedule) + schedule = child; + else + schedule = isl_schedule_sequence(schedule, child); + } + assert(schedule); + + LLVM_DEBUG({ + llvm::errs() << "Created sequence schedule:\n"; + isl_schedule_dump(schedule); + llvm::errs() << "\n"; + }); + + return schedule; +} + +IslScop::IslStmt &IslScop::getIslStmt(std::string name) { + auto found = std::find(scopStmtNames.begin(), scopStmtNames.end(), name); + assert(found != scopStmtNames.end()); + auto id = std::distance(scopStmtNames.begin(), found); + return islStmts[id]; +} + +void IslScop::dumpAccesses(llvm::raw_ostream &os) { + auto o = [&os](unsigned n) -> llvm::raw_ostream & { + return os << std::string(n, ' '); + }; + isl_union_set *domain = isl_schedule_get_domain(schedule); + o(0) << "domain: \"" << IslStr(isl_union_set_to_str(domain)) << "\"\n"; + domain = isl_union_set_free(domain); + o(0) << "accesses:\n"; + for (unsigned stmtId = 0; stmtId < islStmts.size(); stmtId++) { + auto &stmt = islStmts[stmtId]; + o(2) << "- " << scopStmtNames[stmtId] << ":" + << "\n"; + o(6) << "reads:" + << "\n"; + for (auto rel : stmt.readRelations) + o(8) << "- " << '"' << IslStr(isl_basic_map_to_str(rel)) << '"' << "\n"; + o(6) << "writes:" + << "\n"; + for (auto rel : stmt.writeRelations) + o(8) << "- " << '"' << IslStr(isl_basic_map_to_str(rel)) << '"' << "\n"; + } +} +void IslScop::dumpSchedule(llvm::raw_ostream &os) { + LLVM_DEBUG(llvm::errs() << "Dumping islexternal\n"); + LLVM_DEBUG(llvm::errs() << "Schedule:\n\n"); + LLVM_DEBUG(isl_schedule_dump(schedule)); + LLVM_DEBUG(llvm::errs() << "\n"); + + os << IslStr(isl_schedule_to_str(schedule)) << "\n"; +} + +isl_space *IslScop::setupSpace(isl_space *space, + affine::FlatAffineValueConstraints &cst, + std::string name) { + for (unsigned i = 0; i < cst.getNumSymbolVars(); i++) { + Value val = + cst.getValue(cst.getVarKindOffset(presburger::VarKind::Symbol) + i); + std::string sym = valueTable[val]; + isl_id *id = isl_id_alloc(ctx, sym.c_str(), nullptr); + space = isl_space_set_dim_id(space, isl_dim_param, i, id); + } + space = isl_space_set_tuple_name(space, isl_dim_set, name.c_str()); + return space; +} + +void IslScop::addDomainRelation(int stmtId, + affine::FlatAffineValueConstraints &cst) { + SmallVector eqs, inEqs; + isl_mat *eqMat = createConstraintRows(cst, /*isEq=*/true); + isl_mat *ineqMat = createConstraintRows(cst, /*isEq=*/false); + LLVM_DEBUG({ + llvm::errs() << "Adding domain relation\n"; + llvm::errs() << " ISL eq mat:\n"; + isl_mat_dump(eqMat); + llvm::errs() << " ISL ineq mat:\n"; + isl_mat_dump(ineqMat); + llvm::errs() << "\n"; + }); + + isl_space *space = + isl_space_set_alloc(ctx, cst.getNumSymbolVars(), cst.getNumDimVars()); + space = setupSpace(space, cst, scopStmtNames[stmtId]); + LLVM_DEBUG(llvm::errs() << "space: "); + LLVM_DEBUG(isl_space_dump(space)); + islStmts[stmtId].domain = isl_basic_set_from_constraint_matrices( + space, eqMat, ineqMat, isl_dim_set, isl_dim_div, isl_dim_param, + isl_dim_cst); + LLVM_DEBUG(llvm::errs() << "bset: "); + LLVM_DEBUG(isl_basic_set_dump(islStmts[stmtId].domain)); +} + +LogicalResult +IslScop::addAccessRelation(int stmtId, bool isRead, mlir::Value memref, + affine::AffineValueMap &vMap, + affine::FlatAffineValueConstraints &domain) { + affine::FlatAffineValueConstraints cst; + // Insert the address dims and put constraints in it. + if (createAccessRelationConstraints(vMap, cst, domain).failed()) { + LLVM_DEBUG(llvm::dbgs() << "createAccessRelationConstraints failed\n"); + return failure(); + } + + // Create a new dim of memref and set its value to its corresponding ID. + memRefIdMap.try_emplace(memref, "A" + std::to_string(memRefIdMap.size() + 1)); + + isl_mat *eqMat = createConstraintRows(cst, /*isEq=*/true); + isl_mat *ineqMat = createConstraintRows(cst, /*isEq=*/false); + + LLVM_DEBUG({ + llvm::errs() << "Adding access relation\n"; + dbgs() << "Resolved MLIR access constraints:\n"; + cst.dump(); + llvm::errs() << " ISL eq mat:\n"; + isl_mat_dump(eqMat); + llvm::errs() << " ISL ineq mat:\n"; + isl_mat_dump(ineqMat); + llvm::errs() << "\n"; + }); + + assert(cst.getNumInequalities() == 0); + isl_space *space = isl_space_alloc(ctx, cst.getNumSymbolVars(), + cst.getNumDimVars() - vMap.getNumResults(), + vMap.getNumResults()); + space = setupSpace(space, cst, memRefIdMap[memref]); + + isl_basic_map *bmap = isl_basic_map_from_constraint_matrices( + space, eqMat, ineqMat, isl_dim_out, isl_dim_in, isl_dim_div, + isl_dim_param, isl_dim_cst); + if (isRead) + islStmts[stmtId].readRelations.push_back(bmap); + else + islStmts[stmtId].writeRelations.push_back(bmap); + + ISL_DEBUG("Created relation: ", isl_basic_map_dump(bmap)); + + return success(); +} + +void IslScop::initializeSymbolTable(mlir::func::FuncOp f, + affine::FlatAffineValueConstraints *cst) { + symbolTable.clear(); + + // Setup the symbol table. + for (unsigned i = 0; i < cst->getNumDimAndSymbolVars(); i++) { + Value val = cst->getValue(i); + std::string sym; + switch (cst->getVarKindAt(i)) { + case presburger::VarKind::Domain: + sym = "I"; + break; + case presburger::VarKind::Local: + sym = "O"; + break; + case presburger::VarKind::Symbol: + sym = "P"; + break; + case presburger::VarKind::Range: + sym = "R"; + break; + } + sym += std::to_string(i - cst->getVarKindOffset(cst->getVarKindAt(i))); + // symbolTable.insert(std::make_pair(sym, val)); + // valueTable.insert(std::make_pair(val, sym)); + valueTable[val] = sym; + symbolTable[sym] = val; + } + for (const auto &it : memRefIdMap) { + std::string sym(formatv("A{0}", it.second)); + symbolTable.insert(std::make_pair(sym, it.first)); + valueTable.insert(std::make_pair(it.first, sym)); + } + // constants + unsigned numConstants = 0; + for (mlir::Value arg : f.getBody().begin()->getArguments()) { + if (valueTable.find(arg) == valueTable.end()) { + std::string sym(formatv("C{0}", numConstants++)); + symbolTable.insert(std::make_pair(sym, arg)); + valueTable.insert(std::make_pair(arg, sym)); + } + } +} + +isl_mat *IslScop::createConstraintRows(affine::FlatAffineValueConstraints &cst, + bool isEq) { + unsigned numRows = isEq ? cst.getNumEqualities() : cst.getNumInequalities(); + unsigned numDimIds = cst.getNumDimVars(); + unsigned numLocalIds = cst.getNumLocalVars(); + unsigned numSymbolIds = cst.getNumSymbolVars(); + + LLVM_DEBUG(llvm::errs() << "createConstraintRows " << numRows << " " + << numDimIds << " " << numLocalIds << " " + << numSymbolIds << "\n"); + + unsigned numCols = cst.getNumCols(); + isl_mat *mat = isl_mat_alloc(ctx, numRows, numCols); + + for (unsigned i = 0; i < numRows; i++) { + // Get the row based on isEq. + auto row = isEq ? cst.getEquality(i) : cst.getInequality(i); + + assert(row.size() == numCols); + + // Dims stay at the same positions. + for (unsigned j = 0; j < numDimIds; j++) + mat = isl_mat_set_element_si(mat, i, j, (int64_t)row[j]); + // Output local ids before symbols. + for (unsigned j = 0; j < numLocalIds; j++) + mat = isl_mat_set_element_si(mat, i, j + numDimIds, + (int64_t)row[j + numDimIds + numSymbolIds]); + // Output symbols in the end. + for (unsigned j = 0; j < numSymbolIds; j++) + mat = isl_mat_set_element_si(mat, i, j + numDimIds + numLocalIds, + (int64_t)row[j + numDimIds]); + // Finally outputs the constant. + mat = + isl_mat_set_element_si(mat, i, numCols - 1, (int64_t)row[numCols - 1]); + } + return mat; +} + +LogicalResult IslScop::createAccessRelationConstraints( + mlir::affine::AffineValueMap &vMap, + mlir::affine::FlatAffineValueConstraints &cst, + mlir::affine::FlatAffineValueConstraints &domain) { + cst = mlir::affine::FlatAffineValueConstraints(); + cst.mergeAndAlignVarsWithOther(0, &domain); + + LLVM_DEBUG({ + dbgs() << "Building access relation.\n" + << " + Domain:\n"; + domain.dump(); + }); + + SmallVector idValues; + domain.getValues(0, domain.getNumDimAndSymbolVars(), &idValues); + llvm::SetVector idValueSet; + for (auto val : idValues) + idValueSet.insert(val); + + for (auto operand : vMap.getOperands()) + if (!idValueSet.contains(operand)) { + llvm::errs() << "Operand missing: "; + operand.dump(); + } + + // The results of the affine value map, which are the access addresses, will + // be placed to the leftmost of all columns. + return cst.composeMap(&vMap); +} + +IslScop::SymbolTable *IslScop::getSymbolTable() { return &symbolTable; } + +IslScop::ValueTable *IslScop::getValueTable() { return &valueTable; } + +IslScop::MemRefToId *IslScop::getMemRefIdMap() { return &memRefIdMap; } + +IslScop::ScopStmtMap *IslScop::getScopStmtMap() { return &scopStmtMap; } + +IslScop::ScopStmtNames *IslScop::getScopStmtNames() { return &scopStmtNames; } + +namespace polymer { +class IslMLIRBuilder { +public: + OpBuilder &b; + IRMapping funcMapping; + IslScop &scop; + Location loc = b.getUnknownLoc(); + typedef llvm::MapVector IDToValueTy; + IDToValueTy IDToValue{}; + + Value createOp(__isl_take isl_ast_expr *Expr) { + assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op && + "Expression not of type isl_ast_expr_op"); + switch (isl_ast_expr_get_op_type(Expr)) { + case isl_ast_op_error: + case isl_ast_op_cond: + case isl_ast_op_call: + case isl_ast_op_member: + llvm_unreachable("Unsupported isl ast expression"); + case isl_ast_op_access: + return createOpAccess(Expr); + case isl_ast_op_max: + case isl_ast_op_min: + return createOpNAry(Expr); + case isl_ast_op_add: + case isl_ast_op_sub: + case isl_ast_op_mul: + case isl_ast_op_div: + case isl_ast_op_fdiv_q: // Round towards -infty + case isl_ast_op_pdiv_q: // Dividend is non-negative + case isl_ast_op_pdiv_r: // Dividend is non-negative + case isl_ast_op_zdiv_r: // Result only compared against zero + return createOpBin(Expr); + case isl_ast_op_minus: + return createOpUnary(Expr); + case isl_ast_op_select: + return createOpSelect(Expr); + case isl_ast_op_and: + case isl_ast_op_or: + return createOpBoolean(Expr); + case isl_ast_op_and_then: + case isl_ast_op_or_else: + return createOpBooleanConditional(Expr); + case isl_ast_op_eq: + case isl_ast_op_le: + case isl_ast_op_lt: + case isl_ast_op_ge: + case isl_ast_op_gt: + return createOpICmp(Expr); + case isl_ast_op_address_of: + return createOpAddressOf(Expr); + } + + llvm_unreachable("Unsupported isl_ast_expr_op kind."); + } + + Value createOpAddressOf(__isl_take isl_ast_expr *Expr) { + llvm_unreachable("unimplemented"); + } + Value createOpUnary(__isl_take isl_ast_expr *Expr) { + llvm_unreachable("unimplemented"); + } + Value createOpAccess(__isl_take isl_ast_expr *Expr) { + llvm_unreachable("unimplemented"); + } + + Value createMul(Value LHS, Value RHS, std::string Name = "") { + return b.create(loc, LHS, RHS); + } + Value createSub(Value LHS, Value RHS, std::string Name = "") { + return b.create(loc, LHS, RHS); + } + Value createAdd(Value LHS, Value RHS, std::string Name = "") { + return b.create(loc, LHS, RHS); + } + + Value createOpBin(__isl_take isl_ast_expr *Expr) { + Value LHS, RHS, Res; + Type MaxType; + isl_ast_op_type OpType; + + assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op && + "isl ast expression not of type isl_ast_op"); + assert(isl_ast_expr_get_op_n_arg(Expr) == 2 && + "not a binary isl ast expression"); + + OpType = isl_ast_expr_get_op_type(Expr); + + LHS = create(isl_ast_expr_get_op_arg(Expr, 0)); + RHS = create(isl_ast_expr_get_op_arg(Expr, 1)); + + MaxType = convertToMaxWidth(LHS, RHS); + + switch (OpType) { + default: + llvm_unreachable("This is no binary isl ast expression"); + case isl_ast_op_add: + Res = createAdd(LHS, RHS); + break; + case isl_ast_op_sub: + Res = createSub(LHS, RHS); + break; + case isl_ast_op_mul: + Res = createMul(LHS, RHS); + break; + case isl_ast_op_div: + Res = b.create(loc, LHS, RHS); + break; + case isl_ast_op_pdiv_q: // Dividend is non-negative + Res = b.create(loc, LHS, RHS); + break; + case isl_ast_op_fdiv_q: { // Round towards -infty + // if (auto Const = dyn_cast(RHS)) { + // auto &Val = Const.getValue(); + // if (Val.isPowerOf2() && Val.isNonNegative()) { + // Res = b.create(loc, LHS, Val.ceilLogBase2()); + // break; + // } + // } + + // TODO: Review code and check that this calculation does not yield + // incorrect overflow in some edge cases. + // + // floord(n,d) ((n < 0) ? (n - d + 1) : n) / d + Value One = b.create(loc, 1, MaxType); + Value Zero = b.create(loc, 0, MaxType); + Value Sum1 = createSub(LHS, RHS, "pexp.fdiv_q.0"); + Value Sum2 = createAdd(Sum1, One, "pexp.fdiv_q.1"); + Value isNegative = + b.create(loc, arith::CmpIPredicate::slt, LHS, Zero); + Value Dividend = b.create(loc, isNegative, Sum2, LHS); + Res = b.create(loc, Dividend, RHS); + break; + } + case isl_ast_op_pdiv_r: // Dividend is non-negative + Res = b.create(loc, LHS, RHS); + break; + case isl_ast_op_zdiv_r: // Result only compared against zero + Res = b.create(loc, LHS, RHS); + break; + } + isl_ast_expr_free(Expr); + return Res; + } + + Value createOpNAry(__isl_take isl_ast_expr *Expr) { + assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op && + "isl ast expression not of type isl_ast_op"); + assert(isl_ast_expr_get_op_n_arg(Expr) >= 2 && + "We need at least two operands in an n-ary operation"); + + std::function Aggregate; + switch (isl_ast_expr_get_op_type(Expr)) { + default: + llvm_unreachable("This is not a an n-ary isl ast expression"); + case isl_ast_op_max: + Aggregate = [&](Value x, Value y) { + return b.create(loc, x, y); + }; + break; + case isl_ast_op_min: + Aggregate = [&](Value x, Value y) { + return b.create(loc, x, y); + }; + break; + } + + Value V = create(isl_ast_expr_get_op_arg(Expr, 0)); + + for (int i = 1; i < isl_ast_expr_get_op_n_arg(Expr); ++i) { + Value OpV = create(isl_ast_expr_get_op_arg(Expr, i)); + convertToMaxWidth(V, OpV); + V = Aggregate(OpV, V); + } + + isl_ast_expr_free(Expr); + return V; + } + Value createOpSelect(__isl_take isl_ast_expr *Expr) { + llvm_unreachable("unimplemented"); + } + Value createOpICmp(__isl_take isl_ast_expr *Expr) { + assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op && + "Expected an isl_ast_expr_op expression"); + + Value LHS, RHS, Res; + + auto *Op0 = isl_ast_expr_get_op_arg(Expr, 0); + auto *Op1 = isl_ast_expr_get_op_arg(Expr, 1); + bool HasNonAddressOfOperand = + isl_ast_expr_get_type(Op0) != isl_ast_expr_op || + isl_ast_expr_get_type(Op1) != isl_ast_expr_op || + isl_ast_expr_get_op_type(Op0) != isl_ast_op_address_of || + isl_ast_expr_get_op_type(Op1) != isl_ast_op_address_of; + + // TODO not sure if we would ever get pointers here + bool UseUnsignedCmp = !HasNonAddressOfOperand; + + LHS = create(Op0); + RHS = create(Op1); + + if (LHS.getType() != RHS.getType()) { + convertToMaxWidth(LHS, RHS); + } + + isl_ast_op_type OpType = isl_ast_expr_get_op_type(Expr); + assert(OpType >= isl_ast_op_eq && OpType <= isl_ast_op_gt && + "Unsupported ICmp isl ast expression"); + static_assert(isl_ast_op_eq + 4 == isl_ast_op_gt, + "Isl ast op type interface changed"); + + arith::CmpIPredicate Predicates[5][2] = { + {arith::CmpIPredicate::eq, arith::CmpIPredicate::eq}, + {arith::CmpIPredicate::sle, arith::CmpIPredicate::ule}, + {arith::CmpIPredicate::slt, arith::CmpIPredicate::ult}, + {arith::CmpIPredicate::sge, arith::CmpIPredicate::uge}, + {arith::CmpIPredicate::sgt, arith::CmpIPredicate::ugt}, + }; + + Res = b.create( + loc, Predicates[OpType - isl_ast_op_eq][UseUnsignedCmp], LHS, RHS); + + isl_ast_expr_free(Expr); + return Res; + } + + Value createOpBoolean(__isl_take isl_ast_expr *Expr) { + llvm_unreachable("unimplemented"); + } + Value createOpBooleanConditional(__isl_take isl_ast_expr *Expr) { + llvm_unreachable("unimplemented"); + } + Value createId(__isl_take isl_ast_expr *Expr) { + assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_id && + "Expression not of type isl_ast_expr_ident"); + + isl_id *Id; + Value V; + + Id = isl_ast_expr_get_id(Expr); + + assert(IDToValue.count(Id) && "Identifier not found"); + + V = IDToValue[Id]; + assert(V && "Unknown parameter id found"); + + isl_id_free(Id); + isl_ast_expr_free(Expr); + + return V; + } + IntegerType getType(__isl_keep isl_ast_expr *Expr) { + // XXX: We assume i64 is large enough. This is often true, but in general + // incorrect. Also, on 32bit architectures, it would be beneficial to + // use a smaller type. We can and should directly derive this + // information during code generation. + return IntegerType::get(b.getContext(), 64); + } + Value createInt(__isl_take isl_ast_expr *Expr) { + assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_int && + "Expression not of type isl_ast_expr_int"); + isl_val *Val; + Value V; + APInt APValue; + IntegerType T; + + Val = isl_ast_expr_get_val(Expr); + APValue = polly::APIntFromVal(Val); + + auto BitWidth = APValue.getBitWidth(); + if (BitWidth <= 64) + T = getType(Expr); + else + T = b.getIntegerType(BitWidth); + + APValue = APValue.sext(T.getWidth()); + V = b.create(loc, APValue.getSExtValue(), T); + + isl_ast_expr_free(Expr); + return V; + } + Value create(__isl_take isl_ast_expr *Expr) { + switch (isl_ast_expr_get_type(Expr)) { + case isl_ast_expr_error: + llvm_unreachable("Code generation error"); + case isl_ast_expr_op: + return createOp(Expr); + case isl_ast_expr_id: + return createId(Expr); + case isl_ast_expr_int: + return createInt(Expr); + } + llvm_unreachable("Unexpected enum value"); + } + + void createUser(__isl_keep isl_ast_node *User) { + ISL_DEBUG("Building User:\n", isl_ast_node_dump(User)); + + isl_ast_expr *Expr = isl_ast_node_user_get_expr(User); + if (isl_ast_expr_get_op_type(Expr) != isl_ast_op_call) { + llvm_unreachable("unexpected op type"); + } + isl_ast_expr *CalleeExpr = isl_ast_expr_get_op_arg(Expr, 0); + isl_id *Id = isl_ast_expr_get_id(CalleeExpr); + const char *CalleeName = isl_id_get_name(Id); + + SmallVector ivs; + for (int i = 0; i < isl_ast_expr_get_op_n_arg(Expr) - 1; ++i) { + isl_ast_expr *SubExpr = isl_ast_expr_get_op_arg(Expr, i + 1); + Value V = create(SubExpr); + ivs.push_back(V); + } + + ScopStmt &stmt = scop.scopStmtMap.at(std::string(CalleeName)); + func::CallOp origCaller = stmt.getCaller(); + SmallVector args; + for (Value origArg : origCaller.getArgOperands()) { + auto ba = origArg.dyn_cast(); + if (ba) { + Operation *owner = ba.getOwner()->getParentOp(); + if (isa(owner)) { + args.push_back(funcMapping.lookup(ba)); + } else if (isa(owner)) { + SmallVector enclosing; + stmt.getEnclosingOps(enclosing); + unsigned ivId = 0; + for (auto *op : enclosing) { + if (isa(op)) { + continue; + } else if (isa(op)) { + if (owner == op) + break; + ivId++; + } else { + llvm_unreachable("non-affine enclosing op"); + } + } + Value arg = ivs[ivId]; + if (arg.getType() != origArg.getType()) { + // This can only happen to index types as we may have replaced them + // with the target system width + assert(origArg.getType().isa()); + arg = b.create(loc, origArg.getType(), arg); + } + args.push_back(arg); + } else { + llvm_unreachable("unexpected"); + } + } else { + Operation *op = origArg.getDefiningOp(); + assert(op); + if (auto alloca = dyn_cast(op)) { + assert(alloca->getAttr("scop.scratchpad")); + auto newAlloca = funcMapping.lookup(op)->getResult(0); + args.push_back(newAlloca); + } else { + assert("unexpected"); + } + } + } + + b.create(loc, StringRef(CalleeName), TypeRange(), args); + + isl_ast_expr_free(Expr); + isl_ast_node_free(User); + isl_ast_expr_free(CalleeExpr); + isl_id_free(Id); + } + + void createMark(__isl_take isl_ast_node *Node) { + ISL_DEBUG("Building Mark:\n", isl_ast_node_dump(Node)); + + auto *Id = isl_ast_node_mark_get_id(Node); + auto Child = isl_ast_node_mark_get_node(Node); + isl_ast_node_free(Node); + + if (isParallelLoopMark(Id)) { + assert(isl_ast_node_get_type(Child) == isl_ast_node_for); + createFor(Child); + } else { + llvm_unreachable("Unknown mark"); + } + + isl_id_free(Id); + } + + void createIf(__isl_take isl_ast_node *If) { + ISL_DEBUG("Building If:\n", isl_ast_node_dump(If)); + isl_ast_expr *Cond = isl_ast_node_if_get_cond(If); + + Value Predicate = create(Cond); + + bool hasElse = isl_ast_node_if_has_else(If); + auto ifOp = b.create(loc, TypeRange(), Predicate, + /*addThenBlock=*/true, + /*addElseBlock=*/hasElse); + + OpBuilder::InsertionGuard g(b); + b.setInsertionPointToStart(&ifOp.getThenRegion().front()); + b.create(loc); + b.setInsertionPointToStart(&ifOp.getThenRegion().front()); + create(isl_ast_node_if_get_then(If)); + + if (hasElse) { + b.setInsertionPointToStart(&ifOp.getElseRegion().front()); + b.create(loc); + b.setInsertionPointToStart(&ifOp.getElseRegion().front()); + create(isl_ast_node_if_get_else(If)); + } + + isl_ast_node_free(If); + } + + void createBlock(__isl_keep isl_ast_node *Block) { + ISL_DEBUG("Building Block:\n", isl_ast_node_dump(Block)); + isl_ast_node_list *List = isl_ast_node_block_get_children(Block); + + for (int i = 0; i < isl_ast_node_list_n_ast_node(List); ++i) + create(isl_ast_node_list_get_ast_node(List, i)); + + isl_ast_node_free(Block); + isl_ast_node_list_free(List); + } + + isl::ast_expr getUpperBound(isl::ast_node_for For, + arith::CmpIPredicate &Predicate) { + isl::ast_expr Cond = For.cond(); + isl::ast_expr Iterator = For.iterator(); + // The isl code generation can generate arbitrary expressions to check if + // the upper bound of a loop is reached, but it provides an option to + // enforce 'atomic' upper bounds. An 'atomic upper bound is always of the + // form iv <= expr, where expr is an (arbitrary) expression not containing + // iv. + // + // We currently only support atomic upper bounds for ease of codegen + // + // This is needed for parallel loops but maybe we can weaken the requirement + // for sequential loops if needed + assert(isl_ast_expr_get_type(Cond.get()) == isl_ast_expr_op && + "conditional expression is not an atomic upper bound"); + + isl_ast_op_type OpType = isl_ast_expr_get_op_type(Cond.get()); + + switch (OpType) { + case isl_ast_op_le: + Predicate = arith::CmpIPredicate::sle; + break; + case isl_ast_op_lt: + Predicate = arith::CmpIPredicate::slt; + break; + default: + llvm_unreachable("Unexpected comparison type in loop condition"); + } + + isl::ast_expr Arg0 = Cond.get_op_arg(0); + + assert(isl_ast_expr_get_type(Arg0.get()) == isl_ast_expr_id && + "conditional expression is not an atomic upper bound"); + + isl::id UBID = Arg0.get_id(); + + assert(isl_ast_expr_get_type(Iterator.get()) == isl_ast_expr_id && + "Could not get the iterator"); + + isl::id IteratorID = Iterator.get_id(); + + assert(UBID.get() == IteratorID.get() && + "conditional expression is not an atomic upper bound"); + + return Cond.get_op_arg(1); + } + + template void convertToIndex(Ts &&...args) { + SmallVector Args({&args...}); + for (unsigned I = 0; I < Args.size(); I++) { + Type Ty = Args[I]->getType(); + if (!Ty.isa()) { + *Args[I] = + b.create(loc, b.getIndexType(), *Args[I]); + } + } + } + + template Type convertToMaxWidth(Ts &&...args) { + SmallVector Args({&args...}); + if (llvm::all_of(Args, + [&](Value *V) { return V->getType().isa(); })) + return Args[0]->getType(); + Type MaxTypeI = Args[0]->getType(); + IntegerType MaxType; + if (MaxTypeI.isa()) + // TODO This is temporary and we should get the target system index here + MaxType = b.getI64Type(); + else + MaxType = MaxTypeI.cast(); + unsigned MaxWidth = MaxType.getWidth(); + for (unsigned I = 0; I < Args.size(); I++) { + Type Ty = Args[I]->getType(); + if (Ty.isa()) + // TODO This is temporary and we should get the target system index here + Ty = b.getI64Type(); + if (Ty.cast().getWidth() > MaxWidth) { + MaxType = Ty.cast(); + MaxWidth = MaxType.getWidth(); + } + } + for (unsigned I = 0; I < Args.size(); I++) { + Type Ty = Args[I]->getType(); + if (Ty.isa()) { + *Args[I] = b.create(loc, MaxType, *Args[I]); + } else if (Ty != MaxType) { + *Args[I] = b.create(loc, MaxType, *Args[I]); + } + } + return MaxType; + } + + template + void createFor(__isl_take isl_ast_node *For) { + ISL_DEBUG("Building For:\n", isl_ast_node_dump(For)); + isl_ast_node *Body = isl_ast_node_for_get_body(For); + isl_ast_expr *Init = isl_ast_node_for_get_init(For); + isl_ast_expr *Inc = isl_ast_node_for_get_inc(For); + isl_ast_expr *Iterator = isl_ast_node_for_get_iterator(For); + isl_id *IteratorID = isl_ast_expr_get_id(Iterator); + arith::CmpIPredicate Predicate; + isl_ast_expr *UB = + getUpperBound(isl::manage_copy(For).as(), Predicate) + .release(); + + Value ValueLB = create(Init); + Value ValueUB = create(UB); + Value ValueInc = create(Inc); + convertToMaxWidth(ValueLB, ValueUB, ValueInc); + + if (Predicate == arith::CmpIPredicate::sle) + ValueUB = b.create( + loc, ValueUB, + b.create(loc, 1, ValueUB.getType())); + + // scf::ParallelOp only supports index as bounds + if constexpr (std::is_same::value) { + convertToIndex(ValueLB, ValueUB, ValueInc); + } + + auto forOp = b.create(loc, ValueLB, ValueUB, ValueInc); + + if constexpr (std::is_same::value) { + IDToValue[IteratorID] = forOp.getInductionVar(); + } else if constexpr (std::is_same::value) { + IDToValue[IteratorID] = forOp.getInductionVars()[0]; + } else { + // static_assert(0); + llvm_unreachable("?"); + } + + OpBuilder::InsertionGuard g(b); + b.setInsertionPointToStart(forOp.getBody()); + create(Body); + + isl_ast_expr_free(Iterator); + isl_id_free(IteratorID); + isl_ast_node_free(For); + } + + void create(__isl_take isl_ast_node *Node) { + switch (isl_ast_node_get_type(Node)) { + case isl_ast_node_error: + llvm_unreachable("code generation error"); + case isl_ast_node_mark: + createMark(Node); + return; + case isl_ast_node_for: + createFor(Node); + return; + case isl_ast_node_if: + createIf(Node); + return; + case isl_ast_node_user: + createUser(Node); + return; + case isl_ast_node_block: + createBlock(Node); + return; + } + + llvm_unreachable("Unknown isl_ast_node type"); + } + + void mapParams(__isl_take isl_union_set *domain) { + isl_space *space = isl_union_set_get_space(domain); + + int nparams = isl_space_dim(space, isl_dim_param); + for (int i = 0; i < nparams; i++) { + isl_id *Id = isl_space_get_dim_id(space, isl_dim_param, i); + const char *paramName = isl_id_get_name(Id); + Value V = scop.symbolTable[paramName]; + IDToValue[Id] = funcMapping.lookup(V); + isl_id_free(Id); + } + isl_space_free(space); + isl_union_set_free(domain); + } +}; +} // namespace polymer + +func::FuncOp IslScop::applySchedule(isl_schedule *newSchedule, + func::FuncOp originalFunc) { + IRMapping oldToNewMapping; + OpBuilder moduleBuilder(originalFunc); + func::FuncOp f = + cast(moduleBuilder.clone(*originalFunc, oldToNewMapping)); + + assert(f.getFunctionBody().getBlocks().size() == 1); + + // Cleanup body. Leave only scratchpad allocations and tarminator. + // TODO is there anything else we need to keep? + Operation *op = &f.getFunctionBody().front().front(); + while (true) { + if (auto alloca = dyn_cast(op)) { + assert(alloca->getAttr("scop.scratchpad")); + op = op->getNextNode(); + continue; + } + auto next = op->getNextNode(); + if (!next) + break; + // TODO should check it is a stmt call and not some random one + assert(isa(op->getDialect()) || + isa(op)); + op->erase(); + op = next; + } + + // TODO we also need to allocate new arrays which may have been introduced, + // see polly::NodeBuilder::allocateNewArrays, buildAliasScopes + + OpBuilder b(f.getFunctionBody().front().getTerminator()); + + LLVM_DEBUG({ + llvm::dbgs() << "Applying new schedule to scop:\n"; + isl_schedule_dump(newSchedule); + }); + isl_union_set *domain = isl_schedule_get_domain(newSchedule); + isl_ast_build *build = isl_ast_build_alloc(ctx); + IslMLIRBuilder bc = {b, oldToNewMapping, *this}; + isl_ast_node *node = + isl_ast_build_node_from_schedule(build, isl_schedule_copy(newSchedule)); + + bc.mapParams(domain); + bc.create(node); + LLVM_DEBUG(llvm::dbgs() << f << "\n"); + + isl_ast_build_free(build); + isl_schedule_free(newSchedule); + + return f; +} diff --git a/tools/polymer/lib/Support/OslScop.cc b/tools/polymer/lib/Support/OslScop.cc index 721bf16dd0fe..b73f2a659313 100644 --- a/tools/polymer/lib/Support/OslScop.cc +++ b/tools/polymer/lib/Support/OslScop.cc @@ -28,8 +28,11 @@ #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" +#include "isl/ctx.h" #include +#include + using namespace polymer; using namespace mlir; using namespace llvm; diff --git a/tools/polymer/lib/Support/OslScopStmtOpSet.cc b/tools/polymer/lib/Support/OslScopStmtOpSet.cc index b3fe689f1b06..88250615c760 100644 --- a/tools/polymer/lib/Support/OslScopStmtOpSet.cc +++ b/tools/polymer/lib/Support/OslScopStmtOpSet.cc @@ -21,7 +21,7 @@ using namespace llvm; using namespace mlir; using namespace polymer; -void OslScopStmtOpSet::insert(mlir::Operation *op) { +void ScopStmtOpSet::insert(mlir::Operation *op) { opSet.insert(op); if (isa(op)) { assert(!storeOp && @@ -30,7 +30,7 @@ void OslScopStmtOpSet::insert(mlir::Operation *op) { } } -LogicalResult OslScopStmtOpSet::getEnclosingOps( +LogicalResult ScopStmtOpSet::getEnclosingOps( SmallVectorImpl &enclosingOps) { SmallVector ops; SmallPtrSet visited; @@ -51,13 +51,13 @@ LogicalResult OslScopStmtOpSet::getEnclosingOps( } LogicalResult -OslScopStmtOpSet::getDomain(affine::FlatAffineValueConstraints &domain, - SmallVectorImpl &enclosingOps) { +ScopStmtOpSet::getDomain(affine::FlatAffineValueConstraints &domain, + SmallVectorImpl &enclosingOps) { return getIndexSet(enclosingOps, &domain); } LogicalResult -OslScopStmtOpSet::getDomain(affine::FlatAffineValueConstraints &domain) { +ScopStmtOpSet::getDomain(affine::FlatAffineValueConstraints &domain) { SmallVector enclosingOps; if (failed(getEnclosingOps(enclosingOps))) return failure(); diff --git a/tools/polymer/lib/Support/OslSymbolTable.cc b/tools/polymer/lib/Support/OslSymbolTable.cc index 20c8d5c30e4c..6443682078a4 100644 --- a/tools/polymer/lib/Support/OslSymbolTable.cc +++ b/tools/polymer/lib/Support/OslSymbolTable.cc @@ -16,7 +16,7 @@ using namespace llvm; namespace polymer { -Value OslSymbolTable::getValue(StringRef key) { +Value PolymerSymbolTable::getValue(StringRef key) { // Key is a loop IV. if (nameToLoopIV.find(key) != nameToLoopIV.end()) return nameToLoopIV.lookup(key); @@ -27,14 +27,14 @@ Value OslSymbolTable::getValue(StringRef key) { return nullptr; } -OslSymbolTable::OpSet OslSymbolTable::getOpSet(StringRef key) { +PolymerSymbolTable::OpSet PolymerSymbolTable::getOpSet(StringRef key) { // If key corresponds to an Op of a statement. assert(nameToStmtOpSet.find(key) != nameToStmtOpSet.end() && "Key is not found."); return nameToStmtOpSet.lookup(key); } -void OslSymbolTable::setValue(StringRef key, Value val, SymbolType type) { +void PolymerSymbolTable::setValue(StringRef key, Value val, SymbolType type) { switch (type) { case LoopIV: nameToLoopIV[key] = val; @@ -47,7 +47,7 @@ void OslSymbolTable::setValue(StringRef key, Value val, SymbolType type) { } } -void OslSymbolTable::setOpSet(StringRef key, OpSet val, SymbolType type) { +void PolymerSymbolTable::setOpSet(StringRef key, OpSet val, SymbolType type) { switch (type) { case StmtOpSet: nameToStmtOpSet[key] = val; @@ -57,7 +57,7 @@ void OslSymbolTable::setOpSet(StringRef key, OpSet val, SymbolType type) { } } -unsigned OslSymbolTable::getNumValues(SymbolType type) { +unsigned PolymerSymbolTable::getNumValues(SymbolType type) { switch (type) { case LoopIV: return nameToLoopIV.size(); @@ -68,7 +68,7 @@ unsigned OslSymbolTable::getNumValues(SymbolType type) { } } -unsigned OslSymbolTable::getNumOpSets(SymbolType type) { +unsigned PolymerSymbolTable::getNumOpSets(SymbolType type) { switch (type) { case StmtOpSet: return nameToStmtOpSet.size(); @@ -77,7 +77,7 @@ unsigned OslSymbolTable::getNumOpSets(SymbolType type) { } } -void OslSymbolTable::getValueSymbols(SmallVectorImpl &symbols) { +void PolymerSymbolTable::getValueSymbols(SmallVectorImpl &symbols) { symbols.reserve(getNumValues(Memref) + getNumValues(LoopIV)); for (auto &it : nameToLoopIV) @@ -85,7 +85,7 @@ void OslSymbolTable::getValueSymbols(SmallVectorImpl &symbols) { for (auto &it : nameToMemref) symbols.push_back(it.first()); } -void OslSymbolTable::getOpSetSymbols(SmallVectorImpl &symbols) { +void PolymerSymbolTable::getOpSetSymbols(SmallVectorImpl &symbols) { symbols.reserve(getNumOpSets(StmtOpSet)); for (auto &it : nameToStmtOpSet) diff --git a/tools/polymer/lib/Support/ScatteringUtils.cc b/tools/polymer/lib/Support/ScatteringUtils.cc index 2deb1d1a7fba..43e549dae9e7 100644 --- a/tools/polymer/lib/Support/ScatteringUtils.cc +++ b/tools/polymer/lib/Support/ScatteringUtils.cc @@ -9,9 +9,13 @@ #include #include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/IR/Operation.h" +#include "mlir/IR/SymbolTable.h" #include "mlir/IR/Value.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" using namespace polymer; using namespace mlir; diff --git a/tools/polymer/lib/Support/Utils.cc b/tools/polymer/lib/Support/Utils.cc index 87f16118fc9d..99c612e54659 100644 --- a/tools/polymer/lib/Support/Utils.cc +++ b/tools/polymer/lib/Support/Utils.cc @@ -5,19 +5,24 @@ //===----------------------------------------------------------------------===// #include "polymer/Support/Utils.h" +#include "polymer/Support/PolymerUtils.h" #include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/IR/Block.h" #include "mlir/IR/Builders.h" #include "mlir/IR/Operation.h" #include "mlir/IR/Value.h" #include "llvm/ADT/SetVector.h" +#include "llvm/Support/Debug.h" using namespace polymer; using namespace mlir; using namespace llvm; +#define DEBUG_TYPE "polymer-utils" + void polymer::inferBlockArgs(Block *block, llvm::SetVector &args) { // Any value being used will be added to the set first. block->walk([&](Operation *op) { @@ -36,3 +41,28 @@ void polymer::inferBlockArgs(Block *block, llvm::SetVector &args) { args.remove(blkArg); }); } + +void polymer::dedupIndexCast(mlir::func::FuncOp f) { + if (f.getBlocks().empty()) + return; + + Block &entry = f.getBlocks().front(); + llvm::MapVector argToCast; + SmallVector toErase; + for (auto &op : entry) { + if (auto indexCast = dyn_cast(&op)) { + auto arg = dyn_cast(indexCast.getOperand()); + if (argToCast.count(arg)) { + LLVM_DEBUG(llvm::dbgs() + << "Found duplicated index_cast: " << indexCast << '\n'); + indexCast.replaceAllUsesWith(argToCast.lookup(arg)); + toErase.push_back(indexCast); + } else { + argToCast[arg] = indexCast; + } + } + } + + for (auto op : toErase) + op->erase(); +} diff --git a/tools/polymer/lib/Target/CMakeLists.txt b/tools/polymer/lib/Target/CMakeLists.txt index fd4779781d97..82a7263a7b13 100644 --- a/tools/polymer/lib/Target/CMakeLists.txt +++ b/tools/polymer/lib/Target/CMakeLists.txt @@ -1,19 +1,40 @@ -add_mlir_translation_library(PolymerTargetOpenScop - OpenScop/ConvertFromOpenScop.cc - OpenScop/ConvertToOpenScop.cc +if(POLYGEIST_POLYMER_ENABLE_PLUTO) + add_mlir_translation_library(PolymerTargetOpenScop + OpenScop/ConvertFromOpenScop.cc + OpenScop/ConvertToOpenScop.cc - ADDITIONAL_HEADER_DIRS - ${POLYMER_MAIN_INCLUDE_DIR}/polymer/Target/OpenScop + ADDITIONAL_HEADER_DIRS + ${POLYMER_MAIN_INCLUDE_DIR}/polymer/Target/OpenScop - LINK_COMPONENTS - Core - TransformUtils + LINK_COMPONENTS + Core + TransformUtils - LINK_LIBS PUBLIC - MLIRIR - MLIRAffineDialect - MLIRAffineUtils - MLIRSupport + LINK_LIBS PUBLIC + MLIRIR + MLIRAffineDialect + MLIRAffineUtils + MLIRSupport - PolymerSupport -) + PolymerSupport + ) +elseif(POLYGEIST_POLYMER_ENABLE_ISL) + add_mlir_translation_library(PolymerTargetISL + ISL/ConvertToISL.cc + + ADDITIONAL_HEADER_DIRS + ${POLYMER_MAIN_INCLUDE_DIR}/polymer/Target/ISL + + LINK_COMPONENTS + Core + TransformUtils + + LINK_LIBS PUBLIC + MLIRIR + MLIRAffineDialect + MLIRAffineUtils + MLIRSupport + + PolymerSupport + ) +endif() diff --git a/tools/polymer/lib/Target/ISL/ConvertToISL.cc b/tools/polymer/lib/Target/ISL/ConvertToISL.cc new file mode 100644 index 000000000000..14afc7903f71 --- /dev/null +++ b/tools/polymer/lib/Target/ISL/ConvertToISL.cc @@ -0,0 +1,309 @@ +//===- EmitOpenScop.cc ------------------------------------------*- C++ -*-===// +// +// This file implements the interfaces for emitting OpenScop representation from +// MLIR modules. +// +//===----------------------------------------------------------------------===// + +#include "polymer/Support/IslScop.h" +#include "polymer/Support/ScopStmt.h" +#include "polymer/Target/ISL.h" +#include "polymer/Transforms/ExtractScopStmt.h" + +#include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h" +#include "mlir/Dialect/Affine/Analysis/AffineStructures.h" +#include "mlir/Dialect/Affine/Analysis/LoopAnalysis.h" +#include "mlir/Dialect/Affine/Analysis/Utils.h" +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Affine/IR/AffineValueMap.h" +#include "mlir/Dialect/Affine/LoopUtils.h" +#include "mlir/Dialect/Affine/Utils.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/IRMapping.h" +#include "mlir/Interfaces/SideEffectInterfaces.h" +#include "mlir/Tools/mlir-translate/Translation.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include + +#include + +using namespace mlir; +using namespace mlir::func; +using namespace llvm; +using namespace polymer; + +#define DEBUG_TYPE "islscop" + +namespace { + +/// Build IslScop from FuncOp. +class IslScopBuilder { +public: + IslScopBuilder() {} + + /// Build a scop from a common FuncOp. + std::unique_ptr build(mlir::func::FuncOp f); + +private: + /// Find all statements that calls a scop.stmt. + void buildScopStmtMap(mlir::func::FuncOp f, + IslScop::ScopStmtNames *scopStmtNames, + IslScop::ScopStmtMap *scopStmtMap) const; + + /// Build the scop context. The domain of each scop stmt will be updated, by + /// merging and aligning its IDs with the context as well. + void buildScopContext(IslScop *scop, IslScop::ScopStmtMap *scopStmtMap, + affine::FlatAffineValueConstraints &ctx) const; +}; + +} // namespace + +/// Sometimes the domain generated might be malformed. It is always better to +/// inform this at an early stage. +static void sanityCheckDomain(affine::FlatAffineValueConstraints &dom) { + if (dom.isEmpty()) { + llvm::errs() << "A domain is found to be empty!"; + dom.dump(); + } +} + +/// Build IslScop from a given FuncOp. +std::unique_ptr IslScopBuilder::build(mlir::func::FuncOp f) { + + /// Context constraints. + affine::FlatAffineValueConstraints ctx; + + // Initialize a new Scop per FuncOp. The osl_scop object within it will be + // created. It doesn't contain any fields, and this may incur some problems, + // which the validate function won't discover, e.g., no context will cause + // segfault when printing scop. Please don't just return this object. + auto scop = std::make_unique(); + // Mapping between scop stmt names and their caller/callee op pairs. + IslScop::ScopStmtMap *scopStmtMap = scop->getScopStmtMap(); + auto *scopStmtNames = scop->getScopStmtNames(); + + // Find all caller/callee pairs in which the callee has the attribute of name + // SCOP_STMT_ATTR_NAME. + buildScopStmtMap(f, scopStmtNames, scopStmtMap); + if (scopStmtMap->empty()) + return nullptr; + + // Build context in it. + buildScopContext(scop.get(), scopStmtMap, ctx); + + scop->initializeSymbolTable(f, &ctx); + + // Counter for the statement inserted. + unsigned stmtId = 0; + for (const auto &scopStmtName : *scopStmtNames) { + const ScopStmt &stmt = scopStmtMap->find(scopStmtName)->second; + LLVM_DEBUG({ + dbgs() << "Adding relations to statement: \n"; + stmt.getCaller().dump(); + }); + + // Collet the domain + affine::FlatAffineValueConstraints domain = *stmt.getDomain(); + sanityCheckDomain(domain); + + LLVM_DEBUG({ + dbgs() << "Domain:\n"; + domain.dump(); + }); + + // Collect the enclosing ops. + llvm::SmallVector enclosingOps; + stmt.getEnclosingOps(enclosingOps); + // Get the callee. + mlir::func::FuncOp callee = stmt.getCallee(); + + LLVM_DEBUG({ + dbgs() << "Callee:\n"; + callee.dump(); + }); + + // Create a statement in IslScop and setup relations in it. + scop->createStatement(); + scop->addDomainRelation(stmtId, domain); + callee.walk([&](mlir::Operation *op) { + if (isa(op) || + isa(op)) { + LLVM_DEBUG(dbgs() << "Creating access relation for: " << *op << '\n'); + + bool isRead = isa(op); + affine::AffineValueMap vMap; + mlir::Value memref; + + stmt.getAccessMapAndMemRef(op, &vMap, &memref); + [[maybe_unused]] auto ret = + scop->addAccessRelation(stmtId, isRead, memref, vMap, domain); + assert(succeeded(ret)); + } + }); + + stmtId++; + } + + scop->buildSchedule(scop->getSequenceScheduleOpList( + &f.getBody().front().front(), &f.getBody().front().back())); + + return scop; +} + +/// Find all statements that calls a scop.stmt. +void IslScopBuilder::buildScopStmtMap(mlir::func::FuncOp f, + IslScop::ScopStmtNames *scopStmtNames, + IslScop::ScopStmtMap *scopStmtMap) const { + mlir::ModuleOp m = cast(f->getParentOp()); + + f.walk([&](mlir::Operation *op) { + if (mlir::func::CallOp caller = dyn_cast(op)) { + llvm::StringRef calleeName = caller.getCallee(); + mlir::func::FuncOp callee = + m.lookupSymbol(calleeName); + + // If the callee is of scop.stmt, we create a new instance in the map + if (callee->getAttr(SCOP_STMT_ATTR_NAME)) { + scopStmtNames->push_back(std::string(calleeName)); + scopStmtMap->insert( + std::make_pair(calleeName, ScopStmt(caller, callee))); + } + } + }); +} + +void IslScopBuilder::buildScopContext( + IslScop *scop, IslScop::ScopStmtMap *scopStmtMap, + affine::FlatAffineValueConstraints &ctx) const { + LLVM_DEBUG(dbgs() << "--- Building SCoP context ...\n"); + + // First initialize the symbols of the ctx by the order of arg number. + // This simply aims to make mergeAndAlignVarsWithOthers work. + SmallVector symbols; + for (const auto &it : *scopStmtMap) { + auto domain = it.second.getDomain(); + SmallVector syms; + domain->getValues(domain->getNumDimVars(), domain->getNumDimAndSymbolVars(), + &syms); + + for (Value sym : syms) { + // Find the insertion position. + auto it = symbols.begin(); + while (it != symbols.end()) { + auto lhs = it->cast(); + auto rhs = sym.cast(); + if (lhs.getArgNumber() >= rhs.getArgNumber()) + break; + ++it; + } + if (it == symbols.end() || *it != sym) + symbols.insert(it, sym); + } + } + + ctx = affine::FlatAffineValueConstraints(/*numDims=*/0, + /*numSymbols=*/symbols.size()); + ctx.setValues(0, symbols.size(), symbols); + + // Union with the domains of all Scop statements. We first merge and align the + // IDs of the context and the domain of the scop statement, and then append + // the constraints from the domain to the context. Note that we don't want to + // mess up with the original domain at this point. Trivial redundant + // constraints will be removed. + for (const auto &it : *scopStmtMap) { + affine::FlatAffineValueConstraints *domain = it.second.getDomain(); + affine::FlatAffineValueConstraints cst(*domain); + + LLVM_DEBUG(dbgs() << "Statement:\n"); + LLVM_DEBUG(it.second.getCaller().dump()); + LLVM_DEBUG(it.second.getCallee().dump()); + LLVM_DEBUG(dbgs() << "Target domain: \n"); + LLVM_DEBUG(domain->dump()); + + LLVM_DEBUG({ + dbgs() << "Domain values: \n"; + SmallVector values; + domain->getValues(0, domain->getNumDimAndSymbolVars(), &values); + for (Value value : values) + dbgs() << " * " << value << '\n'; + }); + + ctx.mergeAndAlignVarsWithOther(0, &cst); + ctx.append(cst); + ctx.removeRedundantConstraints(); + + LLVM_DEBUG(dbgs() << "Updated context: \n"); + LLVM_DEBUG(ctx.dump()); + + LLVM_DEBUG({ + dbgs() << "Context values: \n"; + SmallVector values; + ctx.getValues(0, ctx.getNumDimAndSymbolVars(), &values); + for (Value value : values) + dbgs() << " * " << value << '\n'; + }); + } + + // Then, create the single context relation in scop. + scop->addContextRelation(ctx); + + // Finally, given that ctx has all the parameters in it, we will make sure + // that each domain is aligned with them, i.e., every domain has the same + // parameter columns (Values & order). + SmallVector symValues; + ctx.getValues(ctx.getNumDimVars(), ctx.getNumDimAndSymbolVars(), &symValues); + + // Add and align domain SYMBOL columns. + for (const auto &it : *scopStmtMap) { + affine::FlatAffineValueConstraints *domain = it.second.getDomain(); + // For any symbol missing in the domain, add them directly to the end. + for (unsigned i = 0; i < ctx.getNumSymbolVars(); ++i) { + unsigned pos; + if (!domain->findVar(symValues[i], &pos)) // insert to the back + domain->appendSymbolVar(symValues[i]); + else + LLVM_DEBUG(dbgs() << "Found " << symValues[i] << '\n'); + } + + // Then do the aligning. + LLVM_DEBUG(domain->dump()); + for (unsigned i = 0; i < ctx.getNumSymbolVars(); i++) { + mlir::Value sym = symValues[i]; + unsigned pos; + domain->findVar(sym, &pos); + + unsigned posAsCtx = i + domain->getNumDimVars(); + LLVM_DEBUG(dbgs() << "Swapping " << posAsCtx << " " << pos << "\n"); + if (pos != posAsCtx) + domain->swapVar(posAsCtx, pos); + } + + // for (unsigned i = 0; i < ctx.getNumSymbolVars(); i++) { + // mlir::Value sym = symValues[i]; + // unsigned pos; + // // If the symbol can be found in the domain, we put it in the same + // // position as the ctx. + // if (domain->findVar(sym, &pos)) { + // if (pos != i + domain->getNumDimVars()) + // domain->swapVar(i + domain->getNumDimVars(), pos); + // } else { + // domain->insertSymbolId(i, sym); + // } + // } + } +} + +std::unique_ptr polymer::createIslFromFuncOp(mlir::func::FuncOp f) { + return IslScopBuilder().build(f); +} diff --git a/tools/polymer/lib/Target/OpenScop/ConvertFromOpenScop.cc b/tools/polymer/lib/Target/OpenScop/ConvertFromOpenScop.cc index 4bc3c4d7e769..d63c501985f8 100644 --- a/tools/polymer/lib/Target/OpenScop/ConvertFromOpenScop.cc +++ b/tools/polymer/lib/Target/OpenScop/ConvertFromOpenScop.cc @@ -59,7 +59,7 @@ typedef llvm::StringMap SymbolTable; /// TODO: manage the priviledge. class AffineExprBuilder { public: - AffineExprBuilder(MLIRContext *context, OslSymbolTable *symTable, + AffineExprBuilder(MLIRContext *context, PolymerSymbolTable *symTable, SymbolTable *symbolTable, OslScop *scop, CloogOptions *options) : b(context), context(context), scop(scop), symTable(symTable), @@ -95,7 +95,7 @@ class AffineExprBuilder { /// The OslScop of the whole program. OslScop *scop; /// TODO: keep only one of them - OslSymbolTable *symTable; + PolymerSymbolTable *symTable; SymbolTable *symbolTable; /// CloogOptions *options; @@ -427,7 +427,7 @@ namespace { /// Import MLIR code from the clast AST. class Importer { public: - Importer(MLIRContext *context, ModuleOp module, OslSymbolTable *symTable, + Importer(MLIRContext *context, ModuleOp module, PolymerSymbolTable *symTable, OslScop *scop, CloogOptions *options); LogicalResult processStmtList(clast_stmt *s); @@ -488,7 +488,7 @@ class Importer { /// The OpenScop object pointer. OslScop *scop; /// The symbol table for labels in the OpenScop input (to be deprecated). - OslSymbolTable *symTable; + PolymerSymbolTable *symTable; /// The symbol table that will be built on the fly. SymbolTable symbolTable; @@ -512,7 +512,7 @@ class Importer { } // namespace Importer::Importer(MLIRContext *context, ModuleOp module, - OslSymbolTable *symTable, OslScop *scop, + PolymerSymbolTable *symTable, OslScop *scop, CloogOptions *options) : b(context), context(context), module(module), scop(scop), symTable(symTable), options(options) { @@ -821,7 +821,7 @@ void Importer::createCalleeAndCallerArgs( Value memref = symTable->getValue(args[i]); if (!memref) { memref = entryBlock.addArgument(memType, b.getUnknownLoc()); - symTable->setValue(args[i], memref, OslSymbolTable::Memref); + symTable->setValue(args[i], memref, PolymerSymbolTable::Memref); } callerArgs.push_back(memref); i++; @@ -841,7 +841,7 @@ void Importer::createCalleeAndCallerArgs( // We should set the symbol table for args[i], otherwise we cannot // build a correct mapping from the original symbol table (only // args[i] exists in it). - symTable->setValue(args[i], iv, OslSymbolTable::LoopIV); + symTable->setValue(args[i], iv, PolymerSymbolTable::LoopIV); } else { llvm::errs() << "Cannot find the scatname " << newArgName << " as a valid loop IV.\n"; @@ -1270,7 +1270,7 @@ LogicalResult Importer::processStmt(clast_for *forStmt) { "affine.for should only have one block argument (iv)."); symTable->setValue(forStmt->iterator, entryBlock.getArgument(0), - OslSymbolTable::LoopIV); + PolymerSymbolTable::LoopIV); // Symbol table is mutable. // TODO: is there a better way to improve this? Not very safe. @@ -1487,9 +1487,11 @@ static void transformClastByPlutoProg(clast_stmt *root, const PlutoProg *prog, markParallel(root, prog, cloogOptions); } -mlir::Operation *polymer::createFuncOpFromOpenScop( - std::unique_ptr scop, ModuleOp module, OslSymbolTable &symTable, - MLIRContext *context, PlutoProg *prog, const char *dumpClastAfterPluto) { +mlir::Operation * +polymer::createFuncOpFromOpenScop(std::unique_ptr scop, + ModuleOp module, PolymerSymbolTable &symTable, + MLIRContext *context, PlutoProg *prog, + const char *dumpClastAfterPluto) { // TODO: turn these C struct into C++ classes. CloogState *state = cloog_state_malloc(); CloogOptions *options = cloog_options_malloc(state); @@ -1555,7 +1557,7 @@ polymer::translateOpenScopToModule(std::unique_ptr scop, OwningOpRef module(ModuleOp::create( FileLineColLoc::get(context, "", /*line=*/0, /*column=*/0))); - OslSymbolTable symTable; + PolymerSymbolTable symTable; if (!createFuncOpFromOpenScop(std::move(scop), module.get(), symTable, context)) return {}; diff --git a/tools/polymer/lib/Target/OpenScop/ConvertToOpenScop.cc b/tools/polymer/lib/Target/OpenScop/ConvertToOpenScop.cc index f111d0da0a1b..b3e0ede2e53a 100644 --- a/tools/polymer/lib/Target/OpenScop/ConvertToOpenScop.cc +++ b/tools/polymer/lib/Target/OpenScop/ConvertToOpenScop.cc @@ -329,7 +329,7 @@ void OslScopBuilder::buildScopContext( std::unique_ptr polymer::createOpenScopFromFuncOp(mlir::func::FuncOp f, - OslSymbolTable &symTable) { + PolymerSymbolTable &symTable) { return OslScopBuilder().build(f); } @@ -398,7 +398,7 @@ class ModuleEmitter : public OpenScopEmitterBase { LogicalResult ModuleEmitter::emitFuncOp( mlir::func::FuncOp func, llvm::SmallVectorImpl> &scops) { - OslSymbolTable symTable; + PolymerSymbolTable symTable; auto scop = createOpenScopFromFuncOp(func, symTable); if (scop) { scops.push_back(std::move(scop)); diff --git a/tools/polymer/lib/Transforms/CMakeLists.txt b/tools/polymer/lib/Transforms/CMakeLists.txt index 9e9f0b46a9e1..f852cdbabfe0 100644 --- a/tools/polymer/lib/Transforms/CMakeLists.txt +++ b/tools/polymer/lib/Transforms/CMakeLists.txt @@ -1,5 +1,15 @@ + +if(POLYGEIST_POLYMER_ENABLE_PLUTO) + set(POLYMER_TRANSFORM_SRC + PlutoTransform.cc + ) +elseif(POLYGEIST_POLYMER_ENABLE_ISL) + set(POLYMER_TRANSFORM_SRC + IslExternalTransform.cc + ) +endif() + add_mlir_conversion_library(PolymerTransforms - PlutoTransform.cc Reg2Mem.cc ExtractScopStmt.cc ScopStmtOpt.cc @@ -7,6 +17,7 @@ add_mlir_conversion_library(PolymerTransforms LoopExtract.cc FoldSCFIf.cc AnnotateScop.cc + "${POLYMER_TRANSFORM_SRC}" ADDITIONAL_HEADER_DIRS "${POLYMER_MAIN_INCLUDE_DIR}/polymer/Transforms" @@ -27,5 +38,5 @@ add_mlir_conversion_library(PolymerTransforms MLIRAffineTransforms PolymerSupport - PolymerTargetOpenScop + "${POLYMER_AVAIL_TARGETS}" ) diff --git a/tools/polymer/lib/Transforms/IslExternalTransform.cc b/tools/polymer/lib/Transforms/IslExternalTransform.cc new file mode 100644 index 000000000000..57431ac9557e --- /dev/null +++ b/tools/polymer/lib/Transforms/IslExternalTransform.cc @@ -0,0 +1,128 @@ + +#include "mlir/IR/Verifier.h" +#include "polymer/Support/IslScop.h" +#include "polymer/Support/ScopStmt.h" +#include "polymer/Target/ISL.h" +#include "polymer/Transforms/ExtractScopStmt.h" +#include "polymer/Transforms/PlutoTransform.h" + +#include "mlir/Conversion/AffineToStandard/AffineToStandard.h" +#include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h" +#include "mlir/Dialect/Affine/Analysis/AffineStructures.h" +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Affine/IR/AffineValueMap.h" +#include "mlir/Dialect/Affine/Utils.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/IRMapping.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/IR/Types.h" +#include "mlir/IR/Value.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Transforms/DialectConversion.h" +#include "mlir/Transforms/Passes.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +#include "isl/id.h" +#include "isl/id_to_id.h" +#include "isl/schedule.h" +#include "isl/schedule_node.h" +#include "isl/val.h" + +using namespace mlir; +using namespace polymer; + +using llvm::dbgs; + +#define DEBUG_TYPE "islexternal-opt" + +static llvm::cl::opt ClIslExternalDumpSchedules( + "islexternal-dump-schedules", + llvm::cl::desc("Directory to dump ISL schedules to")); + +static llvm::cl::opt ClIslExternalDumpAccesses( + "islexternal-dump-accesses", + llvm::cl::desc("Directory to dump ISL accesses to")); + +static llvm::cl::opt ClIslExternalImportSchedules( + "islexternal-import-schedules", + llvm::cl::desc("Directory to import ISL schedules from")); + +namespace polymer { + +mlir::func::FuncOp islexternalTransform(mlir::func::FuncOp f, + OpBuilder &rewriter) { + LLVM_DEBUG(dbgs() << "IslExternal transforming: \n"); + LLVM_DEBUG(f.dump()); + + StringRef funcName = f.getName(); + + std::unique_ptr scop = createIslFromFuncOp(f); + + std::error_code EC; + if (ClIslExternalDumpSchedules.getNumOccurrences() > 0) { + std::string fname = (ClIslExternalDumpSchedules + "/" + funcName).str(); + llvm::raw_fd_ostream ScheduleOut(fname, EC); + if (EC) { + llvm::errs() << "Can't open " << fname << "\n"; + abort(); + } + scop->dumpSchedule(ScheduleOut); + } + if (ClIslExternalDumpAccesses.getNumOccurrences() > 0) { + std::string fname = (ClIslExternalDumpAccesses + "/" + funcName).str(); + llvm::raw_fd_ostream AccessesOut(fname, EC); + if (EC) { + llvm::errs() << "Can't open " << fname << "\n"; + abort(); + } + scop->dumpAccesses(AccessesOut); + } + + isl_schedule *newSchedule = nullptr; + if (ClIslExternalImportSchedules.getNumOccurrences() == 0) { + // Do a round trip + newSchedule = isl_schedule_copy(scop->getSchedule()); + } else { + std::string fname = (ClIslExternalImportSchedules + "/" + funcName).str(); + auto ScheduleIn = llvm::MemoryBuffer::getFileAsStream(fname); + if (std::error_code EC = ScheduleIn.getError()) { + llvm::errs() << "Can't open " << fname << "\n"; + abort(); + } + newSchedule = + isl_schedule_read_from_str(isl_schedule_get_ctx(scop->getSchedule()), + ScheduleIn->get()->getBufferStart()); + if (!newSchedule) { + llvm::errs() << "Could not read valid isl schedule from " << fname + << "\n"; + abort(); + } + } + assert(newSchedule); + + mlir::func::FuncOp g = scop->applySchedule(newSchedule, f); + assert(mlir::verify(g).succeeded()); + + if (g) { + SmallVector argAttrs; + f.getAllArgAttrs(argAttrs); + g.setAllArgAttrs(argAttrs); + } + + return g; +} + +mlir::func::FuncOp plutoTransform(mlir::func::FuncOp f, + mlir::OpBuilder &rewriter, + std::string dumpClastAfterPluto, + bool parallelize = false, bool debug = false, + int cloogf = -1, int cloogl = -1, + bool diamondTiling = false) { + llvm_unreachable("not compiled with pluto support"); +} +void registerPlutoTransformPass() {} +} // namespace polymer diff --git a/tools/polymer/lib/Transforms/PlutoTransform.cc b/tools/polymer/lib/Transforms/PlutoTransform.cc index ff3f0d43bcbb..10fdafbed956 100644 --- a/tools/polymer/lib/Transforms/PlutoTransform.cc +++ b/tools/polymer/lib/Transforms/PlutoTransform.cc @@ -8,6 +8,7 @@ #include "polymer/Support/OslScop.h" #include "polymer/Support/OslScopStmtOpSet.h" #include "polymer/Support/OslSymbolTable.h" +#include "polymer/Support/PolymerUtils.h" #include "polymer/Support/ScopStmt.h" #include "polymer/Target/OpenScop.h" @@ -44,14 +45,13 @@ namespace polymer { /// TODO: transform options? mlir::func::FuncOp plutoTransform(mlir::func::FuncOp f, OpBuilder &rewriter, std::string dumpClastAfterPluto, - bool parallelize = false, bool debug = false, - int cloogf = -1, int cloogl = -1, - bool diamondTiling = false) { + bool parallelize, bool debug, int cloogf, + int cloogl, bool diamondTiling) { LLVM_DEBUG(dbgs() << "Pluto transforming: \n"); LLVM_DEBUG(f.dump()); PlutoContext *context = pluto_context_alloc(); - OslSymbolTable srcTable, dstTable; + PolymerSymbolTable srcTable, dstTable; std::unique_ptr scop = createOpenScopFromFuncOp(f, srcTable); if (!scop) @@ -250,33 +250,6 @@ struct PlutoParallelizePass } }; -namespace polymer { -void dedupIndexCast(func::FuncOp f) { - if (f.getBlocks().empty()) - return; - - Block &entry = f.getBlocks().front(); - llvm::MapVector argToCast; - SmallVector toErase; - for (auto &op : entry) { - if (auto indexCast = dyn_cast(&op)) { - auto arg = dyn_cast(indexCast.getOperand()); - if (argToCast.count(arg)) { - LLVM_DEBUG(dbgs() << "Found duplicated index_cast: " << indexCast - << '\n'); - indexCast.replaceAllUsesWith(argToCast.lookup(arg)); - toErase.push_back(indexCast); - } else { - argToCast[arg] = indexCast; - } - } - } - - for (auto op : toErase) - op->erase(); -} -} // namespace polymer - struct DedupIndexCastPass : public mlir::PassWrapper> { @@ -310,4 +283,8 @@ std::unique_ptr createDedupIndexCastPass() { std::unique_ptr createPlutoParallelizePass() { return std::make_unique(); } +mlir::func::FuncOp islexternalTransform(mlir::func::FuncOp f, + OpBuilder &rewriter) { + llvm_unreachable("not compiled with isl suport"); +} } // namespace polymer diff --git a/tools/polymer/test/CMakeLists.txt b/tools/polymer/test/CMakeLists.txt index 35336842b6f0..724dcfd74058 100644 --- a/tools/polymer/test/CMakeLists.txt +++ b/tools/polymer/test/CMakeLists.txt @@ -3,18 +3,25 @@ configure_lit_site_cfg( "${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py" MAIN_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py" - ) +) -set(POLYMER_TEST_DEPENDS - FileCheck count not - polymer-translate - polymer-opt +if(POLYGEIST_POLYMER_ENABLE_PLUTO) + set(POLYMER_TEST_DEPENDS + FileCheck count not + polymer-translate + polymer-opt + ) +elseif(POLYGEIST_POLYMER_ENABLE_ISL) + set(POLYMER_TEST_DEPENDS + FileCheck count not + polymer-opt ) +endif() add_lit_testsuite(check-polymer "Running the Polymer regression tests" ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${POLYMER_TEST_DEPENDS} - ) +) set_target_properties(check-polymer PROPERTIES FOLDER "Tests") add_lit_testsuites(POLYMER ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tools/polymer/tools/polymer-opt/CMakeLists.txt b/tools/polymer/tools/polymer-opt/CMakeLists.txt index d6369ddad0af..83abd2ec2098 100644 --- a/tools/polymer/tools/polymer-opt/CMakeLists.txt +++ b/tools/polymer/tools/polymer-opt/CMakeLists.txt @@ -1,3 +1,4 @@ + set(LLVM_LINK_COMPONENTS Support ) @@ -10,7 +11,7 @@ target_link_libraries(polymer-opt PRIVATE PolymerTransforms - PolymerTargetOpenScop + ${POLYMER_AVAIL_TARGETS} MLIRFuncDialect MLIRLLVMDialect @@ -28,4 +29,4 @@ target_link_libraries(polymer-opt MLIRAffineTransforms MLIRTranslateLib ${translation_libs} - ) +) diff --git a/tools/polymer/tools/polymer-translate/CMakeLists.txt b/tools/polymer/tools/polymer-translate/CMakeLists.txt index ee2291043783..a06a93c1224f 100644 --- a/tools/polymer/tools/polymer-translate/CMakeLists.txt +++ b/tools/polymer/tools/polymer-translate/CMakeLists.txt @@ -1,3 +1,4 @@ +if(POLYGEIST_POLYMER_ENABLE_PLUTO) set(LLVM_LINK_COMPONENTS Support ) @@ -28,3 +29,4 @@ target_link_libraries(polymer-translate MLIRTranslateLib MLIRToLLVMIRTranslationRegistration ) +endif()