MLIR 24.0.0git
ACCExecutableDirectivePatterns.cpp
Go to the documentation of this file.
1//===- ACCExecutableDirectivePatterns.cpp - ACC exec patterns ---*- 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// Lowers OpenACC executable directives (init, shutdown, wait, set) to calls to
10// an OpenACC offloading runtime compiler interface.
11//
12//===----------------------------------------------------------------------===//
13
16
22#include "llvm/ADT/STLExtras.h"
23#include "llvm/ADT/STLFunctionalExtras.h"
24
25#include <cstdint>
26#include <iterator>
27
28using namespace mlir;
29using namespace mlir::acc;
30
31namespace {
32static Value castToI64(Location loc, Value value,
33 ConversionPatternRewriter &rewriter) {
34 Type i64Ty = IntegerType::get(rewriter.getContext(), 64);
35 unsigned bitwidth = value.getType().getIntOrFloatBitWidth();
36 if (bitwidth > 64)
37 return arith::TruncIOp::create(rewriter, loc, i64Ty, value);
38 if (bitwidth < 64)
39 return arith::ExtSIOp::create(rewriter, loc, i64Ty, value);
40 return value;
41}
42
43static Value getAsyncQueue(WaitOp op, ConversionPatternRewriter &rewriter,
44 const ACCRuntimeCallConfig &config) {
45 Location loc = op->getLoc();
46 Type i64Ty = IntegerType::get(rewriter.getContext(), 64);
47 if (op.getAsync())
48 return LLVM::ConstantOp::create(rewriter, loc, i64Ty,
50 if (Value asyncValue = op.getAsyncOperand()) {
51 asyncValue = rewriter.getRemappedValue(asyncValue);
52 return castToI64(loc, asyncValue, rewriter);
53 }
54 return LLVM::ConstantOp::create(rewriter, loc, i64Ty,
56}
57
58static LogicalResult createIfThen(Location loc, Value ifCond,
59 ConversionPatternRewriter &rewriter,
60 function_ref<LogicalResult()> thenFn) {
61 Block *parentBlock = rewriter.getInsertionBlock();
62 Block *continueBlock =
63 rewriter.splitBlock(parentBlock, rewriter.getInsertionPoint());
64 Block *thenBlock = rewriter.createBlock(
65 parentBlock->getParent(), std::next(Region::iterator(parentBlock)));
66
67 rewriter.setInsertionPointToEnd(parentBlock);
68 LLVM::CondBrOp::create(rewriter, loc, ifCond, thenBlock, ValueRange{},
69 continueBlock, ValueRange{});
70
71 rewriter.setInsertionPointToStart(thenBlock);
72 LogicalResult result = thenFn();
73 rewriter.setInsertionPointToEnd(thenBlock);
74 LLVM::BrOp::create(rewriter, loc, ValueRange{}, continueBlock);
75 rewriter.setInsertionPointToStart(continueBlock);
76 return result;
77}
78
79/// Run \p emitFn, guarded by a branch on \p ifCond when it is present.
80static LogicalResult emitGuardedByIfCond(Location loc, Value ifCond,
81 ConversionPatternRewriter &rewriter,
82 function_ref<LogicalResult()> emitFn) {
83 if (ifCond)
84 return createIfThen(loc, ifCond, rewriter, emitFn);
85 return emitFn();
86}
87
88template <typename OpTy>
89struct ACCExecutableDirectivePattern : public ConvertOpToLLVMPattern<OpTy> {
90 ACCExecutableDirectivePattern(const LLVMTypeConverter &converter,
91 const ACCRuntimeCallConfig &config,
92 PatternBenefit benefit = 1)
93 : ConvertOpToLLVMPattern<OpTy>(converter, benefit), config(config) {}
94
95 ACCRuntimeCallConfig config;
96};
97
98struct WaitOpLowering : public ACCExecutableDirectivePattern<WaitOp> {
99 using ACCExecutableDirectivePattern<WaitOp>::ACCExecutableDirectivePattern;
100
101 LogicalResult
102 matchAndRewrite(WaitOp op, WaitOp::Adaptor,
103 ConversionPatternRewriter &rewriter) const override {
104 Location loc = op->getLoc();
105 ModuleOp module = op->getParentOfType<ModuleOp>();
106 Type i32Ty = rewriter.getI32Type();
107 Type i64Ty = rewriter.getI64Type();
108 Type ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
109
110 auto emitWait = [&]() -> LogicalResult {
111 Value asyncQueue = getAsyncQueue(op, rewriter, config);
112 SmallVector<Value> waitValues;
113 for (Value operand : op.getWaitOperands())
114 waitValues.push_back(
115 castToI64(loc, rewriter.getRemappedValue(operand), rewriter));
116
117 unsigned size = waitValues.size();
118 Value waitNum = LLVM::ConstantOp::create(rewriter, loc, i32Ty, size);
119 Value waitList;
120 if (size == 0) {
121 waitList = LLVM::ZeroOp::create(rewriter, loc, ptrTy);
122 } else {
123 waitList = LLVM::AllocaOp::create(rewriter, loc, ptrTy, i64Ty, waitNum);
124 for (auto [index, waitValue] : llvm::enumerate(waitValues)) {
125 Value idx = LLVM::ConstantOp::create(rewriter, loc, i32Ty,
126 static_cast<int64_t>(index));
127 Value elementPtr = LLVM::GEPOp::create(
128 rewriter, loc, ptrTy, i64Ty, waitList, ArrayRef<Value>{idx});
129 LLVM::StoreOp::create(rewriter, loc, waitValue, elementPtr);
130 }
131 }
132
133 StringRef functionName = getParentFunctionName(waitValues);
134 if (functionName.empty())
135 functionName = getParentFunctionName(op);
136 Value ident = createIdent(loc, functionName, rewriter, module, config);
137 Value flags = LLVM::ConstantOp::create(rewriter, loc, i64Ty, 0);
138 Value deviceType = LLVM::ConstantOp::create(
139 rewriter, loc, i64Ty,
140 config.getDeviceTypeRuntimeValue(DeviceType::None));
141 Value deviceNum = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 0);
142
143 return createRuntimeCall(
144 loc, rewriter, module, RuntimeFunction::ACCRTL_tgt_acc_wait, config,
145 {ident, flags, deviceType, deviceNum, waitNum, waitList, asyncQueue});
146 };
147
148 if (failed(emitGuardedByIfCond(loc, op.getIfCond(), rewriter, emitWait)))
149 return failure();
150
151 rewriter.eraseOp(op);
152 return success();
153 }
154};
155
156/// Emit a call to a runtime entry point taking
157/// `(ident, flags, deviceType, deviceNum)`. A null `deviceNum` selects the
158/// current device.
159static LogicalResult
160emitDeviceOperationCall(Location loc, RuntimeFunction fn, DeviceType deviceType,
161 Value deviceNum, StringRef functionName,
162 ModuleOp module, ConversionPatternRewriter &rewriter,
163 const ACCRuntimeCallConfig &config) {
164 Type i64Ty = rewriter.getI64Type();
165 Value deviceTypeValue = LLVM::ConstantOp::create(
166 rewriter, loc, i64Ty, config.getDeviceTypeRuntimeValue(deviceType));
167 Value ident = createIdent(loc, functionName, rewriter, module, config);
168 Value flags = LLVM::ConstantOp::create(rewriter, loc, i64Ty, 0);
169 Value deviceNumValue =
170 deviceNum ? castToI64(loc, deviceNum, rewriter)
171 : LLVM::ConstantOp::create(rewriter, loc, i64Ty, -1);
172 return createRuntimeCall(loc, rewriter, module, fn, config,
173 {ident, flags, deviceTypeValue, deviceNumValue});
174}
175
176static LogicalResult rewriteInitOrShutdown(Operation *op, Value deviceNum,
177 ArrayAttr deviceTypesAttr,
178 Value ifCond, bool isInit,
179 ConversionPatternRewriter &rewriter,
180 const ACCRuntimeCallConfig &config) {
181 ModuleOp module = op->getParentOfType<ModuleOp>();
182 Location loc = op->getLoc();
183
184 auto emitCalls = [&]() -> LogicalResult {
185 StringRef functionName = deviceNum ? getParentFunctionName(deviceNum)
187 RuntimeFunction fn = isInit ? RuntimeFunction::ACCRTL_tgt_acc_init
188 : RuntimeFunction::ACCRTL_tgt_acc_shutdown;
189
190 auto emitOne = [&](DeviceType deviceType) {
191 return emitDeviceOperationCall(loc, fn, deviceType, deviceNum,
192 functionName, module, rewriter, config);
193 };
194
195 if (!deviceTypesAttr)
196 return emitOne(DeviceType::None);
197
198 for (Attribute attr : deviceTypesAttr) {
199 if (auto typeAttr = dyn_cast<DeviceTypeAttr>(attr))
200 if (failed(emitOne(typeAttr.getValue())))
201 return failure();
202 }
203 return success();
204 };
205
206 if (failed(emitGuardedByIfCond(loc, ifCond, rewriter, emitCalls)))
207 return failure();
208
209 rewriter.eraseOp(op);
210 return success();
211}
212
213struct InitOpLowering : public ACCExecutableDirectivePattern<InitOp> {
214 using ACCExecutableDirectivePattern<InitOp>::ACCExecutableDirectivePattern;
215
216 LogicalResult
217 matchAndRewrite(InitOp op, InitOp::Adaptor adaptor,
218 ConversionPatternRewriter &rewriter) const override {
219 return rewriteInitOrShutdown(op, adaptor.getDeviceNum(),
220 op.getDeviceTypesAttr(), op.getIfCond(),
221 /*isInit=*/true, rewriter, config);
222 }
223};
224
225struct ShutdownOpLowering : public ACCExecutableDirectivePattern<ShutdownOp> {
226 using ACCExecutableDirectivePattern<
227 ShutdownOp>::ACCExecutableDirectivePattern;
228
229 LogicalResult
230 matchAndRewrite(ShutdownOp op, ShutdownOp::Adaptor adaptor,
231 ConversionPatternRewriter &rewriter) const override {
232 return rewriteInitOrShutdown(op, adaptor.getDeviceNum(),
233 op.getDeviceTypesAttr(), op.getIfCond(),
234 /*isInit=*/false, rewriter, config);
235 }
236};
237
238struct SetOpLowering : public ACCExecutableDirectivePattern<SetOp> {
239 using ACCExecutableDirectivePattern<SetOp>::ACCExecutableDirectivePattern;
240
241 LogicalResult
242 matchAndRewrite(SetOp op, SetOp::Adaptor adaptor,
243 ConversionPatternRewriter &rewriter) const override {
244 ModuleOp module = op->getParentOfType<ModuleOp>();
245 Location loc = op.getLoc();
246 Type i64Ty = rewriter.getI64Type();
247
248 auto emitSet = [&]() -> LogicalResult {
249 if (Value asyncValue = adaptor.getDefaultAsync()) {
250 asyncValue = castToI64(loc, asyncValue, rewriter);
251 Value ident = createIdent(loc, getParentFunctionName(asyncValue),
252 rewriter, module, config);
254 loc, rewriter, module,
255 RuntimeFunction::ACCRTL_tgt_acc_set_default_async, config,
256 {ident, asyncValue})))
257 return failure();
258 }
259
260 if (op.getDeviceNum()) {
261 Value deviceNum = adaptor.getDeviceNum();
262 DeviceType deviceType = DeviceType::None;
263 if (auto deviceTypeAttr = op.getDeviceTypeAttr())
264 deviceType = deviceTypeAttr.getValue();
265 return emitDeviceOperationCall(
266 loc, RuntimeFunction::ACCRTL_tgt_acc_set_device_num, deviceType,
267 deviceNum, getParentFunctionName(deviceNum), module, rewriter,
268 config);
269 }
270 if (auto deviceTypeAttr = op.getDeviceTypeAttr()) {
271 Value deviceTypeValue = LLVM::ConstantOp::create(
272 rewriter, loc, i64Ty,
273 config.getDeviceTypeRuntimeValue(deviceTypeAttr.getValue()));
274 Value ident = createIdent(loc, StringRef(), rewriter, module, config);
275 Value flags = LLVM::ConstantOp::create(rewriter, loc, i64Ty, 0);
276 return createRuntimeCall(
277 loc, rewriter, module,
278 RuntimeFunction::ACCRTL_tgt_acc_set_device_type, config,
279 {ident, flags, deviceTypeValue});
280 }
281 return success();
282 };
283
284 if (failed(emitGuardedByIfCond(loc, op.getIfCond(), rewriter, emitSet)))
285 return failure();
286
287 rewriter.eraseOp(op);
288 return success();
289 }
290};
291
292} // namespace
293
296 target.addIllegalOp<acc::InitOp, acc::ShutdownOp, acc::WaitOp, acc::SetOp>();
297}
298
300 LLVMTypeConverter &converter, RewritePatternSet &patterns,
301 const acc::ACCRuntimeCallConfig &config) {
302 patterns
303 .add<WaitOpLowering, InitOpLowering, ShutdownOpLowering, SetOpLowering>(
304 converter, config);
305}
return success()
ArrayAttr()
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:33
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition Pattern.h:227
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
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
BlockListType::iterator iterator
Definition Region.h:52
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
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
Type getType() const
Return the type of this value.
Definition Value.h:105
Optional overrides for OpenACC to LLVM runtime lowering.
int64_t getDeviceTypeRuntimeValue(DeviceType type) const
FailureOr< LLVM::CallOp > createRuntimeCall(Location loc, OpBuilder &builder, ModuleOp module, 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 ...
RuntimeFunction
IDs for OpenACC compiler-to-runtime entry points (__tgt_acc_*).
Value createIdent(Location loc, StringRef functionName, OpBuilder &builder, ModuleOp module, const ACCRuntimeCallConfig &config)
Returns a pointer to a constant global holding an ident_t for OpenACC runtime calls.
StringRef getParentFunctionName(Operation *op)
Returns the enclosing function symbol name for op.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
void configureACCExecutableDirectiveConversionLegality(ConversionTarget &target)
Configure conversion legality for OpenACC executable directives lowered to runtime calls.
void populateACCExecutableDirectivePatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns, const acc::ACCRuntimeCallConfig &config={})
Populate patterns that lower OpenACC executable directives (init, shutdown, wait, set) to LLVM runtim...
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147