Skip to content

Commit 33892e5

Browse files
razvanlupusoruDebadri Basak
authored andcommitted
[acc][flang] Implement OpenACC interface for partial entity accesses (llvm#165911)
For OpenACC clause ordering, such as maintaining appropriate parent-child relationship ordering, we need to be able to walk references back to their base entities. This introduces the operation interface in the `acc` dialect named `PartialEntityAccessOpInterface` which can be used for this purpose. The interface provides two methods: - `getBaseEntity()`: Returns the base entity being accessed - `isCompleteView()`: Indicates whether the access covers the complete entity to allow this interface to be attached to cases that only conditionally offer a partial view This also adds a utility function `mlir::acc::getBaseEntity()` that uses this interface to retrieve the base entity from a value. This work has some similarities with the ViewLikeOpInterface proposal for FIR: llvm#164020 but it differs in the following ways: - Attached only to operations where we can assume a partial entity access - Includes fir.declare operations due to common block storage associations Tests are included that demonstrate the interface on memref.subview operations, implemented locally in the test since memref operations already have ViewLikeOpInterface for similar purposes.
1 parent f2bf3a4 commit 33892e5

File tree

10 files changed

+285
-1
lines changed

10 files changed

+285
-1
lines changed

flang/include/flang/Optimizer/Dialect/FIROps.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
2121
#include "mlir/Interfaces/LoopLikeInterface.h"
2222
#include "mlir/Interfaces/SideEffectInterfaces.h"
23+
#include "mlir/Interfaces/ViewLikeInterface.h"
2324

2425
namespace fir {
2526

flang/include/flang/Optimizer/Dialect/FIROps.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
include "mlir/Dialect/Arith/IR/ArithBase.td"
1818
include "mlir/Dialect/Arith/IR/ArithOpsInterfaces.td"
1919
include "mlir/Dialect/LLVMIR/LLVMAttrDefs.td"
20+
include "mlir/Interfaces/ViewLikeInterface.td"
2021
include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.td"
2122
include "flang/Optimizer/Dialect/FIRDialect.td"
2223
include "flang/Optimizer/Dialect/FIRTypes.td"
@@ -2828,7 +2829,8 @@ def fir_VolatileCastOp : fir_SimpleOneResultOp<"volatile_cast", [Pure]> {
28282829
let hasFolder = 1;
28292830
}
28302831

2831-
def fir_ConvertOp : fir_SimpleOneResultOp<"convert", [NoMemoryEffect]> {
2832+
def fir_ConvertOp
2833+
: fir_SimpleOneResultOp<"convert", [NoMemoryEffect, ViewLikeOpInterface]> {
28322834
let summary = "encapsulates all Fortran entity type conversions";
28332835

28342836
let description = [{
@@ -2866,6 +2868,7 @@ def fir_ConvertOp : fir_SimpleOneResultOp<"convert", [NoMemoryEffect]> {
28662868
static bool isPointerCompatible(mlir::Type ty);
28672869
static bool canBeConverted(mlir::Type inType, mlir::Type outType);
28682870
static bool areVectorsCompatible(mlir::Type inTy, mlir::Type outTy);
2871+
mlir::Value getViewSource() { return getValue(); }
28692872
}];
28702873
let hasCanonicalizer = 1;
28712874
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===- FIROpenACCOpsInterfaces.h --------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contains external operation interfaces for FIR.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef FLANG_OPTIMIZER_OPENACC_FIROPENACC_OPS_INTERFACES_H_
14+
#define FLANG_OPTIMIZER_OPENACC_FIROPENACC_OPS_INTERFACES_H_
15+
16+
#include "mlir/Dialect/OpenACC/OpenACC.h"
17+
18+
namespace fir {
19+
class DeclareOp;
20+
} // namespace fir
21+
22+
namespace hlfir {
23+
class DeclareOp;
24+
class DesignateOp;
25+
} // namespace hlfir
26+
27+
namespace fir::acc {
28+
29+
template <typename Op>
30+
struct PartialEntityAccessModel
31+
: public mlir::acc::PartialEntityAccessOpInterface::ExternalModel<
32+
PartialEntityAccessModel<Op>, Op> {
33+
mlir::Value getBaseEntity(mlir::Operation *op) const;
34+
35+
// Default implementation - returns false (partial view)
36+
bool isCompleteView(mlir::Operation *op) const { return false; }
37+
};
38+
39+
// Full specializations for declare operations
40+
template <>
41+
struct PartialEntityAccessModel<fir::DeclareOp>
42+
: public mlir::acc::PartialEntityAccessOpInterface::ExternalModel<
43+
PartialEntityAccessModel<fir::DeclareOp>, fir::DeclareOp> {
44+
mlir::Value getBaseEntity(mlir::Operation *op) const;
45+
bool isCompleteView(mlir::Operation *op) const;
46+
};
47+
48+
template <>
49+
struct PartialEntityAccessModel<hlfir::DeclareOp>
50+
: public mlir::acc::PartialEntityAccessOpInterface::ExternalModel<
51+
PartialEntityAccessModel<hlfir::DeclareOp>, hlfir::DeclareOp> {
52+
mlir::Value getBaseEntity(mlir::Operation *op) const;
53+
bool isCompleteView(mlir::Operation *op) const;
54+
};
55+
56+
} // namespace fir::acc
57+
58+
#endif // FLANG_OPTIMIZER_OPENACC_FIROPENACC_OPS_INTERFACES_H_

flang/lib/Optimizer/OpenACC/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
22

33
add_flang_library(FIROpenACCSupport
44
FIROpenACCAttributes.cpp
5+
FIROpenACCOpsInterfaces.cpp
56
FIROpenACCTypeInterfaces.cpp
67
RegisterOpenACCExtensions.cpp
78

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===-- FIROpenACCOpsInterfaces.cpp ---------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Implementation of external operation interfaces for FIR.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h"
14+
15+
#include "flang/Optimizer/Dialect/FIROps.h"
16+
#include "flang/Optimizer/HLFIR/HLFIROps.h"
17+
18+
namespace fir::acc {
19+
20+
template <>
21+
mlir::Value PartialEntityAccessModel<fir::ArrayCoorOp>::getBaseEntity(
22+
mlir::Operation *op) const {
23+
return mlir::cast<fir::ArrayCoorOp>(op).getMemref();
24+
}
25+
26+
template <>
27+
mlir::Value PartialEntityAccessModel<fir::CoordinateOp>::getBaseEntity(
28+
mlir::Operation *op) const {
29+
return mlir::cast<fir::CoordinateOp>(op).getRef();
30+
}
31+
32+
template <>
33+
mlir::Value PartialEntityAccessModel<hlfir::DesignateOp>::getBaseEntity(
34+
mlir::Operation *op) const {
35+
return mlir::cast<hlfir::DesignateOp>(op).getMemref();
36+
}
37+
38+
mlir::Value PartialEntityAccessModel<fir::DeclareOp>::getBaseEntity(
39+
mlir::Operation *op) const {
40+
return mlir::cast<fir::DeclareOp>(op).getStorage();
41+
}
42+
43+
bool PartialEntityAccessModel<fir::DeclareOp>::isCompleteView(
44+
mlir::Operation *op) const {
45+
// Return false (partial view) only if storage is present
46+
// Return true (complete view) if storage is absent
47+
return !getBaseEntity(op);
48+
}
49+
50+
mlir::Value PartialEntityAccessModel<hlfir::DeclareOp>::getBaseEntity(
51+
mlir::Operation *op) const {
52+
return mlir::cast<hlfir::DeclareOp>(op).getStorage();
53+
}
54+
55+
bool PartialEntityAccessModel<hlfir::DeclareOp>::isCompleteView(
56+
mlir::Operation *op) const {
57+
// Return false (partial view) only if storage is present
58+
// Return true (complete view) if storage is absent
59+
return !getBaseEntity(op);
60+
}
61+
62+
} // namespace fir::acc

flang/lib/Optimizer/OpenACC/Support/RegisterOpenACCExtensions.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "flang/Optimizer/OpenACC/Support/RegisterOpenACCExtensions.h"
14+
1415
#include "flang/Optimizer/Dialect/FIRDialect.h"
16+
#include "flang/Optimizer/Dialect/FIROps.h"
1517
#include "flang/Optimizer/Dialect/FIRType.h"
18+
#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
19+
#include "flang/Optimizer/HLFIR/HLFIROps.h"
20+
#include "flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h"
1621
#include "flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h"
1722

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

3843
fir::LLVMPointerType::attachInterface<
3944
OpenACCPointerLikeModel<fir::LLVMPointerType>>(*ctx);
45+
46+
fir::ArrayCoorOp::attachInterface<
47+
PartialEntityAccessModel<fir::ArrayCoorOp>>(*ctx);
48+
fir::CoordinateOp::attachInterface<
49+
PartialEntityAccessModel<fir::CoordinateOp>>(*ctx);
50+
fir::DeclareOp::attachInterface<PartialEntityAccessModel<fir::DeclareOp>>(
51+
*ctx);
4052
});
53+
54+
// Register HLFIR operation interfaces
55+
registry.addExtension(
56+
+[](mlir::MLIRContext *ctx, hlfir::hlfirDialect *dialect) {
57+
hlfir::DesignateOp::attachInterface<
58+
PartialEntityAccessModel<hlfir::DesignateOp>>(*ctx);
59+
hlfir::DeclareOp::attachInterface<
60+
PartialEntityAccessModel<hlfir::DeclareOp>>(*ctx);
61+
});
62+
4163
registerAttrsExtensions(registry);
4264
}
4365

mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,22 @@ def ComputeRegionOpInterface : OpInterface<"ComputeRegionOpInterface"> {
2626
];
2727
}
2828

29+
def PartialEntityAccessOpInterface : OpInterface<"PartialEntityAccessOpInterface"> {
30+
let cppNamespace = "::mlir::acc";
31+
32+
let description = [{
33+
An interface for operations that access a partial entity such as
34+
field or array element access.
35+
}];
36+
37+
let methods = [
38+
InterfaceMethod<"Get the base entity being accessed", "::mlir::Value",
39+
"getBaseEntity", (ins)>,
40+
InterfaceMethod<"Check if this is a complete view of the entity", "bool",
41+
"isCompleteView", (ins), [{
42+
return false;
43+
}]>,
44+
];
45+
}
46+
2947
#endif // OPENACC_OPS_INTERFACES

mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ std::string getVariableName(mlir::Value v);
4747
/// Returns an empty string if not possible to generate a recipe name.
4848
std::string getRecipeName(mlir::acc::RecipeKind kind, mlir::Type type);
4949

50+
// Get the base entity from partial entity access. This is used for getting
51+
// the base `struct` from an operation that only accesses a field or the
52+
// base `array` from an operation that only accesses a subarray.
53+
mlir::Value getBaseEntity(mlir::Value val);
54+
5055
} // namespace acc
5156
} // namespace mlir
5257

mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,13 @@ std::string mlir::acc::getRecipeName(mlir::acc::RecipeKind kind,
145145

146146
return recipeName;
147147
}
148+
149+
mlir::Value mlir::acc::getBaseEntity(mlir::Value val) {
150+
if (auto partialEntityAccessOp =
151+
dyn_cast<PartialEntityAccessOpInterface>(val.getDefiningOp())) {
152+
if (!partialEntityAccessOp.isCompleteView())
153+
return partialEntityAccessOp.getBaseEntity();
154+
}
155+
156+
return val;
157+
}

mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,3 +570,107 @@ TEST_F(OpenACCUtilsTest, getRecipeNamePrivateUnrankedMemref) {
570570
getRecipeName(RecipeKind::private_recipe, unrankedMemrefTy);
571571
EXPECT_EQ(recipeName, "privatization_memref_Zxi32_");
572572
}
573+
574+
//===----------------------------------------------------------------------===//
575+
// getBaseEntity Tests
576+
//===----------------------------------------------------------------------===//
577+
578+
// Local implementation of PartialEntityAccessOpInterface for memref.subview.
579+
// This is implemented locally in the test rather than officially because memref
580+
// operations already have ViewLikeOpInterface, which serves a similar purpose
581+
// for walking through views to the base entity. This test demonstrates how
582+
// getBaseEntity() would work if the interface were attached to memref.subview.
583+
namespace {
584+
struct SubViewOpPartialEntityAccessOpInterface
585+
: public acc::PartialEntityAccessOpInterface::ExternalModel<
586+
SubViewOpPartialEntityAccessOpInterface, memref::SubViewOp> {
587+
Value getBaseEntity(Operation *op) const {
588+
auto subviewOp = cast<memref::SubViewOp>(op);
589+
return subviewOp.getSource();
590+
}
591+
592+
bool isCompleteView(Operation *op) const {
593+
// For testing purposes, we'll consider it a partial view (return false).
594+
// The real implementation would need to look at the offsets.
595+
return false;
596+
}
597+
};
598+
} // namespace
599+
600+
TEST_F(OpenACCUtilsTest, getBaseEntityFromSubview) {
601+
// Register the local interface implementation for memref.subview
602+
memref::SubViewOp::attachInterface<SubViewOpPartialEntityAccessOpInterface>(
603+
context);
604+
605+
// Create a base memref
606+
auto memrefTy = MemRefType::get({10, 20}, b.getF32Type());
607+
OwningOpRef<memref::AllocaOp> allocOp =
608+
memref::AllocaOp::create(b, loc, memrefTy);
609+
Value baseMemref = allocOp->getResult();
610+
611+
// Create a subview of the base memref with non-zero offsets
612+
// This creates a 5x10 view starting at [2, 3] in the original 10x20 memref
613+
SmallVector<OpFoldResult> offsets = {b.getIndexAttr(2), b.getIndexAttr(3)};
614+
SmallVector<OpFoldResult> sizes = {b.getIndexAttr(5), b.getIndexAttr(10)};
615+
SmallVector<OpFoldResult> strides = {b.getIndexAttr(1), b.getIndexAttr(1)};
616+
617+
OwningOpRef<memref::SubViewOp> subviewOp =
618+
memref::SubViewOp::create(b, loc, baseMemref, offsets, sizes, strides);
619+
Value subview = subviewOp->getResult();
620+
621+
// Test that getBaseEntity returns the base memref, not the subview
622+
Value baseEntity = getBaseEntity(subview);
623+
EXPECT_EQ(baseEntity, baseMemref);
624+
}
625+
626+
TEST_F(OpenACCUtilsTest, getBaseEntityNoInterface) {
627+
// Create a memref without the interface
628+
auto memrefTy = MemRefType::get({10}, b.getI32Type());
629+
OwningOpRef<memref::AllocaOp> allocOp =
630+
memref::AllocaOp::create(b, loc, memrefTy);
631+
Value varPtr = allocOp->getResult();
632+
633+
// Test that getBaseEntity returns the value itself when there's no interface
634+
Value baseEntity = getBaseEntity(varPtr);
635+
EXPECT_EQ(baseEntity, varPtr);
636+
}
637+
638+
TEST_F(OpenACCUtilsTest, getBaseEntityChainedSubviews) {
639+
// Register the local interface implementation for memref.subview
640+
memref::SubViewOp::attachInterface<SubViewOpPartialEntityAccessOpInterface>(
641+
context);
642+
643+
// Create a base memref
644+
auto memrefTy = MemRefType::get({100, 200}, b.getI64Type());
645+
OwningOpRef<memref::AllocaOp> allocOp =
646+
memref::AllocaOp::create(b, loc, memrefTy);
647+
Value baseMemref = allocOp->getResult();
648+
649+
// Create first subview
650+
SmallVector<OpFoldResult> offsets1 = {b.getIndexAttr(10), b.getIndexAttr(20)};
651+
SmallVector<OpFoldResult> sizes1 = {b.getIndexAttr(50), b.getIndexAttr(80)};
652+
SmallVector<OpFoldResult> strides1 = {b.getIndexAttr(1), b.getIndexAttr(1)};
653+
654+
OwningOpRef<memref::SubViewOp> subview1Op =
655+
memref::SubViewOp::create(b, loc, baseMemref, offsets1, sizes1, strides1);
656+
Value subview1 = subview1Op->getResult();
657+
658+
// Create second subview (subview of subview)
659+
SmallVector<OpFoldResult> offsets2 = {b.getIndexAttr(5), b.getIndexAttr(10)};
660+
SmallVector<OpFoldResult> sizes2 = {b.getIndexAttr(20), b.getIndexAttr(30)};
661+
SmallVector<OpFoldResult> strides2 = {b.getIndexAttr(1), b.getIndexAttr(1)};
662+
663+
OwningOpRef<memref::SubViewOp> subview2Op =
664+
memref::SubViewOp::create(b, loc, subview1, offsets2, sizes2, strides2);
665+
Value subview2 = subview2Op->getResult();
666+
667+
// Test that getBaseEntity on the nested subview returns the first subview
668+
// (since our implementation returns the immediate source, not the ultimate
669+
// base)
670+
Value baseEntity = getBaseEntity(subview2);
671+
EXPECT_EQ(baseEntity, subview1);
672+
673+
// Test that calling getBaseEntity again returns the original base
674+
Value ultimateBase = getBaseEntity(baseEntity);
675+
EXPECT_EQ(ultimateBase, baseMemref);
676+
}

0 commit comments

Comments
 (0)