MLIR 23.0.0git
ACCRecipeMaterialization.cpp
Go to the documentation of this file.
1//===- ACCRecipeMaterialization.cpp - Materialize ACC recipes -------------===//
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// Overview:
10// ---------
11// OpenACC compute constructs (acc.parallel, acc.serial, acc.kernels) and
12// acc.loop can carry data clauses (acc.private, acc.firstprivate,
13// acc.reduction) that refer to recipes (acc.private.recipe,
14// acc.firstprivate.recipe, acc.reduction.recipe). Recipes define how to
15// initialize, copy, combine, or destroy a particular variable. This pass clones
16// those regions into the construct and ensures the materialized SSA values are
17// used instead.
18//
19// Transforms:
20// -----------
21// 1. Firstprivate: Inserts acc.firstprivate_map so the initial value is
22// available on the device, then clones the recipe init and copy regions
23// into the construct and replaces uses with the materialized alloca.
24// Optional destroy region is cloned before the region terminator.
25//
26// 2. Private: Clones the recipe init region into the construct (at the
27// region entry or at the loop op for acc.loop private). Replaces uses
28// of the recipe result with the materialized alloca. Optional destroy
29// region is cloned before the region terminator.
30//
31// 3. Reduction: Creates acc.reduction_init (init region inlined) and
32// acc.reduction_combine_region (combiner region inlined). Uses within
33// the region are updated to the reduction init result.
34//
35// Requirements:
36// -------------
37// 1. OpenACCSupport: The pass uses the `acc::OpenACCSupport` analysis
38// including emitNYI for unsupported cases.
39//
40//===----------------------------------------------------------------------===//
41
49#include "mlir/IR/Block.h"
50#include "mlir/IR/Builders.h"
51#include "mlir/IR/IRMapping.h"
52#include "mlir/IR/SymbolTable.h"
53#include "mlir/IR/Value.h"
54#include "mlir/IR/ValueRange.h"
56#include "mlir/Support/LLVM.h"
58#include "llvm/ADT/STLExtras.h"
59#include "llvm/ADT/TypeSwitch.h"
60#include "llvm/Support/Debug.h"
61#include "llvm/Support/ErrorHandling.h"
62
63namespace mlir {
64namespace acc {
65#define GEN_PASS_DEF_ACCRECIPEMATERIALIZATION
66#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
67} // namespace acc
68} // namespace mlir
69
70#define DEBUG_TYPE "acc-recipe-materialization"
71
72namespace {
73
74using namespace mlir;
75
76static void saveVarName(StringRef name, Value dst) {
77 if (name.empty())
78 return;
79 if (Operation *dstOp = dst.getDefiningOp()) {
80 if (dstOp->getAttrOfType<acc::VarNameAttr>(acc::getVarNameAttrName()))
81 return;
82 if (isa<ACC_DATA_ENTRY_OPS>(dstOp))
83 return;
84 dstOp->setAttr(acc::getVarNameAttrName(),
85 acc::VarNameAttr::get(dstOp->getContext(), name));
86 return;
87 }
88 auto blockArg = dyn_cast<BlockArgument>(dst);
89 if (!blockArg)
90 return;
91 Block *block = blockArg.getOwner();
92 Region *region = block ? block->getParent() : nullptr;
93 if (!region || !block->isEntryBlock())
94 return;
95 Operation *parent = region->getParentOp();
96 if (!parent)
97 return;
98 auto funcOp = dyn_cast<FunctionOpInterface>(parent);
99 if (!funcOp)
100 return;
101 unsigned argIdx = blockArg.getArgNumber();
102 if (argIdx >= funcOp.getNumArguments())
103 return;
104 if (funcOp.getArgAttr(argIdx, acc::getVarNameAttrName()))
105 return;
106 funcOp.setArgAttr(argIdx, acc::getVarNameAttrName(),
107 acc::VarNameAttr::get(parent->getContext(), name));
108}
109
110static void saveVarName(Value src, Value dst) {
111 saveVarName(acc::getVariableName(src), dst);
112}
113
114static void resolveVarNamePlaceholders(Block *block, Block::iterator ip,
115 StringRef name) {
116 StringRef placeholder = acc::getVarNamePlaceholder();
117 for (auto it = block->begin(); it != std::next(ip); ++it) {
118 auto attr = it->getAttrOfType<acc::VarNameAttr>(acc::getVarNameAttrName());
119 if (attr && attr.getName() == placeholder) {
120 if (name.empty())
121 it->removeAttr(acc::getVarNameAttrName());
122 else
123 it->setAttr(acc::getVarNameAttrName(),
124 acc::VarNameAttr::get(it->getContext(), name));
125 }
126 }
127}
128
129// Clone the destroy region of the recipe before the terminator of the provided
130// block. Values must be provided for the destroy region block arguments
131// according to the recipe specifications.
132template <typename RecipeOpTy>
133static void cloneDestroy(RecipeOpTy recipe, mlir::Block *block,
135 const llvm::SmallVector<mlir::Value> &arguments) {
136 IRMapping mapping{};
137 Region &destroyRegion = recipe.getDestroyRegion();
138 assert(destroyRegion.getBlocks().front().getNumArguments() ==
139 arguments.size() &&
140 "unexpected acc recipe destroy block arguments");
141 mapping.map(destroyRegion.getBlocks().front().getArguments(), arguments);
142 acc::cloneACCRegionInto(&destroyRegion, block, ip, mapping,
143 /*resultsToReplace=*/{});
144}
145
146class ACCRecipeMaterialization
147 : public acc::impl::ACCRecipeMaterializationBase<ACCRecipeMaterialization> {
148public:
150 ACCRecipeMaterialization>::ACCRecipeMaterializationBase;
151 void runOnOperation() override;
152
153private:
154 // When handling firstprivate, the initial value needs to be available on
155 // the GPU. One way to get that value there is to map the variable through
156 // global memory.
157 // Thus, when we materialize a firstprivate, we materialize it into
158 // a mapping action first. This function ends up with doing the following:
159 // %dev = acc.firstprivate var(%var)
160 // =>
161 // %copy = acc.firstprivate_map var(%var)
162 // %dev = acc.firstprivate var(%copy)
163 // When the recipe materialization happens, the `acc.firstprivate` ends up
164 // being removed. But because of the way we chain it to the
165 // `acc.firstprivate_map`, then its result becomes live-in to the
166 // compute region and used as the variable the initial value is loaded from.
167 void handleFirstprivateMapping(acc::FirstprivateOp firstprivateOp) const;
168 template <typename OpTy>
169 void removeRecipe(OpTy op, ModuleOp moduleOp) const;
170 template <typename OpTy, typename RecipeOpTy, typename AccOpTy>
171 LogicalResult materialize(OpTy op, RecipeOpTy recipe, AccOpTy accOp,
172 acc::OpenACCSupport &accSupport) const;
173 template <typename OpTy>
174 LogicalResult materializeForACCOp(OpTy accOp,
175 acc::OpenACCSupport &accSupport) const;
176};
177
178void ACCRecipeMaterialization::handleFirstprivateMapping(
179 acc::FirstprivateOp firstprivateOp) const {
180 OpBuilder builder(firstprivateOp);
181 auto mapFirstprivateOp = acc::FirstprivateMapInitialOp::create(
182 builder, firstprivateOp.getLoc(), firstprivateOp.getVar(),
183 firstprivateOp.getStructured(), firstprivateOp.getImplicit(),
184 firstprivateOp.getBounds());
185 mapFirstprivateOp.setName(firstprivateOp.getName());
186 firstprivateOp.getVarMutable().assign(mapFirstprivateOp.getAccVar());
187}
188
189template <typename OpTy>
190void ACCRecipeMaterialization::removeRecipe(OpTy op, ModuleOp moduleOp) const {
191 auto recipeName = op.getNameAttr();
192 if (SymbolTable::symbolKnownUseEmpty(recipeName, moduleOp)) {
193 LLVM_DEBUG(llvm::dbgs() << "erasing recipe: " << recipeName << "\n");
194 op.erase();
195 } else {
196 LLVM_DEBUG({
197 std::optional<SymbolTable::UseRange> symbolUses =
198 op.getSymbolUses(moduleOp);
199 if (symbolUses.has_value()) {
200 for (SymbolTable::SymbolUse symbolUse : *symbolUses) {
201 llvm::dbgs() << "symbol use: ";
202 symbolUse.getUser()->dump();
203 }
204 }
205 });
206 llvm_unreachable("expected no use of recipe symbol");
207 }
208}
209
210template <typename OpTy, typename RecipeOpTy, typename AccOpTy>
211LogicalResult
212ACCRecipeMaterialization::materialize(OpTy op, RecipeOpTy recipe, AccOpTy accOp,
213 acc::OpenACCSupport &accSupport) const {
214 Region &region = accOp.getRegion();
215 Value origPtr = op.getVar();
216 Value accPtr = op.getAccVar();
217 assert(accPtr && "invalid op: null acc var");
218
219 OpBuilder b(op);
220 SmallVector<Value> triples;
221
222 // Clone init block into the region at the insertion point specified.
223 Region &initRegion = recipe.getInitRegion();
224 unsigned initNumArguments =
225 initRegion.getBlocks().front().getArguments().size();
226 if (initNumArguments > 1) {
227 // Code from C/C++ will most likely only provide extent arguments to the
228 // recipe arguments.
229 if ((initNumArguments - 1) % 3 != 0) {
230 (void)accSupport.emitNYI(recipe.getLoc(),
231 "privatization of array section with extents");
232 return failure();
233 }
234 // The remaining arguments must be the bounds triples
235 // (lower-bound, upper-bound, step), ...
236 unsigned argIdx = 1;
237 // Cast the given value to the type of the combiner region's argument
238 // at position argIdx, and increment argIdx.
239 auto castValueToArgType = [&](Location loc, Value v) {
241 b, loc, v,
242 initRegion.getBlocks().front().getArgument(argIdx++).getType(),
243 /*isUnsignedCast=*/false);
244 };
245 for (Value bound : acc::getBounds(op)) {
246 auto dataBound = bound.getDefiningOp<acc::DataBoundsOp>();
247 assert(dataBound &&
248 "acc.reduction's bound must be defined by acc.bounds");
249 // NOTE: we should probably generate get_lowerbound, get_upperbound
250 // and get_stride here, so that we can stop looking for the acc.bounds
251 // operation above, and just use the `bound` value.
252 Value lb =
253 castValueToArgType(dataBound.getLoc(), dataBound.getLowerbound());
254 Value ub =
255 castValueToArgType(dataBound.getLoc(), dataBound.getUpperbound());
256 Value step =
257 castValueToArgType(dataBound.getLoc(), dataBound.getStride());
258 triples.append({lb, ub, step});
259 }
260 assert(triples.size() + 1 == initNumArguments &&
261 "mismatch between number bounds and number of recipe init block "
262 "arguments");
263 }
264
265 IRMapping mapping;
266 SmallVector<Value> initArgs{origPtr};
267 initArgs.append(triples);
268 mapping.map(initRegion.getBlocks().front().getArguments(), initArgs);
269
270 if constexpr (std::is_same_v<OpTy, acc::PrivateOp>) {
271 // Clone the init region for a private.
272 Block *block = &region.front();
273 auto [results, ip] = acc::cloneACCRegionInto(
274 &initRegion, block, block->begin(), mapping, {accPtr});
275 assert(results.size() == 1 && "expected single result from init region");
276 saveVarName(op.getAccVar(), results[0]);
277 resolveVarNamePlaceholders(block, ip, acc::getVariableName(op.getAccVar()));
278 // Clone the destroy region for a private, if it exists.
279 if (!recipe.getDestroyRegion().empty()) {
280 results.insert(results.begin(), origPtr);
281 results.append(triples);
282 cloneDestroy(recipe, block, std::prev(block->end()), results);
283 }
284 } else if constexpr (std::is_same_v<OpTy, acc::FirstprivateOp>) {
285 // Clone the init region for a firstprivate.
286 Block *block = &region.front();
287 auto [results, ip] = acc::cloneACCRegionInto(
288 &initRegion, block, block->begin(), mapping, {accPtr});
289 assert(results.size() == 1 && "expected single result from init region");
290 saveVarName(op.getAccVar(), results[0]);
291 resolveVarNamePlaceholders(block, ip, acc::getVariableName(op.getAccVar()));
292 // We want the copy to store the origPtr to private
293 results.insert(results.begin(), origPtr);
294 results.append(triples);
295
296 // Clone the copy region for a firstprivate
297 mapping.clear();
298 mapping.map(recipe.getCopyRegion().front().getArguments(), results);
299 // Clone the copy region for a firstprivate.
300 acc::cloneACCRegionInto(&recipe.getCopyRegion(), block, std::next(ip),
301 mapping, {});
302 if (!recipe.getDestroyRegion().empty()) {
303 // origPtr was already pushed.
304 cloneDestroy(recipe, block, std::prev(block->end()), results);
305 }
306 } else if constexpr (std::is_same_v<OpTy, acc::ReductionOp>) {
307 auto cloneRegionIntoAccRegion = [&](Region *src, Region *dest,
308 bool hasResult) {
309 src->cloneInto(dest, mapping);
310 Block *block = &dest->front();
311 Operation *terminator = block->getTerminator();
312 b.setInsertionPoint(terminator);
313 if (hasResult)
314 acc::YieldOp::create(b, op.getLoc(), terminator->getOperands());
315 else
316 acc::YieldOp::create(b, op.getLoc(), ValueRange{});
317 terminator->erase();
318 };
319
320 // Clone the init region into acc.reduction_init.
321 if constexpr (std::is_same_v<AccOpTy, acc::ParallelOp>)
322 b.setInsertionPointToStart(&region.front());
323 else if constexpr (std::is_same_v<AccOpTy, acc::LoopOp>)
324 b.setInsertionPoint(op);
325 else
326 llvm_unreachable("unexpected acc op with reduction recipe");
327
328 auto reductionOp = acc::ReductionInitOp::create(
329 b, op.getLoc(), origPtr, recipe.getReductionOperatorAttr());
330 saveVarName(op.getAccVar(), reductionOp.getResult());
331 cloneRegionIntoAccRegion(&initRegion, &reductionOp.getRegion(),
332 /*hasResult=*/true);
333 Block *initBlock = &reductionOp.getRegion().front();
334 resolveVarNamePlaceholders(initBlock, std::prev(initBlock->end()),
335 acc::getVariableName(op.getAccVar()));
336
337 // Update the uses within the loop to use the reduction op result.
338 replaceAllUsesInRegionWith(accPtr, reductionOp.getResult(), region);
339
340 // Clone the combiner region into acc.reduction_combine_region.
341 Region &combinerRegion = recipe.getCombinerRegion();
342 Block *entryBlock = &combinerRegion.front();
343
344 if constexpr (std::is_same_v<AccOpTy, acc::ParallelOp>)
345 b.setInsertionPoint(region.back().getTerminator());
346 else if constexpr (std::is_same_v<AccOpTy, acc::LoopOp>)
347 b.setInsertionPointAfter(accOp);
348 else
349 llvm_unreachable("unexpected acc op with reduction recipe");
350
351 // Map the first two block arguments to the original and private
352 // reduction variables. If the recipe's combiner region has the bounds
353 // arguments, we have to map them to the corresponding operands of
354 // acc.reduction operation.
355 mapping.clear();
356 SmallVector<Value, 2> argsRemapping{origPtr, reductionOp.getResult()};
357 argsRemapping.append(triples);
358 mapping.map(entryBlock->getArguments(), argsRemapping);
359
360 auto combineRegionOp = acc::ReductionCombineRegionOp::create(
361 b, op.getLoc(), origPtr, reductionOp.getResult());
362 cloneRegionIntoAccRegion(&combinerRegion, &combineRegionOp.getRegion(),
363 /*hasResult=*/false);
364
365 auto setSeqParDimsForRecipeLoops = [](Region *r) {
366 r->walk([](LoopLikeOpInterface loopLike) {
367 loopLike->setAttr(
368 acc::GPUParallelDimsAttr::name,
369 acc::GPUParallelDimsAttr::seq(loopLike->getContext()));
370 });
371 };
372 setSeqParDimsForRecipeLoops(&reductionOp.getRegion());
373 setSeqParDimsForRecipeLoops(&combineRegionOp.getRegion());
374
375 if (!recipe.getDestroyRegion().empty()) {
376 SmallVector<Value> results{origPtr, reductionOp.getResult()};
377 Block::iterator ip = std::next(Block::iterator(combineRegionOp));
378 cloneDestroy(recipe, combineRegionOp->getBlock(), ip, results);
379 }
380 } else {
381 llvm_unreachable("unexpected op type");
382 }
383
384 op.erase();
385 return success();
386}
387
388template <typename OpTy>
389LogicalResult ACCRecipeMaterialization::materializeForACCOp(
390 OpTy accOp, acc::OpenACCSupport &accSupport) const {
391 assert(isa<ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS>(accOp));
392
393 if (!accOp.getFirstprivateOperands().empty()) {
394 // Clear the firstprivate operands list so there will be no uses after
395 // the recipe is materialized.
396 SmallVector<Value> operands(accOp.getFirstprivateOperands());
397 accOp.getFirstprivateOperandsMutable().clear();
398 for (Value operand : operands) {
399 auto firstprivateOp = cast<acc::FirstprivateOp>(operand.getDefiningOp());
400 auto symbolRef = cast<SymbolRefAttr>(firstprivateOp.getRecipeAttr());
401 auto decl = SymbolTable::lookupNearestSymbolFrom(accOp, symbolRef);
402 auto recipeOp = cast<acc::FirstprivateRecipeOp>(decl);
403 LLVM_DEBUG(llvm::dbgs() << "materializing: " << firstprivateOp << "\n"
404 << symbolRef << "\n");
405 handleFirstprivateMapping(firstprivateOp);
406 if (failed(materialize(firstprivateOp, recipeOp, accOp, accSupport)))
407 return failure();
408 }
409 }
410
411 if (!accOp.getPrivateOperands().empty()) {
412 // Clear the private operands list so there will be no uses after
413 // the recipe is materialized.
414 SmallVector<Value> operands(accOp.getPrivateOperands());
415 accOp.getPrivateOperandsMutable().clear();
416 for (Value operand : operands) {
417 auto privateOp = cast<acc::PrivateOp>(operand.getDefiningOp());
418 auto symbolRef = cast<SymbolRefAttr>(privateOp.getRecipeAttr());
419 auto decl = SymbolTable::lookupNearestSymbolFrom(accOp, symbolRef);
420 auto recipeOp = cast<acc::PrivateRecipeOp>(decl);
421 LLVM_DEBUG(llvm::dbgs() << "materializing: " << privateOp << "\n"
422 << symbolRef << "\n");
423 if (failed(materialize(privateOp, recipeOp, accOp, accSupport)))
424 return failure();
425 }
426 }
427
428 if (!accOp.getReductionOperands().empty()) {
429 // Clear the reduction operands list so there will be no uses after
430 // the recipe is materialized.
431 SmallVector<Value> operands(accOp.getReductionOperands());
432 accOp.getReductionOperandsMutable().clear();
433 for (Value operand : operands) {
434 auto reductionOp = cast<acc::ReductionOp>(operand.getDefiningOp());
435 auto symbolRef = cast<SymbolRefAttr>(reductionOp.getRecipeAttr());
436 auto decl = SymbolTable::lookupNearestSymbolFrom(accOp, symbolRef);
437 auto recipeOp = cast<acc::ReductionRecipeOp>(decl);
438 LLVM_DEBUG(llvm::dbgs() << "materializing: " << reductionOp << "\n"
439 << symbolRef << "\n");
440 if (failed(materialize(reductionOp, recipeOp, accOp, accSupport)))
441 return failure();
442 }
443 }
444 return success();
445}
446
447void ACCRecipeMaterialization::runOnOperation() {
448 ModuleOp moduleOp = getOperation();
449 acc::OpenACCSupport &accSupport = getAnalysis<acc::OpenACCSupport>();
450
451 // Materialize all recipes for all compute constructs and loop constructs.
452 bool anyFailed = false;
453 moduleOp.walk([&](Operation *op) {
454 if (anyFailed)
455 return;
457 [&](auto constructOp) {
458 if (failed(materializeForACCOp(constructOp, accSupport)))
459 anyFailed = true;
460 });
461 });
462 if (anyFailed) {
463 signalPassFailure();
464 return;
465 }
466
467 // Remove all recipes.
468 moduleOp.walk([&](Operation *op) {
469 if (auto recipe = dyn_cast<acc::ReductionRecipeOp>(op))
470 removeRecipe(recipe, moduleOp);
471 else if (auto recipe = dyn_cast<acc::PrivateRecipeOp>(op))
472 removeRecipe(recipe, moduleOp);
473 else if (auto recipe = dyn_cast<acc::FirstprivateRecipeOp>(op))
474 removeRecipe(recipe, moduleOp);
475 });
476}
477
478} // namespace
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
Block represents an ordered list of Operations.
Definition Block.h:33
OpListType::iterator iterator
Definition Block.h:150
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
Operation & front()
Definition Block.h:163
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:97
iterator end()
Definition Block.h:154
iterator begin()
Definition Block.h:153
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition Block.cpp:36
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
void clear()
Clears all mappings held by the mapper.
Definition IRMapping.h:79
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
This class helps build Operations.
Definition Builders.h:209
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
void erase()
Remove this operation from its parent block and delete it.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
Block & back()
Definition Region.h:64
void cloneInto(Region *dest, IRMapping &mapper)
Clone the internal blocks from this region into dest.
Definition Region.cpp:70
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition Region.h:200
BlockListType & getBlocks()
Definition Region.h:45
This class represents a specific symbol use.
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
static bool symbolKnownUseEmpty(StringAttr symbol, Operation *from)
Return if the given symbol is known to have no uses that are nested within the given operation 'from'...
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
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
#define ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS
Definition OpenACC.h:65
std::string getVariableName(mlir::Value v)
Attempts to extract the variable name from a value by walking through view-like operations until an a...
mlir::SmallVector< mlir::Value > getBounds(mlir::Operation *accDataClauseOp)
Used to obtain bounds from an acc data clause operation.
Definition OpenACC.cpp:5266
llvm::StringLiteral getVarNamePlaceholder()
Returns a placeholder string for use as an acc.var_name attribute value when the actual variable name...
static constexpr StringLiteral getVarNameAttrName()
Definition OpenACC.h:215
std::pair< llvm::SmallVector< Value >, Block::iterator > cloneACCRegionInto(Region *src, Block *dest, Block::iterator inlinePoint, IRMapping &mapping, ValueRange resultsToReplace)
Clone an ACC region into a destination block at the given insertion point.
Include the generated interface declarations.
Value convertScalarToDtype(OpBuilder &b, Location loc, Value operand, Type toType, bool isUnsignedCast)
Converts a scalar value operand to type toType.
Definition Utils.cpp:241
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region &region)
Replace all uses of orig within the given region with replacement.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:139