-
Notifications
You must be signed in to change notification settings - Fork 0
Inline lifetime intrinsics #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2848,31 +2848,75 @@ struct LLVMOpAsmDialectInterface : public OpAsmDialectInterface { | |
| // DialectInlinerInterface | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // Check whether the given alloca is an input to a lifetime intrinsic, | ||
| // optionally passing through one or more casts on the way. | ||
| static bool hasLifetimeMarkers(LLVM::AllocaOp allocaOp) { | ||
| SmallVector<Operation *, 2> stack(allocaOp->getUsers().begin(), | ||
definelicht marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| allocaOp->getUsers().end()); | ||
| while (!stack.empty()) { | ||
| Operation *op = stack.pop_back_val(); | ||
| if (isa<LLVM::LifetimeStartOp, LLVM::LifetimeEndOp>(op)) | ||
| return true; | ||
| if (isa<LLVM::BitcastOp>(op)) | ||
| stack.append(op->getUsers().begin(), op->getUsers().end()); | ||
| } | ||
definelicht marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return false; | ||
| } | ||
|
|
||
| /// Move all alloca operations with a constant size in the former entry block of | ||
| /// the newly inlined callee into the entry block of the caller. | ||
| /// the newly inlined callee into the entry block of the caller, and insert | ||
| /// lifetime intrinsics that limit their scope to the inlined blocks. | ||
| static void moveConstantAllocasToEntryBlock( | ||
| iterator_range<Region::iterator> inlinedBlocks) { | ||
| Block *calleeEntryBlock = &(*inlinedBlocks.begin()); | ||
| Block *callerEntryBlock = &(*calleeEntryBlock->getParent()->begin()); | ||
| if (calleeEntryBlock == callerEntryBlock) | ||
| // Nothing to do. | ||
| return; | ||
| SmallVector<std::pair<LLVM::AllocaOp, IntegerAttr>> allocasToMove; | ||
| SmallVector<std::tuple<LLVM::AllocaOp, IntegerAttr, bool>> allocasToMove; | ||
| bool shouldInsertLifetimes = false; | ||
| // Conservatively only move alloca operations that are part of the entry block | ||
| // and do not inspect nested regions, since they may execute conditionally or | ||
| // have other unknown semantics. | ||
| for (auto allocaOp : calleeEntryBlock->getOps<LLVM::AllocaOp>()) { | ||
| IntegerAttr arraySize; | ||
| if (matchPattern(allocaOp.getArraySize(), m_Constant(&arraySize))) | ||
| allocasToMove.emplace_back(allocaOp, arraySize); | ||
| if (!matchPattern(allocaOp.getArraySize(), m_Constant(&arraySize))) | ||
| continue; | ||
| bool shouldInsertLifetime = | ||
| arraySize.getValue() != 0 && !hasLifetimeMarkers(allocaOp); | ||
| shouldInsertLifetimes |= shouldInsertLifetime; | ||
| allocasToMove.emplace_back(allocaOp, arraySize, shouldInsertLifetime); | ||
| } | ||
| OpBuilder builder(callerEntryBlock, callerEntryBlock->begin()); | ||
| for (auto &[allocaOp, arraySize] : allocasToMove) { | ||
| for (auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) { | ||
| auto newConstant = builder.create<LLVM::ConstantOp>( | ||
| allocaOp->getLoc(), allocaOp.getArraySize().getType(), arraySize); | ||
| // Insert a lifetime start intrinsic where the alloca was before moving it. | ||
| if (shouldInsertLifetime) { | ||
| OpBuilder::InsertionGuard insertionGuard(builder); | ||
| builder.setInsertionPoint(allocaOp); | ||
| builder.create<LLVM::LifetimeStartOp>( | ||
| allocaOp.getLoc(), arraySize.getValue().getLimitedValue(), | ||
| allocaOp.getResult()); | ||
| } | ||
| allocaOp->moveAfter(newConstant); | ||
| allocaOp.getArraySizeMutable().assign(newConstant.getResult()); | ||
| } | ||
| if (!shouldInsertLifetimes) | ||
| return; | ||
| // Insert a lifetime end intrinsic before each return in the callee function. | ||
| for (Block &block : inlinedBlocks) { | ||
| if (!block.back().hasTrait<OpTrait::ReturnLike>()) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there should be block->getTerminator() which is a bit more idiomatic here. Also can we have anything else than a LLVM_ReturnOp? If not I would prefer:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought Regarding
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I am fine with hasTrait. It is not very commonly used outside of the core infra though... |
||
| continue; | ||
| builder.setInsertionPoint(&block.back()); | ||
| for (auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) { | ||
| if (!shouldInsertLifetime) | ||
| continue; | ||
| builder.create<LLVM::LifetimeEndOp>( | ||
| allocaOp.getLoc(), arraySize.getValue().getLimitedValue(), | ||
| allocaOp.getResult()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| namespace { | ||
|
|
@@ -2912,7 +2956,8 @@ struct LLVMInlinerInterface : public DialectInlinerInterface { | |
| return false; | ||
| return true; | ||
| }) | ||
| .Case<LLVM::CallOp, LLVM::AllocaOp>([](auto) { return true; }) | ||
| .Case<LLVM::CallOp, LLVM::AllocaOp, LLVM::LifetimeStartOp, | ||
| LLVM::LifetimeEndOp>([](auto) { return true; }) | ||
| .Default([](auto) { return false; }); | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.