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 @@ -692,11 +692,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>; +// 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 @@ -949,18 +949,6 @@ def warn_pragma_unknown_extension : Warning< "unknown OpenCL extension %0 - ignoring">, InGroup; -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, DefaultIgnore; Index: include/clang/Sema/Scope.h =================================================================== --- include/clang/Sema/Scope.h +++ include/clang/Sema/Scope.h @@ -115,8 +115,14 @@ /// 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 @@ -407,6 +413,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; @@ -3845,6 +3847,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) { // ::= TH Index: lib/AST/MicrosoftMangle.cpp =================================================================== --- lib/AST/MicrosoftMangle.cpp +++ lib/AST/MicrosoftMangle.cpp @@ -89,6 +89,7 @@ llvm::DenseMap Discriminator; llvm::DenseMap Uniquifier; llvm::DenseMap LambdaIds; + llvm::DenseMap 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. @@ -2318,6 +2321,17 @@ 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. + // + // ::= ?filt$ @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. Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -1650,6 +1650,13 @@ 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 Index: lib/CodeGen/CGException.cpp =================================================================== --- lib/CodeGen/CGException.cpp +++ lib/CodeGen/CGException.cpp @@ -16,6 +16,7 @@ #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" @@ -98,9 +99,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 @@ -137,6 +139,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; }; } @@ -159,6 +163,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 }; /// On Win64, use libgcc's SEH personality function. We fall back to dwarf on /// other platforms, unless the user asked for SjLj exceptions. @@ -231,9 +239,37 @@ 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) @@ -395,6 +431,13 @@ CGF.DeactivateCleanupBlock(cleanup, cast(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"); @@ -1642,7 +1685,210 @@ } 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 { + // 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(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(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(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(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 (SEHFinallyStmt *Finally = 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(*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) { Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -305,6 +305,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(); @@ -1990,6 +1994,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::Function *GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, + const SEHExceptStmt &Except); + + void EmitSEHExceptionCodeSave(); + llvm::Value *EmitSEHExceptionCode(); + llvm::Value *EmitSEHExceptionInfo(); + void EmitCXXForRangeStmt(const CXXForRangeStmt &S, ArrayRef Attrs = None); Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -43,7 +43,7 @@ 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/Parse/ParseStmt.cpp =================================================================== --- lib/Parse/ParseStmt.cpp +++ lib/Parse/ParseStmt.cpp @@ -466,14 +466,21 @@ 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); 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 @@ -202,6 +202,28 @@ 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(TheCall->getCallee()->IgnoreParenCasts()); + SemaRef.Diag(TheCall->getExprLoc(), DiagID) + << DRE->getDecl()->getIdentifier(); + return true; + } + + return false; +} + ExprResult Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, CallExpr *TheCall) { @@ -461,6 +483,22 @@ 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 Index: test/CodeGen/exceptions-seh-finally.c =================================================================== --- /dev/null +++ test/CodeGen/exceptions-seh-finally.c @@ -0,0 +1,38 @@ +// 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 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,154 @@ -// 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 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 @.omp_outlined.(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 @.omp_outlined.1(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,7 +146,11 @@ __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}} @@ -160,15 +167,27 @@ __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() { @@ -188,4 +207,3 @@ } __leave; // expected-error{{'__leave' statement not in __try block}} } - Index: test/SemaCXX/exceptions-seh.cpp =================================================================== --- /dev/null +++ test/SemaCXX/exceptions-seh.cpp @@ -0,0 +1,48 @@ +// 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 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(); +} + +// FIXME: Diagnose this case. For now we produce undef in codegen. +template +T func_template() { + return FN(); +} +void inject_builtins() { + func_template(); + func_template(); +}