Hi rjmccall, rsmith, majnemer,
Most of the complexity here is emitting filter expressions as standalone
LLVM functions. The filter functions are used in catch clauses in place
of type_info globals, and LLVM does the rest of the lowering.
Major aspects that still need work:
- __try body outlining with noinline and optnone.
- Cleanup support for __finally and destructors. LLVM won't be able to
lower the current IR to anything useful.
- Local variable access in filter expressions.
- Don't use TLS to figure out which filter function fired. This requires
work in LLVM.
http://reviews.llvm.org/D5607
Files:
include/clang/AST/Decl.h
include/clang/AST/Mangle.h
include/clang/Basic/Builtins.def
include/clang/Basic/DiagnosticCommonKinds.td
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Sema/Scope.h
lib/AST/ItaniumMangle.cpp
lib/AST/MicrosoftMangle.cpp
lib/CodeGen/CGBuiltin.cpp
lib/CodeGen/CGException.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/CodeGen/CodeGenFunction.h
lib/Parse/ParseStmt.cpp
lib/Sema/JumpDiagnostics.cpp
lib/Sema/Scope.cpp
lib/Sema/SemaChecking.cpp
test/CodeGen/exceptions-seh-leave.c
test/CodeGen/exceptions-seh.c
test/OpenMP/parallel_codegen.cpp
test/Sema/__try.c
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -1477,6 +1477,7 @@
bool HasImplicitReturnZero : 1;
bool IsLateTemplateParsed : 1;
bool IsConstexpr : 1;
+ bool HasSEHTry : 1;
/// \brief Indicates if the function was a definition but its body was
/// skipped.
@@ -1566,8 +1567,8 @@
HasWrittenPrototype(true), IsDeleted(false), IsTrivial(false),
IsDefaulted(false), IsExplicitlyDefaulted(false),
HasImplicitReturnZero(false), IsLateTemplateParsed(false),
- IsConstexpr(isConstexprSpecified), HasSkippedBody(false),
- EndRangeLoc(NameInfo.getEndLoc()),
+ IsConstexpr(isConstexprSpecified), HasSEHTry(false),
+ HasSkippedBody(false), EndRangeLoc(NameInfo.getEndLoc()),
TemplateOrSpecialization(),
DNLoc(NameInfo.getInfo()) {}
Index: include/clang/AST/Mangle.h
===================================================================
--- include/clang/AST/Mangle.h
+++ include/clang/AST/Mangle.h
@@ -132,6 +132,9 @@
virtual void mangleDynamicAtExitDestructor(const VarDecl *D,
raw_ostream &) = 0;
+ virtual void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl,
+ raw_ostream &Out) = 0;
+
/// Generates a unique string for an externally visible type for use with TBAA
/// or type uniquing.
/// TODO: Extend this to internal types by generating names that are unique
Index: include/clang/Basic/Builtins.def
===================================================================
--- include/clang/Basic/Builtins.def
+++ include/clang/Basic/Builtins.def
@@ -691,11 +691,15 @@
BUILTIN(__builtin_rindex, "c*cC*i", "Fn")
// Microsoft builtins. These are only active with -fms-extensions.
-LANGBUILTIN(_alloca, "v*z", "n", ALL_MS_LANGUAGES)
-LANGBUILTIN(__assume, "vb", "n", ALL_MS_LANGUAGES)
-LANGBUILTIN(__noop, "i.", "n", ALL_MS_LANGUAGES)
-LANGBUILTIN(__debugbreak, "v", "n", ALL_MS_LANGUAGES)
-LANGBUILTIN(__va_start, "vc**.", "nt", ALL_MS_LANGUAGES)
+LANGBUILTIN(_alloca, "v*z", "n", ALL_MS_LANGUAGES)
+LANGBUILTIN(__assume, "vb", "n", ALL_MS_LANGUAGES)
+LANGBUILTIN(_exception_code, "ULi", "n", ALL_MS_LANGUAGES)
+LANGBUILTIN(__exception_code, "ULi", "n", ALL_MS_LANGUAGES)
+LANGBUILTIN(_exception_info, "v*", "n", ALL_MS_LANGUAGES)
+LANGBUILTIN(__exception_info, "v*", "n", ALL_MS_LANGUAGES)
+LANGBUILTIN(__noop, "i.", "n", ALL_MS_LANGUAGES)
+LANGBUILTIN(__debugbreak, "v", "n", ALL_MS_LANGUAGES)
+LANGBUILTIN(__va_start, "vc**.", "nt", ALL_MS_LANGUAGES)
LANGBUILTIN(_InterlockedCompareExchange, "LiLiD*LiLi", "n", ALL_MS_LANGUAGES)
LANGBUILTIN(_InterlockedCompareExchangePointer, "v*v*D*v*v*", "n", ALL_MS_LANGUAGES)
LANGBUILTIN(_InterlockedIncrement, "LiLiD*", "n", ALL_MS_LANGUAGES)
Index: include/clang/Basic/DiagnosticCommonKinds.td
===================================================================
--- include/clang/Basic/DiagnosticCommonKinds.td
+++ include/clang/Basic/DiagnosticCommonKinds.td
@@ -112,6 +112,16 @@
"interpreting as unsigned">,
InGroup<DiagGroup<"implicitly-unsigned-literal">>;
+// SEH
+def err_seh_expected_handler : Error<
+ "expected '__except' or '__finally' block">;
+def err_seh___except_block : Error<
+ "%0 only allowed in __except block or filter expression">;
+def err_seh___except_filter : Error<
+ "%0 only allowed in __except filter expression">;
+def err_seh___finally_block : Error<
+ "%0 only allowed in __finally block">;
+
// Sema && AST
def note_invalid_subexpr_in_const_expr : Note<
"subexpression not valid in a constant expression">;
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td
+++ include/clang/Basic/DiagnosticParseKinds.td
@@ -916,18 +916,6 @@
def warn_pragma_unknown_extension : Warning<
"unknown OpenCL extension %0 - ignoring">, InGroup<IgnoredPragmas>;
-def err_seh_expected_handler : Error<
- "expected '__except' or '__finally' block">;
-
-def err_seh___except_block : Error<
- "%0 only allowed in __except block">;
-
-def err_seh___except_filter : Error<
- "%0 only allowed in __except filter expression">;
-
-def err_seh___finally_block : Error<
- "%0 only allowed in __finally block">;
-
// OpenMP support.
def warn_pragma_omp_ignored : Warning<
"unexpected '#pragma omp ...' in program">, InGroup<SourceUsesOpenMP>, DefaultIgnore;
Index: include/clang/Sema/Scope.h
===================================================================
--- include/clang/Sema/Scope.h
+++ include/clang/Sema/Scope.h
@@ -115,8 +115,11 @@
/// This scope corresponds to an enum.
EnumScope = 0x40000,
- /// This scope corresponds to a SEH try.
+ /// This scope corresponds to an SEH try.
SEHTryScope = 0x80000,
+
+ /// This scope corresponds to an SEH except.
+ SEHExceptScope = 0x100000,
};
private:
/// The parent scope for this scope. This is null for the translation-unit
@@ -407,6 +410,9 @@
/// \brief Determine whether this scope is a SEH '__try' block.
bool isSEHTryScope() const { return getFlags() & Scope::SEHTryScope; }
+ /// \brief Determine whether this scope is a SEH '__except' block.
+ bool isSEHExceptScope() const { return getFlags() & Scope::SEHExceptScope; }
+
/// containedInPrototypeScope - Return true if this or a parent scope
/// is a FunctionPrototypeScope.
bool containedInPrototypeScope() const;
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -156,6 +156,8 @@
void mangleDynamicInitializer(const VarDecl *D, raw_ostream &Out) override;
void mangleDynamicAtExitDestructor(const VarDecl *D,
raw_ostream &Out) override;
+ void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl,
+ raw_ostream &Out) override;
void mangleItaniumThreadLocalInit(const VarDecl *D, raw_ostream &) override;
void mangleItaniumThreadLocalWrapper(const VarDecl *D,
raw_ostream &) override;
@@ -3817,6 +3819,16 @@
Mangler.getStream() << D->getName();
}
+void ItaniumMangleContextImpl::mangleSEHFilterExpression(
+ const NamedDecl *EnclosingDecl, raw_ostream &Out) {
+ CXXNameMangler Mangler(*this, Out);
+ Mangler.getStream() << "__filt_";
+ if (shouldMangleDeclName(EnclosingDecl))
+ Mangler.mangle(EnclosingDecl);
+ else
+ Mangler.getStream() << EnclosingDecl->getName();
+}
+
void ItaniumMangleContextImpl::mangleItaniumThreadLocalInit(const VarDecl *D,
raw_ostream &Out) {
// <special-name> ::= TH <object name>
Index: lib/AST/MicrosoftMangle.cpp
===================================================================
--- lib/AST/MicrosoftMangle.cpp
+++ lib/AST/MicrosoftMangle.cpp
@@ -89,6 +89,7 @@
llvm::DenseMap<DiscriminatorKeyTy, unsigned> Discriminator;
llvm::DenseMap<const NamedDecl *, unsigned> Uniquifier;
llvm::DenseMap<const CXXRecordDecl *, unsigned> LambdaIds;
+ llvm::DenseMap<const NamedDecl *, unsigned> SEHFilterIds;
public:
MicrosoftMangleContextImpl(ASTContext &Context, DiagnosticsEngine &Diags)
@@ -134,6 +135,8 @@
void mangleDynamicInitializer(const VarDecl *D, raw_ostream &Out) override;
void mangleDynamicAtExitDestructor(const VarDecl *D,
raw_ostream &Out) override;
+ void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl,
+ raw_ostream &Out) override;
void mangleStringLiteral(const StringLiteral *SL, raw_ostream &Out) override;
bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) {
// Lambda closure types are already numbered.
@@ -2316,6 +2319,18 @@
Mangler.getStream() << '@';
}
+void MicrosoftMangleContextImpl::mangleSEHFilterExpression(
+ const NamedDecl *EnclosingDecl, raw_ostream &Out) {
+ MicrosoftCXXNameMangler Mangler(*this, Out);
+ // The function body is in the same comdat as the function with the handler,
+ // so the numbering here doesn't have to be the same across TUs.
+ //
+ // <mangled-name> ::= ?filt$ <filter-number> @0
+ Mangler.getStream() << "\01?filt$" << SEHFilterIds[EnclosingDecl]++ << "@0@";
+ // FIXME
+ Mangler.mangleName(EnclosingDecl);
+}
+
void MicrosoftMangleContextImpl::mangleTypeName(QualType T, raw_ostream &Out) {
// This is just a made up unique string for the purposes of tbaa. undname
// does *not* know how to demangle it.
Index: lib/CodeGen/CGBuiltin.cpp
===================================================================
--- lib/CodeGen/CGBuiltin.cpp
+++ lib/CodeGen/CGBuiltin.cpp
@@ -1624,6 +1624,15 @@
RMWI->setVolatile(true);
return RValue::get(RMWI);
}
+
+ case Builtin::BI__exception_code:
+ case Builtin::BI_exception_code: {
+ return RValue::get(EmitSEHExceptionCode());
+ }
+ case Builtin::BI__exception_info:
+ case Builtin::BI_exception_info: {
+ return RValue::get(EmitSEHExceptionInfo(E));
+ }
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
Index: lib/CodeGen/CGException.cpp
===================================================================
--- lib/CodeGen/CGException.cpp
+++ lib/CodeGen/CGException.cpp
@@ -13,8 +13,10 @@
#include "CodeGenFunction.h"
#include "CGCleanup.h"
+#include "CGCXXABI.h"
#include "CGObjCRuntime.h"
#include "TargetInfo.h"
+#include "clang/AST/Mangle.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
#include "llvm/IR/CallSite.h"
@@ -106,9 +108,10 @@
StringRef name;
// In C++, use std::terminate().
- if (CGM.getLangOpts().CPlusPlus)
- name = "_ZSt9terminatev"; // FIXME: mangling!
- else if (CGM.getLangOpts().ObjC1 &&
+ if (CGM.getLangOpts().CPlusPlus &&
+ CGM.getTarget().getCXXABI().isItaniumFamily()) {
+ name = "_ZSt9terminatev";
+ } else if (CGM.getLangOpts().ObjC1 &&
CGM.getLangOpts().ObjCRuntime.hasTerminate())
name = "objc_terminate";
else
@@ -134,7 +137,7 @@
// This function must have prototype void(void*).
const char *CatchallRethrowFn;
- static const EHPersonality &get(const LangOptions &Lang);
+ static const EHPersonality &get(CodeGenModule &CGM);
static const EHPersonality GNU_C;
static const EHPersonality GNU_C_SJLJ;
static const EHPersonality GNU_C_SEH;
@@ -145,6 +148,8 @@
static const EHPersonality GNU_CPlusPlus;
static const EHPersonality GNU_CPlusPlus_SJLJ;
static const EHPersonality GNU_CPlusPlus_SEH;
+ static const EHPersonality MSVC_except_handler;
+ static const EHPersonality MSVC_C_specific_handler;
};
}
@@ -167,6 +172,10 @@
EHPersonality::GNU_ObjCXX = { "__gnustep_objcxx_personality_v0", nullptr };
const EHPersonality
EHPersonality::GNUstep_ObjC = { "__gnustep_objc_personality_v0", nullptr };
+const EHPersonality
+EHPersonality::MSVC_except_handler = { "_except_handler4", nullptr };
+const EHPersonality
+EHPersonality::MSVC_C_specific_handler = { "__C_specific_handler", nullptr };
static const EHPersonality &getCPersonality(const LangOptions &L) {
if (L.SjLjExceptions)
@@ -230,7 +239,37 @@
llvm_unreachable("bad runtime kind");
}
-const EHPersonality &EHPersonality::get(const LangOptions &L) {
+static const EHPersonality &getCPersonalityMSVC(const llvm::Triple &T,
+ const LangOptions &L) {
+ if (L.SjLjExceptions)
+ return EHPersonality::GNU_C_SJLJ;
+
+ if (T.getArch() == llvm::Triple::x86)
+ return EHPersonality::MSVC_except_handler;
+ return EHPersonality::MSVC_C_specific_handler;
+}
+
+static const EHPersonality &getCXXPersonalityMSVC(const llvm::Triple &T,
+ const LangOptions &L) {
+ if (L.SjLjExceptions)
+ return EHPersonality::GNU_CPlusPlus_SJLJ;
+ // FIXME: Implement C++ exceptions.
+ return getCPersonalityMSVC(T, L);
+}
+
+const EHPersonality &EHPersonality::get(CodeGenModule &CGM) {
+ const llvm::Triple &T = CGM.getTarget().getTriple();
+ const LangOptions &L = CGM.getLangOpts();
+ // Try to pick a personality function that is compatible with MSVC if we're
+ // not compiling Obj-C. Obj-C users better have an Obj-C runtime that supports
+ // the GCC-style personality function.
+ if (T.isWindowsMSVCEnvironment() && !L.ObjC1) {
+ if (L.CPlusPlus)
+ return getCXXPersonalityMSVC(T, L);
+ else
+ return getCPersonalityMSVC(T, L);
+ }
+
if (L.CPlusPlus && L.ObjC1)
return getObjCXXPersonality(L);
else if (L.CPlusPlus)
@@ -315,7 +354,7 @@
if (!LangOpts.ObjCRuntime.isNeXTFamily())
return;
- const EHPersonality &ObjCXX = EHPersonality::get(LangOpts);
+ const EHPersonality &ObjCXX = EHPersonality::get(*this);
const EHPersonality &CXX = getCXXPersonality(LangOpts);
if (&ObjCXX == &CXX)
return;
@@ -391,15 +430,26 @@
CGF.DeactivateCleanupBlock(cleanup, cast<llvm::Instruction>(typedAddr));
}
+/// Returns true if this landing pad is using filter functions to select catches
+/// instead of RTTI.
+static bool EHUsesFilterFunctions(CodeGenModule &CGM) {
+ const EHPersonality &P = EHPersonality::get(CGM);
+ return StringRef("__C_specific_handler") == P.PersonalityFn;
+}
+
llvm::Value *CodeGenFunction::getExceptionSlot() {
if (!ExceptionSlot)
ExceptionSlot = CreateTempAlloca(Int8PtrTy, "exn.slot");
return ExceptionSlot;
}
llvm::Value *CodeGenFunction::getEHSelectorSlot() {
- if (!EHSelectorSlot)
- EHSelectorSlot = CreateTempAlloca(Int32Ty, "ehselector.slot");
+ if (!EHSelectorSlot) {
+ if (EHUsesFilterFunctions(CGM))
+ EHSelectorSlot = CreateTempAlloca(VoidPtrTy, "ehselector.slot");
+ else
+ EHSelectorSlot = CreateTempAlloca(Int32Ty, "ehselector.slot");
+ }
return EHSelectorSlot;
}
@@ -712,6 +762,26 @@
return LP;
}
+/// Return a linkonce_odr __clang_seh_filter_id thread local global. This global
+/// is used to return information from SEH filter functions to landing pads when
+/// using the __C_specific_handler personality function.
+/// FIXME: Communicate through frame_pointer instead.
+static llvm::GlobalValue *getFilterFuncTLS(CodeGenModule &CGM) {
+ llvm::GlobalValue *Entry = CGM.GetGlobalValue("__clang_seh_filter_id");
+ if (Entry) {
+ assert(isa<llvm::GlobalVariable>(Entry));
+ assert(Entry->getType() == CGM.VoidPtrTy->getPointerTo());
+ assert(Entry->isThreadLocal());
+ return Entry;
+ }
+ Entry = new llvm::GlobalVariable(
+ CGM.getModule(), CGM.VoidPtrTy, /*isConstant=*/false,
+ llvm::GlobalValue::LinkOnceODRLinkage,
+ llvm::Constant::getNullValue(CGM.VoidPtrTy), "__clang_seh_filter_id");
+ Entry->setThreadLocalMode(llvm::GlobalValue::LocalDynamicTLSModel);
+ return Entry;
+}
+
llvm::BasicBlock *CodeGenFunction::EmitLandingPad() {
assert(EHStack.requiresLandingPad());
@@ -733,7 +803,7 @@
if (CGDebugInfo *DI = getDebugInfo())
DI->EmitLocation(Builder, CurEHLocation);
- const EHPersonality &personality = EHPersonality::get(getLangOpts());
+ const EHPersonality &personality = EHPersonality::get(CGM);
// Create and configure the landing pad.
llvm::BasicBlock *lpad = createBasicBlock("lpad");
@@ -745,7 +815,11 @@
llvm::Value *LPadExn = Builder.CreateExtractValue(LPadInst, 0);
Builder.CreateStore(LPadExn, getExceptionSlot());
- llvm::Value *LPadSel = Builder.CreateExtractValue(LPadInst, 1);
+ llvm::Value *LPadSel = nullptr;
+ if (EHUsesFilterFunctions(CGM))
+ LPadSel = Builder.CreateLoad(getFilterFuncTLS(CGM));
+ else
+ LPadSel = Builder.CreateExtractValue(LPadInst, 1);
Builder.CreateStore(LPadSel, getEHSelectorSlot());
// Save the exception pointer. It's safe to use a single exception
@@ -1167,13 +1241,17 @@
nextIsEnd = false;
}
- // Figure out the catch type's index in the LSDA's type table.
- llvm::CallInst *typeIndex =
- CGF.Builder.CreateCall(llvm_eh_typeid_for, typeValue);
- typeIndex->setDoesNotThrow();
+ llvm::Value *IdValue = typeValue;
+ if (!EHUsesFilterFunctions(CGF.CGM)) {
+ // Figure out the catch type's index in the LSDA's type table.
+ llvm::CallInst *typeIndex =
+ CGF.Builder.CreateCall(llvm_eh_typeid_for, typeValue);
+ typeIndex->setDoesNotThrow();
+ IdValue = typeIndex;
+ }
llvm::Value *matchesTypeIndex =
- CGF.Builder.CreateICmpEQ(selector, typeIndex, "matches");
+ CGF.Builder.CreateICmpEQ(selector, IdValue, "matches");
CGF.Builder.CreateCondBr(matchesTypeIndex, handler.Block, nextBlock);
// If the next handler is a catch-all, we're completely done.
@@ -1550,7 +1628,7 @@
Builder.SetInsertPoint(TerminateLandingPad);
// Tell the backend that this is a landing pad.
- const EHPersonality &Personality = EHPersonality::get(CGM.getLangOpts());
+ const EHPersonality &Personality = EHPersonality::get(CGM);
llvm::LandingPadInst *LPadInst =
Builder.CreateLandingPad(llvm::StructType::get(Int8PtrTy, Int32Ty, NULL),
getOpaquePersonalityFn(CGM, Personality), 0);
@@ -1609,7 +1687,7 @@
EHResumeBlock = createBasicBlock("eh.resume");
Builder.SetInsertPoint(EHResumeBlock);
- const EHPersonality &Personality = EHPersonality::get(CGM.getLangOpts());
+ const EHPersonality &Personality = EHPersonality::get(CGM);
// This can always be a call because we necessarily didn't find
// anything on the EH stack which needs our help.
@@ -1639,7 +1717,216 @@
}
void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
- CGM.ErrorUnsupported(&S, "SEH __try");
+ EnterSEHTryStmt(S);
+ EmitStmt(S.getTryBlock());
+ ExitSEHTryStmt(S);
+}
+
+namespace {
+struct PerformSEHFinally : EHScopeStack::Cleanup {
+ Stmt *Block;
+ PerformSEHFinally(Stmt *Block) : Block(Block) {}
+ void Emit(CodeGenFunction &CGF, Flags F) override { CGF.EmitStmt(Block); }
+};
+}
+
+llvm::Constant *
+CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
+ const SEHExceptStmt &Except) {
+ const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl;
+ llvm::Function *ParentFn = ParentCGF.CurFn;
+
+ Expr *FilterExpr = Except.getFilterExpr();
+
+ // Get the mangled function name.
+ SmallString<128> Name;
+ {
+ llvm::raw_svector_ostream OS(Name);
+ const NamedDecl *Parent = dyn_cast_or_null<NamedDecl>(ParentCodeDecl);
+ assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH");
+ CGM.getCXXABI().getMangleContext().mangleSEHFilterExpression(Parent, OS);
+ }
+
+ // Arrange a function with the declaration:
+ // int filt(EXCEPTION_POINTERS *exception_pointers, void *frame_pointer)
+ QualType RetTy = getContext().IntTy;
+ FunctionArgList Args;
+ SEHPointersDecl = ImplicitParamDecl::Create(
+ getContext(), nullptr, FilterExpr->getLocStart(),
+ &getContext().Idents.get("exception_pointers"), getContext().VoidPtrTy);
+ Args.push_back(SEHPointersDecl);
+ Args.push_back(ImplicitParamDecl::Create(
+ getContext(), nullptr, FilterExpr->getLocStart(),
+ &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy));
+ const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionDeclaration(
+ RetTy, Args, FunctionType::ExtInfo(), /*isVariadic=*/false);
+ llvm::FunctionType *FnTy = CGM.getTypes().GetFunctionType(FnInfo);
+ llvm::Function *Fn = llvm::Function::Create(FnTy, ParentFn->getLinkage(),
+ Name.str(), &CGM.getModule());
+
+ // The filter is either in the same comdat as the function, or it's internal.
+ if (llvm::Comdat *C = ParentFn->getComdat()) {
+ Fn->setComdat(C);
+ } else if (ParentFn->hasWeakLinkage() || ParentFn->hasLinkOnceLinkage()) {
+ llvm::Comdat *C = CGM.getModule().getOrInsertComdat(ParentFn->getName());
+ ParentFn->setComdat(C);
+ Fn->setComdat(C);
+ } else {
+ Fn->setLinkage(llvm::GlobalValue::InternalLinkage);
+ }
+
+ StartFunction(GlobalDecl(), RetTy, Fn, FnInfo, Args,
+ FilterExpr->getLocStart(), FilterExpr->getLocStart());
+
+ EmitSEHExceptionCodeSave();
+
+ // Insert dummy allocas for every local variable in scope. We'll initialize
+ // them and prune the unused ones after we find out which ones were
+ // referenced.
+ for (const auto &DeclPtrs : ParentCGF.LocalDeclMap) {
+ const Decl *VD = DeclPtrs.first;
+ llvm::Value *Ptr = DeclPtrs.second;
+ auto *ValTy = cast<llvm::PointerType>(Ptr->getType())->getElementType();
+ LocalDeclMap[VD] = CreateTempAlloca(ValTy, Ptr->getName() + ".filt");
+ }
+
+ // Emit the original filter expression.
+ llvm::Value *R = EmitScalarExpr(FilterExpr);
+ R = Builder.CreateIntCast(R, CGM.IntTy,
+ FilterExpr->getType()->isSignedIntegerType());
+ Builder.CreateStore(R, ReturnValue);
+
+ // Remember if the filter fired and store it to TLS.
+ // __clang_seh_filter_id = Result ? Fn : nullptr;
+ // FIXME: LLVM should figure out which filter fired even with the SEH
+ // personality functions.
+ llvm::Constant *VoidPtrFn = llvm::ConstantExpr::getBitCast(CurFn, Int8PtrTy);
+ llvm::Value *Cond =
+ Builder.CreateICmpEQ(R, llvm::ConstantInt::get(CGM.IntTy, 1));
+ llvm::Value *Id = Builder.CreateSelect(
+ Cond, VoidPtrFn, llvm::Constant::getNullValue(Int8PtrTy));
+ Builder.CreateStore(Id, getFilterFuncTLS(CGM));
+
+ FinishFunction(FilterExpr->getLocEnd());
+
+ // Prune unused local references to locals from the parent function, and
+ // diagnose the remaining uses until we can rewrite them to point into the
+ // parent frame.
+ // FIXME: We can use the frame_pointer parameter to write to the parent
+ // function's local stack variables instead, but probably need intrinsics to
+ // make that work.
+ for (const auto &DeclPtrs : ParentCGF.LocalDeclMap) {
+ const Decl *VD = DeclPtrs.first;
+ auto *Alloca = cast<llvm::AllocaInst>(LocalDeclMap[VD]);
+ if (Alloca->hasNUses(0)) {
+ Alloca->eraseFromParent();
+ } else {
+ ErrorUnsupported(FilterExpr,
+ "local variable reference in SEH filter expression");
+ Alloca->replaceAllUsesWith(llvm::UndefValue::get(Alloca->getType()));
+ Alloca->eraseFromParent();
+ }
+ }
+
+ return VoidPtrFn;
+}
+
+void CodeGenFunction::EmitSEHExceptionCodeSave() {
+ // Save the exception code in the exception slot to unify exception access in
+ // the filter function and the landing pad.
+ // struct EXCEPTION_POINTERS {
+ // EXCEPTION_RECORD *ExceptionRecord;
+ // CONTEXT *ContextRecord;
+ // };
+ // void *exn.slot =
+ // (void *)(uintptr_t)exception_pointers->ExceptionRecord->ExceptionCode;
+ llvm::Value *Ptrs = Builder.CreateLoad(GetAddrOfLocalVar(SEHPointersDecl));
+ llvm::Type *RecordTy = CGM.Int32Ty->getPointerTo();
+ llvm::Type *PtrsTy = llvm::StructType::get(RecordTy, CGM.VoidPtrTy, nullptr);
+ Ptrs = Builder.CreateBitCast(Ptrs, PtrsTy->getPointerTo());
+ llvm::Value *Rec = Builder.CreateStructGEP(Ptrs, 0);
+ Rec = Builder.CreateLoad(Rec);
+ llvm::Value *Code = Builder.CreateLoad(Rec);
+ Code = Builder.CreateZExt(Code, CGM.IntPtrTy);
+ Code = Builder.CreateIntToPtr(Code, CGM.VoidPtrTy);
+ Builder.CreateStore(Code, getExceptionSlot());
+}
+
+llvm::Value *CodeGenFunction::EmitSEHExceptionCode() {
+ // If we're in a landing pad or filter function, exn.slot will point to the
+ // EXCEPTION_RECORD. The first field of the EXCEPTION_RECORD is always
+ // "DWORD ExceptionCode", so load an int.
+ assert(ExceptionSlot);
+ llvm::Value *Code =
+ Builder.CreatePtrToInt(getExceptionFromSlot(), CGM.IntPtrTy);
+ return Builder.CreateTrunc(Code, CGM.Int32Ty);
+}
+
+llvm::Value *CodeGenFunction::EmitSEHExceptionInfo(const CallExpr *E) {
+ if (!SEHPointersDecl) {
+ // FIXME: Diagnose earlier.
+ unsigned DiagID = CGM.getDiags().getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "cannot use _exception_info outside of filter expressions");
+ CGM.getDiags().Report(E->getLocStart(), DiagID) << E->getSourceRange();
+ return llvm::Constant::getNullValue(CGM.VoidPtrTy);
+ }
+ return Builder.CreateLoad(GetAddrOfLocalVar(SEHPointersDecl));
+}
+
+void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) {
+ if (SEHExceptStmt *Except = S.getExceptHandler()) {
+ EHCatchScope *CatchScope = EHStack.pushCatch(1);
+ CodeGenFunction FilterCGF(CGM, /*suppressNewContext=*/true);
+ llvm::Constant *FilterFuncPtr =
+ FilterCGF.GenerateSEHFilterFunction(*this, *Except);
+ llvm::BasicBlock *ExceptBB = createBasicBlock("__except");
+ CatchScope->setHandler(0, FilterFuncPtr, ExceptBB);
+ }
+
+ if (SEHFinallyStmt *Finally = S.getFinallyHandler()) {
+ // SEH cleanups should be simple.
+ EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup,
+ Finally->getBlock());
+ }
+}
+
+void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) {
+ if (SEHExceptStmt *Except = S.getExceptHandler()) {
+ EHCatchScope &CatchScope = cast<EHCatchScope>(*EHStack.begin());
+
+ // Don't emit the __except block if the __try block lacked invokes.
+ // TODO: Model unwind edges from instructions, either with iload / istore or
+ // a try body function.
+ if (!CatchScope.hasEHBranches()) {
+ CatchScope.clearHandlerBlocks();
+ EHStack.popCatch();
+ return;
+ }
+
+ // The fall-through block.
+ llvm::BasicBlock *ContBB = createBasicBlock("__try.cont");
+
+ // We just emitted the body of the __try; jump to the continue block.
+ if (HaveInsertPoint())
+ Builder.CreateBr(ContBB);
+
+ // Check if our filter function returned true.
+ emitCatchDispatchBlock(*this, CatchScope);
+
+ // Grab the block before we pop the handler.
+ llvm::BasicBlock *ExceptBB = CatchScope.getHandler(0).Block;
+ EHStack.popCatch();
+
+ EmitBlockAfterUses(ExceptBB);
+ EmitStmt(Except->getBlock());
+ Builder.CreateBr(ContBB);
+ EmitBlock(ContBB);
+ }
+
+ if (SEHFinallyStmt *Finally = S.getFinallyHandler()) {
+ PopCleanupBlock();
+ }
}
void CodeGenFunction::EmitSEHLeaveStmt(const SEHLeaveStmt &S) {
Index: lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- lib/CodeGen/CodeGenFunction.cpp
+++ lib/CodeGen/CodeGenFunction.cpp
@@ -42,7 +42,7 @@
SawAsmBlock(false), BlockInfo(nullptr), BlockPointer(nullptr),
LambdaThisCaptureField(nullptr), NormalCleanupDest(nullptr),
NextCleanupDestIndex(1), FirstBlockInfo(nullptr), EHResumeBlock(nullptr),
- ExceptionSlot(nullptr), EHSelectorSlot(nullptr),
+ ExceptionSlot(nullptr), EHSelectorSlot(nullptr), SEHPointersDecl(nullptr),
DebugInfo(CGM.getModuleDebugInfo()), DisableDebugInfo(false),
DidCallStackSave(false), IndirectBranch(nullptr), PGO(cgm),
SwitchInsn(nullptr), SwitchWeights(nullptr), CaseRangeBlock(nullptr),
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -312,6 +312,10 @@
/// write the current selector value into this alloca.
llvm::AllocaInst *EHSelectorSlot;
+ /// The implicit parameter to SEH filter functions of type
+ /// 'EXCEPTION_POINTERS*'.
+ ImplicitParamDecl *SEHPointersDecl;
+
/// Emits a landing pad for the current EH stack.
llvm::BasicBlock *EmitLandingPad();
@@ -1987,6 +1991,16 @@
void EmitCXXTryStmt(const CXXTryStmt &S);
void EmitSEHTryStmt(const SEHTryStmt &S);
void EmitSEHLeaveStmt(const SEHLeaveStmt &S);
+ void EnterSEHTryStmt(const SEHTryStmt &S);
+ void ExitSEHTryStmt(const SEHTryStmt &S);
+
+ llvm::Constant *GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
+ const SEHExceptStmt &S);
+
+ void EmitSEHExceptionCodeSave();
+ llvm::Value *EmitSEHExceptionCode();
+ llvm::Value *EmitSEHExceptionInfo(const CallExpr *E);
+
void EmitCXXForRangeStmt(const CXXForRangeStmt &S,
ArrayRef<const Attr *> Attrs = None);
Index: lib/Parse/ParseStmt.cpp
===================================================================
--- lib/Parse/ParseStmt.cpp
+++ lib/Parse/ParseStmt.cpp
@@ -466,7 +466,8 @@
if (ExpectAndConsume(tok::l_paren))
return StmtError();
- ParseScope ExpectScope(this, Scope::DeclScope | Scope::ControlScope);
+ ParseScope ExpectScope(this, Scope::DeclScope | Scope::ControlScope |
+ Scope::SEHExceptScope);
if (getLangOpts().Borland) {
Ident__exception_info->setIsPoisoned(false);
Index: lib/Sema/JumpDiagnostics.cpp
===================================================================
--- lib/Sema/JumpDiagnostics.cpp
+++ lib/Sema/JumpDiagnostics.cpp
@@ -338,6 +338,10 @@
return;
}
+ case Stmt::SEHTryStmtClass:
+ // FIXME: Implement jump diagnostics for bad SEH jumps.
+ break;
+
default:
break;
}
Index: lib/Sema/Scope.cpp
===================================================================
--- lib/Sema/Scope.cpp
+++ lib/Sema/Scope.cpp
@@ -185,6 +185,9 @@
} else if (Flags & SEHTryScope) {
OS << "SEHTryScope";
Flags &= ~SEHTryScope;
+ } else if (Flags & SEHExceptScope) {
+ OS << "SEHExceptScope";
+ Flags &= ~SEHExceptScope;
} else if (Flags & OpenMPDirectiveScope) {
OS << "OpenMPDirectiveScope";
Flags &= ~OpenMPDirectiveScope;
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp
+++ lib/Sema/SemaChecking.cpp
@@ -393,6 +393,36 @@
case Builtin::BI__builtin___vsnprintf_chk:
SemaBuiltinMemChkCall(*this, FDecl, TheCall, 1, 3);
break;
+
+ case Builtin::BI__exception_code:
+ case Builtin::BI_exception_code: {
+ Scope *S = getCurScope();
+ while (S && !S->isSEHExceptScope())
+ S = S->getParent();
+ if (!S || !S->isSEHExceptScope()) {
+ auto *DRE = cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+ Diag(TheCall->getExprLoc(), diag::err_seh___except_block)
+ << DRE->getDecl()->getIdentifier();
+ return ExprError();
+ }
+ break;
+ }
+
+ case Builtin::BI__exception_info:
+ case Builtin::BI_exception_info: {
+ // FIXME: Diagnose if this is used in the __except block.
+ Scope *S = getCurScope();
+ while (S && !S->isSEHExceptScope())
+ S = S->getParent();
+ if (!S || !S->isSEHExceptScope()) {
+ auto *DRE = cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+ Diag(TheCall->getExprLoc(), diag::err_seh___except_filter)
+ << DRE->getDecl()->getIdentifier();
+ return ExprError();
+ }
+ break;
+ }
+
}
// Since the target specific builtins for each arch overlap, only check those
Index: test/CodeGen/exceptions-seh-leave.c
===================================================================
--- test/CodeGen/exceptions-seh-leave.c
+++ test/CodeGen/exceptions-seh-leave.c
@@ -15,5 +15,5 @@
return 1;
}
// CHECK-NOT: error:
-// CHECK: error: cannot compile this SEH __try yet
+// CHECK: error: cannot compile this SEH __leave yet
// CHECK-NOT: error:
Index: test/CodeGen/exceptions-seh.c
===================================================================
--- test/CodeGen/exceptions-seh.c
+++ test/CodeGen/exceptions-seh.c
@@ -1,19 +1,118 @@
-// RUN: not %clang_cc1 -triple i686-pc-win32 -fexceptions -fms-extensions -emit-llvm -o - %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fexceptions -fms-extensions -emit-llvm -o - | FileCheck %s
-// This is a codegen test because we only emit the diagnostic when we start
-// generating code.
+// CHECK: @__clang_seh_filter_id = linkonce_odr thread_local(localdynamic) global i8* null
-int SaveDiv(int numerator, int denominator, int *res) {
+// FIXME: Perform this outlining automatically CodeGen.
+void try_body(int numerator, int denominator, int *myres) {
+ *myres = numerator / denominator;
+}
+// CHECK-LABEL: define void @try_body(i32 %numerator, i32 %denominator, i32* %myres)
+// CHECK: sdiv i32
+// CHECK: store i32 %{{.*}}, i32*
+// CHECK: ret void
+
+int SafeDiv(int numerator, int denominator, int *res) {
int myres = 0;
+ int success = 1;
__try {
- myres = numerator / denominator;
- __leave;
+ try_body(numerator, denominator, &myres);
} __except (1) {
- return 0;
+ success = 0;
}
*res = myres;
- return 1;
+ return success;
+}
+// CHECK-LABEL: define i32 @SafeDiv(i32 %numerator, i32 %denominator, i32* %res)
+// CHECK: invoke void @try_body(i32 %{{.*}}, i32 %{{.*}}, i32* %{{.*}})
+// CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
+// CHECK: [[lpad]]
+// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK: catch i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@SafeDiv@@" to i8*)
+// CHECK: load i8** @__clang_seh_filter_id
+// CHECK: icmp eq i8* %{{.*}}, bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@SafeDiv@@" to i8*)
+// CHECK: [[cont]]
+// CHECK: %[[myres:[^ ]*]] = load i32*
+// CHECK: store i32 %[[myres]], i32*
+
+void j(void);
+
+int nested_try() {
+ int r = 42;
+ __try {
+ __try {
+ j();
+ r = 0;
+ } __except(_exception_code() == 123) {
+ r = 123;
+ }
+ } __except(_exception_code() == 456) {
+ r = 456;
+ }
+ return r;
+}
+// CHECK-LABEL: define i32 @nested_try()
+// CHECK: store i32 42, i32* %[[r:[^ ,]*]]
+// CHECK: invoke void @j()
+// CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
+//
+// CHECK: [[cont]]
+// CHECK: store i32 0, i32* %[[r]]
+// CHECK: br label %[[inner_try_cont:[^ ]*]]
+//
+// CHECK: [[lpad]]
+// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK: catch i8* bitcast (i32 (i8*, i8*)* @"\01?filt$1@0@nested_try@@" to i8*)
+// CHECK: catch i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@nested_try@@" to i8*)
+// CHECK: load i8** @__clang_seh_filter_id
+//
+// CHECK: icmp eq i8* %{{.*}}, bitcast (i32 (i8*, i8*)* @"\01?filt$1@0@nested_try@@" to i8*)
+// CHECK: br i1
+//
+// CHECK: icmp eq i8* %{{.*}}, bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@nested_try@@" to i8*)
+// CHECK: br i1
+//
+// CHECK: store i32 456, i32* %[[r]]
+// CHECK: br label %[[outer_try_cont:[^ ]*]]
+//
+// CHECK: [[outer_try_cont]]
+// CHECK: %[[r_load:[^ ]*]] = load i32* %[[r]]
+// CHECK: ret i32 %[[r_load]]
+//
+// CHECK: store i32 123, i32* %[[r]]
+// CHECK: br label %[[inner_try_cont]]
+//
+// CHECK: [[inner_try_cont]]
+// CHECK: br label %[[outer_try_cont]]
+
+// FIXME: This lowering of __finally can't actually work, it will have to
+// change.
+static unsigned g = 0;
+void basic_finally() {
+ ++g;
+ __try {
+ j();
+ } __finally {
+ --g;
+ }
}
-// CHECK-NOT: error:
-// CHECK: error: cannot compile this SEH __try yet
-// CHECK-NOT: error:
+// CHECK-LABEL: define void @basic_finally()
+// CHECK: load i32* @g
+// CHECK: add i32 %{{.*}}, 1
+// CHECK: store i32 %{{.*}}, i32* @g
+//
+// CHECK: invoke void @j()
+// CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
+//
+// CHECK: [[cont]]
+// CHECK: load i32* @g
+// CHECK: add i32 %{{.*}}, -1
+// CHECK: store i32 %{{.*}}, i32* @g
+// CHECK: ret void
+//
+// CHECK: [[lpad]]
+// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK-NEXT: cleanup
+// CHECK: load i32* @g
+// CHECK: add i32 %{{.*}}, -1
+// CHECK: store i32 %{{.*}}, i32* @g
+// CHECK: resume
Index: test/OpenMP/parallel_codegen.cpp
===================================================================
--- test/OpenMP/parallel_codegen.cpp
+++ test/OpenMP/parallel_codegen.cpp
@@ -70,7 +70,7 @@
// CHECK-NEXT: [[ARGC:%.+]] = load i32* [[ARGC_REF]]
// CHECK-NEXT: invoke void [[FOO:@.+foo.+]](i32{{[ ]?[a-z]*}} [[ARGC]])
// CHECK: ret void
-// CHECK: call void @{{.+terminate.*}}(
+// CHECK: call void @{{.+terminate.*|abort}}(
// CHECK-NEXT: unreachable
// CHECK-NEXT: }
// CHECK-DEBUG-LABEL: define internal void @__captured_stmt(i32* %.global_tid., i32* %.bound_tid., %struct.anon* %__context)
@@ -82,7 +82,7 @@
// CHECK-DEBUG-NEXT: [[ARGC:%.+]] = load i32* [[ARGC_REF]]
// CHECK-DEBUG-NEXT: invoke void [[FOO:@.+foo.+]](i32 [[ARGC]])
// CHECK-DEBUG: ret void
-// CHECK-DEBUG: call void @{{.+terminate.*}}(
+// CHECK-DEBUG: call void @{{.+terminate.*|abort}}(
// CHECK-DEBUG-NEXT: unreachable
// CHECK-DEBUG-NEXT: }
@@ -123,7 +123,7 @@
// CHECK-NEXT: [[ARGC:%.+]] = load i8*** [[ARGC_REF]]
// CHECK-NEXT: invoke void [[FOO1:@.+foo.+]](i8** [[ARGC]])
// CHECK: ret void
-// CHECK: call void @{{.+terminate.*}}(
+// CHECK: call void @{{.+terminate.*|abort}}(
// CHECK-NEXT: unreachable
// CHECK-NEXT: }
// CHECK-DEBUG-LABEL: define internal void @__captured_stmt1(i32* %.global_tid., i32* %.bound_tid., %struct.anon.0* %__context)
@@ -135,7 +135,7 @@
// CHECK-DEBUG-NEXT: [[ARGC:%.+]] = load i8*** [[ARGC_REF]]
// CHECK-DEBUG-NEXT: invoke void [[FOO1:@.+foo.+]](i8** [[ARGC]])
// CHECK-DEBUG: ret void
-// CHECK-DEBUG: call void @{{.+terminate.*}}(
+// CHECK-DEBUG: call void @{{.+terminate.*|abort}}(
// CHECK-DEBUG-NEXT: unreachable
// CHECK-DEBUG-NEXT: }
Index: test/Sema/__try.c
===================================================================
--- test/Sema/__try.c
+++ test/Sema/__try.c
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fborland-extensions -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fborland-extensions -DBORLAND -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s
#define JOIN2(x,y) x ## y
#define JOIN(x,y) JOIN2(x,y)
@@ -10,8 +11,10 @@
struct EXCEPTION_INFO{};
-int __exception_code();
+unsigned long __exception_code();
+#ifdef BORLAND
struct EXCEPTION_INFO* __exception_info();
+#endif
void __abnormal_termination();
#define GetExceptionCode __exception_code
@@ -143,6 +146,7 @@
__except( function_scope ? 1 : -1 ) {}
}
+#ifdef BORLAND
void TEST() {
__try {
(void)AbnormalTermination; // expected-error{{only allowed in __finally block}}
@@ -162,14 +166,21 @@
}
void TEST() {
- (void)__exception_code; // expected-error{{only allowed in __except block}}
(void)__exception_info; // expected-error{{only allowed in __except filter expression}}
(void)__abnormal_termination; // expected-error{{only allowed in __finally block}}
- (void)GetExceptionCode(); // expected-error{{only allowed in __except block}}
(void)GetExceptionInformation(); // expected-error{{only allowed in __except filter expression}}
(void)AbnormalTermination(); // expected-error{{only allowed in __finally block}}
}
+#endif
+
+void TEST() {
+#ifndef BORLAND
+ (void)__exception_code; // expected-error{{builtin functions must be directly called}}
+#endif
+ (void)__exception_code(); // expected-error{{only allowed in __except block or filter expression}}
+ (void)GetExceptionCode(); // expected-error{{only allowed in __except block or filter expression}}
+}
void test_seh_leave_stmt() {
__leave; // expected-error{{'__leave' statement not in __try block}}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits