MLIR 24.0.0git
ACCDeclareGPUModuleInsertion.cpp
Go to the documentation of this file.
1//===- ACCDeclareGPUModuleInsertion.cpp
2//------------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// This pass copies globals marked with the `acc.declare` attribute into the
11// GPU module so that device code (e.g. acc routine, compute regions) can
12// reference them.
13//
14// Overview:
15// ---------
16// Globals that have the `acc.declare` attribute (from the OpenACC declare
17// directive or from the `ACCImplicitDeclare` pass) must be present in the
18// GPU module for device code to use them. This pass inserts copies of those
19// globals into the GPU module, creating the module if it does not yet exist.
20// The host copy of each global remains in the parent module.
21//
22// Example:
23// --------
24//
25// Before:
26// module {
27// memref.global @arr : memref<7xf32> = dense<0.0>
28// {acc.declare = #acc.declare<dataClause = acc_create>}
29// }
30//
31// After:
32// module attributes {gpu.container_module} {
33// memref.global @arr : memref<7xf32> = dense<0.0>
34// {acc.declare = #acc.declare<dataClause = acc_create>}
35// gpu.module @acc_gpu_module {
36// memref.global @arr : memref<7xf32> = dense<0.0>
37// {acc.declare = #acc.declare<dataClause = acc_create>}
38// }
39// }
40//
41// Requirements:
42// -------------
43// The pass uses the `acc::OpenACCSupport` for:
44// - getOrCreateGPUModule: to obtain or create the GPU module.
45// - emitNYI: to report failure when GPU module creation is not supported.
46// If no custom implementation is registered, the default implementation is
47// used (see OpenACCSupport).
48//
49//===----------------------------------------------------------------------===//
50
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 {
72
73static bool hasAccDeclareGlobals(ModuleOp mod) {
74 for (Operation &op : mod.getBody()->getOperations())
75 if (op.getAttr(acc::getDeclareAttrName()))
76 return true;
77 return false;
78}
79
80static void makeDeviceGlobalDeclaration(Operation &globalOp) {
81 globalOp.removeAttr("initVal");
82 globalOp.removeAttr("linkName");
83 for (Region &region : globalOp.getRegions()) {
84 region.dropAllReferences();
85 region.getBlocks().clear();
86 }
87}
88
89class ACCDeclareGPUModuleInsertion
90 : public acc::impl::ACCDeclareGPUModuleInsertionBase<
91 ACCDeclareGPUModuleInsertion> {
92public:
93 using acc::impl::ACCDeclareGPUModuleInsertionBase<
94 ACCDeclareGPUModuleInsertion>::ACCDeclareGPUModuleInsertionBase;
95
96 LogicalResult copyGlobalsToGPUModule(gpu::GPUModuleOp gpuMod, ModuleOp mod,
97 acc::OpenACCSupport &accSupport) const {
98 SymbolTable gpuSymTable(gpuMod);
99
100 for (Operation &globalOp : mod.getBody()->getOperations()) {
101 if (!globalOp.getAttr(acc::getDeclareAttrName()))
102 continue;
103
104 auto symOp = dyn_cast<SymbolOpInterface>(&globalOp);
105 if (!symOp)
106 continue;
107
108 StringAttr name = symOp.getNameAttr();
109 Operation *deviceGlobal = globalOp.clone();
110 auto declareAttr =
111 globalOp.getAttrOfType<acc::DeclareAttr>(acc::getDeclareAttrName());
112 auto globalVar = dyn_cast<acc::GlobalVariableOpInterface>(&globalOp);
113 bool makeUnifiedDeclaration =
114 cudaUnified &&
115 declareAttr.getDataClause().getValue() !=
116 acc::DataClause::acc_declare_device_resident &&
117 (!globalVar || !globalVar.isConstant());
118 if (makeUnifiedDeclaration)
119 makeDeviceGlobalDeclaration(*deviceGlobal);
120
121 if (Operation *existing = gpuSymTable.lookup(name.getValue())) {
122 // Reuse when structurally equivalent ignoring locations and discardable
123 // attrs such as `acc.declare` attributes. Only a different op type or a
124 // true definition mismatch is a conflict.
125 auto isEquivalent = [](Operation *lhs, Operation *rhs) {
126 return lhs->getName() == rhs->getName() &&
129 /*markEquivalent=*/nullptr,
132 };
133 if (!isEquivalent(existing, deviceGlobal)) {
134 // Earlier GPU lowering can create a global in the GPU module before
135 // this pass. In unified memory, convert an equivalent pre-existing
136 // global to the declaration form expected for an OpenACC global.
137 if (makeUnifiedDeclaration) {
138 Operation *normalizedExisting = existing->clone();
139 makeDeviceGlobalDeclaration(*normalizedExisting);
140 bool canReuse = isEquivalent(normalizedExisting, deviceGlobal);
141 normalizedExisting->destroy();
142 if (canReuse)
143 makeDeviceGlobalDeclaration(*existing);
144 }
145 }
146 if (!isEquivalent(existing, deviceGlobal)) {
147 deviceGlobal->destroy();
148 accSupport.emitNYI(globalOp.getLoc(),
149 llvm::Twine("duplicate global symbol '") +
150 name.getValue() + "' in gpu module");
151 return failure();
152 }
153 // Propagate acc.declare onto the GPU copy if it was cloned before the
154 // host global was marked.
155 if (!existing->getAttr(acc::getDeclareAttrName()))
156 if (Attribute declareAttr =
158 existing->setAttr(acc::getDeclareAttrName(), declareAttr);
159 deviceGlobal->destroy();
160 continue;
161 }
162
163 gpuSymTable.insert(deviceGlobal);
164 }
165 return success();
166 }
167
168 void runOnOperation() override {
169 ModuleOp mod = getOperation();
170
171 // Check for any candidates first - do this to avoid creating the GPU module
172 // if there are no candidates.
173 if (!hasAccDeclareGlobals(mod))
174 return;
175
176 acc::OpenACCSupport &accSupport = getAnalysis<acc::OpenACCSupport>();
177 std::optional<gpu::GPUModuleOp> gpuMod =
178 accSupport.getOrCreateGPUModule(mod);
179 if (!gpuMod) {
180 accSupport.emitNYI(mod.getLoc(), "Failed to create GPU module");
181 return;
182 }
183
184 if (failed(copyGlobalsToGPUModule(*gpuMod, mod, accSupport)))
185 return;
186 }
187};
188
189} // namespace
return success()
lhs
Attributes are known-constant values of operations.
Definition Attributes.h:25
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
AttrClass getAttrOfType(StringAttr name)
Definition Operation.h:575
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
Definition Operation.h:559
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:702
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...
Attribute removeAttr(StringAttr name)
Remove the attribute with the specified name if it exists.
Definition Operation.h:625
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:24
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.
static constexpr StringLiteral getDeclareAttrName()
Used to obtain the attribute name for declare.
Definition OpenACC.h:177
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...