From c6d5cf58fd920f8d5e5211337ce7d9aca8b424dd Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Wed, 9 Jul 2025 14:25:58 -0700 Subject: [PATCH 1/2] [flang][acc] Update FIR ref, heap, and pointer to be MappableType The MappableType OpenACC type interface is a richer interface that allows OpenACC dialect to be capable to better interact with a source dialect, FIR in this case. fir.box and fir.class types already implemented this interface. Now the same is being done with the other FIR types that represent variables. One additional notable change is that fir.array no longer implements this interface. This is because MappableType is primarily intended for variables - and FIR variables of this type have storage associated and thus there's a pointer-like type (fir.ref/heap/pointer) that holds the array type. The end goal of promoting these FIR types to MappableType is that we will soon implement ability to generate recipes outside of the frontend via this interface. --- flang/lib/Lower/OpenACC.cpp | 13 +- flang/lib/Optimizer/Dialect/FIRType.cpp | 4 +- .../OpenACC/FIROpenACCTypeInterfaces.cpp | 398 ++++++++++-------- .../OpenACC/RegisterOpenACCExtensions.cpp | 8 +- flang/test/Fir/OpenACC/openacc-mappable.fir | 11 +- .../OpenACC/openacc-type-categories-class.f90 | 4 +- .../Fir/OpenACC/openacc-type-categories.f90 | 20 +- .../lib/OpenACC/TestOpenACCInterfaces.cpp | 14 +- mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp | 17 +- 9 files changed, 277 insertions(+), 212 deletions(-) diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp index e56d7f7ed9b6f..387b0c67952a7 100644 --- a/flang/lib/Lower/OpenACC.cpp +++ b/flang/lib/Lower/OpenACC.cpp @@ -164,14 +164,13 @@ createDataEntryOp(fir::FirOpBuilder &builder, mlir::Location loc, op.setStructured(structured); op.setImplicit(implicit); op.setDataClause(dataClause); - if (auto mappableTy = - mlir::dyn_cast(baseAddr.getType())) { - op.setVarType(baseAddr.getType()); + if (auto pointerLikeTy = + mlir::dyn_cast(baseAddr.getType())) { + op.setVarType(pointerLikeTy.getElementType()); } else { - assert(mlir::isa(baseAddr.getType()) && - "expected pointer-like"); - op.setVarType(mlir::cast(baseAddr.getType()) - .getElementType()); + assert(mlir::isa(baseAddr.getType()) && + "expected mappable"); + op.setVarType(baseAddr.getType()); } op->setAttr(Op::getOperandSegmentSizeAttr(), diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp index 2ff1d6d945ba3..4a9579cfde37c 100644 --- a/flang/lib/Optimizer/Dialect/FIRType.cpp +++ b/flang/lib/Optimizer/Dialect/FIRType.cpp @@ -1533,7 +1533,9 @@ std::optional> fir::getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty, const mlir::DataLayout &dl, const fir::KindMapping &kindMap) { - if (mlir::isa(ty)) { + if (ty.isIntOrIndexOrFloat() || + mlir::isa(ty)) { llvm::TypeSize size = dl.getTypeSize(ty); unsigned short alignment = dl.getTypeABIAlignment(ty); return std::pair{size, alignment}; diff --git a/flang/lib/Optimizer/OpenACC/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/FIROpenACCTypeInterfaces.cpp index 317a41a2129c3..0767733f53728 100644 --- a/flang/lib/Optimizer/OpenACC/FIROpenACCTypeInterfaces.cpp +++ b/flang/lib/Optimizer/OpenACC/FIROpenACCTypeInterfaces.cpp @@ -29,8 +29,9 @@ namespace fir::acc { -static mlir::TypedValue -getPtrFromVar(mlir::Value var) { +template +mlir::TypedValue +OpenACCMappableModel::getVarPtr(mlir::Type type, mlir::Value var) const { if (auto ptr = mlir::dyn_cast>(var)) return ptr; @@ -44,34 +45,51 @@ getPtrFromVar(mlir::Value var) { return {}; } -template <> -mlir::TypedValue -OpenACCMappableModel::getVarPtr(mlir::Type type, - mlir::Value var) const { - return getPtrFromVar(var); -} - -template <> -mlir::TypedValue +template mlir::TypedValue OpenACCMappableModel::getVarPtr(mlir::Type type, - mlir::Value var) const { - return getPtrFromVar(var); -} + mlir::Value var) const; -template <> -std::optional -OpenACCMappableModel::getSizeInBytes( +template mlir::TypedValue +OpenACCMappableModel::getVarPtr(mlir::Type type, + mlir::Value var) const; + +template mlir::TypedValue +OpenACCMappableModel::getVarPtr(mlir::Type type, + mlir::Value var) const; + +template mlir::TypedValue +OpenACCMappableModel::getVarPtr(mlir::Type type, + mlir::Value var) const; + +template +std::optional OpenACCMappableModel::getSizeInBytes( mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, const mlir::DataLayout &dataLayout) const { - // TODO: Bounds operation affect the total size - add support to take them + // TODO: Bounds operation affect the size - add support to take them // into account. if (!accBounds.empty()) return {}; + // Class-type is either a polymorphic or unlimited polymorphic. In the latter + // case, the size is not computable. But in the former it should be - however, + // fir::getTypeSizeAndAlignment does not support polymorphic types. + if (mlir::isa(type)) { + return {}; + } + + // When requesting the size of a box entity or a reference, the intent + // is to get the size of the data that it is referring to. + mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(type); + assert(eleTy && "expect to be able to unwrap the element type"); + + // If the type enclosed is a mappable type, then have it provide the size. + if (auto mappableTy = mlir::dyn_cast(eleTy)) + return mappableTy.getSizeInBytes(var, accBounds, dataLayout); + // Dynamic extents or unknown ranks generally do not have compile-time // computable dimensions. - auto seqType = mlir::cast(type); - if (seqType.hasDynamicExtents() || seqType.hasUnknownShape()) + auto seqType = mlir::dyn_cast(eleTy); + if (seqType && (seqType.hasDynamicExtents() || seqType.hasUnknownShape())) return {}; // Attempt to find an operation that a lookup for KindMapping can be done @@ -85,99 +103,113 @@ OpenACCMappableModel::getSizeInBytes( auto kindMap = fir::getKindMapping(kindMapSrcOp); auto sizeAndAlignment = - fir::getTypeSizeAndAlignment(var.getLoc(), type, dataLayout, kindMap); + fir::getTypeSizeAndAlignment(var.getLoc(), eleTy, dataLayout, kindMap); if (!sizeAndAlignment.has_value()) return {}; return {llvm::TypeSize::getFixed(sizeAndAlignment->first)}; } -template <> -std::optional +template std::optional OpenACCMappableModel::getSizeInBytes( mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, - const mlir::DataLayout &dataLayout) const { - // If we have a box value instead of box reference, the intent is to - // get the size of the data not the box itself. - if (auto boxTy = mlir::dyn_cast(var.getType())) { - if (auto mappableTy = mlir::dyn_cast( - fir::unwrapRefType(boxTy.getEleTy()))) { - return mappableTy.getSizeInBytes(var, accBounds, dataLayout); - } - } - // Size for boxes is not computable until it gets materialized. - return {}; -} + const mlir::DataLayout &dataLayout) const; -template <> -std::optional -OpenACCMappableModel::getOffsetInBytes( +template std::optional +OpenACCMappableModel::getSizeInBytes( + mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, + const mlir::DataLayout &dataLayout) const; + +template std::optional +OpenACCMappableModel::getSizeInBytes( + mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, + const mlir::DataLayout &dataLayout) const; + +template std::optional +OpenACCMappableModel::getSizeInBytes( + mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, + const mlir::DataLayout &dataLayout) const; + +template +std::optional OpenACCMappableModel::getOffsetInBytes( mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, const mlir::DataLayout &dataLayout) const { - // TODO: Bounds operation affect the offset- add support to take them + // TODO: Bounds operation affect the offset - add support to take them // into account. if (!accBounds.empty()) return {}; + // Class-type does not behave like a normal box because it does not hold an + // element type. Thus special handle it here. + if (mlir::isa(type)) { + // The pointer to the class-type is always at the start address. + return {0}; + } + + mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(type); + assert(eleTy && "expect to be able to unwrap the element type"); + + // If the type enclosed is a mappable type, then have it provide the offset. + if (auto mappableTy = mlir::dyn_cast(eleTy)) + return mappableTy.getOffsetInBytes(var, accBounds, dataLayout); + // Dynamic extents (aka descriptor-based arrays) - may have a offset. // For example, a negative stride may mean a negative offset to compute the // start of array. - auto seqType = mlir::cast(type); - if (seqType.hasDynamicExtents() || seqType.hasUnknownShape()) + auto seqType = mlir::dyn_cast(eleTy); + if (seqType && (seqType.hasDynamicExtents() || seqType.hasUnknownShape())) return {}; - // We have non-dynamic extents - but if for some reason the size is not - // computable - assume offset is not either. Otherwise, it is an offset of - // zero. + // If the size is computable and since there are no bounds or dynamic extents, + // then the offset relative to pointer must be zero. if (getSizeInBytes(type, var, accBounds, dataLayout).has_value()) { return {0}; } + + // The offset is not evident because it is relative to the pointer being held. + // And we don't have any further details about this type. return {}; } -template <> -std::optional OpenACCMappableModel::getOffsetInBytes( +template std::optional +OpenACCMappableModel::getOffsetInBytes( mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, - const mlir::DataLayout &dataLayout) const { - // If we have a box value instead of box reference, the intent is to - // get the offset of the data not the offset of the box itself. - if (auto boxTy = mlir::dyn_cast(var.getType())) { - if (auto mappableTy = mlir::dyn_cast( - fir::unwrapRefType(boxTy.getEleTy()))) { - return mappableTy.getOffsetInBytes(var, accBounds, dataLayout); - } - } - // Until boxes get materialized, the offset is not evident because it is - // relative to the pointer being held. - return {}; -} + const mlir::DataLayout &dataLayout) const; -template <> -llvm::SmallVector -OpenACCMappableModel::generateAccBounds( - mlir::Type type, mlir::Value var, mlir::OpBuilder &builder) const { +template std::optional +OpenACCMappableModel::getOffsetInBytes( + mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, + const mlir::DataLayout &dataLayout) const; + +template std::optional +OpenACCMappableModel::getOffsetInBytes( + mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, + const mlir::DataLayout &dataLayout) const; + +template std::optional +OpenACCMappableModel::getOffsetInBytes( + mlir::Type type, mlir::Value var, mlir::ValueRange accBounds, + const mlir::DataLayout &dataLayout) const; + +static llvm::SmallVector +generateSeqTyAccBounds(fir::SequenceType seqType, mlir::Value var, + mlir::OpBuilder &builder) { assert((mlir::isa(var.getType()) || mlir::isa(var.getType())) && "must be pointer-like or mappable"); - fir::FirOpBuilder firBuilder(builder, var.getDefiningOp()); - auto seqType = mlir::cast(type); mlir::Location loc = var.getLoc(); - mlir::Value varPtr = - mlir::isa(var.getType()) - ? var - : mlir::cast(var.getType()).getVarPtr(var); - if (seqType.hasDynamicExtents() || seqType.hasUnknownShape()) { if (auto boxAddr = - mlir::dyn_cast_if_present(varPtr.getDefiningOp())) { + mlir::dyn_cast_if_present(var.getDefiningOp())) { mlir::Value box = boxAddr.getVal(); auto res = hlfir::translateToExtendedValue(loc, firBuilder, hlfir::Entity(box)); fir::ExtendedValue exv = res.first; mlir::Value boxRef = box; - if (auto boxPtr = getPtrFromVar(box)) { + if (auto boxPtr = mlir::cast(box.getType()) + .getVarPtr(box)) { boxRef = boxPtr; } // TODO: Handle Fortran optional. @@ -189,7 +221,7 @@ OpenACCMappableModel::generateAccBounds( firBuilder, loc, exv, info); } - if (mlir::isa(varPtr.getDefiningOp())) { + if (mlir::isa(var.getDefiningOp())) { mlir::Value zero = firBuilder.createIntegerConstant(loc, builder.getIndexType(), 0); mlir::Value one = @@ -197,10 +229,10 @@ OpenACCMappableModel::generateAccBounds( mlir::Value shape; if (auto declareOp = - mlir::dyn_cast_if_present(varPtr.getDefiningOp())) + mlir::dyn_cast_if_present(var.getDefiningOp())) shape = declareOp.getShape(); else if (auto declareOp = mlir::dyn_cast_if_present( - varPtr.getDefiningOp())) + var.getDefiningOp())) shape = declareOp.getShape(); const bool strideIncludeLowerExtent = true; @@ -265,9 +297,9 @@ OpenACCMappableModel::generateAccBounds( // TODO: Detect assumed-size case. const bool isAssumedSize = false; - auto valToCheck = varPtr; + auto valToCheck = var; if (auto boxAddr = - mlir::dyn_cast_if_present(varPtr.getDefiningOp())) { + mlir::dyn_cast_if_present(var.getDefiningOp())) { valToCheck = boxAddr.getVal(); } auto res = hlfir::translateToExtendedValue(loc, firBuilder, @@ -279,86 +311,34 @@ OpenACCMappableModel::generateAccBounds( /*isAssumedSize=*/isAssumedSize); } -template <> +template llvm::SmallVector -OpenACCMappableModel::generateAccBounds( - mlir::Type type, mlir::Value var, mlir::OpBuilder &builder) const { - // If we have a box value instead of box reference, the intent is to - // get the bounds of the data not the bounds of the box itself. - if (auto boxTy = mlir::dyn_cast(var.getType())) { - if (auto mappableTy = mlir::dyn_cast( - fir::unwrapRefType(boxTy.getEleTy()))) { - mlir::Value data = builder.create(var.getLoc(), var); - return mappableTy.generateAccBounds(data, builder); - } +OpenACCMappableModel::generateAccBounds(mlir::Type type, mlir::Value var, + mlir::OpBuilder &builder) const { + // acc bounds only make sense for arrays - thus look for sequence type. + mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(type); + if (auto seqTy = mlir::dyn_cast_if_present(eleTy)) { + return generateSeqTyAccBounds(seqTy, var, builder); } - // Box references are not arrays - thus generating acc.bounds does not make - // sense. - return {}; -} - -static bool isScalarLike(mlir::Type type) { - return fir::isa_trivial(type) || fir::isa_ref_type(type); -} - -static bool isArrayLike(mlir::Type type) { - return mlir::isa(type); -} -static bool isCompositeLike(mlir::Type type) { - // class(*) is not a composite type since it does not have a determined type. - if (fir::isUnlimitedPolymorphicType(type)) - return false; - - return mlir::isa(type); -} - -template <> -mlir::acc::VariableTypeCategory -OpenACCMappableModel::getTypeCategory( - mlir::Type type, mlir::Value var) const { - return mlir::acc::VariableTypeCategory::array; + return {}; } -template <> -mlir::acc::VariableTypeCategory -OpenACCMappableModel::getTypeCategory(mlir::Type type, - mlir::Value var) const { - // Class-type does not behave like a normal box because it does not hold an - // element type. Thus special handle it here. - if (mlir::isa(type)) { - // class(*) is not a composite type since it does not have a determined - // type. - if (fir::isUnlimitedPolymorphicType(type)) - return mlir::acc::VariableTypeCategory::uncategorized; - return mlir::acc::VariableTypeCategory::composite; - } - - mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(type); - assert(eleTy && "expect to be able to unwrap the element type"); +template llvm::SmallVector +OpenACCMappableModel::generateAccBounds( + mlir::Type type, mlir::Value var, mlir::OpBuilder &builder) const; - // If the type enclosed by the box is a mappable type, then have it - // provide the type category. - if (auto mappableTy = mlir::dyn_cast(eleTy)) - return mappableTy.getTypeCategory(var); +template llvm::SmallVector +OpenACCMappableModel::generateAccBounds( + mlir::Type type, mlir::Value var, mlir::OpBuilder &builder) const; - // For all arrays, despite whether they are allocatable, pointer, assumed, - // etc, we'd like to categorize them as "array". - if (isArrayLike(eleTy)) - return mlir::acc::VariableTypeCategory::array; - - // We got here because we don't have an array nor a mappable type. At this - // point, we know we have a type that fits the "aggregate" definition since it - // is a type with a descriptor. Try to refine it by checking if it matches the - // "composite" definition. - if (isCompositeLike(eleTy)) - return mlir::acc::VariableTypeCategory::composite; +template llvm::SmallVector +OpenACCMappableModel::generateAccBounds( + mlir::Type type, mlir::Value var, mlir::OpBuilder &builder) const; - // Even if we have a scalar type - simply because it is wrapped in a box - // we want to categorize it as "nonscalar". Anything else would've been - // non-scalar anyway. - return mlir::acc::VariableTypeCategory::nonscalar; -} +template llvm::SmallVector +OpenACCMappableModel::generateAccBounds( + mlir::Type type, mlir::Value var, mlir::OpBuilder &builder) const; static mlir::Value getBaseRef(mlir::TypedValue varPtr) { @@ -389,33 +369,44 @@ getBaseRef(mlir::TypedValue varPtr) { return baseRef; } -static mlir::acc::VariableTypeCategory -categorizePointee(mlir::Type pointer, - mlir::TypedValue varPtr, - mlir::Type varType) { - // FIR uses operations to compute interior pointers. - // So for example, an array element or composite field access to a float - // value would both be represented as !fir.ref. We do not want to treat - // such a reference as a scalar. Thus unwrap interior pointer calculations. - auto baseRef = getBaseRef(varPtr); +static bool isScalarLike(mlir::Type type) { + return fir::isa_trivial(type) || fir::isa_ref_type(type); +} - if (auto mappableTy = - mlir::dyn_cast(baseRef.getType())) - return mappableTy.getTypeCategory(baseRef); +static bool isArrayLike(mlir::Type type) { + return mlir::isa(type); +} - // It must be a pointer-like type since it is not a MappableType. - auto ptrLikeTy = mlir::cast(baseRef.getType()); - mlir::Type eleTy = ptrLikeTy.getElementType(); +static bool isCompositeLike(mlir::Type type) { + // class(*) is not a composite type since it does not have a determined type. + if (fir::isUnlimitedPolymorphicType(type)) + return false; - if (auto mappableEleTy = mlir::dyn_cast(eleTy)) - return mappableEleTy.getTypeCategory(varPtr); + return mlir::isa(type); +} - if (isScalarLike(eleTy)) - return mlir::acc::VariableTypeCategory::scalar; +static mlir::acc::VariableTypeCategory +categorizeElemType(mlir::Type enclosingTy, mlir::Type eleTy, mlir::Value var) { + // If the type enclosed is a mappable type, then have it provide the type + // category. + if (auto mappableTy = mlir::dyn_cast(eleTy)) + return mappableTy.getTypeCategory(var); + + // For all arrays, despite whether they are allocatable, pointer, assumed, + // etc, we'd like to categorize them as "array". if (isArrayLike(eleTy)) return mlir::acc::VariableTypeCategory::array; + if (isCompositeLike(eleTy)) return mlir::acc::VariableTypeCategory::composite; + if (mlir::isa(enclosingTy)) { + // Even if we have a scalar type - simply because it is wrapped in a box + // we want to categorize it as "nonscalar". Anything else would've been + // non-scalar anyway. + return mlir::acc::VariableTypeCategory::nonscalar; + } + if (isScalarLike(eleTy)) + return mlir::acc::VariableTypeCategory::scalar; if (mlir::isa(eleTy)) return mlir::acc::VariableTypeCategory::nonscalar; // Assumed-type (type(*))does not have a determined type that can be @@ -431,6 +422,77 @@ categorizePointee(mlir::Type pointer, return mlir::acc::VariableTypeCategory::uncategorized; } +template +mlir::acc::VariableTypeCategory +OpenACCMappableModel::getTypeCategory(mlir::Type type, + mlir::Value var) const { + // FIR uses operations to compute interior pointers. + // So for example, an array element or composite field access to a float + // value would both be represented as !fir.ref. We do not want to treat + // such a reference as a scalar. Thus unwrap interior pointer calculations. + mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(type); + if (eleTy && isScalarLike(eleTy)) { + if (auto ptrLikeVar = mlir::dyn_cast_if_present< + mlir::TypedValue>(var)) { + auto baseRef = getBaseRef(ptrLikeVar); + if (baseRef != var) { + type = baseRef.getType(); + if (auto mappableTy = mlir::dyn_cast(type)) + return mappableTy.getTypeCategory(baseRef); + } + } + } + + // Class-type does not behave like a normal box because it does not hold an + // element type. Thus special handle it here. + if (mlir::isa(type)) { + // class(*) is not a composite type since it does not have a determined + // type. + if (fir::isUnlimitedPolymorphicType(type)) + return mlir::acc::VariableTypeCategory::uncategorized; + return mlir::acc::VariableTypeCategory::composite; + } + + assert(eleTy && "expect to be able to unwrap the element type"); + return categorizeElemType(type, eleTy, var); +} + +template mlir::acc::VariableTypeCategory +OpenACCMappableModel::getTypeCategory(mlir::Type type, + mlir::Value var) const; + +template mlir::acc::VariableTypeCategory +OpenACCMappableModel::getTypeCategory( + mlir::Type type, mlir::Value var) const; + +template mlir::acc::VariableTypeCategory +OpenACCMappableModel::getTypeCategory(mlir::Type type, + mlir::Value var) const; + +template mlir::acc::VariableTypeCategory +OpenACCMappableModel::getTypeCategory(mlir::Type type, + mlir::Value var) const; + +static mlir::acc::VariableTypeCategory +categorizePointee(mlir::Type pointer, + mlir::TypedValue varPtr, + mlir::Type varType) { + // FIR uses operations to compute interior pointers. + // So for example, an array element or composite field access to a float + // value would both be represented as !fir.ref. We do not want to treat + // such a reference as a scalar. Thus unwrap interior pointer calculations. + auto baseRef = getBaseRef(varPtr); + + if (auto mappableTy = + mlir::dyn_cast(baseRef.getType())) + return mappableTy.getTypeCategory(baseRef); + + // It must be a pointer-like type since it is not a MappableType. + auto ptrLikeTy = mlir::cast(baseRef.getType()); + mlir::Type eleTy = ptrLikeTy.getElementType(); + return categorizeElemType(pointer, eleTy, varPtr); +} + template <> mlir::acc::VariableTypeCategory OpenACCPointerLikeModel::getPointeeTypeCategory( diff --git a/flang/lib/Optimizer/OpenACC/RegisterOpenACCExtensions.cpp b/flang/lib/Optimizer/OpenACC/RegisterOpenACCExtensions.cpp index 5f174ad4b40fe..869f9c2429aa0 100644 --- a/flang/lib/Optimizer/OpenACC/RegisterOpenACCExtensions.cpp +++ b/flang/lib/Optimizer/OpenACC/RegisterOpenACCExtensions.cpp @@ -19,11 +19,14 @@ namespace fir::acc { void registerOpenACCExtensions(mlir::DialectRegistry ®istry) { registry.addExtension(+[](mlir::MLIRContext *ctx, fir::FIROpsDialect *dialect) { - fir::SequenceType::attachInterface>( - *ctx); fir::BoxType::attachInterface>(*ctx); fir::ClassType::attachInterface>( *ctx); + fir::ReferenceType::attachInterface< + OpenACCMappableModel>(*ctx); + fir::PointerType::attachInterface>( + *ctx); + fir::HeapType::attachInterface>(*ctx); fir::ReferenceType::attachInterface< OpenACCPointerLikeModel>(*ctx); @@ -31,6 +34,7 @@ void registerOpenACCExtensions(mlir::DialectRegistry ®istry) { OpenACCPointerLikeModel>(*ctx); fir::HeapType::attachInterface>( *ctx); + fir::LLVMPointerType::attachInterface< OpenACCPointerLikeModel>(*ctx); }); diff --git a/flang/test/Fir/OpenACC/openacc-mappable.fir b/flang/test/Fir/OpenACC/openacc-mappable.fir index 3e3e455469f69..71576f4b71075 100644 --- a/flang/test/Fir/OpenACC/openacc-mappable.fir +++ b/flang/test/Fir/OpenACC/openacc-mappable.fir @@ -23,7 +23,7 @@ module attributes {dlti.dl_spec = #dlti.dl_spec : vector<2xi64>, // CHECK: Size: 40 // CHECK: Visiting: %{{.*}} = acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {name = "arr", structured = false} - // CHECK: Mappable: !fir.array<10xf32> + // CHECK: Pointer-like and Mappable: !fir.ref> // CHECK: Type category: array // CHECK: Size: 40 @@ -60,20 +60,17 @@ module attributes {dlti.dl_spec = #dlti.dl_spec : vector<2xi64>, } // CHECK: Visiting: %{{.*}} = acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {name = "arr1", structured = false} - // CHECK: Pointer-like: !fir.ref> - // CHECK: Mappable: !fir.array + // CHECK: Pointer-like and Mappable: !fir.ref> // CHECK: Type category: array // CHECK: Bound[0]: %{{.*}} = acc.bounds lowerbound(%c0{{.*}} : index) upperbound(%{{.*}} : index) extent(%{{.*}} : index) stride(%c1{{.*}} : index) startIdx(%c1{{.*}} : index) // CHECK: Visiting: %{{.*}} = acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {name = "arr2", structured = false} - // CHECK: Pointer-like: !fir.ref> - // CHECK: Mappable: !fir.array + // CHECK: Pointer-like and Mappable: !fir.ref> // CHECK: Type category: array // CHECK: Bound[0]: %{{.*}} = acc.bounds lowerbound(%c0{{.*}} : index) upperbound(%{{.*}} : index) extent(%{{.*}} : index) stride(%c1{{.*}} : index) startIdx(%c2{{.*}} : index) // CHECK: Visiting: %{{.*}} = acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {name = "arr3", structured = false} - // CHECK: Pointer-like: !fir.ref> - // CHECK: Mappable: !fir.array<10xf32> + // CHECK: Pointer-like and Mappable: !fir.ref> // CHECK: Type category: array // CHECK: Size: 40 // CHECK: Offset: 0 diff --git a/flang/test/Fir/OpenACC/openacc-type-categories-class.f90 b/flang/test/Fir/OpenACC/openacc-type-categories-class.f90 index 58025bfa556a5..e8951cceeeaeb 100644 --- a/flang/test/Fir/OpenACC/openacc-type-categories-class.f90 +++ b/flang/test/Fir/OpenACC/openacc-type-categories-class.f90 @@ -29,13 +29,13 @@ subroutine init_unlimited(this) ! CHECK: Mappable: !fir.class> ! CHECK: Type category: composite ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "this%field", structured = false} -! CHECK: Pointer-like: !fir.ref +! CHECK: Pointer-like and Mappable: !fir.ref ! CHECK: Type category: composite ! For unlimited polymorphic entities and assumed types - they effectively have ! no declared type. Thus the type categorizer cannot categorize it. ! CHECK: Visiting: {{.*}} = acc.copyin {{.*}} {name = "var", structured = false} -! CHECK: Pointer-like: !fir.ref +! CHECK: Pointer-like and Mappable: !fir.ref ! CHECK: Type category: uncategorized ! CHECK: Visiting: {{.*}} = acc.copyin {{.*}} {name = "this", structured = false} ! CHECK: Mappable: !fir.class diff --git a/flang/test/Fir/OpenACC/openacc-type-categories.f90 b/flang/test/Fir/OpenACC/openacc-type-categories.f90 index c25c38422b755..3d6067db8224d 100644 --- a/flang/test/Fir/OpenACC/openacc-type-categories.f90 +++ b/flang/test/Fir/OpenACC/openacc-type-categories.f90 @@ -18,32 +18,32 @@ program main end program ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "scalar", structured = false} -! CHECK: Pointer-like: !fir.ref +! CHECK: Pointer-like and Mappable: !fir.ref ! CHECK: Type category: scalar ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "scalaralloc", structured = false} -! CHECK: Pointer-like: !fir.ref>> +! CHECK: Pointer-like and Mappable: !fir.ref>> ! CHECK: Type category: nonscalar ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "ttvar", structured = false} -! CHECK: Pointer-like: !fir.ref}>> +! CHECK: Pointer-like and Mappable: !fir.ref}>> ! CHECK: Type category: composite ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "arrayconstsize", structured = false} -! CHECK: Pointer-like: !fir.ref> +! CHECK: Pointer-like and Mappable: !fir.ref> ! CHECK: Type category: array ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "arrayalloc", structured = false} -! CHECK: Pointer-like: !fir.ref>>> +! CHECK: Pointer-like and Mappable: !fir.ref>>> ! CHECK: Type category: array ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "complexvar", structured = false} -! CHECK: Pointer-like: !fir.ref> +! CHECK: Pointer-like and Mappable: !fir.ref> ! CHECK: Type category: scalar ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "charvar", structured = false} -! CHECK: Pointer-like: !fir.ref> +! CHECK: Pointer-like and Mappable: !fir.ref> ! CHECK: Type category: nonscalar ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "ttvar%field", structured = false} -! CHECK: Pointer-like: !fir.ref +! CHECK: Pointer-like and Mappable: !fir.ref ! CHECK: Type category: composite ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "ttvar%fieldarray", structured = false} -! CHECK: Pointer-like: !fir.ref> +! CHECK: Pointer-like and Mappable: !fir.ref> ! CHECK: Type category: array ! CHECK: Visiting: {{.*}} acc.copyin {{.*}} {name = "arrayconstsize(1)", structured = false} -! CHECK: Pointer-like: !fir.ref> +! CHECK: Pointer-like and Mappable: !fir.ref> ! CHECK: Type category: array diff --git a/flang/test/lib/OpenACC/TestOpenACCInterfaces.cpp b/flang/test/lib/OpenACC/TestOpenACCInterfaces.cpp index e72b96fe7cd10..1888893a4f8b8 100644 --- a/flang/test/lib/OpenACC/TestOpenACCInterfaces.cpp +++ b/flang/test/lib/OpenACC/TestOpenACCInterfaces.cpp @@ -58,8 +58,18 @@ struct TestFIROpenACCInterfaces llvm::errs() << "Visiting: " << *op << "\n"; llvm::errs() << "\tVar: " << var << "\n"; - if (auto ptrTy = dyn_cast_if_present(typeOfVar)) { + if (mlir::isa(typeOfVar) && + mlir::isa(typeOfVar)) { + llvm::errs() << "\tPointer-like and Mappable: " << typeOfVar << "\n"; + } else if (mlir::isa(typeOfVar)) { llvm::errs() << "\tPointer-like: " << typeOfVar << "\n"; + } else { + assert(mlir::isa(typeOfVar) && + "expected mappable"); + llvm::errs() << "\tMappable: " << typeOfVar << "\n"; + } + + if (auto ptrTy = dyn_cast_if_present(typeOfVar)) { // If the pointee is not mappable, print details about it. Otherwise, // we defer to the mappable printing below to print those details. if (!mappableTy) { @@ -72,8 +82,6 @@ struct TestFIROpenACCInterfaces } if (mappableTy) { - llvm::errs() << "\tMappable: " << mappableTy << "\n"; - acc::VariableTypeCategory typeCategory = mappableTy.getTypeCategory(var); llvm::errs() << "\t\tType category: " << typeCategory << "\n"; diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index 80c807e774a7e..f2eab62b286af 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -293,22 +293,15 @@ static LogicalResult checkVarAndVarType(Op op) { if (!op.getVar()) return op.emitError("must have var operand"); - if (mlir::isa(op.getVar().getType()) && - mlir::isa(op.getVar().getType())) { - // TODO: If a type implements both interfaces (mappable and pointer-like), - // it is unclear which semantics to apply without additional info which - // would need captured in the data operation. For now restrict this case - // unless a compelling reason to support disambiguating between the two. - return op.emitError("var must be mappable or pointer-like (not both)"); - } - + // A variable must have a type that is either pointer-like or mappable. if (!mlir::isa(op.getVar().getType()) && !mlir::isa(op.getVar().getType())) return op.emitError("var must be mappable or pointer-like"); - if (mlir::isa(op.getVar().getType()) && - op.getVarType() != op.getVar().getType()) - return op.emitError("varType must match when var is mappable"); + // When it is a pointer-like type, the varType must capture the target type. + if (mlir::isa(op.getVar().getType()) && + op.getVarType() == op.getVar().getType()) + return op.emitError("varType must capture the element type of var"); return success(); } From de232922767bb21a4a17651d73d6969a2e70ee40 Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Wed, 9 Jul 2025 14:53:39 -0700 Subject: [PATCH 2/2] Fix format --- flang/test/lib/OpenACC/TestOpenACCInterfaces.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flang/test/lib/OpenACC/TestOpenACCInterfaces.cpp b/flang/test/lib/OpenACC/TestOpenACCInterfaces.cpp index 1888893a4f8b8..de6cb1d09080d 100644 --- a/flang/test/lib/OpenACC/TestOpenACCInterfaces.cpp +++ b/flang/test/lib/OpenACC/TestOpenACCInterfaces.cpp @@ -64,8 +64,8 @@ struct TestFIROpenACCInterfaces } else if (mlir::isa(typeOfVar)) { llvm::errs() << "\tPointer-like: " << typeOfVar << "\n"; } else { - assert(mlir::isa(typeOfVar) && - "expected mappable"); + assert( + mlir::isa(typeOfVar) && "expected mappable"); llvm::errs() << "\tMappable: " << typeOfVar << "\n"; }