MLIR 24.0.0git
ACCToLLVMUtils.h
Go to the documentation of this file.
1//===- ACCToLLVMUtils.h - OpenACC to LLVM helpers ---------------*- 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#ifndef MLIR_CONVERSION_OPENACCTOLLVM_ACCTOLLVMUTILS_H
10#define MLIR_CONVERSION_OPENACCTOLLVM_ACCTOLLVMUTILS_H
11
15#include "mlir/IR/Builders.h"
16#include "mlir/IR/BuiltinOps.h"
17#include "mlir/IR/Location.h"
19#include "mlir/IR/Region.h"
21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/STLForwardCompat.h"
23#include "llvm/ADT/STLFunctionalExtras.h"
24#include "llvm/ADT/StringRef.h"
25
26#include <optional>
27#include <string>
28#include <utility>
29
30namespace mlir {
31namespace acc {
32
33/// Unfuses fused locations, returning the last sub-location.
34Location unfuseLoc(Location loc);
35
36/// Returns file:line:column location information when available.
37std::optional<FileLineColLoc> getFileLineColLoc(Location loc,
38 bool errorOnInvalidLocation);
39
40/// Returns the symbol name of the function \p op belongs to, or of \p op itself
41/// when it is a function.
42StringRef getParentFunctionName(Operation *op);
43
44/// Returns the enclosing function symbol name for \p value's defining op.
45StringRef getParentFunctionName(Value value);
46
47/// Returns the first non-empty enclosing function name from \p values.
48StringRef getParentFunctionName(ValueRange values);
49
50/// Returns the name to give a global that the conversion creates to hold
51/// \p detail of \p kind, such as the name of a variable or a source position.
52/// The dots make a name no identifier of the program can carry, so these
53/// globals are reachable by name without walking the symbols of the module.
54/// \p detail keeps letters, digits, `_`, `$` and `.` and folds every other
55/// character to an underscore. Distinct details can therefore collide on the
56/// same name; getOrCreateGlobalString is what then keeps the globals apart.
57std::string getInternalGlobalName(StringRef kind, StringRef detail);
58
59/// Creates or reuses a null-terminated string global in \p globalSymbolRegion.
60/// With \p symbolTable, a global already going by \p name is reused when it
61/// holds \p value, and a name that two different values arrive under gets a
62/// suffix to tell the globals apart. Without it the global is created under
63/// \p name as given, which only a caller whose names are unique by
64/// construction can ask for.
65Value getOrCreateGlobalString(Location loc, OpBuilder &builder, StringRef name,
66 StringRef value, Region &globalSymbolRegion,
67 SymbolTable *symbolTable = nullptr);
68
69/// Returns a pointer to a constant global holding an ident_t for OpenACC
70/// runtime calls. \p globalSymbolRegion and \p symbolTable are as in
71/// getOrCreateGlobalString; the ident and the source string it points to are
72/// named after the position they describe, so leaving out the table creates a
73/// set of them per call.
74Value createIdent(Location loc, StringRef functionName, OpBuilder &builder,
75 Region &globalSymbolRegion,
76 const ACCRuntimeCallConfig &config,
77 SymbolTable *symbolTable = nullptr);
78
79/// Sign-extends or truncates \p value to the i64 the runtime entry points take
80/// for values like queue numbers.
81Value castToI64(Location loc, Value value, OpBuilder &builder);
82
83/// Creates an i64 constant.
84Value constantI64(Location loc, int64_t value, OpBuilder &builder);
85
86/// Returns the value \p value was converted to, or \p value itself when the
87/// conversion left it as it was. Returns a null value for a null \p value.
88Value remapOrSelf(Value value, ConversionPatternRewriter &rewriter);
89
90/// Returns the queue an `async` clause selects: the value of the clause when it
91/// has one, the queue standing for an `async` clause without a value when
92/// \p asyncOnly is set, and the synchronous queue when there is no clause at
93/// all. \p asyncOperand must already be converted to the LLVM dialect.
94Value getAsyncQueue(Location loc, Value asyncOperand, bool asyncOnly,
95 OpBuilder &builder, const ACCRuntimeCallConfig &config);
96
97/// Emits the runtime call that waits for \p waitOperands on \p asyncQueue,
98/// which is what a `wait` clause or an `acc.wait` directive asks for. An empty
99/// \p waitOperands waits for every queue, as a `wait` clause without values
100/// does. The values must already be converted to the LLVM dialect.
101/// If provided, \p deviceNum must be an i32 value; otherwise the device number
102/// defaults to zero.
103LogicalResult emitWaitCall(Location loc, ValueRange waitOperands,
104 Value asyncQueue, OpBuilder &builder,
105 Region &globalSymbolRegion, SymbolTable &symbolTable,
106 const ACCRuntimeCallConfig &config,
107 Value deviceNum = {});
108
109/// Emits the runtime call that asks for the device address the object of the
110/// data clause \p clauseOp is mapped to, which is what a `use_device` clause
111/// exposes in the body of its construct. \p hostPtr is the address of that
112/// object, already converted to the LLVM dialect. With \p ifPresent, an object
113/// that is not mapped keeps its host address instead of being reported.
114///
115/// Bounds on the clause say which part of the object it names, which the
116/// address asked about does not have to state: the result stands for the
117/// object, so whatever reads it addresses the part it wants as it would on the
118/// host.
119FailureOr<Value> emitGetDevicePtrCall(Operation *clauseOp, Value hostPtr,
120 bool ifPresent, OpBuilder &builder,
121 Region &globalSymbolRegion,
122 SymbolTable &symbolTable,
123 const ACCRuntimeCallConfig &config);
124
125/// Runs \p emitFn guarded by a branch on \p ifCond, or unguarded when there is
126/// no condition. Leaves the insertion point after the guarded code, so that a
127/// caller can keep emitting into the same block either way.
128LogicalResult emitGuardedByIfCond(Location loc, Value ifCond,
129 RewriterBase &rewriter,
130 function_ref<LogicalResult()> emitFn);
131
132/// Emits \p thenFn on the path a branch on \p ifCond takes and \p elseFn on
133/// the other one, and returns the value that reaches the code following the
134/// branch. Both have to produce a value, and of the same type. With no
135/// condition only \p thenFn is emitted and its value returned. As with
136/// emitGuardedByIfCond, the insertion point is left after the branch.
137FailureOr<Value>
138emitValueSelectedByIfCond(Location loc, Value ifCond, RewriterBase &rewriter,
139 function_ref<FailureOr<Value>()> thenFn,
140 function_ref<FailureOr<Value>()> elseFn);
141
142/// Splices \p region, the body of a structured construct, into the block
143/// holding \p op, so that the construct itself can be erased. The code that
144/// follows \p op is branched to where the region ends, whether it ends in an
145/// `acc.terminator` or, as a region holding no terminator does, at the end of
146/// its blocks.
147void spliceConstructRegion(Operation *op, Region &region,
148 RewriterBase &rewriter);
149
150namespace detail {
151/// The constructs carrying a `device_type` clause hold their async and wait
152/// clauses per device type. The others, such as `acc.enter_data` or
153/// `acc.kernel_environment`, hold a single value for each clause, spelling the
154/// value-less form either `asyncOnly`/`waitOnly` or `async`/`wait`.
155template <typename OpTy>
157 decltype(std::declval<OpTy>().hasAsyncOnly(DeviceType::None));
158template <typename OpTy>
159using has_async_only_t = decltype(std::declval<OpTy>().getAsyncOnly());
160template <typename OpTy>
161using has_wait_only_t = decltype(std::declval<OpTy>().getWaitOnly());
162} // namespace detail
163
164/// Returns the value the `async` clause of \p op names for \p deviceType, and
165/// sets \p asyncOnly when the clause names no queue. The value is the one the
166/// operation holds, so a caller in a conversion has to remap it.
167template <typename OpTy>
168Value getAsyncClauseValue(OpTy op, DeviceType deviceType, bool &asyncOnly) {
169 asyncOnly = false;
170 if constexpr (llvm::is_detected<detail::has_device_type_clauses_t,
171 OpTy>::value) {
172 for (DeviceType candidate : getDeviceTypesByPrecedence(deviceType)) {
173 if (op.hasAsyncOnly(candidate)) {
174 asyncOnly = true;
175 return {};
176 }
177 if (Value asyncValue = op.getAsyncValue(candidate))
178 return asyncValue;
179 }
180 return {};
181 } else if constexpr (llvm::is_detected<detail::has_async_only_t,
182 OpTy>::value) {
183 asyncOnly = op.getAsyncOnly();
184 return op.getAsyncOperand();
185 } else {
186 asyncOnly = op.getAsync();
187 return op.getAsyncOperand();
188 }
189}
190
191/// Appends to \p waitValues the queues the `wait` clause of \p op names for
192/// \p deviceType, and returns the device type the clause naming them is given
193/// for - a clause naming no queue waits for every one of them. Returns
194/// std::nullopt when \p op gives no such clause for \p deviceType. The values
195/// are the ones the operation holds, so a caller in a conversion has to remap
196/// them.
197template <typename OpTy>
198std::optional<DeviceType>
199getWaitClauseValues(OpTy op, DeviceType deviceType,
200 SmallVectorImpl<Value> &waitValues) {
201 if constexpr (llvm::is_detected<detail::has_device_type_clauses_t,
202 OpTy>::value) {
203 for (DeviceType candidate : getDeviceTypesByPrecedence(deviceType)) {
204 if (op.hasWaitOnly(candidate))
205 return candidate;
206 auto values = op.getWaitValues(candidate);
207 if (!values.empty()) {
208 llvm::append_range(waitValues, values);
209 return candidate;
210 }
211 }
212 return std::nullopt;
213 } else {
214 llvm::append_range(waitValues, op.getWaitOperands());
215 bool waitsForEveryQueue;
216 if constexpr (llvm::is_detected<detail::has_wait_only_t, OpTy>::value)
217 waitsForEveryQueue = op.getWaitOnly();
218 else
219 waitsForEveryQueue = op.getWait();
220 if (!waitsForEveryQueue && waitValues.empty())
221 return std::nullopt;
222 // The operation holds a single wait clause, which no device_type clause
223 // narrows to a device type.
224 return DeviceType::None;
225 }
226}
227
228/// Returns whether the `wait` clause \p op gives for \p deviceType carries a
229/// devnum modifier, which selects the device the queues belong to. Only the
230/// clause given for that device type is asked, so a caller passes the device
231/// type getWaitClauseValues took the queues from.
232template <typename OpTy>
233bool hasWaitDevnum(OpTy op, DeviceType deviceType) {
234 if constexpr (llvm::is_detected<detail::has_device_type_clauses_t,
235 OpTy>::value) {
236 return static_cast<bool>(op.getWaitDevnum(deviceType));
237 } else {
238 return static_cast<bool>(op.getWaitDevnum());
239 }
240}
241
242/// Returns the queue that the runtime calls of \p op run on, from the `async`
243/// clause it gives for \p deviceType.
244template <typename OpTy>
245Value getAsyncQueue(OpTy op, DeviceType deviceType,
246 ConversionPatternRewriter &rewriter,
247 const ACCRuntimeCallConfig &config) {
248 bool asyncOnly = false;
249 Value asyncValue = getAsyncClauseValue(op, deviceType, asyncOnly);
250 if (asyncValue)
251 asyncValue = rewriter.getRemappedValue(asyncValue);
252 return getAsyncQueue(op.getLoc(), asyncValue, asyncOnly, rewriter, config);
253}
254
255/// Emits the wait that a `wait` clause on \p op asks for before the runtime
256/// calls of the construct, waiting on \p asyncQueue for the queues the clause
257/// names, or for every queue when it names none. Nothing is emitted when there
258/// is no such clause for \p deviceType. A devnum modifier, which selects the
259/// device the queues belong to, is reported through \p accSupport as not yet
260/// implemented.
261template <typename OpTy>
262LogicalResult
263emitWaitClause(OpTy op, DeviceType deviceType, Value asyncQueue,
264 ConversionPatternRewriter &rewriter, OpenACCSupport &accSupport,
265 Region &globalSymbolRegion, SymbolTable &symbolTable,
266 const ACCRuntimeCallConfig &config) {
267 SmallVector<Value> waitValues;
268 std::optional<DeviceType> clauseDeviceType =
269 getWaitClauseValues(op, deviceType, waitValues);
270 if (!clauseDeviceType)
271 return success();
272 if (hasWaitDevnum(op, *clauseDeviceType)) {
273 (void)accSupport.emitNYI(op.getLoc(), "wait clause with a devnum modifier");
274 return failure();
275 }
276 for (Value &waitValue : waitValues)
277 waitValue = rewriter.getRemappedValue(waitValue);
278 return emitWaitCall(op.getLoc(), waitValues, asyncQueue, rewriter,
279 globalSymbolRegion, symbolTable, config);
280}
281
282} // namespace acc
283} // namespace mlir
284
285#endif // MLIR_CONVERSION_OPENACCTOLLVM_ACCTOLLVMUTILS_H
return success()
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
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Configuration for OpenACC to LLVM runtime lowering.
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
decltype(std::declval< OpTy >().getAsyncOnly()) has_async_only_t
decltype(std::declval< OpTy >().hasAsyncOnly(DeviceType::None)) has_device_type_clauses_t
The constructs carrying a device_type clause hold their async and wait clauses per device type.
decltype(std::declval< OpTy >().getWaitOnly()) has_wait_only_t
SmallVector< DeviceType, 3 > getDeviceTypesByPrecedence(DeviceType deviceType)
The clauses of a construct can be given once per device type.
Definition OpenACC.cpp:5138
std::optional< FileLineColLoc > getFileLineColLoc(Location loc, bool errorOnInvalidLocation)
Returns file:line:column location information when available.
std::optional< DeviceType > getWaitClauseValues(OpTy op, DeviceType deviceType, SmallVectorImpl< Value > &waitValues)
Appends to waitValues the queues the wait clause of op names for deviceType, and returns the device t...
LogicalResult emitWaitCall(Location loc, ValueRange waitOperands, Value asyncQueue, OpBuilder &builder, Region &globalSymbolRegion, SymbolTable &symbolTable, const ACCRuntimeCallConfig &config, Value deviceNum={})
Emits the runtime call that waits for waitOperands on asyncQueue, which is what a wait clause or an a...
Value getAsyncClauseValue(OpTy op, DeviceType deviceType, bool &asyncOnly)
Returns the value the async clause of op names for deviceType, and sets asyncOnly when the clause nam...
LogicalResult emitWaitClause(OpTy op, DeviceType deviceType, Value asyncQueue, ConversionPatternRewriter &rewriter, OpenACCSupport &accSupport, Region &globalSymbolRegion, SymbolTable &symbolTable, const ACCRuntimeCallConfig &config)
Emits the wait that a wait clause on op asks for before the runtime calls of the construct,...
Value getAsyncQueue(Location loc, Value asyncOperand, bool asyncOnly, OpBuilder &builder, const ACCRuntimeCallConfig &config)
Returns the queue an async clause selects: the value of the clause when it has one,...
bool hasWaitDevnum(OpTy op, DeviceType deviceType)
Returns whether the wait clause op gives for deviceType carries a devnum modifier,...
Value createIdent(Location loc, StringRef functionName, OpBuilder &builder, Region &globalSymbolRegion, const ACCRuntimeCallConfig &config, SymbolTable *symbolTable=nullptr)
Returns a pointer to a constant global holding an ident_t for OpenACC runtime calls.
Value remapOrSelf(Value value, ConversionPatternRewriter &rewriter)
Returns the value value was converted to, or value itself when the conversion left it as it was.
FailureOr< Value > emitValueSelectedByIfCond(Location loc, Value ifCond, RewriterBase &rewriter, function_ref< FailureOr< Value >()> thenFn, function_ref< FailureOr< Value >()> elseFn)
Emits thenFn on the path a branch on ifCond takes and elseFn on the other one, and returns the value ...
Value constantI64(Location loc, int64_t value, OpBuilder &builder)
Creates an i64 constant.
std::string getInternalGlobalName(StringRef kind, StringRef detail)
Returns the name to give a global that the conversion creates to hold detail of kind,...
StringRef getParentFunctionName(Operation *op)
Returns the symbol name of the function op belongs to, or of op itself when it is a function.
LogicalResult emitGuardedByIfCond(Location loc, Value ifCond, RewriterBase &rewriter, function_ref< LogicalResult()> emitFn)
Runs emitFn guarded by a branch on ifCond, or unguarded when there is no condition.
Value castToI64(Location loc, Value value, OpBuilder &builder)
Sign-extends or truncates value to the i64 the runtime entry points take for values like queue number...
Value getOrCreateGlobalString(Location loc, OpBuilder &builder, StringRef name, StringRef value, Region &globalSymbolRegion, SymbolTable *symbolTable=nullptr)
Creates or reuses a null-terminated string global in globalSymbolRegion.
void spliceConstructRegion(Operation *op, Region &region, RewriterBase &rewriter)
Splices region, the body of a structured construct, into the block holding op, so that the construct ...
Location unfuseLoc(Location loc)
Unfuses fused locations, returning the last sub-location.
FailureOr< Value > emitGetDevicePtrCall(Operation *clauseOp, Value hostPtr, bool ifPresent, OpBuilder &builder, Region &globalSymbolRegion, SymbolTable &symbolTable, const ACCRuntimeCallConfig &config)
Emits the runtime call that asks for the device address the object of the data clause clauseOp is map...
Include the generated interface declarations.
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155