https://github.com/E00N777 updated https://github.com/llvm/llvm-project/pull/199599
>From 7177fbc9d94ad38f1d1400982aa01bc1eb1084bb Mon Sep 17 00:00:00 2001 From: E0N777 <[email protected]> Date: Tue, 26 May 2026 12:08:52 +0800 Subject: [PATCH] [CIR] Add cir.lifetime.start and cir.lifetime.end Op --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 56 +++++++++++++++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 26 +++++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 15 +++++ clang/test/CIR/IR/lifetime.cir | 14 +++++ clang/test/CIR/Lowering/lifetime.cir | 12 ++++ 5 files changed, 123 insertions(+) create mode 100644 clang/test/CIR/IR/lifetime.cir create mode 100644 clang/test/CIR/Lowering/lifetime.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 67ddaa73d9184..84a6b6f3f4f32 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4670,6 +4670,62 @@ def CIR_StackRestoreOp : CIR_Op<"stackrestore"> { let assemblyFormat = "$ptr attr-dict `:` qualified(type($ptr))"; } +//===----------------------------------------------------------------------===// +// LifetimeStartOp & LifetimeEndOp +//===----------------------------------------------------------------------===// + +def CIR_LifetimeStartOp : CIR_Op<"lifetime.start"> { + let summary = "Marks the beginning of an alloca object's live range"; + let description = [{ + The `cir.lifetime.start` operation marks the beginning of the live range + of the memory region pointed to by `$ptr`. Between this operation and a + matching `cir.lifetime.end` on the same pointer, the underlying storage + is considered live; outside that range it is considered dead, and the + optimizer is free to reuse the storage for other purposes. + + The verifier requires `$ptr` to be produced by a `cir.alloca`. For the + live range to be meaningful, a matching `cir.lifetime.end` on the same + pointer should follow on every control-flow path. + + This operation corresponds to the LLVM intrinsic `llvm.lifetime.start`. + + Example: + ``` + cir.lifetime.start %ptr : !cir.ptr<!s32i> + ``` + }]; + + let arguments = (ins CIR_PointerType:$ptr); + let assemblyFormat = "$ptr attr-dict `:` qualified(type($ptr))"; + let hasVerifier = 1; +} + +def CIR_LifetimeEndOp : CIR_Op<"lifetime.end"> { + let summary = "Marks the end of an alloca object's live range"; + let description = [{ + The `cir.lifetime.end` operation marks the end of the live range of the + memory region pointed to by `$ptr`. After this operation the underlying + storage is considered dead until a subsequent `cir.lifetime.start` on + the same pointer; accesses to the storage in the dead range are + undefined behavior. + + The verifier requires `$ptr` to be produced by a `cir.alloca`. It should + be preceded by a matching `cir.lifetime.start` on the same pointer on + every control-flow path that reaches it. + + This operation corresponds to the LLVM intrinsic `llvm.lifetime.end`. + + Example: + ``` + cir.lifetime.end %ptr : !cir.ptr<!s32i> + ``` + }]; + + let arguments = (ins CIR_PointerType:$ptr); + let assemblyFormat = "$ptr attr-dict `:` qualified(type($ptr))"; + let hasVerifier = 1; +} + //===----------------------------------------------------------------------===// // InlineAsmOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index c23a02d6f49fb..92cedea28cfca 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -19,6 +19,7 @@ #include "mlir/IR/Attributes.h" #include "mlir/IR/DialectImplementation.h" #include "mlir/IR/PatternMatch.h" +#include "mlir/IR/Value.h" #include "mlir/Interfaces/ControlFlowInterfaces.h" #include "mlir/Interfaces/FunctionImplementation.h" #include "mlir/Support/LLVM.h" @@ -208,6 +209,19 @@ static bool omitRegionTerm(mlir::Region &r) { return singleNonEmptyBlock && yieldsNothing(); } +// Verifies that the given operand is produced by an operation of type +// ExpectedProducerOp. +template <typename ExpectedProducerOp> +static LogicalResult verifyProducedBy(Operation *op, Value operand, + StringRef operandName) { + Operation *producer = operand.getDefiningOp(); + if (!producer || !isa<ExpectedProducerOp>(producer)) + return op->emitOpError() + << "operand '" << operandName << "' must be produced by '" + << ExpectedProducerOp::getOperationName() << "'"; + return success(); +} + //===----------------------------------------------------------------------===// // InlineKindAttr (FIXME: remove once FuncOp uses assembly format) //===----------------------------------------------------------------------===// @@ -4377,6 +4391,18 @@ cir::EhTypeIdOp::verifySymbolUses(SymbolTableCollection &symbolTable) { return success(); } +//===----------------------------------------------------------------------===// +// LifetimeStartOp & LifetimeEndOp +//===----------------------------------------------------------------------===// + +LogicalResult cir::LifetimeStartOp::verify() { + return verifyProducedBy<cir::AllocaOp>(*this, getPtr(), "ptr"); +} + +LogicalResult cir::LifetimeEndOp::verify() { + return verifyProducedBy<cir::AllocaOp>(*this, getPtr(), "ptr"); +} + //===----------------------------------------------------------------------===// // ConstructCatchParamOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index c4e98e299dfc1..7fdf6ce101303 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -4193,6 +4193,21 @@ mlir::LogicalResult CIRToLLVMStackRestoreOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMLifetimeStartOpLowering::matchAndRewrite( + cir::LifetimeStartOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + rewriter.replaceOpWithNewOp<mlir::LLVM::LifetimeStartOp>(op, + adaptor.getPtr()); + return mlir::success(); +} + +mlir::LogicalResult CIRToLLVMLifetimeEndOpLowering::matchAndRewrite( + cir::LifetimeEndOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + rewriter.replaceOpWithNewOp<mlir::LLVM::LifetimeEndOp>(op, adaptor.getPtr()); + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMVecCreateOpLowering::matchAndRewrite( cir::VecCreateOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { diff --git a/clang/test/CIR/IR/lifetime.cir b/clang/test/CIR/IR/lifetime.cir new file mode 100644 index 0000000000000..5b310d93679d1 --- /dev/null +++ b/clang/test/CIR/IR/lifetime.cir @@ -0,0 +1,14 @@ +// Test the CIR operations can parse and print correctly (roundtrip) + +// RUN: cir-opt %s --verify-roundtrip | FileCheck %s + +!s32i = !cir.int<s, 32> + +cir.func @lifetime_start_end() { + %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["x"] {alignment = 4 : i64} + // CHECK: cir.lifetime.start %0 : !cir.ptr<!s32i> + cir.lifetime.start %0 : !cir.ptr<!s32i> + // CHECK: cir.lifetime.end %0 : !cir.ptr<!s32i> + cir.lifetime.end %0 : !cir.ptr<!s32i> + cir.return +} diff --git a/clang/test/CIR/Lowering/lifetime.cir b/clang/test/CIR/Lowering/lifetime.cir new file mode 100644 index 0000000000000..70db17eae76bf --- /dev/null +++ b/clang/test/CIR/Lowering/lifetime.cir @@ -0,0 +1,12 @@ +// RUN: cir-opt %s -cir-to-llvm -o - | mlir-translate -mlir-to-llvmir | FileCheck %s + +!s32i = !cir.int<s, 32> + +cir.func @lifetime_markers() { + %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["x"] {alignment = 4 : i64} + // CHECK: call void @llvm.lifetime.start.p0(ptr %[[X:.*]]) + cir.lifetime.start %0 : !cir.ptr<!s32i> + // CHECK: call void @llvm.lifetime.end.p0(ptr %[[X]]) + cir.lifetime.end %0 : !cir.ptr<!s32i> + cir.return +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
