aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/CodeGen/CGException.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-07-29 20:15:26 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-07-29 20:15:26 +0000
commit344a3780b2e33f6ca763666c380202b18aab72a3 (patch)
treef0b203ee6eb71d7fdd792373e3c81eb18d6934dd /clang/lib/CodeGen/CGException.cpp
parentb60736ec1405bb0a8dd40989f67ef4c93da068ab (diff)
Diffstat (limited to 'clang/lib/CodeGen/CGException.cpp')
-rw-r--r--clang/lib/CodeGen/CGException.cpp153
1 files changed, 124 insertions, 29 deletions
diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index 7a64963183bc..9f65e9eb120c 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -39,6 +39,18 @@ static llvm::FunctionCallee getFreeExceptionFn(CodeGenModule &CGM) {
return CGM.CreateRuntimeFunction(FTy, "__cxa_free_exception");
}
+static llvm::FunctionCallee getSehTryBeginFn(CodeGenModule &CGM) {
+ llvm::FunctionType *FTy =
+ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
+ return CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.begin");
+}
+
+static llvm::FunctionCallee getSehTryEndFn(CodeGenModule &CGM) {
+ llvm::FunctionType *FTy =
+ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
+ return CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.end");
+}
+
static llvm::FunctionCallee getUnexpectedFn(CodeGenModule &CGM) {
// void __cxa_call_unexpected(void *thrown_exception);
@@ -467,15 +479,16 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) {
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot) {
// noexcept functions are simple terminate scopes.
- EHStack.pushTerminate();
+ if (!getLangOpts().EHAsynch) // -EHa: HW exception still can occur
+ EHStack.pushTerminate();
} else if (EST == EST_Dynamic || EST == EST_DynamicNone) {
// TODO: Revisit exception specifications for the MS ABI. There is a way to
// encode these in an object file but MSVC doesn't do anything with it.
if (getTarget().getCXXABI().isMicrosoft())
return;
- // In wasm we currently treat 'throw()' in the same way as 'noexcept'. In
+ // In Wasm EH we currently treat 'throw()' in the same way as 'noexcept'. In
// case of throw with types, we ignore it and print a warning for now.
- // TODO Correctly handle exception specification in wasm
+ // TODO Correctly handle exception specification in Wasm EH
if (CGM.getLangOpts().hasWasmExceptions()) {
if (EST == EST_DynamicNone)
EHStack.pushTerminate();
@@ -485,6 +498,19 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) {
<< FD->getExceptionSpecSourceRange();
return;
}
+ // Currently Emscripten EH only handles 'throw()' but not 'throw' with
+ // types. 'throw()' handling will be done in JS glue code so we don't need
+ // to do anything in that case. Just print a warning message in case of
+ // throw with types.
+ // TODO Correctly handle exception specification in Emscripten EH
+ if (getTarget().getCXXABI() == TargetCXXABI::WebAssembly &&
+ CGM.getLangOpts().getExceptionHandling() ==
+ LangOptions::ExceptionHandlingKind::None &&
+ EST == EST_Dynamic)
+ CGM.getDiags().Report(D->getLocation(),
+ diag::warn_wasm_dynamic_exception_spec_ignored)
+ << FD->getExceptionSpecSourceRange();
+
unsigned NumExceptions = Proto->getNumExceptions();
EHFilterScope *Filter = EHStack.pushFilter(NumExceptions);
@@ -544,7 +570,7 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
if (!FD) {
// Check if CapturedDecl is nothrow and pop terminate scope for it.
if (const CapturedDecl* CD = dyn_cast_or_null<CapturedDecl>(D)) {
- if (CD->isNothrow())
+ if (CD->isNothrow() && !EHStack.empty())
EHStack.popTerminate();
}
return;
@@ -554,7 +580,8 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
return;
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
- if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot) {
+ if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot &&
+ !EHStack.empty() /* possible empty when under async exceptions */) {
EHStack.popTerminate();
} else if (EST == EST_Dynamic || EST == EST_DynamicNone) {
// TODO: Revisit exception specifications for the MS ABI. There is a way to
@@ -610,6 +637,10 @@ void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
} else {
// No exception decl indicates '...', a catch-all.
CatchScope->setHandler(I, CGM.getCXXABI().getCatchAllTypeInfo(), Handler);
+ // Under async exceptions, catch(...) need to catch HW exception too
+ // Mark scope with SehTryBegin as a SEH __try scope
+ if (getLangOpts().EHAsynch)
+ EmitRuntimeCallOrInvoke(getSehTryBeginFn(CGM));
}
}
}
@@ -724,7 +755,7 @@ llvm::BasicBlock *CodeGenFunction::getInvokeDestImpl() {
// If exceptions are disabled/ignored and SEH is not in use, then there is no
// invoke destination. SEH "works" even if exceptions are off. In practice,
// this means that C++ destructors and other EH cleanups don't run, which is
- // consistent with MSVC's behavior.
+ // consistent with MSVC's behavior, except in the presence of -EHa
const LangOptions &LO = CGM.getLangOpts();
if (!LO.Exceptions || LO.IgnoreExceptions) {
if (!LO.Borland && !LO.MicrosoftExt)
@@ -1343,7 +1374,8 @@ namespace {
CGF.EmitBlock(RethrowBB);
if (SavedExnVar) {
CGF.EmitRuntimeCallOrInvoke(RethrowFn,
- CGF.Builder.CreateAlignedLoad(SavedExnVar, CGF.getPointerAlign()));
+ CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar,
+ CGF.getPointerAlign()));
} else {
CGF.EmitRuntimeCallOrInvoke(RethrowFn);
}
@@ -1552,17 +1584,8 @@ llvm::BasicBlock *CodeGenFunction::getTerminateFunclet() {
CurrentFuncletPad = Builder.CreateCleanupPad(ParentPad);
// Emit the __std_terminate call.
- llvm::Value *Exn = nullptr;
- // In case of wasm personality, we need to pass the exception value to
- // __clang_call_terminate function.
- if (getLangOpts().CPlusPlus &&
- EHPersonality::get(*this).isWasmPersonality()) {
- llvm::Function *GetExnFn =
- CGM.getIntrinsic(llvm::Intrinsic::wasm_get_exception);
- Exn = Builder.CreateCall(GetExnFn, CurrentFuncletPad);
- }
llvm::CallInst *terminateCall =
- CGM.getCXXABI().emitTerminateForUnexpectedException(*this, Exn);
+ CGM.getCXXABI().emitTerminateForUnexpectedException(*this, nullptr);
terminateCall->setDoesNotReturn();
Builder.CreateUnreachable();
@@ -1614,7 +1637,23 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
JumpDest TryExit = getJumpDestInCurrentScope("__try.__leave");
SEHTryEpilogueStack.push_back(&TryExit);
+
+ llvm::BasicBlock *TryBB = nullptr;
+ // IsEHa: emit an invoke to _seh_try_begin() runtime for -EHa
+ if (getLangOpts().EHAsynch) {
+ EmitRuntimeCallOrInvoke(getSehTryBeginFn(CGM));
+ if (SEHTryEpilogueStack.size() == 1) // outermost only
+ TryBB = Builder.GetInsertBlock();
+ }
+
EmitStmt(S.getTryBlock());
+
+ // Volatilize all blocks in Try, till current insert point
+ if (TryBB) {
+ llvm::SmallPtrSet<llvm::BasicBlock *, 10> Visited;
+ VolatilizeTryBlocks(TryBB, Visited);
+ }
+
SEHTryEpilogueStack.pop_back();
if (!TryExit.getBlock()->use_empty())
@@ -1625,6 +1664,35 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
ExitSEHTryStmt(S);
}
+// Recursively walk through blocks in a _try
+// and make all memory instructions volatile
+void CodeGenFunction::VolatilizeTryBlocks(
+ llvm::BasicBlock *BB, llvm::SmallPtrSet<llvm::BasicBlock *, 10> &V) {
+ if (BB == SEHTryEpilogueStack.back()->getBlock() /* end of Try */ ||
+ !V.insert(BB).second /* already visited */ ||
+ !BB->getParent() /* not emitted */ || BB->empty())
+ return;
+
+ if (!BB->isEHPad()) {
+ for (llvm::BasicBlock::iterator J = BB->begin(), JE = BB->end(); J != JE;
+ ++J) {
+ if (auto LI = dyn_cast<llvm::LoadInst>(J)) {
+ LI->setVolatile(true);
+ } else if (auto SI = dyn_cast<llvm::StoreInst>(J)) {
+ SI->setVolatile(true);
+ } else if (auto* MCI = dyn_cast<llvm::MemIntrinsic>(J)) {
+ MCI->setVolatile(llvm::ConstantInt::get(Builder.getInt1Ty(), 1));
+ }
+ }
+ }
+ const llvm::Instruction *TI = BB->getTerminator();
+ if (TI) {
+ unsigned N = TI->getNumSuccessors();
+ for (unsigned I = 0; I < N; I++)
+ VolatilizeTryBlocks(TI->getSuccessor(I), V);
+ }
+}
+
namespace {
struct PerformSEHFinally final : EHScopeStack::Cleanup {
llvm::Function *OutlinedFinally;
@@ -1702,10 +1770,8 @@ struct CaptureFinder : ConstStmtVisitor<CaptureFinder> {
void VisitDeclRefExpr(const DeclRefExpr *E) {
// If this is already a capture, just make sure we capture 'this'.
- if (E->refersToEnclosingVariableOrCapture()) {
+ if (E->refersToEnclosingVariableOrCapture())
Captures.insert(ParentThis);
- return;
- }
const auto *D = dyn_cast<VarDecl>(E->getDecl());
if (D && D->isLocalVarDeclOrParm() && D->hasLocalStorage())
@@ -1865,11 +1931,6 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF,
// Create llvm.localrecover calls for all captures.
for (const VarDecl *VD : Finder.Captures) {
- if (isa<ImplicitParamDecl>(VD)) {
- CGM.ErrorUnsupported(VD, "'this' captured by SEH");
- CXXThisValue = llvm::UndefValue::get(ConvertTypeForMem(VD->getType()));
- continue;
- }
if (VD->getType()->isVariablyModifiedType()) {
CGM.ErrorUnsupported(VD, "VLA captured by SEH");
continue;
@@ -1877,6 +1938,12 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF,
assert((isa<ImplicitParamDecl>(VD) || VD->isLocalVarDeclOrParm()) &&
"captured non-local variable");
+ auto L = ParentCGF.LambdaCaptureFields.find(VD);
+ if (L != ParentCGF.LambdaCaptureFields.end()) {
+ LambdaCaptureFields[VD] = L->second;
+ continue;
+ }
+
// If this decl hasn't been declared yet, it will be declared in the
// OutlinedStmt.
auto I = ParentCGF.LocalDeclMap.find(VD);
@@ -1884,8 +1951,30 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF,
continue;
Address ParentVar = I->second;
- setAddrOfLocalVar(
- VD, recoverAddrOfEscapedLocal(ParentCGF, ParentVar, ParentFP));
+ Address Recovered =
+ recoverAddrOfEscapedLocal(ParentCGF, ParentVar, ParentFP);
+ setAddrOfLocalVar(VD, Recovered);
+
+ if (isa<ImplicitParamDecl>(VD)) {
+ CXXABIThisAlignment = ParentCGF.CXXABIThisAlignment;
+ CXXThisAlignment = ParentCGF.CXXThisAlignment;
+ CXXABIThisValue = Builder.CreateLoad(Recovered, "this");
+ if (ParentCGF.LambdaThisCaptureField) {
+ LambdaThisCaptureField = ParentCGF.LambdaThisCaptureField;
+ // We are in a lambda function where "this" is captured so the
+ // CXXThisValue need to be loaded from the lambda capture
+ LValue ThisFieldLValue =
+ EmitLValueForLambdaField(LambdaThisCaptureField);
+ if (!LambdaThisCaptureField->getType()->isPointerType()) {
+ CXXThisValue = ThisFieldLValue.getAddress(*this).getPointer();
+ } else {
+ CXXThisValue = EmitLoadOfLValue(ThisFieldLValue, SourceLocation())
+ .getScalarVal();
+ }
+ } else {
+ CXXThisValue = CXXABIThisValue;
+ }
+ }
}
if (Finder.SEHCodeSlot.isValid()) {
@@ -2025,8 +2114,8 @@ void CodeGenFunction::EmitSEHExceptionCodeSave(CodeGenFunction &ParentCGF,
llvm::Type *PtrsTy = llvm::StructType::get(RecordTy, CGM.VoidPtrTy);
llvm::Value *Ptrs = Builder.CreateBitCast(SEHInfo, PtrsTy->getPointerTo());
llvm::Value *Rec = Builder.CreateStructGEP(PtrsTy, Ptrs, 0);
- Rec = Builder.CreateAlignedLoad(Rec, getPointerAlign());
- llvm::Value *Code = Builder.CreateAlignedLoad(Rec, getIntAlign());
+ Rec = Builder.CreateAlignedLoad(RecordTy, Rec, getPointerAlign());
+ llvm::Value *Code = Builder.CreateAlignedLoad(Int32Ty, Rec, getIntAlign());
assert(!SEHCodeSlotStack.empty() && "emitting EH code outside of __except");
Builder.CreateStore(Code, SEHCodeSlotStack.back());
}
@@ -2105,6 +2194,12 @@ void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) {
return;
}
+ // IsEHa: emit an invoke _seh_try_end() to mark end of FT flow
+ if (getLangOpts().EHAsynch && Builder.GetInsertBlock()) {
+ llvm::FunctionCallee SehTryEnd = getSehTryEndFn(CGM);
+ EmitRuntimeCallOrInvoke(SehTryEnd);
+ }
+
// Otherwise, we must have an __except block.
const SEHExceptStmt *Except = S.getExceptHandler();
assert(Except && "__try must have __finally xor __except");