Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions llvm/lib/Target/X86/X86RegisterInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not familar with SjLj. Is the following the same rule on both Linux and Windows?

Copy link
Contributor Author

@s-barannikov s-barannikov Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! I never thought about supporting SJLJ on Windows, but I think it should be the same there.
Unlike DWARF unwinder, SJLJ implementation does not actually unwind the stack (does not step through frames), it just does __builtin_longjmp to the dispatch block. The builtin only restores program counter and stack pointer(s), as that is all that is saved at the beginning of each function that can potentially throw an exception. (Saving all registers at the beginning of each function would be too expensive.)

Also, there is a related workaround that adds the same "no-preserved" mask to an arbitrary instruction in the dispatch block. (It should be removed and any remaining issues fixed in the generic code, but I'm hesitant to do this as test SJLJ test coverage is pretty poor.)


Apparently, the link doesn't work, look for

  // Add a register mask with no preserved registers.  This results in all
  // registers being marked as clobbered.

in X86ISelLowering.cpp

return getNoPreservedMask();
return TargetRegisterInfo::getCustomEHPadPreservedMask(MF);
}

const uint32_t *X86RegisterInfo::getDarwinTLSCallPreservedMask() const {
return CSR_64_TLS_Darwin_RegMask;
}
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/X86/X86RegisterInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
76 changes: 76 additions & 0 deletions llvm/test/CodeGen/X86/sjlj-unwind-clobber.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
; 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@PLT
; 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: 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: 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), %eax
; CHECK-NEXT: movl -120(%rbp), %eax
; CHECK-NEXT: jmp .LBB0_1
; CHECK-NEXT: .LBB0_2: # %while.end
; CHECK-NEXT: leaq -136(%rbp), %rdi
; CHECK-NEXT: callq _Unwind_SjLj_Unregister@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
}