brintos

brintos / llvm-project-archived public Read only

0
0
Text · 34.5 KiB · 8b1f532 Raw
916 lines · cpp
1//===- OpenACCUtilsTest.cpp - Unit tests for OpenACC utilities -----------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/Dialect/OpenACC/OpenACCUtils.h"10#include "mlir/Dialect/Arith/IR/Arith.h"11#include "mlir/Dialect/Func/IR/FuncOps.h"12#include "mlir/Dialect/MemRef/IR/MemRef.h"13#include "mlir/Dialect/OpenACC/OpenACC.h"14#include "mlir/IR/BuiltinOps.h"15#include "mlir/IR/BuiltinTypes.h"16#include "mlir/IR/Diagnostics.h"17#include "mlir/IR/MLIRContext.h"18#include "mlir/IR/OwningOpRef.h"19#include "mlir/IR/Value.h"20#include "gtest/gtest.h"21 22using namespace mlir;23using namespace mlir::acc;24 25//===----------------------------------------------------------------------===//26// Test Fixture27//===----------------------------------------------------------------------===//28 29class OpenACCUtilsTest : public ::testing::Test {30protected:31  OpenACCUtilsTest() : b(&context), loc(UnknownLoc::get(&context)) {32    context.loadDialect<acc::OpenACCDialect, arith::ArithDialect,33                        memref::MemRefDialect, func::FuncDialect>();34  }35 36  MLIRContext context;37  OpBuilder b;38  Location loc;39};40 41//===----------------------------------------------------------------------===//42// getEnclosingComputeOp Tests43//===----------------------------------------------------------------------===//44 45TEST_F(OpenACCUtilsTest, getEnclosingComputeOpParallel) {46  // Create a parallel op with a region47  OwningOpRef<ParallelOp> parallelOp =48      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});49  Region &parallelRegion = parallelOp->getRegion();50  parallelRegion.emplaceBlock();51 52  // Test that we can find the parallel op from its region53  Operation *enclosingOp = getEnclosingComputeOp(parallelRegion);54  EXPECT_EQ(enclosingOp, parallelOp.get());55}56 57TEST_F(OpenACCUtilsTest, getEnclosingComputeOpKernels) {58  // Create a kernels op with a region59  OwningOpRef<KernelsOp> kernelsOp =60      KernelsOp::create(b, loc, TypeRange{}, ValueRange{});61  Region &kernelsRegion = kernelsOp->getRegion();62  kernelsRegion.emplaceBlock();63 64  // Test that we can find the kernels op from its region65  Operation *enclosingOp = getEnclosingComputeOp(kernelsRegion);66  EXPECT_EQ(enclosingOp, kernelsOp.get());67}68 69TEST_F(OpenACCUtilsTest, getEnclosingComputeOpSerial) {70  // Create a serial op with a region71  OwningOpRef<SerialOp> serialOp =72      SerialOp::create(b, loc, TypeRange{}, ValueRange{});73  Region &serialRegion = serialOp->getRegion();74  serialRegion.emplaceBlock();75 76  // Test that we can find the serial op from its region77  Operation *enclosingOp = getEnclosingComputeOp(serialRegion);78  EXPECT_EQ(enclosingOp, serialOp.get());79}80 81TEST_F(OpenACCUtilsTest, getEnclosingComputeOpNested) {82  // Create nested ops: parallel containing a loop op83  OwningOpRef<ParallelOp> parallelOp =84      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});85  Region &parallelRegion = parallelOp->getRegion();86  Block *parallelBlock = &parallelRegion.emplaceBlock();87 88  OpBuilder::InsertionGuard guard(b);89  b.setInsertionPointToStart(parallelBlock);90 91  // Create a loop op inside the parallel region92  OwningOpRef<LoopOp> loopOp =93      LoopOp::create(b, loc, TypeRange{}, ValueRange{});94  Region &loopRegion = loopOp->getRegion();95  loopRegion.emplaceBlock();96 97  // Test that from the loop region, we find the parallel op (loop is not a98  // compute op)99  Operation *enclosingOp = getEnclosingComputeOp(loopRegion);100  EXPECT_EQ(enclosingOp, parallelOp.get());101}102 103TEST_F(OpenACCUtilsTest, getEnclosingComputeOpNone) {104  // Create a module with a region that's not inside a compute construct105  OwningOpRef<ModuleOp> moduleOp = ModuleOp::create(loc);106  Region &moduleRegion = moduleOp->getBodyRegion();107 108  // Test that we get nullptr when there's no enclosing compute op109  Operation *enclosingOp = getEnclosingComputeOp(moduleRegion);110  EXPECT_EQ(enclosingOp, nullptr);111}112 113//===----------------------------------------------------------------------===//114// isOnlyUsedByPrivateClauses Tests115//===----------------------------------------------------------------------===//116 117TEST_F(OpenACCUtilsTest, isOnlyUsedByPrivateClausesTrue) {118  // Create a value (memref) outside the compute region119  auto memrefTy = MemRefType::get({10}, b.getI32Type());120  OwningOpRef<memref::AllocaOp> allocOp =121      memref::AllocaOp::create(b, loc, memrefTy);122  TypedValue<PointerLikeType> varPtr =123      cast<TypedValue<PointerLikeType>>(allocOp->getResult());124 125  // Create a parallel op with a region126  OwningOpRef<ParallelOp> parallelOp =127      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});128  Region &parallelRegion = parallelOp->getRegion();129  Block *parallelBlock = &parallelRegion.emplaceBlock();130 131  OpBuilder::InsertionGuard guard(b);132  b.setInsertionPointToStart(parallelBlock);133 134  // Create a private op using the value135  OwningOpRef<PrivateOp> privateOp = PrivateOp::create(136      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);137 138  // Test that the value is only used by private clauses139  EXPECT_TRUE(isOnlyUsedByPrivateClauses(varPtr, parallelRegion));140}141 142TEST_F(OpenACCUtilsTest, isOnlyUsedByPrivateClausesFalse) {143  // Create a value (memref) outside the compute region144  auto memrefTy = MemRefType::get({10}, b.getI32Type());145  OwningOpRef<memref::AllocaOp> allocOp =146      memref::AllocaOp::create(b, loc, memrefTy);147  TypedValue<PointerLikeType> varPtr =148      cast<TypedValue<PointerLikeType>>(allocOp->getResult());149 150  // Create a parallel op with a region151  OwningOpRef<ParallelOp> parallelOp =152      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});153  Region &parallelRegion = parallelOp->getRegion();154  Block *parallelBlock = &parallelRegion.emplaceBlock();155 156  OpBuilder::InsertionGuard guard(b);157  b.setInsertionPointToStart(parallelBlock);158 159  // Create a private op using the value160  OwningOpRef<PrivateOp> privateOp = PrivateOp::create(161      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);162 163  // Also use the value in a function call (escape)164  OwningOpRef<func::CallOp> callOp = func::CallOp::create(165      b, loc, "some_func", TypeRange{}, ValueRange{varPtr});166 167  // Test that the value is NOT only used by private clauses (it escapes via168  // call)169  EXPECT_FALSE(isOnlyUsedByPrivateClauses(varPtr, parallelRegion));170}171 172TEST_F(OpenACCUtilsTest, isOnlyUsedByPrivateClausesMultiple) {173  // Create a value (memref) outside the compute region174  auto memrefTy = MemRefType::get({10}, b.getI32Type());175  OwningOpRef<memref::AllocaOp> allocOp =176      memref::AllocaOp::create(b, loc, memrefTy);177  TypedValue<PointerLikeType> varPtr =178      cast<TypedValue<PointerLikeType>>(allocOp->getResult());179 180  // Create a parallel op with a region181  OwningOpRef<ParallelOp> parallelOp =182      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});183  Region &parallelRegion = parallelOp->getRegion();184  Block *parallelBlock = &parallelRegion.emplaceBlock();185 186  OpBuilder::InsertionGuard guard(b);187  b.setInsertionPointToStart(parallelBlock);188 189  // Create multiple private ops using the value190  OwningOpRef<PrivateOp> privateOp1 = PrivateOp::create(191      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);192  OwningOpRef<PrivateOp> privateOp2 = PrivateOp::create(193      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);194 195  // Test that the value is only used by private clauses even with multiple uses196  EXPECT_TRUE(isOnlyUsedByPrivateClauses(varPtr, parallelRegion));197}198 199//===----------------------------------------------------------------------===//200// isOnlyUsedByReductionClauses Tests201//===----------------------------------------------------------------------===//202 203TEST_F(OpenACCUtilsTest, isOnlyUsedByReductionClausesTrue) {204  // Create a value (memref) outside the compute region205  auto memrefTy = MemRefType::get({10}, b.getI32Type());206  OwningOpRef<memref::AllocaOp> allocOp =207      memref::AllocaOp::create(b, loc, memrefTy);208  TypedValue<PointerLikeType> varPtr =209      cast<TypedValue<PointerLikeType>>(allocOp->getResult());210 211  // Create a parallel op with a region212  OwningOpRef<ParallelOp> parallelOp =213      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});214  Region &parallelRegion = parallelOp->getRegion();215  Block *parallelBlock = &parallelRegion.emplaceBlock();216 217  OpBuilder::InsertionGuard guard(b);218  b.setInsertionPointToStart(parallelBlock);219 220  // Create a reduction op using the value221  OwningOpRef<ReductionOp> reductionOp = ReductionOp::create(222      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);223 224  // Test that the value is only used by reduction clauses225  EXPECT_TRUE(isOnlyUsedByReductionClauses(varPtr, parallelRegion));226}227 228TEST_F(OpenACCUtilsTest, isOnlyUsedByReductionClausesFalse) {229  // Create a value (memref) outside the compute region230  auto memrefTy = MemRefType::get({10}, b.getI32Type());231  OwningOpRef<memref::AllocaOp> allocOp =232      memref::AllocaOp::create(b, loc, memrefTy);233  TypedValue<PointerLikeType> varPtr =234      cast<TypedValue<PointerLikeType>>(allocOp->getResult());235 236  // Create a parallel op with a region237  OwningOpRef<ParallelOp> parallelOp =238      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});239  Region &parallelRegion = parallelOp->getRegion();240  Block *parallelBlock = &parallelRegion.emplaceBlock();241 242  OpBuilder::InsertionGuard guard(b);243  b.setInsertionPointToStart(parallelBlock);244 245  // Create a reduction op using the value246  OwningOpRef<ReductionOp> reductionOp = ReductionOp::create(247      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);248 249  // Also use the value in a function call (escape)250  OwningOpRef<func::CallOp> callOp = func::CallOp::create(251      b, loc, "some_func", TypeRange{}, ValueRange{varPtr});252 253  // Test that the value is NOT only used by reduction clauses (it escapes via254  // call)255  EXPECT_FALSE(isOnlyUsedByReductionClauses(varPtr, parallelRegion));256}257 258TEST_F(OpenACCUtilsTest, isOnlyUsedByReductionClausesMultiple) {259  // Create a value (memref) outside the compute region260  auto memrefTy = MemRefType::get({10}, b.getI32Type());261  OwningOpRef<memref::AllocaOp> allocOp =262      memref::AllocaOp::create(b, loc, memrefTy);263  TypedValue<PointerLikeType> varPtr =264      cast<TypedValue<PointerLikeType>>(allocOp->getResult());265 266  // Create a parallel op with a region267  OwningOpRef<ParallelOp> parallelOp =268      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});269  Region &parallelRegion = parallelOp->getRegion();270  Block *parallelBlock = &parallelRegion.emplaceBlock();271 272  OpBuilder::InsertionGuard guard(b);273  b.setInsertionPointToStart(parallelBlock);274 275  // Create multiple reduction ops using the value276  OwningOpRef<ReductionOp> reductionOp1 = ReductionOp::create(277      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);278  OwningOpRef<ReductionOp> reductionOp2 = ReductionOp::create(279      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);280 281  // Test that the value is only used by reduction clauses even with multiple282  // uses283  EXPECT_TRUE(isOnlyUsedByReductionClauses(varPtr, parallelRegion));284}285 286//===----------------------------------------------------------------------===//287// getDefaultAttr Tests288//===----------------------------------------------------------------------===//289 290TEST_F(OpenACCUtilsTest, getDefaultAttrOnParallel) {291  // Create a parallel op with a default attribute292  OwningOpRef<ParallelOp> parallelOp =293      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});294  parallelOp->setDefaultAttr(ClauseDefaultValue::None);295 296  // Test that we can retrieve the default attribute297  std::optional<ClauseDefaultValue> defaultAttr =298      getDefaultAttr(parallelOp.get());299  EXPECT_TRUE(defaultAttr.has_value());300  EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::None);301}302 303TEST_F(OpenACCUtilsTest, getDefaultAttrOnKernels) {304  // Create a kernels op with a default attribute305  OwningOpRef<KernelsOp> kernelsOp =306      KernelsOp::create(b, loc, TypeRange{}, ValueRange{});307  kernelsOp->setDefaultAttr(ClauseDefaultValue::Present);308 309  // Test that we can retrieve the default attribute310  std::optional<ClauseDefaultValue> defaultAttr =311      getDefaultAttr(kernelsOp.get());312  EXPECT_TRUE(defaultAttr.has_value());313  EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::Present);314}315 316TEST_F(OpenACCUtilsTest, getDefaultAttrOnSerial) {317  // Create a serial op with a default attribute318  OwningOpRef<SerialOp> serialOp =319      SerialOp::create(b, loc, TypeRange{}, ValueRange{});320  serialOp->setDefaultAttr(ClauseDefaultValue::None);321 322  // Test that we can retrieve the default attribute323  std::optional<ClauseDefaultValue> defaultAttr =324      getDefaultAttr(serialOp.get());325  EXPECT_TRUE(defaultAttr.has_value());326  EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::None);327}328 329TEST_F(OpenACCUtilsTest, getDefaultAttrOnData) {330  // Create a data op with a default attribute331  OwningOpRef<DataOp> dataOp =332      DataOp::create(b, loc, TypeRange{}, ValueRange{});333  dataOp->setDefaultAttr(ClauseDefaultValue::Present);334 335  // Test that we can retrieve the default attribute336  std::optional<ClauseDefaultValue> defaultAttr = getDefaultAttr(dataOp.get());337  EXPECT_TRUE(defaultAttr.has_value());338  EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::Present);339}340 341TEST_F(OpenACCUtilsTest, getDefaultAttrNone) {342  // Create a parallel op without setting a default attribute343  OwningOpRef<ParallelOp> parallelOp =344      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});345  // Do not set default attribute346 347  // Test that we get std::nullopt when there's no default attribute348  std::optional<ClauseDefaultValue> defaultAttr =349      getDefaultAttr(parallelOp.get());350  EXPECT_FALSE(defaultAttr.has_value());351}352 353TEST_F(OpenACCUtilsTest, getDefaultAttrNearest) {354  // Create a data op with a default attribute355  OwningOpRef<DataOp> dataOp =356      DataOp::create(b, loc, TypeRange{}, ValueRange{});357  dataOp->setDefaultAttr(ClauseDefaultValue::Present);358 359  Region &dataRegion = dataOp->getRegion();360  Block *dataBlock = &dataRegion.emplaceBlock();361 362  OpBuilder::InsertionGuard guard(b);363  b.setInsertionPointToStart(dataBlock);364 365  // Create a parallel op inside the data region with NO default attribute366  OwningOpRef<ParallelOp> parallelOp =367      ParallelOp::create(b, loc, TypeRange{}, ValueRange{});368  // Do not set default attribute on parallel op369 370  Region &parallelRegion = parallelOp->getRegion();371  Block *parallelBlock = &parallelRegion.emplaceBlock();372 373  b.setInsertionPointToStart(parallelBlock);374 375  // Create a loop op inside the parallel region376  OwningOpRef<LoopOp> loopOp =377      LoopOp::create(b, loc, TypeRange{}, ValueRange{});378 379  // Test that from the loop op, we find the nearest default attribute (from380  // data op)381  std::optional<ClauseDefaultValue> defaultAttr = getDefaultAttr(loopOp.get());382  EXPECT_TRUE(defaultAttr.has_value());383  EXPECT_EQ(defaultAttr.value(), ClauseDefaultValue::Present);384}385 386//===----------------------------------------------------------------------===//387// getTypeCategory Tests388//===----------------------------------------------------------------------===//389 390TEST_F(OpenACCUtilsTest, getTypeCategoryScalar) {391  // Create a scalar memref (no dimensions)392  auto scalarMemrefTy = MemRefType::get({}, b.getI32Type());393  OwningOpRef<memref::AllocaOp> allocOp =394      memref::AllocaOp::create(b, loc, scalarMemrefTy);395  Value varPtr = allocOp->getResult();396 397  // Test that a scalar memref returns scalar category398  VariableTypeCategory category = getTypeCategory(varPtr);399  EXPECT_EQ(category, VariableTypeCategory::scalar);400}401 402TEST_F(OpenACCUtilsTest, getTypeCategoryArray) {403  // Create an array memref (with dimensions)404  auto arrayMemrefTy = MemRefType::get({10}, b.getI32Type());405  OwningOpRef<memref::AllocaOp> allocOp =406      memref::AllocaOp::create(b, loc, arrayMemrefTy);407  Value varPtr = allocOp->getResult();408 409  // Test that an array memref returns array category410  VariableTypeCategory category = getTypeCategory(varPtr);411  EXPECT_EQ(category, VariableTypeCategory::array);412}413 414//===----------------------------------------------------------------------===//415// getVariableName Tests416//===----------------------------------------------------------------------===//417 418TEST_F(OpenACCUtilsTest, getVariableNameDirect) {419  // Create a memref with acc.var_name attribute420  auto memrefTy = MemRefType::get({10}, b.getI32Type());421  OwningOpRef<memref::AllocaOp> allocOp =422      memref::AllocaOp::create(b, loc, memrefTy);423 424  // Set the acc.var_name attribute425  auto varNameAttr = VarNameAttr::get(&context, "my_variable");426  allocOp.get()->setAttr(getVarNameAttrName(), varNameAttr);427 428  Value varPtr = allocOp->getResult();429 430  // Test that getVariableName returns the variable name431  std::string varName = getVariableName(varPtr);432  EXPECT_EQ(varName, "my_variable");433}434 435TEST_F(OpenACCUtilsTest, getVariableNameThroughCast) {436  // Create a 5x2 memref with acc.var_name attribute437  auto memrefTy = MemRefType::get({5, 2}, b.getI32Type());438  OwningOpRef<memref::AllocaOp> allocOp =439      memref::AllocaOp::create(b, loc, memrefTy);440 441  // Set the acc.var_name attribute on the alloca442  auto varNameAttr = VarNameAttr::get(&context, "casted_variable");443  allocOp.get()->setAttr(getVarNameAttrName(), varNameAttr);444 445  Value allocResult = allocOp->getResult();446 447  // Create a memref.cast operation to a flattened 10-element array448  auto castedMemrefTy = MemRefType::get({10}, b.getI32Type());449  OwningOpRef<memref::CastOp> castOp =450      memref::CastOp::create(b, loc, castedMemrefTy, allocResult);451 452  Value castedPtr = castOp->getResult();453 454  // Test that getVariableName walks through the cast to find the variable name455  std::string varName = getVariableName(castedPtr);456  EXPECT_EQ(varName, "casted_variable");457}458 459TEST_F(OpenACCUtilsTest, getVariableNameNotFound) {460  // Create a memref without acc.var_name attribute461  auto memrefTy = MemRefType::get({10}, b.getI32Type());462  OwningOpRef<memref::AllocaOp> allocOp =463      memref::AllocaOp::create(b, loc, memrefTy);464 465  Value varPtr = allocOp->getResult();466 467  // Test that getVariableName returns empty string when no name is found468  std::string varName = getVariableName(varPtr);469  EXPECT_EQ(varName, "");470}471 472TEST_F(OpenACCUtilsTest, getVariableNameFromCopyin) {473  // Create a memref474  auto memrefTy = MemRefType::get({10}, b.getI32Type());475  OwningOpRef<memref::AllocaOp> allocOp =476      memref::AllocaOp::create(b, loc, memrefTy);477 478  Value varPtr = allocOp->getResult();479  StringRef name = "data_array";480  OwningOpRef<CopyinOp> copyinOp =481      CopyinOp::create(b, loc, varPtr, /*structured=*/true, /*implicit=*/true,482                       /*name=*/name);483 484  // Test that getVariableName extracts the name from the copyin operation485  std::string varName = getVariableName(copyinOp->getAccVar());486  EXPECT_EQ(varName, name);487}488 489//===----------------------------------------------------------------------===//490// getRecipeName Tests491//===----------------------------------------------------------------------===//492 493TEST_F(OpenACCUtilsTest, getRecipeNamePrivateScalarMemref) {494  // Create a scalar memref type495  auto scalarMemrefTy = MemRefType::get({}, b.getI32Type());496 497  // Test private recipe with scalar memref498  std::string recipeName =499      getRecipeName(RecipeKind::private_recipe, scalarMemrefTy);500  EXPECT_EQ(recipeName, "privatization_memref_i32_");501}502 503TEST_F(OpenACCUtilsTest, getRecipeNameFirstprivateScalarMemref) {504  // Create a scalar memref type505  auto scalarMemrefTy = MemRefType::get({}, b.getF32Type());506 507  // Test firstprivate recipe with scalar memref508  std::string recipeName =509      getRecipeName(RecipeKind::firstprivate_recipe, scalarMemrefTy);510  EXPECT_EQ(recipeName, "firstprivatization_memref_f32_");511}512 513TEST_F(OpenACCUtilsTest, getRecipeNameReductionScalarMemref) {514  // Create a scalar memref type515  auto scalarMemrefTy = MemRefType::get({}, b.getI64Type());516 517  // Test reduction recipe with scalar memref518  std::string recipeName =519      getRecipeName(RecipeKind::reduction_recipe, scalarMemrefTy);520  EXPECT_EQ(recipeName, "reduction_memref_i64_");521}522 523TEST_F(OpenACCUtilsTest, getRecipeNamePrivate2DMemref) {524  // Create a 2D memref type525  auto memref2DTy = MemRefType::get({5, 10}, b.getF32Type());526 527  // Test private recipe with 2D memref528  std::string recipeName =529      getRecipeName(RecipeKind::private_recipe, memref2DTy);530  EXPECT_EQ(recipeName, "privatization_memref_5x10xf32_");531}532 533TEST_F(OpenACCUtilsTest, getRecipeNameFirstprivate2DMemref) {534  // Create a 2D memref type535  auto memref2DTy = MemRefType::get({8, 16}, b.getF64Type());536 537  // Test firstprivate recipe with 2D memref538  std::string recipeName =539      getRecipeName(RecipeKind::firstprivate_recipe, memref2DTy);540  EXPECT_EQ(recipeName, "firstprivatization_memref_8x16xf64_");541}542 543TEST_F(OpenACCUtilsTest, getRecipeNameReduction2DMemref) {544  // Create a 2D memref type545  auto memref2DTy = MemRefType::get({4, 8}, b.getI32Type());546 547  // Test reduction recipe with 2D memref548  std::string recipeName =549      getRecipeName(RecipeKind::reduction_recipe, memref2DTy);550  EXPECT_EQ(recipeName, "reduction_memref_4x8xi32_");551}552 553TEST_F(OpenACCUtilsTest, getRecipeNamePrivateDynamicMemref) {554  // Create a memref with dynamic dimensions555  auto dynamicMemrefTy =556      MemRefType::get({ShapedType::kDynamic, 10}, b.getI32Type());557 558  // Test private recipe with dynamic memref559  std::string recipeName =560      getRecipeName(RecipeKind::private_recipe, dynamicMemrefTy);561  EXPECT_EQ(recipeName, "privatization_memref_Ux10xi32_");562}563 564TEST_F(OpenACCUtilsTest, getRecipeNamePrivateUnrankedMemref) {565  // Create an unranked memref type566  auto unrankedMemrefTy = UnrankedMemRefType::get(b.getI32Type(), 0);567 568  // Test private recipe with unranked memref569  std::string recipeName =570      getRecipeName(RecipeKind::private_recipe, unrankedMemrefTy);571  EXPECT_EQ(recipeName, "privatization_memref_Zxi32_");572}573 574//===----------------------------------------------------------------------===//575// getBaseEntity Tests576//===----------------------------------------------------------------------===//577 578// Local implementation of PartialEntityAccessOpInterface for memref.subview.579// This is implemented locally in the test rather than officially because memref580// operations already have ViewLikeOpInterface, which serves a similar purpose581// for walking through views to the base entity. This test demonstrates how582// getBaseEntity() would work if the interface were attached to memref.subview.583namespace {584struct SubViewOpPartialEntityAccessOpInterface585    : public acc::PartialEntityAccessOpInterface::ExternalModel<586          SubViewOpPartialEntityAccessOpInterface, memref::SubViewOp> {587  Value getBaseEntity(Operation *op) const {588    auto subviewOp = cast<memref::SubViewOp>(op);589    return subviewOp.getSource();590  }591 592  bool isCompleteView(Operation *op) const {593    // For testing purposes, we'll consider it a partial view (return false).594    // The real implementation would need to look at the offsets.595    return false;596  }597};598} // namespace599 600TEST_F(OpenACCUtilsTest, getBaseEntityFromSubview) {601  // Register the local interface implementation for memref.subview602  memref::SubViewOp::attachInterface<SubViewOpPartialEntityAccessOpInterface>(603      context);604 605  // Create a base memref606  auto memrefTy = MemRefType::get({10, 20}, b.getF32Type());607  OwningOpRef<memref::AllocaOp> allocOp =608      memref::AllocaOp::create(b, loc, memrefTy);609  Value baseMemref = allocOp->getResult();610 611  // Create a subview of the base memref with non-zero offsets612  // This creates a 5x10 view starting at [2, 3] in the original 10x20 memref613  SmallVector<OpFoldResult> offsets = {b.getIndexAttr(2), b.getIndexAttr(3)};614  SmallVector<OpFoldResult> sizes = {b.getIndexAttr(5), b.getIndexAttr(10)};615  SmallVector<OpFoldResult> strides = {b.getIndexAttr(1), b.getIndexAttr(1)};616 617  OwningOpRef<memref::SubViewOp> subviewOp =618      memref::SubViewOp::create(b, loc, baseMemref, offsets, sizes, strides);619  Value subview = subviewOp->getResult();620 621  // Test that getBaseEntity returns the base memref, not the subview622  Value baseEntity = getBaseEntity(subview);623  EXPECT_EQ(baseEntity, baseMemref);624}625 626TEST_F(OpenACCUtilsTest, getBaseEntityNoInterface) {627  // Create a memref without the interface628  auto memrefTy = MemRefType::get({10}, b.getI32Type());629  OwningOpRef<memref::AllocaOp> allocOp =630      memref::AllocaOp::create(b, loc, memrefTy);631  Value varPtr = allocOp->getResult();632 633  // Test that getBaseEntity returns the value itself when there's no interface634  Value baseEntity = getBaseEntity(varPtr);635  EXPECT_EQ(baseEntity, varPtr);636}637 638TEST_F(OpenACCUtilsTest, getBaseEntityChainedSubviews) {639  // Register the local interface implementation for memref.subview640  memref::SubViewOp::attachInterface<SubViewOpPartialEntityAccessOpInterface>(641      context);642 643  // Create a base memref644  auto memrefTy = MemRefType::get({100, 200}, b.getI64Type());645  OwningOpRef<memref::AllocaOp> allocOp =646      memref::AllocaOp::create(b, loc, memrefTy);647  Value baseMemref = allocOp->getResult();648 649  // Create first subview650  SmallVector<OpFoldResult> offsets1 = {b.getIndexAttr(10), b.getIndexAttr(20)};651  SmallVector<OpFoldResult> sizes1 = {b.getIndexAttr(50), b.getIndexAttr(80)};652  SmallVector<OpFoldResult> strides1 = {b.getIndexAttr(1), b.getIndexAttr(1)};653 654  OwningOpRef<memref::SubViewOp> subview1Op =655      memref::SubViewOp::create(b, loc, baseMemref, offsets1, sizes1, strides1);656  Value subview1 = subview1Op->getResult();657 658  // Create second subview (subview of subview)659  SmallVector<OpFoldResult> offsets2 = {b.getIndexAttr(5), b.getIndexAttr(10)};660  SmallVector<OpFoldResult> sizes2 = {b.getIndexAttr(20), b.getIndexAttr(30)};661  SmallVector<OpFoldResult> strides2 = {b.getIndexAttr(1), b.getIndexAttr(1)};662 663  OwningOpRef<memref::SubViewOp> subview2Op =664      memref::SubViewOp::create(b, loc, subview1, offsets2, sizes2, strides2);665  Value subview2 = subview2Op->getResult();666 667  // Test that getBaseEntity on the nested subview returns the first subview668  // (since our implementation returns the immediate source, not the ultimate669  // base)670  Value baseEntity = getBaseEntity(subview2);671  EXPECT_EQ(baseEntity, subview1);672 673  // Test that calling getBaseEntity again returns the original base674  Value ultimateBase = getBaseEntity(baseEntity);675  EXPECT_EQ(ultimateBase, baseMemref);676}677 678//===----------------------------------------------------------------------===//679// isValidSymbolUse Tests680//===----------------------------------------------------------------------===//681 682TEST_F(OpenACCUtilsTest, isValidSymbolUseNoDefiningOp) {683  // Create a memref.get_global that references a non-existent global684  auto memrefType = MemRefType::get({10}, b.getI32Type());685  llvm::StringRef globalName = "nonexistent_global";686  SymbolRefAttr nonExistentSymbol = SymbolRefAttr::get(&context, globalName);687 688  OwningOpRef<memref::GetGlobalOp> getGlobalOp =689      memref::GetGlobalOp::create(b, loc, memrefType, globalName);690 691  Operation *definingOp = nullptr;692  bool result =693      isValidSymbolUse(getGlobalOp.get(), nonExistentSymbol, &definingOp);694 695  EXPECT_FALSE(result);696  EXPECT_EQ(definingOp, nullptr);697}698 699TEST_F(OpenACCUtilsTest, isValidSymbolUseRecipe) {700  // Create a module to hold the recipe701  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);702  Block *moduleBlock = module->getBody();703 704  OpBuilder::InsertionGuard guard(b);705  b.setInsertionPointToStart(moduleBlock);706 707  // Create a private recipe (any recipe type would work)708  auto i32Type = b.getI32Type();709  llvm::StringRef recipeName = "test_recipe";710  OwningOpRef<PrivateRecipeOp> recipeOp =711      PrivateRecipeOp::create(b, loc, recipeName, i32Type);712 713  // Create a value to privatize714  auto memrefTy = MemRefType::get({10}, b.getI32Type());715  OwningOpRef<memref::AllocaOp> allocOp =716      memref::AllocaOp::create(b, loc, memrefTy);717  TypedValue<PointerLikeType> varPtr =718      cast<TypedValue<PointerLikeType>>(allocOp->getResult());719 720  // Create a private op as the user operation721  OwningOpRef<PrivateOp> privateOp = PrivateOp::create(722      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);723 724  // Create a symbol reference to the recipe725  SymbolRefAttr recipeSymbol = SymbolRefAttr::get(&context, recipeName);726 727  Operation *definingOp = nullptr;728  bool result = isValidSymbolUse(privateOp.get(), recipeSymbol, &definingOp);729 730  EXPECT_TRUE(result);731  EXPECT_EQ(definingOp, recipeOp.get());732}733 734TEST_F(OpenACCUtilsTest, isValidSymbolUseFunctionWithRoutineInfo) {735  // Create a module to hold the function736  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);737  Block *moduleBlock = module->getBody();738 739  OpBuilder::InsertionGuard guard(b);740  b.setInsertionPointToStart(moduleBlock);741 742  // Create a function with routine_info attribute743  auto funcType = b.getFunctionType({}, {});744  llvm::StringRef funcName = "routine_func";745  OwningOpRef<func::FuncOp> funcOp =746      func::FuncOp::create(b, loc, funcName, funcType);747 748  // Add routine_info attribute with a reference to a routine749  SmallVector<SymbolRefAttr> routineRefs = {750      SymbolRefAttr::get(&context, "acc_routine")};751  funcOp.get()->setAttr(getRoutineInfoAttrName(),752                        RoutineInfoAttr::get(&context, routineRefs));753 754  // Create a call operation that uses the function symbol755  SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, funcName);756  OwningOpRef<func::CallOp> callOp = func::CallOp::create(757      b, loc, funcSymbol, funcType.getResults(), ValueRange{});758 759  Operation *definingOp = nullptr;760  bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);761 762  EXPECT_TRUE(result);763  EXPECT_NE(definingOp, nullptr);764}765 766TEST_F(OpenACCUtilsTest, isValidSymbolUseLLVMIntrinsic) {767  // Create a module to hold the function768  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);769  Block *moduleBlock = module->getBody();770 771  OpBuilder::InsertionGuard guard(b);772  b.setInsertionPointToStart(moduleBlock);773 774  // Create a private function with LLVM intrinsic name775  auto funcType = b.getFunctionType({b.getF32Type()}, {b.getF32Type()});776  llvm::StringRef intrinsicName = "llvm.sqrt.f32";777  OwningOpRef<func::FuncOp> funcOp =778      func::FuncOp::create(b, loc, intrinsicName, funcType);779 780  // Set visibility to private (required for intrinsics)781  funcOp->setPrivate();782 783  // Create a call operation that uses the intrinsic784  SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, intrinsicName);785  OwningOpRef<func::CallOp> callOp = func::CallOp::create(786      b, loc, funcSymbol, funcType.getResults(), ValueRange{});787 788  Operation *definingOp = nullptr;789  bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);790 791  EXPECT_TRUE(result);792  EXPECT_NE(definingOp, nullptr);793}794 795TEST_F(OpenACCUtilsTest, isValidSymbolUseFunctionNotIntrinsic) {796  // Create a module to hold the function797  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);798  Block *moduleBlock = module->getBody();799 800  OpBuilder::InsertionGuard guard(b);801  b.setInsertionPointToStart(moduleBlock);802 803  // Create a private function that looks like intrinsic but isn't804  auto funcType = b.getFunctionType({}, {});805  llvm::StringRef funcName = "llvm.not_a_real_intrinsic";806  OwningOpRef<func::FuncOp> funcOp =807      func::FuncOp::create(b, loc, funcName, funcType);808  funcOp->setPrivate();809 810  // Create a call operation that uses the function811  SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, funcName);812  OwningOpRef<func::CallOp> callOp = func::CallOp::create(813      b, loc, funcSymbol, funcType.getResults(), ValueRange{});814 815  Operation *definingOp = nullptr;816  bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);817 818  // Should be false because it's not a valid intrinsic and has no819  // acc.routine_info attr820  EXPECT_FALSE(result);821  EXPECT_NE(definingOp, nullptr);822}823 824TEST_F(OpenACCUtilsTest, isValidSymbolUseWithDeclareAttr) {825  // Create a module to hold a function826  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);827  Block *moduleBlock = module->getBody();828 829  OpBuilder::InsertionGuard guard(b);830  b.setInsertionPointToStart(moduleBlock);831 832  // Create a function with declare attribute833  auto funcType = b.getFunctionType({}, {});834  llvm::StringRef funcName = "declared_func";835  OwningOpRef<func::FuncOp> funcOp =836      func::FuncOp::create(b, loc, funcName, funcType);837 838  // Add declare attribute839  funcOp.get()->setAttr(840      getDeclareAttrName(),841      DeclareAttr::get(&context,842                       DataClauseAttr::get(&context, DataClause::acc_copy)));843 844  // Create a call operation that uses the function845  SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, funcName);846  OwningOpRef<func::CallOp> callOp = func::CallOp::create(847      b, loc, funcSymbol, funcType.getResults(), ValueRange{});848 849  Operation *definingOp = nullptr;850  bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);851 852  EXPECT_TRUE(result);853  EXPECT_NE(definingOp, nullptr);854}855 856TEST_F(OpenACCUtilsTest, isValidSymbolUseWithoutValidAttributes) {857  // Create a module to hold a function858  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);859  Block *moduleBlock = module->getBody();860 861  OpBuilder::InsertionGuard guard(b);862  b.setInsertionPointToStart(moduleBlock);863 864  // Create a function without any special attributes865  auto funcType = b.getFunctionType({}, {});866  llvm::StringRef funcName = "regular_func";867  OwningOpRef<func::FuncOp> funcOp =868      func::FuncOp::create(b, loc, funcName, funcType);869 870  // Create a call operation that uses the function871  SymbolRefAttr funcSymbol = SymbolRefAttr::get(&context, funcName);872  OwningOpRef<func::CallOp> callOp = func::CallOp::create(873      b, loc, funcSymbol, funcType.getResults(), ValueRange{});874 875  Operation *definingOp = nullptr;876  bool result = isValidSymbolUse(callOp.get(), funcSymbol, &definingOp);877 878  // Should be false - no routine_info, not an intrinsic, no declare attribute879  EXPECT_FALSE(result);880  EXPECT_NE(definingOp, nullptr);881}882 883TEST_F(OpenACCUtilsTest, isValidSymbolUseNullDefiningOpPtr) {884  // Create a module to hold a recipe885  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);886  Block *moduleBlock = module->getBody();887 888  OpBuilder::InsertionGuard guard(b);889  b.setInsertionPointToStart(moduleBlock);890 891  // Create a private recipe892  auto i32Type = b.getI32Type();893  llvm::StringRef recipeName = "test_recipe";894  OwningOpRef<PrivateRecipeOp> recipeOp =895      PrivateRecipeOp::create(b, loc, recipeName, i32Type);896 897  // Create a value to privatize898  auto memrefTy = MemRefType::get({10}, b.getI32Type());899  OwningOpRef<memref::AllocaOp> allocOp =900      memref::AllocaOp::create(b, loc, memrefTy);901  TypedValue<PointerLikeType> varPtr =902      cast<TypedValue<PointerLikeType>>(allocOp->getResult());903 904  // Create a private op as the user operation905  OwningOpRef<PrivateOp> privateOp = PrivateOp::create(906      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);907 908  // Create a symbol reference to the recipe909  SymbolRefAttr recipeSymbol = SymbolRefAttr::get(&context, recipeName);910 911  // Call without definingOpPtr (nullptr)912  bool result = isValidSymbolUse(privateOp.get(), recipeSymbol, nullptr);913 914  EXPECT_TRUE(result);915}916