[libcxx-commits] [clang-tools-extra] [libcxx] [mlir] [openmp] [llvm] [clang] [lldb] [SEH] Fix register liveness verification for EHa (PR #76933)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jan 4 20:23:49 PST 2024


https://github.com/HaohaiWen updated https://github.com/llvm/llvm-project/pull/76933

>From 8305e5e15eaaedba58a57b179e32c6d4b2a11a44 Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Thu, 4 Jan 2024 15:35:52 +0800
Subject: [PATCH 1/5] [SEH] Add test to track EHa register liveness
 verification

This test tracks bug of MachineVerifier to check live range segment for
EHa. Async exception can happen at any place within seh scope, not only
the call instruction. Need to teach MachineVerifier to know that.
---
 .../X86/windows-seh-EHa-RegisterLiveness.ll   | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll

diff --git a/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll b/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
new file mode 100644
index 00000000000000..d23318c6e16a11
--- /dev/null
+++ b/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
@@ -0,0 +1,69 @@
+; XFAIL: *
+; RUN: llc --verify-machineinstrs < %s | FileCheck %s
+source_filename = "test.cpp"
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.12.0"
+
+$"?test at Test@@Plugin@@Host@@@Z" = comdat any
+
+declare dso_local i32 @__CxxFrameHandler3(...)
+
+; Function Attrs: nounwind memory(none)
+declare dso_local void @llvm.seh.scope.begin() #1
+
+; Function Attrs: nobuiltin allocsize(0)
+declare dso_local noundef nonnull ptr @"??2 at Test@Z"(i64 noundef) #1
+
+; Function Attrs: nounwind memory(none)
+declare dso_local void @llvm.seh.scope.end() #0
+
+; Function Attrs: nobuiltin nounwind
+declare dso_local void @"??3 at YAXPEAX@Z"(ptr noundef) #2
+
+; Function Attrs: mustprogress uwtable
+define weak_odr dso_local noundef ptr @"?test at Test@@Plugin@@Host@@@Z"(ptr noundef nonnull align 8 dereferenceable(48) %this, ptr noundef %host) unnamed_addr #3 comdat align 2 personality ptr @__CxxFrameHandler3 {
+entry:
+  %host.addr = alloca ptr, align 8
+  %this.addr = alloca ptr, align 8
+  store ptr %host, ptr %host.addr, align 8
+  store ptr %this, ptr %this.addr, align 8
+  %this1 = load ptr, ptr %this.addr, align 8
+  %call = call noalias noundef nonnull ptr @"??2 at Test@Z"(i64 noundef 152) #5
+  invoke void @llvm.seh.scope.begin()
+          to label %invoke.cont unwind label %ehcleanup
+
+invoke.cont:                                      ; preds = %entry
+  %call3 = invoke noundef ptr @"??Test@?A0x2749C4FD@@QEAA at Test@Test@@@Z"(ptr noundef nonnull align 8 dereferenceable(152) %call, ptr noundef %this1)
+          to label %invoke.cont2 unwind label %ehcleanup
+
+invoke.cont2:                                     ; preds = %invoke.cont
+  invoke void @llvm.seh.scope.end()
+          to label %invoke.cont4 unwind label %ehcleanup
+
+invoke.cont4:                                     ; preds = %invoke.cont2
+  ret ptr %call
+
+ehcleanup:                                        ; preds = %invoke.cont2, %invoke.cont, %entry
+  %0 = cleanuppad within none []
+  call void @"??3 at YAXPEAX@Z"(ptr noundef %call) #6 [ "funclet"(token %0) ]
+  cleanupret from %0 unwind to caller
+}
+
+; Function Attrs: uwtable
+declare hidden noundef ptr @"??Test@?A0x2749C4FD@@QEAA at Test@Test@@@Z"(ptr noundef nonnull returned align 8 dereferenceable(152), ptr noundef) unnamed_addr #4 align 2
+
+attributes #0 = { nounwind memory(none) }
+attributes #1 = { nobuiltin allocsize(0) "target-cpu"="x86-64" "target-features"="+cmov,+crc32,+cx8,+fxsr,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87" "tune-cpu"="generic" }
+attributes #2 = { nobuiltin nounwind "target-cpu"="x86-64" "target-features"="+cmov,+crc32,+cx8,+fxsr,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87" "tune-cpu"="generic" }
+attributes #3 = { mustprogress uwtable "target-cpu"="x86-64" "target-features"="+cmov,+crc32,+cx8,+fxsr,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87" "tune-cpu"="generic" }
+attributes #4 = { uwtable "target-cpu"="x86-64" "target-features"="+cmov,+crc32,+cx8,+fxsr,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87" "tune-cpu"="generic" }
+attributes #5 = { builtin allocsize(0) }
+attributes #6 = { builtin nounwind }
+
+!llvm.module.flags = !{!1, !2, !3, !4, !5}
+
+!1 = !{i32 1, !"wchar_size", i32 2}
+!2 = !{i32 2, !"eh-asynch", i32 1}
+!3 = !{i32 8, !"PIC Level", i32 2}
+!4 = !{i32 7, !"uwtable", i32 2}
+!5 = !{i32 1, !"MaxTLSAlign", i32 65536}

>From 2d60e94362fdfbcf6964f9b50369c45f37a20398 Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Thu, 4 Jan 2024 17:15:59 +0800
Subject: [PATCH 2/5] Fix typo

---
 llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll b/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
index d23318c6e16a11..c21ac1b5436c9c 100644
--- a/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
+++ b/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
@@ -1,5 +1,5 @@
 ; XFAIL: *
-; RUN: llc --verify-machineinstrs < %s | FileCheck %s
+; RUN: llc --verify-machineinstrs < %s
 source_filename = "test.cpp"
 target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-pc-windows-msvc19.12.0"

>From 26291fef8a025b2fa42b0f884117bf003ac7a7a9 Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Thu, 4 Jan 2024 17:40:18 +0800
Subject: [PATCH 3/5] Remove attributes

---
 .../X86/windows-seh-EHa-RegisterLiveness.ll   | 29 +++++++++----------
 1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll b/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
index c21ac1b5436c9c..ff07f4ddf00546 100644
--- a/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
+++ b/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
@@ -6,34 +6,34 @@ target triple = "x86_64-pc-windows-msvc19.12.0"
 
 $"?test at Test@@Plugin@@Host@@@Z" = comdat any
 
-declare dso_local i32 @__CxxFrameHandler3(...)
+declare i32 @__CxxFrameHandler3(...)
 
 ; Function Attrs: nounwind memory(none)
-declare dso_local void @llvm.seh.scope.begin() #1
+declare void @llvm.seh.scope.begin() #1
 
 ; Function Attrs: nobuiltin allocsize(0)
-declare dso_local noundef nonnull ptr @"??2 at Test@Z"(i64 noundef) #1
+declare ptr @"??2 at Test@Z"(i64) #1
 
 ; Function Attrs: nounwind memory(none)
-declare dso_local void @llvm.seh.scope.end() #0
+declare void @llvm.seh.scope.end() #0
 
 ; Function Attrs: nobuiltin nounwind
-declare dso_local void @"??3 at YAXPEAX@Z"(ptr noundef) #2
+declare void @"??3 at YAXPEAX@Z"(ptr) #2
 
 ; Function Attrs: mustprogress uwtable
-define weak_odr dso_local noundef ptr @"?test at Test@@Plugin@@Host@@@Z"(ptr noundef nonnull align 8 dereferenceable(48) %this, ptr noundef %host) unnamed_addr #3 comdat align 2 personality ptr @__CxxFrameHandler3 {
+define ptr @"?test at Test@@Plugin@@Host@@@Z"(ptr %this, ptr %host) #3 comdat align 2 personality ptr @__CxxFrameHandler3 {
 entry:
   %host.addr = alloca ptr, align 8
   %this.addr = alloca ptr, align 8
   store ptr %host, ptr %host.addr, align 8
   store ptr %this, ptr %this.addr, align 8
   %this1 = load ptr, ptr %this.addr, align 8
