From da8e0ede0b3ee6c55ca8a90de4d98ce0a8535578 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Mon, 12 Feb 2024 16:19:28 -0800 Subject: [PATCH] [CIR][CIRGen][Exceptions] Prep work for using cir.try_call outside cir.try The final destination here is to support cir.try_calls that are not within a `try {}` statement in C++. This only affect untested paths that will assert a bit later than before, testcase coming soon. --- clang/lib/CIR/CodeGen/CIRGenCleanup.cpp | 54 ++++++++++++++++++- clang/lib/CIR/CodeGen/CIRGenException.cpp | 47 ++++++++++++++-- clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 ++ .../CodeGen/UnimplementedFeatureGuarding.h | 12 +++-- 4 files changed, 106 insertions(+), 10 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp index 6ed4c7049d83..7dc94348368b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp @@ -16,6 +16,8 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Support/SaveAndRestore.h" + #include "CIRGenCleanup.h" #include "CIRGenFunction.h" @@ -159,6 +161,7 @@ void CIRGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { auto *EHEntry = Scope.getCachedEHDispatchBlock(); assert(Scope.hasEHBranches() == (EHEntry != nullptr)); bool RequiresEHCleanup = (EHEntry != nullptr); + EHScopeStack::stable_iterator EHParent = Scope.getEnclosingEHScope(); // Check the three conditions which might require a normal cleanup: @@ -270,7 +273,50 @@ void CIRGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // Emit the EH cleanup if required. if (RequiresEHCleanup) { - llvm_unreachable("NYI"); + // FIXME(cir): should we guard insertion point here? + auto *NextAction = getEHDispatchBlock(EHParent); + (void)NextAction; + + // Push a terminate scope or cleanupendpad scope around the potentially + // throwing cleanups. For funclet EH personalities, the cleanupendpad models + // program termination when cleanups throw. + bool PushedTerminate = false; + SaveAndRestore RestoreCurrentFuncletPad(CurrentFuncletPad); + mlir::Operation *CPI = nullptr; + + const EHPersonality &Personality = EHPersonality::get(*this); + if (Personality.usesFuncletPads()) { + llvm_unreachable("NYI"); + } + + // Non-MSVC personalities need to terminate when an EH cleanup throws. + if (!Personality.isMSVCPersonality()) { + EHStack.pushTerminate(); + PushedTerminate = true; + } else if (IsEHa && getInvokeDest()) { + llvm_unreachable("NYI"); + } + + // We only actually emit the cleanup code if the cleanup is either + // active or was used before it was deactivated. + if (EHActiveFlag.isValid() || IsActive) { + cleanupFlags.setIsForEHCleanup(); + buildCleanup(*this, Fn, cleanupFlags, EHActiveFlag); + } + + // In LLVM traditional codegen, here's where it branches off to + // NextAction. + if (CPI) + llvm_unreachable("NYI"); + + // Leave the terminate scope. + if (PushedTerminate) + EHStack.popTerminate(); + + // FIXME(cir): LLVM traditional codegen tries to simplify some of the + // codegen here. Once we are further down with EH support revisit whether we + // need to this during lowering. + assert(!UnimplementedFeature::simplifyCleanupEntry()); } } @@ -470,3 +516,9 @@ EHCatchScope *EHScopeStack::pushCatch(unsigned numHandlers) { InnermostEHScope = stable_begin(); return scope; } + +void EHScopeStack::pushTerminate() { + char *Buffer = allocate(EHTerminateScope::getSize()); + new (Buffer) EHTerminateScope(InnermostEHScope); + InnermostEHScope = stable_begin(); +} \ No newline at end of file diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index c7228393e6fb..1c0b686154f4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -621,7 +621,7 @@ mlir::Operation *CIRGenFunction::buildLandingPad() { EHScope &innermostEHScope = *EHStack.find(EHStack.getInnermostEHScope()); switch (innermostEHScope.getKind()) { case EHScope::Terminate: - llvm_unreachable("NYI"); + return getTerminateLandingPad(); case EHScope::Catch: case EHScope::Cleanup: @@ -635,7 +635,38 @@ mlir::Operation *CIRGenFunction::buildLandingPad() { // are enabled but a throwing function is called anyways. auto catchOp = currLexScope->getExceptionInfo().catchOp; if (!catchOp) { - llvm_unreachable("NYI"); + auto loc = *currSrcLoc; + auto ehPtrTy = mlir::cir::PointerType::get( + getBuilder().getContext(), + getBuilder().getType<::mlir::cir::ExceptionInfoType>()); + + mlir::Value exceptionAddr; + { + // Get a new alloca within the current scope. + mlir::OpBuilder::InsertionGuard guard(builder); + exceptionAddr = buildAlloca( + "__exception_ptr", ehPtrTy, loc, CharUnits::One(), + builder.getBestAllocaInsertPoint(builder.getInsertionBlock())); + } + + { + // Insert catch at the end of the block, and place the insert pointer + // back to where it was. + mlir::OpBuilder::InsertionGuard guard(builder); + auto exceptionPtr = + builder.create(loc, ehPtrTy, exceptionAddr); + catchOp = builder.create( + loc, exceptionPtr, + [&](mlir::OpBuilder &b, mlir::Location loc, + mlir::OperationState &result) { + // There's no source code level catch here, create one region for + // the resume block. + mlir::OpBuilder::InsertionGuard guard(b); + auto *r = result.addRegion(); + builder.createBlock(r); + }); + } + currLexScope->setExceptionInfo({exceptionAddr, catchOp}); } { @@ -660,7 +691,7 @@ mlir::Operation *CIRGenFunction::buildLandingPad() { switch (I->getKind()) { case EHScope::Cleanup: // If we have a cleanup, remember that. - llvm_unreachable("NYI"); + hasCleanup = (hasCleanup || cast(*I).isEHCleanup()); continue; case EHScope::Filter: { @@ -717,7 +748,8 @@ mlir::Operation *CIRGenFunction::buildLandingPad() { // Otherwise, signal that we at least have cleanups. } else if (hasCleanup) { - llvm_unreachable("NYI"); + // FIXME(cir): figure out whether and how we need this in CIR. + assert(!UnimplementedFeature::setLandingPadCleanup()); } assert((clauses.size() > 0 || hasCleanup) && "CatchOp has no clauses!"); @@ -782,7 +814,8 @@ CIRGenFunction::getEHDispatchBlock(EHScopeStack::stable_iterator si) { } case EHScope::Cleanup: - llvm_unreachable("NYI"); + assert(!UnimplementedFeature::setLandingPadCleanup()); + dispatchBlock = currLexScope->getOrCreateCleanupBlock(builder); break; case EHScope::Filter: @@ -850,3 +883,7 @@ mlir::Operation *CIRGenFunction::getInvokeDestImpl() { return LP; } + +mlir::Operation *CIRGenFunction::getTerminateLandingPad() { + llvm_unreachable("NYI"); +} \ No newline at end of file diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index bb1b433a60c9..ea4a3ffae685 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -900,6 +900,9 @@ class CIRGenFunction : public CIRGenTypeCache { return false; } + /// Return a landing pad that just calls terminate. + mlir::Operation *getTerminateLandingPad(); + /// Emit code to compute the specified expression, /// ignoring the result. void buildIgnoredExpr(const clang::Expr *E); diff --git a/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h b/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h index daba1ab454a3..0214e7ae521a 100644 --- a/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h +++ b/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h @@ -79,8 +79,6 @@ struct UnimplementedFeature { static bool variablyModifiedTypeEmission() { return false; } static bool buildLValueAlignmentAssumption() { return false; } static bool buildDerivedToBaseCastForDevirt() { return false; } - static bool emitStartEHSpec() { return false; } - static bool emitEndEHSpec() { return false; } static bool emitFunctionEpilog() { return false; } // Data layout @@ -110,6 +108,14 @@ struct UnimplementedFeature { static bool fastMathFlags() { return false; } static bool fastMathFuncAttributes() { return false; } + // Exception handling + static bool setLandingPadCleanup() { return false; } + static bool isSEHTryScope() { return false; } + static bool ehStack() { return false; } + static bool emitStartEHSpec() { return false; } + static bool emitEndEHSpec() { return false; } + static bool simplifyCleanupEntry() { return false; } + // Type qualifiers. static bool atomicTypes() { return false; } static bool volatileTypes() { return false; } @@ -128,7 +134,6 @@ struct UnimplementedFeature { static bool openMP() { return false; } static bool openMPRuntime() { return false; } static bool openMPTarget() { return false; } - static bool ehStack() { return false; } static bool isVarArg() { return false; } static bool setNonGC() { return false; } static bool volatileLoadOrStore() { return false; } @@ -151,7 +156,6 @@ struct UnimplementedFeature { static bool exceptions() { return false; } static bool metaDataNode() { return false; } static bool emitDeclMetadata() { return false; } - static bool isSEHTryScope() { return false; } static bool emitScalarRangeCheck() { return false; } static bool stmtExprEvaluation() { return false; } static bool setCallingConv() { return false; }