Skip to content

[Exception] landing pad like logic is NYI and some related question to exception handling #1183

Open
@ChuanqiXu9

Description

@ChuanqiXu9

Reproducer:

class Error {
public:
  Error(const char *) noexcept;
};
void test()
{
  struct Guard {
    ~Guard() {}
  } __guard;

  throw Error("testing");
}

it will trigger

assert(!CGF.isInvokeDest() && "landing pad like logic NYI");

In the traditional pipeline, we would generate an invoke and one to unreachable BB and one to the invoke dest BB.

I tried to take a look. But the logics about creating and handling for invoke dest BB looks pretty much more different than the traditional pipeline.

e.g., in clangir, the methods to getInvokeDestImpl and emitLandingPad will take a tryOp argument

mlir::Operation *emitLandingPad(cir::TryOp tryOp);
void emitEHResumeBlock(bool isCleanup, mlir::Block *ehResumeBlock,
mlir::Location loc);
mlir::Block *getEHResumeBlock(bool isCleanup, cir::TryOp tryOp);
mlir::Block *getEHDispatchBlock(EHScopeStack::stable_iterator scope,
cir::TryOp tryOp);
/// Unified block containing a call to cir.resume
mlir::Block *ehResumeBlock = nullptr;
llvm::DenseMap<mlir::Block *, mlir::Block *> cleanupsToPatch;
/// The cleanup depth enclosing all the cleanups associated with the
/// parameters.
EHScopeStack::stable_iterator PrologueCleanupDepth;
mlir::Operation *getInvokeDestImpl(cir::TryOp tryOp);
mlir::Operation *getInvokeDest(cir::TryOp tryOp) {

while it is not the case in traditional pipeline

llvm::BasicBlock *EmitLandingPad();
llvm::BasicBlock *getInvokeDestImpl();

And also the return type in traditional pipeline is Block but the return type in CIR is Operation.

Other example including CIRGenFunction::getEHDispatchBlock, where the implementation in traditional pipeline looks much simpler:

case EHScope::Cleanup:
dispatchBlock = createBasicBlock("ehcleanup");
break;
case EHScope::Filter:
dispatchBlock = createBasicBlock("filter.dispatch");
break;
case EHScope::Terminate:
dispatchBlock = getTerminateHandler();
break;
}

than in CIR:

case EHScope::Catch: {
// LLVM does some optimization with branches here, CIR just keep track of
// the corresponding calls.
assert(callWithExceptionCtx && "expected call information");
{
mlir::OpBuilder::InsertionGuard guard(getBuilder());
assert(callWithExceptionCtx.getCleanup().empty() &&
"one per call: expected empty region at this point");
dispatchBlock = builder.createBlock(&callWithExceptionCtx.getCleanup());
builder.createYield(callWithExceptionCtx.getLoc());
}
break;
}
case EHScope::Cleanup: {
if (callWithExceptionCtx && "expected call information") {
mlir::OpBuilder::InsertionGuard guard(getBuilder());
assert(callWithExceptionCtx.getCleanup().empty() &&
"one per call: expected empty region at this point");
dispatchBlock = builder.createBlock(&callWithExceptionCtx.getCleanup());
builder.createYield(callWithExceptionCtx.getLoc());
} else {
// Usually coming from general cir.scope cleanups that aren't
// tried to a specific throwing call.
assert(currLexScope && currLexScope->isRegular() &&
"expected regular cleanup");
dispatchBlock = currLexScope->getOrCreateCleanupBlock(builder);
if (dispatchBlock->empty()) {
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToEnd(dispatchBlock);
mlir::Location loc =
currSrcLoc ? *currSrcLoc : builder.getUnknownLoc();
builder.createYield(loc);
}
}
break;
}


So I am pretty confused and have some questions.

  1. What is the design for supporting exceptions in CIR and what is the core design ideas?

Since I noticed the difference in supporting exceptions in CIR are traditional pipeline are much more different than other places I know. I guess there are some internal reasons and I want to know our plan for it.

  1. Can we move these handling for exceptions to later passes?

Given the existing implementation already differs from the traditional pipeline, I feel may be it will be good to move these handlings to later passes. It is helpful to make the CIRGen smaller and helpful for the analysis purpose. e.g., the generated CIR code may look like the codes in the frontend more.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions