diff options
Diffstat (limited to 'mlir/unittests/Dialect')
| -rw-r--r-- | mlir/unittests/Dialect/OpenACC/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | mlir/unittests/Dialect/OpenACC/OpenACCOpsInterfacesTest.cpp | 117 | ||||
| -rw-r--r-- | mlir/unittests/Dialect/OpenACC/OpenACCOpsTest.cpp | 4 | ||||
| -rw-r--r-- | mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp | 882 |
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); +} |
