aboutsummaryrefslogtreecommitdiff
path: root/mlir/unittests/Dialect
diff options
context:
space:
mode:
Diffstat (limited to 'mlir/unittests/Dialect')
-rw-r--r--mlir/unittests/Dialect/OpenACC/CMakeLists.txt1
-rw-r--r--mlir/unittests/Dialect/OpenACC/OpenACCOpsInterfacesTest.cpp117
-rw-r--r--mlir/unittests/Dialect/OpenACC/OpenACCOpsTest.cpp4
-rw-r--r--mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp882
4 files changed, 1003 insertions, 1 deletions
diff --git a/mlir/unittests/Dialect/OpenACC/CMakeLists.txt b/mlir/unittests/Dialect/OpenACC/CMakeLists.txt
index 177c868..c8c2bb9 100644
--- a/mlir/unittests/Dialect/OpenACC/CMakeLists.txt
+++ b/mlir/unittests/Dialect/OpenACC/CMakeLists.txt
@@ -1,5 +1,6 @@
add_mlir_unittest(MLIROpenACCTests
OpenACCOpsTest.cpp
+ OpenACCOpsInterfacesTest.cpp
OpenACCUtilsTest.cpp
)
mlir_target_link_libraries(MLIROpenACCTests
diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCOpsInterfacesTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCOpsInterfacesTest.cpp
new file mode 100644
index 0000000..7d52ef31
--- /dev/null
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCOpsInterfacesTest.cpp
@@ -0,0 +1,117 @@
+//===- OpenACCOpsInterfacesTest.cpp - Unit tests for OpenACC interfaces --===//
+//
+// 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/MemRef/IR/MemRef.h"
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/MLIRContext.h"
+#include "mlir/IR/OwningOpRef.h"
+#include "gtest/gtest.h"
+
+using namespace mlir;
+using namespace mlir::acc;
+
+//===----------------------------------------------------------------------===//
+// Test Fixture
+//===----------------------------------------------------------------------===//
+
+class OpenACCOpsInterfacesTest : public ::testing::Test {
+protected:
+ OpenACCOpsInterfacesTest()
+ : context(), builder(&context), loc(UnknownLoc::get(&context)) {
+ context.loadDialect<acc::OpenACCDialect, memref::MemRefDialect>();
+ }
+
+ MLIRContext context;
+ OpBuilder builder;
+ Location loc;
+};
+
+//===----------------------------------------------------------------------===//
+// GlobalVariableOpInterface Tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(OpenACCOpsInterfacesTest, GlobalVariableOpInterfaceNonConstant) {
+ // Test that a non-constant global returns false for isConstant()
+
+ auto memrefType = MemRefType::get({10}, builder.getF32Type());
+ OwningOpRef<memref::GlobalOp> globalOp = memref::GlobalOp::create(
+ builder, loc,
+ /*sym_name=*/builder.getStringAttr("mutable_global"),
+ /*sym_visibility=*/builder.getStringAttr("private"),
+ /*type=*/TypeAttr::get(memrefType),
+ /*initial_value=*/Attribute(),
+ /*constant=*/UnitAttr(),
+ /*alignment=*/IntegerAttr());
+
+ auto globalVarIface =
+ dyn_cast<GlobalVariableOpInterface>(globalOp->getOperation());
+ ASSERT_TRUE(globalVarIface != nullptr);
+ EXPECT_FALSE(globalVarIface.isConstant());
+}
+
+TEST_F(OpenACCOpsInterfacesTest, GlobalVariableOpInterfaceConstant) {
+ // Test that a constant global returns true for isConstant()
+
+ auto memrefType = MemRefType::get({5}, builder.getI32Type());
+ OwningOpRef<memref::GlobalOp> constantGlobalOp = memref::GlobalOp::create(
+ builder, loc,
+ /*sym_name=*/builder.getStringAttr("constant_global"),
+ /*sym_visibility=*/builder.getStringAttr("public"),
+ /*type=*/TypeAttr::get(memrefType),
+ /*initial_value=*/Attribute(),
+ /*constant=*/builder.getUnitAttr(),
+ /*alignment=*/IntegerAttr());
+
+ auto globalVarIface =
+ dyn_cast<GlobalVariableOpInterface>(constantGlobalOp->getOperation());
+ ASSERT_TRUE(globalVarIface != nullptr);
+ EXPECT_TRUE(globalVarIface.isConstant());
+}
+
+TEST_F(OpenACCOpsInterfacesTest, GlobalVariableOpInterfaceInitRegion) {
+ // Test that memref::GlobalOp returns nullptr for getInitRegion()
+ // since it uses attributes for initialization, not regions
+
+ auto memrefType = MemRefType::get({10}, builder.getF32Type());
+ OwningOpRef<memref::GlobalOp> globalOp = memref::GlobalOp::create(
+ builder, loc,
+ /*sym_name=*/builder.getStringAttr("test_global"),
+ /*sym_visibility=*/builder.getStringAttr("private"),
+ /*type=*/TypeAttr::get(memrefType),
+ /*initial_value=*/Attribute(),
+ /*constant=*/UnitAttr(),
+ /*alignment=*/IntegerAttr());
+
+ auto globalVarIface =
+ dyn_cast<GlobalVariableOpInterface>(globalOp->getOperation());
+ ASSERT_TRUE(globalVarIface != nullptr);
+
+ // memref::GlobalOp doesn't have regions for initialization
+ EXPECT_EQ(globalVarIface.getInitRegion(), nullptr);
+}
+
+//===----------------------------------------------------------------------===//
+// AddressOfGlobalOpInterface Tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(OpenACCOpsInterfacesTest, AddressOfGlobalOpInterfaceGetSymbol) {
+ // Test that getSymbol() returns the correct symbol reference
+
+ auto memrefType = MemRefType::get({5}, builder.getI32Type());
+ const auto *symbolName = "test_global_symbol";
+
+ OwningOpRef<memref::GetGlobalOp> getGlobalOp = memref::GetGlobalOp::create(
+ builder, loc, memrefType, FlatSymbolRefAttr::get(&context, symbolName));
+
+ auto addrOfGlobalIface =
+ dyn_cast<AddressOfGlobalOpInterface>(getGlobalOp->getOperation());
+ ASSERT_TRUE(addrOfGlobalIface != nullptr);
+ EXPECT_EQ(addrOfGlobalIface.getSymbol().getLeafReference(), symbolName);
+}
diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCOpsTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCOpsTest.cpp
index 6ac9a87..d6203b9 100644
--- a/mlir/unittests/Dialect/OpenACC/OpenACCOpsTest.cpp
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCOpsTest.cpp
@@ -766,7 +766,9 @@ void testShortDataEntryOpBuildersMappableVar(OpBuilder &b, MLIRContext &context,
struct IntegerOpenACCMappableModel
: public mlir::acc::MappableType::ExternalModel<IntegerOpenACCMappableModel,
- IntegerType> {};
+ IntegerType> {
+ bool hasUnknownDimensions(mlir::Type type) const { return false; }
+};
TEST_F(OpenACCOpsTest, mappableTypeBuilderDataEntry) {
// First, set up the test by attaching MappableInterface to IntegerType.
diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
index 3fbbcc9..60d8732 100644
--- a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
@@ -485,3 +485,885 @@ TEST_F(OpenACCUtilsTest, getVariableNameFromCopyin) {
std::string varName = getVariableName(copyinOp->getAccVar());
EXPECT_EQ(varName, name);
}
+
+//===----------------------------------------------------------------------===//
+// getRecipeName Tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(OpenACCUtilsTest, getRecipeNamePrivateScalarMemref) {
+ // Create a scalar memref type
+ auto scalarMemrefTy = MemRefType::get({}, b.getI32Type());
+
+ // Test private recipe with scalar memref
+ std::string recipeName =
+ getRecipeName(RecipeKind::private_recipe, scalarMemrefTy);
+ EXPECT_EQ(recipeName, "privatization_memref_i32_");
+}
+
+TEST_F(OpenACCUtilsTest, getRecipeNameFirstprivateScalarMemref) {
+ // Create a scalar memref type
+ auto scalarMemrefTy = MemRefType::get({}, b.getF32Type());
+
+ // Test firstprivate recipe with scalar memref
+ std::string recipeName =
+ getRecipeName(RecipeKind::firstprivate_recipe, scalarMemrefTy);
+ EXPECT_EQ(recipeName, "firstprivatization_memref_f32_");
+}
+
+TEST_F(OpenACCUtilsTest, getRecipeNameReductionScalarMemref) {
+ // Create a scalar memref type
+ auto scalarMemrefTy = MemRefType::get({}, b.getI64Type());
+
+ // Test reduction recipe with scalar memref
+ std::string recipeName =
+ getRecipeName(RecipeKind::reduction_recipe, scalarMemrefTy);
+ EXPECT_EQ(recipeName, "reduction_memref_i64_");
+}
+
+TEST_F(OpenACCUtilsTest, getRecipeNamePrivate2DMemref) {
+ // Create a 2D memref type
+ auto memref2DTy = MemRefType::get({5, 10}, b.getF32Type());
+
+ // Test private recipe with 2D memref
+ std::string recipeName =
+ getRecipeName(RecipeKind::private_recipe, memref2DTy);
+ EXPECT_EQ(recipeName, "privatization_memref_5x10xf32_");
+}
+
+TEST_F(OpenACCUtilsTest, getRecipeNameFirstprivate2DMemref) {
+ // Create a 2D memref type
+ auto memref2DTy = MemRefType::get({8, 16}, b.getF64Type());
+
+ // Test firstprivate recipe with 2D memref
+ std::string recipeName =
+ getRecipeName(RecipeKind::firstprivate_recipe, memref2DTy);
+ EXPECT_EQ(recipeName, "firstprivatization_memref_8x16xf64_");
+}
+
+TEST_F(OpenACCUtilsTest, getRecipeNameReduction2DMemref) {
+ // Create a 2D memref type
+ auto memref2DTy = MemRefType::get({4, 8}, b.getI32Type());
+
+ // Test reduction recipe with 2D memref
+ std::string recipeName =
+ getRecipeName(RecipeKind::reduction_recipe, memref2DTy);
+ EXPECT_EQ(recipeName, "reduction_memref_4x8xi32_");
+}
+
+TEST_F(OpenACCUtilsTest, getRecipeNamePrivateDynamicMemref) {
+ // Create a memref with dynamic dimensions
+ auto dynamicMemrefTy =
+ MemRefType::get({ShapedType::kDynamic, 10}, b.getI32Type());
+
+ // Test private recipe with dynamic memref
+ std::string recipeName =
+ getRecipeName(RecipeKind::private_recipe, dynamicMemrefTy);
+ EXPECT_EQ(recipeName, "privatization_memref_Ux10xi32_");
+}
+
+TEST_F(OpenACCUtilsTest, getRecipeNamePrivateUnrankedMemref) {
+ // Create an unranked memref type
+ auto unrankedMemrefTy = UnrankedMemRefType::get(b.getI32Type(), 0);
+
+ // Test private recipe with unranked memref
+ std::string recipeName =
+ getRecipeName(RecipeKind::private_recipe, unrankedMemrefTy);
+ EXPECT_EQ(recipeName, "privatization_memref_Zxi32_");
+}
+
+//===----------------------------------------------------------------------===//
+// getBaseEntity Tests
+//===----------------------------------------------------------------------===//
+
+// Local implementation of PartialEntityAccessOpInterface for memref.subview.
+// This is implemented locally in the test rather than officially because memref
+// operations already have ViewLikeOpInterface, which serves a similar purpose
+// for walking through views to the base entity. This test demonstrates how
+// getBaseEntity() would work if the interface were attached to memref.subview.
+namespace {
+struct SubViewOpPartialEntityAccessOpInterface
+ : public acc::PartialEntityAccessOpInterface::ExternalModel<
+ SubViewOpPartialEntityAccessOpInterface, memref::SubViewOp> {
+ Value getBaseEntity(Operation *op) const {
+ auto subviewOp = cast<memref::SubViewOp>(op);
+ return subviewOp.getSource();
+ }
+
+ bool isCompleteView(Operation *op) const {
+ // For testing purposes, we'll consider it a partial view (return false).
+ // The real implementation would need to look at the offsets.
+ return false;
+ }
+};
+} // namespace
+
+TEST_F(OpenACCUtilsTest, getBaseEntityFromSubview) {
+ // Register the local interface implementation for memref.subview
+ memref::SubViewOp::attachInterface<SubViewOpPartialEntityAccessOpInterface>(
+ context);
+
+ // Create a base memref
+ auto memrefTy = MemRefType::get({10, 20}, b.getF32Type());
+ OwningOpRef<memref::AllocaOp> allocOp =
+ memref::AllocaOp::create(b, loc, memrefTy);
+ Value baseMemref = allocOp->getResult();
+
+ // Create a subview of the base memref with non-zero offsets
+ // This creates a 5x10 view starting at [2, 3] in the original 10x20 memref
+ SmallVector<OpFoldResult> offsets = {b.getIndexAttr(2), b.getIndexAttr(3)};
+ SmallVector<OpFoldResult> sizes = {b.getIndexAttr(5), b.getIndexAttr(10)};
+ SmallVector<OpFoldResult> strides = {b.getIndexAttr(1), b.getIndexAttr(1)};
+
+ OwningOpRef<memref::SubViewOp> subviewOp =
+ memref::SubViewOp::create(b, loc, baseMemref, offsets, sizes, strides);
+ Value subview = subviewOp->getResult();
+
+ // Test that getBaseEntity returns the base memref, not the subview
+ Value baseEntity = getBaseEntity(subview);
+ EXPECT_EQ(baseEntity, baseMemref);
+}
+
+TEST_F(OpenACCUtilsTest, getBaseEntityNoInterface) {
+ // Create a memref without the interface
+ auto memrefTy = MemRefType::get({10}, b.getI32Type());
+ OwningOpRef<memref::AllocaOp> allocOp =
+ memref::AllocaOp::create(b, loc, memrefTy);
+ Value varPtr = allocOp->getResult();
+
+ // Test that getBaseEntity returns the value itself when there's no interface
+ Value baseEntity = getBaseEntity(varPtr);
+ EXPECT_EQ(baseEntity, varPtr);
+}
+
+TEST_F(OpenACCUtilsTest, getBaseEntityChainedSubviews) {
+ // Register the local interface implementation for memref.subview
+ memref::SubViewOp::attachInterface<SubViewOpPartialEntityAccessOpInterface>(
+ context);
+
+ // Create a base memref
+ auto memrefTy = MemRefType::get({100, 200}, b.getI64Type());
+ OwningOpRef<memref::AllocaOp> allocOp =
+ memref::AllocaOp::create(b, loc, memrefTy);
+ Value baseMemref = allocOp->getResult();
+
+ // Create first subview
+ SmallVector<OpFoldResult> offsets1 = {b.getIndexAttr(10), b.getIndexAttr(20)};
+ SmallVector<OpFoldResult> sizes1 = {b.getIndexAttr(50), b.getIndexAttr(80)};
+ SmallVector<OpFoldResult> strides1 = {b.getIndexAttr(1), b.getIndexAttr(1)};
+
+ OwningOpRef<memref::SubViewOp> subview1Op =
+ memref::SubViewOp::create(b, loc, baseMemref, offsets1, sizes1, strides1);
+ Value subview1 = subview1Op->getResult();
+
+ // Create second subview (subview of subview)
+ SmallVector<OpFoldResult> offsets2 = {b.getIndexAttr(5), b.getIndexAttr(10)};
+ SmallVector<OpFoldResult> sizes2 = {b.getIndexAttr(20), b.getIndexAttr(30)};
+ SmallVector<OpFoldResult> strides2 = {b.getIndexAttr(1), b.getIndexAttr(1)};
+
+ OwningOpRef<memref::SubViewOp> subview2Op =
+ memref::SubViewOp::create(b, loc, subview1, offsets2, sizes2, strides2);
+ Value subview2 = subview2Op->getResult();
+
+ // Test that getBaseEntity on the nested subview returns the first subview
+ // (since our implementation returns the immediate source, not the ultimate
+ // base)
+ Value baseEntity = getBaseEntity(subview2);
+ EXPECT_EQ(baseEntity, subview1);
+
+ // Test that calling getBaseEntity again returns the original base
+ Value ultimateBase = getBaseEntity(baseEntity);
+ EXPECT_EQ(ultimateBase, baseMemref);
+}
+
+//===----------------------------------------------------------------------===//
+// isValidSymbolUse Tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(OpenACCUtilsTest, isValidSymbolUseNoDefiningOp) {
+ // Create a memref.get_global that references a non-existent global
+ auto memrefType = MemRefType::get({10}, b.getI32Type());
+ llvm::StringRef globalName = "nonexistent_global";
+ SymbolRefAttr nonExistentSymbol = SymbolRefAttr::get(&context, globalName);
+
+ OwningOpRef<memref::GetGlobalOp> getGlobalOp =
+ memref::GetGlobalOp::create(b, loc, memrefType, globalName);
+
+ Operation *definingOp = nullptr;
+ bool result =
+ isValidSymbolUse(getGlobalOp.get(), nonExistentSymbol, &definingOp);
+
+ EXPECT_FALSE(result);
+ EXPECT_EQ(definingOp, nullptr);
+}
+
+TEST_F(OpenACCUtilsTest, isValidSymbolUseRecipe) {
+ // Create a module to hold the recipe
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a private recipe (any recipe type would work)
+ auto i32Type = b.getI32Type();
+ llvm::StringRef recipeName = "test_recipe";
+ OwningOpRef<PrivateRecipeOp> recipeOp =
+ PrivateRecipeOp::create(b, loc, recipeName, i32Type);
+
+ // Create a value to privatize
+ 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 private op as the user operation
+ OwningOpRef<PrivateOp> privateOp = PrivateOp::create(
+ b, loc, varPtr, /*structured=*/true, /*implicit=*/false);
+
+ // Create a symbol reference to the recipe
+ SymbolRefAttr recipeSymbol = SymbolRefAttr::get(&context, recipeName);
+
+ Operation *definingOp = nullptr;
+ bool result = isValidSymbolUse(privateOp.get(), recipeSymbol, &definingOp);
+
+ EXPECT_TRUE(result);
+ EXPECT_EQ(definingOp, recipeOp.get());
+}
+
+TEST_F(OpenACCUtilsTest, isValidSymbolUseFunctionWithRoutineInfo) {
+ // Create a module to hold the function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function with routine_info attribute
+ auto funcType = b.getFunctionType({}, {});
+ llvm::StringRef funcName = "routine_func";
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, funcName, funcType);
+
+ // Add routine_info attribute with a reference to a routine
+ SmallVector<SymbolRefAttr> routineRefs = {
+ SymbolRefAttr::get(&context, "acc_routine")};
+ funcOp.get()->setAttr(getRoutineInfoAttrName(),
+ RoutineInfoAttr::get(&context, routineRefs));
+
+ // Create a call operation that uses the function symbol
+ SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, funcName);
+ OwningOpRef<func::CallOp> callOp = func::CallOp::create(
+ b, loc, funcSymbol, funcType.getResults(), ValueRange{});
+
+ Operation *definingOp = nullptr;
+ bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);
+
+ EXPECT_TRUE(result);
+ EXPECT_NE(definingOp, nullptr);
+}
+
+TEST_F(OpenACCUtilsTest, isValidSymbolUseLLVMIntrinsic) {
+ // Create a module to hold the function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a private function with LLVM intrinsic name
+ auto funcType = b.getFunctionType({b.getF32Type()}, {b.getF32Type()});
+ llvm::StringRef intrinsicName = "llvm.sqrt.f32";
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, intrinsicName, funcType);
+
+ // Set visibility to private (required for intrinsics)
+ funcOp->setPrivate();
+
+ // Create a call operation that uses the intrinsic
+ SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, intrinsicName);
+ OwningOpRef<func::CallOp> callOp = func::CallOp::create(
+ b, loc, funcSymbol, funcType.getResults(), ValueRange{});
+
+ Operation *definingOp = nullptr;
+ bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);
+
+ EXPECT_TRUE(result);
+ EXPECT_NE(definingOp, nullptr);
+}
+
+TEST_F(OpenACCUtilsTest, isValidSymbolUseFunctionNotIntrinsic) {
+ // Create a module to hold the function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a private function that looks like intrinsic but isn't
+ auto funcType = b.getFunctionType({}, {});
+ llvm::StringRef funcName = "llvm.not_a_real_intrinsic";
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, funcName, funcType);
+ funcOp->setPrivate();
+
+ // Create a call operation that uses the function
+ SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, funcName);
+ OwningOpRef<func::CallOp> callOp = func::CallOp::create(
+ b, loc, funcSymbol, funcType.getResults(), ValueRange{});
+
+ Operation *definingOp = nullptr;
+ bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);
+
+ // Should be false because it's not a valid intrinsic and has no
+ // acc.routine_info attr
+ EXPECT_FALSE(result);
+ EXPECT_NE(definingOp, nullptr);
+}
+
+TEST_F(OpenACCUtilsTest, isValidSymbolUseWithDeclareAttr) {
+ // Create a module to hold a function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function with declare attribute
+ auto funcType = b.getFunctionType({}, {});
+ llvm::StringRef funcName = "declared_func";
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, funcName, funcType);
+
+ // Add declare attribute
+ funcOp.get()->setAttr(
+ getDeclareAttrName(),
+ DeclareAttr::get(&context,
+ DataClauseAttr::get(&context, DataClause::acc_copy)));
+
+ // Create a call operation that uses the function
+ SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, funcName);
+ OwningOpRef<func::CallOp> callOp = func::CallOp::create(
+ b, loc, funcSymbol, funcType.getResults(), ValueRange{});
+
+ Operation *definingOp = nullptr;
+ bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);
+
+ EXPECT_TRUE(result);
+ EXPECT_NE(definingOp, nullptr);
+}
+
+TEST_F(OpenACCUtilsTest, isValidSymbolUseWithoutValidAttributes) {
+ // Create a module to hold a function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function without any special attributes
+ auto funcType = b.getFunctionType({}, {});
+ llvm::StringRef funcName = "regular_func";
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, funcName, funcType);
+
+ // Create a call operation that uses the function
+ SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, funcName);
+ OwningOpRef<func::CallOp> callOp = func::CallOp::create(
+ b, loc, funcSymbol, funcType.getResults(), ValueRange{});
+
+ Operation *definingOp = nullptr;
+ bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);
+
+ // Should be false - no routine_info, not an intrinsic, no declare attribute
+ EXPECT_FALSE(result);
+ EXPECT_NE(definingOp, nullptr);
+}
+
+TEST_F(OpenACCUtilsTest, isValidSymbolUseNullDefiningOpPtr) {
+ // Create a module to hold a recipe
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a private recipe
+ auto i32Type = b.getI32Type();
+ llvm::StringRef recipeName = "test_recipe";
+ OwningOpRef<PrivateRecipeOp> recipeOp =
+ PrivateRecipeOp::create(b, loc, recipeName, i32Type);
+
+ // Create a value to privatize
+ 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 private op as the user operation
+ OwningOpRef<PrivateOp> privateOp = PrivateOp::create(
+ b, loc, varPtr, /*structured=*/true, /*implicit=*/false);
+
+ // Create a symbol reference to the recipe
+ SymbolRefAttr recipeSymbol = SymbolRefAttr::get(&context, recipeName);
+
+ // Call without definingOpPtr (nullptr)
+ bool result = isValidSymbolUse(privateOp.get(), recipeSymbol, nullptr);
+
+ EXPECT_TRUE(result);
+}
+
+//===----------------------------------------------------------------------===//
+// getDominatingDataClauses Tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(OpenACCUtilsTest, getDominatingDataClausesFromComputeConstruct) {
+ // Create a module to hold a function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function
+ auto funcType = b.getFunctionType({}, {});
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, "test_func", funcType);
+ Block *funcBlock = funcOp->addEntryBlock();
+
+ b.setInsertionPointToStart(funcBlock);
+
+ // Create a memref for the data clause
+ 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 copyin op to represent a data clause
+ OwningOpRef<CopyinOp> copyinOp =
+ CopyinOp::create(b, loc, varPtr, /*structured=*/true, /*implicit=*/false,
+ /*name=*/"test_var");
+
+ // Create a parallel op
+ OwningOpRef<ParallelOp> parallelOp =
+ ParallelOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Set the data clause operands
+ parallelOp->getDataClauseOperandsMutable().append(copyinOp->getAccVar());
+
+ // Create dominance info
+ DominanceInfo domInfo(funcOp.get());
+ PostDominanceInfo postDomInfo(funcOp.get());
+
+ // Get dominating data clauses
+ auto dataClauses =
+ getDominatingDataClauses(parallelOp.get(), domInfo, postDomInfo);
+
+ // Should contain the copyin from the parallel op
+ EXPECT_EQ(dataClauses.size(), 1ul);
+ EXPECT_EQ(dataClauses[0], copyinOp->getAccVar());
+}
+
+TEST_F(OpenACCUtilsTest, getDominatingDataClausesFromEnclosingDataOp) {
+ // Create a module to hold a function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function
+ auto funcType = b.getFunctionType({}, {});
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, "test_func", funcType);
+ Block *funcBlock = funcOp->addEntryBlock();
+
+ b.setInsertionPointToStart(funcBlock);
+
+ // Create a memref for the data clause
+ 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 copyin op for the data construct
+ OwningOpRef<CopyinOp> copyinOp =
+ CopyinOp::create(b, loc, varPtr, /*structured=*/true, /*implicit=*/false,
+ /*name=*/"test_var");
+
+ // Create a data op
+ OwningOpRef<DataOp> dataOp =
+ DataOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Set the data clause operands
+ dataOp->getDataClauseOperandsMutable().append(copyinOp->getAccVar());
+
+ Region &dataRegion = dataOp->getRegion();
+ Block *dataBlock = &dataRegion.emplaceBlock();
+
+ b.setInsertionPointToStart(dataBlock);
+
+ // Create a parallel op inside the data region (no data clauses on parallel)
+ OwningOpRef<ParallelOp> parallelOp =
+ ParallelOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Create dominance info
+ DominanceInfo domInfo(funcOp.get());
+ PostDominanceInfo postDomInfo(funcOp.get());
+
+ // Get dominating data clauses
+ auto dataClauses =
+ getDominatingDataClauses(parallelOp.get(), domInfo, postDomInfo);
+
+ // Should contain the copyin from the enclosing data op
+ EXPECT_EQ(dataClauses.size(), 1ul);
+ EXPECT_EQ(dataClauses[0], copyinOp->getAccVar());
+}
+
+TEST_F(OpenACCUtilsTest, getDominatingDataClausesFromComputeAndEnclosingData) {
+ // Create a module to hold a function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function
+ auto funcType = b.getFunctionType({}, {});
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, "test_func", funcType);
+ Block *funcBlock = funcOp->addEntryBlock();
+
+ b.setInsertionPointToStart(funcBlock);
+
+ // Create two memrefs for different data clauses
+ auto memrefTy = MemRefType::get({10}, b.getI32Type());
+ OwningOpRef<memref::AllocaOp> allocOp1 =
+ memref::AllocaOp::create(b, loc, memrefTy);
+ TypedValue<PointerLikeType> varPtr1 =
+ cast<TypedValue<PointerLikeType>>(allocOp1->getResult());
+
+ OwningOpRef<memref::AllocaOp> allocOp2 =
+ memref::AllocaOp::create(b, loc, memrefTy);
+ TypedValue<PointerLikeType> varPtr2 =
+ cast<TypedValue<PointerLikeType>>(allocOp2->getResult());
+
+ // Create copyin ops
+ OwningOpRef<CopyinOp> copyinOp1 =
+ CopyinOp::create(b, loc, varPtr1, /*structured=*/true, /*implicit=*/false,
+ /*name=*/"var1");
+ OwningOpRef<CopyinOp> copyinOp2 =
+ CopyinOp::create(b, loc, varPtr2, /*structured=*/true, /*implicit=*/false,
+ /*name=*/"var2");
+
+ // Create a data op
+ OwningOpRef<DataOp> dataOp =
+ DataOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Set the data clause operands for data op
+ dataOp->getDataClauseOperandsMutable().append(copyinOp1->getAccVar());
+
+ Region &dataRegion = dataOp->getRegion();
+ Block *dataBlock = &dataRegion.emplaceBlock();
+
+ b.setInsertionPointToStart(dataBlock);
+
+ // Create a parallel op inside the data region
+ OwningOpRef<ParallelOp> parallelOp =
+ ParallelOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Set the data clause operands for parallel op
+ parallelOp->getDataClauseOperandsMutable().append(copyinOp2->getAccVar());
+
+ // Create dominance info
+ DominanceInfo domInfo(funcOp.get());
+ PostDominanceInfo postDomInfo(funcOp.get());
+
+ // Get dominating data clauses
+ auto dataClauses =
+ getDominatingDataClauses(parallelOp.get(), domInfo, postDomInfo);
+
+ // Should contain both copyins (from data op and parallel op)
+ EXPECT_EQ(dataClauses.size(), 2ul);
+ // Note: Order might not be guaranteed, so check both are present
+ EXPECT_TRUE(llvm::is_contained(dataClauses, copyinOp1->getAccVar()));
+ EXPECT_TRUE(llvm::is_contained(dataClauses, copyinOp2->getAccVar()));
+}
+
+TEST_F(OpenACCUtilsTest, getDominatingDataClausesWithDeclareDirectives) {
+ // Create a module to hold a function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function
+ auto funcType = b.getFunctionType({}, {});
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, "test_func", funcType);
+ Block *funcBlock = funcOp->addEntryBlock();
+
+ b.setInsertionPointToStart(funcBlock);
+
+ // Create a memref for the declare directive
+ 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 copyin op for declare
+ OwningOpRef<CopyinOp> copyinOp =
+ CopyinOp::create(b, loc, varPtr, /*structured=*/false, /*implicit=*/false,
+ /*name=*/"declare_var");
+
+ // Create a declare_enter op
+ OwningOpRef<DeclareEnterOp> declareEnterOp = DeclareEnterOp::create(
+ b, loc, TypeRange{b.getType<acc::DeclareTokenType>()},
+ ValueRange{copyinOp->getAccVar()});
+
+ // Create a parallel op
+ OwningOpRef<ParallelOp> parallelOp =
+ ParallelOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Create a declare_exit op that post-dominates the parallel
+ OwningOpRef<DeclareExitOp> declareExitOp = DeclareExitOp::create(
+ b, loc, declareEnterOp->getToken(), ValueRange{copyinOp->getAccVar()});
+
+ // Add a return to complete the function
+ OwningOpRef<func::ReturnOp> returnOp = func::ReturnOp::create(b, loc);
+
+ // Create dominance info
+ DominanceInfo domInfo(funcOp.get());
+ PostDominanceInfo postDomInfo(funcOp.get());
+
+ // Get dominating data clauses
+ auto dataClauses =
+ getDominatingDataClauses(parallelOp.get(), domInfo, postDomInfo);
+
+ // Should contain the copyin from the declare directive
+ EXPECT_EQ(dataClauses.size(), 1ul);
+ EXPECT_EQ(dataClauses[0], copyinOp->getAccVar());
+}
+
+TEST_F(OpenACCUtilsTest, getDominatingDataClausesMultipleDataConstructs) {
+ // Create a module to hold a function
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function
+ auto funcType = b.getFunctionType({}, {});
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, "test_func", funcType);
+ Block *funcBlock = funcOp->addEntryBlock();
+
+ b.setInsertionPointToStart(funcBlock);
+
+ // Create three memrefs
+ auto memrefTy = MemRefType::get({10}, b.getI32Type());
+ OwningOpRef<memref::AllocaOp> allocOp1 =
+ memref::AllocaOp::create(b, loc, memrefTy);
+ TypedValue<PointerLikeType> varPtr1 =
+ cast<TypedValue<PointerLikeType>>(allocOp1->getResult());
+
+ OwningOpRef<memref::AllocaOp> allocOp2 =
+ memref::AllocaOp::create(b, loc, memrefTy);
+ TypedValue<PointerLikeType> varPtr2 =
+ cast<TypedValue<PointerLikeType>>(allocOp2->getResult());
+
+ OwningOpRef<memref::AllocaOp> allocOp3 =
+ memref::AllocaOp::create(b, loc, memrefTy);
+ TypedValue<PointerLikeType> varPtr3 =
+ cast<TypedValue<PointerLikeType>>(allocOp3->getResult());
+
+ // Create copyin ops
+ OwningOpRef<CopyinOp> copyinOp1 =
+ CopyinOp::create(b, loc, varPtr1, /*structured=*/true, /*implicit=*/false,
+ /*name=*/"var1");
+ OwningOpRef<CopyinOp> copyinOp2 =
+ CopyinOp::create(b, loc, varPtr2, /*structured=*/true, /*implicit=*/false,
+ /*name=*/"var2");
+ OwningOpRef<CopyinOp> copyinOp3 =
+ CopyinOp::create(b, loc, varPtr3, /*structured=*/true, /*implicit=*/false,
+ /*name=*/"var3");
+
+ // Create outer data op
+ OwningOpRef<DataOp> outerDataOp =
+ DataOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Set the data clause operands for outer data op
+ outerDataOp->getDataClauseOperandsMutable().append(copyinOp1->getAccVar());
+
+ Region &outerDataRegion = outerDataOp->getRegion();
+ Block *outerDataBlock = &outerDataRegion.emplaceBlock();
+
+ b.setInsertionPointToStart(outerDataBlock);
+
+ // Create inner data op
+ OwningOpRef<DataOp> innerDataOp =
+ DataOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Set the data clause operands for inner data op
+ innerDataOp->getDataClauseOperandsMutable().append(copyinOp2->getAccVar());
+
+ Region &innerDataRegion = innerDataOp->getRegion();
+ Block *innerDataBlock = &innerDataRegion.emplaceBlock();
+
+ b.setInsertionPointToStart(innerDataBlock);
+
+ // Create a parallel op
+ OwningOpRef<ParallelOp> parallelOp =
+ ParallelOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Set the data clause operands for parallel op
+ parallelOp->getDataClauseOperandsMutable().append(copyinOp3->getAccVar());
+
+ // Create dominance info
+ DominanceInfo domInfo(funcOp.get());
+ PostDominanceInfo postDomInfo(funcOp.get());
+
+ // Get dominating data clauses
+ auto dataClauses =
+ getDominatingDataClauses(parallelOp.get(), domInfo, postDomInfo);
+
+ // Should contain all three copyins
+ EXPECT_EQ(dataClauses.size(), 3ul);
+ EXPECT_TRUE(llvm::is_contained(dataClauses, copyinOp1->getAccVar()));
+ EXPECT_TRUE(llvm::is_contained(dataClauses, copyinOp2->getAccVar()));
+ EXPECT_TRUE(llvm::is_contained(dataClauses, copyinOp3->getAccVar()));
+}
+
+TEST_F(OpenACCUtilsTest, getDominatingDataClausesKernelsOp) {
+ // Test with KernelsOp instead of ParallelOp
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function
+ auto funcType = b.getFunctionType({}, {});
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, "test_func", funcType);
+ Block *funcBlock = funcOp->addEntryBlock();
+
+ b.setInsertionPointToStart(funcBlock);
+
+ // Create a memref
+ 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 copyin op
+ OwningOpRef<CopyinOp> copyinOp =
+ CopyinOp::create(b, loc, varPtr, /*structured=*/true, /*implicit=*/false,
+ /*name=*/"test_var");
+
+ // Create a kernels op
+ OwningOpRef<KernelsOp> kernelsOp =
+ KernelsOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Set the data clause operands
+ kernelsOp->getDataClauseOperandsMutable().append(copyinOp->getAccVar());
+
+ // Create dominance info
+ DominanceInfo domInfo(funcOp.get());
+ PostDominanceInfo postDomInfo(funcOp.get());
+
+ // Get dominating data clauses
+ auto dataClauses =
+ getDominatingDataClauses(kernelsOp.get(), domInfo, postDomInfo);
+
+ // Should contain the copyin from the kernels op
+ EXPECT_EQ(dataClauses.size(), 1ul);
+ EXPECT_EQ(dataClauses[0], copyinOp->getAccVar());
+}
+
+TEST_F(OpenACCUtilsTest, getDominatingDataClausesSerialOp) {
+ // Test with SerialOp
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function
+ auto funcType = b.getFunctionType({}, {});
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, "test_func", funcType);
+ Block *funcBlock = funcOp->addEntryBlock();
+
+ b.setInsertionPointToStart(funcBlock);
+
+ // Create a memref
+ 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 copyin op
+ OwningOpRef<CopyinOp> copyinOp =
+ CopyinOp::create(b, loc, varPtr, /*structured=*/true, /*implicit=*/false,
+ /*name=*/"test_var");
+
+ // Create a serial op
+ OwningOpRef<SerialOp> serialOp =
+ SerialOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Set the data clause operands
+ serialOp->getDataClauseOperandsMutable().append(copyinOp->getAccVar());
+
+ // Create dominance info
+ DominanceInfo domInfo(funcOp.get());
+ PostDominanceInfo postDomInfo(funcOp.get());
+
+ // Get dominating data clauses
+ auto dataClauses =
+ getDominatingDataClauses(serialOp.get(), domInfo, postDomInfo);
+
+ // Should contain the copyin from the serial op
+ EXPECT_EQ(dataClauses.size(), 1ul);
+ EXPECT_EQ(dataClauses[0], copyinOp->getAccVar());
+}
+
+TEST_F(OpenACCUtilsTest, getDominatingDataClausesEmpty) {
+ // Test with no data clauses at all
+ OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+ Block *moduleBlock = module->getBody();
+
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPointToStart(moduleBlock);
+
+ // Create a function
+ auto funcType = b.getFunctionType({}, {});
+ OwningOpRef<func::FuncOp> funcOp =
+ func::FuncOp::create(b, loc, "test_func", funcType);
+ Block *funcBlock = funcOp->addEntryBlock();
+
+ b.setInsertionPointToStart(funcBlock);
+
+ // Create a parallel op with no data clauses
+ OwningOpRef<ParallelOp> parallelOp =
+ ParallelOp::create(b, loc, TypeRange{}, ValueRange{});
+
+ // Create dominance info
+ DominanceInfo domInfo(funcOp.get());
+ PostDominanceInfo postDomInfo(funcOp.get());
+
+ // Get dominating data clauses
+ auto dataClauses =
+ getDominatingDataClauses(parallelOp.get(), domInfo, postDomInfo);
+
+ // Should be empty
+ EXPECT_EQ(dataClauses.size(), 0ul);
+}