MLIR 24.0.0git
ACCSpecializeForDevice.cpp
Go to the documentation of this file.
1//===- ACCSpecializeForDevice.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 strips OpenACC constructs that are invalid or unnecessary inside
10// device code (specialized acc routines or compute construct regions).
11//
12// Overview:
13// ---------
14// In a specialized acc routine or compute construct, many OpenACC operations
15// do not make sense because they are host-side constructs. This pass removes
16// or transforms these operations appropriately:
17//
18// - Data operations that manage device memory from host perspective
19// - Compute constructs that launch kernels (we're already on device)
20// - Runtime operations like init/shutdown/set/wait
21//
22// Transformations:
23// ----------------
24// The pass applies the following transformations:
25//
26// 1. Data Entry Ops (replaced with var operand):
27// acc.attach, acc.copyin, acc.create, acc.declare_device_resident,
28// acc.declare_link, acc.deviceptr, acc.get_deviceptr, acc.nocreate,
29// acc.present, acc.update_device, acc.use_device
30//
31// 2. Data Exit Ops (erased):
32// acc.copyout, acc.delete, acc.detach, acc.update_host
33//
34// 3. Structured Data/Compute Constructs (region inlined):
35// acc.data, acc.host_data, acc.kernel_environment, acc.parallel,
36// acc.serial, acc.kernels
37//
38// 4. Unstructured Data Ops (erased):
39// acc.enter_data, acc.exit_data, acc.update, acc.declare_enter,
40// acc.declare_exit
41//
42// 5. Runtime Ops (erased):
43// acc.init, acc.shutdown, acc.set, acc.wait
44//
45// 6. acc.on_device (folded):
46// acc.on_device with constant device type is folded to a boolean constant.
47// With a device type only known at runtime it is replaced by comparisons
48// against the device types being specialized for.
49//
50// Scope of Application:
51// ---------------------
52// - For functions with `acc.specialized_routine` attribute: patterns are
53// applied to the entire function body.
54// - For non-specialized functions: patterns are applied only to ACC
55// operations INSIDE compute constructs (parallel, serial, kernels),
56// not to the compute constructs themselves or their data operands.
57//
58// Note: acc.cache, acc.private, acc.reduction, acc.firstprivate are NOT
59// transformed by this pass as they are valid in device code.
60//
61//===----------------------------------------------------------------------===//
62
65
71#include "mlir/IR/MLIRContext.h"
74
75namespace mlir {
76namespace acc {
77#define GEN_PASS_DEF_ACCSPECIALIZEFORDEVICE
78#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
79} // namespace acc
80} // namespace mlir
81
82using namespace mlir;
83using namespace mlir::acc;
84
85namespace {
86/// Fold acc.on_device in device code. A constant device type folds to a
87/// boolean constant, any other device type to a comparison against the device
88/// types being specialized for.
89class FoldAccOnDeviceOpConversion : public OpRewritePattern<acc::OnDeviceOp> {
90 using OpRewritePattern<acc::OnDeviceOp>::OpRewritePattern;
91
92 const TypesForDevice &theDeviceTypes;
93
94public:
95 FoldAccOnDeviceOpConversion(MLIRContext *context,
96 const TypesForDevice &theDeviceTypes)
97 : OpRewritePattern<acc::OnDeviceOp>(context),
98 theDeviceTypes(theDeviceTypes) {}
99
100 LogicalResult matchAndRewrite(acc::OnDeviceOp op,
101 PatternRewriter &rewriter) const override {
102 Value deviceType = op.getDeviceType();
103 APInt constVal;
104 if (matchPattern(deviceType, m_ConstantInt(&constVal))) {
105 bool result = theDeviceTypes.contains(constVal.getSExtValue());
106 rewriter.replaceOpWithNewOp<arith::ConstantOp>(
107 op, rewriter.getI1Type(), rewriter.getBoolAttr(result));
108 return success();
109 }
110
111 // The device type is only known at runtime, inline the definition of
112 // `on_device`.
113 Location loc = op.getLoc();
114 Value result;
115 for (int64_t type : theDeviceTypes) {
116 Value typeValue = arith::ConstantOp::create(
117 rewriter, loc, deviceType.getType(),
118 rewriter.getIntegerAttr(deviceType.getType(), type));
119 Value isType = arith::CmpIOp::create(
120 rewriter, loc, arith::CmpIPredicate::eq, deviceType, typeValue);
121 if (result)
122 result = arith::OrIOp::create(rewriter, loc, result, isType);
123 else
124 result = isType;
125 }
126 assert(result && "At least one device type expected.");
127 rewriter.replaceOp(op, result);
128 return success();
129 }
130};
131
132class ACCSpecializeForDevice
133 : public acc::impl::ACCSpecializeForDeviceBase<ACCSpecializeForDevice> {
134public:
135 using ACCSpecializeForDeviceBase<
136 ACCSpecializeForDevice>::ACCSpecializeForDeviceBase;
137
138 void getDependentDialects(DialectRegistry &registry) const override {
139 ACCSpecializeForDeviceBase<ACCSpecializeForDevice>::getDependentDialects(
140 registry);
142 }
143
144 void runOnOperation() override {
145 func::FuncOp func = getOperation();
146
147 TypesForDevice types{theDeviceTypes.begin(), theDeviceTypes.end()};
148
149 RewritePatternSet patterns(&getContext());
151 GreedyRewriteConfig config;
152 config.setUseTopDownTraversal(true);
153
155 // For specialized acc routines, apply patterns to the entire function
156 (void)applyPatternsGreedily(func, std::move(patterns), config);
157 } else {
158 // For non-specialized functions, apply patterns only to ACC operations
159 // inside compute constructs (not to the compute constructs themselves).
160 // Use ExistingOps strictness so the greedy driver does not expand the
161 // worklist to parent ops, which would accidentally unwrap the compute
162 // construct (e.g. after inlining acc routines with their own data
163 // regions).
164 config.setStrictness(GreedyRewriteStrictness::ExistingOps);
165 SmallVector<Operation *> opsToTransform;
166 func.walk([&](Operation *op) {
167 if (isa<ACC_COMPUTE_CONSTRUCT_OPS>(op)) {
168 // Walk inside the compute construct and collect ACC ops
169 op->walk([&](Operation *innerOp) {
170 // Skip the compute construct itself
171 if (innerOp == op)
172 return;
173 if (isa<acc::OpenACCDialect>(innerOp->getDialect()))
174 opsToTransform.push_back(innerOp);
175 });
176 }
177 });
178 if (!opsToTransform.empty())
179 (void)applyOpPatternsGreedily(opsToTransform, std::move(patterns),
180 config);
181 }
182 }
183};
184} // namespace
185
186//===----------------------------------------------------------------------===//
187// Pattern population functions
188//===----------------------------------------------------------------------===//
189
191 RewritePatternSet &patterns, const TypesForDevice &theDeviceTypes) {
192 MLIRContext *context = patterns.getContext();
193
194 // Declare patterns - erase declare_enter and its associated declare_exit
195 patterns.insert<ACCDeclareEnterOpConversion>(context);
196
197 // Data entry ops - replaced with their var operand
198 // Note: acc.cache, acc.private, acc.reduction, acc.firstprivate are NOT
199 // included here - they are valid in device code
211
212 // Data exit ops - simply erased (no results)
217
218 // Structured data constructs - unwrap their regions
222
223 // Compute constructs - unwrap their regions
227
228 // Unstructured data operations - erase them
232
233 // Runtime operations - erase them
234 patterns.insert<
237 context);
238
239 // Fold acc.on_device calls so dead-code elimination can remove host-only
240 // code paths in device code.
241 patterns.insert<FoldAccOnDeviceOpConversion>(context, theDeviceTypes);
242}
return success()
b getContext())
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
BoolAttr getBoolAttr(bool value)
Definition Builders.cpp:108
IntegerType getI1Type()
Definition Builders.cpp:61
GreedyRewriteConfig & setUseTopDownTraversal(bool use=true)
GreedyRewriteConfig & setStrictness(GreedyRewriteStrictness mode)
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:71
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition Operation.h:237
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:851
RewritePatternSet & insert(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
MLIRContext * getContext() const
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
Type getType() const
Return the type of this value.
Definition Value.h:105
Pattern to erase acc.declare_enter and its associated acc.declare_exit.
Pattern to simply erase an ACC op (for ops with no results).
Pattern to replace an ACC op with its var operand.
Pattern to unwrap a region from an ACC op and erase the wrapper.
llvm::SmallSetVector< int64_t, 3 > TypesForDevice
Holds information for which integers represent a device type in the runtime.
Definition Passes.h:33
bool isSpecializedAccRoutine(mlir::Operation *op)
Used to check whether this is a specialized accelerator version of acc routine function.
Definition OpenACC.h:213
void registerAllExtensions(DialectRegistry &registry)
Register the standard OpenACC models for GPU, LLVM and MemRef IR.
void populateACCSpecializeForDevicePatterns(RewritePatternSet &patterns, const TypesForDevice &theDeviceTypes)
Populates all patterns for device specialization.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
Definition Matchers.h:527
LogicalResult applyPatternsGreedily(Region &region, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
LogicalResult applyOpPatternsGreedily(ArrayRef< Operation * > ops, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr, bool *allErased=nullptr)
Rewrite the specified ops by repeatedly applying the highest benefit patterns in a greedy worklist dr...
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...