[llvm] [ShrinkWrap] Modify shrink wrapping to accommodate functions terminated by no-return blocks (PR #167548)

Nathan Corbyn via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 25 09:13:44 PST 2025


https://github.com/cofibrant updated https://github.com/llvm/llvm-project/pull/167548

>From 3dcc75ddeedd60e4f306aa85a75bf20881a1651f Mon Sep 17 00:00:00 2001
From: Nathan Corbyn <n_corbyn at apple.com>
Date: Tue, 11 Nov 2025 17:07:57 +0000
Subject: [PATCH 1/3] [AArch64] [ShrinkWrap] Add test identifying missed shrink
 wrapping opportunity

---
 .../CodeGen/AArch64/shrinkwrap-no-return.ll   | 77 +++++++++++++++++++
 1 file changed, 77 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll

diff --git a/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll b/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll
new file mode 100644
index 0000000000000..7263de8ecdc70
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll
@@ -0,0 +1,77 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=aarch64 -o - %s | FileCheck %s
+
+ at exception = external hidden constant { ptr, ptr, ptr }
+
+define noundef i32 @early_exit_or_throw(i32 %in) personality ptr @__gxx_personality_v0 {
+; CHECK-LABEL: early_exit_or_throw:
+; CHECK:       .Lfunc_begin0:
+; CHECK-NEXT:    .cfi_startproc
+; CHECK-NEXT:    .cfi_personality 156, DW.ref.__gxx_personality_v0
+; CHECK-NEXT:    .cfi_lsda 28, .Lexception0
+; CHECK-NEXT:  // %bb.0: // %entry
+; CHECK-NEXT:    str x30, [sp, #-32]! // 8-byte Folded Spill
+; CHECK-NEXT:    stp x20, x19, [sp, #16] // 16-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    .cfi_offset w19, -8
+; CHECK-NEXT:    .cfi_offset w20, -16
+; CHECK-NEXT:    .cfi_offset w30, -32
+; CHECK-NEXT:    cmp w0, #1
+; CHECK-NEXT:    b.eq .LBB1_2
+; CHECK-NEXT:  // %bb.1: // %exit
+; CHECK-NEXT:    ldp x20, x19, [sp, #16] // 16-byte Folded Reload
+; CHECK-NEXT:    mov w0, wzr
+; CHECK-NEXT:    ldr x30, [sp], #32 // 8-byte Folded Reload
+; CHECK-NEXT:    ret
+; CHECK-NEXT:  .LBB1_2: // %setup
+; CHECK-NEXT:    mov w0, #32 // =0x20
+; CHECK-NEXT:    bl __cxa_allocate_exception
+; CHECK-NEXT:    mov x19, x0
+; CHECK-NEXT:  .Ltmp0: // EH_LABEL
+; CHECK-NEXT:    bl construct_exception
+; CHECK-NEXT:  .Ltmp1: // EH_LABEL
+; CHECK-NEXT:  // %bb.3: // %throw
+; CHECK-NEXT:    adrp x2, :got:destruct_exception
+; CHECK-NEXT:    adrp x1, exception
+; CHECK-NEXT:    add x1, x1, :lo12:exception
+; CHECK-NEXT:    ldr x2, [x2, :got_lo12:destruct_exception]
+; CHECK-NEXT:    mov x0, x19
+; CHECK-NEXT:    bl __cxa_throw
+; CHECK-NEXT:  .LBB1_4: // %teardown
+; CHECK-NEXT:  .Ltmp2: // EH_LABEL
+; CHECK-NEXT:    mov x20, x0
+; CHECK-NEXT:    mov x0, x19
+; CHECK-NEXT:    bl __cxa_free_exception
+; CHECK-NEXT:    mov x0, x20
+; CHECK-NEXT:    bl _Unwind_Resume
+entry:
+  %cmp = icmp eq i32 %in, 1
+  br i1 %cmp, label %setup, label %exit
+
+setup:
+  %exception = tail call ptr @__cxa_allocate_exception(i64 32) nounwind
+  %call = invoke noundef ptr @construct_exception(ptr noundef nonnull %exception) to label %throw unwind label %teardown
+
+throw:
+  tail call void @__cxa_throw(ptr nonnull %exception, ptr nonnull @exception, ptr nonnull @destruct_exception) noreturn
+  unreachable
+
+teardown:
+  %caught = landingpad { ptr, i32 } cleanup
+  tail call void @__cxa_free_exception(ptr nonnull %exception) nounwind
+  resume { ptr, i32 } %caught
+
+exit:
+  ret i32 0
+}
+
+declare i32 @__gxx_personality_v0(...)
+
+declare ptr @__cxa_allocate_exception(i64) local_unnamed_addr
+declare void @__cxa_free_exception(ptr) local_unnamed_addr
+declare void @__cxa_throw(ptr, ptr, ptr) local_unnamed_addr cold noreturn
+
+declare noundef ptr @construct_exception(ptr noundef nonnull returned) unnamed_addr
+declare noundef ptr @destruct_exception(ptr noundef nonnull returned) unnamed_addr mustprogress nounwind ssp uwtable(sync)
+
+declare void @noreturn() noreturn

>From 789cfcf84e8e52e33aa15ba530e2c402bd2d6b1c Mon Sep 17 00:00:00 2001
From: Nathan Corbyn <n_corbyn at apple.com>
Date: Tue, 11 Nov 2025 14:12:37 +0000
Subject: [PATCH 2/3] [ShrinkWrap] Ignore no-return blocks in shrink wrapping

---
 llvm/include/llvm/CodeGen/MachineBasicBlock.h |  7 +++++++
 llvm/lib/CodeGen/ShrinkWrap.cpp               | 12 +++++-------
 .../CodeGen/AArch64/shrinkwrap-no-return.ll   | 19 +++++++++----------
 3 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/MachineBasicBlock.h b/llvm/include/llvm/CodeGen/MachineBasicBlock.h
index fcf7bab09fcff..a56cf56c81cd9 100644
--- a/llvm/include/llvm/CodeGen/MachineBasicBlock.h
+++ b/llvm/include/llvm/CodeGen/MachineBasicBlock.h
@@ -988,6 +988,13 @@ class MachineBasicBlock
     return !empty() && back().isEHScopeReturn();
   }
 
+  /// Convenience function that returns true if the block exits the function
+  /// without returning.
+  bool isNoReturnBlock() const {
+    return !empty() && succ_empty() && !back().isReturn() &&
+           !back().isIndirectBranch();
+  }
+
   /// Split a basic block into 2 pieces at \p SplitPoint. A new block will be
   /// inserted after this block, and all instructions after \p SplitInst moved
   /// to it (\p SplitInst will be in the original block). If \p LIS is provided,
diff --git a/llvm/lib/CodeGen/ShrinkWrap.cpp b/llvm/lib/CodeGen/ShrinkWrap.cpp
index 83581052560cb..c2221cacc5bd5 100644
--- a/llvm/lib/CodeGen/ShrinkWrap.cpp
+++ b/llvm/lib/CodeGen/ShrinkWrap.cpp
@@ -697,14 +697,12 @@ void ShrinkWrapImpl::updateSaveRestorePoints(MachineBasicBlock &MBB,
 
   if (!Restore)
     Restore = &MBB;
-  else if (MPDT->getNode(&MBB)) // If the block is not in the post dom tree, it
-                                // means the block never returns. If that's the
-                                // case, we don't want to call
-                                // `findNearestCommonDominator`, which will
-                                // return `Restore`.
+  else if (MBB.isNoReturnBlock()) {
+    // MBB exits the function without returning, so we don't need an epilogue
+    // here. This is common for things like cleanup landing pads etc. In these
+    // cases, we can skip updating `Restore`.
+  } else
     Restore = MPDT->findNearestCommonDominator(Restore, &MBB);
-  else
-    Restore = nullptr; // Abort, we can't find a restore point in this case.
 
   // Make sure we would be able to insert the restore code before the
   // terminator.
diff --git a/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll b/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll
index 7263de8ecdc70..55e8b539e0a35 100644
--- a/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll
+++ b/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll
@@ -10,26 +10,25 @@ define noundef i32 @early_exit_or_throw(i32 %in) personality ptr @__gxx_personal
 ; CHECK-NEXT:    .cfi_personality 156, DW.ref.__gxx_personality_v0
 ; CHECK-NEXT:    .cfi_lsda 28, .Lexception0
 ; CHECK-NEXT:  // %bb.0: // %entry
+; CHECK-NEXT:    cmp w0, #1
+; CHECK-NEXT:    b.eq .LBB0_2
+; CHECK-NEXT:  // %bb.1: // %exit
+; CHECK-NEXT:    mov w0, wzr
+; CHECK-NEXT:    ret
+; CHECK-NEXT:  .LBB0_2: // %setup
 ; CHECK-NEXT:    str x30, [sp, #-32]! // 8-byte Folded Spill
 ; CHECK-NEXT:    stp x20, x19, [sp, #16] // 16-byte Folded Spill
 ; CHECK-NEXT:    .cfi_def_cfa_offset 32
 ; CHECK-NEXT:    .cfi_offset w19, -8
 ; CHECK-NEXT:    .cfi_offset w20, -16
 ; CHECK-NEXT:    .cfi_offset w30, -32
-; CHECK-NEXT:    cmp w0, #1
-; CHECK-NEXT:    b.eq .LBB1_2
-; CHECK-NEXT:  // %bb.1: // %exit
-; CHECK-NEXT:    ldp x20, x19, [sp, #16] // 16-byte Folded Reload
-; CHECK-NEXT:    mov w0, wzr
-; CHECK-NEXT:    ldr x30, [sp], #32 // 8-byte Folded Reload
-; CHECK-NEXT:    ret
-; CHECK-NEXT:  .LBB1_2: // %setup
 ; CHECK-NEXT:    mov w0, #32 // =0x20
 ; CHECK-NEXT:    bl __cxa_allocate_exception
-; CHECK-NEXT:    mov x19, x0
 ; CHECK-NEXT:  .Ltmp0: // EH_LABEL
 ; CHECK-NEXT:    bl construct_exception
 ; CHECK-NEXT:  .Ltmp1: // EH_LABEL
+; CHECK-NEXT:    ldp x20, x19, [sp, #16] // 16-byte Folded Reload
+; CHECK-NEXT:    ldr x30, [sp], #32 // 8-byte Folded Reload
 ; CHECK-NEXT:  // %bb.3: // %throw
 ; CHECK-NEXT:    adrp x2, :got:destruct_exception
 ; CHECK-NEXT:    adrp x1, exception
@@ -37,7 +36,7 @@ define noundef i32 @early_exit_or_throw(i32 %in) personality ptr @__gxx_personal
 ; CHECK-NEXT:    ldr x2, [x2, :got_lo12:destruct_exception]
 ; CHECK-NEXT:    mov x0, x19
 ; CHECK-NEXT:    bl __cxa_throw
-; CHECK-NEXT:  .LBB1_4: // %teardown
+; CHECK-NEXT:  .LBB0_4: // %teardown
 ; CHECK-NEXT:  .Ltmp2: // EH_LABEL
 ; CHECK-NEXT:    mov x20, x0
 ; CHECK-NEXT:    mov x0, x19

>From 8d8b30869a714a100018f7967293add648b8effc Mon Sep 17 00:00:00 2001
From: Nathan Corbyn <n_corbyn at apple.com>
Date: Tue, 25 Nov 2025 17:13:29 +0000
Subject: [PATCH 3/3] Update test

---
 .../CodeGen/AArch64/shrinkwrap-no-return.ll   | 98 ++++++++++++-------
 1 file changed, 64 insertions(+), 34 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll b/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll
index 55e8b539e0a35..8acb653e9ee1d 100644
--- a/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll
+++ b/llvm/test/CodeGen/AArch64/shrinkwrap-no-return.ll
@@ -1,48 +1,80 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
-; RUN: llc -mtriple=aarch64 -o - %s | FileCheck %s
+; RUN: llc -mtriple=arm64-apple-ios -o - %s | FileCheck %s
 
 @exception = external hidden constant { ptr, ptr, ptr }
 
-define noundef i32 @early_exit_or_throw(i32 %in) personality ptr @__gxx_personality_v0 {
-; CHECK-LABEL: early_exit_or_throw:
-; CHECK:       .Lfunc_begin0:
+define noundef i32 @call_with_no_ret(i32 %in) personality ptr @__gxx_personality_v0 {
+; CHECK-LABEL: call_with_no_ret:
+; CHECK:       Lfunc_begin0:
 ; CHECK-NEXT:    .cfi_startproc
-; CHECK-NEXT:    .cfi_personality 156, DW.ref.__gxx_personality_v0
-; CHECK-NEXT:    .cfi_lsda 28, .Lexception0
-; CHECK-NEXT:  // %bb.0: // %entry
+; CHECK-NEXT:    .cfi_personality 155, ___gxx_personality_v0
+; CHECK-NEXT:    .cfi_lsda 16, Lexception0
+; CHECK-NEXT:  ; %bb.0: ; %entry
 ; CHECK-NEXT:    cmp w0, #1
-; CHECK-NEXT:    b.eq .LBB0_2
-; CHECK-NEXT:  // %bb.1: // %exit
+; CHECK-NEXT:    b.eq LBB0_2
+; CHECK-NEXT:  ; %bb.1: ; %exit
 ; CHECK-NEXT:    mov w0, wzr
 ; CHECK-NEXT:    ret
-; CHECK-NEXT:  .LBB0_2: // %setup
-; CHECK-NEXT:    str x30, [sp, #-32]! // 8-byte Folded Spill
-; CHECK-NEXT:    stp x20, x19, [sp, #16] // 16-byte Folded Spill
+; CHECK-NEXT:  LBB0_2: ; %setup
+; CHECK-NEXT:    stp x20, x19, [sp, #-32]! ; 16-byte Folded Spill
+; CHECK-NEXT:    stp x29, x30, [sp, #16] ; 16-byte Folded Spill
 ; CHECK-NEXT:    .cfi_def_cfa_offset 32
-; CHECK-NEXT:    .cfi_offset w19, -8
-; CHECK-NEXT:    .cfi_offset w20, -16
-; CHECK-NEXT:    .cfi_offset w30, -32
-; CHECK-NEXT:    mov w0, #32 // =0x20
-; CHECK-NEXT:    bl __cxa_allocate_exception
-; CHECK-NEXT:  .Ltmp0: // EH_LABEL
-; CHECK-NEXT:    bl construct_exception
-; CHECK-NEXT:  .Ltmp1: // EH_LABEL
-; CHECK-NEXT:    ldp x20, x19, [sp, #16] // 16-byte Folded Reload
-; CHECK-NEXT:    ldr x30, [sp], #32 // 8-byte Folded Reload
-; CHECK-NEXT:  // %bb.3: // %throw
-; CHECK-NEXT:    adrp x2, :got:destruct_exception
-; CHECK-NEXT:    adrp x1, exception
-; CHECK-NEXT:    add x1, x1, :lo12:exception
-; CHECK-NEXT:    ldr x2, [x2, :got_lo12:destruct_exception]
+; CHECK-NEXT:    .cfi_offset w30, -8
+; CHECK-NEXT:    .cfi_offset w29, -16
+; CHECK-NEXT:    .cfi_offset w19, -24
+; CHECK-NEXT:    .cfi_offset w20, -32
+; CHECK-NEXT:    mov w0, #32 ; =0x20
+; CHECK-NEXT:    bl ___cxa_allocate_exception
+; CHECK-NEXT:  Ltmp0: ; EH_LABEL
+; CHECK-NEXT:    bl _construct_exception
+; CHECK-NEXT:  Ltmp1: ; EH_LABEL
+; CHECK-NEXT:    ldp x29, x30, [sp, #16] ; 16-byte Folded Reload
+; CHECK-NEXT:    ldp x20, x19, [sp], #32 ; 16-byte Folded Reload
+; CHECK-NEXT:  ; %bb.3: ; %throw
+; CHECK-NEXT:  Lloh0:
+; CHECK-NEXT:    adrp x2, _destruct_exception at GOTPAGE
+; CHECK-NEXT:  Lloh1:
+; CHECK-NEXT:    adrp x1, _exception at PAGE
+; CHECK-NEXT:  Lloh2:
+; CHECK-NEXT:    add x1, x1, _exception at PAGEOFF
+; CHECK-NEXT:  Lloh3:
+; CHECK-NEXT:    ldr x2, [x2, _destruct_exception at GOTPAGEOFF]
 ; CHECK-NEXT:    mov x0, x19
-; CHECK-NEXT:    bl __cxa_throw
-; CHECK-NEXT:  .LBB0_4: // %teardown
-; CHECK-NEXT:  .Ltmp2: // EH_LABEL
+; CHECK-NEXT:    bl ___cxa_throw
+; CHECK-NEXT:  LBB0_4: ; %teardown
+; CHECK-NEXT:  Ltmp2: ; EH_LABEL
 ; CHECK-NEXT:    mov x20, x0
 ; CHECK-NEXT:    mov x0, x19
-; CHECK-NEXT:    bl __cxa_free_exception
+; CHECK-NEXT:    bl ___cxa_free_exception
 ; CHECK-NEXT:    mov x0, x20
-; CHECK-NEXT:    bl _Unwind_Resume
+; CHECK-NEXT:    bl __Unwind_Resume
+; CHECK-NEXT:    .loh AdrpAdd Lloh1, Lloh2
+; CHECK-NEXT:    .loh AdrpLdrGot Lloh0, Lloh3
+; CHECK-NEXT:  Lfunc_end0:
+; CHECK-NEXT:    .cfi_endproc
+; CHECK-NEXT:    .section __TEXT,__gcc_except_tab
+; CHECK-NEXT:    .p2align 2, 0x0
+; CHECK-NEXT:  GCC_except_table0:
+; CHECK-NEXT:  Lexception0:
+; CHECK-NEXT:    .byte 255 ; @LPStart Encoding = omit
+; CHECK-NEXT:    .byte 255 ; @TType Encoding = omit
+; CHECK-NEXT:    .byte 1 ; Call site Encoding = uleb128
+; CHECK-NEXT:    .uleb128 Lcst_end0-Lcst_begin0
+; CHECK-NEXT:  Lcst_begin0:
+; CHECK-NEXT:    .uleb128 Lfunc_begin0-Lfunc_begin0 ; >> Call Site 1 <<
+; CHECK-NEXT:    .uleb128 Ltmp0-Lfunc_begin0 ; Call between Lfunc_begin0 and Ltmp0
+; CHECK-NEXT:    .byte 0 ; has no landing pad
+; CHECK-NEXT:    .byte 0 ; On action: cleanup
+; CHECK-NEXT:    .uleb128 Ltmp0-Lfunc_begin0 ; >> Call Site 2 <<
+; CHECK-NEXT:    .uleb128 Ltmp1-Ltmp0 ; Call between Ltmp0 and Ltmp1
+; CHECK-NEXT:    .uleb128 Ltmp2-Lfunc_begin0 ; jumps to Ltmp2
+; CHECK-NEXT:    .byte 0 ; On action: cleanup
+; CHECK-NEXT:    .uleb128 Ltmp1-Lfunc_begin0 ; >> Call Site 3 <<
+; CHECK-NEXT:    .uleb128 Lfunc_end0-Ltmp1 ; Call between Ltmp1 and Lfunc_end0
+; CHECK-NEXT:    .byte 0 ; has no landing pad
+; CHECK-NEXT:    .byte 0 ; On action: cleanup
+; CHECK-NEXT:  Lcst_end0:
+; CHECK-NEXT:    .p2align 2, 0x0
 entry:
   %cmp = icmp eq i32 %in, 1
   br i1 %cmp, label %setup, label %exit
@@ -72,5 +104,3 @@ declare void @__cxa_throw(ptr, ptr, ptr) local_unnamed_addr cold noreturn
 
 declare noundef ptr @construct_exception(ptr noundef nonnull returned) unnamed_addr
 declare noundef ptr @destruct_exception(ptr noundef nonnull returned) unnamed_addr mustprogress nounwind ssp uwtable(sync)
-
-declare void @noreturn() noreturn



More information about the llvm-commits mailing list