diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -860,6 +860,9 @@ // TODO: base this on the number of branch-afters and fixups const unsigned SwitchCapacity = 10; + // pass the abnormal exit flag to Fn (SEH cleanup) + cleanupFlags.setHasSehAbnormalExits(); + llvm::LoadInst *Load = createLoadInstBefore(getNormalCleanupDestSlot(), "cleanup.dest", nullptr); diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1639,6 +1639,19 @@ llvm::Value *IsForEH = llvm::ConstantInt::get(CGF.ConvertType(ArgTys[0]), F.isForEHCleanup()); + + // Except _leave and fall-through at the end, all other exits in a _try + // (return/goto/continue/break) are considered as abnormal terminations + // since _leave/fall-through is always Indexed 0, + // just use NormalCleanupDestSlot (>= 1 for goto/return/..), + // as 1st Arg to indicate abnormal termination + if (!F.isForEHCleanup() && F.hasSehAbnormalExits()) { + assert(CGF.NextCleanupDestIndex < 256 && " JumpDest Index exceeds 255"); + Address Addr = CGF.getNormalCleanupDestSlot(); + llvm::Value* Load = CGF.Builder.CreateLoad(Addr, "cleanup.dest"); + IsForEH = CGF.Builder.CreateTrunc(Load, CGM.Int8Ty); + } + Args.add(RValue::get(IsForEH), ArgTys[0]); Args.add(RValue::get(FP), ArgTys[1]); diff --git a/clang/lib/CodeGen/EHScopeStack.h b/clang/lib/CodeGen/EHScopeStack.h --- a/clang/lib/CodeGen/EHScopeStack.h +++ b/clang/lib/CodeGen/EHScopeStack.h @@ -158,9 +158,10 @@ /// Generation flags. class Flags { enum { - F_IsForEH = 0x1, + F_IsForEH = 0x1, F_IsNormalCleanupKind = 0x2, - F_IsEHCleanupKind = 0x4 + F_IsEHCleanupKind = 0x4, + F_HasSehAbnormalExits = 0x8, }; unsigned flags; @@ -179,8 +180,10 @@ /// cleanup. bool isEHCleanupKind() const { return flags & F_IsEHCleanupKind; } void setIsEHCleanupKind() { flags |= F_IsEHCleanupKind; } - }; + bool hasSehAbnormalExits() const { return flags & F_HasSehAbnormalExits; } + void setHasSehAbnormalExits() { flags |= F_HasSehAbnormalExits; } + }; /// Emit the cleanup. For normal cleanups, this is run in the /// same EH context as when the cleanup was pushed, i.e. the diff --git a/clang/test/CodeGen/windows-seh-abnormal-exits.c b/clang/test/CodeGen/windows-seh-abnormal-exits.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/windows-seh-abnormal-exits.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-windows -fms-extensions -Wno-implicit-function-declaration -S -emit-llvm %s -o - | FileCheck %s + +// CHECK: %[[src:[0-9-]+]] = call i8* @llvm.localaddress() +// CHECK-NEXT: %cleanup.dest = load i32, i32* %cleanup.dest.slot, align 4 +// CHECK-NEXT: %[[src2:[0-9-]+]] = trunc i32 %cleanup.dest to i8 +// CHECK-NEXT: call void @"?fin$0@0@seh_abnormal_exits@@"(i8 %[[src2]], i8* %[[src]]) + +void seh_abnormal_exits(int *Counter) { + for (int i = 0; i < 5; i++) { + __try { + if (i == 0) + continue; // abnormal termination + else if (i == 1) + goto t10; // abnormal termination + else if (i == 2) + __leave; // normal execution + else if (i == 4) + return; // abnormal termination + } + __finally { + if (AbnormalTermination()) { + *Counter += 1; + } + } + t10:; + } + return; // *Counter == 3 +} +