[llvm] [X86] Fix RegAlloc issue by implementing TRI::getCustomEHPadPreservedMask (PR #135518)

Sergei Barannikov via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 13 08:49:41 PDT 2025


https://github.com/s-barannikov updated https://github.com/llvm/llvm-project/pull/135518

>From 46745b042906b80948ec34ad603234e6f912fedb Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Sun, 13 Apr 2025 07:13:54 +0300
Subject: [PATCH 1/3] Add a test

---
 llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll | 74 ++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll

diff --git a/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll b/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll
new file mode 100644
index 0000000000000..3a4dc8f652eb4
--- /dev/null
+++ b/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll
@@ -0,0 +1,74 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=x86_64 -exception-model=sjlj -o - %s | FileCheck %s
+
+declare dso_local i32 @__gxx_personality_sj0(...)
+declare dso_local preserve_allcc void @foo(ptr)
+
+define void @test() personality ptr @__gxx_personality_sj0 {
+; CHECK-LABEL: test:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    pushq %rbp
+; CHECK-NEXT:    movq %rsp, %rbp
+; CHECK-NEXT:    pushq %r15
+; CHECK-NEXT:    pushq %r14
+; CHECK-NEXT:    pushq %r13
+; CHECK-NEXT:    pushq %r12
+; CHECK-NEXT:    pushq %rbx
+; CHECK-NEXT:    subq $104, %rsp
+; CHECK-NEXT:    movq $__gxx_personality_sj0, -104(%rbp)
+; CHECK-NEXT:    movq $GCC_except_table0, -96(%rbp)
+; CHECK-NEXT:    movq %rbp, -88(%rbp)
+; CHECK-NEXT:    movq %rsp, -72(%rbp)
+; CHECK-NEXT:    movq $.LBB0_3, -80(%rbp)
+; CHECK-NEXT:    leaq -136(%rbp), %rdi
+; CHECK-NEXT:    callq _Unwind_SjLj_Register at PLT
+; CHECK-NEXT:    leaq -44(%rbp), %rdi
+; CHECK-NEXT:    leaq .LJTI0_0(%rip), %rax
+; CHECK-NEXT:  .LBB0_1: # %while.cond
+; CHECK-NEXT:    # =>This Inner Loop Header: Depth=1
+; CHECK-NEXT:    movl $1, -128(%rbp)
+; CHECK-NEXT:  .Ltmp0:
+; CHECK-NEXT:    callq foo
+; CHECK-NEXT:  .Ltmp1:
+; CHECK-NEXT:    jmp .LBB0_2
+; CHECK-NEXT:  .LBB0_3: # in Loop: Header=BB0_1 Depth=1
+; CHECK-NEXT:    movl -128(%rbp), %ecx
+; CHECK-NEXT:    cmpl $1, %ecx
+; CHECK-NEXT:    jae .LBB0_5
+; CHECK-NEXT:  # %bb.4: # in Loop: Header=BB0_1 Depth=1
+; CHECK-NEXT:    jmpq *(%rax,%rcx,8)
+; CHECK-NEXT:  .LBB0_6: # %lpad
+; CHECK-NEXT:    # in Loop: Header=BB0_1 Depth=1
+; CHECK-NEXT:  .Ltmp2:
+; CHECK-NEXT:    movl -124(%rbp), %ecx
+; CHECK-NEXT:    movl -120(%rbp), %ecx
+; CHECK-NEXT:    jmp .LBB0_1
+; CHECK-NEXT:  .LBB0_2: # %while.end
+; CHECK-NEXT:    leaq -136(%rbp), %rdi
+; CHECK-NEXT:    callq _Unwind_SjLj_Unregister at PLT
+; CHECK-NEXT:    addq $104, %rsp
+; CHECK-NEXT:    popq %rbx
+; CHECK-NEXT:    popq %r12
+; CHECK-NEXT:    popq %r13
+; CHECK-NEXT:    popq %r14
+; CHECK-NEXT:    popq %r15
+; CHECK-NEXT:    popq %rbp
+; CHECK-NEXT:    retq
+; CHECK-NEXT:  .LBB0_5:
+; CHECK-NEXT:    ud2
+entry:
+  %ptr = alloca i32, align 4
+  br label %while.cond
+
+while.cond:
+  invoke preserve_allcc void @foo(ptr %ptr)
+          to label %while.end unwind label %lpad
+
+lpad:
+  %lp = landingpad { ptr, i32 }
+          catch ptr null
+  br label %while.cond
+
+while.end:
+  ret void
+}

>From b5f35ecfcd7c653f61ee67e2fc25a9273ae776a6 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Sun, 13 Apr 2025 07:18:26 +0300
Subject: [PATCH 2/3] [X86] Implement
 TargetRegisterInfo::getCustomEHPadPreservedMask

This method needs to be implemented so that the register allocator can
correctly split live ranges of loop invariant virtual registers.
This default return value is correct for DWARF unwinder, which preserves
all registers (except exception pointer/selector), but not for SjLj,
which only preserves sp/fp and possibly bp (they should be reserved).

In the added test `rcx` on the entry to the loop contains the argument
to `foo`. It survives the call due to `preserve_allcc`, but not the
unwind edge entering LBB0_3, and so must be recomputed before going to
the next iteration.
Similarly, `rdx` holds the address of a jump table and is recomputed on
every iteration rather than once before the loop.
---
 llvm/lib/Target/X86/X86RegisterInfo.cpp      |  7 +++++++
 llvm/lib/Target/X86/X86RegisterInfo.h        |  2 ++
 llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll | 16 +++++++++-------
 3 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp
index 1c4114f8cc9c6..68c227cb987de 100644
--- a/llvm/lib/Target/X86/X86RegisterInfo.cpp
+++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp
@@ -525,6 +525,13 @@ X86RegisterInfo::getNoPreservedMask() const {
   return CSR_NoRegs_RegMask;
 }
 
+const uint32_t *
+X86RegisterInfo::getCustomEHPadPreservedMask(const MachineFunction &MF) const {
+  if (MF.getTarget().Options.ExceptionModel == ExceptionHandling::SjLj)
+    return getNoPreservedMask();
+  return TargetRegisterInfo::getCustomEHPadPreservedMask(MF);
+}
+
 const uint32_t *X86RegisterInfo::getDarwinTLSCallPreservedMask() const {
   return CSR_64_TLS_Darwin_RegMask;
 }
diff --git a/llvm/lib/Target/X86/X86RegisterInfo.h b/llvm/lib/Target/X86/X86RegisterInfo.h
index 5b6ac3c5da019..d9491cd225827 100644
--- a/llvm/lib/Target/X86/X86RegisterInfo.h
+++ b/llvm/lib/Target/X86/X86RegisterInfo.h
@@ -102,6 +102,8 @@ class X86RegisterInfo final : public X86GenRegisterInfo {
   const uint32_t *getCallPreservedMask(const MachineFunction &MF,
                                        CallingConv::ID) const override;
   const uint32_t *getNoPreservedMask() const override;
+  const uint32_t *
+  getCustomEHPadPreservedMask(const MachineFunction &MF) const override;
 
   // Calls involved in thread-local variable lookup save more registers than
   // normal calls, so they need a different mask to represent this.
diff --git a/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll b/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll
index 3a4dc8f652eb4..c1d718a9f7031 100644
--- a/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll
+++ b/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll
@@ -22,26 +22,28 @@ define void @test() personality ptr @__gxx_personality_sj0 {
 ; CHECK-NEXT:    movq $.LBB0_3, -80(%rbp)
 ; CHECK-NEXT:    leaq -136(%rbp), %rdi
 ; CHECK-NEXT:    callq _Unwind_SjLj_Register at PLT
-; CHECK-NEXT:    leaq -44(%rbp), %rdi
-; CHECK-NEXT:    leaq .LJTI0_0(%rip), %rax
+; CHECK-NEXT:    leaq -44(%rbp), %rcx
 ; CHECK-NEXT:  .LBB0_1: # %while.cond
 ; CHECK-NEXT:    # =>This Inner Loop Header: Depth=1
 ; CHECK-NEXT:    movl $1, -128(%rbp)
 ; CHECK-NEXT:  .Ltmp0:
+; CHECK-NEXT:    movq %rcx, %rdi
 ; CHECK-NEXT:    callq foo
 ; CHECK-NEXT:  .Ltmp1:
 ; CHECK-NEXT:    jmp .LBB0_2
 ; CHECK-NEXT:  .LBB0_3: # in Loop: Header=BB0_1 Depth=1
-; CHECK-NEXT:    movl -128(%rbp), %ecx
-; CHECK-NEXT:    cmpl $1, %ecx
+; CHECK-NEXT:    leaq -44(%rbp), %rcx
+; CHECK-NEXT:    movl -128(%rbp), %eax
+; CHECK-NEXT:    cmpl $1, %eax
 ; CHECK-NEXT:    jae .LBB0_5
 ; CHECK-NEXT:  # %bb.4: # in Loop: Header=BB0_1 Depth=1
-; CHECK-NEXT:    jmpq *(%rax,%rcx,8)
+; CHECK-NEXT:    leaq .LJTI0_0(%rip), %rdx
+; CHECK-NEXT:    jmpq *(%rdx,%rax,8)
 ; CHECK-NEXT:  .LBB0_6: # %lpad
 ; CHECK-NEXT:    # in Loop: Header=BB0_1 Depth=1
 ; CHECK-NEXT:  .Ltmp2:
-; CHECK-NEXT:    movl -124(%rbp), %ecx
-; CHECK-NEXT:    movl -120(%rbp), %ecx
+; CHECK-NEXT:    movl -124(%rbp), %eax
+; CHECK-NEXT:    movl -120(%rbp), %eax
 ; CHECK-NEXT:    jmp .LBB0_1
 ; CHECK-NEXT:  .LBB0_2: # %while.end
 ; CHECK-NEXT:    leaq -136(%rbp), %rdi

>From c369ab5d627391903a8612e56fbf1825d85ca5a8 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Sun, 13 Apr 2025 18:48:58 +0300
Subject: [PATCH 3/3] Drop dso_local, comment the test

---
 llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll b/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll
index c1d718a9f7031..596e42fa8c6a2 100644
--- a/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll
+++ b/llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll
@@ -1,8 +1,17 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
 ; RUN: llc -mtriple=x86_64 -exception-model=sjlj -o - %s | FileCheck %s
 
-declare dso_local i32 @__gxx_personality_sj0(...)
-declare dso_local preserve_allcc void @foo(ptr)
+declare i32 @__gxx_personality_sj0(...)
+declare preserve_allcc void @foo(ptr)
+
+; Check that register allocator splits live ranges of loop invariant virtual
+; registers as they don't survive the unwind edge of an invoke.
+; The argument of `foo` (passed in %rdi) and the address of the jump table
+; (base register of `jmpq`) are loop invariant. They survive the call to `foo`,
+; but are clobbered by the unwinder when `foo` throws an exception, and so
+; their live ranges must be split around the unwind edge `callq foo` -> LBB0_3.
+; This is achieved by recalculating their values before each use rather than
+; once before the loop as was the case before register allocation.
 
 define void @test() personality ptr @__gxx_personality_sj0 {
 ; CHECK-LABEL: test:
@@ -15,7 +24,8 @@ define void @test() personality ptr @__gxx_personality_sj0 {
 ; CHECK-NEXT:    pushq %r12
 ; CHECK-NEXT:    pushq %rbx
 ; CHECK-NEXT:    subq $104, %rsp
-; CHECK-NEXT:    movq $__gxx_personality_sj0, -104(%rbp)
+; CHECK-NEXT:    movq __gxx_personality_sj0 at GOTPCREL(%rip), %rax
+; CHECK-NEXT:    movq %rax, -104(%rbp)
 ; CHECK-NEXT:    movq $GCC_except_table0, -96(%rbp)
 ; CHECK-NEXT:    movq %rbp, -88(%rbp)
 ; CHECK-NEXT:    movq %rsp, -72(%rbp)
@@ -28,7 +38,7 @@ define void @test() personality ptr @__gxx_personality_sj0 {
 ; CHECK-NEXT:    movl $1, -128(%rbp)
 ; CHECK-NEXT:  .Ltmp0:
 ; CHECK-NEXT:    movq %rcx, %rdi
-; CHECK-NEXT:    callq foo
+; CHECK-NEXT:    callq foo at PLT
 ; CHECK-NEXT:  .Ltmp1:
 ; CHECK-NEXT:    jmp .LBB0_2
 ; CHECK-NEXT:  .LBB0_3: # in Loop: Header=BB0_1 Depth=1



More information about the llvm-commits mailing list