MLIR 24.0.0git
InlinerInterfaceImpl.cpp
Go to the documentation of this file.
1//===- InlinerInterfaceImpl.cpp - Inlining for LLVM the dialect -----------===//
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// Logic for inlining LLVM functions and the definition of the
10// LLVMInliningInterface.
11//
12//===----------------------------------------------------------------------===//
13
17#include "mlir/IR/Matchers.h"
21#include "llvm/ADT/ScopeExit.h"
22#include "llvm/Support/Debug.h"
23
24#include "llvm/Support/DebugLog.h"
25
26#define DEBUG_TYPE "llvm-inliner"
27
28using namespace mlir;
29
30/// Check whether the given alloca is an input to a lifetime intrinsic,
31/// optionally passing through one or more casts on the way. This is not
32/// transitive through block arguments.
33static bool hasLifetimeMarkers(LLVM::AllocaOp allocaOp) {
34 SmallVector<Operation *> stack(allocaOp->getUsers().begin(),
35 allocaOp->getUsers().end());
36 while (!stack.empty()) {
37 Operation *op = stack.pop_back_val();
38 if (isa<LLVM::LifetimeStartOp, LLVM::LifetimeEndOp>(op))
39 return true;
40 if (isa<LLVM::BitcastOp>(op))
41 stack.append(op->getUsers().begin(), op->getUsers().end());
42 }
43 return false;
44}
45
46/// Handles alloca operations in the inlined blocks:
47/// - Moves all alloca operations with a constant size in the former entry block
48/// of the callee into the entry block of the caller, so they become part of
49/// the function prologue/epilogue during code generation.
50/// - Inserts lifetime intrinsics that limit the scope of inlined static allocas
51/// to the inlined blocks.
52/// - Inserts StackSave and StackRestore operations if dynamic allocas were
53/// inlined.
54static void
57 // Locate the entry block of the closest callsite ancestor that has either the
58 // IsolatedFromAbove or AutomaticAllocationScope trait. In pure LLVM dialect
59 // programs, this is the LLVMFuncOp containing the call site. However, in
60 // mixed-dialect programs, the callsite might be nested in another operation
61 // that carries one of these traits. In such scenarios, this traversal stops
62 // at the closest ancestor with either trait, ensuring visibility post
63 // relocation and respecting allocation scopes.
64 Block *callerEntryBlock = nullptr;
65 Operation *currentOp = call;
66 while (Operation *parentOp = currentOp->getParentOp()) {
67 if (parentOp->mightHaveTrait<OpTrait::IsIsolatedFromAbove>() ||
68 parentOp->mightHaveTrait<OpTrait::AutomaticAllocationScope>()) {
69 callerEntryBlock = &currentOp->getParentRegion()->front();
70 break;
71 }
72 currentOp = parentOp;
73 }
74
75 // Avoid relocating the alloca operations if the call has been inlined into
76 // the entry block already, which is typically the encompassing
77 // LLVM function, or if the relevant entry block cannot be identified.
78 Block *calleeEntryBlock = &(*inlinedBlocks.begin());
79 if (!callerEntryBlock || callerEntryBlock == calleeEntryBlock)
80 return;
81
83 bool shouldInsertLifetimes = false;
84 bool hasDynamicAlloca = false;
85 // Conservatively only move static alloca operations that are part of the
86 // entry block and do not inspect nested regions, since they may execute
87 // conditionally or have other unknown semantics.
88 for (auto allocaOp : calleeEntryBlock->getOps<LLVM::AllocaOp>()) {
89 IntegerAttr arraySize;
90 if (!matchPattern(allocaOp.getArraySize(), m_Constant(&arraySize))) {
91 hasDynamicAlloca = true;
92 continue;
93 }
94 bool shouldInsertLifetime =
95 arraySize.getValue() != 0 && !hasLifetimeMarkers(allocaOp);
96 shouldInsertLifetimes |= shouldInsertLifetime;
97 allocasToMove.emplace_back(allocaOp, arraySize, shouldInsertLifetime);
98 }
99 // Check the remaining inlined blocks for dynamic allocas as well.
100 for (Block &block : llvm::drop_begin(inlinedBlocks)) {
101 if (hasDynamicAlloca)
102 break;
103 hasDynamicAlloca =
104 llvm::any_of(block.getOps<LLVM::AllocaOp>(), [](auto allocaOp) {
105 return !matchPattern(allocaOp.getArraySize(), m_Constant());
106 });
107 }
108 if (allocasToMove.empty() && !hasDynamicAlloca)
109 return;
110 OpBuilder builder(calleeEntryBlock, calleeEntryBlock->begin());
111 Value stackPtr;
112 if (hasDynamicAlloca) {
113 // This may result in multiple stacksave/stackrestore intrinsics in the same
114 // scope if some are already present in the body of the caller. This is not
115 // invalid IR, but LLVM cleans these up in InstCombineCalls.cpp, along with
116 // other cases where the stacksave/stackrestore is redundant.
117 stackPtr = LLVM::StackSaveOp::create(
118 builder, call->getLoc(),
119 LLVM::LLVMPointerType::get(call->getContext()));
120 }
121 builder.setInsertionPointToStart(callerEntryBlock);
122 for (auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) {
123 auto newConstant =
124 LLVM::ConstantOp::create(builder, allocaOp->getLoc(),
125 allocaOp.getArraySize().getType(), arraySize);
126 // Insert a lifetime start intrinsic where the alloca was before moving it.
127 if (shouldInsertLifetime) {
128 OpBuilder::InsertionGuard insertionGuard(builder);
129 builder.setInsertionPoint(allocaOp);
130 LLVM::LifetimeStartOp::create(builder, allocaOp.getLoc(),
131 allocaOp.getResult());
132 }
133 allocaOp->moveAfter(newConstant);
134 allocaOp.getArraySizeMutable().assign(newConstant.getResult());
135 }
136 if (!shouldInsertLifetimes && !hasDynamicAlloca)
137 return;
138 // Insert a lifetime end intrinsic before each return in the callee function.
139 for (Block &block : inlinedBlocks) {
140 if (!block.getTerminator()->hasTrait<OpTrait::ReturnLike>())
141 continue;
142 builder.setInsertionPoint(block.getTerminator());
143 if (hasDynamicAlloca)
144 LLVM::StackRestoreOp::create(builder, call->getLoc(), stackPtr);
145 for (auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) {
146 if (shouldInsertLifetime)
147 LLVM::LifetimeEndOp::create(builder, allocaOp.getLoc(),
148 allocaOp.getResult());
149 }
150 }
151}
152
153/// Maps all alias scopes in the inlined operations to deep clones of the scopes
154/// and domain. This is required for code such as `foo(a, b); foo(a2, b2);` to
155/// not incorrectly return `noalias` for e.g. operations on `a` and `a2`.
156static void
159
160 // Register handles in the walker to create the deep clones.
161 // The walker ensures that an attribute is only ever walked once and does a
162 // post-order walk, ensuring the domain is visited prior to the scope.
163 AttrTypeWalker walker;
164
165 // Perform the deep clones while visiting. Builders create a distinct
166 // attribute to make sure that new instances are always created by the
167 // uniquer.
168 walker.addWalk([&](LLVM::AliasScopeDomainAttr domainAttr) {
169 mapping[domainAttr] = LLVM::AliasScopeDomainAttr::get(
170 domainAttr.getContext(), domainAttr.getDescription());
171 });
172
173 walker.addWalk([&](LLVM::AliasScopeAttr scopeAttr) {
174 mapping[scopeAttr] = LLVM::AliasScopeAttr::get(
175 cast<LLVM::AliasScopeDomainAttr>(mapping.lookup(scopeAttr.getDomain())),
176 scopeAttr.getDescription());
177 });
178
179 // Map an array of scopes to an array of deep clones.
180 auto convertScopeList = [&](ArrayAttr arrayAttr) -> ArrayAttr {
181 if (!arrayAttr)
182 return nullptr;
183
184 // Create the deep clones if necessary.
185 walker.walk(arrayAttr);
186
187 return ArrayAttr::get(arrayAttr.getContext(),
188 llvm::map_to_vector(arrayAttr, [&](Attribute attr) {
189 return mapping.lookup(attr);
190 }));
191 };
192
193 for (Block &block : inlinedBlocks) {
194 block.walk([&](Operation *op) {
195 if (auto aliasInterface = dyn_cast<LLVM::AliasAnalysisOpInterface>(op)) {
196 aliasInterface.setAliasScopes(
197 convertScopeList(aliasInterface.getAliasScopesOrNull()));
198 aliasInterface.setNoAliasScopes(
199 convertScopeList(aliasInterface.getNoAliasScopesOrNull()));
200 }
201
202 if (auto noAliasScope = dyn_cast<LLVM::NoAliasScopeDeclOp>(op)) {
203 // Create the deep clones if necessary.
204 walker.walk(noAliasScope.getScopeAttr());
205
206 noAliasScope.setScopeAttr(cast<LLVM::AliasScopeAttr>(
207 mapping.lookup(noAliasScope.getScopeAttr())));
208 }
209 });
210 }
211}
212
213/// Creates a new ArrayAttr by concatenating `lhs` with `rhs`.
214/// Returns null if both parameters are null. If only one attribute is null,
215/// return the other.
217 if (!lhs)
218 return rhs;
219 if (!rhs)
220 return lhs;
221
223 llvm::append_range(result, lhs);
224 llvm::append_range(result, rhs);
225 return ArrayAttr::get(lhs.getContext(), result);
226}
227
228/// Attempts to return the set of all underlying pointer values that
229/// `pointerValue` is based on. This function traverses through select
230/// operations and block arguments.
231static FailureOr<SmallVector<Value>>
234 WalkContinuation walkResult = walkSlice(pointerValue, [&](Value val) {
235 // Attempt to advance to the source of the underlying view-like operation.
236 // Examples of view-like operations include GEPOp and AddrSpaceCastOp.
237 if (auto viewOp = val.getDefiningOp<ViewLikeOpInterface>()) {
238 if (val == viewOp.getViewDest())
239 return WalkContinuation::advanceTo(viewOp.getViewSource());
240 }
241
242 // Attempt to advance to control flow predecessors.
243 std::optional<SmallVector<Value>> controlFlowPredecessors =
245 if (controlFlowPredecessors)
246 return WalkContinuation::advanceTo(*controlFlowPredecessors);
247
248 // For all non-control flow results, consider `val` an underlying object.
249 if (isa<OpResult>(val)) {
250 result.push_back(val);
251 return WalkContinuation::skip();
252 }
253
254 // If this place is reached, `val` is a block argument that is not
255 // understood. Therefore, we conservatively interrupt.
256 // Note: Dealing with function arguments is not necessary, as the slice
257 // would have to go through an SSACopyOp first.
259 });
260
261 if (walkResult.wasInterrupted())
262 return failure();
263
264 return result;
265}
266
267/// Creates a new AliasScopeAttr for every noalias parameter and attaches it to
268/// the appropriate inlined memory operations in an attempt to preserve the
269/// original semantics of the parameter attribute.
271 Operation *call, iterator_range<Region::iterator> inlinedBlocks) {
272
273 // First, collect all ssa copy operations, which correspond to function
274 // parameters, and additionally store the noalias parameters. All parameters
275 // have been marked by the `handleArgument` implementation by using the
276 // `ssa.copy` intrinsic. Additionally, noalias parameters have an attached
277 // `noalias` attribute to the intrinsics. These intrinsics are only meant to
278 // be temporary and should therefore be deleted after we're done using them
279 // here.
281 SetVector<LLVM::SSACopyOp> noAliasParams;
282 for (Value argument : cast<LLVM::CallOp>(call).getArgOperands()) {
283 for (auto ssaCopy :
284 llvm::make_isa_range<LLVM::SSACopyOp>(argument.getUsers())) {
285 ssaCopies.insert(ssaCopy);
286
287 if (!ssaCopy->hasDiscardableAttr(LLVM::LLVMDialect::getNoAliasAttrName()))
288 continue;
289 noAliasParams.insert(ssaCopy);
290 }
291 }
292
293 // Scope exit block to make it impossible to forget to get rid of the
294 // intrinsics.
295 llvm::scope_exit exit([&] {
296 for (LLVM::SSACopyOp ssaCopyOp : ssaCopies) {
297 ssaCopyOp.replaceAllUsesWith(ssaCopyOp.getOperand());
298 ssaCopyOp->erase();
299 }
300 });
301
302 // If there were no noalias parameters, we have nothing to do here.
303 if (noAliasParams.empty())
304 return;
305
306 // Create a new domain for this specific inlining and a new scope for every
307 // noalias parameter.
308 auto functionDomain = LLVM::AliasScopeDomainAttr::get(
309 call->getContext(), cast<LLVM::CallOp>(call).getCalleeAttr().getAttr());
311 for (LLVM::SSACopyOp copyOp : noAliasParams) {
312 auto scope = LLVM::AliasScopeAttr::get(functionDomain);
313 pointerScopes[copyOp] = scope;
314
315 auto builder = OpBuilder(call);
316 LLVM::NoAliasScopeDeclOp::create(builder, call->getLoc(), scope);
317 }
318
319 // Go through every instruction and attempt to find which noalias parameters
320 // it is definitely based on and definitely not based on.
321 for (Block &inlinedBlock : inlinedBlocks) {
322 inlinedBlock.walk([&](LLVM::AliasAnalysisOpInterface aliasInterface) {
323 // Collect the pointer arguments affected by the alias scopes.
324 SmallVector<Value> pointerArgs = aliasInterface.getAccessedOperands();
325
326 // Find the set of underlying pointers that this pointer is based on.
327 SmallPtrSet<Value, 4> basedOnPointers;
328 for (Value pointer : pointerArgs) {
329 FailureOr<SmallVector<Value>> underlyingObjectSet =
330 getUnderlyingObjectSet(pointer);
331 if (failed(underlyingObjectSet))
332 return;
333 llvm::copy(*underlyingObjectSet,
334 std::inserter(basedOnPointers, basedOnPointers.begin()));
335 }
336
337 bool aliasesOtherKnownObject = false;
338 // Go through the based on pointers and check that they are either:
339 // * Constants that can be ignored (undef, poison, null pointer).
340 // * Based on a pointer parameter.
341 // * Other pointers that we know can't alias with our noalias parameter.
342 //
343 // Any other value might be a pointer based on any noalias parameter that
344 // hasn't been identified. In that case conservatively don't add any
345 // scopes to this operation indicating either aliasing or not aliasing
346 // with any parameter.
347 if (llvm::any_of(basedOnPointers, [&](Value object) {
348 if (matchPattern(object, m_Constant()))
349 return false;
350
351 if (auto ssaCopy = object.getDefiningOp<LLVM::SSACopyOp>()) {
352 // If that value is based on a noalias parameter, it is guaranteed
353 // to not alias with any other object.
354 aliasesOtherKnownObject |= !noAliasParams.contains(ssaCopy);
355 return false;
356 }
357
358 if (isa_and_nonnull<LLVM::AllocaOp, LLVM::AddressOfOp>(
359 object.getDefiningOp())) {
360 aliasesOtherKnownObject = true;
361 return false;
362 }
363 return true;
364 }))
365 return;
366
367 // Add all noalias parameter scopes to the noalias scope list that we are
368 // not based on.
369 SmallVector<Attribute> noAliasScopes;
370 for (LLVM::SSACopyOp noAlias : noAliasParams) {
371 if (basedOnPointers.contains(noAlias))
372 continue;
373
374 noAliasScopes.push_back(pointerScopes[noAlias]);
375 }
376
377 if (!noAliasScopes.empty())
378 aliasInterface.setNoAliasScopes(
379 concatArrayAttr(aliasInterface.getNoAliasScopesOrNull(),
380 ArrayAttr::get(call->getContext(), noAliasScopes)));
381
382 // Don't add alias scopes to call operations or operations that might
383 // operate on pointers not based on any noalias parameter.
384 // Since we add all scopes to an operation's noalias list that it
385 // definitely doesn't alias, we mustn't do the same for the alias.scope
386 // list if other objects are involved.
387 //
388 // Consider the following case:
389 // %0 = llvm.alloca
390 // %1 = select %magic, %0, %noalias_param
391 // store 5, %1 (1) noalias=[scope(...)]
392 // ...
393 // store 3, %0 (2) noalias=[scope(noalias_param), scope(...)]
394 //
395 // We can add the scopes of any noalias parameters that aren't
396 // noalias_param's scope to (1) and add all of them to (2). We mustn't add
397 // the scope of noalias_param to the alias.scope list of (1) since
398 // that would mean (2) cannot alias with (1) which is wrong since both may
399 // store to %0.
400 //
401 // In conclusion, only add scopes to the alias.scope list if all pointers
402 // have a corresponding scope.
403 // Call operations are included in this list since we do not know whether
404 // the callee accesses any memory besides the ones passed as its
405 // arguments.
406 if (aliasesOtherKnownObject ||
407 isa<LLVM::CallOp>(aliasInterface.getOperation()))
408 return;
409
410 SmallVector<Attribute> aliasScopes;
411 for (LLVM::SSACopyOp noAlias : noAliasParams)
412 if (basedOnPointers.contains(noAlias))
413 aliasScopes.push_back(pointerScopes[noAlias]);
414
415 if (!aliasScopes.empty())
416 aliasInterface.setAliasScopes(
417 concatArrayAttr(aliasInterface.getAliasScopesOrNull(),
418 ArrayAttr::get(call->getContext(), aliasScopes)));
419 });
420 }
421}
422
423/// Appends any alias scopes of the call operation to any inlined memory
424/// operation.
425static void
427 iterator_range<Region::iterator> inlinedBlocks) {
428 auto callAliasInterface = dyn_cast<LLVM::AliasAnalysisOpInterface>(call);
429 if (!callAliasInterface)
430 return;
431
432 ArrayAttr aliasScopes = callAliasInterface.getAliasScopesOrNull();
433 ArrayAttr noAliasScopes = callAliasInterface.getNoAliasScopesOrNull();
434 // If the call has neither alias scopes or noalias scopes we have nothing to
435 // do here.
436 if (!aliasScopes && !noAliasScopes)
437 return;
438
439 // Simply append the call op's alias and noalias scopes to any operation
440 // implementing AliasAnalysisOpInterface.
441 for (Block &block : inlinedBlocks) {
442 block.walk([&](LLVM::AliasAnalysisOpInterface aliasInterface) {
443 if (aliasScopes)
444 aliasInterface.setAliasScopes(concatArrayAttr(
445 aliasInterface.getAliasScopesOrNull(), aliasScopes));
446
447 if (noAliasScopes)
448 aliasInterface.setNoAliasScopes(concatArrayAttr(
449 aliasInterface.getNoAliasScopesOrNull(), noAliasScopes));
450 });
451 }
452}
453
454/// Handles all interactions with alias scopes during inlining.
455static void handleAliasScopes(Operation *call,
456 iterator_range<Region::iterator> inlinedBlocks) {
457 deepCloneAliasScopes(inlinedBlocks);
458 createNewAliasScopesFromNoAliasParameter(call, inlinedBlocks);
459 appendCallOpAliasScopes(call, inlinedBlocks);
460}
461
462/// Appends any access groups of the call operation to any inlined memory
463/// operation.
465 iterator_range<Region::iterator> inlinedBlocks) {
466 auto callAccessGroupInterface = dyn_cast<LLVM::AccessGroupOpInterface>(call);
467 if (!callAccessGroupInterface)
468 return;
469
470 auto accessGroups = callAccessGroupInterface.getAccessGroupsOrNull();
471 if (!accessGroups)
472 return;
473
474 // Simply append the call op's access groups to any operation implementing
475 // AccessGroupOpInterface.
476 for (Block &block : inlinedBlocks)
477 for (auto accessGroupOpInterface :
478 block.getOps<LLVM::AccessGroupOpInterface>())
479 accessGroupOpInterface.setAccessGroups(concatArrayAttr(
480 accessGroupOpInterface.getAccessGroupsOrNull(), accessGroups));
481}
482
483/// Whether the two call stacks agree up to the shorter length, matching
484/// LLVM's haveCommonPrefix: a MemProf context matches the new inlined
485/// callsite context when they share a prefix, even if the callsite stack
486/// is longer than the MemProf stack.
488 ArrayRef<int64_t> callsiteStack) {
489 size_t len = std::min(mibStack.size(), callsiteStack.size());
490 return mibStack.take_front(len) == callsiteStack.take_front(len);
491}
492
493/// Updates MemProf metadata on calls inlined into `call`, matching LLVM's
494/// propagateMemProfMetadata. Each cloned call's `llvm.callsite` stack is
495/// concatenated with the inlined callsite's stack, and its `llvm.memprof`
496/// MIBs are filtered to the ones whose call stack shares a prefix with the
497/// new callsite stack; the kept MIBs are unchanged. If the inlined call
498/// has no callsite metadata, or no MIB matches, both attributes are removed
499/// from the cloned call. A cloned call with `llvm.memprof` but no
500/// `llvm.callsite` cannot have its context extended, so its `llvm.memprof`
501/// attribute is removed.
502///
503/// Known gap: unlike LLVM, which rebuilds a filtered subset through a
504/// call-stack trie (trimming contexts, and replacing the metadata with a
505/// function attribute when a single allocation type is left), this keeps
506/// the raw subset. There is no MLIR way to carry that function attribute.
507static void
509 iterator_range<Region::iterator> inlinedBlocks) {
510 MLIRContext *context = call->getContext();
511 StringRef callsiteName = LLVM::LLVMDialect::getCallsiteAttrName();
512 StringRef memprofName = LLVM::LLVMDialect::getMemProfAttrName();
513 DenseI64ArrayAttr inlinedCallsite =
514 call->getDiscardableAttrOfType<DenseI64ArrayAttr>(callsiteName);
515 ArrayRef<int64_t> inlinedStack =
516 inlinedCallsite ? inlinedCallsite.asArrayRef() : ArrayRef<int64_t>{};
517
518 // Any op carrying these attributes lowers to a call, so act on every
519 // carrier, including calls nested in region ops and intrinsic calls that
520 // are not CallOpInterface, matching the set translation can produce.
521 for (Block &block : inlinedBlocks) {
522 block.walk([&](Operation *op) {
523 DenseI64ArrayAttr origCallsite =
525 ArrayAttr memprof = op->getDiscardableAttrOfType<ArrayAttr>(memprofName);
526 if (!origCallsite && !memprof)
527 return;
528 if (!origCallsite) {
529 // No callsite context to extend from: drop the memprof attribute
530 // when the clone carries one. LLVM asserts this case cannot happen;
531 // dropping is the conservative equivalent.
532 op->removeDiscardableAttr(memprofName);
533 return;
534 }
535 if (inlinedStack.empty()) {
536 // The inlined call carries no callsite context: the cloned call is
537 // not part of any profiled context.
538 op->removeDiscardableAttr(memprofName);
539 op->removeDiscardableAttr(callsiteName);
540 return;
541 }
542 SmallVector<int64_t> newStack(origCallsite.asArrayRef());
543 newStack.append(inlinedStack.begin(), inlinedStack.end());
544 op->setDiscardableAttr(callsiteName,
545 DenseI64ArrayAttr::get(context, newStack));
546 if (!memprof)
547 return;
548 SmallVector<Attribute> keptMIBs;
549 bool malformed = false;
550 for (Attribute mibAttr : memprof) {
551 auto mib = dyn_cast<LLVM::MemProfMIBAttr>(mibAttr);
552 if (!mib) {
553 malformed = true;
554 break;
555 }
556 if (haveCommonPrefix(mib.getCallStack().asArrayRef(), newStack))
557 keptMIBs.push_back(mibAttr);
558 }
559 if (malformed || keptMIBs.empty()) {
560 op->removeDiscardableAttr(memprofName);
561 op->removeDiscardableAttr(callsiteName);
562 } else if (keptMIBs.size() != memprof.size()) {
563 op->setDiscardableAttr(memprofName, ArrayAttr::get(context, keptMIBs));
564 }
565 });
566 }
567}
568
569/// Updates locations inside loop annotations to reflect that they were inlined.
570static void
572 iterator_range<Region::iterator> inlinedBlocks) {
573 // Attempt to extract a DISubprogram from the callee.
574 auto func = call->getParentOfType<FunctionOpInterface>();
575 if (!func)
576 return;
577 LocationAttr funcLoc = func->getLoc();
578 auto fusedLoc = dyn_cast_if_present<FusedLoc>(funcLoc);
579 if (!fusedLoc)
580 return;
581 auto scope =
582 dyn_cast_if_present<LLVM::DISubprogramAttr>(fusedLoc.getMetadata());
583 if (!scope)
584 return;
585
586 // Helper to build a new fused location that reflects the inlining of the loop
587 // annotation.
588 auto updateLoc = [&](FusedLoc loc) -> FusedLoc {
589 if (!loc)
590 return {};
591 Location callSiteLoc = CallSiteLoc::get(loc, call->getLoc());
592 return FusedLoc::get(loc.getContext(), callSiteLoc, scope);
593 };
594
595 AttrTypeReplacer replacer;
596 replacer.addReplacement([&](LLVM::LoopAnnotationAttr loopAnnotation)
597 -> std::pair<Attribute, WalkResult> {
598 FusedLoc newStartLoc = updateLoc(loopAnnotation.getStartLoc());
599 FusedLoc newEndLoc = updateLoc(loopAnnotation.getEndLoc());
600 if (!newStartLoc && !newEndLoc)
601 return {loopAnnotation, WalkResult::advance()};
602 auto newLoopAnnotation = LLVM::LoopAnnotationAttr::get(
603 loopAnnotation.getContext(), loopAnnotation.getId(),
604 loopAnnotation.getDisableNonforced(), loopAnnotation.getVectorize(),
605 loopAnnotation.getInterleave(), loopAnnotation.getUnroll(),
606 loopAnnotation.getUnrollAndJam(), loopAnnotation.getLicm(),
607 loopAnnotation.getDistribute(), loopAnnotation.getPipeline(),
608 loopAnnotation.getPeeled(), loopAnnotation.getUnswitch(),
609 loopAnnotation.getMustProgress(), loopAnnotation.getIsVectorized(),
610 loopAnnotation.getAlign(), newStartLoc, newEndLoc,
611 loopAnnotation.getParallelAccesses());
612 // Needs to advance, as loop annotations can be nested.
613 return {newLoopAnnotation, WalkResult::advance()};
614 });
615
616 for (Block &block : inlinedBlocks)
617 for (Operation &op : block)
618 replacer.recursivelyReplaceElementsIn(&op);
619}
620
621/// If `requestedAlignment` is higher than the alignment specified on `alloca`,
622/// realigns `alloca` if this does not exceed the natural stack alignment.
623/// Returns the post-alignment of `alloca`, whether it was realigned or not.
624static uint64_t tryToEnforceAllocaAlignment(LLVM::AllocaOp alloca,
625 uint64_t requestedAlignment,
626 DataLayout const &dataLayout) {
627 uint64_t allocaAlignment = alloca.getAlignment().value_or(1);
628 if (requestedAlignment <= allocaAlignment)
629 // No realignment necessary.
630 return allocaAlignment;
631 uint64_t naturalStackAlignmentBits = dataLayout.getStackAlignment();
632 // If the natural stack alignment is not specified, the data layout returns
633 // zero. Optimistically allow realignment in this case.
634 if (naturalStackAlignmentBits == 0 ||
635 // If the requested alignment exceeds the natural stack alignment, this
636 // will trigger a dynamic stack realignment, so we prefer to copy...
637 8 * requestedAlignment <= naturalStackAlignmentBits ||
638 // ...unless the alloca already triggers dynamic stack realignment. Then
639 // we might as well further increase the alignment to avoid a copy.
640 8 * allocaAlignment > naturalStackAlignmentBits) {
641 alloca.setAlignment(requestedAlignment);
642 allocaAlignment = requestedAlignment;
643 }
644 return allocaAlignment;
645}
646
647/// Tries to find and return the alignment of the pointer `value` by looking for
648/// an alignment attribute on the defining allocation op or function argument.
649/// If the found alignment is lower than `requestedAlignment`, tries to realign
650/// the pointer, then returns the resulting post-alignment, regardless of
651/// whether it was realigned or not. If no existing alignment attribute is
652/// found, returns 1 (i.e., assume that no alignment is guaranteed).
653static uint64_t tryToEnforceAlignment(Value value, uint64_t requestedAlignment,
654 DataLayout const &dataLayout) {
655 if (Operation *definingOp = value.getDefiningOp()) {
656 if (auto alloca = dyn_cast<LLVM::AllocaOp>(definingOp))
657 return tryToEnforceAllocaAlignment(alloca, requestedAlignment,
658 dataLayout);
659 if (auto addressOf = dyn_cast<LLVM::AddressOfOp>(definingOp))
661 definingOp, addressOf.getGlobalNameAttr()))
662 return global.getAlignment().value_or(1);
663 // We don't currently handle this operation; assume no alignment.
664 return 1;
665 }
666 // Since there is no defining op, this is a block argument. Probably this
667 // comes directly from a function argument, so check that this is the case.
668 Operation *parentOp = value.getParentBlock()->getParentOp();
669 if (auto func = dyn_cast<LLVM::LLVMFuncOp>(parentOp)) {
670 // Use the alignment attribute set for this argument in the parent function
671 // if it has been set.
672 auto blockArg = llvm::cast<BlockArgument>(value);
673 if (Attribute alignAttr = func.getArgAttr(
674 blockArg.getArgNumber(), LLVM::LLVMDialect::getAlignAttrName()))
675 return cast<IntegerAttr>(alignAttr).getValue().getLimitedValue();
676 }
677 // We didn't find anything useful; assume no alignment.
678 return 1;
679}
680
681/// Introduces a new alloca and copies the memory pointed to by `argument` to
682/// the address of the new alloca, then returns the value of the new alloca.
684 Value argument, Type elementType,
685 uint64_t elementTypeSize,
686 uint64_t targetAlignment) {
687 // Allocate the new value on the stack.
688 Value allocaOp;
689 {
690 // Walk up from the call site to find the innermost AutomaticAllocationScope
691 // (e.g. an llvm.func or scf.forall). Placing the alloca at the entry block
692 // of that scope keeps it inside parallel regions rather than hoisting it
693 // out, while still landing at the function entry block for the common
694 // non-parallel case.
695 OpBuilder::InsertionGuard insertionGuard(builder);
696 Operation *scope = builder.getInsertionBlock()->getParentOp();
699 Block *entryBlock = &scope->getRegion(0).front();
700 builder.setInsertionPointToStart(entryBlock);
701 Value one = LLVM::ConstantOp::create(builder, loc, builder.getI64Type(),
702 builder.getI64IntegerAttr(1));
703 allocaOp = LLVM::AllocaOp::create(builder, loc, argument.getType(),
704 elementType, one, targetAlignment);
705 }
706 // Copy the pointee to the newly allocated value.
707 Value copySize =
708 LLVM::ConstantOp::create(builder, loc, builder.getI64Type(),
709 builder.getI64IntegerAttr(elementTypeSize));
710 // Preserve the alignment of the destination (alloca) in the memcpy's
711 // arg_attrs.
712 NamedAttribute dstAlignAttr =
713 builder.getNamedAttr(LLVM::LLVMDialect::getAlignAttrName(),
714 builder.getI64IntegerAttr(targetAlignment));
715 ArrayAttr argAttrs =
716 builder.getArrayAttr({builder.getDictionaryAttr({dstAlignAttr})});
717 LLVM::MemcpyOp::create(builder, loc, allocaOp, argument, copySize,
718 /*isVolatile=*/false,
719 /*tbaa_struct=*/nullptr,
720 /*access_groups=*/nullptr, /*alias_scopes=*/nullptr,
721 /*noalias_scopes=*/nullptr, /*tbaa=*/nullptr, argAttrs,
722 /*res_attrs=*/nullptr);
723 return allocaOp;
724}
725
726/// Handles a function argument marked with the byval attribute by introducing a
727/// memcpy or realigning the defining operation, if required either due to the
728/// pointee being writeable in the callee, and/or due to an alignment mismatch.
729/// `requestedAlignment` specifies the alignment set in the "align" argument
730/// attribute (or 1 if no align attribute was set).
731static Value handleByValArgument(OpBuilder &builder, Operation *callable,
732 Value argument, Type elementType,
733 uint64_t requestedAlignment) {
734 auto func = cast<LLVM::LLVMFuncOp>(callable);
735 LLVM::MemoryEffectsAttr memoryEffects = func.getMemoryEffectsAttr();
736 // If there is no memory effects attribute, assume that the function is
737 // not read-only.
738 bool isReadOnly = memoryEffects &&
739 memoryEffects.getArgMem() != LLVM::ModRefInfo::ModRef &&
740 memoryEffects.getArgMem() != LLVM::ModRefInfo::Mod;
741 // Check if there's an alignment mismatch requiring us to copy.
742 DataLayout dataLayout = DataLayout::closest(callable);
743 uint64_t minimumAlignment = dataLayout.getTypeABIAlignment(elementType);
744 if (isReadOnly) {
745 if (requestedAlignment <= minimumAlignment)
746 return argument;
747 uint64_t currentAlignment =
748 tryToEnforceAlignment(argument, requestedAlignment, dataLayout);
749 if (currentAlignment >= requestedAlignment)
750 return argument;
751 }
752 uint64_t targetAlignment = std::max(requestedAlignment, minimumAlignment);
754 builder, argument.getLoc(), argument, elementType,
755 dataLayout.getTypeSize(elementType), targetAlignment);
756}
757
758namespace {
759struct LLVMInlinerInterface : public DialectInlinerInterface {
760 using DialectInlinerInterface::DialectInlinerInterface;
761
762 LLVMInlinerInterface(Dialect *dialect)
763 : DialectInlinerInterface(dialect),
764 // Cache set of StringAttrs for fast lookup in `isLegalToInline`.
765 disallowedFunctionAttrs({
766 StringAttr::get(dialect->getContext(), "noduplicate"),
767 StringAttr::get(dialect->getContext(), "presplitcoroutine"),
768 StringAttr::get(dialect->getContext(), "returns_twice"),
769 StringAttr::get(dialect->getContext(), "strictfp"),
770 }) {}
771
772 bool isLegalToInline(Operation *call, Operation *callable,
773 bool wouldBeCloned) const final {
774 auto callOp = dyn_cast<LLVM::CallOp>(call);
775 if (!callOp) {
776 LDBG() << "Cannot inline: call is not an '"
777 << LLVM::CallOp::getOperationName() << "' op";
778 return false;
779 }
780 if (callOp.getNoInline()) {
781 LDBG() << "Cannot inline: call is marked no_inline";
782 return false;
783 }
784 auto funcOp = dyn_cast<LLVM::LLVMFuncOp>(callable);
785 if (!funcOp) {
786 LDBG() << "Cannot inline: callable is not an '"
787 << LLVM::LLVMFuncOp::getOperationName() << "' op";
788 return false;
789 }
790 if (funcOp.isNoInline()) {
791 LDBG() << "Cannot inline: function is marked no_inline";
792 return false;
793 }
794 if (funcOp.isVarArg()) {
795 LDBG() << "Cannot inline: callable is variadic";
796 return false;
797 }
798 // TODO: Generate aliasing metadata from noalias result attributes.
799 if (auto attrs = funcOp.getArgAttrs()) {
800 for (DictionaryAttr attrDict : attrs->getAsRange<DictionaryAttr>()) {
801 if (attrDict.contains(LLVM::LLVMDialect::getInAllocaAttrName())) {
802 LDBG() << "Cannot inline " << funcOp.getSymName()
803 << ": inalloca arguments not supported";
804 return false;
805 }
806 }
807 }
808 // TODO: Handle exceptions.
809 if (funcOp.getPersonality()) {
810 LDBG() << "Cannot inline " << funcOp.getSymName()
811 << ": unhandled function personality";
812 return false;
813 }
814 if (funcOp.getPassthrough()) {
815 // TODO: Used attributes should not be passthrough.
816 if (llvm::any_of(*funcOp.getPassthrough(), [&](Attribute attr) {
817 auto stringAttr = dyn_cast<StringAttr>(attr);
818 if (!stringAttr)
819 return false;
820 if (disallowedFunctionAttrs.contains(stringAttr)) {
821 LDBG() << "Cannot inline " << funcOp.getSymName()
822 << ": found disallowed function attribute " << stringAttr;
823 return true;
824 }
825 return false;
826 }))
827 return false;
828 }
829 // Refuse to inline if any block in the callee ends with an op that does
830 // not have the terminator trait. The MLIR verifier conservatively accepts
831 // unregistered ops as potential terminators (via mightHaveTrait), but
832 // handleTerminator uses cast<LLVM::ReturnOp> in the single-block path and
833 // would crash on such ops. Registered terminators from other dialects
834 // (e.g. cf.br) are safe: the multi-block path uses dyn_cast and skips
835 // non-llvm.return ops gracefully.
836 for (Block &block : funcOp.getBody()) {
837 if (!block.empty() && !block.back().hasTrait<OpTrait::IsTerminator>()) {
838 LDBG() << "Cannot inline " << funcOp.getSymName()
839 << ": block ends with non-terminator op";
840 return false;
841 }
842 }
843 return true;
844 }
845
846 bool isLegalToInline(Region *, Region *, bool, IRMapping &) const final {
847 return true;
848 }
849
850 bool isLegalToInline(Operation *op, Region *, bool, IRMapping &) const final {
851 // The inliner cannot handle variadic function arguments and blocktag
852 // operations prevent inlining since they the blockaddress operations
853 // reference them via the callee symbol.
854 return !(isa<LLVM::VaStartOp>(op) || isa<LLVM::BlockTagOp>(op));
855 }
856
857 /// Handle the given inlined return by replacing it with a branch. This
858 /// overload is called when the inlined region has more than one block.
859 void handleTerminator(Operation *op, Block *newDest) const final {
860 // Only return needs to be handled here.
861 auto returnOp = dyn_cast<LLVM::ReturnOp>(op);
862 if (!returnOp)
863 return;
864
865 // Replace the return with a branch to the dest.
866 OpBuilder builder(op);
867 LLVM::BrOp::create(builder, op->getLoc(), returnOp.getOperands(), newDest);
868 op->erase();
869 }
870
871 bool allowSingleBlockOptimization(
872 iterator_range<Region::iterator> inlinedBlocks) const final {
873 return !(!inlinedBlocks.empty() &&
874 isa<LLVM::UnreachableOp>(inlinedBlocks.begin()->getTerminator()));
875 }
876
877 /// Handle the given inlined return by replacing the uses of the call with the
878 /// operands of the return. This overload is called when the inlined region
879 /// only contains one block.
880 void handleTerminator(Operation *op, ValueRange valuesToRepl) const final {
881 // Return will be the only terminator present.
882 auto returnOp = cast<LLVM::ReturnOp>(op);
883
884 // Replace the values directly with the return operands.
885 assert(returnOp.getNumOperands() == valuesToRepl.size());
886 for (auto [dst, src] : llvm::zip(valuesToRepl, returnOp.getOperands()))
887 dst.replaceAllUsesWith(src);
888 }
889
890 Value handleArgument(OpBuilder &builder, Operation *call, Operation *callable,
891 Value argument,
892 DictionaryAttr argumentAttrs) const final {
893 if (std::optional<NamedAttribute> attr =
894 argumentAttrs.getNamed(LLVM::LLVMDialect::getByValAttrName())) {
895 Type elementType = cast<TypeAttr>(attr->getValue()).getValue();
896 uint64_t requestedAlignment = 1;
897 if (std::optional<NamedAttribute> alignAttr =
898 argumentAttrs.getNamed(LLVM::LLVMDialect::getAlignAttrName())) {
899 requestedAlignment = cast<IntegerAttr>(alignAttr->getValue())
900 .getValue()
901 .getLimitedValue();
902 }
903 return handleByValArgument(builder, callable, argument, elementType,
904 requestedAlignment);
905 }
906
907 // This code is essentially a workaround for deficiencies in the inliner
908 // interface: We need to transform operations *after* inlined based on the
909 // argument attributes of the parameters *before* inlining. This method runs
910 // prior to actual inlining and thus cannot transform the post-inlining
911 // code, while `processInlinedCallBlocks` does not have access to
912 // pre-inlining function arguments. Additionally, it is required to
913 // distinguish which parameter an SSA value originally came from. As a
914 // workaround until this is changed: Create an ssa.copy intrinsic with the
915 // noalias attribute (when it was present before) that can easily be found,
916 // and is extremely unlikely to exist in the code prior to inlining, using
917 // this to communicate between this method and `processInlinedCallBlocks`.
918 // TODO: Fix this by refactoring the inliner interface.
919 auto copyOp = LLVM::SSACopyOp::create(builder, call->getLoc(), argument);
920 if (argumentAttrs.contains(LLVM::LLVMDialect::getNoAliasAttrName()))
921 copyOp->setDiscardableAttr(
922 builder.getStringAttr(LLVM::LLVMDialect::getNoAliasAttrName()),
923 builder.getUnitAttr());
924 return copyOp;
925 }
926
927 void processInlinedCallBlocks(
928 Operation *call,
929 iterator_range<Region::iterator> inlinedBlocks) const override {
930 handleInlinedAllocas(call, inlinedBlocks);
931 handleAliasScopes(call, inlinedBlocks);
932 handleAccessGroups(call, inlinedBlocks);
933 handleMemProfMetadata(call, inlinedBlocks);
934 handleLoopAnnotations(call, inlinedBlocks);
935 }
936
937 // Keeping this (immutable) state on the interface allows us to look up
938 // StringAttrs instead of looking up strings, since StringAttrs are bound to
939 // the current context and thus cannot be initialized as static fields.
940 const DenseSet<StringAttr> disallowedFunctionAttrs;
941};
942
943} // end anonymous namespace
944
946 registry.addExtension(+[](MLIRContext *ctx, LLVM::LLVMDialect *dialect) {
947 dialect->addInterfaces<LLVMInlinerInterface>();
948 });
949}
static bool hasLifetimeMarkers(LLVM::AllocaOp allocaOp)
Check whether the given alloca is an input to a lifetime intrinsic, optionally passing through one or...
static void handleMemProfMetadata(Operation *call, iterator_range< Region::iterator > inlinedBlocks)
Updates MemProf metadata on calls inlined into call, matching LLVM's propagateMemProfMetadata.
static void appendCallOpAliasScopes(Operation *call, iterator_range< Region::iterator > inlinedBlocks)
Appends any alias scopes of the call operation to any inlined memory operation.
static void handleLoopAnnotations(Operation *call, iterator_range< Region::iterator > inlinedBlocks)
Updates locations inside loop annotations to reflect that they were inlined.
static ArrayAttr concatArrayAttr(ArrayAttr lhs, ArrayAttr rhs)
Creates a new ArrayAttr by concatenating lhs with rhs.
static void createNewAliasScopesFromNoAliasParameter(Operation *call, iterator_range< Region::iterator > inlinedBlocks)
Creates a new AliasScopeAttr for every noalias parameter and attaches it to the appropriate inlined m...
static FailureOr< SmallVector< Value > > getUnderlyingObjectSet(Value pointerValue)
Attempts to return the set of all underlying pointer values that pointerValue is based on.
static void handleAccessGroups(Operation *call, iterator_range< Region::iterator > inlinedBlocks)
Appends any access groups of the call operation to any inlined memory operation.
static Value handleByValArgument(OpBuilder &builder, Operation *callable, Value argument, Type elementType, uint64_t requestedAlignment)
Handles a function argument marked with the byval attribute by introducing a memcpy or realigning the...
static void handleAliasScopes(Operation *call, iterator_range< Region::iterator > inlinedBlocks)
Handles all interactions with alias scopes during inlining.
static uint64_t tryToEnforceAllocaAlignment(LLVM::AllocaOp alloca, uint64_t requestedAlignment, DataLayout const &dataLayout)
If requestedAlignment is higher than the alignment specified on alloca, realigns alloca if this does ...
static bool haveCommonPrefix(ArrayRef< int64_t > mibStack, ArrayRef< int64_t > callsiteStack)
Whether the two call stacks agree up to the shorter length, matching LLVM's haveCommonPrefix: a MemPr...
static uint64_t tryToEnforceAlignment(Value value, uint64_t requestedAlignment, DataLayout const &dataLayout)
Tries to find and return the alignment of the pointer value by looking for an alignment attribute on ...
static void deepCloneAliasScopes(iterator_range< Region::iterator > inlinedBlocks)
Maps all alias scopes in the inlined operations to deep clones of the scopes and domain.
static Value handleByValArgumentInit(OpBuilder &builder, Location loc, Value argument, Type elementType, uint64_t elementTypeSize, uint64_t targetAlignment)
Introduces a new alloca and copies the memory pointed to by argument to the address of the new alloca...
static void handleInlinedAllocas(Operation *call, iterator_range< Region::iterator > inlinedBlocks)
Handles alloca operations in the inlined blocks:
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
ArrayAttr()
This is an attribute/type replacer that is naively cached.
void addWalk(WalkFn< Attribute > &&fn)
Register a walk function for a given attribute or type.
WalkResult walk(T element)
Walk the given attribute/type, and recursively walk any sub elements.
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:34
iterator begin()
Definition Block.h:168
auto getOps()
Return an iterator range over the operations within this block that are of 'OpT'.
Definition Block.h:213
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
IntegerType getI64Type()
Definition Builders.cpp:73
IntegerAttr getI64IntegerAttr(int64_t value)
Definition Builders.cpp:120
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
Definition Builders.h:94
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Definition Builders.cpp:275
DictionaryAttr getDictionaryAttr(ArrayRef< NamedAttribute > value)
Definition Builders.cpp:112
NamedAttribute getNamedAttr(StringRef name, Attribute val)
Definition Builders.cpp:102
The main mechanism for performing data layout queries.
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
llvm::TypeSize getTypeSize(Type t) const
Returns the size of the given type in the current scope.
uint64_t getStackAlignment() const
Returns the natural alignment of the stack in bits.
uint64_t getTypeABIAlignment(Type t) const
Returns the required alignment of the given type in the current scope.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
MLIRContext * getContext() const
Definition Dialect.h:52
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
Location objects represent source locations information in MLIR.
Definition Location.h:32
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:71
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:166
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
Block * getInsertionBlock() const
Return the block the current insertion point belongs to.
Definition Builders.h:445
A trait of region holding operations that define a new scope for automatic allocations,...
This class provides the API for ops that are known to be isolated from above.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:740
bool mightHaveTrait()
Returns true if the operation might have the provided trait.
Definition Operation.h:811
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
Definition Operation.h:514
Operation * getParentWithTrait()
Returns the closest surrounding parent operation with trait Trait.
Definition Operation.h:273
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
Attribute removeDiscardableAttr(StringAttr name)
Remove the discardable attribute with the specified name if it exists.
Definition Operation.h:526
AttrClass getDiscardableAttrOfType(StringRef name)
Access a discardable attribute by name and cast it to AttrClass.
Definition Operation.h:495
user_range getUsers()
Returns a range of all users.
Definition Operation.h:927
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
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
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
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:392
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
Block * getParentBlock()
Return the Block in which this Value is defined.
Definition Value.cpp:46
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
A class to signal how to proceed with the walk of the backward slice:
Definition SliceWalk.h:20
bool wasInterrupted() const
Returns true if the walk was interrupted.
Definition SliceWalk.h:60
static WalkContinuation skip()
Creates a continuation that advances the walk without adding any predecessor values to the work list.
Definition SliceWalk.h:55
static WalkContinuation advanceTo(mlir::ValueRange nextValues)
Creates a continuation that adds the user-specified nextValues to the work list and advances the walk...
Definition SliceWalk.h:49
static WalkContinuation interrupt()
Creates a continuation that interrupts the walk.
Definition SliceWalk.h:43
static WalkResult advance()
Definition WalkResult.h:47
void recursivelyReplaceElementsIn(Operation *op, bool replaceAttrs=true, bool replaceLocs=false, bool replaceTypes=false)
Replace the elements within the given operation, and all nested operations.
void addReplacement(ReplaceFn< Attribute > fn)
AttrTypeReplacerBase.
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int64_t > content)
void registerInlinerInterface(DialectRegistry &registry)
Register the LLVMInlinerInterface implementation of DialectInlinerInterface with the LLVM dialect.
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::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< SmallVector< Value > > getControlFlowPredecessors(Value value)
Computes a vector of all control predecessors of value.
Definition SliceWalk.cpp:60
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:125
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:128
WalkContinuation walkSlice(mlir::ValueRange rootValues, WalkCallback walkCallback)
Walks the slice starting from the rootValues using a depth-first traversal.
Definition SliceWalk.cpp:6
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
This trait indicates that a terminator operation is "return-like".