Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -4606,6 +4606,44 @@ def CIR_TryOp : CIR_Op<"try",[
let hasLLVMLowering = false;
}

//===----------------------------------------------------------------------===//
// Exception related: EhInflightOp
//===----------------------------------------------------------------------===//

def CIR_EhInflightOp : CIR_Op<"eh.inflight_exception"> {
let summary = "Materialize the catch clause formal parameter";
let description = [{
`cir.eh.inflight_exception` returns two values:
- `exception_ptr`: The exception pointer for the inflight exception
- `type_id`: the type info index for the exception type
This operation is expected to be the first operation in the unwind
destination basic blocks of a `cir.try_call` operation.

The `cleanup` attribute indicates that clean up code must be run before the
values produced by this operation are used to dispatch the exception. This
cleanup code must be executed even if the exception is not caught.
This helps CIR to pass down more accurate information for LLVM lowering
to landingpads.

Example:

```mlir
%exception_ptr, %type_id = cir.eh.inflight_exception
%exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
%exception_ptr, %type_id = cir.eh.inflight_exception cleanup
``
}];

let arguments = (ins UnitAttr:$cleanup,
OptionalAttr<FlatSymbolRefArrayAttr>:$catch_type_list);
let results = (outs CIR_VoidPtrType:$exception_ptr, CIR_UInt32:$type_id);
let assemblyFormat = [{
(`cleanup` $cleanup^)?
($catch_type_list^)?
attr-dict
}];
}

//===----------------------------------------------------------------------===//
// Atomic operations
//===----------------------------------------------------------------------===//
Expand Down
84 changes: 84 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3065,6 +3065,90 @@ mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite(
return mlir::success();
}

static mlir::LLVM::LLVMStructType
getLLVMLandingPadStructTy(mlir::ConversionPatternRewriter &rewriter) {
// Create the landing pad type: struct { ptr, i32 }
mlir::MLIRContext *ctx = rewriter.getContext();
auto llvmPtr = mlir::LLVM::LLVMPointerType::get(ctx);
llvm::SmallVector<mlir::Type> structFields = {llvmPtr, rewriter.getI32Type()};
return mlir::LLVM::LLVMStructType::getLiteral(ctx, structFields);
}

mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a CIR -> LLVM test here

cir::EhInflightOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
auto llvmFn = op->getParentOfType<mlir::LLVM::LLVMFuncOp>();
assert(llvmFn && "expected LLVM function parent");
mlir::Block *entryBlock = &llvmFn.getRegion().front();
assert(entryBlock->isEntryBlock());

mlir::ArrayAttr catchListAttr = op.getCatchTypeListAttr();
mlir::SmallVector<mlir::Value> catchSymAddrs;

auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
mlir::Location loc = op.getLoc();

// %landingpad = landingpad { ptr, i32 }
// Note that since llvm.landingpad has to be the first operation on the
// block, any needed value for its operands has to be added somewhere else.
if (catchListAttr) {
// catch ptr @_ZTIi
// catch ptr @_ZTIPKc
for (mlir::Attribute catchAttr : catchListAttr) {
auto symAttr = cast<mlir::FlatSymbolRefAttr>(catchAttr);
// Generate `llvm.mlir.addressof` for each symbol, and place those
// operations in the LLVM function entry basic block.
mlir::OpBuilder::InsertionGuard guard(rewriter);
rewriter.setInsertionPointToStart(entryBlock);
mlir::Value addrOp = mlir::LLVM::AddressOfOp::create(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit strange that the LLVM landingpad op doesn't take flat symbol references. Won't this always be a global object?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if it takes ValueRange to be able to represent a null value 🤔

rewriter, loc, llvmPtrTy, symAttr.getValue());
catchSymAddrs.push_back(addrOp);
}
} else if (!op.getCleanup()) {
// We need to emit catch-all only if cleanup is not set, because when we
// have catch-all handler, there is no case when we set would unwind past
// the handler
mlir::OpBuilder::InsertionGuard guard(rewriter);
rewriter.setInsertionPointToStart(entryBlock);
mlir::Value nullOp = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy);
catchSymAddrs.push_back(nullOp);
}

// %slot = extractvalue { ptr, i32 } %x, 0
// %selector = extractvalue { ptr, i32 } %x, 1
mlir::LLVM::LLVMStructType llvmLandingPadStructTy =
getLLVMLandingPadStructTy(rewriter);
auto landingPadOp = mlir::LLVM::LandingpadOp::create(
rewriter, loc, llvmLandingPadStructTy, catchSymAddrs);

if (op.getCleanup())
landingPadOp.setCleanup(true);

mlir::Value slot =
mlir::LLVM::ExtractValueOp::create(rewriter, loc, landingPadOp, 0);
mlir::Value selector =
mlir::LLVM::ExtractValueOp::create(rewriter, loc, landingPadOp, 1);
rewriter.replaceOp(op, mlir::ValueRange{slot, selector});

// Landing pads are required to be in LLVM functions with personality
// attribute.
// TODO(cir): for now hardcode personality creation in order to start
// adding exception tests, once we annotate CIR with such information,
// change it to be in FuncOp lowering instead.
mlir::OpBuilder::InsertionGuard guard(rewriter);
// Insert personality decl before the current function.
rewriter.setInsertionPoint(llvmFn);
auto personalityFnTy =
mlir::LLVM::LLVMFunctionType::get(rewriter.getI32Type(), {},
/*isVarArg=*/true);

const StringRef fnName = "__gxx_personality_v0";
createLLVMFuncOpIfNotExist(rewriter, op, fnName, personalityFnTy);
llvmFn.setPersonality(fnName);

return mlir::success();
}

mlir::LogicalResult CIRToLLVMTrapOpLowering::matchAndRewrite(
cir::TrapOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
Expand Down
40 changes: 40 additions & 0 deletions clang/test/CIR/IR/eh-inflight.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// RUN: cir-opt %s --verify-roundtrip | FileCheck %s

!u8i = !cir.int<u, 8>

module {

cir.func dso_local @inflight_exception() {
%exception_ptr, %type_id = cir.eh.inflight_exception
cir.return
}

// CHECK: cir.func dso_local @inflight_exception() {
// CHECK: %exception_ptr, %type_id = cir.eh.inflight_exception
// CHECK: cir.return
// CHECK: }

cir.func dso_local @inflight_exception_with_cleanup() {
%exception_ptr, %type_id = cir.eh.inflight_exception cleanup
cir.return
}

// CHECK: cir.func dso_local @inflight_exception_with_cleanup() {
// CHECK: %exception_ptr, %type_id = cir.eh.inflight_exception cleanup
// CHECK: cir.return
// CHECK: }

cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i>
cir.global "private" constant external @_ZTIPKc : !cir.ptr<!u8i>

cir.func dso_local @inflight_exception_with_catch_type_list() {
%exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
cir.return
}

// CHECK: cir.func dso_local @inflight_exception_with_catch_type_list() {
// CHECK: %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
// CHECK: cir.return
// CHECK:}

}
53 changes: 53 additions & 0 deletions clang/test/CIR/Lowering/eh-inflight.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// RUN: cir-opt %s -cir-to-llvm -o %t.cir

!u8i = !cir.int<u, 8>

module {

// CHECK: llvm.func @__gxx_personality_v0(...) -> i32

cir.func @inflight_exception() {
%exception_ptr, %type_id = cir.eh.inflight_exception
cir.return
}

// CHECK: llvm.func @inflight_exception() attributes {personality = @__gxx_personality_v0} {
// CHECK: %[[CONST_0:.*]] = llvm.mlir.zero : !llvm.ptr
// CHECK: %[[LP:.*]] = llvm.landingpad (catch %[[CONST_0]] : !llvm.ptr) : !llvm.struct<(ptr, i32)>
// CHECK: %[[EXCEPTION_PTR:.*]] = llvm.extractvalue %[[LP]][0] : !llvm.struct<(ptr, i32)>
// CHECK: %[[TYPE_ID:.*]] = llvm.extractvalue %[[LP]][1] : !llvm.struct<(ptr, i32)>
// CHECK: llvm.return
// CHECK: }

cir.func @inflight_exception_with_cleanup() {
%exception_ptr, %type_id = cir.eh.inflight_exception cleanup
cir.return
}

// CHECK: llvm.func @inflight_exception_with_cleanup() attributes {personality = @__gxx_personality_v0} {
// CHECK: %[[LP:.*]] = llvm.landingpad cleanup : !llvm.struct<(ptr, i32)>
// CHECK: %[[EXCEPTION_PTR:.*]] = llvm.extractvalue %[[LP]][0] : !llvm.struct<(ptr, i32)>
// CHECK: %[[TYPE_ID:.*]] = llvm.extractvalue %[[LP]][1] : !llvm.struct<(ptr, i32)>
// CHECK: llvm.return
// CHECK: }


cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i>
cir.global "private" constant external @_ZTIPKc : !cir.ptr<!u8i>

cir.func @inflight_exception_with_catch_type_list() {
%exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
cir.return
}

// CHECK: llvm.func @inflight_exception_with_catch_type_list() attributes {personality = @__gxx_personality_v0} {
// CHECK: %[[TI_1_ADDR:.*]] = llvm.mlir.addressof @_ZTIPKc : !llvm.ptr
// CHECK: %[[TI_2_ADDR:.*]] = llvm.mlir.addressof @_ZTIi : !llvm.ptr
// CHECK: %[[LP:.*]] = llvm.landingpad (catch %[[TI_2_ADDR]] : !llvm.ptr) (catch %[[TI_1_ADDR]] : !llvm.ptr) : !llvm.struct<(ptr, i32)>
// CHECK: %[[EXCEPTION_PTR:.*]] = llvm.extractvalue %[[LP]][0] : !llvm.struct<(ptr, i32)>
// CHECK: %[[TYPE_ID:.*]] = llvm.extractvalue %[[LP]][1] : !llvm.struct<(ptr, i32)>
// CHECK: llvm.return
// CHECK: }


}
Loading