https://github.com/Meinersbur created https://github.com/llvm/llvm-project/pull/147069
Support for translating the operations introduced in #144785 to LLVM-IR. >From da2613d525deb4edcf0fac41e865ca0510c75210 Mon Sep 17 00:00:00 2001 From: Michael Kruse <llvm-proj...@meinersbur.de> Date: Fri, 4 Jul 2025 16:26:20 +0200 Subject: [PATCH] omp.canonical_loop and omp.unroll_heuristic lowering --- .../mlir/Target/LLVMIR/ModuleTranslation.h | 43 +++++ .../Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp | 10 + .../OpenMP/OpenMPToLLVMIRTranslation.cpp | 78 ++++++++ .../LLVMIR/openmp-cli-canonical_loop.mlir | 175 ++++++++++++++++++ .../LLVMIR/openmp-cli-unroll-heuristic01.mlir | 56 ++++++ .../LLVMIR/openmp-cli-unroll-heuristic02.mlir | 93 ++++++++++ 6 files changed, 455 insertions(+) create mode 100644 mlir/test/Target/LLVMIR/openmp-cli-canonical_loop.mlir create mode 100644 mlir/test/Target/LLVMIR/openmp-cli-unroll-heuristic01.mlir create mode 100644 mlir/test/Target/LLVMIR/openmp-cli-unroll-heuristic02.mlir diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h index 79e8bb6add0da..5d52cf3f04b6a 100644 --- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -15,6 +15,7 @@ #define MLIR_TARGET_LLVMIR_MODULETRANSLATION_H #include "mlir/Dialect/LLVMIR/LLVMInterfaces.h" +#include "mlir/Dialect/OpenMP/OpenMPDialect.h" #include "mlir/IR/Operation.h" #include "mlir/IR/SymbolTable.h" #include "mlir/IR/Value.h" @@ -24,6 +25,7 @@ #include "mlir/Target/LLVMIR/TypeToLLVM.h" #include "llvm/ADT/SetVector.h" +#include "llvm/Frontend/OpenMP/OMPIRBuilder.h" #include "llvm/IR/FPEnv.h" namespace llvm { @@ -108,6 +110,41 @@ class ModuleTranslation { return blockMapping.lookup(block); } + /// Find the LLVM-IR loop that represents an MLIR loop. + llvm::CanonicalLoopInfo *lookupOMPLoop(omp::NewCliOp mlir) const { + llvm::CanonicalLoopInfo *result = loopMapping.lookup(mlir); + assert(result && "attempt to get non-existing loop"); + return result; + } + + /// Find the LLVM-IR loop that represents an MLIR loop. + llvm::CanonicalLoopInfo *lookupOMPLoop(Value mlir) const { + return lookupOMPLoop(mlir.getDefiningOp<omp::NewCliOp>()); + } + + /// Mark an OpenMP loop as having been consumed. + void invalidateOmpLoop(omp::NewCliOp mlir) { loopMapping.erase(mlir); } + + /// Mark an OpenMP loop as having been consumed. + void invalidateOmpLoop(Value mlir) { + invalidateOmpLoop(mlir.getDefiningOp<omp::NewCliOp>()); + } + + /// Map an MLIR OpenMP dialect CanonicalLoopInfo to its lowered LLVM-IR + /// OpenMPIRBuilder CanonicalLoopInfo + void mapOmpLoop(omp::NewCliOp mlir, llvm::CanonicalLoopInfo *llvm) { + assert(llvm && "argument must be non-null"); + llvm::CanonicalLoopInfo *&cur = loopMapping[mlir]; + assert(cur == nullptr && "attempting to map a loop that is already mapped"); + cur = llvm; + } + + /// Map an MLIR OpenMP dialect CanonicalLoopInfo to its lowered LLVM-IR + /// OpenMPIRBuilder CanonicalLoopInfo + void mapOmpLoop(Value mlir, llvm::CanonicalLoopInfo *llvm) { + mapOmpLoop(mlir.getDefiningOp<omp::NewCliOp>(), llvm); + } + /// Stores the mapping between an MLIR operation with successors and a /// corresponding LLVM IR instruction. void mapBranch(Operation *mlir, llvm::Instruction *llvm) { @@ -381,6 +418,12 @@ class ModuleTranslation { DenseMap<Value, llvm::Value *> valueMapping; DenseMap<Block *, llvm::BasicBlock *> blockMapping; + /// List of not yet consumed MLIR loop handles (represented by an omp.new_cli + /// operation which creates a value of type CanonicalLoopInfoType) and their + /// LLVM-IR representation as CanonicalLoopInfo which is managed by the + /// OpenMPIRBuilder. + DenseMap<omp::NewCliOp, llvm::CanonicalLoopInfo *> loopMapping; + /// A mapping between MLIR LLVM dialect terminators and LLVM IR terminators /// they are converted to. This allows for connecting PHI nodes to the source /// values after all operations are converted. diff --git a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp index 7a0a7f86bc1e9..e77c4a0b94de9 100644 --- a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp +++ b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp @@ -42,6 +42,16 @@ template <typename T> struct OpenMPOpConversion : public ConvertOpToLLVMPattern<T> { using ConvertOpToLLVMPattern<T>::ConvertOpToLLVMPattern; + OpenMPOpConversion(LLVMTypeConverter &typeConverter, + PatternBenefit benefit = 1) + : ConvertOpToLLVMPattern<T>(typeConverter, benefit) { + // Operations using CanonicalLoopInfoType are lowered only by + // mlir::translateModuleToLLVMIR() using the OpenMPIRBuilder. Until then, + // the type and operations using it must be preserved. + typeConverter.addConversion( + [&](::mlir::omp::CanonicalLoopInfoType type) { return type; }); + } + LogicalResult matchAndRewrite(T op, typename T::Adaptor adaptor, ConversionPatternRewriter &rewriter) const override { diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp index 7b07243c5f843..6b54d957a30f3 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -3090,6 +3090,67 @@ convertOmpLoopNest(Operation &opInst, llvm::IRBuilderBase &builder, return success(); } +/// Convert an omp.canonical_loop to LLVM-IR +static LogicalResult +convertOmpCanonicalLoopOp(omp::CanonicalLoopOp op, llvm::IRBuilderBase &builder, + LLVM::ModuleTranslation &moduleTranslation) { + llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder(); + + llvm::OpenMPIRBuilder::LocationDescription loopLoc(builder); + Value loopIV = op.getInductionVar(); + Value loopTC = op.getTripCount(); + + llvm::Value *llvmTC = moduleTranslation.lookupValue(loopTC); + + llvm::Expected<llvm::CanonicalLoopInfo *> llvmOrError = + ompBuilder->createCanonicalLoop( + loopLoc, + [&](llvm::OpenMPIRBuilder::InsertPointTy ip, llvm::Value *llvmIV) { + // Register the mapping of MLIR induction variable to LLVM-IR + // induction variable + moduleTranslation.mapValue(loopIV, llvmIV); + + builder.restoreIP(ip); + llvm::Expected<llvm::BasicBlock *> bodyGenStatus = + convertOmpOpRegions(op.getRegion(), "omp.loop.region", builder, + moduleTranslation); + + return bodyGenStatus.takeError(); + }, + llvmTC, "omp.loop"); + if (!llvmOrError) + return op.emitError(llvm::toString(llvmOrError.takeError())); + + llvm::CanonicalLoopInfo *llvmCLI = *llvmOrError; + llvm::IRBuilderBase::InsertPoint afterIP = llvmCLI->getAfterIP(); + builder.restoreIP(afterIP); + + // Register the mapping of MLIR loop to LLVM-IR OpenMPIRBuilder loop + if (Value cli = op.getCli()) + moduleTranslation.mapOmpLoop(cli, llvmCLI); + + return success(); +} + +/// Apply a `#pragma omp unroll` / "!$omp unroll" transformation using the +/// OpenMPIRBuilder. +static LogicalResult +applyUnrollHeuristic(omp::UnrollHeuristicOp op, llvm::IRBuilderBase &builder, + LLVM::ModuleTranslation &moduleTranslation) { + llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder(); + + Value applyee = op.getApplyee(); + assert(applyee && "Loop to apply unrolling on required"); + + llvm::CanonicalLoopInfo *consBuilderCLI = + moduleTranslation.lookupOMPLoop(applyee); + llvm::OpenMPIRBuilder::LocationDescription loc(builder); + ompBuilder->unrollLoopHeuristic(loc.DL, consBuilderCLI); + + moduleTranslation.invalidateOmpLoop(applyee); + return success(); +} + /// Convert an Atomic Ordering attribute to llvm::AtomicOrdering. static llvm::AtomicOrdering convertAtomicOrdering(std::optional<omp::ClauseMemoryOrderKind> ao) { @@ -5961,6 +6022,23 @@ convertHostOrTargetOperation(Operation *op, llvm::IRBuilderBase &builder, // etc. and then discarded return success(); }) + .Case([&](omp::NewCliOp op) { + // Meta-operation: Doesn't do anything by itself, but used to + // identify a loop. + return success(); + }) + .Case([&](omp::CanonicalLoopOp op) { + return convertOmpCanonicalLoopOp(op, builder, moduleTranslation); + }) + .Case([&](omp::UnrollHeuristicOp op) { + // FIXME: Handling omp.unroll_heuristic as an executable requires + // that the generator (e.g. omp.canonical_loop) has been seen first. + // For construct that require all codegen to occur inside a callback + // (e.g. OpenMPIRBilder::createParallel), all codegen of that + // contained region including their transformations must occur at + // the omp.canonical_loop. + return applyUnrollHeuristic(op, builder, moduleTranslation); + }) .Default([&](Operation *inst) { return inst->emitError() << "not yet implemented: " << inst->getName(); diff --git a/mlir/test/Target/LLVMIR/openmp-cli-canonical_loop.mlir b/mlir/test/Target/LLVMIR/openmp-cli-canonical_loop.mlir new file mode 100644 index 0000000000000..9abef003d6183 --- /dev/null +++ b/mlir/test/Target/LLVMIR/openmp-cli-canonical_loop.mlir @@ -0,0 +1,175 @@ +// Test lowering of standalone omp.canonical_loop +// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s + +// CHECK-LABEL: define void @anon_loop( +// CHECK-SAME: ptr %[[ptr:.+]], +// CHECK-SAME: i32 %[[tc:.+]]) { +// CHECK-NEXT: br label %omp_omp.loop.preheader +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.preheader: +// CHECK-NEXT: br label %omp_omp.loop.header +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.header: +// CHECK-NEXT: %omp_omp.loop.iv = phi i32 [ 0, %omp_omp.loop.preheader ], [ %omp_omp.loop.next, %omp_omp.loop.inc ] +// CHECK-NEXT: br label %omp_omp.loop.cond +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.cond: +// CHECK-NEXT: %omp_omp.loop.cmp = icmp ult i32 %omp_omp.loop.iv, %[[tc]] +// CHECK-NEXT: br i1 %omp_omp.loop.cmp, label %omp_omp.loop.body, label %omp_omp.loop.exit +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.body: +// CHECK-NEXT: br label %omp.loop.region +// CHECK-EMPTY: +// CHECK-NEXT: omp.loop.region: +// CHECK-NEXT: store float 4.200000e+01, ptr %[[ptr]], align 4 +// CHECK-NEXT: br label %omp.region.cont +// CHECK-EMPTY: +// CHECK-NEXT: omp.region.cont: +// CHECK-NEXT: br label %omp_omp.loop.inc +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.inc: +// CHECK-NEXT: %omp_omp.loop.next = add nuw i32 %omp_omp.loop.iv, 1 +// CHECK-NEXT: br label %omp_omp.loop.header +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.exit: +// CHECK-NEXT: br label %omp_omp.loop.after +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.after: +// CHECK-NEXT: ret void +// CHECK-NEXT: } +llvm.func @anon_loop(%ptr: !llvm.ptr, %tc : i32) -> () { + omp.canonical_loop %iv : i32 in range(%tc) { + %val = llvm.mlir.constant(42.0 : f32) : f32 + llvm.store %val, %ptr : f32, !llvm.ptr + omp.terminator + } + llvm.return +} + + + +// CHECK-LABEL: define void @trivial_loop( +// CHECK-SAME: ptr %[[ptr:.+]], +// CHECK-SAME: i32 %[[tc:.+]]) { +// CHECK-NEXT: br label %omp_omp.loop.preheader +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.preheader: +// CHECK-NEXT: br label %omp_omp.loop.header +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.header: +// CHECK-NEXT: %omp_omp.loop.iv = phi i32 [ 0, %omp_omp.loop.preheader ], [ %omp_omp.loop.next, %omp_omp.loop.inc ] +// CHECK-NEXT: br label %omp_omp.loop.cond +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.cond: +// CHECK-NEXT: %omp_omp.loop.cmp = icmp ult i32 %omp_omp.loop.iv, %[[tc]] +// CHECK-NEXT: br i1 %omp_omp.loop.cmp, label %omp_omp.loop.body, label %omp_omp.loop.exit +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.body: +// CHECK-NEXT: br label %omp.loop.region +// CHECK-EMPTY: +// CHECK-NEXT: omp.loop.region: +// CHECK-NEXT: store float 4.200000e+01, ptr %[[ptr]], align 4 +// CHECK-NEXT: br label %omp.region.cont +// CHECK-EMPTY: +// CHECK-NEXT: omp.region.cont: +// CHECK-NEXT: br label %omp_omp.loop.inc +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.inc: +// CHECK-NEXT: %omp_omp.loop.next = add nuw i32 %omp_omp.loop.iv, 1 +// CHECK-NEXT: br label %omp_omp.loop.header +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.exit: +// CHECK-NEXT: br label %omp_omp.loop.after +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.after: +// CHECK-NEXT: ret void +// CHECK-NEXT: } +llvm.func @trivial_loop(%ptr: !llvm.ptr, %tc : i32) -> () { + %cli = omp.new_cli + omp.canonical_loop(%cli) %iv : i32 in range(%tc) { + %val = llvm.mlir.constant(42.0 : f32) : f32 + llvm.store %val, %ptr : f32, !llvm.ptr + omp.terminator + } + llvm.return +} + + +// CHECK-LABEL: define void @nested_loop( +// CHECK-SAME: ptr %[[ptr:.+]], i32 %[[outer_tc:.+]], i32 %[[inner_tc:.+]]) { +// CHECK-NEXT: br label %omp_omp.loop.preheader +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.preheader: +// CHECK-NEXT: br label %omp_omp.loop.header +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.header: +// CHECK-NEXT: %omp_omp.loop.iv = phi i32 [ 0, %omp_omp.loop.preheader ], [ %omp_omp.loop.next, %omp_omp.loop.inc ] +// CHECK-NEXT: br label %omp_omp.loop.cond +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.cond: +// CHECK-NEXT: %omp_omp.loop.cmp = icmp ult i32 %omp_omp.loop.iv, %[[outer_tc]] +// CHECK-NEXT: br i1 %omp_omp.loop.cmp, label %omp_omp.loop.body, label %omp_omp.loop.exit +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.body: +// CHECK-NEXT: br label %omp.loop.region +// CHECK-EMPTY: +// CHECK-NEXT: omp.loop.region: +// CHECK-NEXT: br label %omp_omp.loop.preheader1 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.preheader1: +// CHECK-NEXT: br label %omp_omp.loop.header2 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.header2: +// CHECK-NEXT: %omp_omp.loop.iv8 = phi i32 [ 0, %omp_omp.loop.preheader1 ], [ %omp_omp.loop.next10, %omp_omp.loop.inc5 ] +// CHECK-NEXT: br label %omp_omp.loop.cond3 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.cond3: +// CHECK-NEXT: %omp_omp.loop.cmp9 = icmp ult i32 %omp_omp.loop.iv8, %[[inner_tc]] +// CHECK-NEXT: br i1 %omp_omp.loop.cmp9, label %omp_omp.loop.body4, label %omp_omp.loop.exit6 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.body4: +// CHECK-NEXT: br label %omp.loop.region12 +// CHECK-EMPTY: +// CHECK-NEXT: omp.loop.region12: +// CHECK-NEXT: store float 4.200000e+01, ptr %[[ptr]], align 4 +// CHECK-NEXT: br label %omp.region.cont11 +// CHECK-EMPTY: +// CHECK-NEXT: omp.region.cont11: +// CHECK-NEXT: br label %omp_omp.loop.inc5 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.inc5: +// CHECK-NEXT: %omp_omp.loop.next10 = add nuw i32 %omp_omp.loop.iv8, 1 +// CHECK-NEXT: br label %omp_omp.loop.header2 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.exit6: +// CHECK-NEXT: br label %omp_omp.loop.after7 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.after7: +// CHECK-NEXT: br label %omp.region.cont +// CHECK-EMPTY: +// CHECK-NEXT: omp.region.cont: +// CHECK-NEXT: br label %omp_omp.loop.inc +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.inc: +// CHECK-NEXT: %omp_omp.loop.next = add nuw i32 %omp_omp.loop.iv, 1 +// CHECK-NEXT: br label %omp_omp.loop.header +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.exit: +// CHECK-NEXT: br label %omp_omp.loop.after +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.after: +// CHECK-NEXT: ret void +// CHECK-NEXT: } +llvm.func @nested_loop(%ptr: !llvm.ptr, %outer_tc : i32, %inner_tc : i32) -> () { + %outer_cli = omp.new_cli + %inner_cli = omp.new_cli + omp.canonical_loop(%outer_cli) %outer_iv : i32 in range(%outer_tc) { + omp.canonical_loop(%inner_cli) %inner_iv : i32 in range(%inner_tc) { + %val = llvm.mlir.constant(42.0 : f32) : f32 + llvm.store %val, %ptr : f32, !llvm.ptr + omp.terminator + } + omp.terminator + } + llvm.return +} diff --git a/mlir/test/Target/LLVMIR/openmp-cli-unroll-heuristic01.mlir b/mlir/test/Target/LLVMIR/openmp-cli-unroll-heuristic01.mlir new file mode 100644 index 0000000000000..0f0448e15f983 --- /dev/null +++ b/mlir/test/Target/LLVMIR/openmp-cli-unroll-heuristic01.mlir @@ -0,0 +1,56 @@ +// Test lowering of the omp.unroll_heuristic +// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s + + +// CHECK-LABEL: define void @unroll_heuristic_trivial_loop( +// CHECK-SAME: ptr %[[ptr:.+]], i32 %[[tc:.+]]) { +// CHECK-NEXT: br label %omp_omp.loop.preheader +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.preheader: +// CHECK-NEXT: br label %omp_omp.loop.header +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.header: +// CHECK-NEXT: %omp_omp.loop.iv = phi i32 [ 0, %omp_omp.loop.preheader ], [ %omp_omp.loop.next, %omp_omp.loop.inc ] +// CHECK-NEXT: br label %omp_omp.loop.cond +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.cond: +// CHECK-NEXT: %omp_omp.loop.cmp = icmp ult i32 %omp_omp.loop.iv, %[[tc]] +// CHECK-NEXT: br i1 %omp_omp.loop.cmp, label %omp_omp.loop.body, label %omp_omp.loop.exit +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.body: +// CHECK-NEXT: br label %omp.loop.region +// CHECK-EMPTY: +// CHECK-NEXT: omp.loop.region: +// CHECK-NEXT: store float 4.200000e+01, ptr %[[ptr]], align 4 +// CHECK-NEXT: br label %omp.region.cont +// CHECK-EMPTY: +// CHECK-NEXT: omp.region.cont: +// CHECK-NEXT: br label %omp_omp.loop.inc +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.inc: +// CHECK-NEXT: %omp_omp.loop.next = add nuw i32 %omp_omp.loop.iv, 1 +// CHECK-NEXT: br label %omp_omp.loop.header, !llvm.loop ![[$MD1:[0-9]+]] +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.exit: +// CHECK-NEXT: br label %omp_omp.loop.after +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.after: +// CHECK-NEXT: ret void +// CHECK-NEXT: } +llvm.func @unroll_heuristic_trivial_loop(%ptr: !llvm.ptr, %tc: i32) -> () { + %literal_cli = omp.new_cli + omp.canonical_loop(%literal_cli) %iv : i32 in range(%tc) { + %val = llvm.mlir.constant(42.0 : f32) : f32 + llvm.store %val, %ptr : f32, !llvm.ptr + omp.terminator + } + omp.unroll_heuristic(%literal_cli) + llvm.return +} + + +// Start of metadata +// CHECK-LABEL: !llvm.module.flags + +// CHECK: ![[$MD1]] = distinct !{![[$MD1]], ![[$MD2:[0-9]+]]} +// CHECK: ![[$MD2]] = !{!"llvm.loop.unroll.enable"} diff --git a/mlir/test/Target/LLVMIR/openmp-cli-unroll-heuristic02.mlir b/mlir/test/Target/LLVMIR/openmp-cli-unroll-heuristic02.mlir new file mode 100644 index 0000000000000..f82b4990e378e --- /dev/null +++ b/mlir/test/Target/LLVMIR/openmp-cli-unroll-heuristic02.mlir @@ -0,0 +1,93 @@ +// Test lowering of the omp.unroll_heuristic +// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s + + +// CHECK-LABEL: define void @unroll_heuristic_nested_loop( +// CHECK-SAME: ptr %[[ptr:.+]], i32 %[[outer_tc:.+]], i32 %[[inner_tc:.+]]) { +// CHECK-NEXT: br label %omp_omp.loop.preheader +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.preheader: +// CHECK-NEXT: br label %omp_omp.loop.header +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.header: +// CHECK-NEXT: %omp_omp.loop.iv = phi i32 [ 0, %omp_omp.loop.preheader ], [ %omp_omp.loop.next, %omp_omp.loop.inc ] +// CHECK-NEXT: br label %omp_omp.loop.cond +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.cond: +// CHECK-NEXT: %omp_omp.loop.cmp = icmp ult i32 %omp_omp.loop.iv, %[[outer_tc]] +// CHECK-NEXT: br i1 %omp_omp.loop.cmp, label %omp_omp.loop.body, label %omp_omp.loop.exit +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.body: +// CHECK-NEXT: br label %omp.loop.region +// CHECK-EMPTY: +// CHECK-NEXT: omp.loop.region: +// CHECK-NEXT: br label %omp_omp.loop.preheader1 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.preheader1: +// CHECK-NEXT: br label %omp_omp.loop.header2 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.header2: +// CHECK-NEXT: %omp_omp.loop.iv8 = phi i32 [ 0, %omp_omp.loop.preheader1 ], [ %omp_omp.loop.next10, %omp_omp.loop.inc5 ] +// CHECK-NEXT: br label %omp_omp.loop.cond3 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.cond3: +// CHECK-NEXT: %omp_omp.loop.cmp9 = icmp ult i32 %omp_omp.loop.iv8, %[[inner_tc]] +// CHECK-NEXT: br i1 %omp_omp.loop.cmp9, label %omp_omp.loop.body4, label %omp_omp.loop.exit6 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.body4: +// CHECK-NEXT: br label %omp.loop.region12 +// CHECK-EMPTY: +// CHECK-NEXT: omp.loop.region12: +// CHECK-NEXT: store float 4.200000e+01, ptr %[[ptr]], align 4 +// CHECK-NEXT: br label %omp.region.cont11 +// CHECK-EMPTY: +// CHECK-NEXT: omp.region.cont11: +// CHECK-NEXT: br label %omp_omp.loop.inc5 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.inc5: +// CHECK-NEXT: %omp_omp.loop.next10 = add nuw i32 %omp_omp.loop.iv8, 1 +// CHECK-NEXT: br label %omp_omp.loop.header2, !llvm.loop ![[$MD1:[0-9]+]] +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.exit6: +// CHECK-NEXT: br label %omp_omp.loop.after7 +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.after7: +// CHECK-NEXT: br label %omp.region.cont +// CHECK-EMPTY: +// CHECK-NEXT: omp.region.cont: +// CHECK-NEXT: br label %omp_omp.loop.inc +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.inc: +// CHECK-NEXT: %omp_omp.loop.next = add nuw i32 %omp_omp.loop.iv, 1 +// CHECK-NEXT: br label %omp_omp.loop.header, !llvm.loop ![[$MD3:[0-9]+]] +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.exit: +// CHECK-NEXT: br label %omp_omp.loop.after +// CHECK-EMPTY: +// CHECK-NEXT: omp_omp.loop.after: +// CHECK-NEXT: ret void +// CHECK-NEXT: } +llvm.func @unroll_heuristic_nested_loop(%ptr: !llvm.ptr, %outer_tc: i32, %inner_tc: i32) -> () { + %outer_cli = omp.new_cli + %inner_cli = omp.new_cli + omp.canonical_loop(%outer_cli) %outer_iv : i32 in range(%outer_tc) { + omp.canonical_loop(%inner_cli) %inner_iv : i32 in range(%inner_tc) { + %val = llvm.mlir.constant(42.0 : f32) : f32 + llvm.store %val, %ptr : f32, !llvm.ptr + omp.terminator + } + omp.terminator + } + omp.unroll_heuristic(%outer_cli) + omp.unroll_heuristic(%inner_cli) + llvm.return +} + + +// Start of metadata +// CHECK-LABEL: !llvm.module.flags + +// CHECK: ![[$MD1]] = distinct !{![[$MD1]], ![[$MD2:[0-9]+]]} +// CHECK: ![[$MD2]] = !{!"llvm.loop.unroll.enable"} +// CHECK: ![[$MD3]] = distinct !{![[$MD3]], ![[$MD2]]} + _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits