19 #include "llvm/ADT/ScopeExit.h"
20 #include "llvm/Support/Debug.h"
22 #define DEBUG_TYPE "llvm-inliner"
31 allocaOp->getUsers().end());
32 while (!stack.empty()) {
34 if (isa<LLVM::LifetimeStartOp, LLVM::LifetimeEndOp>(op))
36 if (isa<LLVM::BitcastOp>(op))
53 Block *calleeEntryBlock = &(*inlinedBlocks.begin());
55 if (calleeEntryBlock == callerEntryBlock)
59 bool shouldInsertLifetimes =
false;
60 bool hasDynamicAlloca =
false;
64 for (
auto allocaOp : calleeEntryBlock->
getOps<LLVM::AllocaOp>()) {
65 IntegerAttr arraySize;
67 hasDynamicAlloca =
true;
70 bool shouldInsertLifetime =
72 shouldInsertLifetimes |= shouldInsertLifetime;
73 allocasToMove.emplace_back(allocaOp, arraySize, shouldInsertLifetime);
76 for (
Block &block : llvm::drop_begin(inlinedBlocks)) {
80 llvm::any_of(block.getOps<LLVM::AllocaOp>(), [](
auto allocaOp) {
81 return !matchPattern(allocaOp.getArraySize(), m_Constant());
84 if (allocasToMove.empty() && !hasDynamicAlloca)
88 if (hasDynamicAlloca) {
93 stackPtr = builder.
create<LLVM::StackSaveOp>(
97 for (
auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) {
98 auto newConstant = builder.
create<LLVM::ConstantOp>(
99 allocaOp->getLoc(), allocaOp.getArraySize().getType(), arraySize);
101 if (shouldInsertLifetime) {
104 builder.
create<LLVM::LifetimeStartOp>(
105 allocaOp.getLoc(), arraySize.getValue().getLimitedValue(),
106 allocaOp.getResult());
109 allocaOp.getArraySizeMutable().assign(newConstant.getResult());
111 if (!shouldInsertLifetimes && !hasDynamicAlloca)
114 for (
Block &block : inlinedBlocks) {
118 if (hasDynamicAlloca)
119 builder.
create<LLVM::StackRestoreOp>(call->
getLoc(), stackPtr);
120 for (
auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) {
121 if (shouldInsertLifetime)
122 builder.
create<LLVM::LifetimeEndOp>(
123 allocaOp.getLoc(), arraySize.getValue().getLimitedValue(),
124 allocaOp.getResult());
144 walker.
addWalk([&](LLVM::AliasScopeDomainAttr domainAttr) {
146 domainAttr.getContext(), domainAttr.getDescription());
149 walker.
addWalk([&](LLVM::AliasScopeAttr scopeAttr) {
151 cast<LLVM::AliasScopeDomainAttr>(mapping.lookup(scopeAttr.getDomain())),
152 scopeAttr.getDescription());
156 auto convertScopeList = [&](ArrayAttr arrayAttr) -> ArrayAttr {
161 walker.
walk(arrayAttr);
164 llvm::map_to_vector(arrayAttr, [&](
Attribute attr) {
165 return mapping.lookup(attr);
169 for (
Block &block : inlinedBlocks) {
171 if (
auto aliasInterface = dyn_cast<LLVM::AliasAnalysisOpInterface>(op)) {
172 aliasInterface.setAliasScopes(
173 convertScopeList(aliasInterface.getAliasScopesOrNull()));
174 aliasInterface.setNoAliasScopes(
175 convertScopeList(aliasInterface.getNoAliasScopesOrNull()));
178 if (
auto noAliasScope = dyn_cast<LLVM::NoAliasScopeDeclOp>(op)) {
180 walker.
walk(noAliasScope.getScopeAttr());
182 noAliasScope.setScopeAttr(cast<LLVM::AliasScopeAttr>(
183 mapping.lookup(noAliasScope.getScopeAttr())));
199 llvm::append_range(result, lhs);
200 llvm::append_range(result, rhs);
210 if (
auto gepOp = pointerValue.
getDefiningOp<LLVM::GEPOp>()) {
211 pointerValue = gepOp.getBase();
215 if (
auto addrCast = pointerValue.
getDefiningOp<LLVM::AddrSpaceCastOp>()) {
216 pointerValue = addrCast.getOperand();
236 Value current = workList.pop_back_val();
239 if (!seen.insert(current).second)
242 if (
auto selectOp = current.
getDefiningOp<LLVM::SelectOp>()) {
243 workList.push_back(selectOp.getTrueValue());
244 workList.push_back(selectOp.getFalseValue());
248 if (
auto blockArg = dyn_cast<BlockArgument>(current)) {
249 Block *parentBlock = blockArg.getParentBlock();
255 bool anyUnknown =
false;
257 iter != parentBlock->
pred_end(); iter++) {
258 auto branch = dyn_cast<BranchOpInterface>((*iter)->getTerminator());
260 result.push_back(blockArg);
265 Value operand = branch.getSuccessorOperands(
266 iter.getSuccessorIndex())[blockArg.getArgNumber()];
268 result.push_back(blockArg);
273 operands.push_back(operand);
277 llvm::append_range(workList, operands);
282 result.push_back(current);
283 }
while (!workList.empty());
300 for (
Value argument : cast<LLVM::CallOp>(call).getArgOperands()) {
302 auto ssaCopy = llvm::dyn_cast<LLVM::SSACopyOp>(user);
305 if (!ssaCopy->hasAttr(LLVM::LLVMDialect::getNoAliasAttrName()))
308 noAliasParams.insert(ssaCopy);
313 if (noAliasParams.empty())
318 auto exit = llvm::make_scope_exit([&] {
319 for (LLVM::SSACopyOp ssaCopyOp : noAliasParams) {
320 ssaCopyOp.replaceAllUsesWith(ssaCopyOp.getOperand());
328 call->
getContext(), cast<LLVM::CallOp>(call).getCalleeAttr().getAttr());
330 for (LLVM::SSACopyOp copyOp : noAliasParams) {
332 pointerScopes[copyOp] = scope;
339 for (
Block &inlinedBlock : inlinedBlocks) {
340 for (
auto aliasInterface :
341 inlinedBlock.getOps<LLVM::AliasAnalysisOpInterface>()) {
348 for (
Value pointer : pointerArgs)
350 std::inserter(basedOnPointers, basedOnPointers.begin()));
352 bool aliasesOtherKnownObject =
false;
362 if (llvm::any_of(basedOnPointers, [&](
Value object) {
366 if (noAliasParams.contains(
object.getDefiningOp<LLVM::SSACopyOp>()))
371 if (isa_and_nonnull<LLVM::AllocaOp, LLVM::AddressOfOp>(
372 object.getDefiningOp())) {
373 aliasesOtherKnownObject =
true;
383 for (LLVM::SSACopyOp noAlias : noAliasParams) {
384 if (basedOnPointers.contains(noAlias))
387 noAliasScopes.push_back(pointerScopes[noAlias]);
390 if (!noAliasScopes.empty())
391 aliasInterface.setNoAliasScopes(
419 if (aliasesOtherKnownObject ||
420 isa<LLVM::CallOp>(aliasInterface.getOperation()))
424 for (LLVM::SSACopyOp noAlias : noAliasParams)
425 if (basedOnPointers.contains(noAlias))
426 aliasScopes.push_back(pointerScopes[noAlias]);
428 if (!aliasScopes.empty())
429 aliasInterface.setAliasScopes(
441 auto callAliasInterface = dyn_cast<LLVM::AliasAnalysisOpInterface>(call);
442 if (!callAliasInterface)
445 ArrayAttr aliasScopes = callAliasInterface.getAliasScopesOrNull();
446 ArrayAttr noAliasScopes = callAliasInterface.getNoAliasScopesOrNull();
449 if (!aliasScopes && !noAliasScopes)
454 for (
Block &block : inlinedBlocks) {
455 for (
auto aliasInterface : block.getOps<LLVM::AliasAnalysisOpInterface>()) {
458 aliasInterface.getAliasScopesOrNull(), aliasScopes));
462 aliasInterface.getNoAliasScopesOrNull(), noAliasScopes));
479 auto callAccessGroupInterface = dyn_cast<LLVM::AccessGroupOpInterface>(call);
480 if (!callAccessGroupInterface)
483 auto accessGroups = callAccessGroupInterface.getAccessGroupsOrNull();
489 for (
Block &block : inlinedBlocks)
490 for (
auto accessGroupOpInterface :
491 block.getOps<LLVM::AccessGroupOpInterface>())
493 accessGroupOpInterface.getAccessGroupsOrNull(), accessGroups));
500 uint64_t requestedAlignment,
502 uint64_t allocaAlignment = alloca.getAlignment().value_or(1);
503 if (requestedAlignment <= allocaAlignment)
505 return allocaAlignment;
509 if (naturalStackAlignmentBits == 0 ||
512 8 * requestedAlignment <= naturalStackAlignmentBits ||
515 8 * allocaAlignment > naturalStackAlignmentBits) {
516 alloca.setAlignment(requestedAlignment);
517 allocaAlignment = requestedAlignment;
519 return allocaAlignment;
531 if (
auto alloca = dyn_cast<LLVM::AllocaOp>(definingOp))
534 if (
auto addressOf = dyn_cast<LLVM::AddressOfOp>(definingOp))
535 if (
auto global = SymbolTable::lookupNearestSymbolFrom<LLVM::GlobalOp>(
536 definingOp, addressOf.getGlobalNameAttr()))
537 return global.getAlignment().value_or(1);
544 if (
auto func = dyn_cast<LLVM::LLVMFuncOp>(parentOp)) {
547 auto blockArg = llvm::cast<BlockArgument>(value);
548 if (
Attribute alignAttr = func.getArgAttr(
549 blockArg.getArgNumber(), LLVM::LLVMDialect::getAlignAttrName()))
550 return cast<IntegerAttr>(alignAttr).getValue().getLimitedValue();
560 uint64_t elementTypeSize,
561 uint64_t targetAlignment) {
572 allocaOp = builder.
create<LLVM::AllocaOp>(
573 loc, argument.
getType(), elementType, one, targetAlignment);
578 builder.
create<LLVM::MemcpyOp>(loc, allocaOp, argument, copySize,
590 uint64_t requestedAlignment) {
591 auto func = cast<LLVM::LLVMFuncOp>(callable);
592 LLVM::MemoryEffectsAttr memoryEffects = func.getMemoryAttr();
595 bool isReadOnly = memoryEffects &&
596 memoryEffects.getArgMem() != LLVM::ModRefInfo::ModRef &&
597 memoryEffects.getArgMem() != LLVM::ModRefInfo::Mod;
602 if (requestedAlignment <= minimumAlignment)
604 uint64_t currentAlignment =
606 if (currentAlignment >= requestedAlignment)
609 uint64_t targetAlignment =
std::max(requestedAlignment, minimumAlignment);
619 LLVMInlinerInterface(
Dialect *dialect)
622 disallowedFunctionAttrs({
632 bool wouldBeCloned)
const final {
635 auto callOp = dyn_cast<LLVM::CallOp>(call);
637 LLVM_DEBUG(llvm::dbgs()
638 <<
"Cannot inline: call is not an LLVM::CallOp\n");
641 auto funcOp = dyn_cast<LLVM::LLVMFuncOp>(callable);
643 LLVM_DEBUG(llvm::dbgs()
644 <<
"Cannot inline: callable is not an LLVM::LLVMFuncOp\n");
648 if (
auto attrs = funcOp.getArgAttrs()) {
649 for (DictionaryAttr attrDict : attrs->getAsRange<DictionaryAttr>()) {
650 if (attrDict.contains(LLVM::LLVMDialect::getInAllocaAttrName())) {
651 LLVM_DEBUG(llvm::dbgs() <<
"Cannot inline " << funcOp.getSymName()
652 <<
": inalloca arguments not supported\n");
658 if (funcOp.getPersonality()) {
659 LLVM_DEBUG(llvm::dbgs() <<
"Cannot inline " << funcOp.getSymName()
660 <<
": unhandled function personality\n");
663 if (funcOp.getPassthrough()) {
665 if (llvm::any_of(*funcOp.getPassthrough(), [&](
Attribute attr) {
666 auto stringAttr = dyn_cast<StringAttr>(attr);
669 if (disallowedFunctionAttrs.contains(stringAttr)) {
670 LLVM_DEBUG(llvm::dbgs()
671 <<
"Cannot inline " << funcOp.getSymName()
672 <<
": found disallowed function attribute "
673 << stringAttr <<
"\n");
692 if (isa<LLVM::AllocaOp,
695 LLVM::AtomicCmpXchgOp,
697 LLVM::CallIntrinsicOp,
704 LLVM::LifetimeStartOp,
707 LLVM::MemcpyInlineOp,
710 LLVM::NoAliasScopeDeclOp,
711 LLVM::StackRestoreOp,
714 LLVM::UnreachableOp>(op))
717 LLVM_DEBUG(llvm::dbgs()
718 <<
"Cannot inline: unhandled side effecting operation \""
725 void handleTerminator(
Operation *op,
Block *newDest)
const final {
727 auto returnOp = dyn_cast<LLVM::ReturnOp>(op);
733 builder.create<LLVM::BrOp>(op->
getLoc(), returnOp.getOperands(), newDest);
742 auto returnOp = cast<LLVM::ReturnOp>(op);
745 assert(returnOp.getNumOperands() == valuesToRepl.size());
746 for (
auto [dst, src] : llvm::zip(valuesToRepl, returnOp.getOperands()))
747 dst.replaceAllUsesWith(src);
752 DictionaryAttr argumentAttrs)
const final {
753 if (std::optional<NamedAttribute> attr =
754 argumentAttrs.getNamed(LLVM::LLVMDialect::getByValAttrName())) {
755 Type elementType = cast<TypeAttr>(attr->getValue()).getValue();
756 uint64_t requestedAlignment = 1;
757 if (std::optional<NamedAttribute> alignAttr =
758 argumentAttrs.getNamed(LLVM::LLVMDialect::getAlignAttrName())) {
759 requestedAlignment = cast<IntegerAttr>(alignAttr->getValue())
766 if ([[maybe_unused]] std::optional<NamedAttribute> attr =
767 argumentAttrs.getNamed(LLVM::LLVMDialect::getNoAliasAttrName())) {
768 if (argument.use_empty())
783 auto copyOp = builder.create<LLVM::SSACopyOp>(call->getLoc(), argument);
784 copyOp->setDiscardableAttr(
785 builder.getStringAttr(LLVM::LLVMDialect::getNoAliasAttrName()),
786 builder.getUnitAttr());
792 void processInlinedCallBlocks(
809 dialect->addInterfaces<LLVMInlinerInterface>();
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
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.
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 appendCallOpAliasScopes(Operation *call, iterator_range< Region::iterator > inlinedBlocks)
Appends any alias scopes of the call operation to any inlined memory operation.
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 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 SmallVector< Value > getUnderlyingObjectSet(Value pointerValue)
Attempts to return the set of all underlying pointer values that pointerValue is based on.
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 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 Value getUnderlyingObject(Value pointerValue)
Attempts to return the underlying pointer value that pointerValue is based 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 Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
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.
Block represents an ordered list of Operations.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
pred_iterator pred_begin()
iterator_range< op_iterator< OpT > > getOps()
Return an iterator range over the operations within this block that are of 'OpT'.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
IntegerAttr getI64IntegerAttr(int64_t value)
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.
This is the interface that must be implemented by the dialects of operations to be inlined.
DialectInlinerInterface(Dialect *dialect)
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
MLIRContext * getContext() const
This is a utility class for mapping one set of IR entities to another.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Operation is the basic unit of execution within MLIR.
MLIRContext * getContext()
Return the context this operation is associated with.
Location getLoc()
The source location the operation was defined or derived from.
OperationName getName()
The name of an operation is the key identifier for it.
user_range getUsers()
Returns a range of all users.
void moveAfter(Operation *existingOp)
Unlink this operation from its current block and insert it right after existingOp which may be in the...
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.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Block * getParentBlock()
Return the Block in which this Value is defined.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Region * getParentRegion()
Return the Region in which this Value is defined.
void addLLVMInlinerInterface(LLVMDialect *dialect)
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.
bool isPure(Operation *op)
Returns true if the given operation is pure, i.e., is speculatable that does not touch memory.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
This trait indicates that a terminator operation is "return-like".