MLIR 24.0.0git
OffloadTargetVerifier.cpp
Go to the documentation of this file.
1//===- OffloadTargetVerifier.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 verifies that values and symbols used within offload regions are
10// legal for the target execution model.
11//
12// Overview:
13// ---------
14// Offload regions execute on a target device (e.g., GPU) where not all values
15// and symbols from the host context are accessible. This pass checks that
16// live-in values (values defined outside but used inside the region) and
17// symbol references are valid for device execution.
18//
19// The pass operates on any operation implementing `OffloadRegionOpInterface`,
20// which includes OpenACC compute constructs (`acc.parallel`, `acc.kernels`,
21// `acc.serial`) as well as GPU operations like `gpu.launch`.
22//
23// Verification:
24// -------------
25// For each offload region, the pass checks:
26//
27// 1. Live-in Values: Values flowing into the region must be valid for device
28// use. This includes checking that data has been properly mapped via
29// OpenACC data clauses (copyin, copyout, present, etc.) or is a scalar
30// that can be passed by value.
31//
32// 2. Symbol References: Symbols referenced inside the region must be
33// accessible on the device. This includes checking for proper `declare`
34// attributes on globals or device-resident data attributes.
35//
36// Requirements:
37// -------------
38// 1. Target Region Identification: Operations representing offload regions
39// must implement `acc::OffloadRegionOpInterface`.
40//
41// 2. OpenACCSupport Analysis: The pass relies on the `OpenACCSupport`
42// analysis to determine value and symbol validity. This analysis provides
43// dialect-specific hooks for checking legality through `isValidValueUse`
44// and `isValidSymbolUse` methods. Custom dialect support can be registered
45// by providing a derived `OpenACCSupport` analysis before running this
46// pass.
47//
48// 3. Device Type: The `device_type` option specifies the target device.
49// For `host` or `multicore` targets, verification of ACC compute
50// constructs is not yet implemented.
51//
52//===----------------------------------------------------------------------===//
53
60#include "mlir/IR/SymbolTable.h"
61#include "llvm/Support/Debug.h"
62
63namespace mlir {
64namespace acc {
65#define GEN_PASS_DEF_OFFLOADTARGETVERIFIER
66#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
67} // namespace acc
68} // namespace mlir
69
70#define DEBUG_TYPE "offload-target-verifier"
71
72using namespace mlir;
73
74namespace {
75class OffloadTargetVerifier
76 : public acc::impl::OffloadTargetVerifierBase<OffloadTargetVerifier> {
77public:
78 using OffloadTargetVerifierBase::OffloadTargetVerifierBase;
79
80 void getDependentDialects(DialectRegistry &registry) const override {
81 OffloadTargetVerifierBase::getDependentDialects(registry);
83 }
84
85 /// Returns true if the target device type corresponds to host execution.
86 bool isHostTarget() const {
87 return deviceType == acc::DeviceType::Host ||
88 deviceType == acc::DeviceType::Multicore;
89 }
90
91 /// Check live-in values for legality.
93 getIllegalLiveInValues(Region &region, Liveness &liveness,
94 acc::OpenACCSupport &accSupport) const {
95 auto isInvalid = [&](Value val) -> bool {
96 return !accSupport.isValidValueUse(val, region);
97 };
98
99 SmallVector<Value> illegalValues(llvm::make_filter_range(
100 liveness.getLiveIn(&region.front()), isInvalid));
101
102 return illegalValues;
103 }
104
105 /// Check symbol uses for legality.
107 getIllegalUsedSymbols(Region &region, acc::OpenACCSupport &accSupport) const {
108 auto symUses = SymbolTable::getSymbolUses(&region);
109
110 // When there are no symbols used in the region, there are no illegal ones.
111 if (!symUses.has_value())
112 return {};
113
114 auto isInvalidSymbol = [&](const SymbolTable::SymbolUse &symUse) -> bool {
115 Operation *definingOp = nullptr;
116 return !accSupport.isValidSymbolUse(symUse.getUser(),
117 symUse.getSymbolRef(), &definingOp);
118 };
119
120 auto invalidSyms =
121 llvm::make_filter_range(symUses.value(), isInvalidSymbol);
122 SmallVector<SymbolTable::SymbolUse> invalidSymsList(invalidSyms);
123 return invalidSymsList;
124 }
125
126 /// Retrieve variable names for the given values.
128 getVariableNames(ArrayRef<Value> values, acc::OpenACCSupport &accSupport) {
130 names.reserve(values.size());
131 for (Value value : values)
132 names.push_back(accSupport.getVariableName(value));
133 return names;
134 }
135
136 /// Check if the region has illegal live-in values.
137 bool hasIllegalLiveInValues(Operation *regionOp,
138 acc::OpenACCSupport &accSupport) const {
139 if (regionOp->getNumRegions() == 0)
140 return false;
141
142 Liveness liveness(regionOp);
143 SmallVector<Value> invalidValues =
144 getIllegalLiveInValues(regionOp->getRegion(0), liveness, accSupport);
145
146 bool hasIllegalValues = !invalidValues.empty();
147
148 if (hasIllegalValues) {
149 SmallVector<std::string> invalidVarNames =
150 getVariableNames(invalidValues, accSupport);
151
152 if (softCheck) {
153 // Emit warnings for each illegal value.
154 auto diag = regionOp->emitWarning("offload target verifier: ")
155 << invalidValues.size() << " illegal live-in value(s)";
156 for (auto [invalidValue, name] :
157 llvm::zip(invalidValues, invalidVarNames)) {
158 if (name.empty()) {
159 diag.attachNote(invalidValue.getLoc()) << "value: " << invalidValue;
160 } else {
161 diag.attachNote(invalidValue.getLoc())
162 << "value: " << invalidValue << ", name: " << name;
163 }
164 }
165 } else {
166 std::string message = "offload target verifier failed due to " +
167 std::to_string(invalidValues.size()) +
168 " illegal live-in value(s)";
169 SmallVector<std::string> availableVarNames;
170 for (const std::string &name : invalidVarNames)
171 if (!name.empty())
172 availableVarNames.push_back(name);
173 if (!availableVarNames.empty())
174 message += " including: " + llvm::join(availableVarNames, ", ");
175 accSupport.emitNYI(regionOp->getLoc(), message);
176 }
177 }
178
179 return hasIllegalValues;
180 }
181
182 /// Check if the region has illegal symbol uses.
183 bool hasIllegalSymbolUses(Operation *regionOp,
184 acc::OpenACCSupport &accSupport) const {
185 if (regionOp->getNumRegions() == 0)
186 return false;
187
189 getIllegalUsedSymbols(regionOp->getRegion(0), accSupport);
190
191 bool hasIllegalSymbols = !invalidSyms.empty();
192
193 if (hasIllegalSymbols) {
194 auto getSymName = [&](SymbolTable::SymbolUse symUse) -> std::string {
195 return symUse.getSymbolRef().getLeafReference().str();
196 };
197 std::string invalidString =
198 llvm::join(llvm::map_range(invalidSyms, getSymName), ", ");
199
200 // Emit only warnings when softCheck is enabled.
201 if (softCheck)
202 regionOp->emitWarning("offload target verifier: illegal symbol(s): ")
203 << invalidString;
204 else
205 accSupport.emitNYI(regionOp->getLoc(),
206 "offload target verifier failed due to illegal "
207 "symbol(s): " +
208 invalidString);
209 }
210
211 return hasIllegalSymbols;
212 }
213
214 void runOnOperation() override {
215 LLVM_DEBUG(llvm::dbgs() << "Enter OffloadTargetVerifier()\n");
216 func::FuncOp func = getOperation();
217
218 // Try to get cached parent analysis first, fall back to local analysis.
219 auto cachedAnalysis =
220 getCachedParentAnalysis<acc::OpenACCSupport>(func->getParentOp());
221 acc::OpenACCSupport &accSupport = cachedAnalysis
222 ? cachedAnalysis->get()
223 : getAnalysis<acc::OpenACCSupport>();
224
225 bool hasErrors = false;
226
227 func.walk([&](Operation *op) {
228 // Only process offload region operations.
229 if (!isa<acc::OffloadRegionOpInterface>(op))
230 return WalkResult::advance();
231
232 // TODO: Host/multicore verification for ACC compute constructs is not yet
233 // implemented.
234 if (isHostTarget() && isa<ACC_COMPUTE_CONSTRUCT_OPS>(op)) {
235 accSupport.emitNYI(op->getLoc(),
236 "host/multicore verification for ACC compute "
237 "constructs");
238 return WalkResult::advance();
239 }
240
241 // Check for illegal live-in values.
242 bool hasIllegalValues = hasIllegalLiveInValues(op, accSupport);
243 if (hasIllegalValues)
244 hasErrors = true;
245
246 // Check for illegal symbol uses.
247 bool hasIllegalSyms = hasIllegalSymbolUses(op, accSupport);
248 if (hasIllegalSyms)
249 hasErrors = true;
250
251 if (!hasIllegalValues && !hasIllegalSyms && softCheck)
252 op->emitRemark("offload target verifier: passed validity check");
253
254 return WalkResult::advance();
255 });
256
257 if (hasErrors && !softCheck)
258 signalPassFailure();
259
260 LLVM_DEBUG(llvm::dbgs() << "Exit OffloadTargetVerifier()\n");
261 }
262};
263} // namespace
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
Represents an analysis for computing liveness information from a given top-level operation.
Definition Liveness.h:47
const ValueSetT & getLiveIn(Block *block) const
Returns a reference to a set containing live-in values (unordered).
Definition Liveness.cpp:231
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:738
InFlightDiagnostic emitWarning(const Twine &message={})
Emit a warning about this operation, reporting up to any diagnostic handlers that may be listening.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:726
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
InFlightDiagnostic emitRemark(const Twine &message={})
Emit a remark about this operation, reporting up to any diagnostic handlers that may be listening.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
This class represents a specific symbol use.
static std::optional< UseRange > getSymbolUses(Operation *from)
Get an iterator range for all of the uses, for any symbol, that are nested within the given operation...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
static WalkResult advance()
Definition WalkResult.h:47
std::string getVariableName(Value v, VariableNameConfig config={})
Get the variable name for a given value.
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
bool isValidValueUse(Value v, Region &region)
Check if a value use is legal in an OpenACC region.
bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol, Operation **definingOpPtr=nullptr)
Check if a symbol use is valid for use in an OpenACC region.
void registerAllExtensions(DialectRegistry &registry)
Register the standard OpenACC models for GPU, LLVM and MemRef IR.
Include the generated interface declarations.