4 changes: 4 additions & 0 deletions clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1737,6 +1737,7 @@ class ValueStmt : public Stmt {
class LabelStmt : public ValueStmt {
LabelDecl *TheDecl;
Stmt *SubStmt;
bool SideEntry = false;

public:
/// Build a label statement.
Expand Down Expand Up @@ -1772,6 +1773,9 @@ class LabelStmt : public ValueStmt {
static bool classof(const Stmt *T) {
return T->getStmtClass() == LabelStmtClass;
}

bool IsSideEntry() const { return SideEntry; }
void setSideEntry() { SideEntry = true; }
};

/// Represents an attribute applied to a statement.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ LANGOPT(ZVector , 1, 0, "System z vector extensions")
LANGOPT(Exceptions , 1, 0, "exception handling")
LANGOPT(ObjCExceptions , 1, 0, "Objective-C exceptions")
LANGOPT(CXXExceptions , 1, 0, "C++ exceptions")
LANGOPT(EHAsynch , 1, 0, "C/C++ EH Asynch exceptions")
LANGOPT(DWARFExceptions , 1, 0, "dwarf exception handling")
LANGOPT(SjLjExceptions , 1, 0, "setjmp-longjump exception handling")
LANGOPT(SEHExceptions , 1, 0, "SEH .xdata exception handling")
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,8 @@ def fcrash_diagnostics_dir : Joined<["-"], "fcrash-diagnostics-dir=">, Group<f_c
def fcreate_profile : Flag<["-"], "fcreate-profile">, Group<f_Group>;
def fcxx_exceptions: Flag<["-"], "fcxx-exceptions">, Group<f_Group>,
HelpText<"Enable C++ exceptions">, Flags<[CC1Option]>;
def feh_asynch: Flag<["-"], "feh-asynch">, Group<f_Group>,
HelpText<"Enable EH Asynchronous exceptions">, Flags<[CC1Option]>;
def fcxx_modules : Flag <["-"], "fcxx-modules">, Group<f_Group>,
Flags<[DriverOption]>;
def fdebug_pass_arguments : Flag<["-"], "fdebug-pass-arguments">, Group<f_Group>;
Expand Down Expand Up @@ -1518,6 +1520,7 @@ def fno_constant_cfstrings : Flag<["-"], "fno-constant-cfstrings">, Group<f_Grou
Flags<[CC1Option]>,
HelpText<"Disable creation of CodeFoundation-type constant strings">;
def fno_cxx_exceptions: Flag<["-"], "fno-cxx-exceptions">, Group<f_Group>;
def fno_eh_asynch: Flag<["-"], "fno-eh-asynch">, Group<f_Group>;
def fno_cxx_modules : Flag <["-"], "fno-cxx-modules">, Group<f_Group>,
Flags<[DriverOption]>;
def fno_diagnostics_fixit_info : Flag<["-"], "fno-diagnostics-fixit-info">, Group<f_Group>,
Expand Down
83 changes: 83 additions & 0 deletions clang/lib/CodeGen/CGCleanup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,12 @@ static void destroyOptimisticNormalEntry(CodeGenFunction &CGF,
delete entry;
}

static llvm::FunctionCallee getSehTryEndFn(CodeGenModule& CGM) {
llvm::FunctionType* FTy =
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
return CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.end");
}

/// Pops a cleanup block. If the block includes a normal cleanup, the
/// current insertion point is threaded through the cleanup, as are
/// any branch fixups on the cleanup.
Expand Down Expand Up @@ -766,11 +772,22 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
destroyOptimisticNormalEntry(*this, Scope);
EHStack.popCleanup();
} else {
// Under -EHa, invoke eha_scope_end() to mark scope end before dtor
bool IsEHa = getLangOpts().EHAsynch && !Scope.isLifetimeMarker();
const EHPersonality& Personality = EHPersonality::get(*this);

// If we have a fallthrough and no other need for the cleanup,
// emit it directly.
if (HasFallthrough && !HasPrebranchedFallthrough &&
!HasFixups && !HasExistingBranches) {

// mark EHa scope end for fall-through flow
if (IsEHa && getInvokeDest())
if (Personality.isMSVCXXPersonality())
EmitSehCppScopeEnd();
else
EmitSehTryScopeEnd();

destroyOptimisticNormalEntry(*this, Scope);
EHStack.popCleanup();

Expand Down Expand Up @@ -804,6 +821,13 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
// should already be branched to it.
EmitBlock(NormalEntry);

// intercept normal cleanup to mark EHa scope end
if (IsEHa)
if (Personality.isMSVCXXPersonality())
EmitSehCppScopeEnd();
else
EmitSehTryScopeEnd();

// III. Figure out where we're going and build the cleanup
// epilogue.

Expand Down Expand Up @@ -1276,3 +1300,62 @@ void CodeGenFunction::EmitCXXTemporary(const CXXTemporary *Temporary,
pushDestroy(NormalAndEHCleanup, Ptr, TempType, destroyCXXObject,
/*useEHCleanup*/ true);
}

// EmitRuntimeCallOrInvoke() does not work bcause "funclet" not set
// in OperandBundle properly for noThrow intrinsic (see CGCall.cpp)
static void EmitSehEHaScope(CodeGenFunction& CGF,
llvm::FunctionCallee& SehCppScope)
{
llvm::BasicBlock* InvokeDest = CGF.getInvokeDest();
llvm::BasicBlock* BB = CGF.Builder.GetInsertBlock();
assert(BB && InvokeDest);
llvm::BasicBlock* Cont = CGF.createBasicBlock("invoke.cont");
SmallVector<llvm::OperandBundleDef, 1> BundleList =
CGF.getBundlesForFunclet(SehCppScope.getCallee());
if (CGF.CurrentFuncletPad)
BundleList.emplace_back("funclet", CGF.CurrentFuncletPad);
CGF.Builder.CreateInvoke(SehCppScope, Cont, InvokeDest, None, BundleList);
CGF.EmitBlock(Cont);
}

// Invoke a llvm.eha.scope.begin at the beginning of a CPP scope for -EHa
void CodeGenFunction::EmitSehCppScopeBegin() {
assert(getLangOpts().EHAsynch);
llvm::FunctionType* FTy =
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
llvm::FunctionCallee SehCppScope =
CGM.CreateRuntimeFunction(FTy, "llvm.eha.scope.begin");
EmitSehEHaScope(*this, SehCppScope);
}

// Invoke a llvm.eha.scope.end at the end of a CPP scope for -EHa
// llvm.eha.scope.end is emitted before popCleanup, so it's "invoked"
void CodeGenFunction::EmitSehCppScopeEnd() {
assert(getLangOpts().EHAsynch);
llvm::FunctionType* FTy =
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
llvm::FunctionCallee SehCppScope =
CGM.CreateRuntimeFunction(FTy, "llvm.eha.scope.end");
EmitSehEHaScope(*this, SehCppScope);
}

// Invoke a llvm.eha.scope.begin at the beginning of a CPP scope for -EHa
void CodeGenFunction::EmitSehTryScopeBegin() {
assert(getLangOpts().EHAsynch);
llvm::FunctionType* FTy =
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
llvm::FunctionCallee SehCppScope =
CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.begin");
EmitSehEHaScope(*this, SehCppScope);
}

// Invoke a llvm.eha.scope.end at the end of a CPP scope for -EHa
// llvm.eha.scope.end is emitted before popCleanup, so it's "invoked"
void CodeGenFunction::EmitSehTryScopeEnd() {
assert(getLangOpts().EHAsynch);
llvm::FunctionType* FTy =
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
llvm::FunctionCallee SehCppScope =
CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.end");
EmitSehEHaScope(*this, SehCppScope);
}
9 changes: 8 additions & 1 deletion clang/lib/CodeGen/CGDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1996,9 +1996,16 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) {
const VarDecl &D = *emission.Variable;

// Check the type for a cleanup.
if (QualType::DestructionKind dtorKind = D.needsDestruction(getContext()))
if (QualType::DestructionKind dtorKind = D.needsDestruction(getContext())) {
emitAutoVarTypeCleanup(emission, dtorKind);

// <tentzen>: Under -EHa, Invoke llvm.eha.scope.begin() right after
// Ctor is emitted and EHStack.pushCleanup
bool IsEHa = getLangOpts().EHAsynch;
if (IsEHa && dtorKind == QualType::DK_cxx_destructor)
EmitSehCppScopeBegin();
}

// In GC mode, honor objc_precise_lifetime.
if (getLangOpts().getGC() != LangOptions::NonGC &&
D.hasAttr<ObjCPreciseLifetimeAttr>()) {
Expand Down
90 changes: 85 additions & 5 deletions clang/lib/CodeGen/CGException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,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);

Expand Down Expand Up @@ -450,7 +462,7 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) {
if (!FD) {
// Check if CapturedDecl is nothrow and create terminate scope for it.
if (const CapturedDecl* CD = dyn_cast_or_null<CapturedDecl>(D)) {
if (CD->isNothrow())
if (CD->isNothrow() && !getLangOpts().EHAsynch /* !IsEHa */)
EHStack.pushTerminate();
}
return;
Expand All @@ -462,7 +474,8 @@ 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.
Expand Down Expand Up @@ -527,7 +540,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;
Expand All @@ -537,7 +550,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 -EHa */) {
EHStack.popTerminate();
} else if (EST == EST_Dynamic || EST == EST_DynamicNone) {
// TODO: Revisit exception specifications for the MS ABI. There is a way to
Expand Down Expand Up @@ -584,7 +598,16 @@ void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
CatchScope->setHandler(I, TypeInfo, Handler);
} else {
// No exception decl indicates '...', a catch-all.
CatchScope->setHandler(I, CGM.getCXXABI().getCatchAllTypeInfo(), Handler);
CatchTypeInfo TypeInfo = CGM.getCXXABI().getCatchAllTypeInfo();

// For IsEHa catch(...) must handle HW exception
// Adjective = HT_IsStdDotDot (0x40), only catch C++ exceptions
// Also mark scope with SehTryBegin
if (getLangOpts().EHAsynch) {
TypeInfo.Flags = 0;
EmitRuntimeCallOrInvoke(getSehTryBeginFn(CGM));
}
CatchScope->setHandler(I, TypeInfo, Handler);
}
}
}
Expand Down Expand Up @@ -1602,7 +1625,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())
Expand All @@ -1613,6 +1652,41 @@ 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 (isa<llvm::LoadInst>(J)) {
auto LI = cast<llvm::LoadInst>(J);
LI->setVolatile(true);
}
else if (isa<llvm::StoreInst>(J)) {
auto SI = cast<llvm::StoreInst>(J);
SI->setVolatile(true);
}
else if (isa<llvm::MemIntrinsic>(J)) {
auto* MCI = cast<llvm::MemIntrinsic>(J);
MCI->setVolatile(llvm::ConstantInt::get(Builder.getInt1Ty(), 1));
}
} // end of for
}
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;
Expand Down Expand Up @@ -2037,6 +2111,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");
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,11 @@ void CodeGenFunction::LexicalScope::rescopeLabels() {

void CodeGenFunction::EmitLabelStmt(const LabelStmt &S) {
EmitLabel(S.getDecl());

// IsEHa - emit eha.scope.begin if it's a side entry of a scope
if (getLangOpts().EHAsynch && S.IsSideEntry())
EmitSehCppScopeBegin();

EmitStmt(S.getSubStmt());
}

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2687,6 +2687,11 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitCXXTemporary(const CXXTemporary *Temporary, QualType TempType,
Address Ptr);

void CodeGenFunction::EmitSehCppScopeBegin();
void CodeGenFunction::EmitSehCppScopeEnd();
void CodeGenFunction::EmitSehTryScopeBegin();
void CodeGenFunction::EmitSehTryScopeEnd();

llvm::Value *EmitLifetimeStart(uint64_t Size, llvm::Value *Addr);
void EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr);

Expand Down Expand Up @@ -3037,6 +3042,7 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitSEHLeaveStmt(const SEHLeaveStmt &S);
void EnterSEHTryStmt(const SEHTryStmt &S);
void ExitSEHTryStmt(const SEHTryStmt &S);
void VolatilizeTryBlocks(llvm::BasicBlock* BB, llvm::SmallPtrSet<llvm::BasicBlock*, 10>& V);

void pushSEHCleanup(CleanupKind kind,
llvm::Function *FinallyFunc);
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,10 @@ void CodeGenModule::Release() {
llvm::DenormalMode::IEEE);
}

if (LangOpts.EHAsynch) {
getModule().addModuleFlag(llvm::Module::Warning, "eh-asynch", 1);
}

// Emit OpenCL specific module metadata: OpenCL/SPIR version.
if (LangOpts.OpenCL) {
EmitOpenCLMetadata();
Expand Down
Loading