255 changes: 251 additions & 4 deletions clang/lib/CodeGen/CGException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -98,9 +99,10 @@ static llvm::Constant *getTerminateFn(CodeGenModule &CGM) {
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
Expand Down Expand Up @@ -137,6 +139,8 @@ namespace {
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;
};
}

Expand All @@ -159,6 +163,10 @@ const EHPersonality
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.
Expand Down Expand Up @@ -231,9 +239,37 @@ static const EHPersonality &getObjCXXPersonality(const llvm::Triple &T,
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)
Expand Down Expand Up @@ -1642,7 +1678,218 @@ llvm::BasicBlock *CodeGenFunction::getEHResumeBlock(bool isCleanup) {
}

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) {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
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),
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ class CodeGenFunction : public CodeGenTypeCache {
/// 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();

Expand Down Expand Up @@ -1990,6 +1994,16 @@ class CodeGenFunction : public CodeGenTypeCache {
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);

Expand Down
11 changes: 9 additions & 2 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,14 +466,21 @@ StmtResult Parser::ParseSEHExceptBlock(SourceLocation ExceptLoc) {
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);
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/JumpDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope)
return;
}

case Stmt::SEHTryStmtClass:
// FIXME: Implement jump diagnostics for bad SEH jumps.
break;

default:
break;
}
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/Scope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ void Scope::dumpImpl(raw_ostream &OS) const {
} else if (Flags & SEHTryScope) {
OS << "SEHTryScope";
Flags &= ~SEHTryScope;
} else if (Flags & SEHExceptScope) {
OS << "SEHExceptScope";
Flags &= ~SEHExceptScope;
} else if (Flags & OpenMPDirectiveScope) {
OS << "OpenMPDirectiveScope";
Flags &= ~OpenMPDirectiveScope;
Expand Down
38 changes: 38 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,28 @@ static bool SemaBuiltinCallWithStaticChain(Sema &S, CallExpr *BuiltinCall) {
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) {
Expand Down Expand Up @@ -461,6 +483,22 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
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
Expand Down
38 changes: 38 additions & 0 deletions clang/test/CodeGen/exceptions-seh-finally.c
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions clang/test/CodeGen/exceptions-seh-leave.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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:
157 changes: 146 additions & 11 deletions clang/test/CodeGen/exceptions-seh.c
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions clang/test/OpenMP/parallel_codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ int main (int argc, char **argv) {
// 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)
Expand All @@ -82,7 +82,7 @@ int main (int argc, char **argv) {
// 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: }

Expand Down Expand Up @@ -123,7 +123,7 @@ int main (int argc, char **argv) {
// 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)
Expand All @@ -135,7 +135,7 @@ int main (int argc, char **argv) {
// 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: }

Expand Down
36 changes: 27 additions & 9 deletions clang/test/Sema/__try.c
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -10,8 +11,10 @@ typedef int DWORD;

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
Expand Down Expand Up @@ -143,7 +146,11 @@ void TEST() {
__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}}
Expand All @@ -160,15 +167,27 @@ void TEST() {
__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() {
Expand All @@ -188,4 +207,3 @@ void test_seh_leave_stmt() {
}
__leave; // expected-error{{'__leave' statement not in __try block}}
}

48 changes: 48 additions & 0 deletions clang/test/SemaCXX/exceptions-seh.cpp
Original file line number Diff line number Diff line change
@@ -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 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>();
}