[llvm] f284ae2 - [RISCV] Ignore debug instructions in epilogue block checks (#219401)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 11 00:25:43 PDT 2026
Author: Yongqiang Tian
Date: 2026-09-11T15:25:37+08:00
New Revision: f284ae2ed3f9b74b213633b0e5cab52b490728f2
URL: https://github.com/llvm/llvm-project/commit/f284ae2ed3f9b74b213633b0e5cab52b490728f2
DIFF: https://github.com/llvm/llvm-project/commit/f284ae2ed3f9b74b213633b0e5cab52b490728f2.diff
LOG: [RISCV] Ignore debug instructions in epilogue block checks (#219401)
A debug instruction in a return-only block can make
`RISCVFrameLowering::canUseAsEpilogue` reject an otherwise valid
shrink-wrap
point. This moves the save and restore libcalls onto the fast path.
Ignore debug instructions in the successor cardinality check while
continuing
to count bundle interiors, pseudo probes, and other non-debug
instructions.
The first commit records the existing behavior. The second applies the
fix and
updates the test expectations to require localized save and restore
calls.
Fixes #219378
Testing:
- Verified that the regression test fails on an affected compiler.
- Verified the targeted test after the fix.
- RISC-V CodeGen: 2654/2654 passed.
Assisted-by: OpenAI Codex 5.6 Sol, co-reviewed by Claude and Yongqiang
Tian.
Added:
llvm/test/CodeGen/RISCV/shrinkwrap-save-restore-debug.ll
Modified:
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index b50a18b539555..4732b4153aae6 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -2713,9 +2713,13 @@ bool RISCVFrameLowering::canUseAsEpilogue(const MachineBasicBlock &MBB) const {
if (!SuccMBB)
return true;
- // The successor can only contain a return, since we would effectively be
- // replacing the successor with our own tail return at the end of our block.
- return SuccMBB->isReturnBlock() && SuccMBB->size() == 1;
+ // The successor can only contain a return and debug instructions, since we
+ // would effectively replace it with our own tail return at the end of this
+ // block. The debug instructions would not execute on the tail-return path.
+ return SuccMBB->isReturnBlock() &&
+ llvm::count_if(SuccMBB->instrs(), [](const MachineInstr &MI) {
+ return !MI.isDebugInstr();
+ }) == 1;
}
bool RISCVFrameLowering::isSupportedStackID(TargetStackID::Value ID) const {
diff --git a/llvm/test/CodeGen/RISCV/shrinkwrap-save-restore-debug.ll b/llvm/test/CodeGen/RISCV/shrinkwrap-save-restore-debug.ll
new file mode 100644
index 0000000000000..77ab591e7e4bb
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/shrinkwrap-save-restore-debug.ll
@@ -0,0 +1,93 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -mattr=+save-restore -verify-machineinstrs < %s | FileCheck %s
+;
+; Compare save/restore shrink wrapping with and without a debug instruction in
+; the common return block.
+
+declare void @notdead(ptr)
+
+define void @without_debug(i32 %n) nounwind {
+; CHECK-LABEL: without_debug:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: li a1, 32
+; CHECK-NEXT: bltu a1, a0, .LBB0_2
+; CHECK-NEXT: # %bb.1: # %if.then
+; CHECK-NEXT: call t0, __riscv_save_2
+; CHECK-NEXT: addi s0, sp, 16
+; CHECK-NEXT: mv s1, sp
+; CHECK-NEXT: addi a0, a0, 15
+; CHECK-NEXT: andi a0, a0, -16
+; CHECK-NEXT: sub a0, sp, a0
+; CHECK-NEXT: mv sp, a0
+; CHECK-NEXT: call notdead
+; CHECK-NEXT: mv sp, s1
+; CHECK-NEXT: addi sp, s0, -16
+; CHECK-NEXT: tail __riscv_restore_2
+; CHECK-NEXT: .LBB0_2: # %if.end
+; CHECK-NEXT: ret
+entry:
+ %cmp = icmp ult i32 %n, 33
+ br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+ %size = zext i32 %n to i64
+ %stack = call ptr @llvm.stacksave.p0()
+ %buffer = alloca i8, i64 %size, align 16
+ call void @notdead(ptr %buffer)
+ call void @llvm.stackrestore.p0(ptr %stack)
+ br label %if.end
+
+if.end:
+ ret void
+}
+
+define void @with_debug(i32 %n) nounwind !dbg !4 {
+; CHECK-LABEL: with_debug:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: li a1, 32
+; CHECK-NEXT: bltu a1, a0, .LBB1_2
+; CHECK-NEXT: # %bb.1: # %if.then
+; CHECK-NEXT: call t0, __riscv_save_2
+; CHECK-NEXT: addi s0, sp, 16
+; CHECK-NEXT: mv s1, sp
+; CHECK-NEXT: addi a0, a0, 15
+; CHECK-NEXT: andi a0, a0, -16
+; CHECK-NEXT: sub a0, sp, a0
+; CHECK-NEXT: mv sp, a0
+; CHECK-NEXT: call notdead
+; CHECK-NEXT: mv sp, s1
+; CHECK-NEXT: addi sp, s0, -16
+; CHECK-NEXT: tail __riscv_restore_2
+; CHECK-NEXT: .LBB1_2: # %if.end
+; CHECK-NEXT: #DEBUG_VALUE: with_debug:ghost <- 0
+; CHECK-NEXT: ret
+entry:
+ %cmp = icmp ult i32 %n, 33
+ br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+ %size = zext i32 %n to i64
+ %stack = call ptr @llvm.stacksave.p0()
+ %buffer = alloca i8, i64 %size, align 16
+ call void @notdead(ptr %buffer)
+ call void @llvm.stackrestore.p0(ptr %stack)
+ br label %if.end
+
+if.end:
+ #dbg_value(i32 0, !8, !DIExpression(), !9)
+ ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3}
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: true, emissionKind: NoDebug)
+!1 = !DIFile(filename: "x.c", directory: "")
+!2 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = distinct !DISubprogram(name: "with_debug", scope: !1, file: !1, line: 1, type: !5, unit: !0, retainedNodes: !6)
+!5 = !DISubroutineType(types: !11)
+!6 = !{!8}
+!8 = !DILocalVariable(name: "ghost", scope: !4, file: !1, line: 2, type: !2)
+!9 = !DILocation(line: 0, scope: !4)
+!10 = !DILocation(line: 3, scope: !4)
+!11 = !{null, !2}
More information about the llvm-commits
mailing list