[clang] 4eabd00 - [Windows SEH] Fix abnormal-exits in _try

Aaron Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 30 09:41:03 PDT 2020


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 at 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
+}
+


        


More information about the cfe-commits mailing list