-  %call = call noalias noundef nonnull ptr @"??2 at Test@Z"(i64 noundef 152) #5
+  %call = call noalias ptr @"??2 at Test@Z"(i64 152) #5
   invoke void @llvm.seh.scope.begin()
           to label %invoke.cont unwind label %ehcleanup
 
 invoke.cont:                                      ; preds = %entry
-  %call3 = invoke noundef ptr @"??Test@?A0x2749C4FD@@QEAA at Test@Test@@@Z"(ptr noundef nonnull align 8 dereferenceable(152) %call, ptr noundef %this1)
+  %call3 = invoke ptr @"??Test@?A0x2749C4FD@@QEAA at Test@Test@@@Z"(ptr %call, ptr %this1)
           to label %invoke.cont2 unwind label %ehcleanup
 
 invoke.cont2:                                     ; preds = %invoke.cont
@@ -45,12 +45,12 @@ invoke.cont4:                                     ; preds = %invoke.cont2
 
 ehcleanup:                                        ; preds = %invoke.cont2, %invoke.cont, %entry
   %0 = cleanuppad within none []
-  call void @"??3 at YAXPEAX@Z"(ptr noundef %call) #6 [ "funclet"(token %0) ]
+  call void @"??3 at YAXPEAX@Z"(ptr %call) #6 [ "funclet"(token %0) ]
   cleanupret from %0 unwind to caller
 }
 
 ; Function Attrs: uwtable
-declare hidden noundef ptr @"??Test@?A0x2749C4FD@@QEAA at Test@Test@@@Z"(ptr noundef nonnull returned align 8 dereferenceable(152), ptr noundef) unnamed_addr #4 align 2
+declare hidden ptr @"??Test@?A0x2749C4FD@@QEAA at Test@Test@@@Z"(ptr, ptr) #4 align 2
 
 attributes #0 = { nounwind memory(none) }
 attributes #1 = { nobuiltin allocsize(0) "target-cpu"="x86-64" "target-features"="+cmov,+crc32,+cx8,+fxsr,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87" "tune-cpu"="generic" }
@@ -60,10 +60,7 @@ attributes #4 = { uwtable "target-cpu"="x86-64" "target-features"="+cmov,+crc32,
 attributes #5 = { builtin allocsize(0) }
 attributes #6 = { builtin nounwind }
 
-!llvm.module.flags = !{!1, !2, !3, !4, !5}
+!llvm.module.flags = !{!1, !2}
 
-!1 = !{i32 1, !"wchar_size", i32 2}
-!2 = !{i32 2, !"eh-asynch", i32 1}
-!3 = !{i32 8, !"PIC Level", i32 2}
-!4 = !{i32 7, !"uwtable", i32 2}
-!5 = !{i32 1, !"MaxTLSAlign", i32 65536}
+!1 = !{i32 2, !"eh-asynch", i32 1}
+!2 = !{i32 7, !"uwtable", i32 2}

>From 74f5abb5d6718ed3557dd84b038e1ba28fb10954 Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Thu, 4 Jan 2024 17:25:03 +0800
Subject: [PATCH 4/5] [SEH] Fix register liveness verification for EHa

For sync EH, exception can only happen on last call of machine basic
block. For async EH, exception can happen on any place within seh scope.
For register lives into landing pad, it should live before entering seh
scope. However, we don't know the scope range in machine IR. Therefore
don't check its liveness under EHa.
---
 llvm/lib/CodeGen/MachineVerifier.cpp                  | 11 ++++++++---
 .../CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll   |  1 -
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index a015d9bbd2d3f5..08f5ddfccc4f60 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -48,6 +48,7 @@
 #include "llvm/CodeGen/MachineMemOperand.h"
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/PseudoSourceValue.h"
 #include "llvm/CodeGen/RegisterBank.h"
 #include "llvm/CodeGen/RegisterBankInfo.h"
@@ -3347,10 +3348,13 @@ void MachineVerifier::verifyLiveRangeSegment(const LiveRange &LR,
     OwnerLI.computeSubRangeUndefs(Undefs, LaneMask, *MRI, *Indexes);
   }
 
+  bool IsEHa = MF->getMMI().getModule()->getModuleFlag("eh-asynch");
   while (true) {
     assert(LiveInts->isLiveInToMBB(LR, &*MFI));
-    // We don't know how to track physregs into a landing pad.
-    if (!Reg.isVirtual() && MFI->isEHPad()) {
+    // TODO: we don't know how to track physregs into a landing pad. For async
+    // EH, the virtual reg lives before scope begin, but we don't know seh scope
+    // range of landing pad in Machine IR. Therefore don't check its liveness.
+    if (MFI->isEHPad() && (!Reg.isVirtual() || IsEHa)) {
       if (&*MFI == EndMBB)
         break;
       ++MFI;
@@ -3364,8 +3368,9 @@ void MachineVerifier::verifyLiveRangeSegment(const LiveRange &LR,
     // Check that VNI is live-out of all predecessors.
     for (const MachineBasicBlock *Pred : MFI->predecessors()) {
       SlotIndex PEnd = LiveInts->getMBBEndIdx(Pred);
-      // Predecessor of landing pad live-out on last call.
+      // Predecessor of landing pad live-out on last call for sync EH.
       if (MFI->isEHPad()) {
+        assert(!IsEHa && "EHa may raise exception on non call");
         for (const MachineInstr &MI : llvm::reverse(*Pred)) {
           if (MI.isCall()) {
             PEnd = Indexes->getInstructionIndex(MI).getBoundaryIndex();
diff --git a/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll b/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
index ff07f4ddf00546..20f91702c08849 100644
--- a/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
+++ b/llvm/test/CodeGen/X86/windows-seh-EHa-RegisterLiveness.ll
@@ -1,4 +1,3 @@
-; XFAIL: *
 ; RUN: llc --verify-machineinstrs < %s
 source_filename = "test.cpp"
 target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"

>From d6786e410b6821d5bd43f900f6f6a31e9f20e7ad Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Fri, 5 Jan 2024 12:23:17 +0800
Subject: [PATCH 5/5] Only do not check seh handler

---
 llvm/lib/CodeGen/MachineVerifier.cpp | 28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 08f5ddfccc4f60..e259f89df35037 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -46,9 +46,9 @@
 #include "llvm/CodeGen/MachineInstr.h"
 #include "llvm/CodeGen/MachineInstrBundle.h"
 #include "llvm/CodeGen/MachineMemOperand.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
-#include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/PseudoSourceValue.h"
 #include "llvm/CodeGen/RegisterBank.h"
 #include "llvm/CodeGen/RegisterBankInfo.h"
@@ -3351,10 +3351,34 @@ void MachineVerifier::verifyLiveRangeSegment(const LiveRange &LR,
   bool IsEHa = MF->getMMI().getModule()->getModuleFlag("eh-asynch");
   while (true) {
     assert(LiveInts->isLiveInToMBB(LR, &*MFI));
+    auto IsSEHHandler = [](const MachineBasicBlock &Handler) -> bool {
+      if (!Handler.isMachineBlockAddressTaken())
+        return false;
+
+      for (const User *U : Handler.getBasicBlock()->users()) {
+        if (!isa<InvokeInst>(U))
+          continue;
+        const Function *Fn = cast<CallBase>(U)->getCalledFunction();
+        if (!Fn || !Fn->isIntrinsic())
+          continue;
+
+        switch (Fn->getIntrinsicID()) {
+        default:
+          continue;
+        case Intrinsic::seh_scope_begin:
+        case Intrinsic::seh_scope_end:
+        case Intrinsic::seh_try_begin:
+        case Intrinsic::seh_try_end:
+          return true;
+        }
+      }
+      return false;
+    };
+
     // TODO: we don't know how to track physregs into a landing pad. For async
     // EH, the virtual reg lives before scope begin, but we don't know seh scope
     // range of landing pad in Machine IR. Therefore don't check its liveness.
-    if (MFI->isEHPad() && (!Reg.isVirtual() || IsEHa)) {
+    if (MFI->isEHPad() && (!Reg.isVirtual() || (IsEHa && IsSEHHandler(*MFI)))) {
       if (&*MFI == EndMBB)
         break;
       ++MFI;



More information about the libcxx-commits mailing list