Author: Aaron Smith Date: 2020-04-30T09:38:19-07:00 New Revision: 4eabd006125424f879a7129eca824998192d89a9
URL: https://github.com/llvm/llvm-project/commit/4eabd006125424f879a7129eca824998192d89a9 DIFF: https://github.com/llvm/llvm-project/commit/4eabd006125424f879a7129eca824998192d89a9.diff LOG: [Windows SEH] Fix abnormal-exits in _try Summary: Per Windows SEH Spec, except _leave, all other early exits of a _try (goto/return/continue/break) are considered abnormal exits. In those cases, the first parameter passes to its _finally funclet should be TRUE to indicate an abnormal-termination. One way to implement abnormal exits in _try is to invoke Windows runtime _local_unwind() (MSVC approach) that will invoke _dtor funclet where abnormal-termination flag is always TRUE when calling _finally. Obviously this approach is less optimal and is complicated to implement in Clang. Clang today has a NormalCleanupDestSlot mechanism to dispatch multiple exits at the end of _try. Since _leave (or try-end fall-through) is always Indexed with 0 in that NormalCleanupDestSlot, this fix takes the advantage of that mechanism and just passes NormalCleanupDest ID as 1st Arg to _finally. Reviewers: rnk, eli.friedman, JosephTremoulet, asmith, efriedma Reviewed By: efriedma Subscribers: efriedma, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D77936 Added: clang/test/CodeGen/windows-seh-abnormal-exits.c Modified: clang/lib/CodeGen/CGCleanup.cpp clang/lib/CodeGen/CGException.cpp clang/lib/CodeGen/EHScopeStack.h Removed: ################################################################################ diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index 5e01100db163..70eaa321a007 100644 --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -860,6 +860,9 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // 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.setHasExitSwitch(); + llvm::LoadInst *Load = createLoadInstBefore(getNormalCleanupDestSlot(), "cleanup.dest", nullptr); diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index a542c3d85a84..a5dae1b32e69 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1639,6 +1639,19 @@ struct PerformSEHFinally final : EHScopeStack::Cleanup { 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.hasExitSwitch()) { + Address Addr = CGF.getNormalCleanupDestSlot(); + llvm::Value *Load = CGF.Builder.CreateLoad(Addr, "cleanup.dest"); + llvm::Value *Zero = llvm::Constant::getNullValue(CGM.Int32Ty); + IsForEH = CGF.Builder.CreateICmpNE(Load, Zero); + } + 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 index 0ed67aabcd62..0fa0b54be2f0 100644 --- a/clang/lib/CodeGen/EHScopeStack.h +++ b/clang/lib/CodeGen/EHScopeStack.h @@ -158,9 +158,10 @@ class EHScopeStack { /// Generation flags. class Flags { enum { - F_IsForEH = 0x1, + F_IsForEH = 0x1, F_IsNormalCleanupKind = 0x2, - F_IsEHCleanupKind = 0x4 + F_IsEHCleanupKind = 0x4, + F_HasExitSwitch = 0x8, }; unsigned flags; @@ -179,8 +180,10 @@ class EHScopeStack { /// cleanup. bool isEHCleanupKind() const { return flags & F_IsEHCleanupKind; } void setIsEHCleanupKind() { flags |= F_IsEHCleanupKind; } - }; + bool hasExitSwitch() const { return flags & F_HasExitSwitch; } + void setHasExitSwitch() { flags |= F_HasExitSwitch; } + }; /// 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 index 000000000000..971e4f008a41 --- /dev/null +++ b/clang/test/CodeGen/windows-seh-abnormal-exits.c @@ -0,0 +1,30 @@ +// 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-]+]] = icmp ne i32 %cleanup.dest, 0 +// CHECK-NEXT: %[[src3:[0-9-]+]] = zext i1 %[[src2]] to i8 +// CHECK-NEXT: call void @"?fin$0@0@seh_abnormal_exits@@"(i8 %[[src3]], 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 +} + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits