Skip to content

Commit 797ad70

Browse files
committed
[Windows SEH]: HARDWARE EXCEPTION HANDLING (MSVC -EHa) - Part 1
This patch is the Part-1 (FE Clang) implementation of HW Exception handling. This new feature adds the support of Hardware Exception for Microsoft Windows SEH (Structured Exception Handling). This is the first step of this project; only X86_64 target is enabled in this patch. Compiler options: For clang-cl.exe, the option is -EHa, the same as MSVC. For clang.exe, the extra option is -fasync-exceptions, plus -triple x86_64-windows -fexceptions and -fcxx-exceptions as usual. NOTE:: Without the -EHa or -fasync-exceptions, this patch is a NO-DIFF change. The rules for C code: For C-code, one way (MSVC approach) to achieve SEH -EHa semantic is to follow three rules: * First, no exception can move in or out of _try region., i.e., no "potential faulty instruction can be moved across _try boundary. * Second, the order of exceptions for instructions 'directly' under a _try must be preserved (not applied to those in callees). * Finally, global states (local/global/heap variables) that can be read outside of _try region must be updated in memory (not just in register) before the subsequent exception occurs. The impact to C++ code: Although SEH is a feature for C code, -EHa does have a profound effect on C++ side. When a C++ function (in the same compilation unit with option -EHa ) is called by a SEH C function, a hardware exception occurs in C++ code can also be handled properly by an upstream SEH _try-handler or a C++ catch(...). As such, when that happens in the middle of an object's life scope, the dtor must be invoked the same way as C++ Synchronous Exception during unwinding process. Design: A natural way to achieve the rules above in LLVM today is to allow an EH edge added on memory/computation instruction (previous iload/istore idea) so that exception path is modeled in Flow graph preciously. However, tracking every single memory instruction and potential faulty instruction can create many Invokes, complicate flow graph and possibly result in negative performance impact for downstream optimization and code generation. Making all optimizations be aware of the new semantic is also substantial. This design does not intend to model exception path at instruction level. Instead, the proposed design tracks and reports EH state at BLOCK-level to reduce the complexity of flow graph and minimize the performance-impact on CPP code under -EHa option. One key element of this design is the ability to compute State number at block-level. Our algorithm is based on the following rationales: A _try scope is always a SEME (Single Entry Multiple Exits) region as jumping into a _try is not allowed. The single entry must start with a seh_try_begin() invoke with a correct State number that is the initial state of the SEME. Through control-flow, state number is propagated into all blocks. Side exits marked by seh_try_end() will unwind to parent state based on existing SEHUnwindMap[]. Note side exits can ONLY jump into parent scopes (lower state number). Thus, when a block succeeds various states from its predecessors, the lowest State triumphs others. If some exits flow to unreachable, propagation on those paths terminate, not affecting remaining blocks. For CPP code, object lifetime region is usually a SEME as SEH _try. However there is one rare exception: jumping into a lifetime that has Dtor but has no Ctor is warned, but allowed: Warning: jump bypasses variable with a non-trivial destructor In that case, the region is actually a MEME (multiple entry multiple exits). Our solution is to inject a eha_scope_begin() invoke in the side entry block to ensure a correct State. Implementation: Part-1: Clang implementation described below. Two intrinsic are created to track CPP object scopes; eha_scope_begin() and eha_scope_end(). _scope_begin() is immediately added after ctor() is called and EHStack is pushed. So it must be an invoke, not a call. With that it's also guaranteed an EH-cleanup-pad is created regardless whether there exists a call in this scope. _scope_end is added before dtor(). These two intrinsics make the computation of Block-State possible in downstream code gen pass, even in the presence of ctor/dtor inlining. Two intrinsic, seh_try_begin() and seh_try_end(), are added for C-code to mark _try boundary and to prevent from exceptions being moved across _try boundary. All memory instructions inside a _try are considered as 'volatile' to assure 2nd and 3rd rules for C-code above. This is a little sub-optimized. But it's acceptable as the amount of code directly under _try is very small. Part-2 (will be in Part-2 patch): LLVM implementation described below. For both C++ & C-code, the state of each block is computed at the same place in BE (WinEHPreparing pass) where all other EH tables/maps are calculated. In addition to _scope_begin & _scope_end, the computation of block state also rely on the existing State tracking code (UnwindMap and InvokeStateMap). For both C++ & C-code, the state of each block with potential trap instruction is marked and reported in DAG Instruction Selection pass, the same place where the state for -EHsc (synchronous exceptions) is done. If the first instruction in a reported block scope can trap, a Nop is injected before this instruction. This nop is needed to accommodate LLVM Windows EH implementation, in which the address in IPToState table is offset by +1. (note the purpose of that is to ensure the return address of a call is in the same scope as the call address. The handler for catch(...) for -EHa must handle HW exception. So it is 'adjective' flag is reset (it cannot be IsStdDotDot (0x40) that only catches C++ exceptions). Suppress push/popTerminate() scope (from noexcept/noTHrow) so that HW exceptions can be passed through. Original llvm-dev [RFC] discussions can be found in these two threads below: https://lists.llvm.org/pipermail/llvm-dev/2020-March/140541.html https://lists.llvm.org/pipermail/llvm-dev/2020-April/141338.html Differential Revision: https://reviews.llvm.org/D80344/new/
1 parent 3bc2b97 commit 797ad70

