Skip to content
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

[CIR][CodeGen] Add initial support for __cxa_rethrow #1290

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

bruteforceboy
Copy link
Contributor

@bruteforceboy bruteforceboy commented Jan 23, 2025

This PR adds an initial support for __cxa_rethrow, and one test that produces a rethrow.

I am very open to suggestions regarding this PR, because I'm still a bit unsure if this replicates the original codegen properly. For example, using the test added, the OG CodeGen produces:

entry:
  invoke void @_ZN1SC2Ev(ptr noundef nonnull align 1 dereferenceable(1) %s)
          to label %invoke.cont unwind label %lpad

invoke.cont:                                      ; preds = %entry
  invoke void @__cxa_rethrow() #2
          to label %unreachable unwind label %lpad

lpad:                                             ; preds = %invoke.cont, %entry
  %0 = landingpad { ptr, i32 }
          catch ptr null
  %1 = extractvalue { ptr, i32 } %0, 0
  store ptr %1, ptr %exn.slot, align 8
  %2 = extractvalue { ptr, i32 } %0, 1
  store i32 %2, ptr %ehselector.slot, align 4
  br label %catch

catch:                                            ; preds = %lpad
  %exn = load ptr, ptr %exn.slot, align 8
  %3 = call ptr @__cxa_begin_catch(ptr %exn) #3
  %4 = load i32, ptr %r, align 4
  %inc = add nsw i32 %4, 1
  store i32 %inc, ptr %r, align 4
  call void @__cxa_end_catch()
  br label %try.cont

and the proposed CIR equivalent produces:

  invoke void @_ZN1SC2Ev(ptr %1)
          to label %5 unwind label %9

5:                                                ; preds = %4
  invoke void @__cxa_rethrow()
          to label %6 unwind label %13

6:                                                ; preds = %5
  br label %7

7:                                                ; preds = %6
  unreachable

8:                                                ; No predecessors!
  br label %22

9:                                                ; preds = %4
  %10 = landingpad { ptr, i32 }
          catch ptr null
  %11 = extractvalue { ptr, i32 } %10, 0
  %12 = extractvalue { ptr, i32 } %10, 1
  br label %17

13:                                               ; preds = %5
  %14 = landingpad { ptr, i32 }
          catch ptr null
  %15 = extractvalue { ptr, i32 } %14, 0
  %16 = extractvalue { ptr, i32 } %14, 1
  br label %17

17:                                               ; preds = %13, %9
  %18 = phi ptr [ %11, %9 ], [ %15, %13 ]
  %19 = call ptr @__cxa_begin_catch(ptr %18)
  %20 = load i32, ptr %2, align 4
  %21 = add i32 %20, 1
  store i32 %21, ptr %2, align 4
  call void @__cxa_end_catch()
  br label %22

There are quite a number of differences: phi in the CIR version VS loading from %exn.slot in the OG, having multiple landing pads, etc.

The CIR version still seems reasonable to me, mostly because currently we are unable to replicate the exact behavior of the OG codegen. Again, I am very open to more discussions and suggestions here)

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Overall this looks good, some minor change needed.

There are quite a number of differences: phi in the CIR version VS loading from %exn.slot in the OG, having multiple landing pads, etc.

Yes, we use block arguments for the slots, and don't unique landing pads (yet). We might stay with the former but fix the later.

The CIR version still seems reasonable to me, mostly because currently we are unable to replicate the exact behavior of the OG codegen. Again, I am very open to more discussions and suggestions here)

I think it's similar enough!

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp Outdated Show resolved Hide resolved
@bcardosolopes
Copy link
Member

We just went over a rebase against upstream, apologies for the churn but please update your branch for this PR and force-push!

cir::FuncType FTy =
CGF.getBuilder().getFuncType({}, CGF.getBuilder().getVoidTy());

auto Fn = CGF.CGM.createRuntimeFunction(FTy, "__cxa_rethrow");
Copy link
Member

Choose a reason for hiding this comment

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

Instead of calling this function, you should emit ThrowOp here (the version without operands, see rethrows() defined in the operation) and change LLVM lowering (CIRToLLVMThrowOpLowering::matchAndRewrite) to handle the rethrows() version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be honest, I think this is fine as it is. A few reasons:

  • The OG does it this way
  • emitRethrow currently doesn't have any source location information to create a ThrowOp as it is. Of course this is easy to update, I just need to pass the CXXThrowExpr but I think its worth mentioning.
  • I am not quite sure how the LLVM lowering will work when we have to lower a ThrowOp that is the rethrow version.

For now, I have updated the PR, so we have this structure:

  • emitRuntimeFunction
  • emitTryCallOp
  • emitUnreachable

So, this gets rid of all the basic blocks and ignores all potential code after the throw. I also added llvm output to the tests so you can see.

If you still insist on using the ThrowOp, it would be nice to get some suggestions on how to implement the LLVM lowering for rethrows in CIRToLLVMThrowOpLowering.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the updates, this looks almost there.

currently doesn't have any source location information to create a ThrowOp as it is

in this case we use assert(CGF.currSrcLoc && "expected source location"); followed by the actual use of CGF.currSrcLoc.

I am not quite sure how the LLVM lowering will work when we have to lower a ThrowOp that is the rethrow version.

It's actually way easier than the more complex version in CIRToLLVMThrowOpLowering::matchAndRewrite. Note how we create a declaration for __cxa_throw and pass down some params. You can identify if the ThrowOp is rethrow by calling op.rethrows(), like explained before. You then create a declaration for __cxa_rethrow...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can identify if the ThrowOp is rethrow by calling op.rethrows(), like explained before. You then create a declaration for __cxa_rethrow...

Yes, I got this. Okay, I think another thing I forgot to explain is why I used createTryCallOp. The createTryCallOp gets flattened and creates a landing pad, because we need the call to __cxa_rethrow to be an invoke, but if we go through the CIRToLLVMThrowOpLowering path, how can we make an invoke + landing pad instead of just a call after creating the declaration? It seems currently even emitThrow has no landing pad logic implemented. TLDR: how do we build a landing pad for the rethrow from CIRToLLVMThrowOpLowering?

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp Outdated Show resolved Hide resolved
clang/test/CIR/CodeGen/try-catch-dtors.cpp Outdated Show resolved Hide resolved

mlir::Block *beforeCatch = rewriter.getInsertionBlock();
rewriter.setInsertionPointToEnd(beforeCatch);
rewriter.replaceOpWithNewOp<cir::BrOp>(tryBodyYield, afterTry);

if (auto tryBodyYield = dyn_cast<cir::YieldOp>(afterBody->getTerminator()))
Copy link
Member

Choose a reason for hiding this comment

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

Add a comment here explaining that some other terminator might be present here, e.g. unreachable

cir::FuncType FTy =
CGF.getBuilder().getFuncType({}, CGF.getBuilder().getVoidTy());

auto Fn = CGF.CGM.createRuntimeFunction(FTy, "__cxa_rethrow");
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the updates, this looks almost there.

currently doesn't have any source location information to create a ThrowOp as it is

in this case we use assert(CGF.currSrcLoc && "expected source location"); followed by the actual use of CGF.currSrcLoc.

I am not quite sure how the LLVM lowering will work when we have to lower a ThrowOp that is the rethrow version.

It's actually way easier than the more complex version in CIRToLLVMThrowOpLowering::matchAndRewrite. Note how we create a declaration for __cxa_throw and pass down some params. You can identify if the ThrowOp is rethrow by calling op.rethrows(), like explained before. You then create a declaration for __cxa_rethrow...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants