diff options
Diffstat (limited to 'mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp')
-rw-r--r-- | mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp new file mode 100644 index 0000000..ab817b6 --- /dev/null +++ b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp @@ -0,0 +1,412 @@ +//===- OpenACCUtilsTest.cpp - Unit tests for OpenACC utilities -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/OpenACC/OpenACCUtils.h" +#include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/OpenACC/OpenACC.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/OwningOpRef.h" +#include "mlir/IR/Value.h" +#include "gtest/gtest.h" + +using namespace mlir; +using namespace mlir::acc; + +//===----------------------------------------------------------------------===// +// Test Fixture +//===----------------------------------------------------------------------===// + +class OpenACCUtilsTest : public ::testing::Test { +protected: + OpenACCUtilsTest() : b(&context), loc(UnknownLoc::get(&context)) { + context.loadDialect<acc::OpenACCDialect, arith::ArithDialect, + memref::MemRefDialect, func::FuncDialect>(); + } + + MLIRContext context; + OpBuilder b; + Location loc; +}; + +//===----------------------------------------------------------------------===// +// getEnclosingComputeOp Tests +//===----------------------------------------------------------------------===// + +TEST_F(OpenACCUtilsTest, getEnclosingComputeOpParallel) { + // Create a parallel op with a region + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + Region ¶llelRegion = parallelOp->getRegion(); + parallelRegion.emplaceBlock(); + + // Test that we can find the parallel op from its region + Operation *enclosingOp = getEnclosingComputeOp(parallelRegion); + EXPECT_EQ(enclosingOp, parallelOp.get()); +} + +TEST_F(OpenACCUtilsTest, getEnclosingComputeOpKernels) { + // Create a kernels op with a region + OwningOpRef<KernelsOp> kernelsOp = + KernelsOp::create(b, loc, TypeRange{}, ValueRange{}); + Region &kernelsRegion = kernelsOp->getRegion(); + kernelsRegion.emplaceBlock(); + + // Test that we can find the kernels op from its region + Operation *enclosingOp = getEnclosingComputeOp(kernelsRegion); + EXPECT_EQ(enclosingOp, kernelsOp.get()); +} + +TEST_F(OpenACCUtilsTest, getEnclosingComputeOpSerial) { + // Create a serial op with a region + OwningOpRef<SerialOp> serialOp = + SerialOp::create(b, loc, TypeRange{}, ValueRange{}); + Region &serialRegion = serialOp->getRegion(); + serialRegion.emplaceBlock(); + + // Test that we can find the serial op from its region + Operation *enclosingOp = getEnclosingComputeOp(serialRegion); + EXPECT_EQ(enclosingOp, serialOp.get()); +} + +TEST_F(OpenACCUtilsTest, getEnclosingComputeOpNested) { + // Create nested ops: parallel containing a loop op + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + Region ¶llelRegion = parallelOp->getRegion(); + Block *parallelBlock = ¶llelRegion.emplaceBlock(); + + OpBuilder::InsertionGuard guard(b); + b.setInsertionPointToStart(parallelBlock); + + // Create a loop op inside the parallel region + OwningOpRef<LoopOp> loopOp = + LoopOp::create(b, loc, TypeRange{}, ValueRange{}); + Region &loopRegion = loopOp->getRegion(); + loopRegion.emplaceBlock(); + + // Test that from the loop region, we find the parallel op (loop is not a + // compute op) + Operation *enclosingOp = getEnclosingComputeOp(loopRegion); + EXPECT_EQ(enclosingOp, parallelOp.get()); +} + +TEST_F(OpenACCUtilsTest, getEnclosingComputeOpNone) { + // Create a module with a region that's not inside a compute construct + OwningOpRef<ModuleOp> moduleOp = ModuleOp::create(loc); + Region &moduleRegion = moduleOp->getBodyRegion(); + + // Test that we get nullptr when there's no enclosing compute op + Operation *enclosingOp = getEnclosingComputeOp(moduleRegion); + EXPECT_EQ(enclosingOp, nullptr); +} + +//===----------------------------------------------------------------------===// +// isOnlyUsedByPrivateClauses Tests +//===----------------------------------------------------------------------===// + +TEST_F(OpenACCUtilsTest, isOnlyUsedByPrivateClausesTrue) { + // Create a value (memref) outside the compute region + auto memrefTy = MemRefType::get({10}, b.getI32Type()); + OwningOpRef<memref::AllocaOp> allocOp = + memref::AllocaOp::create(b, loc, memrefTy); + TypedValue<PointerLikeType> varPtr = + cast<TypedValue<PointerLikeType>>(allocOp->getResult()); + + // Create a parallel op with a region + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + Region ¶llelRegion = parallelOp->getRegion(); + Block *parallelBlock = ¶llelRegion.emplaceBlock(); + + OpBuilder::InsertionGuard guard(b); + b.setInsertionPointToStart(parallelBlock); + + // Create a private op using the value + OwningOpRef<PrivateOp> privateOp = PrivateOp::create( + b, loc, varPtr, /*structured=*/true, /*implicit=*/false); + + // Test that the value is only used by private clauses + EXPECT_TRUE(isOnlyUsedByPrivateClauses(varPtr, parallelRegion)); +} + +TEST_F(OpenACCUtilsTest, isOnlyUsedByPrivateClausesFalse) { + // Create a value (memref) outside the compute region + auto memrefTy = MemRefType::get({10}, b.getI32Type()); + OwningOpRef<memref::AllocaOp> allocOp = + memref::AllocaOp::create(b, loc, memrefTy); + TypedValue<PointerLikeType> varPtr = + cast<TypedValue<PointerLikeType>>(allocOp->getResult()); + + // Create a parallel op with a region + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + Region ¶llelRegion = parallelOp->getRegion(); + Block *parallelBlock = ¶llelRegion.emplaceBlock(); + + OpBuilder::InsertionGuard guard(b); + b.setInsertionPointToStart(parallelBlock); + + // Create a private op using the value + OwningOpRef<PrivateOp> privateOp = PrivateOp::create( + b, loc, varPtr, /*structured=*/true, /*implicit=*/false); + + // Also use the value in a function call (escape) + OwningOpRef<func::CallOp> callOp = func::CallOp::create( + b, loc, "some_func", TypeRange{}, ValueRange{varPtr}); + + // Test that the value is NOT only used by private clauses (it escapes via + // call) + EXPECT_FALSE(isOnlyUsedByPrivateClauses(varPtr, parallelRegion)); +} + +TEST_F(OpenACCUtilsTest, isOnlyUsedByPrivateClausesMultiple) { + // Create a value (memref) outside the compute region + auto memrefTy = MemRefType::get({10}, b.getI32Type()); + OwningOpRef<memref::AllocaOp> allocOp = + memref::AllocaOp::create(b, loc, memrefTy); + TypedValue<PointerLikeType> varPtr = + cast<TypedValue<PointerLikeType>>(allocOp->getResult()); + + // Create a parallel op with a region + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + Region ¶llelRegion = parallelOp->getRegion(); + Block *parallelBlock = ¶llelRegion.emplaceBlock(); + + OpBuilder::InsertionGuard guard(b); + b.setInsertionPointToStart(parallelBlock); + + // Create multiple private ops using the value + OwningOpRef<PrivateOp> privateOp1 = PrivateOp::create( + b, loc, varPtr, /*structured=*/true, /*implicit=*/false); + OwningOpRef<PrivateOp> privateOp2 = PrivateOp::create( + b, loc, varPtr, /*structured=*/true, /*implicit=*/false); + + // Test that the value is only used by private clauses even with multiple uses + EXPECT_TRUE(isOnlyUsedByPrivateClauses(varPtr, parallelRegion)); +} + +//===----------------------------------------------------------------------===// +// isOnlyUsedByReductionClauses Tests +//===----------------------------------------------------------------------===// + +TEST_F(OpenACCUtilsTest, isOnlyUsedByReductionClausesTrue) { + // Create a value (memref) outside the compute region + auto memrefTy = MemRefType::get({10}, b.getI32Type()); + OwningOpRef<memref::AllocaOp> allocOp = + memref::AllocaOp::create(b, loc, memrefTy); + TypedValue<PointerLikeType> varPtr = + cast<TypedValue<PointerLikeType>>(allocOp->getResult()); + + // Create a parallel op with a region + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + Region ¶llelRegion = parallelOp->getRegion(); + Block *parallelBlock = ¶llelRegion.emplaceBlock(); + + OpBuilder::InsertionGuard guard(b); + b.setInsertionPointToStart(parallelBlock); + + // Create a reduction op using the value + OwningOpRef<ReductionOp> reductionOp = ReductionOp::create( + b, loc, varPtr, /*structured=*/true, /*implicit=*/false); + + // Test that the value is only used by reduction clauses + EXPECT_TRUE(isOnlyUsedByReductionClauses(varPtr, parallelRegion)); +} + +TEST_F(OpenACCUtilsTest, isOnlyUsedByReductionClausesFalse) { + // Create a value (memref) outside the compute region + auto memrefTy = MemRefType::get({10}, b.getI32Type()); + OwningOpRef<memref::AllocaOp> allocOp = + memref::AllocaOp::create(b, loc, memrefTy); + TypedValue<PointerLikeType> varPtr = + cast<TypedValue<PointerLikeType>>(allocOp->getResult()); + + // Create a parallel op with a region + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + Region ¶llelRegion = parallelOp->getRegion(); + Block *parallelBlock = ¶llelRegion.emplaceBlock(); + + OpBuilder::InsertionGuard guard(b); + b.setInsertionPointToStart(parallelBlock); + + // Create a reduction op using the value + OwningOpRef<ReductionOp> reductionOp = ReductionOp::create( + b, loc, varPtr, /*structured=*/true, /*implicit=*/false); + + // Also use the value in a function call (escape) + OwningOpRef<func::CallOp> callOp = func::CallOp::create( + b, loc, "some_func", TypeRange{}, ValueRange{varPtr}); + + // Test that the value is NOT only used by reduction clauses (it escapes via + // call) + EXPECT_FALSE(isOnlyUsedByReductionClauses(varPtr, parallelRegion)); +} + +TEST_F(OpenACCUtilsTest, isOnlyUsedByReductionClausesMultiple) { + // Create a value (memref) outside the compute region + auto memrefTy = MemRefType::get({10}, b.getI32Type()); + OwningOpRef<memref::AllocaOp> allocOp = + memref::AllocaOp::create(b, loc, memrefTy); + TypedValue<PointerLikeType> varPtr = + cast<TypedValue<PointerLikeType>>(allocOp->getResult()); + + // Create a parallel op with a region + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + Region ¶llelRegion = parallelOp->getRegion(); + Block *parallelBlock = ¶llelRegion.emplaceBlock(); + + OpBuilder::InsertionGuard guard(b); + b.setInsertionPointToStart(parallelBlock); + + // Create multiple reduction ops using the value + OwningOpRef<ReductionOp> reductionOp1 = ReductionOp::create( + b, loc, varPtr, /*structured=*/true, /*implicit=*/false); + OwningOpRef<ReductionOp> reductionOp2 = ReductionOp::create( + b, loc, varPtr, /*structured=*/true, /*implicit=*/false); + + // Test that the value is only used by reduction clauses even with multiple + // uses + EXPECT_TRUE(isOnlyUsedByReductionClauses(varPtr, parallelRegion)); +} + +//===----------------------------------------------------------------------===// +// getDefaultAttr Tests +//===----------------------------------------------------------------------===// + +TEST_F(OpenACCUtilsTest, getDefaultAttrOnParallel) { + // Create a parallel op with a default attribute + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + parallelOp->setDefaultAttr(ClauseDefaultValue::None); + + // Test that we can retrieve the default attribute + std::optional<ClauseDefaultValue> defaultAttr = + getDefaultAttr(parallelOp.get()); + EXPECT_TRUE(defaultAttr.has_value()); + EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::None); +} + +TEST_F(OpenACCUtilsTest, getDefaultAttrOnKernels) { + // Create a kernels op with a default attribute + OwningOpRef<KernelsOp> kernelsOp = + KernelsOp::create(b, loc, TypeRange{}, ValueRange{}); + kernelsOp->setDefaultAttr(ClauseDefaultValue::Present); + + // Test that we can retrieve the default attribute + std::optional<ClauseDefaultValue> defaultAttr = + getDefaultAttr(kernelsOp.get()); + EXPECT_TRUE(defaultAttr.has_value()); + EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::Present); +} + +TEST_F(OpenACCUtilsTest, getDefaultAttrOnSerial) { + // Create a serial op with a default attribute + OwningOpRef<SerialOp> serialOp = + SerialOp::create(b, loc, TypeRange{}, ValueRange{}); + serialOp->setDefaultAttr(ClauseDefaultValue::None); + + // Test that we can retrieve the default attribute + std::optional<ClauseDefaultValue> defaultAttr = + getDefaultAttr(serialOp.get()); + EXPECT_TRUE(defaultAttr.has_value()); + EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::None); +} + +TEST_F(OpenACCUtilsTest, getDefaultAttrOnData) { + // Create a data op with a default attribute + OwningOpRef<DataOp> dataOp = + DataOp::create(b, loc, TypeRange{}, ValueRange{}); + dataOp->setDefaultAttr(ClauseDefaultValue::Present); + + // Test that we can retrieve the default attribute + std::optional<ClauseDefaultValue> defaultAttr = getDefaultAttr(dataOp.get()); + EXPECT_TRUE(defaultAttr.has_value()); + EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::Present); +} + +TEST_F(OpenACCUtilsTest, getDefaultAttrNone) { + // Create a parallel op without setting a default attribute + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + // Do not set default attribute + + // Test that we get std::nullopt when there's no default attribute + std::optional<ClauseDefaultValue> defaultAttr = + getDefaultAttr(parallelOp.get()); + EXPECT_FALSE(defaultAttr.has_value()); +} + +TEST_F(OpenACCUtilsTest, getDefaultAttrNearest) { + // Create a data op with a default attribute + OwningOpRef<DataOp> dataOp = + DataOp::create(b, loc, TypeRange{}, ValueRange{}); + dataOp->setDefaultAttr(ClauseDefaultValue::Present); + + Region &dataRegion = dataOp->getRegion(); + Block *dataBlock = &dataRegion.emplaceBlock(); + + OpBuilder::InsertionGuard guard(b); + b.setInsertionPointToStart(dataBlock); + + // Create a parallel op inside the data region with NO default attribute + OwningOpRef<ParallelOp> parallelOp = + ParallelOp::create(b, loc, TypeRange{}, ValueRange{}); + // Do not set default attribute on parallel op + + Region ¶llelRegion = parallelOp->getRegion(); + Block *parallelBlock = ¶llelRegion.emplaceBlock(); + + b.setInsertionPointToStart(parallelBlock); + + // Create a loop op inside the parallel region + OwningOpRef<LoopOp> loopOp = + LoopOp::create(b, loc, TypeRange{}, ValueRange{}); + + // Test that from the loop op, we find the nearest default attribute (from + // data op) + std::optional<ClauseDefaultValue> defaultAttr = getDefaultAttr(loopOp.get()); + EXPECT_TRUE(defaultAttr.has_value()); + EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::Present); +} + +//===----------------------------------------------------------------------===// +// getTypeCategory Tests +//===----------------------------------------------------------------------===// + +TEST_F(OpenACCUtilsTest, getTypeCategoryScalar) { + // Create a scalar memref (no dimensions) + auto scalarMemrefTy = MemRefType::get({}, b.getI32Type()); + OwningOpRef<memref::AllocaOp> allocOp = + memref::AllocaOp::create(b, loc, scalarMemrefTy); + Value varPtr = allocOp->getResult(); + + // Test that a scalar memref returns scalar category + VariableTypeCategory category = getTypeCategory(varPtr); + EXPECT_EQ(category, VariableTypeCategory::scalar); +} + +TEST_F(OpenACCUtilsTest, getTypeCategoryArray) { + // Create an array memref (with dimensions) + auto arrayMemrefTy = MemRefType::get({10}, b.getI32Type()); + OwningOpRef<memref::AllocaOp> allocOp = + memref::AllocaOp::create(b, loc, arrayMemrefTy); + Value varPtr = allocOp->getResult(); + + // Test that an array memref returns array category + VariableTypeCategory category = getTypeCategory(varPtr); + EXPECT_EQ(category, VariableTypeCategory::array); +} |