From 91465baa8fb657e18c92f1c7651302b5a09fed69 Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Tue, 2 Dec 2025 10:39:34 -0800 Subject: [PATCH 1/4] [acc][flang] Add genLoad and genStore to PointerLikeType This patch extends the OpenACC PointerLikeType interface with two new methods for generating load and store operations, enabling dialect-agnostic memory access patterns. New Interface Methods: - genLoad(builder, loc, srcPtr, valueType): Generates a load operation from a pointer-like value. Returns the loaded value. - genStore(builder, loc, valueToStore, destPtr): Generates a store operation to a pointer-like value. Implementations provided for FIR pointer-like types, memref type (rank-0 only), and LLVM pointer types. Extended TestPointerLikeTypeInterface.cpp with 'load' and 'store' test modes. --- .../Support/FIROpenACCTypeInterfaces.h | 9 ++ .../Support/FIROpenACCTypeInterfaces.cpp | 112 ++++++++++++++++++ .../OpenACC/pointer-like-interface-load.mlir | 85 +++++++++++++ .../OpenACC/pointer-like-interface-store.mlir | 75 ++++++++++++ .../Dialect/OpenACC/OpenACCTypeInterfaces.td | 44 +++++++ mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp | 56 +++++++++ .../OpenACC/pointer-like-interface-load.mlir | 29 +++++ .../OpenACC/pointer-like-interface-store.mlir | 39 ++++++ .../OpenACC/TestPointerLikeTypeInterface.cpp | 109 ++++++++++++++++- 9 files changed, 556 insertions(+), 2 deletions(-) create mode 100644 flang/test/Fir/OpenACC/pointer-like-interface-load.mlir create mode 100644 flang/test/Fir/OpenACC/pointer-like-interface-store.mlir create mode 100644 mlir/test/Dialect/OpenACC/pointer-like-interface-load.mlir create mode 100644 mlir/test/Dialect/OpenACC/pointer-like-interface-store.mlir diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h index 3167c554abbdd..38150b294f4e1 100644 --- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h +++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h @@ -43,6 +43,15 @@ struct OpenACCPointerLikeModel mlir::TypedValue destination, mlir::TypedValue source, mlir::Type varType) const; + + mlir::Value genLoad(mlir::Type pointer, mlir::OpBuilder &builder, + mlir::Location loc, + mlir::TypedValue srcPtr, + mlir::Type valueType) const; + + bool genStore(mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::Value valueToStore, + mlir::TypedValue destPtr) const; }; template diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp index ae0f5fb8197fa..a6109c3a04010 100644 --- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp +++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp @@ -1014,4 +1014,116 @@ template bool OpenACCPointerLikeModel::genCopy( mlir::TypedValue source, mlir::Type varType) const; +template +mlir::Value OpenACCPointerLikeModel::genLoad( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::TypedValue srcPtr, + mlir::Type valueType) const { + + // Unwrap to get the pointee type. + mlir::Type pointeeTy = fir::dyn_cast_ptrEleTy(pointer); + assert(pointeeTy && "expected pointee type to be extractable"); + + // Box types contain both a descriptor and referenced data. The genLoad API + // handles simple loads and cannot properly manage both parts. + if (fir::isa_box_type(pointeeTy)) + return {}; + + // Unlimited polymorphic (class(*)) cannot be handled because type is unknown. + if (fir::isUnlimitedPolymorphicType(pointeeTy)) + return {}; + + // Return empty for dynamic size types because the load logic + // cannot be determined simply from the type. + if (fir::hasDynamicSize(pointeeTy)) + return {}; + + mlir::Value loadedValue = fir::LoadOp::create(builder, loc, srcPtr); + + // If valueType is provided and differs from the loaded type, insert a convert + if (valueType && loadedValue.getType() != valueType) { + return fir::ConvertOp::create(builder, loc, valueType, loadedValue); + } + + return loadedValue; +} + +template mlir::Value OpenACCPointerLikeModel::genLoad( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::TypedValue srcPtr, + mlir::Type valueType) const; + +template mlir::Value OpenACCPointerLikeModel::genLoad( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::TypedValue srcPtr, + mlir::Type valueType) const; + +template mlir::Value OpenACCPointerLikeModel::genLoad( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::TypedValue srcPtr, + mlir::Type valueType) const; + +template mlir::Value OpenACCPointerLikeModel::genLoad( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::TypedValue srcPtr, + mlir::Type valueType) const; + +template +bool OpenACCPointerLikeModel::genStore( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::Value valueToStore, + mlir::TypedValue destPtr) const { + + // Unwrap to get the pointee type. + mlir::Type pointeeTy = fir::dyn_cast_ptrEleTy(pointer); + assert(pointeeTy && "expected pointee type to be extractable"); + + // Box types contain both a descriptor and referenced data. The genStore API + // handles simple stores and cannot properly manage both parts. + if (fir::isa_box_type(pointeeTy)) + return false; + + // Unlimited polymorphic (class(*)) cannot be handled because type is unknown. + if (fir::isUnlimitedPolymorphicType(pointeeTy)) + return false; + + // Return false for dynamic size types because the store logic + // cannot be determined simply from the type. + if (fir::hasDynamicSize(pointeeTy)) + return false; + + // Get the type from the value being stored + mlir::Type valueType = valueToStore.getType(); + mlir::Value convertedValue = valueToStore; + + // If the value type differs from the pointee type, insert a convert + if (valueType != pointeeTy) { + convertedValue = + fir::ConvertOp::create(builder, loc, pointeeTy, valueToStore); + } + + fir::StoreOp::create(builder, loc, convertedValue, destPtr); + return true; +} + +template bool OpenACCPointerLikeModel::genStore( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::Value valueToStore, + mlir::TypedValue destPtr) const; + +template bool OpenACCPointerLikeModel::genStore( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::Value valueToStore, + mlir::TypedValue destPtr) const; + +template bool OpenACCPointerLikeModel::genStore( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::Value valueToStore, + mlir::TypedValue destPtr) const; + +template bool OpenACCPointerLikeModel::genStore( + mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, + mlir::Value valueToStore, + mlir::TypedValue destPtr) const; + } // namespace fir::acc diff --git a/flang/test/Fir/OpenACC/pointer-like-interface-load.mlir b/flang/test/Fir/OpenACC/pointer-like-interface-load.mlir new file mode 100644 index 0000000000000..79a944280445f --- /dev/null +++ b/flang/test/Fir/OpenACC/pointer-like-interface-load.mlir @@ -0,0 +1,85 @@ +// RUN: fir-opt %s --split-input-file --pass-pipeline="builtin.module(func.func(test-acc-pointer-like-interface{test-mode=load}))" 2>&1 | FileCheck %s + +func.func @test_load_scalar_f32() { + %ptr = fir.alloca f32 {test.ptr} + // CHECK: Successfully generated load for operation: %{{.*}} = fir.alloca f32 {test.ptr} + // CHECK: Loaded value type: f32 + // CHECK: Generated: %{{.*}} = fir.load %{{.*}} : !fir.ref + return +} + +// ----- + +func.func @test_load_scalar_i32() { + %ptr = fir.alloca i32 {test.ptr} + // CHECK: Successfully generated load for operation: %{{.*}} = fir.alloca i32 {test.ptr} + // CHECK: Loaded value type: i32 + // CHECK: Generated: %{{.*}} = fir.load %{{.*}} : !fir.ref + return +} + +// ----- + +func.func @test_load_scalar_i64() { + %ptr = fir.alloca i64 {test.ptr} + // CHECK: Successfully generated load for operation: %{{.*}} = fir.alloca i64 {test.ptr} + // CHECK: Loaded value type: i64 + // CHECK: Generated: %{{.*}} = fir.load %{{.*}} : !fir.ref + return +} + +// ----- + +func.func @test_load_heap_scalar() { + %ptr = fir.allocmem f64 {test.ptr} + // CHECK: Successfully generated load for operation: %{{.*}} = fir.allocmem f64 {test.ptr} + // CHECK: Loaded value type: f64 + // CHECK: Generated: %{{.*}} = fir.load %{{.*}} : !fir.heap + return +} + +// ----- + +func.func @test_load_logical() { + %ptr = fir.alloca !fir.logical<4> {test.ptr} + // CHECK: Successfully generated load for operation: %{{.*}} = fir.alloca !fir.logical<4> {test.ptr} + // CHECK: Loaded value type: !fir.logical<4> + // CHECK: Generated: %{{.*}} = fir.load %{{.*}} : !fir.ref> + return +} + +// ----- + +func.func @test_load_derived_type() { + %ptr = fir.alloca !fir.type<_QTt{i:i32}> {test.ptr} + // CHECK: Successfully generated load for operation: %{{.*}} = fir.alloca !fir.type<_QTt{i:i32}> {test.ptr} + // CHECK: Loaded value type: !fir.type<_QTt{i:i32}> + // CHECK: Generated: %{{.*}} = fir.load %{{.*}} : !fir.ref> + return +} + +// ----- + +func.func @test_load_dynamic_array_fails() { + %c10 = arith.constant 10 : index + %ptr = fir.alloca !fir.array, %c10 {test.ptr} + // CHECK: Failed to generate load for operation: %{{.*}} = fir.alloca !fir.array + return +} + +// ----- + +func.func @test_load_box_fails() { + %ptr = fir.alloca !fir.box> {test.ptr} + // CHECK: Failed to generate load for operation: %{{.*}} = fir.alloca !fir.box> + return +} + +// ----- + +func.func @test_load_unlimited_polymorphic_fails() { + %ptr = fir.alloca !fir.class {test.ptr} + // CHECK: Failed to generate load for operation: %{{.*}} = fir.alloca !fir.class + return +} + diff --git a/flang/test/Fir/OpenACC/pointer-like-interface-store.mlir b/flang/test/Fir/OpenACC/pointer-like-interface-store.mlir new file mode 100644 index 0000000000000..6306bf1cf6cee --- /dev/null +++ b/flang/test/Fir/OpenACC/pointer-like-interface-store.mlir @@ -0,0 +1,75 @@ +// RUN: fir-opt %s --split-input-file --pass-pipeline="builtin.module(func.func(test-acc-pointer-like-interface{test-mode=store}))" 2>&1 | FileCheck %s + +func.func @test_store_scalar_f32() { + %ptr = fir.alloca f32 {test.ptr} + // CHECK: Successfully generated store for operation: %{{.*}} = fir.alloca f32 {test.ptr} + // CHECK: Generated: %[[VAL:.*]] = arith.constant 4.200000e+01 : f32 + // CHECK: Generated: fir.store %[[VAL]] to %{{.*}} : !fir.ref + return +} + +// ----- + +func.func @test_store_scalar_i32() { + %ptr = fir.alloca i32 {test.ptr} + // CHECK: Successfully generated store for operation: %{{.*}} = fir.alloca i32 {test.ptr} + // CHECK: Generated: %[[VAL:.*]] = arith.constant 42 : i32 + // CHECK: Generated: fir.store %[[VAL]] to %{{.*}} : !fir.ref + return +} + +// ----- + +func.func @test_store_scalar_i64() { + %ptr = fir.alloca i64 {test.ptr} + // CHECK: Successfully generated store for operation: %{{.*}} = fir.alloca i64 {test.ptr} + // CHECK: Generated: %[[VAL:.*]] = arith.constant 42 : i64 + // CHECK: Generated: fir.store %[[VAL]] to %{{.*}} : !fir.ref + return +} + +// ----- + +func.func @test_store_heap_scalar() { + %ptr = fir.allocmem f64 {test.ptr} + // CHECK: Successfully generated store for operation: %{{.*}} = fir.allocmem f64 {test.ptr} + // CHECK: Generated: %[[VAL:.*]] = arith.constant 4.200000e+01 : f64 + // CHECK: Generated: fir.store %[[VAL]] to %{{.*}} : !fir.heap + return +} + +// ----- + +func.func @test_store_with_type_conversion() { + %ptr = fir.alloca i32 {test.ptr} + // CHECK: Successfully generated store for operation: %{{.*}} = fir.alloca i32 {test.ptr} + // CHECK: Generated: %[[VAL:.*]] = arith.constant 42 : i32 + // CHECK: Generated: fir.store %[[VAL]] to %{{.*}} : !fir.ref + return +} + +// ----- + +func.func @test_store_dynamic_array_fails() { + %c10 = arith.constant 10 : index + %ptr = fir.alloca !fir.array, %c10 {test.ptr} + // CHECK: Failed to generate store for operation: %{{.*}} = fir.alloca !fir.array + return +} + +// ----- + +func.func @test_store_box_fails() { + %ptr = fir.alloca !fir.box> {test.ptr} + // CHECK: Failed to generate store for operation: %{{.*}} = fir.alloca !fir.box> + return +} + +// ----- + +func.func @test_store_unlimited_polymorphic_fails() { + %ptr = fir.alloca !fir.class {test.ptr} + // CHECK: Failed to generate store for operation: %{{.*}} = fir.alloca !fir.class + return +} + diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td index d1bbc7f206ce6..3f11bf6fbfce3 100644 --- a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td @@ -176,6 +176,50 @@ def OpenACC_PointerLikeTypeInterface : TypeInterface<"PointerLikeType"> { return false; }] >, + InterfaceMethod< + /*description=*/[{ + Generates a load operation from the pointer-like type. This dereferences + the pointer and returns the loaded value. + + The `srcPtr` parameter is the pointer to load from. If the current type is + represented in a way that it does not capture the pointee type, `valueType` + must be passed in to provide the necessary type information. + + Returns the loaded value, or an empty Value if load generation failed. + }], + /*retTy=*/"::mlir::Value", + /*methodName=*/"genLoad", + /*args=*/(ins "::mlir::OpBuilder &":$builder, + "::mlir::Location":$loc, + "::mlir::TypedValue<::mlir::acc::PointerLikeType>":$srcPtr, + "::mlir::Type":$valueType), + /*methodBody=*/"", + /*defaultImplementation=*/[{ + return {}; + }] + >, + InterfaceMethod< + /*description=*/[{ + Generates a store operation to the pointer-like type. This stores a value + to the memory location pointed to by the pointer. + + The `destPtr` parameter is the pointer to store to. The `valueToStore` + parameter is the value to be stored. The type information is derived from + the valueToStore parameter itself. + + Returns true if store was successfully generated, false otherwise. + }], + /*retTy=*/"bool", + /*methodName=*/"genStore", + /*args=*/(ins "::mlir::OpBuilder &":$builder, + "::mlir::Location":$loc, + "::mlir::Value":$valueToStore, + "::mlir::TypedValue<::mlir::acc::PointerLikeType>":$destPtr), + /*methodBody=*/"", + /*defaultImplementation=*/[{ + return false; + }] + >, ]; } diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index 841d1d781f1a1..3369cc7a3535b 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -203,12 +203,68 @@ struct MemRefPointerLikeModel return false; } + + mlir::Value genLoad(Type pointer, OpBuilder &builder, Location loc, + TypedValue srcPtr, + Type valueType) const { + // Load from a memref - only valid for scalar memrefs (rank 0). + // This is because the address computation for memrefs is part of the load + // (and not computed separately), but the API does not have arguments for + // indexing. + auto memrefValue = dyn_cast_if_present>(srcPtr); + if (!memrefValue) + return {}; + + auto memrefTy = memrefValue.getType(); + + // Only load from scalar memrefs (rank 0) + if (memrefTy.getRank() != 0) + return {}; + + return memref::LoadOp::create(builder, loc, memrefValue); + } + + bool genStore(Type pointer, OpBuilder &builder, Location loc, + Value valueToStore, TypedValue destPtr) const { + // Store to a memref - only valid for scalar memrefs (rank 0) + // This is because the address computation for memrefs is part of the store + // (and not computed separately), but the API does not have arguments for + // indexing. + auto memrefValue = dyn_cast_if_present>(destPtr); + if (!memrefValue) + return false; + + auto memrefTy = memrefValue.getType(); + + // Only store to scalar memrefs (rank 0) + if (memrefTy.getRank() != 0) + return false; + + memref::StoreOp::create(builder, loc, valueToStore, memrefValue); + return true; + } }; struct LLVMPointerPointerLikeModel : public PointerLikeType::ExternalModel { Type getElementType(Type pointer) const { return Type(); } + + mlir::Value genLoad(Type pointer, OpBuilder &builder, Location loc, + TypedValue srcPtr, + Type valueType) const { + // For LLVM pointers, we need the valueType to determine what to load + if (!valueType) + return {}; + + return LLVM::LoadOp::create(builder, loc, valueType, srcPtr); + } + + bool genStore(Type pointer, OpBuilder &builder, Location loc, + Value valueToStore, TypedValue destPtr) const { + LLVM::StoreOp::create(builder, loc, valueToStore, destPtr); + return true; + } }; struct MemrefAddressOfGlobalModel diff --git a/mlir/test/Dialect/OpenACC/pointer-like-interface-load.mlir b/mlir/test/Dialect/OpenACC/pointer-like-interface-load.mlir new file mode 100644 index 0000000000000..36df6a1d1bbe3 --- /dev/null +++ b/mlir/test/Dialect/OpenACC/pointer-like-interface-load.mlir @@ -0,0 +1,29 @@ +// RUN: mlir-opt %s --split-input-file --pass-pipeline="builtin.module(func.func(test-acc-pointer-like-interface{test-mode=load}))" 2>&1 | FileCheck %s + +func.func @test_memref_load_scalar() { + %ptr = memref.alloca() {test.ptr} : memref + // CHECK: Successfully generated load for operation: %[[PTR:.*]] = memref.alloca() {test.ptr} : memref + // CHECK: Loaded value type: f32 + // CHECK: Generated: %{{.*}} = memref.load %[[PTR]][] : memref + return +} + +// ----- + +func.func @test_memref_load_int() { + %ptr = memref.alloca() {test.ptr} : memref + // CHECK: Successfully generated load for operation: %[[PTR:.*]] = memref.alloca() {test.ptr} : memref + // CHECK: Loaded value type: i64 + // CHECK: Generated: %{{.*}} = memref.load %[[PTR]][] : memref + return +} + +// ----- + +func.func @test_memref_load_dynamic() { + %c10 = arith.constant 10 : index + %ptr = memref.alloc(%c10) {test.ptr} : memref + // CHECK: Failed to generate load for operation: %[[PTR:.*]] = memref.alloc(%{{.*}}) {test.ptr} : memref + return +} + diff --git a/mlir/test/Dialect/OpenACC/pointer-like-interface-store.mlir b/mlir/test/Dialect/OpenACC/pointer-like-interface-store.mlir new file mode 100644 index 0000000000000..0fee43102d6d9 --- /dev/null +++ b/mlir/test/Dialect/OpenACC/pointer-like-interface-store.mlir @@ -0,0 +1,39 @@ +// RUN: mlir-opt %s --split-input-file --pass-pipeline="builtin.module(func.func(test-acc-pointer-like-interface{test-mode=store}))" 2>&1 | FileCheck %s + +func.func @test_memref_store_scalar() { + %ptr = memref.alloca() {test.ptr} : memref + // CHECK: Successfully generated store for operation: %[[PTR:.*]] = memref.alloca() {test.ptr} : memref + // CHECK: Generated: %[[VAL:.*]] = arith.constant 4.200000e+01 : f32 + // CHECK: Generated: memref.store %[[VAL]], %[[PTR]][] : memref + return +} + +// ----- + +func.func @test_memref_store_int() { + %ptr = memref.alloca() {test.ptr} : memref + // CHECK: Successfully generated store for operation: %[[PTR:.*]] = memref.alloca() {test.ptr} : memref + // CHECK: Generated: %[[VAL:.*]] = arith.constant 42 : i32 + // CHECK: Generated: memref.store %[[VAL]], %[[PTR]][] : memref + return +} + +// ----- + +func.func @test_memref_store_i64() { + %ptr = memref.alloca() {test.ptr} : memref + // CHECK: Successfully generated store for operation: %[[PTR:.*]] = memref.alloca() {test.ptr} : memref + // CHECK: Generated: %[[VAL:.*]] = arith.constant 42 : i64 + // CHECK: Generated: memref.store %[[VAL]], %[[PTR]][] : memref + return +} + +// ----- + +func.func @test_memref_store_dynamic() { + %c10 = arith.constant 10 : index + %ptr = memref.alloc(%c10) {test.ptr} : memref + // CHECK: Failed to generate store for operation: %[[PTR:.*]] = memref.alloc(%{{.*}}) {test.ptr} : memref + return +} + diff --git a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp index 027b0a1a8b80b..9e2db6ac64f60 100644 --- a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp +++ b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp @@ -46,7 +46,7 @@ struct TestPointerLikeTypeInterfacePass Pass::Option testMode{ *this, "test-mode", - llvm::cl::desc("Test mode: walk, alloc, copy, or free"), + llvm::cl::desc("Test mode: walk, alloc, copy, free, load, or store"), llvm::cl::init("walk")}; StringRef getArgument() const override { @@ -75,6 +75,10 @@ struct TestPointerLikeTypeInterfacePass void testGenCopy(Operation *srcOp, Operation *destOp, Value srcResult, Value destResult, PointerLikeType pointerType, OpBuilder &builder); + void testGenLoad(Operation *op, Value result, PointerLikeType pointerType, + OpBuilder &builder); + void testGenStore(Operation *op, Value result, PointerLikeType pointerType, + OpBuilder &builder); struct PointerCandidate { Operation *op; @@ -92,7 +96,8 @@ void TestPointerLikeTypeInterfacePass::runOnOperation() { auto func = getOperation(); OpBuilder builder(&getContext()); - if (testMode == "alloc" || testMode == "free") { + if (testMode == "alloc" || testMode == "free" || testMode == "load" || + testMode == "store") { // Collect all candidates first SmallVector candidates; func.walk([&](Operation *op) { @@ -115,6 +120,12 @@ void TestPointerLikeTypeInterfacePass::runOnOperation() { else if (testMode == "free") testGenFree(candidate.op, candidate.result, candidate.pointerType, builder); + else if (testMode == "load") + testGenLoad(candidate.op, candidate.result, candidate.pointerType, + builder); + else if (testMode == "store") + testGenStore(candidate.op, candidate.result, candidate.pointerType, + builder); } } else if (testMode == "copy") { // Collect all source and destination candidates @@ -292,6 +303,100 @@ void TestPointerLikeTypeInterfacePass::testGenCopy( } } +void TestPointerLikeTypeInterfacePass::testGenLoad(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 genLoad API + auto typedResult = cast>(result); + Value loadRes = pointerType.genLoad(newBuilder, loc, typedResult, Type()); + + if (loadRes) { + llvm::errs() << "Successfully generated load for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "\tLoaded value type: "; + loadRes.getType().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 load for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + } +} + +void TestPointerLikeTypeInterfacePass::testGenStore(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); + + // Create a test value to store - use a constant matching the element type + Type elementType = pointerType.getElementType(); + if (!elementType) { + llvm::errs() << "Failed to generate store for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + return; + } + + Value valueToStore; + if (elementType.isIntOrIndex()) { + auto attr = newBuilder.getIntegerAttr(elementType, 42); + valueToStore = + arith::ConstantOp::create(newBuilder, loc, elementType, attr); + } else if (auto floatType = dyn_cast(elementType)) { + auto attr = newBuilder.getFloatAttr(floatType, 42.0); + valueToStore = arith::ConstantOp::create(newBuilder, loc, floatType, attr); + } else { + llvm::errs() << "Failed to generate store for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + return; + } + + // Call the genStore API + auto typedResult = cast>(result); + bool success = + pointerType.genStore(newBuilder, loc, valueToStore, typedResult); + + if (success) { + llvm::errs() << "Successfully generated store 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 store for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + } +} + } // namespace //===----------------------------------------------------------------------===// From 50200aa45393737ad0e5a38315f097577bc53bd0 Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Tue, 2 Dec 2025 11:02:41 -0800 Subject: [PATCH 2/4] Fix format --- .../Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h index 38150b294f4e1..0f133623475f8 100644 --- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h +++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h @@ -49,8 +49,8 @@ struct OpenACCPointerLikeModel mlir::TypedValue srcPtr, mlir::Type valueType) const; - bool genStore(mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc, - mlir::Value valueToStore, + bool genStore(mlir::Type pointer, mlir::OpBuilder &builder, + mlir::Location loc, mlir::Value valueToStore, mlir::TypedValue destPtr) const; }; From c9e740cf535825d98174ca98ef0581867be11f29 Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Tue, 2 Dec 2025 11:19:24 -0800 Subject: [PATCH 3/4] Fix braces --- .../Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp index a6109c3a04010..9fcc7d3681c39 100644 --- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp +++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp @@ -1041,9 +1041,8 @@ mlir::Value OpenACCPointerLikeModel::genLoad( mlir::Value loadedValue = fir::LoadOp::create(builder, loc, srcPtr); // If valueType is provided and differs from the loaded type, insert a convert - if (valueType && loadedValue.getType() != valueType) { + if (valueType && loadedValue.getType() != valueType) return fir::ConvertOp::create(builder, loc, valueType, loadedValue); - } return loadedValue; } @@ -1097,10 +1096,9 @@ bool OpenACCPointerLikeModel::genStore( mlir::Value convertedValue = valueToStore; // If the value type differs from the pointee type, insert a convert - if (valueType != pointeeTy) { + if (valueType != pointeeTy) convertedValue = fir::ConvertOp::create(builder, loc, pointeeTy, valueToStore); - } fir::StoreOp::create(builder, loc, convertedValue, destPtr); return true; From 3cfac1170c954303aae346ab2d80c07e0bd00903 Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Tue, 2 Dec 2025 11:31:22 -0800 Subject: [PATCH 4/4] Add constant dimension array test for FIR --- .../OpenACC/pointer-like-interface-load.mlir | 10 ++++ .../OpenACC/pointer-like-interface-store.mlir | 10 ++++ .../OpenACC/TestPointerLikeTypeInterface.cpp | 60 +++++++++++-------- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/flang/test/Fir/OpenACC/pointer-like-interface-load.mlir b/flang/test/Fir/OpenACC/pointer-like-interface-load.mlir index 79a944280445f..170ea56b24742 100644 --- a/flang/test/Fir/OpenACC/pointer-like-interface-load.mlir +++ b/flang/test/Fir/OpenACC/pointer-like-interface-load.mlir @@ -60,6 +60,16 @@ func.func @test_load_derived_type() { // ----- +func.func @test_load_constant_array() { + %ptr = fir.alloca !fir.array<10xf32> {test.ptr} + // CHECK: Successfully generated load for operation: %{{.*}} = fir.alloca !fir.array<10xf32> {test.ptr} + // CHECK: Loaded value type: !fir.array<10xf32> + // CHECK: Generated: %{{.*}} = fir.load %{{.*}} : !fir.ref> + return +} + +// ----- + func.func @test_load_dynamic_array_fails() { %c10 = arith.constant 10 : index %ptr = fir.alloca !fir.array, %c10 {test.ptr} diff --git a/flang/test/Fir/OpenACC/pointer-like-interface-store.mlir b/flang/test/Fir/OpenACC/pointer-like-interface-store.mlir index 6306bf1cf6cee..5ea4f0e750c65 100644 --- a/flang/test/Fir/OpenACC/pointer-like-interface-store.mlir +++ b/flang/test/Fir/OpenACC/pointer-like-interface-store.mlir @@ -50,6 +50,16 @@ func.func @test_store_with_type_conversion() { // ----- +func.func @test_store_constant_array() { + %val = fir.undefined !fir.array<10xf32> {test.value} + %ptr = fir.alloca !fir.array<10xf32> {test.ptr} + // CHECK: Successfully generated store for operation: %{{.*}} = fir.alloca !fir.array<10xf32> {test.ptr} + // CHECK: Generated: fir.store %{{.*}} to %{{.*}} : !fir.ref> + return +} + +// ----- + func.func @test_store_dynamic_array_fails() { %c10 = arith.constant 10 : index %ptr = fir.alloca !fir.array, %c10 {test.ptr} diff --git a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp index 9e2db6ac64f60..3ff0dc85b2152 100644 --- a/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp +++ b/mlir/test/lib/Dialect/OpenACC/TestPointerLikeTypeInterface.cpp @@ -78,7 +78,7 @@ struct TestPointerLikeTypeInterfacePass void testGenLoad(Operation *op, Value result, PointerLikeType pointerType, OpBuilder &builder); void testGenStore(Operation *op, Value result, PointerLikeType pointerType, - OpBuilder &builder); + OpBuilder &builder, Value providedValue = {}); struct PointerCandidate { Operation *op; @@ -100,6 +100,8 @@ void TestPointerLikeTypeInterfacePass::runOnOperation() { testMode == "store") { // Collect all candidates first SmallVector candidates; + // For store mode, also look for a test value to use + Value testValue; func.walk([&](Operation *op) { if (op->hasAttr("test.ptr")) { for (auto result : op->getResults()) { @@ -110,6 +112,11 @@ void TestPointerLikeTypeInterfacePass::runOnOperation() { } } } + // Collect value marked with test.value for store tests + if (testMode == "store" && op->hasAttr("test.value")) { + if (op->getNumResults() > 0) + testValue = op->getResult(0); + } }); // Now test all candidates @@ -125,7 +132,7 @@ void TestPointerLikeTypeInterfacePass::runOnOperation() { builder); else if (testMode == "store") testGenStore(candidate.op, candidate.result, candidate.pointerType, - builder); + builder, testValue); } } else if (testMode == "copy") { // Collect all source and destination candidates @@ -341,7 +348,8 @@ void TestPointerLikeTypeInterfacePass::testGenLoad(Operation *op, Value result, void TestPointerLikeTypeInterfacePass::testGenStore(Operation *op, Value result, PointerLikeType pointerType, - OpBuilder &builder) { + OpBuilder &builder, + Value providedValue) { Location loc = op->getLoc(); // Create a new builder with the listener and set insertion point @@ -350,28 +358,32 @@ void TestPointerLikeTypeInterfacePass::testGenStore(Operation *op, Value result, newBuilder.setListener(&tracker); newBuilder.setInsertionPointAfter(op); - // Create a test value to store - use a constant matching the element type - Type elementType = pointerType.getElementType(); - if (!elementType) { - llvm::errs() << "Failed to generate store for operation: "; - op->print(llvm::errs()); - llvm::errs() << "\n"; - return; - } + // Use provided value if available, otherwise create a constant + Value valueToStore = providedValue; + if (!valueToStore) { + // Create a test value to store - use a constant matching the element type + Type elementType = pointerType.getElementType(); + if (!elementType) { + llvm::errs() << "Failed to generate store for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + return; + } - Value valueToStore; - if (elementType.isIntOrIndex()) { - auto attr = newBuilder.getIntegerAttr(elementType, 42); - valueToStore = - arith::ConstantOp::create(newBuilder, loc, elementType, attr); - } else if (auto floatType = dyn_cast(elementType)) { - auto attr = newBuilder.getFloatAttr(floatType, 42.0); - valueToStore = arith::ConstantOp::create(newBuilder, loc, floatType, attr); - } else { - llvm::errs() << "Failed to generate store for operation: "; - op->print(llvm::errs()); - llvm::errs() << "\n"; - return; + if (elementType.isIntOrIndex()) { + auto attr = newBuilder.getIntegerAttr(elementType, 42); + valueToStore = + arith::ConstantOp::create(newBuilder, loc, elementType, attr); + } else if (auto floatType = dyn_cast(elementType)) { + auto attr = newBuilder.getFloatAttr(floatType, 42.0); + valueToStore = + arith::ConstantOp::create(newBuilder, loc, floatType, attr); + } else { + llvm::errs() << "Failed to generate store for operation: "; + op->print(llvm::errs()); + llvm::errs() << "\n"; + return; + } } // Call the genStore API