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
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)
"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">;
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;
/// 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,
+
+ /// We are currently in the filter expression of an SEH except block.
+ SEHFilterScope = 0x200000,
};
private:
/// The parent scope for this scope. This is null for the translation-unit
/// \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;
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;
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>
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)
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.
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@";
+ 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.
Builder.CreateAlignedLoad(IntToPtr, /*Align=*/4, /*isVolatile=*/true);
return RValue::get(Load);
}
+
+ 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());
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
#include "CGCleanup.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"
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
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;
};
}
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_handler3", nullptr };
+const EHPersonality
+EHPersonality::MSVC_C_specific_handler = { "__C_specific_handler", nullptr };
/// On Win64, use libgcc's SEH personality function. We fall back to dwarf on
/// other platforms, unless the user asked for SjLj exceptions.
llvm_unreachable("bad runtime kind");
}
+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(T, L);
else if (L.CPlusPlus)
}
void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
- CGM.ErrorUnsupported(&S, "SEH __try");
+ // FIXME: Implement SEH on other architectures.
+ const llvm::Triple &T = CGM.getTarget().getTriple();
+ if (T.getArch() != llvm::Triple::x86_64 ||
+ !T.isKnownWindowsMSVCEnvironment()) {
+ ErrorUnsupported(&S, "__try statement");
+ return;
+ }
+
+ 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 {
+ // FIXME: Don't double-emit LabelDecls.
+ CGF.EmitStmt(Block);
+ }
+};
+}
+
+/// Create a stub filter function that will ultimately hold the code of the
+/// filter expression. The EH preparation passes in LLVM will outline the code
+/// from the main function body into this stub.
+llvm::Function *
+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()) {
+ // FIXME: Unreachable with Rafael's changes?
+ 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, convert to i32, and return.
+ llvm::Value *R = EmitScalarExpr(FilterExpr);
+ R = Builder.CreateIntCast(R, CGM.IntTy,
+ FilterExpr->getType()->isSignedIntegerType());
+ Builder.CreateStore(R, ReturnValue);
+
+ FinishFunction(FilterExpr->getLocEnd());
+
+ for (const auto &DeclPtrs : ParentCGF.LocalDeclMap) {
+ const Decl *VD = DeclPtrs.first;
+ auto *Alloca = cast<llvm::AllocaInst>(LocalDeclMap[VD]);
+ if (Alloca->hasNUses(0)) {
+ Alloca->eraseFromParent();
+ continue;
+ }
+ ErrorUnsupported(FilterExpr,
+ "SEH filter expression local variable capture");
+ }
+
+ return Fn;
+}
+
+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);
+ // FIXME: Change landing pads to produce {i32, i32} and make the exception
+ // slot an i32.
+ Code = Builder.CreateIntToPtr(Code, CGM.VoidPtrTy);
+ Builder.CreateStore(Code, getExceptionSlot());
+}
+
+llvm::Value *CodeGenFunction::EmitSEHExceptionInfo() {
+ // Sema should diagnose calling this builtin outside of a filter context, but
+ // don't crash if we screw up.
+ if (!SEHPointersDecl)
+ return llvm::UndefValue::get(Int8PtrTy);
+ return Builder.CreateLoad(GetAddrOfLocalVar(SEHPointersDecl));
+}
+
+llvm::Value *CodeGenFunction::EmitSEHExceptionCode() {
+ // If we're in a landing pad or filter function, the exception slot contains
+ // the code.
+ assert(ExceptionSlot);
+ llvm::Value *Code =
+ Builder.CreatePtrToInt(getExceptionFromSlot(), CGM.IntPtrTy);
+ return Builder.CreateTrunc(Code, CGM.Int32Ty);
+}
+
+void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) {
+ if (SEHFinallyStmt *Finally = S.getFinallyHandler()) {
+ // Push a cleanup for __finally blocks.
+ EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup,
+ Finally->getBlock());
+ return;
+ }
+
+ // Otherwise, we must have an __except block.
+ SEHExceptStmt *Except = S.getExceptHandler();
+ assert(Except);
+ EHCatchScope *CatchScope = EHStack.pushCatch(1);
+ CodeGenFunction FilterCGF(CGM, /*suppressNewContext=*/true);
+ llvm::Function *FilterFunc =
+ FilterCGF.GenerateSEHFilterFunction(*this, *Except);
+ llvm::Constant *OpaqueFunc =
+ llvm::ConstantExpr::getBitCast(FilterFunc, Int8PtrTy);
+ CatchScope->setHandler(0, OpaqueFunc, createBasicBlock("__except"));
+}
+
+void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) {
+ // Just pop the cleanup if it's a __finally block.
+ if (S.getFinallyHandler()) {
+ PopCleanupBlock();
+ return;
+ }
+
+ // Otherwise, we must have an __except block.
+ SEHExceptStmt *Except = S.getExceptHandler();
+ assert(Except && "__try must have __finally xor __except");
+ 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);
+
+ // Emit the __except body.
+ EmitStmt(Except->getBlock());
+
+ Builder.CreateBr(ContBB);
+
+ EmitBlock(ContBB);
}
void CodeGenFunction::EmitSEHLeaveStmt(const SEHLeaveStmt &S) {
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),
/// 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();
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::Function *GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
+ const SEHExceptStmt &Except);
+
+ void EmitSEHExceptionCodeSave();
+ llvm::Value *EmitSEHExceptionCode();
+ llvm::Value *EmitSEHExceptionInfo();
+
void EmitCXXForRangeStmt(const CXXForRangeStmt &S,
ArrayRef<const Attr *> Attrs = None);
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);
Ident___exception_info->setIsPoisoned(false);
Ident_GetExceptionInfo->setIsPoisoned(false);
}
- ExprResult FilterExpr(ParseExpression());
+
+ ExprResult FilterExpr;
+ {
+ ParseScopeFlags FilterScope(this, getCurScope()->getFlags() |
+ Scope::SEHFilterScope);
+ FilterExpr = ParseExpression();
+ }
if (getLangOpts().Borland) {
Ident__exception_info->setIsPoisoned(true);
return;
}
+ case Stmt::SEHTryStmtClass:
+ // FIXME: Implement jump diagnostics for bad SEH jumps.
+ break;
+
default:
break;
}
} else if (Flags & SEHTryScope) {
OS << "SEHTryScope";
Flags &= ~SEHTryScope;
+ } else if (Flags & SEHExceptScope) {
+ OS << "SEHExceptScope";
+ Flags &= ~SEHExceptScope;
} else if (Flags & OpenMPDirectiveScope) {
OS << "OpenMPDirectiveScope";
Flags &= ~OpenMPDirectiveScope;
return false;
}
+static bool SemaBuiltinSEHScopeCheck(Sema &SemaRef, CallExpr *TheCall,
+ Scope::ScopeFlags NeededScopeFlags,
+ unsigned DiagID) {
+ // Scopes aren't available during instantiation. Fortunately, builtin
+ // functions cannot be template args so they cannot be formed through template
+ // instantiation. Therefore checking once during the parse is sufficient.
+ if (!SemaRef.ActiveTemplateInstantiations.empty())
+ return false;
+
+ Scope *S = SemaRef.getCurScope();
+ while (S && !S->isSEHExceptScope())
+ S = S->getParent();
+ if (!S || !(S->getFlags() & NeededScopeFlags)) {
+ auto *DRE = cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+ SemaRef.Diag(TheCall->getExprLoc(), DiagID)
+ << DRE->getDecl()->getIdentifier();
+ return true;
+ }
+
+ return false;
+}
+
ExprResult
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
CallExpr *TheCall) {
if (SemaBuiltinCallWithStaticChain(*this, TheCall))
return ExprError();
break;
+
+ case Builtin::BI__exception_code:
+ case Builtin::BI_exception_code: {
+ if (SemaBuiltinSEHScopeCheck(*this, TheCall, Scope::SEHExceptScope,
+ diag::err_seh___except_block))
+ return ExprError();
+ break;
+ }
+ case Builtin::BI__exception_info:
+ case Builtin::BI_exception_info: {
+ if (SemaBuiltinSEHScopeCheck(*this, TheCall, Scope::SEHFilterScope,
+ diag::err_seh___except_filter))
+ return ExprError();
+ break;
+ }
+
}
// Since the target specific builtins for each arch overlap, only check those
--- /dev/null
+// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fexceptions -fms-extensions -emit-llvm -o - | FileCheck %s
+
+void might_crash(void);
+void cleanup(void);
+int check_condition(void);
+void basic_finally(void) {
+ __try {
+ might_crash();
+ } __finally {
+ cleanup();
+ }
+}
+
+// CHECK-LABEL: define void @basic_finally()
+// CHECK: invoke void @might_crash()
+// CHECK: call void @cleanup()
+//
+// CHECK: landingpad
+// CHECK-NEXT: cleanup
+// CHECK: invoke void @cleanup()
+//
+// CHECK: landingpad
+// CHECK-NEXT: catch i8* null
+// CHECK: call void @abort()
+
+// FIXME: This crashes.
+#if 0
+void basic_finally(void) {
+ __try {
+ might_crash();
+ } __finally {
+l:
+ cleanup();
+ if (check_condition())
+ goto l;
+ }
+}
+#endif
--- /dev/null
+// RUN: not %clang_cc1 -triple x86_64-pc-win32 -fexceptions -fms-extensions -emit-llvm -o - %s 2>&1 | FileCheck %s
+
+// This is a codegen test because we only emit the diagnostic when we start
+// generating code.
+
+int SaveDiv(int numerator, int denominator, int *res) {
+ int myres = 0;
+ __try {
+ myres = numerator / denominator;
+ __leave;
+ } __except (1) {
+ return 0;
+ }
+ *res = myres;
+ return 1;
+}
+// CHECK-NOT: error:
+// CHECK: error: cannot compile this SEH __leave yet
+// CHECK-NOT: error:
-// 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.
+// 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 SaveDiv(int numerator, int denominator, int *res) {
+int safe_div(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 = -42;
}
*res = myres;
- return 1;
+ return success;
+}
+// CHECK-LABEL: define i32 @safe_div(i32 %numerator, i32 %denominator, i32* %res)
+// CHECK: invoke void @try_body(i32 %{{.*}}, i32 %{{.*}}, i32* %{{.*}})
+// CHECK: to label %{{.*}} unwind label %[[lpad:[^ ]*]]
+//
+// CHECK: [[lpad]]
+// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK: %[[sel:[^ ]*]] = load i32*
+// CHECK: %[[filt_id:[^ ]*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@safe_div@@" to i8*))
+// CHECK: %[[matches:[^ ]*]] = icmp eq i32 %[[sel]], %[[filt_id]]
+// CHECK: br i1 %[[matches]], label %[[except_bb:[^ ]*]], label %{{.*}}
+//
+// CHECK: [[except_bb]]
+// CHECK: store i32 -42, i32* %[[success:[^ ]*]]
+//
+// CHECK: %[[res:[^ ]*]] = load i32* %[[success]]
+// CHECK: ret i32 %[[res]]
+
+void j(void);
+
+// FIXME: Implement local variable captures in filter expressions.
+int filter_expr_capture() {
+ int r = 42;
+ __try {
+ j();
+ } __except(/*r =*/ -1) {
+ r = 13;
+ }
+ return r;
+}
+
+// CHECK-LABEL: define i32 @filter_expr_capture()
+// FIXMECHECK: %[[captures]] = call i8* @llvm.frameallocate(i32 4)
+// CHECK: store i32 42, i32* %[[r:[^ ,]*]]
+// CHECK: invoke void @j()
+//
+// CHECK: landingpad
+// CHECK-NEXT: catch i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@filter_expr_capture@@" to i8*)
+// CHECK: store i32 13, i32* %[[r]]
+//
+// CHECK: %[[rv:[^ ]*]] = load i32* %[[r]]
+// CHECK: ret i32 %[[rv]]
+
+// CHECK-LABEL: define internal i32 @"\01?filt$0@0@filter_expr_capture@@"(i8* %exception_pointers, i8* %frame_pointer)
+// FIXMECHECK: %[[captures]] = call i8* @llvm.framerecover(i8* bitcast (i32 ()* @filter_expr_capture, i8* %frame_pointer)
+// FIXMECHECK: store i32 -1, i32* %{{.*}}
+// CHECK: ret i32 -1
+
+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: store i8* %{{.*}}, i8** %[[ehptr_slot:[^ ]*]]
+// CHECK: store i32 %{{.*}}, i32* %[[sel_slot:[^ ]*]]
+//
+// CHECK: load i32* %[[sel_slot]]
+// CHECK: call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @"\01?filt$1@0@nested_try@@" to i8*))
+// CHECK: icmp eq i32
+// CHECK: br i1
+//
+// CHECK: load i32* %[[sel_slot]]
+// CHECK: call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@nested_try@@" to i8*))
+// CHECK: icmp eq i32
+// 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
// 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 @.omp_outlined.(i32* %.global_tid., i32* %.bound_tid., %struct.anon* %__context)
// 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: }
// 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 @.omp_outlined.1(i32* %.global_tid., i32* %.bound_tid., %struct.anon.0* %__context)
// 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: }
-// 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)
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
__except( function_scope ? 1 : -1 ) {}
}
+#ifdef BORLAND
void TEST() {
+ (void)__abnormal_termination(); // expected-error{{only allowed in __finally block}}
+ (void)AbnormalTermination(); // expected-error{{only allowed in __finally block}}
+
__try {
(void)AbnormalTermination; // expected-error{{only allowed in __finally block}}
(void)__abnormal_termination; // expected-error{{only allowed in __finally block}}
__abnormal_termination();
}
}
+#endif
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)__exception_info(); // expected-error{{only allowed in __except filter expression}}
(void)GetExceptionInformation(); // expected-error{{only allowed in __except filter expression}}
- (void)AbnormalTermination(); // expected-error{{only allowed in __finally block}}
+}
+
+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() {
+ __try {
+ } __except(1) {
+ GetExceptionCode(); // valid
+ GetExceptionInformation(); // expected-error{{only allowed in __except filter expression}}
+ }
}
void test_seh_leave_stmt() {
}
__leave; // expected-error{{'__leave' statement not in __try block}}
}
-
--- /dev/null
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions -fsyntax-only -verify %s
+
+// Basic usage should work.
+int safe_div(int n, int d) {
+ int r;
+ __try {
+ r = n / d;
+ } __except(_exception_code() == 0xC0000094) {
+ r = 0;
+ }
+ return r;
+}
+
+void might_crash();
+
+// Diagnose obvious builtin mis-usage.
+void bad_builtin_scope() {
+ __try {
+ might_crash();
+ } __except(1) {
+ }
+ _exception_code(); // expected-error {{'_exception_code' only allowed in __except block or filter expression}}
+ _exception_info(); // expected-error {{'_exception_info' only allowed in __except filter expression}}
+}
+
+// Diagnose obvious builtin misusage in a template.
+template <void FN()>
+void bad_builtin_scope_template() {
+ __try {
+ FN();
+ } __except(1) {
+ }
+ _exception_code(); // expected-error {{'_exception_code' only allowed in __except block or filter expression}}
+ _exception_info(); // expected-error {{'_exception_info' only allowed in __except filter expression}}
+}
+void instantiate_bad_scope_tmpl() {
+ bad_builtin_scope_template<might_crash>();
+}
+
+// FIXME: Diagnose this case. For now we produce undef in codegen.
+template <typename T, T FN()>
+T func_template() {
+ return FN();
+}
+void inject_builtins() {
+ func_template<void *, __exception_info>();
+ func_template<unsigned long, __exception_code>();
+}