Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions flang/include/flang/Optimizer/Dialect/FIROps.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
#include "mlir/Interfaces/LoopLikeInterface.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "mlir/Interfaces/ViewLikeInterface.h"

namespace fir {

Expand Down
5 changes: 4 additions & 1 deletion flang/include/flang/Optimizer/Dialect/FIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
include "mlir/Dialect/Arith/IR/ArithBase.td"
include "mlir/Dialect/Arith/IR/ArithOpsInterfaces.td"
include "mlir/Dialect/LLVMIR/LLVMAttrDefs.td"
include "mlir/Interfaces/ViewLikeInterface.td"
include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.td"
include "flang/Optimizer/Dialect/FIRDialect.td"
include "flang/Optimizer/Dialect/FIRTypes.td"
Expand Down Expand Up @@ -2828,7 +2829,8 @@ def fir_VolatileCastOp : fir_SimpleOneResultOp<"volatile_cast", [Pure]> {
let hasFolder = 1;
}

def fir_ConvertOp : fir_SimpleOneResultOp<"convert", [NoMemoryEffect]> {
def fir_ConvertOp
: fir_SimpleOneResultOp<"convert", [NoMemoryEffect, ViewLikeOpInterface]> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this looks like unrelated change, but looks good to me :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I am testing with this change because I need to be able to walk past fir.convert operations. Those are not clearly "partial" views so hence my interface was not appropriate to attach to those - so I am attaching ViewLikeOpInterface instead like you did in your ViewLikeOpInterface implementation for FIR. I could break it up to a follow-up change if you prefer - but sounds like you're OK I am including it here since it is a few small changes. :) Thanks!

let summary = "encapsulates all Fortran entity type conversions";

let description = [{
Expand Down Expand Up @@ -2866,6 +2868,7 @@ def fir_ConvertOp : fir_SimpleOneResultOp<"convert", [NoMemoryEffect]> {
static bool isPointerCompatible(mlir::Type ty);
static bool canBeConverted(mlir::Type inType, mlir::Type outType);
static bool areVectorsCompatible(mlir::Type inTy, mlir::Type outTy);
mlir::Value getViewSource() { return getValue(); }
}];
let hasCanonicalizer = 1;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//===- FIROpenACCOpsInterfaces.h --------------------------------*- C++ -*-===//
//
// 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 external operation interfaces for FIR.
//
//===----------------------------------------------------------------------===//

#ifndef FLANG_OPTIMIZER_OPENACC_FIROPENACC_OPS_INTERFACES_H_
#define FLANG_OPTIMIZER_OPENACC_FIROPENACC_OPS_INTERFACES_H_

#include "mlir/Dialect/OpenACC/OpenACC.h"

namespace fir {
class DeclareOp;
} // namespace fir

namespace hlfir {
class DeclareOp;
class DesignateOp;
} // namespace hlfir

namespace fir::acc {

template <typename Op>
struct PartialEntityAccessModel
: public mlir::acc::PartialEntityAccessOpInterface::ExternalModel<
PartialEntityAccessModel<Op>, Op> {
mlir::Value getBaseEntity(mlir::Operation *op) const;

// Default implementation - returns false (partial view)
bool isCompleteView(mlir::Operation *op) const { return false; }
};

// Full specializations for declare operations
template <>
struct PartialEntityAccessModel<fir::DeclareOp>
: public mlir::acc::PartialEntityAccessOpInterface::ExternalModel<
PartialEntityAccessModel<fir::DeclareOp>, fir::DeclareOp> {
mlir::Value getBaseEntity(mlir::Operation *op) const;
bool isCompleteView(mlir::Operation *op) const;
};

template <>
struct PartialEntityAccessModel<hlfir::DeclareOp>
: public mlir::acc::PartialEntityAccessOpInterface::ExternalModel<
PartialEntityAccessModel<hlfir::DeclareOp>, hlfir::DeclareOp> {
mlir::Value getBaseEntity(mlir::Operation *op) const;
bool isCompleteView(mlir::Operation *op) const;
};

} // namespace fir::acc

#endif // FLANG_OPTIMIZER_OPENACC_FIROPENACC_OPS_INTERFACES_H_
1 change: 1 addition & 0 deletions flang/lib/Optimizer/OpenACC/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)

add_flang_library(FIROpenACCSupport
FIROpenACCAttributes.cpp
FIROpenACCOpsInterfaces.cpp
FIROpenACCTypeInterfaces.cpp
RegisterOpenACCExtensions.cpp

Expand Down
62 changes: 62 additions & 0 deletions flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===-- FIROpenACCOpsInterfaces.cpp ---------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Implementation of external operation interfaces for FIR.
//
//===----------------------------------------------------------------------===//

#include "flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h"

#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"

namespace fir::acc {

template <>
mlir::Value PartialEntityAccessModel<fir::ArrayCoorOp>::getBaseEntity(
mlir::Operation *op) const {
return mlir::cast<fir::ArrayCoorOp>(op).getMemref();
}

template <>
mlir::Value PartialEntityAccessModel<fir::CoordinateOp>::getBaseEntity(
mlir::Operation *op) const {
return mlir::cast<fir::CoordinateOp>(op).getRef();
}

template <>
mlir::Value PartialEntityAccessModel<hlfir::DesignateOp>::getBaseEntity(
mlir::Operation *op) const {
return mlir::cast<hlfir::DesignateOp>(op).getMemref();
}

mlir::Value PartialEntityAccessModel<fir::DeclareOp>::getBaseEntity(
mlir::Operation *op) const {
return mlir::cast<fir::DeclareOp>(op).getStorage();
}

bool PartialEntityAccessModel<fir::DeclareOp>::isCompleteView(
mlir::Operation *op) const {
// Return false (partial view) only if storage is present
// Return true (complete view) if storage is absent
return !getBaseEntity(op);
}

mlir::Value PartialEntityAccessModel<hlfir::DeclareOp>::getBaseEntity(
mlir::Operation *op) const {
return mlir::cast<hlfir::DeclareOp>(op).getStorage();
}

bool PartialEntityAccessModel<hlfir::DeclareOp>::isCompleteView(
mlir::Operation *op) const {
// Return false (partial view) only if storage is present
// Return true (complete view) if storage is absent
return !getBaseEntity(op);
}

} // namespace fir::acc
22 changes: 22 additions & 0 deletions flang/lib/Optimizer/OpenACC/Support/RegisterOpenACCExtensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@
//===----------------------------------------------------------------------===//

#include "flang/Optimizer/OpenACC/Support/RegisterOpenACCExtensions.h"

#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include "flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h"
#include "flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h"

namespace fir::acc {
Expand All @@ -37,7 +42,24 @@ void registerOpenACCExtensions(mlir::DialectRegistry &registry) {

fir::LLVMPointerType::attachInterface<
OpenACCPointerLikeModel<fir::LLVMPointerType>>(*ctx);

fir::ArrayCoorOp::attachInterface<
PartialEntityAccessModel<fir::ArrayCoorOp>>(*ctx);
fir::CoordinateOp::attachInterface<
PartialEntityAccessModel<fir::CoordinateOp>>(*ctx);
fir::DeclareOp::attachInterface<
PartialEntityAccessModel<fir::DeclareOp>>(*ctx);
});

// Register HLFIR operation interfaces
registry.addExtension(+[](mlir::MLIRContext *ctx,
hlfir::hlfirDialect *dialect) {
hlfir::DesignateOp::attachInterface<
PartialEntityAccessModel<hlfir::DesignateOp>>(*ctx);
hlfir::DeclareOp::attachInterface<
PartialEntityAccessModel<hlfir::DeclareOp>>(*ctx);
});

registerAttrsExtensions(registry);
}

Expand Down
18 changes: 18 additions & 0 deletions mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,22 @@ def ComputeRegionOpInterface : OpInterface<"ComputeRegionOpInterface"> {
];
}

def PartialEntityAccessOpInterface : OpInterface<"PartialEntityAccessOpInterface"> {
let cppNamespace = "::mlir::acc";

let description = [{
An interface for operations that access a partial entity such as
field or array element access.
}];

let methods = [
InterfaceMethod<"Get the base entity being accessed", "::mlir::Value",
"getBaseEntity", (ins)>,
InterfaceMethod<"Check if this is a complete view of the entity", "bool",
"isCompleteView", (ins), [{
return false;
}]>,
];
}

#endif // OPENACC_OPS_INTERFACES
5 changes: 5 additions & 0 deletions mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ std::string getVariableName(mlir::Value v);
/// Returns an empty string if not possible to generate a recipe name.
std::string getRecipeName(mlir::acc::RecipeKind kind, mlir::Type type);

// Get the base entity from partial entity access. This is used for getting
// the base `struct` from an operation that only accesses a field or the
// base `array` from an operation that only accesses a subarray.
mlir::Value getBaseEntity(mlir::Value val);

} // namespace acc
} // namespace mlir

Expand Down
10 changes: 10 additions & 0 deletions mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,13 @@ std::string mlir::acc::getRecipeName(mlir::acc::RecipeKind kind,

return recipeName;
}

mlir::Value mlir::acc::getBaseEntity(mlir::Value val) {
if (auto partialEntityAccessOp =
dyn_cast<PartialEntityAccessOpInterface>(val.getDefiningOp())) {
if (!partialEntityAccessOp.isCompleteView())
return partialEntityAccessOp.getBaseEntity();
}

return val;
}
103 changes: 103 additions & 0 deletions mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,3 +570,106 @@ TEST_F(OpenACCUtilsTest, getRecipeNamePrivateUnrankedMemref) {
getRecipeName(RecipeKind::private_recipe, unrankedMemrefTy);
EXPECT_EQ(recipeName, "privatization_memref_Zxi32_");
}

//===----------------------------------------------------------------------===//
// getBaseEntity Tests
//===----------------------------------------------------------------------===//

// Local implementation of PartialEntityAccessOpInterface for memref.subview.
// This is implemented locally in the test rather than officially because memref
// operations already have ViewLikeOpInterface, which serves a similar purpose
// for walking through views to the base entity. This test demonstrates how
// getBaseEntity() would work if the interface were attached to memref.subview.
namespace {
struct SubViewOpPartialEntityAccessOpInterface
: public acc::PartialEntityAccessOpInterface::ExternalModel<
SubViewOpPartialEntityAccessOpInterface, memref::SubViewOp> {
Value getBaseEntity(Operation *op) const {
auto subviewOp = cast<memref::SubViewOp>(op);
return subviewOp.getSource();
}

bool isCompleteView(Operation *op) const {
// For testing purposes, we'll consider it a partial view (return false).
// The real implementation would need to look at the offsets.
return false;
}
};
} // namespace

TEST_F(OpenACCUtilsTest, getBaseEntityFromSubview) {
// Register the local interface implementation for memref.subview
memref::SubViewOp::attachInterface<SubViewOpPartialEntityAccessOpInterface>(
context);

// Create a base memref
auto memrefTy = MemRefType::get({10, 20}, b.getF32Type());
OwningOpRef<memref::AllocaOp> allocOp =
memref::AllocaOp::create(b, loc, memrefTy);
Value baseMemref = allocOp->getResult();

// Create a subview of the base memref with non-zero offsets
// This creates a 5x10 view starting at [2, 3] in the original 10x20 memref
SmallVector<OpFoldResult> offsets = {b.getIndexAttr(2), b.getIndexAttr(3)};
SmallVector<OpFoldResult> sizes = {b.getIndexAttr(5), b.getIndexAttr(10)};
SmallVector<OpFoldResult> strides = {b.getIndexAttr(1), b.getIndexAttr(1)};

OwningOpRef<memref::SubViewOp> subviewOp = memref::SubViewOp::create(
b, loc, baseMemref, offsets, sizes, strides);
Value subview = subviewOp->getResult();

// Test that getBaseEntity returns the base memref, not the subview
Value baseEntity = getBaseEntity(subview);
EXPECT_EQ(baseEntity, baseMemref);
}

TEST_F(OpenACCUtilsTest, getBaseEntityNoInterface) {
// Create a memref without the interface
auto memrefTy = MemRefType::get({10}, b.getI32Type());
OwningOpRef<memref::AllocaOp> allocOp =
memref::AllocaOp::create(b, loc, memrefTy);
Value varPtr = allocOp->getResult();

// Test that getBaseEntity returns the value itself when there's no interface
Value baseEntity = getBaseEntity(varPtr);
EXPECT_EQ(baseEntity, varPtr);
}

TEST_F(OpenACCUtilsTest, getBaseEntityChainedSubviews) {
// Register the local interface implementation for memref.subview
memref::SubViewOp::attachInterface<SubViewOpPartialEntityAccessOpInterface>(
context);

// Create a base memref
auto memrefTy = MemRefType::get({100, 200}, b.getI64Type());
OwningOpRef<memref::AllocaOp> allocOp =
memref::AllocaOp::create(b, loc, memrefTy);
Value baseMemref = allocOp->getResult();

// Create first subview
SmallVector<OpFoldResult> offsets1 = {b.getIndexAttr(10), b.getIndexAttr(20)};
SmallVector<OpFoldResult> sizes1 = {b.getIndexAttr(50), b.getIndexAttr(80)};
SmallVector<OpFoldResult> strides1 = {b.getIndexAttr(1), b.getIndexAttr(1)};

OwningOpRef<memref::SubViewOp> subview1Op = memref::SubViewOp::create(
b, loc, baseMemref, offsets1, sizes1, strides1);
Value subview1 = subview1Op->getResult();

// Create second subview (subview of subview)
SmallVector<OpFoldResult> offsets2 = {b.getIndexAttr(5), b.getIndexAttr(10)};
SmallVector<OpFoldResult> sizes2 = {b.getIndexAttr(20), b.getIndexAttr(30)};
SmallVector<OpFoldResult> strides2 = {b.getIndexAttr(1), b.getIndexAttr(1)};

OwningOpRef<memref::SubViewOp> subview2Op = memref::SubViewOp::create(
b, loc, subview1, offsets2, sizes2, strides2);
Value subview2 = subview2Op->getResult();

// Test that getBaseEntity on the nested subview returns the first subview
// (since our implementation returns the immediate source, not the ultimate base)
Value baseEntity = getBaseEntity(subview2);
EXPECT_EQ(baseEntity, subview1);

// Test that calling getBaseEntity again returns the original base
Value ultimateBase = getBaseEntity(baseEntity);
EXPECT_EQ(ultimateBase, baseMemref);
}
Loading