-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[CIR] Upstream Exception ThrowOp with subexpr #161818
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 2 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 |
|---|---|---|
|
|
@@ -4166,6 +4166,40 @@ def CIR_ThrowOp : CIR_Op<"throw"> { | |
| let hasVerifier = 1; | ||
| } | ||
|
|
||
| //===----------------------------------------------------------------------===// | ||
| // AllocExceptionOp | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| def CIR_AllocExceptionOp : CIR_Op<"alloc.exception"> { | ||
| let summary = "Allocates an exception according to Itanium ABI"; | ||
| let description = [{ | ||
| Implements a slightly higher level __cxa_allocate_exception: | ||
|
|
||
| `void *__cxa_allocate_exception(size_t thrown_size);` | ||
|
|
||
| If operation fails, program terminates, not throw. | ||
|
|
||
| Example: | ||
|
|
||
| ```mlir | ||
| // if (b == 0) { | ||
| // ... | ||
| // throw "..."; | ||
| cir.if %10 { | ||
| %11 = cir.alloc_exception 8 -> !cir.ptr<!void> | ||
| ... // store exception content into %11 | ||
| cir.throw %11 : !cir.ptr<!cir.ptr<!u8i>>, ... | ||
| ``` | ||
| }]; | ||
|
|
||
| let arguments = (ins I64Attr:$size); | ||
| let results = (outs Res<CIR_PointerType, "", [MemAlloc<DefaultResource>]>:$addr); | ||
|
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. Can we be more specific about the return type? For instance, is the return type always ptr-to-ptr-to-ui8? 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. From the CIR code, yes, it can be 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 see now. We're allocating an exception for a throw expression, and since we know the throw type, we allocate the exception accordingly. OGCG is just emitting a call to __cxa_allocate_exception, which it "casts" to the correct type, but since OGCG uses opaque types the cast is never visible. |
||
|
|
||
| let assemblyFormat = [{ | ||
| $size `->` qualified(type($addr)) attr-dict | ||
| }]; | ||
| } | ||
|
|
||
| //===----------------------------------------------------------------------===// | ||
| // Atomic operations | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -178,3 +178,36 @@ void CIRGenFunction::popCleanupBlocks( | |
| popCleanupBlock(); | ||
| } | ||
| } | ||
|
|
||
| void CIRGenFunction::deactivateCleanupBlock( | ||
| EHScopeStack::stable_iterator cleanup, mlir::Operation *dominatingIP) { | ||
| assert(cleanup != ehStack.stable_end() && "deactivating bottom of stack?"); | ||
| EHCleanupScope &scope = cast<EHCleanupScope>(*ehStack.find(cleanup)); | ||
| assert(scope.isActive() && "double deactivation"); | ||
|
|
||
| // If it's the top of the stack, just pop it, but do so only if it belongs | ||
| // to the current RunCleanupsScope. | ||
| if (cleanup == ehStack.stable_begin() && | ||
| currentCleanupStackDepth.strictlyEncloses(cleanup)) { | ||
|
|
||
| // Per comment below, checking EHAsynch is not really necessary | ||
|
||
| // it's there to assure zero-impact w/o EHAsynch option | ||
| if (!scope.isNormalCleanup() && getLangOpts().EHAsynch) { | ||
| cgm.errorNYI("deactivateCleanupBlock: EHAsynch & non-normal cleanup"); | ||
| return; | ||
| } | ||
|
|
||
| // From LLVM: If it's a normal cleanup, we need to pretend that the | ||
| // fallthrough is unreachable. | ||
| // CIR remarks: LLVM uses an empty insertion point to signal behavior | ||
| // change to other codegen paths (triggered by PopCleanupBlock). | ||
| // CIRGen doesn't do that yet, but let's mimic just in case. | ||
| mlir::OpBuilder::InsertionGuard guard(builder); | ||
| builder.clearInsertionPoint(); | ||
| popCleanupBlock(); | ||
| return; | ||
| } | ||
|
|
||
| // Otherwise, follow the general case. | ||
| cgm.errorNYI("deactivateCleanupBlock: the general case"); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,11 +31,36 @@ void CIRGenFunction::emitCXXThrowExpr(const CXXThrowExpr *e) { | |
| if (throwType->isObjCObjectPointerType()) { | ||
| cgm.errorNYI("emitCXXThrowExpr ObjCObjectPointerType"); | ||
| return; | ||
| } else { | ||
| cgm.errorNYI("emitCXXThrowExpr with subExpr"); | ||
| return; | ||
| } | ||
| } else { | ||
| cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true); | ||
|
|
||
| cgm.getCXXABI().emitThrow(*this, e); | ||
| return; | ||
| } | ||
|
|
||
| cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true); | ||
| } | ||
|
|
||
| void CIRGenFunction::emitAnyExprToExn(const Expr *e, Address addr) { | ||
| // Make sure the exception object is cleaned up if there's an | ||
| // exception during initialization. | ||
| assert(!cir::MissingFeatures::ehCleanupScope()); | ||
|
|
||
| // __cxa_allocate_exception returns a void*; we need to cast this | ||
| // to the appropriate type for the object. | ||
| mlir::Type ty = convertTypeForMem(e->getType()); | ||
| Address typedAddr = addr.withElementType(builder, ty); | ||
|
|
||
| // From LLVM's codegen: | ||
| // FIXME: this isn't quite right! If there's a final unelided call | ||
| // to a copy constructor, then according to [except.terminate]p1 we | ||
| // must call std::terminate() if that constructor throws, because | ||
| // technically that copy occurs after the exception expression is | ||
| // evaluated but before the exception is caught. But the best way | ||
| // to handle that is to teach EmitAggExpr to do the final copy | ||
| // differently if it can't be elided. | ||
| emitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(), | ||
| /*isInitializer=*/true); | ||
|
|
||
| // Deactivate the cleanup block. | ||
| assert(!cir::MissingFeatures::ehCleanupScope()); | ||
|
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. It's not clear to me why you aren't calling 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. Without |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,7 @@ | |
| // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -emit-llvm %s -o %t.ll | ||
| // RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG | ||
|
|
||
| void foo() { | ||
| void rethrow() { | ||
| throw; | ||
| } | ||
|
|
||
|
|
@@ -18,7 +18,7 @@ void foo() { | |
| // OGCG: call void @__cxa_rethrow() | ||
| // OGCG: unreachable | ||
|
|
||
| int foo1(int a, int b) { | ||
| int rethrow_from_block(int a, int b) { | ||
| if (b == 0) | ||
| throw; | ||
| return a / b; | ||
|
|
@@ -83,3 +83,43 @@ int foo1(int a, int b) { | |
| // OGCG: %[[TMP_B:.*]] = load i32, ptr %[[B_ADDR]], align 4 | ||
| // OGCG: %[[DIV_A_B:.*]] = sdiv i32 %[[TMP_A]], %[[TMP_B]] | ||
| // OGCG: ret i32 %[[DIV_A_B]] | ||
|
|
||
| void throw_scalar() { | ||
| throw 1; | ||
| } | ||
|
|
||
| // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i> | ||
| // CIR: %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<1> : !s32i | ||
| // CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !s32i, !cir.ptr<!s32i> | ||
| // CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, @_ZTIi | ||
| // CIR: cir.unreachable | ||
|
|
||
| // LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4) | ||
| // LLVM: store i32 1, ptr %[[EXCEPTION_ADDR]], align 16 | ||
| // LLVM: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null) | ||
| // LLVM: unreachable | ||
|
|
||
| // OGCG: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4) | ||
| // OGCG: store i32 1, ptr %[[EXCEPTION_ADDR]], align 16 | ||
| // OGCG: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null) | ||
| // OGCG: unreachable | ||
|
|
||
| void paren_expr() { (throw 0, 1 + 2); } | ||
|
|
||
| // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i> | ||
|
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.
|
||
| // CIR: %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<0> : !s32i | ||
| // CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !s32i, !cir.ptr<!s32i> | ||
| // CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, @_ZTIi | ||
| // CIR: cir.unreachable | ||
| // CIR: ^bb1: | ||
| // CIR: %[[CONST_1:.*]] = cir.const #cir.int<1> : !s32i | ||
| // CIR: %[[CONST_2:.*]] = cir.const #cir.int<2> : !s32i | ||
| // CIR: %[[ADD:.*]] = cir.binop(add, %[[CONST_1]], %[[CONST_2]]) nsw : !s32i | ||
|
|
||
| // LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4) | ||
| // LLVM: store i32 0, ptr %[[EXCEPTION_ADDR]], align 16 | ||
| // LLVM: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null) | ||
|
|
||
| // OGCG: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4) | ||
| // OGCG: store i32 0, ptr %[[EXCEPTION_ADDR]], align 16 | ||
| // OGCG: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null) | ||
Uh oh!
There was an error while loading. Please reload this page.