25 files changed

+604
-15
lines changed

clang/include/clang/AST/Stmt.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,7 @@ class ValueStmt : public Stmt {
17981798
class LabelStmt : public ValueStmt {
17991799
LabelDecl *TheDecl;
18001800
Stmt *SubStmt;
1801+
bool SideEntry = false;
18011802

18021803
public:
18031804
/// Build a label statement.
@@ -1833,6 +1834,8 @@ class LabelStmt : public ValueStmt {
18331834
static bool classof(const Stmt *T) {
18341835
return T->getStmtClass() == LabelStmtClass;
18351836
}
1837+
bool isSideEntry() const { return SideEntry; }
1838+
void setSideEntry(bool SE) { SideEntry = SE; }
18361839
};
18371840

18381841
/// Represents an attribute applied to a statement.

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ LANGOPT(ZVector , 1, 0, "System z vector extensions")
132132
LANGOPT(Exceptions , 1, 0, "exception handling")
133133
LANGOPT(ObjCExceptions , 1, 0, "Objective-C exceptions")
134134
LANGOPT(CXXExceptions , 1, 0, "C++ exceptions")
135+
LANGOPT(EHAsynch , 1, 0, "C/C++ EH Asynch exceptions")
135136
ENUM_LANGOPT(ExceptionHandling, ExceptionHandlingKind, 3,
136137
ExceptionHandlingKind::None, "exception handling")
137138
LANGOPT(IgnoreExceptions , 1, 0, "ignore exceptions")

clang/include/clang/Driver/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,9 @@ def fcreate_profile : Flag<["-"], "fcreate-profile">, Group<f_Group>;
13221322
defm cxx_exceptions: BoolFOption<"cxx-exceptions",
13231323
LangOpts<"CXXExceptions">, DefaultFalse,
13241324
PosFlag<SetTrue, [CC1Option], "Enable C++ exceptions">, NegFlag<SetFalse>>;
1325+
defm async_exceptions: BoolFOption<"async-exceptions",
1326+
LangOpts<"EHAsynch">, DefaultFalse,
1327+
PosFlag<SetTrue, [CC1Option], "Enable EH Asynchronous exceptions">, NegFlag<SetFalse>>;
13251328
def fcxx_modules : Flag <["-"], "fcxx-modules">, Group<f_Group>,
13261329
Flags<[NoXarchOption]>;
13271330
def fdebug_pass_arguments : Flag<["-"], "fdebug-pass-arguments">, Group<f_Group>;

clang/lib/AST/JSONNodeDumper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,7 @@ void JSONNodeDumper::VisitCaseStmt(const CaseStmt *CS) {
14511451
void JSONNodeDumper::VisitLabelStmt(const LabelStmt *LS) {
14521452
JOS.attribute("name", LS->getName());
14531453
JOS.attribute("declId", createPointerRepresentation(LS->getDecl()));
1454+
attributeOnlyIfTrue("sideEntry", LS->isSideEntry());
14541455
}
14551456
void JSONNodeDumper::VisitGotoStmt(const GotoStmt *GS) {
14561457
JOS.attribute("targetLabelDeclId",

clang/lib/AST/TextNodeDumper.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,8 @@ void TextNodeDumper::VisitWhileStmt(const WhileStmt *Node) {
923923

924924
void TextNodeDumper::VisitLabelStmt(const LabelStmt *Node) {
925925
OS << " '" << Node->getName() << "'";
926+
if (Node->isSideEntry())
927+
OS << " side_entry";
926928
}
927929

928930
void TextNodeDumper::VisitGotoStmt(const GotoStmt *Node) {

clang/lib/CodeGen/CGCleanup.cpp

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) {
194194
if (IsLifetimeMarker)
195195
Scope->setLifetimeMarker();
196196

197+
// With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup
198+
if (CGF->getLangOpts().EHAsynch && IsEHCleanup &&
199+
CGF->getTarget().getCXXABI().isMicrosoft())
200+
CGF->EmitSehCppScopeBegin();
201+
197202
return Scope->getCleanupBuffer();
198203
}
199204

@@ -759,14 +764,31 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
759764
if (Scope.isEHCleanup())
760765
cleanupFlags.setIsEHCleanupKind();
761766

767+
// Under -EHa, invoke seh.scope.end() to mark scope end before dtor
768+
bool IsEHa = getLangOpts().EHAsynch && !Scope.isLifetimeMarker();
769+
const EHPersonality &Personality = EHPersonality::get(*this);
762770
if (!RequiresNormalCleanup) {
771+
// Mark CPP scope end for passed-by-value Arg temp
772+
// per Windows ABI which is "normally" Cleanup in callee
773+
if (IsEHa && getInvokeDest()) {
774+
if (Personality.isMSVCXXPersonality())
775+
EmitSehCppScopeEnd();
776+
}
763777
destroyOptimisticNormalEntry(*this, Scope);
764778
EHStack.popCleanup();
765779
} else {
766780
// If we have a fallthrough and no other need for the cleanup,
767781
// emit it directly.
768-
if (HasFallthrough && !HasPrebranchedFallthrough &&
769-
!HasFixups && !HasExistingBranches) {
782+
if (HasFallthrough && !HasPrebranchedFallthrough && !HasFixups &&
783+
!HasExistingBranches) {
784+
785+
// mark SEH scope end for fall-through flow
786+
if (IsEHa && getInvokeDest()) {
787+
if (Personality.isMSVCXXPersonality())
788+
EmitSehCppScopeEnd();
789+
else
790+
EmitSehTryScopeEnd();
791+
}
770792

771793
destroyOptimisticNormalEntry(*this, Scope);
772794
EHStack.popCleanup();
@@ -801,6 +823,14 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
801823
// should already be branched to it.
802824
EmitBlock(NormalEntry);
803825

826+
// intercept normal cleanup to mark SEH scope end
827+
if (IsEHa) {
828+
if (Personality.isMSVCXXPersonality())
829+
EmitSehCppScopeEnd();
830+
else
831+
EmitSehTryScopeEnd();
832+
}
833+
804834
// III. Figure out where we're going and build the cleanup
805835
// epilogue.
806836

@@ -1248,11 +1278,17 @@ void CodeGenFunction::DeactivateCleanupBlock(EHScopeStack::stable_iterator C,
12481278
// to the current RunCleanupsScope.
12491279
if (C == EHStack.stable_begin() &&
12501280
CurrentCleanupScopeDepth.strictlyEncloses(C)) {
1251-
// If it's a normal cleanup, we need to pretend that the
1252-
// fallthrough is unreachable.
1253-
CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
1254-
PopCleanupBlock();
1255-
Builder.restoreIP(SavedIP);
1281+
// Per comment below, checking EHAsynch is not really necessary
1282+
// it's there to assure zero-impact w/o EHAsynch option
1283+
if (!Scope.isNormalCleanup() && getLangOpts().EHAsynch) {
1284+
PopCleanupBlock();
1285+
} else {
1286+
// If it's a normal cleanup, we need to pretend that the
1287+
// fallthrough is unreachable.
1288+
CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
1289+
PopCleanupBlock();
1290+
Builder.restoreIP(SavedIP);
1291+
}
12561292
return;
12571293
}
12581294

@@ -1276,3 +1312,60 @@ void CodeGenFunction::EmitCXXTemporary(const CXXTemporary *Temporary,
12761312
pushDestroy(NormalAndEHCleanup, Ptr, TempType, destroyCXXObject,
12771313
/*useEHCleanup*/ true);
12781314
}
1315+
1316+
// Need to set "funclet" in OperandBundle properly for noThrow
1317+
// intrinsic (see CGCall.cpp)
1318+
static void EmitSehScope(CodeGenFunction &CGF,
1319+
llvm::FunctionCallee &SehCppScope) {
1320+
llvm::BasicBlock *InvokeDest = CGF.getInvokeDest();
1321+
llvm::BasicBlock *BB = CGF.Builder.GetInsertBlock();
1322+
assert(BB && InvokeDest);
1323+
llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont");
1324+
SmallVector<llvm::OperandBundleDef, 1> BundleList =
1325+
CGF.getBundlesForFunclet(SehCppScope.getCallee());
1326+
if (CGF.CurrentFuncletPad)
1327+
BundleList.emplace_back("funclet", CGF.CurrentFuncletPad);
1328+
CGF.Builder.CreateInvoke(SehCppScope, Cont, InvokeDest, None, BundleList);
1329+
CGF.EmitBlock(Cont);
1330+
}
1331+
1332+
// Invoke a llvm.seh.scope.begin at the beginning of a CPP scope for -EHa
1333+
void CodeGenFunction::EmitSehCppScopeBegin() {
1334+
assert(getLangOpts().EHAsynch);
1335+
llvm::FunctionType *FTy =
1336+
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
1337+
llvm::FunctionCallee SehCppScope =
1338+
CGM.CreateRuntimeFunction(FTy, "llvm.seh.scope.begin");
1339+
EmitSehScope(*this, SehCppScope);
1340+
}
1341+
1342+
// Invoke a llvm.seh.scope.end at the end of a CPP scope for -EHa
1343+
// llvm.seh.scope.end is emitted before popCleanup, so it's "invoked"
1344+
void CodeGenFunction::EmitSehCppScopeEnd() {
1345+
assert(getLangOpts().EHAsynch);
1346+
llvm::FunctionType *FTy =
1347+
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
1348+
llvm::FunctionCallee SehCppScope =
1349+
CGM.CreateRuntimeFunction(FTy, "llvm.seh.scope.end");
1350+
EmitSehScope(*this, SehCppScope);
1351+
}
1352+
1353+
// Invoke a llvm.seh.try.begin at the beginning of a SEH scope for -EHa
1354+
void CodeGenFunction::EmitSehTryScopeBegin() {
1355+
assert(getLangOpts().EHAsynch);
1356+
llvm::FunctionType *FTy =
1357+
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
1358+
llvm::FunctionCallee SehCppScope =
1359+
CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.begin");
1360+
EmitSehScope(*this, SehCppScope);
1361+
}
1362+
1363+
// Invoke a llvm.seh.try.end at the end of a SEH scope for -EHa
1364+
void CodeGenFunction::EmitSehTryScopeEnd() {
1365+
assert(getLangOpts().EHAsynch);
1366+
llvm::FunctionType *FTy =
1367+
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
1368+
llvm::FunctionCallee SehCppScope =
1369+
CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.end");
1370+
EmitSehScope(*this, SehCppScope);
1371+
}

clang/lib/CodeGen/CGException.cpp

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ static llvm::FunctionCallee getFreeExceptionFn(CodeGenModule &CGM) {
3939
return CGM.CreateRuntimeFunction(FTy, "__cxa_free_exception");
4040
}
4141

42+
static llvm::FunctionCallee getSehTryBeginFn(CodeGenModule &CGM) {
43+
llvm::FunctionType *FTy =
44+
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
45+
return CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.begin");
46+
}
47+
48+
static llvm::FunctionCallee getSehTryEndFn(CodeGenModule &CGM) {
49+
llvm::FunctionType *FTy =
50+
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
51+
return CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.end");
52+
}
53+
4254
static llvm::FunctionCallee getUnexpectedFn(CodeGenModule &CGM) {
4355
// void __cxa_call_unexpected(void *thrown_exception);
4456

@@ -467,7 +479,8 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) {
467479
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
468480
if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot) {
469481
// noexcept functions are simple terminate scopes.
470-
EHStack.pushTerminate();
482+
if (!getLangOpts().EHAsynch) // -EHa: HW exception still can occur
483+
EHStack.pushTerminate();
471484
} else if (EST == EST_Dynamic || EST == EST_DynamicNone) {
472485
// TODO: Revisit exception specifications for the MS ABI. There is a way to
473486
// encode these in an object file but MSVC doesn't do anything with it.
@@ -544,7 +557,7 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
544557
if (!FD) {
545558
// Check if CapturedDecl is nothrow and pop terminate scope for it.
546559
if (const CapturedDecl* CD = dyn_cast_or_null<CapturedDecl>(D)) {
547-
if (CD->isNothrow())
560+
if (CD->isNothrow() && !EHStack.empty())
548561
EHStack.popTerminate();
549562
}
550563
return;
@@ -554,7 +567,8 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
554567
return;
555568

556569
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
557-
if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot) {
570+
if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot &&
571+
!EHStack.empty() /* possible empty when under async exceptions */) {
558572
EHStack.popTerminate();
559573
} else if (EST == EST_Dynamic || EST == EST_DynamicNone) {
560574
// TODO: Revisit exception specifications for the MS ABI. There is a way to
@@ -610,6 +624,10 @@ void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
610624
} else {
611625
// No exception decl indicates '...', a catch-all.
612626
CatchScope->setHandler(I, CGM.getCXXABI().getCatchAllTypeInfo(), Handler);
627+
// Under async exceptions, catch(...) need to catch HW exception too
628+
// Mark scope with SehTryBegin as a SEH __try scope
629+
if (getLangOpts().EHAsynch)
630+
EmitRuntimeCallOrInvoke(getSehTryBeginFn(CGM));
613631
}
614632
}
615633
}
@@ -724,7 +742,7 @@ llvm::BasicBlock *CodeGenFunction::getInvokeDestImpl() {
724742
// If exceptions are disabled/ignored and SEH is not in use, then there is no
725743
// invoke destination. SEH "works" even if exceptions are off. In practice,
726744
// this means that C++ destructors and other EH cleanups don't run, which is
727-
// consistent with MSVC's behavior.
745+
// consistent with MSVC's behavior, except in the presence of -EHa
728746
const LangOptions &LO = CGM.getLangOpts();
729747
if (!LO.Exceptions || LO.IgnoreExceptions) {
730748
if (!LO.Borland && !LO.MicrosoftExt)
@@ -1606,7 +1624,23 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
16061624
JumpDest TryExit = getJumpDestInCurrentScope("__try.__leave");
16071625

16081626
SEHTryEpilogueStack.push_back(&TryExit);
1627+
1628+
llvm::BasicBlock *TryBB = nullptr;
1629+
// IsEHa: emit an invoke to _seh_try_begin() runtime for -EHa
1630+
if (getLangOpts().EHAsynch) {
1631+
EmitRuntimeCallOrInvoke(getSehTryBeginFn(CGM));
1632+
if (SEHTryEpilogueStack.size() == 1) // outermost only
1633+
TryBB = Builder.GetInsertBlock();
1634+
}
1635+
16091636
EmitStmt(S.getTryBlock());
1637+
1638+
// Volatilize all blocks in Try, till current insert point
1639+
if (TryBB) {
1640+
llvm::SmallPtrSet<llvm::BasicBlock *, 10> Visited;
1641+
VolatilizeTryBlocks(TryBB, Visited);
1642+
}
1643+
16101644
SEHTryEpilogueStack.pop_back();
16111645

16121646
if (!TryExit.getBlock()->use_empty())
@@ -1617,6 +1651,35 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
16171651
ExitSEHTryStmt(S);
16181652
}
16191653

1654+
// Recursively walk through blocks in a _try
1655+
// and make all memory instructions volatile
1656+
void CodeGenFunction::VolatilizeTryBlocks(
1657+
llvm::BasicBlock *BB, llvm::SmallPtrSet<llvm::BasicBlock *, 10> &V) {
1658+
if (BB == SEHTryEpilogueStack.back()->getBlock() /* end of Try */ ||
1659+
!V.insert(BB).second /* already visited */ ||
1660+
!BB->getParent() /* not emitted */ || BB->empty())
1661+
return;
1662+
1663+
if (!BB->isEHPad()) {
1664+
for (llvm::BasicBlock::iterator J = BB->begin(), JE = BB->end(); J != JE;
1665+
++J) {
1666+
if (auto LI = dyn_cast<llvm::LoadInst>(J)) {
1667+
LI->setVolatile(true);
1668+
} else if (auto SI = dyn_cast<llvm::StoreInst>(J)) {
1669+
SI->setVolatile(true);
1670+
} else if (auto* MCI = dyn_cast<llvm::MemIntrinsic>(J)) {
1671+
MCI->setVolatile(llvm::ConstantInt::get(Builder.getInt1Ty(), 1));
1672+
}
1673+
}
1674+
}
1675+
const llvm::Instruction *TI = BB->getTerminator();
1676+
if (TI) {
1677+
unsigned N = TI->getNumSuccessors();
1678+
for (unsigned I = 0; I < N; I++)
1679+
VolatilizeTryBlocks(TI->getSuccessor(I), V);
1680+
}
1681+
}
1682+
16201683
namespace {
16211684
struct PerformSEHFinally final : EHScopeStack::Cleanup {
16221685
llvm::Function *OutlinedFinally;
@@ -2118,6 +2181,12 @@ void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) {
21182181
return;
21192182
}
21202183

2184+
// IsEHa: emit an invoke _seh_try_end() to mark end of FT flow
2185+
if (getLangOpts().EHAsynch && Builder.GetInsertBlock()) {
2186+
llvm::FunctionCallee SehTryEnd = getSehTryEndFn(CGM);
2187+
EmitRuntimeCallOrInvoke(SehTryEnd);
2188+
}
2189+
21212190
// Otherwise, we must have an __except block.
21222191
const SEHExceptStmt *Except = S.getExceptHandler();
21232192
assert(Except && "__try must have __finally xor __except");

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,11 @@ void CodeGenFunction::LexicalScope::rescopeLabels() {
646646

647647
void CodeGenFunction::EmitLabelStmt(const LabelStmt &S) {
648648
EmitLabel(S.getDecl());
649+
650+
// IsEHa - emit eha.scope.begin if it's a side entry of a scope
651+
if (getLangOpts().EHAsynch && S.isSideEntry())
652+
EmitSehCppScopeBegin();
653+
649654
EmitStmt(S.getSubStmt());
650655
}
651656

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
7575
shouldEmitLifetimeMarkers(CGM.getCodeGenOpts(), CGM.getLangOpts())) {
7676
if (!suppressNewContext)
7777
CGM.getCXXABI().getMangleContext().startNewFunction();
78+
EHStack.setCGF(this);
7879

7980
SetFastMathFlags(CurFPFeatures);
8081
SetFPModel();

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2867,6 +2867,11 @@ class CodeGenFunction : public CodeGenTypeCache {
28672867
void EmitCXXTemporary(const CXXTemporary *Temporary, QualType TempType,
28682868
Address Ptr);
28692869

2870+
void EmitSehCppScopeBegin();
2871+
void EmitSehCppScopeEnd();
2872+
void EmitSehTryScopeBegin();
2873+
void EmitSehTryScopeEnd();
2874+
28702875
llvm::Value *EmitLifetimeStart(uint64_t Size, llvm::Value *Addr);
28712876
void EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr);
28722877

@@ -3217,6 +3222,8 @@ class CodeGenFunction : public CodeGenTypeCache {
32173222
void EmitSEHLeaveStmt(const SEHLeaveStmt &S);
32183223
void EnterSEHTryStmt(const SEHTryStmt &S);
32193224
void ExitSEHTryStmt(const SEHTryStmt &S);
3225+
void VolatilizeTryBlocks(llvm::BasicBlock *BB,
3226+
llvm::SmallPtrSet<llvm::BasicBlock *, 10> &V);
32203227

32213228
void pushSEHCleanup(CleanupKind kind,
32223229
llvm::Function *FinallyFunc);

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,9 @@ void CodeGenModule::Release() {
697697
llvm::DenormalMode::IEEE);
698698
}
699699

700+
if (LangOpts.EHAsynch)
701+
getModule().addModuleFlag(llvm::Module::Warning, "eh-asynch", 1);
702+
700703
// Emit OpenCL specific module metadata: OpenCL/SPIR version.
701704
if (LangOpts.OpenCL) {
702705
EmitOpenCLMetadata();

0 commit comments

Comments
 (0)