From e70a67d2e103f386e969dc51db7dcb28c333cb1b Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Tue, 7 Oct 2025 10:13:58 -0700 Subject: [PATCH 1/6] [mlir][acc] Extend PointerLikeType to provide alloc, dealloc, copy A variable in an acc data clause operation must have a type that implements either PointerLikeType or a MappableType interface. These interfaces provide the contract that allows acc dialect and its transform passes to interact with a source dialect. One of these requirements is ability to generate code that creates memory for a private copy and ability to initialize that copy from another variable. Thus, update the PointerLikeType API to provide the means to create allocation, deallocation, and copy. This will be used as a way to fill in privatization and firstprivatization recipes. This new API was implemented for memref along with testing to exercise it via the implementation of a testing pass. --- .../Dialect/OpenACC/OpenACCTypeInterfaces.td | 79 +++++ mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp | 113 +++++++ .../OpenACC/pointer-like-interface-alloc.mlir | 24 ++ .../OpenACC/pointer-like-interface-copy.mlir | 23 ++ .../OpenACC/pointer-like-interface-free.mlir | 31 ++ mlir/test/lib/Dialect/CMakeLists.txt | 1 + mlir/test/lib/Dialect/OpenACC/CMakeLists.txt | 16 + mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp | 26 ++ .../OpenACC/TestPointerLikeTypeInterface.cpp | 310 ++++++++++++++++++ mlir/tools/mlir-opt/CMakeLists.txt | 1 + mlir/tools/mlir-opt/mlir-opt.cpp | 2 + 11 files changed, 626 insertions(+) create mode 100644 mlir/test/Dialect/OpenACC/pointer-like-interface-alloc.mlir create mode 100644 mlir/test/Dialect/OpenACC/pointer-like-interface-copy.mlir create mode 100644 mlir/test/Dialect/OpenACC/pointer-like-interface-free.mlir create mode 100644 mlir/test/lib/Dialect/OpenACC/CMakeLists.txt create mode 100644 mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp create mode 100644 mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td index 9123ac34af67d..9a89f8ede191e 100644 --- a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td @@ -70,6 +70,85 @@ def OpenACC_PointerLikeTypeInterface : TypeInterface<"PointerLikeType"> { return ::mlir::acc::VariableTypeCategory::uncategorized; }] >, + InterfaceMethod< + /*description=*/[{ + Generates allocation operations for the pointer-like type. It will create + an allocate that produces memory space for an instance of the current type. + + The `varName` parameter is optional and can be used to provide a name + for the allocated variable. If the current type is represented + in a way that it does not capture the pointee type, `varType` must be + passed in to provide the necessary type information. + + The `originalVar` parameter is optional but enables support for dynamic + types (e.g., dynamic memrefs). When provided, implementations can extract + runtime dimension information from the original variable to create + allocations with matching dynamic sizes. + + Returns true if allocation was successfully generated, false otherwise. + }], + /*retTy=*/"bool", + /*methodName=*/"genAllocate", + /*args=*/(ins "::mlir::OpBuilder &":$builder, + "::mlir::Location":$loc, + "::llvm::StringRef":$varName, + "::mlir::Type":$varType, + "::mlir::Value":$originalVar), + /*methodBody=*/"", + /*defaultImplementation=*/[{ + return false; + }] + >, + InterfaceMethod< + /*description=*/[{ + Generates deallocation operations for the pointer-like type. It deallocates + the instance provided. + + The `varPtr` parameter is required and must represent an instance that was + previously allocated. If the current type is represented in a way that it + does not capture the pointee type, `varType` must be passed in to provide + the necessary type information. Nothing is generated in case the allocate + is `alloca`-like. + + Returns true if deallocation was successfully generated or successfully + deemed as not needed to be generated, false otherwise. + }], + /*retTy=*/"bool", + /*methodName=*/"genFree", + /*args=*/(ins "::mlir::OpBuilder &":$builder, + "::mlir::Location":$loc, + "::mlir::TypedValue<::mlir::acc::PointerLikeType>":$varPtr, + "::mlir::Type":$varType), + /*methodBody=*/"", + /*defaultImplementation=*/[{ + return false; + }] + >, + InterfaceMethod< + /*description=*/[{ + Generates copy operations for the pointer-like type. It copies the memory + from the source to the destination. Typically used to initialize one + variable of this type from another. + + The `destination` and `source` parameters represent the target and source + instances respectively. If the current type is represented in a way that it + does not capture the pointee type, `varType` must be passed in to provide + the necessary type information. + + Returns true if copy was successfully generated, false otherwise. + }], + /*retTy=*/"bool", + /*methodName=*/"genCopy", + /*args=*/(ins "::mlir::OpBuilder &":$builder, + "::mlir::Location":$loc, + "::mlir::TypedValue<::mlir::acc::PointerLikeType>":$destination, + "::mlir::TypedValue<::mlir::acc::PointerLikeType>":$source, + "::mlir::Type":$varType), + /*methodBody=*/"", + /*defaultImplementation=*/[{ + return false; + }] + >, ]; } diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index 6598ac141008f..c2a21acb4d62c 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -7,6 +7,7 @@ // ============================================================================= #include "mlir/Dialect/OpenACC/OpenACC.h" +#include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/LLVMIR/LLVMTypes.h" #include "mlir/Dialect/MemRef/IR/MemRef.h" @@ -44,6 +45,7 @@ struct MemRefPointerLikeModel Type getElementType(Type pointer) const { return cast(pointer).getElementType(); } + mlir::acc::VariableTypeCategory getPointeeTypeCategory(Type pointer, TypedValue varPtr, Type varType) const { @@ -70,6 +72,117 @@ struct MemRefPointerLikeModel assert(memrefTy.getRank() > 0 && "rank expected to be positive"); return mlir::acc::VariableTypeCategory::array; } + + bool genAllocate(Type pointer, OpBuilder &builder, Location loc, + StringRef varName, Type varType, Value originalVar) const { + auto memrefTy = cast(pointer); + + // Check if this is a static memref (all dimensions are known) - if yes + // then we can generate an alloca operation. + if (memrefTy.hasStaticShape()) { + memref::AllocaOp::create(builder, loc, memrefTy); + return true; + } + + // For dynamic memrefs, extract sizes from the original variable if provided. + // Otherwise they cannot be handled. + if (originalVar && originalVar.getType() == memrefTy && + memrefTy.hasRank()) { + SmallVector dynamicSizes; + for (int64_t i = 0; i < memrefTy.getRank(); ++i) { + if (memrefTy.isDynamicDim(i)) { + // Extract the size of dimension i from the original variable + auto indexValue = arith::ConstantIndexOp::create(builder, loc, i); + auto dimSize = + memref::DimOp::create(builder, loc, originalVar, indexValue); + dynamicSizes.push_back(dimSize); + } + // Note: We only add dynamic sizes to the dynamicSizes array + // Static dimensions are handled automatically by AllocOp + } + memref::AllocOp::create(builder, loc, memrefTy, dynamicSizes); + return true; + } + + + // TODO: Unranked not yet supported. + return false; + } + + bool genFree(Type pointer, OpBuilder &builder, Location loc, + TypedValue varPtr, Type varType) const { + if (auto memrefValue = dyn_cast>(varPtr)) { + // Walk through casts to find the original allocation + Value currentValue = memrefValue; + Operation *originalAlloc = nullptr; + + // Follow the chain of operations to find the original allocation + // even if a casted result is provided. + while (currentValue) { + if (auto *definingOp = currentValue.getDefiningOp()) { + // Check if this is an allocation operation + if (isa(definingOp)) { + originalAlloc = definingOp; + break; + } + + // Check if this is a cast operation we can look through + if (auto castOp = dyn_cast(definingOp)) { + currentValue = castOp.getSource(); + continue; + } + + // Check for other cast-like operations + if (auto reinterpretCastOp = + dyn_cast(definingOp)) { + currentValue = reinterpretCastOp.getSource(); + continue; + } + + // If we can't look through this operation, stop + break; + } + // This is a block argument or similar - can't trace further. + break; + } + + if (originalAlloc) { + if (isa(originalAlloc)) { + // This is an alloca - no dealloc needed, but return true (success) + return true; + } + if (isa(originalAlloc)) { + // This is an alloc - generate dealloc + memref::DeallocOp::create(builder, loc, memrefValue); + return true; + } + } + } + + return false; + } + + bool genCopy(Type pointer, OpBuilder &builder, Location loc, + TypedValue destination, + TypedValue source, Type varType) const { + // Generate a copy operation between two memrefs + auto destMemref = dyn_cast_if_present>(destination); + auto srcMemref = dyn_cast_if_present>(source); + + // As per memref documentation, source and destination must have same + // element type and shape in order to be compatible. We do not want to fail + // with an IR verification error - thus check that before generating the + // copy operation. + if (destMemref && srcMemref && + destMemref.getType().getElementType() == + srcMemref.getType().getElementType() && + destMemref.getType().getShape() == srcMemref.getType().getShape()) { + memref::CopyOp::create(builder, loc, srcMemref, destMemref); + return true; + } + + return false; + } }; struct LLVMPointerPointerLikeModel diff --git a/mlir/test/Dialect/OpenACC/pointer-like-interface-alloc.mlir b/mlir/test/Dialect/OpenACC/pointer-like-interface-alloc.mlir new file mode 100644 index 0000000000000..603ace85072ac --- /dev/null +++ b/mlir/test/Dialect/OpenACC/pointer-like-interface-alloc.mlir @@ -0,0 +1,24 @@ +// RUN: mlir-opt %s --split-input-file --pass-pipeline="builtin.module(func.func(test-acc-pointer-like-interface{test-mode=alloc}))" 2>&1 | FileCheck %s + +func.func @test_static_memref_alloc() { + %0 = memref.alloca() {test.ptr} : memref<10x20xf32> + // CHECK: Successfully generated alloc for operation: %[[ORIG:.*]] = memref.alloca() {test.ptr} : memref<10x20xf32> + // CHECK: Generated: %{{.*}} = memref.alloca() : memref<10x20xf32> + return +} + +// ----- + +func.func @test_dynamic_memref_alloc() { + %c10 = arith.constant 10 : index + %c20 = arith.constant 20 : index + %orig = memref.alloc(%c10, %c20) {test.ptr} : memref + + // CHECK: Successfully generated alloc for operation: %[[ORIG:.*]] = memref.alloc(%[[C10:.*]], %[[C20:.*]]) {test.ptr} : memref + // CHECK: Generated: %[[C0:.*]] = arith.constant 0 : index + // CHECK: Generated: %[[DIM0:.*]] = memref.dim %[[ORIG]], %[[C0]] : memref + // CHECK: Generated: %[[C1:.*]] = arith.constant 1 : index + // CHECK: Generated: %[[DIM1:.*]] = memref.dim %[[ORIG]], %[[C1]] : memref + // CHECK: Generated: %{{.*}} = memref.alloc(%[[DIM0]], %[[DIM1]]) : memref + return +} diff --git a/mlir/test/Dialect/OpenACC/pointer-like-interface-copy.mlir b/mlir/test/Dialect/OpenACC/pointer-like-interface-copy.mlir new file mode 100644 index 0000000000000..9220d84a29f94 --- /dev/null +++ b/mlir/test/Dialect/OpenACC/pointer-like-interface-copy.mlir @@ -0,0 +1,23 @@ +// RUN: mlir-opt %s --split-input-file --pass-pipeline="builtin.module(func.func(test-acc-pointer-like-interface{test-mode=copy}))" 2>&1 | FileCheck %s + +func.func @test_copy_static() { + %src = memref.alloca() {test.src_ptr} : memref<10x20xf32> + %dest = memref.alloca() {test.dest_ptr} : memref<10x20xf32> + + // CHECK: Successfully generated copy from source: %[[SRC:.*]] = memref.alloca() {test.src_ptr} : memref<10x20xf32> to destination: %[[DEST:.*]] = memref.alloca() {test.dest_ptr} : memref<10x20xf32> + // CHECK: Generated: memref.copy %[[SRC]], %[[DEST]] : memref<10x20xf32> to memref<10x20xf32> + return +} + +// ----- + +func.func @test_copy_dynamic() { + %c10 = arith.constant 10 : index + %c20 = arith.constant 20 : index + %src = memref.alloc(%c10, %c20) {test.src_ptr} : memref + %dest = memref.alloc(%c10, %c20) {test.dest_ptr} : memref + + // CHECK: Successfully generated copy from source: %[[SRC:.*]] = memref.alloc(%[[C10:.*]], %[[C20:.*]]) {test.src_ptr} : memref to destination: %[[DEST:.*]] = memref.alloc(%[[C10]], %[[C20]]) {test.dest_ptr} : memref + // CHECK: Generated: memref.copy %[[SRC]], %[[DEST]] : memref to memref + return +} diff --git a/mlir/test/Dialect/OpenACC/pointer-like-interface-free.mlir b/mlir/test/Dialect/OpenACC/pointer-like-interface-free.mlir new file mode 100644 index 0000000000000..ecf4f75fe06d1 --- /dev/null +++ b/mlir/test/Dialect/OpenACC/pointer-like-interface-free.mlir @@ -0,0 +1,31 @@ +// RUN: mlir-opt %s --split-input-file --pass-pipeline="builtin.module(func.func(test-acc-pointer-like-interface{test-mode=free}))" 2>&1 | FileCheck %s + +func.func @test_static_memref_free() { + %0 = memref.alloca() {test.ptr} : memref<10x20xf32> + // CHECK: Successfully generated free for operation: %[[ORIG:.*]] = memref.alloca() {test.ptr} : memref<10x20xf32> + // CHECK-NOT: Generated + return +} + +// ----- + +func.func @test_dynamic_memref_free() { + %c10 = arith.constant 10 : index + %c20 = arith.constant 20 : index + %orig = memref.alloc(%c10, %c20) {test.ptr} : memref + + // CHECK: Successfully generated free for operation: %[[ORIG:.*]] = memref.alloc(%[[C10:.*]], %[[C20:.*]]) {test.ptr} : memref + // CHECK: Generated: memref.dealloc %[[ORIG]] : memref + return +} + +// ----- + +func.func @test_cast_walking_free() { + %0 = memref.alloca() : memref<10x20xf32> + %1 = memref.cast %0 {test.ptr} : memref<10x20xf32> to memref + + // CHECK: Successfully generated free for operation: %[[CAST:.*]] = memref.cast %[[ALLOCA:.*]] {test.ptr} : memref<10x20xf32> to memref + // CHECK-NOT: Generated + return +} diff --git a/mlir/test/lib/Dialect/CMakeLists.txt b/mlir/test/lib/Dialect/CMakeLists.txt index 3b7bd9b9637a8..e31140a6f6ba7 100644 --- a/mlir/test/lib/Dialect/CMakeLists.txt +++ b/mlir/test/lib/Dialect/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(Math) add_subdirectory(MemRef) add_subdirectory(Shard) add_subdirectory(NVGPU) +add_subdirectory(OpenACC) add_subdirectory(SCF) add_subdirectory(Shape) add_subdirectory(SPIRV) diff --git a/mlir/test/lib/Dialect/OpenACC/CMakeLists.txt b/mlir/test/lib/Dialect/OpenACC/CMakeLists.txt new file mode 100644 index 0000000000000..f84055df1b6c6 --- /dev/null +++ b/mlir/test/lib/Dialect/OpenACC/CMakeLists.txt @@ -0,0 +1,16 @@ +add_mlir_library(MLIROpenACCTestPasses + TestOpenACC.cpp + TestPointerLikeTypeInterface.cpp + + EXCLUDE_FROM_LIBMLIR +) +mlir_target_link_libraries(MLIROpenACCTestPasses PUBLIC + MLIRIR + MLIRArithDialect + MLIRFuncDialect + MLIRMemRefDialect + MLIROpenACCDialect + MLIRPass + MLIRSupport +) + diff --git a/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp b/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp new file mode 100644 index 0000000000000..6f7bdde176aa7 --- /dev/null +++ b/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp @@ -0,0 +1,26 @@ +//===- TestOpenACC.cpp - OpenACC Test Registration ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains unified registration for all OpenACC test passes. +// +//===----------------------------------------------------------------------===// + +namespace mlir { +namespace test { + +// Forward declarations of individual test pass registration functions +void registerTestPointerLikeTypeInterfacePass(); + +// Unified registration function for all OpenACC tests +void registerTestOpenACC() { + registerTestPointerLikeTypeInterfacePass(); +} + +} // namespace test +} // namespace mlir + diff --git a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp new file mode 100644 index 0000000000000..fc573d4318b5b --- /dev/null +++ b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp @@ -0,0 +1,310 @@ +//===- TestPointerLikeTypeInterface.cpp - Test PointerLikeType interface -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains test passes for testing the OpenACC PointerLikeType +// interface methods. +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/OpenACC/OpenACC.h" +#include "mlir/IR/Builders.h" +#include "mlir/Pass/Pass.h" +#include "llvm/Support/CommandLine.h" + +using namespace mlir; +using namespace mlir::acc; + +namespace { + +struct OperationTracker : public OpBuilder::Listener { + SmallVector insertedOps; + + void notifyOperationInserted(Operation *op, + OpBuilder::InsertPoint previous) override { + insertedOps.push_back(op); + } +}; + +struct TestPointerLikeTypeInterfacePass + : public PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestPointerLikeTypeInterfacePass) + + TestPointerLikeTypeInterfacePass() = default; + TestPointerLikeTypeInterfacePass(const TestPointerLikeTypeInterfacePass &pass) + : PassWrapper(pass) { + testMode = pass.testMode; + } + + Pass::Option testMode{ + *this, "test-mode", + llvm::cl::desc("Test mode: walk, alloc, copy, or free"), + llvm::cl::init("walk")}; + + StringRef getArgument() const override { + return "test-acc-pointer-like-interface"; + } + + StringRef getDescription() const override { + return "Test OpenACC PointerLikeType interface methods on any implementing " + "type"; + } + + void runOnOperation() override; + + void getDependentDialects(DialectRegistry ®istry) const override { + registry.insert(); + registry.insert(); + registry.insert(); + } + +private: + void walkAndPrint(); + void testGenAllocate(Operation *op, Value result, PointerLikeType pointerType, + OpBuilder &builder); + void testGenFree(Operation *op, Value result, PointerLikeType pointerType, + OpBuilder &builder); + void testGenCopy(Operation *srcOp, Operation *destOp, Value srcResult, Value destResult, + PointerLikeType pointerType, OpBuilder &builder); + + struct PointerCandidate { + Operation *op; + Value result; + PointerLikeType pointerType; + }; +}; + +void TestPointerLikeTypeInterfacePass::runOnOperation() { + if (testMode == "walk") { + walkAndPrint(); + return; + } + + auto func = getOperation(); + OpBuilder builder(&getContext()); + + if (testMode == "alloc" || testMode == "free") { + // Collect all candidates first + SmallVector candidates; + func.walk([&](Operation *op) { + if (op->hasAttr("test.ptr")) { + for (auto result : op->getResults()) { + if (isa(result.getType())) { + candidates.push_back( + {op, result, cast(result.getType())}); + break; // Only take the first PointerLikeType result + } + } + } + }); + + // Now test all candidates + for (const auto &candidate : candidates) { + if (testMode == "alloc") { + testGenAllocate(candidate.op, candidate.result, candidate.pointerType, + builder); + } else if (testMode == "free") { + testGenFree(candidate.op, candidate.result, candidate.pointerType, + builder); + } + } + } else if (testMode == "copy") { + // Collect all source and destination candidates + SmallVector sources, destinations; + + func.walk([&](Operation *op) { + if (op->hasAttr("test.src_ptr")) { + for (auto result : op->getResults()) { + if (isa(result.getType())) { + sources.push_back( + {op, result, cast(result.getType())}); + break; + } + } + } + if (op->hasAttr("test.dest_ptr")) { + for (auto result : op->getResults()) { + if (isa(result.getType())) { + destinations.push_back( + {op, result, cast(result.getType())}); + break; + } + } + } + }); + + // Try copying from each source to each destination + for (const auto &src : sources) { + for (const auto &dest : destinations) { + testGenCopy(src.op, dest.op, src.result, dest.result, src.pointerType, builder); + } + } + } +} + +void TestPointerLikeTypeInterfacePass::walkAndPrint() { + auto func = getOperation(); + + func.walk([&](Operation *op) { + // Look for operations marked with "test.ptr", "test.src_ptr", or + // "test.dest_ptr" + if (op->hasAttr("test.ptr") || op->hasAttr("test.src_ptr") || + op->hasAttr("test.dest_ptr")) { + llvm::errs() << "Operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + + // Check each result to see if it's a PointerLikeType + for (auto result : op->getResults()) { + if (isa(result.getType())) { + llvm::errs() << " Result " << result.getResultNumber() + << " is PointerLikeType: "; + result.getType().print(llvm::errs()); + llvm::errs() << "\n"; + } else { + llvm::errs() << " Result " << result.getResultNumber() + << " is NOT PointerLikeType: "; + result.getType().print(llvm::errs()); + llvm::errs() << "\n"; + } + } + + if (op->getNumResults() == 0) { + llvm::errs() << " Operation has no results\n"; + } + + llvm::errs() << "\n"; + } + }); +} + +void TestPointerLikeTypeInterfacePass::testGenAllocate( + Operation *op, Value result, PointerLikeType pointerType, + OpBuilder &builder) { + Location loc = op->getLoc(); + + // Create a new builder with the listener and set insertion point + OperationTracker tracker; + OpBuilder newBuilder(op->getContext()); + newBuilder.setListener(&tracker); + newBuilder.setInsertionPointAfter(op); + + // Call the genAllocate API + bool success = pointerType.genAllocate(newBuilder, loc, "test_alloc", + result.getType(), result); + + if (success) { + llvm::errs() << "Successfully generated alloc for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + + // Print all operations that were inserted + for (Operation *insertedOp : tracker.insertedOps) { + llvm::errs() << "\tGenerated: "; + insertedOp->print(llvm::errs()); + llvm::errs() << "\n"; + } + } else { + llvm::errs() << "Failed to generate alloc for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + } +} + +void TestPointerLikeTypeInterfacePass::testGenFree(Operation *op, Value result, + PointerLikeType pointerType, + OpBuilder &builder) { + Location loc = op->getLoc(); + + // Create a new builder with the listener and set insertion point + OperationTracker tracker; + OpBuilder newBuilder(op->getContext()); + newBuilder.setListener(&tracker); + newBuilder.setInsertionPointAfter(op); + + // Call the genFree API + auto typedResult = cast>(result); + bool success = + pointerType.genFree(newBuilder, loc, typedResult, result.getType()); + + if (success) { + llvm::errs() << "Successfully generated free for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + + // Print all operations that were inserted + for (Operation *insertedOp : tracker.insertedOps) { + llvm::errs() << "\tGenerated: "; + insertedOp->print(llvm::errs()); + llvm::errs() << "\n"; + } + } else { + llvm::errs() << "Failed to generate free for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + } +} + +void TestPointerLikeTypeInterfacePass::testGenCopy(Operation *srcOp, + Operation *destOp, + Value srcResult, + Value destResult, + PointerLikeType pointerType, + OpBuilder &builder) { + Location loc = destOp->getLoc(); + + // Create a new builder with the listener and set insertion point + OperationTracker tracker; + OpBuilder newBuilder(destOp->getContext()); + newBuilder.setListener(&tracker); + newBuilder.setInsertionPointAfter(destOp); + + // Call the genCopy API with the provided source and destination + auto typedSrc = cast>(srcResult); + auto typedDest = cast>(destResult); + bool success = pointerType.genCopy(newBuilder, loc, typedDest, typedSrc, + srcResult.getType()); + + if (success) { + llvm::errs() << "Successfully generated copy from source: "; + srcOp->print(llvm::errs()); + llvm::errs() << " to destination: "; + destOp->print(llvm::errs()); + llvm::errs() << "\n"; + + // Print all operations that were inserted + for (Operation *insertedOp : tracker.insertedOps) { + llvm::errs() << "\tGenerated: "; + insertedOp->print(llvm::errs()); + llvm::errs() << "\n"; + } + } else { + llvm::errs() << "Failed to generate copy from source: "; + srcOp->print(llvm::errs()); + llvm::errs() << " to destination: "; + destOp->print(llvm::errs()); + llvm::errs() << "\n"; + } +} + +} // namespace + +//===----------------------------------------------------------------------===// +// Pass Registration +//===----------------------------------------------------------------------===// + +namespace mlir { +namespace test { +void registerTestPointerLikeTypeInterfacePass() { + PassRegistration(); +} +} // namespace test +} // namespace mlir diff --git a/mlir/tools/mlir-opt/CMakeLists.txt b/mlir/tools/mlir-opt/CMakeLists.txt index 7cc6e78ca08c2..c607ccfa80e3c 100644 --- a/mlir/tools/mlir-opt/CMakeLists.txt +++ b/mlir/tools/mlir-opt/CMakeLists.txt @@ -28,6 +28,7 @@ if(MLIR_INCLUDE_TESTS) MLIRTestMemRefToLLVMWithTransforms MLIRShardTest MLIRNVGPUTestPasses + MLIROpenACCTestPasses MLIRSCFTestPasses MLIRShapeTestPasses MLIRSPIRVTestPasses diff --git a/mlir/tools/mlir-opt/mlir-opt.cpp b/mlir/tools/mlir-opt/mlir-opt.cpp index e4620c009af8c..6432fae615f88 100644 --- a/mlir/tools/mlir-opt/mlir-opt.cpp +++ b/mlir/tools/mlir-opt/mlir-opt.cpp @@ -135,6 +135,7 @@ void registerTestShardSimplificationsPass(); void registerTestMultiBuffering(); void registerTestNextAccessPass(); void registerTestNVGPULowerings(); +void registerTestOpenACC(); void registerTestOneShotModuleBufferizePass(); void registerTestOpaqueLoc(); void registerTestOpLoweringPasses(); @@ -282,6 +283,7 @@ void registerTestPasses() { mlir::test::registerTestMultiBuffering(); mlir::test::registerTestNextAccessPass(); mlir::test::registerTestNVGPULowerings(); + mlir::test::registerTestOpenACC(); mlir::test::registerTestOneShotModuleBufferizePass(); mlir::test::registerTestOpaqueLoc(); mlir::test::registerTestOpLoweringPasses(); From b17b2896a21c2dd50f76b9b8427c64a9b5f165f6 Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Tue, 7 Oct 2025 10:34:16 -0700 Subject: [PATCH 2/6] Fix formatting --- mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp | 5 ++--- mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp | 4 +--- .../OpenACC/TestPointerLikeTypeInterface.cpp | 19 +++++++++---------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index c2a21acb4d62c..065b413763073 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -84,8 +84,8 @@ struct MemRefPointerLikeModel return true; } - // For dynamic memrefs, extract sizes from the original variable if provided. - // Otherwise they cannot be handled. + // For dynamic memrefs, extract sizes from the original variable if + // provided. Otherwise they cannot be handled. if (originalVar && originalVar.getType() == memrefTy && memrefTy.hasRank()) { SmallVector dynamicSizes; @@ -104,7 +104,6 @@ struct MemRefPointerLikeModel return true; } - // TODO: Unranked not yet supported. return false; } diff --git a/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp b/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp index 6f7bdde176aa7..ebc3956f459c9 100644 --- a/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp +++ b/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp @@ -17,9 +17,7 @@ namespace test { void registerTestPointerLikeTypeInterfacePass(); // Unified registration function for all OpenACC tests -void registerTestOpenACC() { - registerTestPointerLikeTypeInterfacePass(); -} +void registerTestOpenACC() { registerTestPointerLikeTypeInterfacePass(); } } // namespace test } // namespace mlir diff --git a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp index fc573d4318b5b..40752b70ae689 100644 --- a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp +++ b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp @@ -26,7 +26,7 @@ namespace { struct OperationTracker : public OpBuilder::Listener { SmallVector insertedOps; - + void notifyOperationInserted(Operation *op, OpBuilder::InsertPoint previous) override { insertedOps.push_back(op); @@ -72,8 +72,9 @@ struct TestPointerLikeTypeInterfacePass OpBuilder &builder); void testGenFree(Operation *op, Value result, PointerLikeType pointerType, OpBuilder &builder); - void testGenCopy(Operation *srcOp, Operation *destOp, Value srcResult, Value destResult, - PointerLikeType pointerType, OpBuilder &builder); + void testGenCopy(Operation *srcOp, Operation *destOp, Value srcResult, + Value destResult, PointerLikeType pointerType, + OpBuilder &builder); struct PointerCandidate { Operation *op; @@ -144,7 +145,8 @@ void TestPointerLikeTypeInterfacePass::runOnOperation() { // Try copying from each source to each destination for (const auto &src : sources) { for (const auto &dest : destinations) { - testGenCopy(src.op, dest.op, src.result, dest.result, src.pointerType, builder); + testGenCopy(src.op, dest.op, src.result, dest.result, src.pointerType, + builder); } } } @@ -253,12 +255,9 @@ void TestPointerLikeTypeInterfacePass::testGenFree(Operation *op, Value result, } } -void TestPointerLikeTypeInterfacePass::testGenCopy(Operation *srcOp, - Operation *destOp, - Value srcResult, - Value destResult, - PointerLikeType pointerType, - OpBuilder &builder) { +void TestPointerLikeTypeInterfacePass::testGenCopy( + Operation *srcOp, Operation *destOp, Value srcResult, Value destResult, + PointerLikeType pointerType, OpBuilder &builder) { Location loc = destOp->getLoc(); // Create a new builder with the listener and set insertion point From 809abd1ec0bf88ad7bfbd98175282887bf2ad92e Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Tue, 7 Oct 2025 11:00:01 -0700 Subject: [PATCH 3/6] Remove extraneous line --- mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp b/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp index ebc3956f459c9..98862401748e1 100644 --- a/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp +++ b/mlir/test/lib/Dialect/OpenACC/TestOpenACC.cpp @@ -21,4 +21,3 @@ void registerTestOpenACC() { registerTestPointerLikeTypeInterfacePass(); } } // namespace test } // namespace mlir - From d1abaf5ec90f1b3ecfd91cb9674c6e41b7f4bf68 Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Wed, 8 Oct 2025 09:36:31 -0700 Subject: [PATCH 4/6] Update genAllocate signature to return Value When generating recipes, the allocated value needs to be obtainable so that it can be threaded through to the copying and freeing regions. Thus instead of returning true/false, return value just for genAllocate. --- .../mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td | 7 ++++--- mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp | 14 +++++++------- .../OpenACC/TestPointerLikeTypeInterface.cpp | 6 +++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td index 9a89f8ede191e..0d16255c5a994 100644 --- a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td @@ -85,9 +85,10 @@ def OpenACC_PointerLikeTypeInterface : TypeInterface<"PointerLikeType"> { runtime dimension information from the original variable to create allocations with matching dynamic sizes. - Returns true if allocation was successfully generated, false otherwise. + Returns a Value representing the result of the allocation. If no value + is returned, it means the allocation was not successfully generated. }], - /*retTy=*/"bool", + /*retTy=*/"::mlir::Value", /*methodName=*/"genAllocate", /*args=*/(ins "::mlir::OpBuilder &":$builder, "::mlir::Location":$loc, @@ -96,7 +97,7 @@ def OpenACC_PointerLikeTypeInterface : TypeInterface<"PointerLikeType"> { "::mlir::Value":$originalVar), /*methodBody=*/"", /*defaultImplementation=*/[{ - return false; + return {}; }] >, InterfaceMethod< diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index 065b413763073..1ab9581cfba31 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -73,15 +73,15 @@ struct MemRefPointerLikeModel return mlir::acc::VariableTypeCategory::array; } - bool genAllocate(Type pointer, OpBuilder &builder, Location loc, - StringRef varName, Type varType, Value originalVar) const { + mlir::Value genAllocate(Type pointer, OpBuilder &builder, Location loc, + StringRef varName, Type varType, + Value originalVar) const { auto memrefTy = cast(pointer); // Check if this is a static memref (all dimensions are known) - if yes // then we can generate an alloca operation. if (memrefTy.hasStaticShape()) { - memref::AllocaOp::create(builder, loc, memrefTy); - return true; + return memref::AllocaOp::create(builder, loc, memrefTy).getResult(); } // For dynamic memrefs, extract sizes from the original variable if @@ -100,12 +100,12 @@ struct MemRefPointerLikeModel // Note: We only add dynamic sizes to the dynamicSizes array // Static dimensions are handled automatically by AllocOp } - memref::AllocOp::create(builder, loc, memrefTy, dynamicSizes); - return true; + return memref::AllocOp::create(builder, loc, memrefTy, dynamicSizes) + .getResult(); } // TODO: Unranked not yet supported. - return false; + return {}; } bool genFree(Type pointer, OpBuilder &builder, Location loc, diff --git a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp index 40752b70ae689..1ce9c5d9efcb0 100644 --- a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp +++ b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp @@ -200,10 +200,10 @@ void TestPointerLikeTypeInterfacePass::testGenAllocate( newBuilder.setInsertionPointAfter(op); // Call the genAllocate API - bool success = pointerType.genAllocate(newBuilder, loc, "test_alloc", - result.getType(), result); + Value allocRes = pointerType.genAllocate(newBuilder, loc, "test_alloc", + result.getType(), result); - if (success) { + if (allocRes) { llvm::errs() << "Successfully generated alloc for operation: "; op->print(llvm::errs()); llvm::errs() << "\n"; From 0251f47eeb6f492ca4064c2946299f85770ade7a Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Wed, 8 Oct 2025 10:00:44 -0700 Subject: [PATCH 5/6] Remove extra braces --- mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index 1ab9581cfba31..6564a4ecdccd3 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -80,9 +80,8 @@ struct MemRefPointerLikeModel // Check if this is a static memref (all dimensions are known) - if yes // then we can generate an alloca operation. - if (memrefTy.hasStaticShape()) { + if (memrefTy.hasStaticShape()) return memref::AllocaOp::create(builder, loc, memrefTy).getResult(); - } // For dynamic memrefs, extract sizes from the original variable if // provided. Otherwise they cannot be handled. From e6d71e1317262ec95dee55dc1c5bb33b55a9d2ed Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Wed, 8 Oct 2025 10:06:36 -0700 Subject: [PATCH 6/6] More brace removal to match LLVM guidelines --- .../OpenACC/TestPointerLikeTypeInterface.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp index 1ce9c5d9efcb0..85f92833a3269 100644 --- a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp +++ b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp @@ -109,13 +109,12 @@ void TestPointerLikeTypeInterfacePass::runOnOperation() { // Now test all candidates for (const auto &candidate : candidates) { - if (testMode == "alloc") { + if (testMode == "alloc") testGenAllocate(candidate.op, candidate.result, candidate.pointerType, builder); - } else if (testMode == "free") { + else if (testMode == "free") testGenFree(candidate.op, candidate.result, candidate.pointerType, builder); - } } } else if (testMode == "copy") { // Collect all source and destination candidates @@ -143,12 +142,10 @@ void TestPointerLikeTypeInterfacePass::runOnOperation() { }); // Try copying from each source to each destination - for (const auto &src : sources) { - for (const auto &dest : destinations) { + for (const auto &src : sources) + for (const auto &dest : destinations) testGenCopy(src.op, dest.op, src.result, dest.result, src.pointerType, builder); - } - } } } @@ -179,9 +176,8 @@ void TestPointerLikeTypeInterfacePass::walkAndPrint() { } } - if (op->getNumResults() == 0) { + if (op->getNumResults() == 0) llvm::errs() << " Operation has no results\n"; - } llvm::errs() << "\n"; }