diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 2b361ed0982c6..34fdd91dd300e 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4490,6 +4490,48 @@ def CIR_TryOp : CIR_Op<"try",[ let hasLLVMLowering = false; } +//===----------------------------------------------------------------------===// +// CatchParamOp +//===----------------------------------------------------------------------===// + +def CIR_CatchParamKind : CIR_I32EnumAttr< + "CatchParamKind", "Designate limits for begin/end of catch param handling", [ + I32EnumAttrCase<"Begin", 0, "begin">, + I32EnumAttrCase<"End", 1, "end"> +]>; + +def CIR_CatchParamOp : CIR_Op<"catch_param"> { + let summary = "Represents catch clause formal parameter"; + let description = [{ + The `cir.catch_param` can operate in two modes: within catch regions of + `cir.try` or anywhere else with the `begin` or `end` markers. The `begin` + version requires an exception pointer of `cir.ptr`. + + Example: + + ```mlir + %exception = cir.catch_param begin %exception_obj -> !cir.ptr + %exception = cir.catch_param -> !cir.ptr + cir.catch_param end + ``` + }]; + + let arguments = (ins + Optional:$exception_ptr, + OptionalAttr:$kind + ); + + let results = (outs Optional:$param); + let assemblyFormat = [{ + ($kind^)? + ($exception_ptr^)? + (`:` qualified(type($param))^)? + attr-dict + }]; + + let hasVerifier = 1; +} + //===----------------------------------------------------------------------===// // Atomic operations //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 2d2ef422bfaef..39cb55df46bf7 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -3096,6 +3096,23 @@ static mlir::ParseResult parseTryHandlerRegions( return mlir::success(); } +//===----------------------------------------------------------------------===// +// CatchParamOp +//===----------------------------------------------------------------------===// + +LogicalResult cir::CatchParamOp::verify() { + std::optional kind = getKind(); + if (getExceptionPtr()) { + if (!kind || *kind != cir::CatchParamKind::Begin) + return emitOpError("with exception pointer must be of `begin` kind"); + return success(); + } + + if (!kind && !(*this)->getParentOfType()) + return emitOpError("without `kind` requires `cir.try` surrounding scope"); + return success(); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 5a6193fa8d840..b4c815065b2af 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2981,6 +2981,35 @@ mlir::LogicalResult CIRToLLVMThrowOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMCatchParamOpLowering::matchAndRewrite( + cir::CatchParamOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + std::optional kind = op.getKind(); + if (!kind) + llvm_unreachable("only begin/end supposed to make to lowering stage"); + + if (kind == cir::CatchParamKind::Begin) { + // Get or create `declare ptr @__cxa_begin_catch(ptr)` + const llvm::StringRef fnName = "__cxa_begin_catch"; + auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext()); + auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {llvmPtrTy}); + createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy); + rewriter.replaceOpWithNewOp( + op, mlir::TypeRange{llvmPtrTy}, fnName, + mlir::ValueRange{adaptor.getExceptionPtr()}); + return mlir::success(); + } + + // Get or create `declare void @__cxa_end_catch()` + const llvm::StringRef fnName = "__cxa_end_catch"; + auto voidTy = mlir::LLVM::LLVMVoidType::get(rewriter.getContext()); + auto fnTy = mlir::LLVM::LLVMFunctionType::get(voidTy, {}); + createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy); + rewriter.replaceOpWithNewOp(op, mlir::TypeRange{}, fnName, + mlir::ValueRange{}); + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite( cir::AllocExceptionOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { diff --git a/clang/test/CIR/IR/catch-param.cir b/clang/test/CIR/IR/catch-param.cir new file mode 100644 index 0000000000000..32b1d10d4f9b4 --- /dev/null +++ b/clang/test/CIR/IR/catch-param.cir @@ -0,0 +1,58 @@ +// RUN: cir-opt %s --verify-roundtrip | FileCheck %s + +!s32i = !cir.int +!void = !cir.void + +module { + +cir.func dso_local @catch_param_inside_catch() { + cir.scope { + cir.try { + cir.yield + } catch all { + cir.catch_param : !cir.ptr + cir.yield + } + } + cir.return +} + +// CHECK: cir.func dso_local @catch_param_inside_catch() { +// CHECK: cir.scope { +// CHECK: cir.try { +// CHECK: cir.yield +// CHECK: } catch all { +// CHECK: cir.catch_param : !cir.ptr +// CHECK: cir.yield +// CHECK: } +// CHECK: } +// CHECK: cir.return +// CHECK: } + +cir.func dso_local @catch_begin_and_end() { + %exn_addr = cir.alloca !cir.ptr, !cir.ptr>, ["exn_addr"] + %tmp_exn_ptr = cir.load %exn_addr : !cir.ptr>, !cir.ptr + cir.br ^bb1(%tmp_exn_ptr : !cir.ptr) + ^bb1(%exn_ptr : !cir.ptr): + %begin = cir.catch_param begin %exn_ptr : !cir.ptr + cir.catch_param end + cir.br ^bb2 + ^bb2: + cir.return +} + +// CHECK: cir.func dso_local @catch_begin_and_end() { +// CHECK: %[[EXN_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["exn_addr"] +// CHECK: %[[TMP_EXN_PTR:.*]] = cir.load %[[EXN_ADDR]] : !cir.ptr>, !cir.ptr +// CHECK: cir.br ^bb1(%[[TMP_EXN_PTR]] : !cir.ptr) +// CHECK: ^bb1(%[[EXN_PTR:.*]]: !cir.ptr): +// CHECK: %[[BEGIN:.*]] = cir.catch_param begin %[[EXN_PTR]] : !cir.ptr +// CHECK: cir.catch_param end +// CHECK: cir.br ^bb2 +// CHECK: ^bb2: +// CHECK: cir.return +// CHECK: } + +} + + diff --git a/clang/test/CIR/IR/invalid-catch-param.cir b/clang/test/CIR/IR/invalid-catch-param.cir new file mode 100644 index 0000000000000..e3d212b118798 --- /dev/null +++ b/clang/test/CIR/IR/invalid-catch-param.cir @@ -0,0 +1,65 @@ +// RUN: cir-opt %s -verify-diagnostics -split-input-file + +!s32i = !cir.int +!void = !cir.void + +module { + +cir.func dso_local @catch_param_without_kind_and_without_try_scope() { + // expected-error @below {{op without `kind` requires `cir.try` surrounding scope}} + %0 = cir.catch_param : !cir.ptr + cir.return +} + +} + +// ----- + +!s32i = !cir.int +!void = !cir.void + +module { + +cir.func private @division() -> !s32i +cir.func dso_local @catch_param_with_exception_ptr_but_without_kind() { + cir.scope { + cir.try { + %0 = cir.call @division() : () -> !s32i + cir.yield + } catch all { + %0 = cir.const #cir.ptr : !cir.ptr + // expected-error @below {{op with exception pointer must be of `begin` kind}} + %1 = cir.catch_param %0 : !cir.ptr + cir.yield + } + } + cir.return +} + +} + +// ----- + +!s32i = !cir.int +!void = !cir.void + +module { + +cir.func private @division() -> !s32i +cir.func dso_local @catch_param_with_exception_ptr_but_with_end_kind() { + cir.scope { + cir.try { + %0 = cir.call @division() : () -> !s32i + cir.yield + } catch all { + %0 = cir.const #cir.ptr : !cir.ptr + // expected-error @below {{'cir.catch_param' op with exception pointer must be of `begin` kind}} + %1 = cir.catch_param end %0 : !cir.ptr + cir.yield + } + } + cir.return +} + +} +