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 ¶llelRegion = 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 ¶llelRegion = parallelOp->getRegion();86 Block *parallelBlock = ¶llelRegion.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 ¶llelRegion = parallelOp->getRegion();129 Block *parallelBlock = ¶llelRegion.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 ¶llelRegion = parallelOp->getRegion();154 Block *parallelBlock = ¶llelRegion.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 ¶llelRegion = parallelOp->getRegion();184 Block *parallelBlock = ¶llelRegion.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 ¶llelRegion = parallelOp->getRegion();215 Block *parallelBlock = ¶llelRegion.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 ¶llelRegion = parallelOp->getRegion();240 Block *parallelBlock = ¶llelRegion.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 ¶llelRegion = parallelOp->getRegion();270 Block *parallelBlock = ¶llelRegion.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 ¶llelRegion = parallelOp->getRegion();371 Block *parallelBlock = ¶llelRegion.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