MLIR 24.0.0git
ACCDeclareGPUModuleInsertion.cpp
Go to the documentation of this file.
1//===- ACCDeclareGPUModuleInsertion.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// This pass copies globals marked with the `acc.declare` attribute into the
10// GPU module so that device code (e.g. acc routine, compute regions) can
11// reference them.
12//
13// Overview:
14// ---------
15// Globals that have the `acc.declare` attribute (from the OpenACC declare
16// directive or from the `ACCImplicitDeclare` pass) must be present in the
17// GPU module for device code to use them. This pass inserts copies of those
18// globals into the GPU module, creating the module if it does not yet exist.
19// The host copy of each global remains in the parent module.
20//
21// Example:
22// --------
23//
24// Before:
25// module {
26// memref.global @arr : memref<7xf32> = dense<0.0>
27// {acc.declare = #acc.declare<dataClause = acc_create>}
28// }
29//
30// After:
31// module attributes {gpu.container_module} {
32// memref.global @arr : memref<7xf32> = dense<0.0>
33// {acc.declare = #acc.declare<dataClause = acc_create>}
34// gpu.module @acc_gpu_module {
35// memref.global @arr : memref<7xf32> = dense<0.0>
36// {acc.declare = #acc.declare<dataClause = acc_create>}
37// }
38// }
39//
40// Requirements:
41// -------------
42// The pass uses the `acc::OpenACCSupport` for:
43// - getOrCreateGPUModule: to obtain or create the GPU module.
44// - emitNYI: to report failure when GPU module creation is not supported.
45// If no custom implementation is registered, the default implementation is
46// used (see OpenACCSupport).
47//
48//===----------------------------------------------------------------------===//
49
55#include "mlir/IR/BuiltinOps.h"
56#include "mlir/IR/Operation.h"
58#include "mlir/IR/SymbolTable.h"
59
60namespace mlir {
61namespace acc {
62#define GEN_PASS_DEF_ACCDECLAREGPUMODULEINSERTION
63#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
64} // namespace acc
65} // namespace mlir
66
67#define DEBUG_TYPE "acc-declare-gpu-module-insertion"
68
69using namespace mlir;
70
71namespace {
72static bool hasAccDeclareGlobals(ModuleOp mod) {
73 for (Operation &op : mod.getBody()->getOperations())
74 if (op.getDiscardableAttr(acc::getDeclareAttrName()))
75 return true;
76 return false;
77}
78
79static void makeDeviceGlobalDeclaration(Operation &globalOp) {
80 globalOp.setInherentAttr(StringAttr::get(globalOp.getContext(), "initVal"),
81 {});
82 globalOp.setInherentAttr(StringAttr::get(globalOp.getContext(), "linkage"),
83 {});
84 for (Region &region : globalOp.getRegions()) {
85 region.dropAllReferences();
86 region.getBlocks().clear();
87 }
88}
89
90class ACCDeclareGPUModuleInsertion
92 ACCDeclareGPUModuleInsertion> {
93public:
95 ACCDeclareGPUModuleInsertion>::ACCDeclareGPUModuleInsertionBase;
96
97 void getDependentDialects(DialectRegistry &registry) const override {
99 ACCDeclareGPUModuleInsertion>::getDependentDialects(registry);
101 }
102
103 LogicalResult copyGlobalsToGPUModule(gpu::GPUModuleOp gpuMod, ModuleOp mod,
104 acc::OpenACCSupport &accSupport) const {
105 SymbolTable gpuSymTable(gpuMod);
106
107 for (Operation &globalOp : mod.getBody()->getOperations()) {
109 continue;
110
111 auto symOp = dyn_cast<SymbolOpInterface>(&globalOp);
112 if (!symOp)
113 continue;
114
115 StringAttr name = symOp.getNameAttr();
116 Operation *deviceGlobal = globalOp.clone();
117 auto declareAttr = globalOp.getDiscardableAttrOfType<acc::DeclareAttr>(
119 auto globalVar = dyn_cast<acc::GlobalVariableOpInterface>(&globalOp);
120 bool makeUnifiedDeclaration =
121 cudaUnified &&
122 declareAttr.getDataClause().getValue() !=
123 acc::DataClause::acc_declare_device_resident &&
124 (!globalVar || !globalVar.isConstant() ||
125 globalVar.isCompilerGenerated());
126 if (makeUnifiedDeclaration)
127 makeDeviceGlobalDeclaration(*deviceGlobal);
128
129 if (Operation *existing = gpuSymTable.lookup(name.getValue())) {
130 // Reuse when structurally equivalent ignoring locations and discardable
131 // attrs such as `acc.declare` attributes. Only a different op type or a
132 // true definition mismatch is a conflict.
133 auto isEquivalent = [](Operation *lhs, Operation *rhs) {
134 return lhs->getName() == rhs->getName() &&
137 /*markEquivalent=*/nullptr,
140 };
141 if (!isEquivalent(existing, deviceGlobal)) {
142 // Earlier GPU lowering can create a global in the GPU module before
143 // this pass. In unified memory, convert an equivalent pre-existing
144 // global to the declaration form expected for an OpenACC global.
145 if (makeUnifiedDeclaration) {
146 Operation *normalizedExisting = existing->clone();
147 makeDeviceGlobalDeclaration(*normalizedExisting);
148 bool canReuse = isEquivalent(normalizedExisting, deviceGlobal);
149 normalizedExisting->destroy();
150 if (canReuse)
151 makeDeviceGlobalDeclaration(*existing);
152 }
153 }
154 if (!isEquivalent(existing, deviceGlobal)) {
155 deviceGlobal->destroy();
156 accSupport.emitNYI(globalOp.getLoc(),
157 llvm::Twine("duplicate global symbol '") +
158 name.getValue() + "' in gpu module");
159 return failure();
160 }
161 // Propagate acc.declare onto the GPU copy if it was cloned before the
162 // host global was marked.
163 if (!existing->getDiscardableAttr(acc::getDeclareAttrName()))
164 if (Attribute declareAttr =
166 existing->setDiscardableAttr(acc::getDeclareAttrName(),
167 declareAttr);
168 deviceGlobal->destroy();
169 continue;
170 }
171
172 gpuSymTable.insert(deviceGlobal);
173 }
174 return success();
175 }
176
177 void runOnOperation() override {
178 ModuleOp mod = getOperation();
179
180 // Check for any candidates first - do this to avoid creating the GPU module
181 // if there are no candidates.
182 if (!hasAccDeclareGlobals(mod))
183 return;
184
185 acc::OpenACCSupport &accSupport = getAnalysis<acc::OpenACCSupport>();
186 std::optional<gpu::GPUModuleOp> gpuMod =
187 accSupport.getOrCreateGPUModule(mod);
188 if (!gpuMod) {
189 accSupport.emitNYI(mod.getLoc(), "Failed to create GPU module");
190 return;
191 }
192
193 if (failed(copyGlobalsToGPUModule(*gpuMod, mod, accSupport)))
194 return;
195 }
196};
197} // namespace
return success()
Attributes are known-constant values of operations.
Definition Attributes.h:25
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
void setInherentAttr(StringAttr name, Attribute value)
Set an inherent attribute by name.
Attribute getDiscardableAttr(StringRef name)
Access a discardable attribute by name, returns a null Attribute if the discardable attribute does no...
Definition Operation.h:485
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
void destroy()
Destroys this operation and its subclass data.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:729
AttrClass getDiscardableAttrOfType(StringRef name)
Access a discardable attribute by name and cast it to AttrClass.
Definition Operation.h:493
Operation * clone(IRMapping &mapper, const CloneOptions &options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition SymbolTable.h:25
Operation * lookup(StringRef name) const
Look up a symbol with the specified name, returning null if no such name exists.
StringAttr insert(Operation *symbol, Block::iterator insertPt={})
Insert a new symbol into the table, and rename it as necessary to avoid collisions.
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
std::optional< gpu::GPUModuleOp > getOrCreateGPUModule(ModuleOp mod, bool create=true, llvm::StringRef name="")
Get or optionally create a GPU module in the given module.
void registerAllExtensions(DialectRegistry &registry)
Register the standard OpenACC models for GPU, LLVM and MemRef IR.
static constexpr StringLiteral getDeclareAttrName()
Used to obtain the attribute name for declare.
Definition OpenACC.h:195
Include the generated interface declarations.
static bool isEquivalentTo(Operation *lhs, Operation *rhs, function_ref< LogicalResult(Value, Value)> checkEquivalent, function_ref< void(Value, Value)> markEquivalent=nullptr, Flags flags=Flags::None, function_ref< LogicalResult(ValueRange, ValueRange)> checkCommutativeEquivalent=nullptr)
Compare two operations (including their regions) and return if they are equivalent.
static LogicalResult ignoreValueEquivalence(Value lhs, Value rhs)
Helper that can be used with isEquivalentTo above to consider ops equivalent even if their operands a...