MLIR 24.0.0git
ACCDataDirectivePatterns.cpp
Go to the documentation of this file.
1//===- ACCDataDirectivePatterns.cpp - ACC data to LLVM ----------*- 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// Lowering of the OpenACC data constructs - data, enter data, exit data and
10// update - to the data entry points of the OpenACC runtime. Each construct
11// becomes one call per point where it establishes or tears down its mappings,
12// which carries the argument arrays its operands are packed into, the queue
13// its async clause selects and, before them, the wait its wait clause asks
14// for. An if clause guards the calls it applies to, and the body of a
15// structured construct is left in place of the construct.
16//
17//===----------------------------------------------------------------------===//
18
22
27
28using namespace mlir;
29using namespace mlir::acc;
30
31/// Emits the call to the mapping entry point \p fn for \p mappingOperands, the
32/// data-clause operands of a data construct, paired with their type converted
33/// values in \p convertedOperands.
34static LogicalResult emitDataRuntimeCall(
35 Location loc, RuntimeFunction fn, ValueRange mappingOperands,
36 ValueRange convertedOperands, Value asyncQueue, ACCDataCallKind callKind,
37 ConversionPatternRewriter &rewriter, OpenACCSupport &accSupport,
38 Region &globalSymbolRegion, SymbolTable &symbolTable,
39 const ACCRuntimeCallConfig &config) {
40 if (mappingOperands.empty())
41 return success();
42
43 Operation *firstMapOp = mappingOperands.front().getDefiningOp();
44 ACCDataRuntimeArgs runtimeArgs;
45 if (failed(emitACCDataRuntimeArgs(
46 loc, mappingOperands, convertedOperands, rewriter, globalSymbolRegion,
47 accSupport, config, runtimeArgs, callKind, &symbolTable)))
48 return rewriter.notifyMatchFailure(
49 firstMapOp, "unsupported OpenACC data-clause operand");
50
51 SmallVector<Value> arguments = runtimeArgs.getCallArgs();
52 arguments.push_back(asyncQueue);
53 return createRuntimeCall(loc, rewriter, globalSymbolRegion, symbolTable, fn,
54 config, arguments);
55}
56
57namespace {
58template <typename OpTy>
59struct ACCDataDirectivePattern : public ConvertOpToLLVMPattern<OpTy> {
60 ACCDataDirectivePattern(const LLVMTypeConverter &converter,
61 OpenACCSupport &accSupport,
62 Region &globalSymbolRegion, SymbolTable &symbolTable,
63 const ACCRuntimeCallConfig &config,
64 DeviceType clauseDeviceType,
65 PatternBenefit benefit = 1)
66 : ConvertOpToLLVMPattern<OpTy>(converter, benefit),
67 accSupport(accSupport), globalSymbolRegion(globalSymbolRegion),
68 symbolTable(symbolTable), config(config),
69 clauseDeviceType(clauseDeviceType) {}
70
71 /// Returns the queue that the mapping calls of \p op run on.
72 Value getAsyncQueue(OpTy op, ConversionPatternRewriter &rewriter) const {
73 return acc::getAsyncQueue(op, clauseDeviceType, rewriter, config);
74 }
75
76 /// Emits the wait that a wait clause on \p op asks for before its mapping
77 /// calls.
78 LogicalResult emitWaitClause(OpTy op, Value asyncQueue,
79 ConversionPatternRewriter &rewriter) const {
80 return acc::emitWaitClause(op, clauseDeviceType, asyncQueue, rewriter,
81 accSupport, globalSymbolRegion, symbolTable,
82 config);
83 }
84
85 /// Emits one mapping call for the data-clause operands of \p op.
86 LogicalResult emitMappingCall(OpTy op, ValueRange convertedOperands,
87 Location loc, RuntimeFunction fn,
88 ACCDataCallKind callKind, Value asyncQueue,
89 ConversionPatternRewriter &rewriter) const {
90 return emitDataRuntimeCall(loc, fn, op.getDataClauseOperands(),
91 convertedOperands, asyncQueue, callKind,
92 rewriter, accSupport, globalSymbolRegion,
93 symbolTable, config);
94 }
95
96 OpenACCSupport &accSupport;
97 Region &globalSymbolRegion;
98 SymbolTable &symbolTable;
99 ACCRuntimeCallConfig config;
100 DeviceType clauseDeviceType;
101};
102
103struct DataOpLowering : public ACCDataDirectivePattern<DataOp> {
104 using ACCDataDirectivePattern<DataOp>::ACCDataDirectivePattern;
105
106 LogicalResult
107 matchAndRewrite(DataOp op, DataOp::Adaptor adaptor,
108 ConversionPatternRewriter &rewriter) const override {
109 // The mappings are established where the clause operations are, and torn
110 // down where the exit operations of the construct were.
111 ValueRange mappingOperands = op.getDataClauseOperands();
112 Location beginLoc = mappingOperands.empty()
113 ? op.getLoc()
114 : mappingOperands.front().getLoc();
115 Location endLoc =
116 acc::getMappingExitLoc(mappingOperands).value_or(beginLoc);
117 Value asyncQueue = getAsyncQueue(op, rewriter);
118 if (failed(emitWaitClause(op, asyncQueue, rewriter)))
119 return failure();
120
121 auto emitBegin = [&]() {
122 return emitMappingCall(op, adaptor.getDataClauseOperands(), beginLoc,
123 RuntimeFunction::ACCRTL_tgt_acc_data_begin,
124 ACCDataCallKind::DataEnter, asyncQueue, rewriter);
125 };
126 if (failed(acc::emitGuardedByIfCond(beginLoc, adaptor.getIfCond(), rewriter,
127 emitBegin)))
128 return failure();
129
130 rewriter.setInsertionPointAfter(op);
131 auto emitEnd = [&]() {
132 return emitMappingCall(op, adaptor.getDataClauseOperands(), endLoc,
133 RuntimeFunction::ACCRTL_tgt_acc_data_end,
134 ACCDataCallKind::DataExit, asyncQueue, rewriter);
135 };
136 if (failed(acc::emitGuardedByIfCond(endLoc, adaptor.getIfCond(), rewriter,
137 emitEnd)))
138 return failure();
139
140 acc::spliceConstructRegion(op, op.getRegion(), rewriter);
141 rewriter.eraseOp(op);
142 return success();
143 }
144};
145
146/// Lowering shared by the directives that map their operands with a single
147/// call: enter data, exit data and update.
148template <typename OpTy, RuntimeFunction fn, ACCDataCallKind callKind>
149struct ACCDataCallLowering : public ACCDataDirectivePattern<OpTy> {
150 using ACCDataDirectivePattern<OpTy>::ACCDataDirectivePattern;
151
152 LogicalResult
153 matchAndRewrite(OpTy op, typename OpTy::Adaptor adaptor,
154 ConversionPatternRewriter &rewriter) const override {
155 Location loc = op.getLoc();
156 Value asyncQueue = this->getAsyncQueue(op, rewriter);
157 if (failed(this->emitWaitClause(op, asyncQueue, rewriter)))
158 return failure();
159
160 auto emit = [&]() {
161 return this->emitMappingCall(op, adaptor.getDataClauseOperands(), loc, fn,
162 callKind, asyncQueue, rewriter);
163 };
164 if (failed(
165 acc::emitGuardedByIfCond(loc, adaptor.getIfCond(), rewriter, emit)))
166 return failure();
167 rewriter.eraseOp(op);
168 return success();
169 }
170};
171
172using EnterDataOpLowering =
173 ACCDataCallLowering<EnterDataOp, RuntimeFunction::ACCRTL_tgt_acc_data_enter,
175using ExitDataOpLowering =
176 ACCDataCallLowering<ExitDataOp, RuntimeFunction::ACCRTL_tgt_acc_data_exit,
178using UpdateOpLowering =
179 ACCDataCallLowering<UpdateOp, RuntimeFunction::ACCRTL_tgt_acc_data_update,
181
182/// A data clause operation only describes a mapping; the runtime calls of the
183/// construct carry everything it said, so the operation itself goes away once
184/// they are in place. Whatever else referred to the mapped object is given the
185/// address of that object, which is what the clause result stood for.
186template <typename OpTy>
187struct DataEntryOpLowering : public ConvertOpToLLVMPattern<OpTy> {
188 using ConvertOpToLLVMPattern<OpTy>::ConvertOpToLLVMPattern;
189 using OpAdaptor = typename OpTy::Adaptor;
190
191 LogicalResult
192 matchAndRewrite(OpTy op, OpAdaptor adaptor,
193 ConversionPatternRewriter &rewriter) const override {
194 rewriter.replaceOp(op, adaptor.getVar());
195 return success();
196 }
197};
198
199/// An `acc.getdeviceptr` clause stands for the device address of an object
200/// whose mapping was established elsewhere. Where it names the object of a
201/// data exit operation, the runtime call of that construct carries the host
202/// address and looks the device one up itself, so the clause is replaced by
203/// the address of the object as any other data entry clause is. Read as a
204/// value, it is the runtime that has to be asked for the address.
205struct GetDevicePtrOpLowering
206 : public ConvertOpToLLVMPattern<acc::GetDevicePtrOp> {
207 GetDevicePtrOpLowering(const LLVMTypeConverter &converter,
208 OpenACCSupport &accSupport, Region &globalSymbolRegion,
209 SymbolTable &symbolTable,
210 const ACCRuntimeCallConfig &config,
211 PatternBenefit benefit = 1)
212 : ConvertOpToLLVMPattern<acc::GetDevicePtrOp>(converter, benefit),
213 accSupport(accSupport), globalSymbolRegion(globalSymbolRegion),
214 symbolTable(symbolTable), config(config) {}
215
216 /// Returns whether \p user only states the mapping the clause describes,
217 /// which is what a data exit operation and the construct holding the clause
218 /// do.
219 static bool isMappingUse(Operation *user) {
221 acc::KernelEnvironmentOp>(user);
222 }
223
224 LogicalResult
225 matchAndRewrite(acc::GetDevicePtrOp op, OpAdaptor adaptor,
226 ConversionPatternRewriter &rewriter) const override {
227 bool readAsValue = llvm::any_of(
228 op->getUsers(), [](Operation *user) { return !isMappingUse(user); });
229 if (!readAsValue) {
230 rewriter.replaceOp(op, adaptor.getVar());
231 return success();
232 }
233 // The mapping hands the host address of the object to the runtime call of
234 // the construct, so the clause cannot stand for the device address at the
235 // same time.
236 if (llvm::any_of(op->getUsers(), isMappingUse)) {
237 (void)accSupport.emitNYI(
238 op.getLoc(), "device address read from a clause that also states a "
239 "mapping of the object");
240 return failure();
241 }
242
243 FailureOr<Value> devicePtr = acc::emitGetDevicePtrCall(
244 op, adaptor.getVar(), /*ifPresent=*/false, rewriter, globalSymbolRegion,
245 symbolTable, config);
246 if (failed(devicePtr))
247 return failure();
248 rewriter.replaceOp(op, *devicePtr);
249 return success();
250 }
251
252 OpenACCSupport &accSupport;
253 Region &globalSymbolRegion;
254 SymbolTable &symbolTable;
255 ACCRuntimeCallConfig config;
256};
257
258/// A data exit operation and the bounds of a mapping have no result to stand
259/// in for, so they are simply removed with the construct that used them.
260template <typename OpTy>
261struct EraseDataClauseOp : public ConvertOpToLLVMPattern<OpTy> {
262 using ConvertOpToLLVMPattern<OpTy>::ConvertOpToLLVMPattern;
263 using OpAdaptor = typename OpTy::Adaptor;
264
265 LogicalResult
266 matchAndRewrite(OpTy op, OpAdaptor,
267 ConversionPatternRewriter &rewriter) const override {
268 rewriter.eraseOp(op);
269 return success();
270 }
271};
272
273} // namespace
274
277 // The map entries themselves stay legal: a construct removes the ones it
278 // consumes, and the ones describing a construct that is lowered elsewhere
279 // have to survive this conversion. An `acc.getdeviceptr` read as a value is
280 // the exception, as the address it stands for is only known to the runtime,
281 // so leaving it behind would state a mapping that nothing carries out.
282 target.addIllegalOp<acc::DataOp, acc::EnterDataOp, acc::ExitDataOp,
283 acc::UpdateOp, acc::GetDevicePtrOp>();
284}
285
287 LLVMTypeConverter &converter, RewritePatternSet &patterns,
288 acc::OpenACCSupport &accSupport, Region &globalSymbolRegion,
289 SymbolTable &symbolTable, const acc::ACCRuntimeCallConfig &config) {
290 // Bounds describe a mapping rather than data, and they are removed together
291 // with the clause operations holding them, so their type only has to survive
292 // this conversion.
293 converter.addConversion(
294 [](acc::DataBoundsType type) -> Type { return type; });
295
296 patterns.add<
297 DataEntryOpLowering<acc::MapInfoOp>, DataEntryOpLowering<acc::CopyinOp>,
298 DataEntryOpLowering<acc::CreateOp>, DataEntryOpLowering<acc::PresentOp>,
299 DataEntryOpLowering<acc::NoCreateOp>, DataEntryOpLowering<acc::AttachOp>,
300 DataEntryOpLowering<acc::DevicePtrOp>,
301 DataEntryOpLowering<acc::UpdateDeviceOp>,
302 DataEntryOpLowering<acc::PrivateOp>,
303 DataEntryOpLowering<acc::FirstprivateOp>,
304 DataEntryOpLowering<acc::FirstprivateMapInitialOp>,
305 DataEntryOpLowering<acc::DeclareDeviceResidentOp>,
306 EraseDataClauseOp<acc::CopyoutOp>, EraseDataClauseOp<acc::DeleteOp>,
307 EraseDataClauseOp<acc::DetachOp>, EraseDataClauseOp<acc::UpdateHostOp>,
308 EraseDataClauseOp<acc::DataBoundsOp>>(converter);
309 patterns.add<GetDevicePtrOpLowering>(converter, accSupport,
310 globalSymbolRegion, symbolTable, config);
311}
312
314 LLVMTypeConverter &converter, RewritePatternSet &patterns,
315 acc::OpenACCSupport &accSupport, Region &globalSymbolRegion,
316 SymbolTable &symbolTable, const acc::ACCRuntimeCallConfig &config,
317 acc::DeviceType clauseDeviceType) {
318 patterns.add<DataOpLowering, EnterDataOpLowering, ExitDataOpLowering,
319 UpdateOpLowering>(converter, accSupport, globalSymbolRegion,
320 symbolTable, config, clauseDeviceType);
321}
static LogicalResult emitDataRuntimeCall(Location loc, RuntimeFunction fn, ValueRange mappingOperands, ValueRange convertedOperands, Value asyncQueue, ACCDataCallKind callKind, ConversionPatternRewriter &rewriter, OpenACCSupport &accSupport, Region &globalSymbolRegion, SymbolTable &symbolTable, const ACCRuntimeCallConfig &config)
Emits the call to the mapping entry point fn for mappingOperands, the data-clause operands of a data ...
return success()
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition Pattern.h:233
Conversion from types to the LLVM IR dialect.
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
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition SymbolTable.h:24
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
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.
#define ACC_COMPUTE_AND_DATA_CONSTRUCT_OPS
Definition OpenACC.h:74
#define ACC_DATA_EXIT_OPS
Definition OpenACC.h:59
std::optional< Location > getMappingExitLoc(ValueRange dataClauseOperands)
Returns where the mappings of dataClauseOperands end, taken from the first of them that says.
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,...
RuntimeFunction
IDs for OpenACC compiler-to-runtime entry points (__tgt_acc_*).
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.
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 ...
FailureOr< LLVM::CallOp > createRuntimeCall(Location loc, OpBuilder &builder, Region &globalSymbolRegion, SymbolTable &symbolTable, RuntimeFunction fn, const ACCRuntimeCallConfig &config, ArrayRef< Value > arguments)
Declares (if needed) and returns a call to the runtime function identified by fn using the name from ...
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...
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:733
Include the generated interface declarations.
void populateACCDataClauseOpPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns, acc::OpenACCSupport &accSupport, Region &globalSymbolRegion, SymbolTable &symbolTable, const acc::ACCRuntimeCallConfig &config={})
Populate the patterns that remove OpenACC data clause operations once the constructs holding them hav...
void populateACCDataDirectivePatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns, acc::OpenACCSupport &accSupport, Region &globalSymbolRegion, SymbolTable &symbolTable, const acc::ACCRuntimeCallConfig &config={}, acc::DeviceType clauseDeviceType=acc::DeviceType::None)
Populate patterns that lower OpenACC data directives (acc.data, enter_data, exit_data,...
ACCDataCallKind
Identifies how data runtime arguments will be consumed.
LogicalResult emitACCDataRuntimeArgs(Location loc, ValueRange mappingOperands, ValueRange convertedOperands, ConversionPatternRewriter &rewriter, Region &globalSymbolRegion, acc::OpenACCSupport &accSupport, const acc::ACCRuntimeCallConfig &config, ACCDataRuntimeArgs &runtimeArgs, ACCDataCallKind callKind=ACCDataCallKind::DataEnter, SymbolTable *symbolTable=nullptr)
Emit the OpenACC data runtime arguments for data-clause operands.
void configureACCDataDirectiveConversionLegality(ConversionTarget &target)
Configure conversion legality for OpenACC data directives.
The arguments every mapping entry point of the OpenACC runtime takes, in the order they are passed.
SmallVector< Value > getCallArgs() const
Returns the fields above in the order the entry points take them, so that a caller only appends the a...