Skip to content

Commit db59654

Browse files
definelichtgysit
authored andcommitted
[MLIR][LLVM] Generate LLVM lifetime intrinsics while inlining.
Extend `LLVMInlinerInterface` to inline lifetime intrinsics for `LLVM::AllocaOp` operations, and to insert new lifetime intrinsics when an alloca is moved to the entry block that restrict its scope to where the call was before inlining. Depends on D142436 Reviewed By: gysit Differential Revision: https://reviews.llvm.org/D142701
1 parent 292eca4 commit db59654

File tree

2 files changed

+132
-6
lines changed

2 files changed

+132
-6
lines changed

mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,31 +2848,78 @@ struct LLVMOpAsmDialectInterface : public OpAsmDialectInterface {
28482848
// DialectInlinerInterface
28492849
//===----------------------------------------------------------------------===//
28502850

2851+
/// Check whether the given alloca is an input to a lifetime intrinsic,
2852+
/// optionally passing through one or more casts on the way. This is not
2853+
/// transitive through block arguments.
2854+
static bool hasLifetimeMarkers(LLVM::AllocaOp allocaOp) {
2855+
SmallVector<Operation *> stack(allocaOp->getUsers().begin(),
2856+
allocaOp->getUsers().end());
2857+
while (!stack.empty()) {
2858+
Operation *op = stack.pop_back_val();
2859+
if (isa<LLVM::LifetimeStartOp, LLVM::LifetimeEndOp>(op))
2860+
return true;
2861+
if (isa<LLVM::BitcastOp>(op))
2862+
stack.append(op->getUsers().begin(), op->getUsers().end());
2863+
}
2864+
return false;
2865+
}
2866+
28512867
/// Move all alloca operations with a constant size in the former entry block of
2852-
/// the newly inlined callee into the entry block of the caller.
2868+
/// the newly inlined callee into the entry block of the caller, and insert
2869+
/// lifetime intrinsics that limit their scope to the inlined blocks.
28532870
static void moveConstantAllocasToEntryBlock(
28542871
iterator_range<Region::iterator> inlinedBlocks) {
28552872
Block *calleeEntryBlock = &(*inlinedBlocks.begin());
28562873
Block *callerEntryBlock = &(*calleeEntryBlock->getParent()->begin());
28572874
if (calleeEntryBlock == callerEntryBlock)
28582875
// Nothing to do.
28592876
return;
2860-
SmallVector<std::pair<LLVM::AllocaOp, IntegerAttr>> allocasToMove;
2877+
SmallVector<std::tuple<LLVM::AllocaOp, IntegerAttr, bool>> allocasToMove;
2878+
bool shouldInsertLifetimes = false;
28612879
// Conservatively only move alloca operations that are part of the entry block
28622880
// and do not inspect nested regions, since they may execute conditionally or
28632881
// have other unknown semantics.
28642882
for (auto allocaOp : calleeEntryBlock->getOps<LLVM::AllocaOp>()) {
28652883
IntegerAttr arraySize;
2866-
if (matchPattern(allocaOp.getArraySize(), m_Constant(&arraySize)))
2867-
allocasToMove.emplace_back(allocaOp, arraySize);
2884+
if (!matchPattern(allocaOp.getArraySize(), m_Constant(&arraySize)))
2885+
continue;
2886+
bool shouldInsertLifetime =
2887+
arraySize.getValue() != 0 && !hasLifetimeMarkers(allocaOp);
2888+
shouldInsertLifetimes |= shouldInsertLifetime;
2889+
allocasToMove.emplace_back(allocaOp, arraySize, shouldInsertLifetime);
28682890
}
2891+
if (allocasToMove.empty())
2892+
return;
28692893
OpBuilder builder(callerEntryBlock, callerEntryBlock->begin());
2870-
for (auto &[allocaOp, arraySize] : allocasToMove) {
2894+
for (auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) {
28712895
auto newConstant = builder.create<LLVM::ConstantOp>(
28722896
allocaOp->getLoc(), allocaOp.getArraySize().getType(), arraySize);
2897+
// Insert a lifetime start intrinsic where the alloca was before moving it.
2898+
if (shouldInsertLifetime) {
2899+
OpBuilder::InsertionGuard insertionGuard(builder);
2900+
builder.setInsertionPoint(allocaOp);
2901+
builder.create<LLVM::LifetimeStartOp>(
2902+
allocaOp.getLoc(), arraySize.getValue().getLimitedValue(),
2903+
allocaOp.getResult());
2904+
}
28732905
allocaOp->moveAfter(newConstant);
28742906
allocaOp.getArraySizeMutable().assign(newConstant.getResult());
28752907
}
2908+
if (!shouldInsertLifetimes)
2909+
return;
2910+
// Insert a lifetime end intrinsic before each return in the callee function.
2911+
for (Block &block : inlinedBlocks) {
2912+
if (!block.getTerminator()->hasTrait<OpTrait::ReturnLike>())
2913+
continue;
2914+
builder.setInsertionPoint(block.getTerminator());
2915+
for (auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) {
2916+
if (!shouldInsertLifetime)
2917+
continue;
2918+
builder.create<LLVM::LifetimeEndOp>(
2919+
allocaOp.getLoc(), arraySize.getValue().getLimitedValue(),
2920+
allocaOp.getResult());
2921+
}
2922+
}
28762923
}
28772924

28782925
namespace {
@@ -2912,7 +2959,8 @@ struct LLVMInlinerInterface : public DialectInlinerInterface {
29122959
return false;
29132960
return true;
29142961
})
2915-
.Case<LLVM::CallOp, LLVM::AllocaOp>([](auto) { return true; })
2962+
.Case<LLVM::CallOp, LLVM::AllocaOp, LLVM::LifetimeStartOp,
2963+
LLVM::LifetimeEndOp>([](auto) { return true; })
29162964
.Default([](auto) { return false; });
29172965
}
29182966

mlir/test/Dialect/LLVMIR/inlining.mlir

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,9 @@ llvm.func @test_inline(%cond : i1, %size : i32) -> f32 {
231231
// CHECK: ^{{.+}}:
232232
^bb1:
233233
// CHECK-NOT: llvm.call @static_alloca
234+
// CHECK: llvm.intr.lifetime.start
234235
%0 = llvm.call @static_alloca() : () -> f32
236+
// CHECK: llvm.intr.lifetime.end
235237
// CHECK: llvm.br
236238
llvm.br ^bb3(%0: f32)
237239
// CHECK: ^{{.+}}:
@@ -275,3 +277,79 @@ llvm.func @test_inline(%cond : i1) -> f32 {
275277
%0 = llvm.call @static_alloca_not_in_entry(%cond) : (i1) -> f32
276278
llvm.return %0 : f32
277279
}
280+
281+
// -----
282+
283+
llvm.func @static_alloca(%cond: i1) -> f32 {
284+
%0 = llvm.mlir.constant(4 : i32) : i32
285+
%1 = llvm.alloca %0 x f32 : (i32) -> !llvm.ptr
286+
llvm.cond_br %cond, ^bb1, ^bb2
287+
^bb1:
288+
%2 = llvm.load %1 : !llvm.ptr -> f32
289+
llvm.return %2 : f32
290+
^bb2:
291+
%3 = llvm.mlir.constant(3.14192 : f32) : f32
292+
llvm.return %3 : f32
293+
}
294+
295+
// CHECK-LABEL: llvm.func @test_inline
296+
llvm.func @test_inline(%cond0 : i1, %cond1 : i1, %funcArg : f32) -> f32 {
297+
// CHECK-NOT: llvm.cond_br
298+
// CHECK: %[[PTR:.+]] = llvm.alloca
299+
// CHECK: llvm.cond_br %{{.+}}, ^[[BB1:.+]], ^{{.+}}
300+
llvm.cond_br %cond0, ^bb1, ^bb2
301+
// CHECK: ^[[BB1]]
302+
^bb1:
303+
// Make sure the lifetime begin intrinsic has been inserted where the call
304+
// used to be, even though the alloca has been moved to the entry block.
305+
// CHECK-NEXT: llvm.intr.lifetime.start 4, %[[PTR]]
306+
%0 = llvm.call @static_alloca(%cond1) : (i1) -> f32
307+
// CHECK: llvm.cond_br %{{.+}}, ^[[BB2:.+]], ^[[BB3:.+]]
308+
llvm.br ^bb3(%0: f32)
309+
// Make sure the lifetime end intrinsic has been inserted at both former
310+
// return sites of the callee.
311+
// CHECK: ^[[BB2]]:
312+
// CHECK-NEXT: llvm.load
313+
// CHECK-NEXT: llvm.intr.lifetime.end 4, %[[PTR]]
314+
// CHECK: ^[[BB3]]:
315+
// CHECK-NEXT: llvm.intr.lifetime.end 4, %[[PTR]]
316+
^bb2:
317+
llvm.br ^bb3(%funcArg: f32)
318+
^bb3(%blockArg: f32):
319+
llvm.return %blockArg : f32
320+
}
321+
322+
// -----
323+
324+
llvm.func @alloca_with_lifetime(%cond: i1) -> f32 {
325+
%0 = llvm.mlir.constant(4 : i32) : i32
326+
%1 = llvm.alloca %0 x f32 : (i32) -> !llvm.ptr
327+
llvm.intr.lifetime.start 4, %1 : !llvm.ptr
328+
%2 = llvm.load %1 : !llvm.ptr -> f32
329+
llvm.intr.lifetime.end 4, %1 : !llvm.ptr
330+
%3 = llvm.fadd %2, %2 : f32
331+
llvm.return %3 : f32
332+
}
333+
334+
// CHECK-LABEL: llvm.func @test_inline
335+
llvm.func @test_inline(%cond0 : i1, %cond1 : i1, %funcArg : f32) -> f32 {
336+
// CHECK-NOT: llvm.cond_br
337+
// CHECK: %[[PTR:.+]] = llvm.alloca
338+
// CHECK: llvm.cond_br %{{.+}}, ^[[BB1:.+]], ^{{.+}}
339+
llvm.cond_br %cond0, ^bb1, ^bb2
340+
// CHECK: ^[[BB1]]
341+
^bb1:
342+
// Make sure the original lifetime intrinsic has been preserved, rather than
343+
// inserting a new one with a larger scope.
344+
// CHECK: llvm.intr.lifetime.start 4, %[[PTR]]
345+
// CHECK-NEXT: llvm.load %[[PTR]]
346+
// CHECK-NEXT: llvm.intr.lifetime.end 4, %[[PTR]]
347+
// CHECK: llvm.fadd
348+
// CHECK-NOT: llvm.intr.lifetime.end
349+
%0 = llvm.call @alloca_with_lifetime(%cond1) : (i1) -> f32
350+
llvm.br ^bb3(%0: f32)
351+
^bb2:
352+
llvm.br ^bb3(%funcArg: f32)
353+
^bb3(%blockArg: f32):
354+
llvm.return %blockArg : f32
355+
}

0 commit comments

Comments
 (0)