MLIR 24.0.0git
OpenACCSupport.h
Go to the documentation of this file.
1//===- OpenACCSupport.h - OpenACC Support Interface -------------*- 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 defines the OpenACCSupport analysis interface, which provides
10// extensible support for OpenACC passes. Custom implementations
11// can be registered to provide pipeline and dialect-specific information
12// that cannot be adequately expressed through type or operation interfaces
13// alone.
14//
15// Usage Pattern:
16// ==============
17//
18// A pass that needs this functionality should call
19// getAnalysis<OpenACCSupport>(), which will provide either:
20// - A cached version if previously initialized, OR
21// - A default implementation if not previously initialized
22//
23// This analysis is never invalidated (isInvalidated returns false), so it only
24// needs to be initialized once and will persist throughout the pass pipeline.
25//
26// Registering a Custom Implementation:
27// =====================================
28//
29// If a custom implementation is needed, create a pass that runs BEFORE the pass
30// that needs the analysis. In this setup pass, use
31// getAnalysis<OpenACCSupport>() followed by setImplementation() to register
32// your custom implementation. The custom implementation will need to provide
33// implementation for all methods defined in the `OpenACCSupportTraits::Concept`
34// class.
35//
36// Example:
37// void MySetupPass::runOnOperation() {
38// OpenACCSupport &support = getAnalysis<OpenACCSupport>();
39// support.setImplementation(MyCustomImpl());
40// }
41//
42// void MyAnalysisConsumerPass::runOnOperation() {
43// OpenACCSupport &support = getAnalysis<OpenACCSupport>();
44// std::string name = support.getVariableName(someValue);
45// // ... use the analysis results
46// }
47//
48//===----------------------------------------------------------------------===//
49
50#ifndef MLIR_DIALECT_OPENACC_ANALYSIS_OPENACCSUPPORT_H
51#define MLIR_DIALECT_OPENACC_ANALYSIS_OPENACCSUPPORT_H
52
57#include "mlir/IR/BuiltinOps.h"
58#include "mlir/IR/Remarks.h"
59#include "mlir/IR/Value.h"
61#include "llvm/ADT/StringRef.h"
62#include <functional>
63#include <memory>
64#include <optional>
65#include <string>
66
67namespace mlir {
68namespace acc {
69
70/// How the name of a variable is to be rendered.
72 /// Render the name the source language spells, which is the name a message
73 /// to the user states. When false, the name the variable is emitted under is
74 /// rendered instead - for a global the symbol it is addressed through -
75 /// which is the name it is resolved against the symbols of a binary by. Only
76 /// a language that uniques or mangles the names of its variables spells the
77 /// two differently.
79};
80
81namespace detail {
82/// This class contains internal trait classes used by OpenACCSupport.
83/// It follows the Concept-Model pattern used throughout MLIR (e.g., in
84/// AliasAnalysis and interface definitions).
86 class Concept {
87 public:
88 virtual ~Concept() = default;
89
90 /// Get the variable name for a given MLIR value.
91 virtual std::string getVariableName(Value v, VariableNameConfig config) = 0;
92
93 /// Get the recipe name for a given kind, type and value.
94 virtual std::string getRecipeName(RecipeKind kind, Type type,
95 Value var) = 0;
96
97 // Used to report a case that is not supported by the implementation.
98 virtual InFlightDiagnostic emitNYI(Location loc, const Twine &message) = 0;
99
100 // Used to emit an OpenACC remark. The category is optional and is used to
101 // either capture the pass name or pipeline phase when the remark is
102 // emitted. When not provided, in the default implementation, the category
103 // is "openacc".
105 emitRemark(Operation *op, std::function<std::string()> messageFn,
106 llvm::StringRef category) = 0;
107
108 /// Check if a symbol use is valid for use in an OpenACC region.
109 virtual bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol,
110 Operation **definingOpPtr) = 0;
111
112 /// Check if a value use is legal in an OpenACC region.
113 virtual bool isValidValueUse(Value v, mlir::Region &region,
114 SymbolTableCollection *symbolTables) = 0;
115
116 /// Get or optionally create a GPU module in the given module.
117 virtual std::optional<gpu::GPUModuleOp>
118 getOrCreateGPUModule(ModuleOp mod, bool create, llvm::StringRef name) = 0;
119
120 /// Returns the size and ABI alignment in bytes for \p ty.
121 virtual std::optional<TypeSizeAndAlignment>
122 getTypeSizeAndAlignment(Type ty, ModuleOp module,
123 OpenACCSupport &support) = 0;
124 };
125
126 /// SFINAE helpers to detect if implementation has optional methods
127 template <typename ImplT, typename... Args>
129 decltype(std::declval<ImplT>().isValidSymbolUse(std::declval<Args>()...));
130
131 template <typename ImplT>
133 llvm::is_detected<isValidSymbolUse_t, ImplT, Operation *, SymbolRefAttr,
134 Operation **>;
135
136 template <typename ImplT, typename... Args>
137
139 decltype(std::declval<ImplT>().isValidValueUse(std::declval<Args>()...));
140
141 /// An implementation may take the symbol table cache or, for compatibility,
142 /// only the value and the region.
143 template <typename ImplT>
145 llvm::is_detected<isValidValueUse_t, ImplT, Value, Region &,
147
148 template <typename ImplT>
150 llvm::is_detected<isValidValueUse_t, ImplT, Value, Region &>;
151
152 template <typename ImplT, typename... Args>
154 decltype(std::declval<ImplT>().emitRemark(std::declval<Args>()...));
155
156 template <typename ImplT>
158 llvm::is_detected<emitRemark_t, ImplT, Operation *,
159 std::function<std::string()>, llvm::StringRef>;
160
161 template <typename ImplT, typename... Args>
163 decltype(std::declval<ImplT>().getOrCreateGPUModule(
164 std::declval<Args>()...));
165
166 template <typename ImplT>
168 llvm::is_detected<getOrCreateGPUModule_t, ImplT, ModuleOp, bool,
169 llvm::StringRef>;
170
171 template <typename ImplT, typename... Args>
173 decltype(std::declval<ImplT>().getTypeSizeAndAlignment(
174 std::declval<Args>()...));
175
176 template <typename ImplT>
178 llvm::is_detected<getTypeSizeAndAlignment_t, ImplT, Type, ModuleOp,
180
181 /// This class wraps a concrete OpenACCSupport implementation and forwards
182 /// interface calls to it. This provides type erasure, allowing different
183 /// implementation types to be used interchangeably without inheritance.
184 /// Methods can be optionally implemented; if not present, default behavior
185 /// is used.
186 template <typename ImplT>
187 class Model final : public Concept {
188 public:
189 explicit Model(ImplT &&impl) : impl(std::forward<ImplT>(impl)) {}
190 ~Model() override = default;
191
192 std::string getVariableName(Value v, VariableNameConfig config) final {
193 return impl.getVariableName(v, config);
194 }
195
196 std::string getRecipeName(RecipeKind kind, Type type, Value var) final {
197 return impl.getRecipeName(kind, type, var);
198 }
199
200 InFlightDiagnostic emitNYI(Location loc, const Twine &message) final {
201 return impl.emitNYI(loc, message);
202 }
203
205 emitRemark(Operation *op, std::function<std::string()> messageFn,
206 llvm::StringRef category) final {
207 if constexpr (has_emitRemark<ImplT>::value)
208 return impl.emitRemark(op, std::move(messageFn), category);
209 else
210 return acc::emitRemark(op, messageFn(), category);
211 }
212
213 bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol,
214 Operation **definingOpPtr) final {
216 return impl.isValidSymbolUse(user, symbol, definingOpPtr);
217 else
218 return acc::isValidSymbolUse(user, symbol, definingOpPtr);
219 }
220
221 bool isValidValueUse(Value v, Region &region,
222 SymbolTableCollection *symbolTables) final {
224 return impl.isValidValueUse(v, region, symbolTables);
226 return impl.isValidValueUse(v, region);
227 else
228 return acc::isValidValueUse(v, region, symbolTables);
229 }
230
231 std::optional<gpu::GPUModuleOp>
232 getOrCreateGPUModule(ModuleOp mod, bool create,
233 llvm::StringRef name) final {
235 return impl.getOrCreateGPUModule(mod, create, name);
236 else
237 return acc::getOrCreateGPUModule(mod, create, name);
238 }
239
240 std::optional<TypeSizeAndAlignment>
241 getTypeSizeAndAlignment(Type ty, ModuleOp module,
242 OpenACCSupport &support) final {
244 return impl.getTypeSizeAndAlignment(ty, module, support);
245 else
246 return acc::getTypeSizeAndAlignment(ty, module, &support);
247 }
248
249 private:
250 ImplT impl;
251 };
252};
253} // namespace detail
254
255//===----------------------------------------------------------------------===//
256// OpenACCSupport
257//===----------------------------------------------------------------------===//
258
261 template <typename ImplT>
263
264public:
265 OpenACCSupport() = default;
267
268 /// Register a custom OpenACCSupport implementation. Only one implementation
269 /// can be registered at a time; calling this replaces any existing
270 /// implementation.
271 template <typename AnalysisT>
272 void setImplementation(AnalysisT &&analysis) {
273 impl =
274 std::make_unique<Model<AnalysisT>>(std::forward<AnalysisT>(analysis));
275 }
276
277 /// Get the variable name for a given value. Which of the names a variable
278 /// goes by is a question only an implementation that knows the source
279 /// language can answer, so the default implementation returns the one name
280 /// the IR states regardless of \p config.
281 ///
282 /// \param v The MLIR value to get the variable name for.
283 /// \param config Which of the names of the variable to return.
284 /// \return The variable name, or an empty string if unavailable.
285 std::string getVariableName(Value v, VariableNameConfig config = {});
286
287 /// Get the recipe name for a given type and value.
288 ///
289 /// \param kind The kind of recipe to get the name for.
290 /// \param type The type to get the recipe name for. Can be null if the
291 /// var is provided instead.
292 /// \param var The MLIR value to get the recipe name for. Can be null if
293 /// the type is provided instead.
294 /// \return The recipe name, or an empty string if not available.
295 std::string getRecipeName(RecipeKind kind, Type type, Value var);
296
297 /// Report a case that is not yet supported by the implementation.
298 ///
299 /// \param loc The location to report the unsupported case at.
300 /// \param message The message to report.
301 /// \return An in-flight diagnostic object that can be used to report the
302 /// unsupported case.
303 InFlightDiagnostic emitNYI(Location loc, const Twine &message) {
304 if (impl)
305 return impl->emitNYI(loc, message);
306 return mlir::emitError(loc, "not yet implemented: " + message);
307 }
308
309 /// Emit an OpenACC remark with lazy message generation.
310 ///
311 /// The messageFn is only invoked if remarks are enabled for the given
312 /// operation, allowing callers to avoid constructing expensive messages
313 /// when remarks are disabled.
314 ///
315 /// \param op The operation to emit the remark for.
316 /// \param messageFn A callable that returns the remark message.
317 /// \param category Optional category for the remark. Defaults to "openacc".
318 /// \return An in-flight remark object that can be used to append
319 /// additional information to the remark.
321 emitRemark(Operation *op, std::function<std::string()> messageFn,
322 llvm::StringRef category = "openacc");
323
324 /// Emit an OpenACC remark.
325 ///
326 /// \param op The operation to emit the remark for.
327 /// \param message The remark message.
328 /// \param category Optional category for the remark. Defaults to "openacc".
329 /// \return An in-flight remark object that can be used to append
330 /// additional information to the remark.
332 emitRemark(Operation *op, const Twine &message,
333 llvm::StringRef category = "openacc") {
334 return emitRemark(op, std::function<std::string()>([msg = message.str()]() {
335 return msg;
336 }),
337 category);
338 }
339
340 /// Check if a symbol use is valid for use in an OpenACC region.
341 ///
342 /// \param user The operation using the symbol.
343 /// \param symbol The symbol reference being used.
344 /// \param definingOpPtr Optional output parameter to receive the defining op.
345 /// \return true if the symbol use is valid, false otherwise.
346 bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol,
347 Operation **definingOpPtr = nullptr);
348
349 /// Check if a value use is legal in an OpenACC region.
350 ///
351 /// \param v The MLIR value to check for legality.
352 /// \param region The MLIR region in which the legality is checked.
353 /// \param symbolTables Optional cache of symbol tables used to resolve
354 /// symbols. Pass one when checking many values of the same module:
355 /// without it each symbol lookup scans the whole module.
356 bool isValidValueUse(Value v, Region &region,
357 SymbolTableCollection *symbolTables = nullptr);
358
359 /// Get or optionally create a GPU module in the given module.
360 ///
361 /// \param mod The module to search or create the GPU module in.
362 /// \param create If true (default), create the GPU module if it doesn't
363 /// exist.
364 /// \param name The name for the GPU module. If empty, implementation uses its
365 /// default name.
366 /// \return The GPU module if found or created, std::nullopt otherwise.
367 std::optional<gpu::GPUModuleOp>
368 getOrCreateGPUModule(ModuleOp mod, bool create = true,
369 llvm::StringRef name = "");
370
371 /// Returns the size and ABI alignment in bytes for \p ty.
372 std::optional<TypeSizeAndAlignment> getTypeSizeAndAlignment(Type ty,
373 ModuleOp module) {
374 if (impl)
375 if (auto result = impl->getTypeSizeAndAlignment(ty, module, *this))
376 return result;
377 return acc::getTypeSizeAndAlignment(ty, module, this);
378 }
379
380 /// Signal that this analysis should always be preserved so that
381 /// underlying implementation registration is not lost.
383 return false;
384 }
385
386private:
387 /// The registered custom implementation (if any).
388 std::unique_ptr<Concept> impl;
389};
390
391} // namespace acc
392} // namespace mlir
393
394#endif // MLIR_DIALECT_OPENACC_ANALYSIS_OPENACCSUPPORT_H
detail::PreservedAnalyses PreservedAnalyses
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
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 represents a collection of SymbolTables.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
remark::detail::InFlightRemark emitRemark(Operation *op, std::function< std::string()> messageFn, llvm::StringRef category="openacc")
Emit an OpenACC remark with lazy message generation.
std::string getVariableName(Value v, VariableNameConfig config={})
Get the variable name for a given value.
remark::detail::InFlightRemark emitRemark(Operation *op, const Twine &message, llvm::StringRef category="openacc")
Emit an OpenACC remark.
void setImplementation(AnalysisT &&analysis)
Register a custom OpenACCSupport implementation.
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol, Operation **definingOpPtr=nullptr)
Check if a symbol use is valid for use in an OpenACC region.
bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa)
Signal that this analysis should always be preserved so that underlying implementation registration i...
std::optional< gpu::GPUModuleOp > getOrCreateGPUModule(ModuleOp mod, bool create=true, llvm::StringRef name="")
Get or optionally create a GPU module in the given module.
std::optional< TypeSizeAndAlignment > getTypeSizeAndAlignment(Type ty, ModuleOp module)
Returns the size and ABI alignment in bytes for ty.
std::string getRecipeName(RecipeKind kind, Type type, Value var)
Get the recipe name for a given type and value.
bool isValidValueUse(Value v, Region &region, SymbolTableCollection *symbolTables=nullptr)
Check if a value use is legal in an OpenACC region.
virtual std::string getRecipeName(RecipeKind kind, Type type, Value var)=0
Get the recipe name for a given kind, type and value.
virtual std::optional< TypeSizeAndAlignment > getTypeSizeAndAlignment(Type ty, ModuleOp module, OpenACCSupport &support)=0
Returns the size and ABI alignment in bytes for ty.
virtual std::string getVariableName(Value v, VariableNameConfig config)=0
Get the variable name for a given MLIR value.
virtual bool isValidValueUse(Value v, mlir::Region &region, SymbolTableCollection *symbolTables)=0
Check if a value use is legal in an OpenACC region.
virtual InFlightDiagnostic emitNYI(Location loc, const Twine &message)=0
virtual bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol, Operation **definingOpPtr)=0
Check if a symbol use is valid for use in an OpenACC region.
virtual remark::detail::InFlightRemark emitRemark(Operation *op, std::function< std::string()> messageFn, llvm::StringRef category)=0
virtual std::optional< gpu::GPUModuleOp > getOrCreateGPUModule(ModuleOp mod, bool create, llvm::StringRef name)=0
Get or optionally create a GPU module in the given module.
This class wraps a concrete OpenACCSupport implementation and forwards interface calls to it.
bool isValidValueUse(Value v, Region &region, SymbolTableCollection *symbolTables) final
Check if a value use is legal in an OpenACC region.
std::string getVariableName(Value v, VariableNameConfig config) final
Get the variable name for a given MLIR value.
remark::detail::InFlightRemark emitRemark(Operation *op, std::function< std::string()> messageFn, llvm::StringRef category) final
std::optional< gpu::GPUModuleOp > getOrCreateGPUModule(ModuleOp mod, bool create, llvm::StringRef name) final
Get or optionally create a GPU module in the given module.
InFlightDiagnostic emitNYI(Location loc, const Twine &message) final
std::string getRecipeName(RecipeKind kind, Type type, Value var) final
Get the recipe name for a given kind, type and value.
bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol, Operation **definingOpPtr) final
Check if a symbol use is valid for use in an OpenACC region.
std::optional< TypeSizeAndAlignment > getTypeSizeAndAlignment(Type ty, ModuleOp module, OpenACCSupport &support) final
Returns the size and ABI alignment in bytes for ty.
InFlightRemark is a RAII class that holds a reference to a Remark instance and allows to build the re...
Definition Remarks.h:488
bool isValidSymbolUse(mlir::Operation *user, mlir::SymbolRefAttr symbol, mlir::Operation **definingOpPtr=nullptr)
Check if a symbol use is valid for use in an OpenACC region.
std::optional< gpu::GPUModuleOp > getOrCreateGPUModule(ModuleOp mod, bool create=true, llvm::StringRef name=kDefaultGPUModuleName)
Get or create a GPU module in the given module.
bool isValidValueUse(mlir::Value val, mlir::Region &region, mlir::SymbolTableCollection *symbolTables=nullptr)
Check if a value use is valid in an OpenACC region.
remark::detail::InFlightRemark emitRemark(mlir::Operation *op, const std::function< std::string()> &messageFn, llvm::StringRef category="openacc")
Emit an OpenACC remark with lazy message generation.
std::optional< TypeSizeAndAlignment > getTypeSizeAndAlignment(Type ty, ModuleOp module, const DataLayout &dl, OpenACCSupport *support=nullptr, Value var={})
Returns the size and ABI alignment in bytes.
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
How the name of a variable is to be rendered.
bool preferDemangledName
Render the name the source language spells, which is the name a message to the user states.
This class contains internal trait classes used by OpenACCSupport.
llvm::is_detected< getOrCreateGPUModule_t, ImplT, ModuleOp, bool, llvm::StringRef > has_getOrCreateGPUModule
llvm::is_detected< isValidValueUse_t, ImplT, Value, Region & > has_isValidValueUseNoSymbolTables
decltype(std::declval< ImplT >().getTypeSizeAndAlignment( std::declval< Args >()...)) getTypeSizeAndAlignment_t
llvm::is_detected< emitRemark_t, ImplT, Operation *, std::function< std::string()>, llvm::StringRef > has_emitRemark
decltype(std::declval< ImplT >().isValidValueUse(std::declval< Args >()...)) isValidValueUse_t
llvm::is_detected< isValidValueUse_t, ImplT, Value, Region &, SymbolTableCollection * > has_isValidValueUse
An implementation may take the symbol table cache or, for compatibility, only the value and the regio...
llvm::is_detected< isValidSymbolUse_t, ImplT, Operation *, SymbolRefAttr, Operation ** > has_isValidSymbolUse
decltype(std::declval< ImplT >().emitRemark(std::declval< Args >()...)) emitRemark_t
decltype(std::declval< ImplT >().getOrCreateGPUModule( std::declval< Args >()...)) getOrCreateGPUModule_t
decltype(std::declval< ImplT >().isValidSymbolUse(std::declval< Args >()...)) isValidSymbolUse_t
SFINAE helpers to detect if implementation has optional methods.
llvm::is_detected< getTypeSizeAndAlignment_t, ImplT, Type, ModuleOp, OpenACCSupport & > has_getTypeSizeAndAlignment