-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[acc] Expand OpenACCSupport to provide getRecipeName and emitNYI #165628
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Extends OpenACCSupport utilities to include recipe name generation and error reporting for unsupported features, providing foundation for variable privatization handling. Changes: - Add RecipeKind enum (private, firstprivate, reduction) for APIs that request a specific kind of recipe - Add getRecipeName() API to OpenACCSupport and OpenACCUtils that generates recipe names from types (e.g., "privatization_memref_5x10xf32_") - Add emitNYI() API to OpenACCSupport for graceful handling of not-yet-implemented cases - Generalize MemRefPointerLikeModel template to support UnrankedMemRefType - Add unit tests and integration tests for new APIs
|
@llvm/pr-subscribers-mlir Author: Razvan Lupusoru (razvanlupusoru) ChangesExtends OpenACCSupport utilities to include recipe name generation and error reporting for unsupported features, providing foundation for variable privatization handling. Changes:
Full diff: https://github.com/llvm/llvm-project/pull/165628.diff 10 Files Affected:
diff --git a/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h b/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
index 0833462ea0509..d9b2646b753f3 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
+++ b/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
@@ -58,6 +58,9 @@
namespace mlir {
namespace acc {
+// Forward declaration for RecipeKind enum
+enum class RecipeKind : uint32_t;
+
namespace detail {
/// This class contains internal trait classes used by OpenACCSupport.
/// It follows the Concept-Model pattern used throughout MLIR (e.g., in
@@ -69,6 +72,13 @@ struct OpenACCSupportTraits {
/// Get the variable name for a given MLIR value.
virtual std::string getVariableName(Value v) = 0;
+
+ /// Get the recipe name for a given kind, type and value.
+ virtual std::string getRecipeName(RecipeKind kind, Type type,
+ Value var) = 0;
+
+ // Used to report a case that is not supported by the implementation.
+ virtual InFlightDiagnostic emitNYI(Location loc, const Twine &message) = 0;
};
/// This class wraps a concrete OpenACCSupport implementation and forwards
@@ -84,6 +94,14 @@ struct OpenACCSupportTraits {
return impl.getVariableName(v);
}
+ std::string getRecipeName(RecipeKind kind, Type type, Value var) final {
+ return impl.getRecipeName(kind, type, var);
+ }
+
+ InFlightDiagnostic emitNYI(Location loc, const Twine &message) final {
+ return impl.emitNYI(loc, message);
+ }
+
private:
ImplT impl;
};
@@ -118,6 +136,24 @@ class OpenACCSupport {
/// \return The variable name, or an empty string if unavailable.
std::string getVariableName(Value v);
+ /// Get the recipe name for a given type and value.
+ ///
+ /// \param kind The kind of recipe to get the name for.
+ /// \param type The type to get the recipe name for. Can be null if the
+ /// var is provided instead.
+ /// \param var The MLIR value to get the recipe name for. Can be null if
+ /// the type is provided instead.
+ /// \return The recipe name, or an empty string if not available.
+ std::string getRecipeName(RecipeKind kind, Type type, Value var);
+
+ /// Report a case that is not yet supported by the implementation.
+ ///
+ /// \param loc The location to report the unsupported case at.
+ /// \param message The message to report.
+ /// \return An in-flight diagnostic object that can be used to report the
+ /// unsupported case.
+ InFlightDiagnostic emitNYI(Location loc, const Twine &message);
+
/// Signal that this analysis should always be preserved so that
/// underlying implementation registration is not lost.
bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index a18c18af8a753..2f4517ddfe754 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -152,6 +152,26 @@ def OpenACC_LoopParMode : I32EnumAttr<
let genSpecializedAttr = 0;
}
+def OpenACC_PrivateRecipe : I32EnumAttrCase<"private_recipe", 0>;
+def OpenACC_FirstprivateRecipe : I32EnumAttrCase<"firstprivate_recipe", 1>;
+def OpenACC_ReductionRecipe : I32EnumAttrCase<"reduction_recipe", 2>;
+
+def OpenACC_RecipeKind : I32EnumAttr<
+ "RecipeKind",
+ "Encodes the options for kinds of recipes availabie in acc dialect",
+ [
+ OpenACC_PrivateRecipe, OpenACC_FirstprivateRecipe,
+ OpenACC_ReductionRecipe]> {
+ let cppNamespace = "::mlir::acc";
+ let genSpecializedAttr = 0;
+}
+
+def OpenACC_RecipeKindAttr : EnumAttr<OpenACC_Dialect,
+ OpenACC_RecipeKind,
+ "recipe_kind"> {
+ let assemblyFormat = [{ ```<` $value `>` }];
+}
+
// Type used in operation below.
def IntOrIndex : AnyTypeOf<[AnyInteger, Index]>;
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index 0ee88c6f47b67..98a30c8d9530a 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -43,6 +43,14 @@ mlir::acc::VariableTypeCategory getTypeCategory(mlir::Value var);
/// empty string if no name is found.
std::string getVariableName(mlir::Value v);
+/// Get the recipe name for a given kind, type and value.
+///
+/// \param kind The kind of recipe to get the name for.
+/// \param type The type to get the recipe name for. Can be null if the
+/// var is provided instead.
+/// \return The recipe name, or an empty string if not available.
+std::string getRecipeName(mlir::acc::RecipeKind kind, mlir::Type type);
+
} // namespace acc
} // namespace mlir
diff --git a/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp b/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
index f6b4534794eaf..78d0e9ab6369c 100644
--- a/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
+++ b/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
@@ -22,5 +22,25 @@ std::string OpenACCSupport::getVariableName(Value v) {
return acc::getVariableName(v);
}
+std::string OpenACCSupport::getRecipeName(RecipeKind kind, Type type,
+ Value var) {
+ if (impl)
+ return impl->getRecipeName(kind, type, var);
+ // The default implementation assumes that only type matters
+ // and the actual instance of variable is not relevant.
+ auto recipeName = acc::getRecipeName(kind, type);
+ if (recipeName.empty())
+ emitNYI(var ? var.getLoc() : UnknownLoc::get(type.getContext()),
+ "variable privatization (incomplete recipe name handling)");
+ return recipeName;
+}
+
+InFlightDiagnostic OpenACCSupport::emitNYI(Location loc,
+ const Twine &message) {
+ if (impl)
+ return impl->emitNYI(loc, message);
+ return mlir::emitError(loc, "not yet implemented: " + message);
+}
+
} // namespace acc
} // namespace mlir
diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index ca46629919dba..35eba724a9059 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -50,11 +50,11 @@ static void attachVarNameAttr(Operation *op, OpBuilder &builder,
}
}
+template <typename T>
struct MemRefPointerLikeModel
- : public PointerLikeType::ExternalModel<MemRefPointerLikeModel,
- MemRefType> {
+ : public PointerLikeType::ExternalModel<MemRefPointerLikeModel<T>, T> {
Type getElementType(Type pointer) const {
- return cast<MemRefType>(pointer).getElementType();
+ return cast<T>(pointer).getElementType();
}
mlir::acc::VariableTypeCategory
@@ -63,7 +63,7 @@ struct MemRefPointerLikeModel
if (auto mappableTy = dyn_cast<MappableType>(varType)) {
return mappableTy.getTypeCategory(varPtr);
}
- auto memrefTy = cast<MemRefType>(pointer);
+ auto memrefTy = cast<T>(pointer);
if (!memrefTy.hasRank()) {
// This memref is unranked - aka it could have any rank, including a
// rank of 0 which could mean scalar. For now, return uncategorized.
@@ -296,7 +296,10 @@ void OpenACCDialect::initialize() {
// By attaching interfaces here, we make the OpenACC dialect dependent on
// the other dialects. This is probably better than having dialects like LLVM
// and memref be dependent on OpenACC.
- MemRefType::attachInterface<MemRefPointerLikeModel>(*getContext());
+ MemRefType::attachInterface<MemRefPointerLikeModel<MemRefType>>(
+ *getContext());
+ UnrankedMemRefType::attachInterface<
+ MemRefPointerLikeModel<UnrankedMemRefType>>(*getContext());
LLVM::LLVMPointerType::attachInterface<LLVMPointerPointerLikeModel>(
*getContext());
}
diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index 89adda82646e6..660c3138af0ec 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -11,6 +11,7 @@
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/Interfaces/ViewLikeInterface.h"
#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/Casting.h"
mlir::Operation *mlir::acc::getEnclosingComputeOp(mlir::Region ®ion) {
mlir::Operation *parentOp = region.getParentOp();
@@ -106,3 +107,41 @@ std::string mlir::acc::getVariableName(mlir::Value v) {
return "";
}
+
+std::string mlir::acc::getRecipeName(mlir::acc::RecipeKind kind,
+ mlir::Type type) {
+ assert(kind == mlir::acc::RecipeKind::private_recipe ||
+ kind == mlir::acc::RecipeKind::firstprivate_recipe ||
+ kind == mlir::acc::RecipeKind::reduction_recipe);
+ if (!llvm::isa<mlir::acc::PointerLikeType, mlir::acc::MappableType>(type))
+ return "";
+
+ std::string recipeName;
+ llvm::raw_string_ostream ss(recipeName);
+ ss << (kind == mlir::acc::RecipeKind::private_recipe ? "privatization_"
+ : kind == mlir::acc::RecipeKind::firstprivate_recipe
+ ? "firstprivatization_"
+ : "reduction_");
+
+ // Print the type using its dialect-defined textual format.
+ type.print(ss);
+ ss.flush();
+
+ // Replace invalid characters (anything that's not a letter, number, or
+ // period) since this needs to be a valid MLIR identifier.
+ for (char &c : recipeName) {
+ if (!std::isalnum(static_cast<unsigned char>(c)) && c != '.' && c != '_') {
+ if (c == '?')
+ c = 'U';
+ else if (c == '*')
+ c = 'Z';
+ else if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' ||
+ c == '}' || c == '<' || c == '>')
+ c = '_';
+ else
+ c = 'X';
+ }
+ }
+
+ return recipeName;
+}
diff --git a/mlir/test/Dialect/OpenACC/support-analysis-recipename.mlir b/mlir/test/Dialect/OpenACC/support-analysis-recipename.mlir
new file mode 100644
index 0000000000000..8ea53b5d0f4d4
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/support-analysis-recipename.mlir
@@ -0,0 +1,78 @@
+// RUN: mlir-opt %s -split-input-file -test-acc-support | FileCheck %s
+
+// Test private recipe with 2D memref
+func.func @test_private_2d_memref() {
+ // Create a 2D memref
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xf32>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xf32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<5x10xf32>)="privatization_memref_5x10xf32_"
+
+ return
+}
+
+// -----
+
+// Test firstprivate recipe with 2D memref
+func.func @test_firstprivate_2d_memref() {
+ // Create a 2D memref
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<firstprivate_recipe>} : memref<8x16xf64>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<firstprivate_recipe>} : memref<8x16xf64>
+ // CHECK-NEXT: getRecipeName(kind=firstprivate_recipe, type=memref<8x16xf64>)="firstprivatization_memref_8x16xf64_"
+
+ return
+}
+
+// -----
+
+// Test reduction recipe with 2D memref
+func.func @test_reduction_2d_memref() {
+ // Create a 2D memref
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<reduction_recipe>} : memref<4x8xi32>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<reduction_recipe>} : memref<4x8xi32>
+ // CHECK-NEXT: getRecipeName(kind=reduction_recipe, type=memref<4x8xi32>)="reduction_memref_4x8xi32_"
+
+ return
+}
+
+// -----
+
+// Test private recipe with dynamic memref
+func.func @test_private_dynamic_memref(%arg0: memref<5x10xi32>) {
+ // Cast to dynamic dimensions
+ %0 = memref.cast %arg0 {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xi32> to memref<?x10xi32>
+
+ // CHECK: op=%{{.*}} = memref.cast %{{.*}} {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xi32> to memref<?x10xi32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<?x10xi32>)="privatization_memref_Ux10xi32_"
+
+ return
+}
+
+// -----
+
+// Test private recipe with scalar memref
+func.func @test_private_scalar_memref() {
+ // Create a scalar memref (no dimensions)
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<i32>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<i32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<i32>)="privatization_memref_i32_"
+
+ return
+}
+
+// -----
+
+// Test private recipe with unranked memref
+func.func @test_private_unranked_memref(%arg0: memref<10xi32>) {
+ // Cast to unranked memref
+ %0 = memref.cast %arg0 {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<10xi32> to memref<*xi32>
+
+ // CHECK: op=%{{.*}} = memref.cast %{{.*}} {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<10xi32> to memref<*xi32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<*xi32>)="privatization_memref_Zxi32_"
+
+ return
+}
+
diff --git a/mlir/test/Dialect/OpenACC/support-analysis-unsupported.mlir b/mlir/test/Dialect/OpenACC/support-analysis-unsupported.mlir
new file mode 100644
index 0000000000000..c4d5b81a1380a
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/support-analysis-unsupported.mlir
@@ -0,0 +1,18 @@
+// RUN: mlir-opt %s --split-input-file -test-acc-support -verify-diagnostics
+
+// Test emitNYI with a simple message
+func.func @test_emit_nyi() {
+ // expected-error @below {{not yet implemented: Unsupported feature in OpenACC}}
+ %0 = memref.alloca() {test.emit_nyi = "Unsupported feature in OpenACC"} : memref<10xi32>
+ return
+}
+
+// -----
+
+// Test recipe name on load operation from scalar memref
+func.func @test_recipe_load_scalar() {
+ %0 = memref.alloca() : memref<i32>
+ // expected-error @below {{not yet implemented: variable privatization (incomplete recipe name handling)}}
+ %1 = memref.load %0[] {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<i32>
+ return
+}
diff --git a/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp b/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp
index 8bf984bdc2632..7c8b08489c62e 100644
--- a/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp
+++ b/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp
@@ -57,6 +57,28 @@ void TestOpenACCSupportPass::runOnOperation() {
<< "\"\n";
}
}
+
+ // Check for test.recipe_name attribute. This is the marker used to identify
+ // the operations that need to be tested for getRecipeName.
+ if (auto recipeAttr =
+ op->getAttrOfType<RecipeKindAttr>("test.recipe_name")) {
+ RecipeKind kind = recipeAttr.getValue();
+ // Get the type from the first result if available
+ if (op->getNumResults() > 0) {
+ Type type = op->getResult(0).getType();
+ std::string recipeName =
+ support.getRecipeName(kind, type, op->getResult(0));
+ llvm::outs() << "op=" << *op
+ << "\n\tgetRecipeName(kind=" << stringifyRecipeKind(kind)
+ << ", type=" << type << ")=\"" << recipeName << "\"\n";
+ }
+ }
+
+ // Check for test.emit_nyi attribute. This is the marker used to
+ // test whether the not yet implemented case is reported correctly.
+ if (auto messageAttr = op->getAttrOfType<StringAttr>("test.emit_nyi")) {
+ support.emitNYI(op->getLoc(), messageAttr.getValue());
+ }
});
}
diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
index 3fbbcc90a67c9..f1fe53c15a6f5 100644
--- a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
@@ -485,3 +485,88 @@ 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_");
+}
|
|
@llvm/pr-subscribers-mlir-openacc Author: Razvan Lupusoru (razvanlupusoru) ChangesExtends OpenACCSupport utilities to include recipe name generation and error reporting for unsupported features, providing foundation for variable privatization handling. Changes:
Full diff: https://github.com/llvm/llvm-project/pull/165628.diff 10 Files Affected:
diff --git a/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h b/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
index 0833462ea0509..d9b2646b753f3 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
+++ b/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
@@ -58,6 +58,9 @@
namespace mlir {
namespace acc {
+// Forward declaration for RecipeKind enum
+enum class RecipeKind : uint32_t;
+
namespace detail {
/// This class contains internal trait classes used by OpenACCSupport.
/// It follows the Concept-Model pattern used throughout MLIR (e.g., in
@@ -69,6 +72,13 @@ struct OpenACCSupportTraits {
/// Get the variable name for a given MLIR value.
virtual std::string getVariableName(Value v) = 0;
+
+ /// Get the recipe name for a given kind, type and value.
+ virtual std::string getRecipeName(RecipeKind kind, Type type,
+ Value var) = 0;
+
+ // Used to report a case that is not supported by the implementation.
+ virtual InFlightDiagnostic emitNYI(Location loc, const Twine &message) = 0;
};
/// This class wraps a concrete OpenACCSupport implementation and forwards
@@ -84,6 +94,14 @@ struct OpenACCSupportTraits {
return impl.getVariableName(v);
}
+ std::string getRecipeName(RecipeKind kind, Type type, Value var) final {
+ return impl.getRecipeName(kind, type, var);
+ }
+
+ InFlightDiagnostic emitNYI(Location loc, const Twine &message) final {
+ return impl.emitNYI(loc, message);
+ }
+
private:
ImplT impl;
};
@@ -118,6 +136,24 @@ class OpenACCSupport {
/// \return The variable name, or an empty string if unavailable.
std::string getVariableName(Value v);
+ /// Get the recipe name for a given type and value.
+ ///
+ /// \param kind The kind of recipe to get the name for.
+ /// \param type The type to get the recipe name for. Can be null if the
+ /// var is provided instead.
+ /// \param var The MLIR value to get the recipe name for. Can be null if
+ /// the type is provided instead.
+ /// \return The recipe name, or an empty string if not available.
+ std::string getRecipeName(RecipeKind kind, Type type, Value var);
+
+ /// Report a case that is not yet supported by the implementation.
+ ///
+ /// \param loc The location to report the unsupported case at.
+ /// \param message The message to report.
+ /// \return An in-flight diagnostic object that can be used to report the
+ /// unsupported case.
+ InFlightDiagnostic emitNYI(Location loc, const Twine &message);
+
/// Signal that this analysis should always be preserved so that
/// underlying implementation registration is not lost.
bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index a18c18af8a753..2f4517ddfe754 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -152,6 +152,26 @@ def OpenACC_LoopParMode : I32EnumAttr<
let genSpecializedAttr = 0;
}
+def OpenACC_PrivateRecipe : I32EnumAttrCase<"private_recipe", 0>;
+def OpenACC_FirstprivateRecipe : I32EnumAttrCase<"firstprivate_recipe", 1>;
+def OpenACC_ReductionRecipe : I32EnumAttrCase<"reduction_recipe", 2>;
+
+def OpenACC_RecipeKind : I32EnumAttr<
+ "RecipeKind",
+ "Encodes the options for kinds of recipes availabie in acc dialect",
+ [
+ OpenACC_PrivateRecipe, OpenACC_FirstprivateRecipe,
+ OpenACC_ReductionRecipe]> {
+ let cppNamespace = "::mlir::acc";
+ let genSpecializedAttr = 0;
+}
+
+def OpenACC_RecipeKindAttr : EnumAttr<OpenACC_Dialect,
+ OpenACC_RecipeKind,
+ "recipe_kind"> {
+ let assemblyFormat = [{ ```<` $value `>` }];
+}
+
// Type used in operation below.
def IntOrIndex : AnyTypeOf<[AnyInteger, Index]>;
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index 0ee88c6f47b67..98a30c8d9530a 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -43,6 +43,14 @@ mlir::acc::VariableTypeCategory getTypeCategory(mlir::Value var);
/// empty string if no name is found.
std::string getVariableName(mlir::Value v);
+/// Get the recipe name for a given kind, type and value.
+///
+/// \param kind The kind of recipe to get the name for.
+/// \param type The type to get the recipe name for. Can be null if the
+/// var is provided instead.
+/// \return The recipe name, or an empty string if not available.
+std::string getRecipeName(mlir::acc::RecipeKind kind, mlir::Type type);
+
} // namespace acc
} // namespace mlir
diff --git a/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp b/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
index f6b4534794eaf..78d0e9ab6369c 100644
--- a/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
+++ b/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
@@ -22,5 +22,25 @@ std::string OpenACCSupport::getVariableName(Value v) {
return acc::getVariableName(v);
}
+std::string OpenACCSupport::getRecipeName(RecipeKind kind, Type type,
+ Value var) {
+ if (impl)
+ return impl->getRecipeName(kind, type, var);
+ // The default implementation assumes that only type matters
+ // and the actual instance of variable is not relevant.
+ auto recipeName = acc::getRecipeName(kind, type);
+ if (recipeName.empty())
+ emitNYI(var ? var.getLoc() : UnknownLoc::get(type.getContext()),
+ "variable privatization (incomplete recipe name handling)");
+ return recipeName;
+}
+
+InFlightDiagnostic OpenACCSupport::emitNYI(Location loc,
+ const Twine &message) {
+ if (impl)
+ return impl->emitNYI(loc, message);
+ return mlir::emitError(loc, "not yet implemented: " + message);
+}
+
} // namespace acc
} // namespace mlir
diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index ca46629919dba..35eba724a9059 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -50,11 +50,11 @@ static void attachVarNameAttr(Operation *op, OpBuilder &builder,
}
}
+template <typename T>
struct MemRefPointerLikeModel
- : public PointerLikeType::ExternalModel<MemRefPointerLikeModel,
- MemRefType> {
+ : public PointerLikeType::ExternalModel<MemRefPointerLikeModel<T>, T> {
Type getElementType(Type pointer) const {
- return cast<MemRefType>(pointer).getElementType();
+ return cast<T>(pointer).getElementType();
}
mlir::acc::VariableTypeCategory
@@ -63,7 +63,7 @@ struct MemRefPointerLikeModel
if (auto mappableTy = dyn_cast<MappableType>(varType)) {
return mappableTy.getTypeCategory(varPtr);
}
- auto memrefTy = cast<MemRefType>(pointer);
+ auto memrefTy = cast<T>(pointer);
if (!memrefTy.hasRank()) {
// This memref is unranked - aka it could have any rank, including a
// rank of 0 which could mean scalar. For now, return uncategorized.
@@ -296,7 +296,10 @@ void OpenACCDialect::initialize() {
// By attaching interfaces here, we make the OpenACC dialect dependent on
// the other dialects. This is probably better than having dialects like LLVM
// and memref be dependent on OpenACC.
- MemRefType::attachInterface<MemRefPointerLikeModel>(*getContext());
+ MemRefType::attachInterface<MemRefPointerLikeModel<MemRefType>>(
+ *getContext());
+ UnrankedMemRefType::attachInterface<
+ MemRefPointerLikeModel<UnrankedMemRefType>>(*getContext());
LLVM::LLVMPointerType::attachInterface<LLVMPointerPointerLikeModel>(
*getContext());
}
diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index 89adda82646e6..660c3138af0ec 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -11,6 +11,7 @@
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/Interfaces/ViewLikeInterface.h"
#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/Casting.h"
mlir::Operation *mlir::acc::getEnclosingComputeOp(mlir::Region ®ion) {
mlir::Operation *parentOp = region.getParentOp();
@@ -106,3 +107,41 @@ std::string mlir::acc::getVariableName(mlir::Value v) {
return "";
}
+
+std::string mlir::acc::getRecipeName(mlir::acc::RecipeKind kind,
+ mlir::Type type) {
+ assert(kind == mlir::acc::RecipeKind::private_recipe ||
+ kind == mlir::acc::RecipeKind::firstprivate_recipe ||
+ kind == mlir::acc::RecipeKind::reduction_recipe);
+ if (!llvm::isa<mlir::acc::PointerLikeType, mlir::acc::MappableType>(type))
+ return "";
+
+ std::string recipeName;
+ llvm::raw_string_ostream ss(recipeName);
+ ss << (kind == mlir::acc::RecipeKind::private_recipe ? "privatization_"
+ : kind == mlir::acc::RecipeKind::firstprivate_recipe
+ ? "firstprivatization_"
+ : "reduction_");
+
+ // Print the type using its dialect-defined textual format.
+ type.print(ss);
+ ss.flush();
+
+ // Replace invalid characters (anything that's not a letter, number, or
+ // period) since this needs to be a valid MLIR identifier.
+ for (char &c : recipeName) {
+ if (!std::isalnum(static_cast<unsigned char>(c)) && c != '.' && c != '_') {
+ if (c == '?')
+ c = 'U';
+ else if (c == '*')
+ c = 'Z';
+ else if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' ||
+ c == '}' || c == '<' || c == '>')
+ c = '_';
+ else
+ c = 'X';
+ }
+ }
+
+ return recipeName;
+}
diff --git a/mlir/test/Dialect/OpenACC/support-analysis-recipename.mlir b/mlir/test/Dialect/OpenACC/support-analysis-recipename.mlir
new file mode 100644
index 0000000000000..8ea53b5d0f4d4
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/support-analysis-recipename.mlir
@@ -0,0 +1,78 @@
+// RUN: mlir-opt %s -split-input-file -test-acc-support | FileCheck %s
+
+// Test private recipe with 2D memref
+func.func @test_private_2d_memref() {
+ // Create a 2D memref
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xf32>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xf32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<5x10xf32>)="privatization_memref_5x10xf32_"
+
+ return
+}
+
+// -----
+
+// Test firstprivate recipe with 2D memref
+func.func @test_firstprivate_2d_memref() {
+ // Create a 2D memref
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<firstprivate_recipe>} : memref<8x16xf64>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<firstprivate_recipe>} : memref<8x16xf64>
+ // CHECK-NEXT: getRecipeName(kind=firstprivate_recipe, type=memref<8x16xf64>)="firstprivatization_memref_8x16xf64_"
+
+ return
+}
+
+// -----
+
+// Test reduction recipe with 2D memref
+func.func @test_reduction_2d_memref() {
+ // Create a 2D memref
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<reduction_recipe>} : memref<4x8xi32>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<reduction_recipe>} : memref<4x8xi32>
+ // CHECK-NEXT: getRecipeName(kind=reduction_recipe, type=memref<4x8xi32>)="reduction_memref_4x8xi32_"
+
+ return
+}
+
+// -----
+
+// Test private recipe with dynamic memref
+func.func @test_private_dynamic_memref(%arg0: memref<5x10xi32>) {
+ // Cast to dynamic dimensions
+ %0 = memref.cast %arg0 {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xi32> to memref<?x10xi32>
+
+ // CHECK: op=%{{.*}} = memref.cast %{{.*}} {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xi32> to memref<?x10xi32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<?x10xi32>)="privatization_memref_Ux10xi32_"
+
+ return
+}
+
+// -----
+
+// Test private recipe with scalar memref
+func.func @test_private_scalar_memref() {
+ // Create a scalar memref (no dimensions)
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<i32>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<i32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<i32>)="privatization_memref_i32_"
+
+ return
+}
+
+// -----
+
+// Test private recipe with unranked memref
+func.func @test_private_unranked_memref(%arg0: memref<10xi32>) {
+ // Cast to unranked memref
+ %0 = memref.cast %arg0 {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<10xi32> to memref<*xi32>
+
+ // CHECK: op=%{{.*}} = memref.cast %{{.*}} {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<10xi32> to memref<*xi32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<*xi32>)="privatization_memref_Zxi32_"
+
+ return
+}
+
diff --git a/mlir/test/Dialect/OpenACC/support-analysis-unsupported.mlir b/mlir/test/Dialect/OpenACC/support-analysis-unsupported.mlir
new file mode 100644
index 0000000000000..c4d5b81a1380a
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/support-analysis-unsupported.mlir
@@ -0,0 +1,18 @@
+// RUN: mlir-opt %s --split-input-file -test-acc-support -verify-diagnostics
+
+// Test emitNYI with a simple message
+func.func @test_emit_nyi() {
+ // expected-error @below {{not yet implemented: Unsupported feature in OpenACC}}
+ %0 = memref.alloca() {test.emit_nyi = "Unsupported feature in OpenACC"} : memref<10xi32>
+ return
+}
+
+// -----
+
+// Test recipe name on load operation from scalar memref
+func.func @test_recipe_load_scalar() {
+ %0 = memref.alloca() : memref<i32>
+ // expected-error @below {{not yet implemented: variable privatization (incomplete recipe name handling)}}
+ %1 = memref.load %0[] {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<i32>
+ return
+}
diff --git a/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp b/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp
index 8bf984bdc2632..7c8b08489c62e 100644
--- a/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp
+++ b/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp
@@ -57,6 +57,28 @@ void TestOpenACCSupportPass::runOnOperation() {
<< "\"\n";
}
}
+
+ // Check for test.recipe_name attribute. This is the marker used to identify
+ // the operations that need to be tested for getRecipeName.
+ if (auto recipeAttr =
+ op->getAttrOfType<RecipeKindAttr>("test.recipe_name")) {
+ RecipeKind kind = recipeAttr.getValue();
+ // Get the type from the first result if available
+ if (op->getNumResults() > 0) {
+ Type type = op->getResult(0).getType();
+ std::string recipeName =
+ support.getRecipeName(kind, type, op->getResult(0));
+ llvm::outs() << "op=" << *op
+ << "\n\tgetRecipeName(kind=" << stringifyRecipeKind(kind)
+ << ", type=" << type << ")=\"" << recipeName << "\"\n";
+ }
+ }
+
+ // Check for test.emit_nyi attribute. This is the marker used to
+ // test whether the not yet implemented case is reported correctly.
+ if (auto messageAttr = op->getAttrOfType<StringAttr>("test.emit_nyi")) {
+ support.emitNYI(op->getLoc(), messageAttr.getValue());
+ }
});
}
diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
index 3fbbcc90a67c9..f1fe53c15a6f5 100644
--- a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
@@ -485,3 +485,88 @@ 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_");
+}
|
|
@llvm/pr-subscribers-openacc Author: Razvan Lupusoru (razvanlupusoru) ChangesExtends OpenACCSupport utilities to include recipe name generation and error reporting for unsupported features, providing foundation for variable privatization handling. Changes:
Full diff: https://github.com/llvm/llvm-project/pull/165628.diff 10 Files Affected:
diff --git a/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h b/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
index 0833462ea0509..d9b2646b753f3 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
+++ b/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
@@ -58,6 +58,9 @@
namespace mlir {
namespace acc {
+// Forward declaration for RecipeKind enum
+enum class RecipeKind : uint32_t;
+
namespace detail {
/// This class contains internal trait classes used by OpenACCSupport.
/// It follows the Concept-Model pattern used throughout MLIR (e.g., in
@@ -69,6 +72,13 @@ struct OpenACCSupportTraits {
/// Get the variable name for a given MLIR value.
virtual std::string getVariableName(Value v) = 0;
+
+ /// Get the recipe name for a given kind, type and value.
+ virtual std::string getRecipeName(RecipeKind kind, Type type,
+ Value var) = 0;
+
+ // Used to report a case that is not supported by the implementation.
+ virtual InFlightDiagnostic emitNYI(Location loc, const Twine &message) = 0;
};
/// This class wraps a concrete OpenACCSupport implementation and forwards
@@ -84,6 +94,14 @@ struct OpenACCSupportTraits {
return impl.getVariableName(v);
}
+ std::string getRecipeName(RecipeKind kind, Type type, Value var) final {
+ return impl.getRecipeName(kind, type, var);
+ }
+
+ InFlightDiagnostic emitNYI(Location loc, const Twine &message) final {
+ return impl.emitNYI(loc, message);
+ }
+
private:
ImplT impl;
};
@@ -118,6 +136,24 @@ class OpenACCSupport {
/// \return The variable name, or an empty string if unavailable.
std::string getVariableName(Value v);
+ /// Get the recipe name for a given type and value.
+ ///
+ /// \param kind The kind of recipe to get the name for.
+ /// \param type The type to get the recipe name for. Can be null if the
+ /// var is provided instead.
+ /// \param var The MLIR value to get the recipe name for. Can be null if
+ /// the type is provided instead.
+ /// \return The recipe name, or an empty string if not available.
+ std::string getRecipeName(RecipeKind kind, Type type, Value var);
+
+ /// Report a case that is not yet supported by the implementation.
+ ///
+ /// \param loc The location to report the unsupported case at.
+ /// \param message The message to report.
+ /// \return An in-flight diagnostic object that can be used to report the
+ /// unsupported case.
+ InFlightDiagnostic emitNYI(Location loc, const Twine &message);
+
/// Signal that this analysis should always be preserved so that
/// underlying implementation registration is not lost.
bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index a18c18af8a753..2f4517ddfe754 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -152,6 +152,26 @@ def OpenACC_LoopParMode : I32EnumAttr<
let genSpecializedAttr = 0;
}
+def OpenACC_PrivateRecipe : I32EnumAttrCase<"private_recipe", 0>;
+def OpenACC_FirstprivateRecipe : I32EnumAttrCase<"firstprivate_recipe", 1>;
+def OpenACC_ReductionRecipe : I32EnumAttrCase<"reduction_recipe", 2>;
+
+def OpenACC_RecipeKind : I32EnumAttr<
+ "RecipeKind",
+ "Encodes the options for kinds of recipes availabie in acc dialect",
+ [
+ OpenACC_PrivateRecipe, OpenACC_FirstprivateRecipe,
+ OpenACC_ReductionRecipe]> {
+ let cppNamespace = "::mlir::acc";
+ let genSpecializedAttr = 0;
+}
+
+def OpenACC_RecipeKindAttr : EnumAttr<OpenACC_Dialect,
+ OpenACC_RecipeKind,
+ "recipe_kind"> {
+ let assemblyFormat = [{ ```<` $value `>` }];
+}
+
// Type used in operation below.
def IntOrIndex : AnyTypeOf<[AnyInteger, Index]>;
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index 0ee88c6f47b67..98a30c8d9530a 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -43,6 +43,14 @@ mlir::acc::VariableTypeCategory getTypeCategory(mlir::Value var);
/// empty string if no name is found.
std::string getVariableName(mlir::Value v);
+/// Get the recipe name for a given kind, type and value.
+///
+/// \param kind The kind of recipe to get the name for.
+/// \param type The type to get the recipe name for. Can be null if the
+/// var is provided instead.
+/// \return The recipe name, or an empty string if not available.
+std::string getRecipeName(mlir::acc::RecipeKind kind, mlir::Type type);
+
} // namespace acc
} // namespace mlir
diff --git a/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp b/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
index f6b4534794eaf..78d0e9ab6369c 100644
--- a/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
+++ b/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
@@ -22,5 +22,25 @@ std::string OpenACCSupport::getVariableName(Value v) {
return acc::getVariableName(v);
}
+std::string OpenACCSupport::getRecipeName(RecipeKind kind, Type type,
+ Value var) {
+ if (impl)
+ return impl->getRecipeName(kind, type, var);
+ // The default implementation assumes that only type matters
+ // and the actual instance of variable is not relevant.
+ auto recipeName = acc::getRecipeName(kind, type);
+ if (recipeName.empty())
+ emitNYI(var ? var.getLoc() : UnknownLoc::get(type.getContext()),
+ "variable privatization (incomplete recipe name handling)");
+ return recipeName;
+}
+
+InFlightDiagnostic OpenACCSupport::emitNYI(Location loc,
+ const Twine &message) {
+ if (impl)
+ return impl->emitNYI(loc, message);
+ return mlir::emitError(loc, "not yet implemented: " + message);
+}
+
} // namespace acc
} // namespace mlir
diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index ca46629919dba..35eba724a9059 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -50,11 +50,11 @@ static void attachVarNameAttr(Operation *op, OpBuilder &builder,
}
}
+template <typename T>
struct MemRefPointerLikeModel
- : public PointerLikeType::ExternalModel<MemRefPointerLikeModel,
- MemRefType> {
+ : public PointerLikeType::ExternalModel<MemRefPointerLikeModel<T>, T> {
Type getElementType(Type pointer) const {
- return cast<MemRefType>(pointer).getElementType();
+ return cast<T>(pointer).getElementType();
}
mlir::acc::VariableTypeCategory
@@ -63,7 +63,7 @@ struct MemRefPointerLikeModel
if (auto mappableTy = dyn_cast<MappableType>(varType)) {
return mappableTy.getTypeCategory(varPtr);
}
- auto memrefTy = cast<MemRefType>(pointer);
+ auto memrefTy = cast<T>(pointer);
if (!memrefTy.hasRank()) {
// This memref is unranked - aka it could have any rank, including a
// rank of 0 which could mean scalar. For now, return uncategorized.
@@ -296,7 +296,10 @@ void OpenACCDialect::initialize() {
// By attaching interfaces here, we make the OpenACC dialect dependent on
// the other dialects. This is probably better than having dialects like LLVM
// and memref be dependent on OpenACC.
- MemRefType::attachInterface<MemRefPointerLikeModel>(*getContext());
+ MemRefType::attachInterface<MemRefPointerLikeModel<MemRefType>>(
+ *getContext());
+ UnrankedMemRefType::attachInterface<
+ MemRefPointerLikeModel<UnrankedMemRefType>>(*getContext());
LLVM::LLVMPointerType::attachInterface<LLVMPointerPointerLikeModel>(
*getContext());
}
diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index 89adda82646e6..660c3138af0ec 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -11,6 +11,7 @@
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/Interfaces/ViewLikeInterface.h"
#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/Casting.h"
mlir::Operation *mlir::acc::getEnclosingComputeOp(mlir::Region ®ion) {
mlir::Operation *parentOp = region.getParentOp();
@@ -106,3 +107,41 @@ std::string mlir::acc::getVariableName(mlir::Value v) {
return "";
}
+
+std::string mlir::acc::getRecipeName(mlir::acc::RecipeKind kind,
+ mlir::Type type) {
+ assert(kind == mlir::acc::RecipeKind::private_recipe ||
+ kind == mlir::acc::RecipeKind::firstprivate_recipe ||
+ kind == mlir::acc::RecipeKind::reduction_recipe);
+ if (!llvm::isa<mlir::acc::PointerLikeType, mlir::acc::MappableType>(type))
+ return "";
+
+ std::string recipeName;
+ llvm::raw_string_ostream ss(recipeName);
+ ss << (kind == mlir::acc::RecipeKind::private_recipe ? "privatization_"
+ : kind == mlir::acc::RecipeKind::firstprivate_recipe
+ ? "firstprivatization_"
+ : "reduction_");
+
+ // Print the type using its dialect-defined textual format.
+ type.print(ss);
+ ss.flush();
+
+ // Replace invalid characters (anything that's not a letter, number, or
+ // period) since this needs to be a valid MLIR identifier.
+ for (char &c : recipeName) {
+ if (!std::isalnum(static_cast<unsigned char>(c)) && c != '.' && c != '_') {
+ if (c == '?')
+ c = 'U';
+ else if (c == '*')
+ c = 'Z';
+ else if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' ||
+ c == '}' || c == '<' || c == '>')
+ c = '_';
+ else
+ c = 'X';
+ }
+ }
+
+ return recipeName;
+}
diff --git a/mlir/test/Dialect/OpenACC/support-analysis-recipename.mlir b/mlir/test/Dialect/OpenACC/support-analysis-recipename.mlir
new file mode 100644
index 0000000000000..8ea53b5d0f4d4
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/support-analysis-recipename.mlir
@@ -0,0 +1,78 @@
+// RUN: mlir-opt %s -split-input-file -test-acc-support | FileCheck %s
+
+// Test private recipe with 2D memref
+func.func @test_private_2d_memref() {
+ // Create a 2D memref
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xf32>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xf32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<5x10xf32>)="privatization_memref_5x10xf32_"
+
+ return
+}
+
+// -----
+
+// Test firstprivate recipe with 2D memref
+func.func @test_firstprivate_2d_memref() {
+ // Create a 2D memref
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<firstprivate_recipe>} : memref<8x16xf64>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<firstprivate_recipe>} : memref<8x16xf64>
+ // CHECK-NEXT: getRecipeName(kind=firstprivate_recipe, type=memref<8x16xf64>)="firstprivatization_memref_8x16xf64_"
+
+ return
+}
+
+// -----
+
+// Test reduction recipe with 2D memref
+func.func @test_reduction_2d_memref() {
+ // Create a 2D memref
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<reduction_recipe>} : memref<4x8xi32>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<reduction_recipe>} : memref<4x8xi32>
+ // CHECK-NEXT: getRecipeName(kind=reduction_recipe, type=memref<4x8xi32>)="reduction_memref_4x8xi32_"
+
+ return
+}
+
+// -----
+
+// Test private recipe with dynamic memref
+func.func @test_private_dynamic_memref(%arg0: memref<5x10xi32>) {
+ // Cast to dynamic dimensions
+ %0 = memref.cast %arg0 {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xi32> to memref<?x10xi32>
+
+ // CHECK: op=%{{.*}} = memref.cast %{{.*}} {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<5x10xi32> to memref<?x10xi32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<?x10xi32>)="privatization_memref_Ux10xi32_"
+
+ return
+}
+
+// -----
+
+// Test private recipe with scalar memref
+func.func @test_private_scalar_memref() {
+ // Create a scalar memref (no dimensions)
+ %0 = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<i32>
+
+ // CHECK: op=%{{.*}} = memref.alloca() {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<i32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<i32>)="privatization_memref_i32_"
+
+ return
+}
+
+// -----
+
+// Test private recipe with unranked memref
+func.func @test_private_unranked_memref(%arg0: memref<10xi32>) {
+ // Cast to unranked memref
+ %0 = memref.cast %arg0 {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<10xi32> to memref<*xi32>
+
+ // CHECK: op=%{{.*}} = memref.cast %{{.*}} {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<10xi32> to memref<*xi32>
+ // CHECK-NEXT: getRecipeName(kind=private_recipe, type=memref<*xi32>)="privatization_memref_Zxi32_"
+
+ return
+}
+
diff --git a/mlir/test/Dialect/OpenACC/support-analysis-unsupported.mlir b/mlir/test/Dialect/OpenACC/support-analysis-unsupported.mlir
new file mode 100644
index 0000000000000..c4d5b81a1380a
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/support-analysis-unsupported.mlir
@@ -0,0 +1,18 @@
+// RUN: mlir-opt %s --split-input-file -test-acc-support -verify-diagnostics
+
+// Test emitNYI with a simple message
+func.func @test_emit_nyi() {
+ // expected-error @below {{not yet implemented: Unsupported feature in OpenACC}}
+ %0 = memref.alloca() {test.emit_nyi = "Unsupported feature in OpenACC"} : memref<10xi32>
+ return
+}
+
+// -----
+
+// Test recipe name on load operation from scalar memref
+func.func @test_recipe_load_scalar() {
+ %0 = memref.alloca() : memref<i32>
+ // expected-error @below {{not yet implemented: variable privatization (incomplete recipe name handling)}}
+ %1 = memref.load %0[] {test.recipe_name = #acc.recipe_kind<private_recipe>} : memref<i32>
+ return
+}
diff --git a/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp b/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp
index 8bf984bdc2632..7c8b08489c62e 100644
--- a/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp
+++ b/mlir/test/lib/Dialect/OpenACC/TestOpenACCSupport.cpp
@@ -57,6 +57,28 @@ void TestOpenACCSupportPass::runOnOperation() {
<< "\"\n";
}
}
+
+ // Check for test.recipe_name attribute. This is the marker used to identify
+ // the operations that need to be tested for getRecipeName.
+ if (auto recipeAttr =
+ op->getAttrOfType<RecipeKindAttr>("test.recipe_name")) {
+ RecipeKind kind = recipeAttr.getValue();
+ // Get the type from the first result if available
+ if (op->getNumResults() > 0) {
+ Type type = op->getResult(0).getType();
+ std::string recipeName =
+ support.getRecipeName(kind, type, op->getResult(0));
+ llvm::outs() << "op=" << *op
+ << "\n\tgetRecipeName(kind=" << stringifyRecipeKind(kind)
+ << ", type=" << type << ")=\"" << recipeName << "\"\n";
+ }
+ }
+
+ // Check for test.emit_nyi attribute. This is the marker used to
+ // test whether the not yet implemented case is reported correctly.
+ if (auto messageAttr = op->getAttrOfType<StringAttr>("test.emit_nyi")) {
+ support.emitNYI(op->getLoc(), messageAttr.getValue());
+ }
});
}
diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
index 3fbbcc90a67c9..f1fe53c15a6f5 100644
--- a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
@@ -485,3 +485,88 @@ 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_");
+}
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
clementval
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
vzakhari
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, Razvan!
…m#165628) Extends OpenACCSupport utilities to include recipe name generation and error reporting for unsupported features, providing foundation for variable privatization handling. Changes: - Add RecipeKind enum (private, firstprivate, reduction) for APIs that request a specific kind of recipe - Add getRecipeName() API to OpenACCSupport and OpenACCUtils that generates recipe names from types (e.g., "privatization_memref_5x10xf32_") - Add emitNYI() API to OpenACCSupport for graceful handling of not-yet-implemented cases - Generalize MemRefPointerLikeModel template to support UnrankedMemRefType - Add unit tests and integration tests for new APIs
…m#165628) Extends OpenACCSupport utilities to include recipe name generation and error reporting for unsupported features, providing foundation for variable privatization handling. Changes: - Add RecipeKind enum (private, firstprivate, reduction) for APIs that request a specific kind of recipe - Add getRecipeName() API to OpenACCSupport and OpenACCUtils that generates recipe names from types (e.g., "privatization_memref_5x10xf32_") - Add emitNYI() API to OpenACCSupport for graceful handling of not-yet-implemented cases - Generalize MemRefPointerLikeModel template to support UnrankedMemRefType - Add unit tests and integration tests for new APIs
Extends OpenACCSupport utilities to include recipe name generation and error reporting for unsupported features, providing foundation for variable privatization handling.
Changes: