Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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;
}
104 changes: 104 additions & 0 deletions mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,3 +570,107 @@ 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);
}