[llvm-branch-commits] [llvm] release/23.x: [RISC-V] Fix assert after 255162a (#212791) (PR #226322)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Sep 24 15:55:29 PDT 2026


https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/226322

Backport 255162ae0ebe805df151c5c4b48e1a47a5dd74f0 f24da9d03ba5d25c523e050da2ca3c4060ca23d5

Requested by: @lenary

>From 44f8099e26679bcf617932e4c080c2029666c60b Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja.i.llvm at gmail.com>
Date: Tue, 28 Jul 2026 21:59:19 +0200
Subject: [PATCH 1/2] [RISC-V] Do not emit cm.popret[z] with zicfiss (#196267)

When emitting shadow call stack protection instructions, the push/pop
optimization needs to be turned off because an sspopchk before a
cm.popret[z] is guaranteed to fail in a non-leaf function. In addition,
the sspopchk must be emitted after a cm.pop so that the ra has the
correct value when the check is performed.

Fixes: https://github.com/llvm/llvm-project/issues/196261

Co-authored-by: Nemanja Ivanovic <nemanja at synopsys.com>
(cherry picked from commit 255162ae0ebe805df151c5c4b48e1a47a5dd74f0)
---
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp  |   4 +
 .../Target/RISCV/RISCVPushPopOptimizer.cpp    |   9 +
 llvm/test/CodeGen/RISCV/shadow-stack-zcmp.ll  |  57 +++
 llvm/test/CodeGen/RISCV/shadowcallstack.ll    | 330 ++++++++++++++++++
 4 files changed, 400 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/shadow-stack-zcmp.ll

diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index bbd1738cd51171..516cb557feaba6 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -12,6 +12,7 @@
 
 #include "RISCVFrameLowering.h"
 #include "MCTargetDesc/RISCVBaseInfo.h"
+#include "MCTargetDesc/RISCVMCTargetDesc.h"
 #include "RISCVMachineFunctionInfo.h"
 #include "RISCVSubtarget.h"
 #include "llvm/BinaryFormat/Dwarf.h"
@@ -193,6 +194,9 @@ static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB,
           CSI, [&](CalleeSavedInfo &CSR) { return CSR.getReg() == RAReg; }))
     return;
 
+  // The shadow call stack popchk needs to happen after cm.pop that loads ra.
+  if (MI->getOpcode() == RISCV::CM_POP || MI->getOpcode() == RISCV::QC_CM_POP)
+    ++MI;
   const RISCVInstrInfo *TII = STI.getInstrInfo();
   if (HasHWShadowStack) {
     BuildMI(MBB, MI, DL, TII->get(RISCV::SSPOPCHK))
diff --git a/llvm/lib/Target/RISCV/RISCVPushPopOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVPushPopOptimizer.cpp
index eae7e8697f0ad7..46acc5a6874ef8 100644
--- a/llvm/lib/Target/RISCV/RISCVPushPopOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVPushPopOptimizer.cpp
@@ -138,6 +138,15 @@ bool RISCVPushPopOpt::runOnMachineFunction(MachineFunction &Fn) {
   if (!Subtarget->hasStdExtZcmp() && !Subtarget->hasVendorXqccmp())
     return false;
 
+  // We don't want any popret[z] instructions when emitting code with shadow
+  // stack protection. Note that this pass would actually fail to insert any
+  // popret[z] instructions in this case since the cm.pop and ret will not be
+  // adjacent. But there's no point in running a pass that won't do anything.
+  if ((Fn.getFunction().hasFnAttribute("hw-shadow-stack") &&
+       Subtarget->hasStdExtZimop()) ||
+      Fn.getFunction().hasFnAttribute(Attribute::ShadowCallStack))
+    return false;
+
   TII = Subtarget->getInstrInfo();
   TRI = Subtarget->getRegisterInfo();
 
diff --git a/llvm/test/CodeGen/RISCV/shadow-stack-zcmp.ll b/llvm/test/CodeGen/RISCV/shadow-stack-zcmp.ll
new file mode 100644
index 00000000000000..77feb850369813
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/shadow-stack-zcmp.ll
@@ -0,0 +1,57 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s --check-prefix=RV32
+ at .str = private unnamed_addr constant [13 x i8] c"Val[%d]: %d\0A\00", align 1
+
+define i32 @printSomething(ptr %arr, i32 %len) #0 {
+; RV32-LABEL: printSomething:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    sspush ra
+; RV32-NEXT:    cm.push {ra, s0-s3}, -32
+; RV32-NEXT:    .cfi_def_cfa_offset 32
+; RV32-NEXT:    .cfi_offset ra, -20
+; RV32-NEXT:    .cfi_offset s0, -16
+; RV32-NEXT:    .cfi_offset s1, -12
+; RV32-NEXT:    .cfi_offset s2, -8
+; RV32-NEXT:    .cfi_offset s3, -4
+; RV32-NEXT:    mv s0, a1
+; RV32-NEXT:    blez a1, .LBB0_3
+; RV32-NEXT:  # %bb.1: # %for.body.preheader
+; RV32-NEXT:    mv s1, a0
+; RV32-NEXT:    li s2, 0
+; RV32-NEXT:    lui s3, %hi(.L.str)
+; RV32-NEXT:    addi s3, s3, %lo(.L.str)
+; RV32-NEXT:  .LBB0_2: # %for.body
+; RV32-NEXT:    # =>This Inner Loop Header: Depth=1
+; RV32-NEXT:    lw a2, 0(s1)
+; RV32-NEXT:    cm.mva01s s3, s2
+; RV32-NEXT:    call printf
+; RV32-NEXT:    addi s2, s2, 1
+; RV32-NEXT:    addi s1, s1, 4
+; RV32-NEXT:    bne s0, s2, .LBB0_2
+; RV32-NEXT:  .LBB0_3: # %for.cond.cleanup
+; RV32-NEXT:    mv a0, s0
+; RV32-NEXT:    cm.pop {ra, s0-s3}, 32
+; RV32-NEXT:    sspopchk ra
+; RV32-NEXT:    ret
+entry:
+  %cmp5 = icmp sgt i32 %len, 0
+  br i1 %cmp5, label %for.body, label %for.cond.cleanup
+
+for.cond.cleanup:                                 ; preds = %for.body, %entry
+  ret i32 %len
+
+for.body:                                         ; preds = %entry, %for.body
+  %i.06 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
+  %arrayidx = getelementptr inbounds nuw [4 x i8], ptr %arr, i32 %i.06
+  %0 = load i32, ptr %arrayidx, align 4
+  %call = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str, i32 noundef %i.06, i32 noundef %0)
+  %inc = add nuw nsw i32 %i.06, 1
+  %exitcond.not = icmp eq i32 %inc, %len
+  br i1 %exitcond.not, label %for.cond.cleanup, label %for.body
+}
+
+; Function Attrs: nofree nounwind
+declare dso_local noundef i32 @printf(ptr noundef readonly captures(none), ...) local_unnamed_addr #0
+
+attributes #0 = { "hw-shadow-stack" "target-features"="+experimental-zicfiss,+zcmop,+zcmp" }
diff --git a/llvm/test/CodeGen/RISCV/shadowcallstack.ll b/llvm/test/CodeGen/RISCV/shadowcallstack.ll
index e68331b86371b9..82a5e70e2977d1 100644
--- a/llvm/test/CodeGen/RISCV/shadowcallstack.ll
+++ b/llvm/test/CodeGen/RISCV/shadowcallstack.ll
@@ -3,6 +3,10 @@
 ; RUN:   | FileCheck %s --check-prefix=RV32
 ; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
 ; RUN:   | FileCheck %s --check-prefix=RV64
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
+; RUN:   -mattr=+zcmp,+experimental-zicfiss,+zcmop | FileCheck %s --check-prefix=RV64-ZCMP
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   -mattr=+zcmp,+experimental-zicfiss | FileCheck %s --check-prefix=RV32-ZCMP-NOZCMOP
 ; RUN: llc -mtriple=riscv32 -mattr=+experimental-zicfiss < %s \
 ; RUN:   -verify-machineinstrs | FileCheck %s --check-prefixes=RV32-ZICFISS,RV32-NOZCMOP
 ; RUN: llc -mtriple=riscv64 -mattr=+experimental-zicfiss < %s \
@@ -21,6 +25,14 @@ define void @f1() shadowcallstack {
 ; RV64:       # %bb.0:
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f1:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f1:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f1:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    ret
@@ -50,6 +62,14 @@ define void @f2() shadowcallstack {
 ; RV64:       # %bb.0:
 ; RV64-NEXT:    tail foo
 ;
+; RV64-ZCMP-LABEL: f2:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    tail foo
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f2:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    tail foo
+;
 ; RV32-ZICFISS-LABEL: f2:
 ; RV32-ZICFISS:       # %bb.0:
 ; RV32-ZICFISS-NEXT:    tail foo
@@ -102,6 +122,36 @@ define i32 @f3() shadowcallstack {
 ; RV64-NEXT:    .cfi_restore gp
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f3:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    addi gp, gp, 8
+; RV64-ZCMP-NEXT:    sd ra, -8(gp)
+; RV64-ZCMP-NEXT:    .cfi_escape 0x16, 0x03, 0x02, 0x73, 0x78 #
+; RV64-ZCMP-NEXT:    cm.push {ra}, -16
+; RV64-ZCMP-NEXT:    .cfi_def_cfa_offset 16
+; RV64-ZCMP-NEXT:    .cfi_offset ra, -8
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    cm.pop {ra}, 16
+; RV64-ZCMP-NEXT:    ld ra, -8(gp)
+; RV64-ZCMP-NEXT:    addi gp, gp, -8
+; RV64-ZCMP-NEXT:    .cfi_restore gp
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f3:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    addi gp, gp, 4
+; RV32-ZCMP-NOZCMOP-NEXT:    sw ra, -4(gp)
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_escape 0x16, 0x03, 0x02, 0x73, 0x7c #
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.push {ra}, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_def_cfa_offset 16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset ra, -4
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.pop {ra}, 16
+; RV32-ZCMP-NOZCMOP-NEXT:    lw ra, -4(gp)
+; RV32-ZCMP-NOZCMOP-NEXT:    addi gp, gp, -4
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_restore gp
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f3:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    addi gp, gp, 4
@@ -263,6 +313,60 @@ define i32 @f4() shadowcallstack {
 ; RV64-NEXT:    .cfi_restore gp
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f4:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    addi gp, gp, 8
+; RV64-ZCMP-NEXT:    sd ra, -8(gp)
+; RV64-ZCMP-NEXT:    .cfi_escape 0x16, 0x03, 0x02, 0x73, 0x78 #
+; RV64-ZCMP-NEXT:    cm.push {ra, s0-s2}, -32
+; RV64-ZCMP-NEXT:    .cfi_def_cfa_offset 32
+; RV64-ZCMP-NEXT:    .cfi_offset ra, -32
+; RV64-ZCMP-NEXT:    .cfi_offset s0, -24
+; RV64-ZCMP-NEXT:    .cfi_offset s1, -16
+; RV64-ZCMP-NEXT:    .cfi_offset s2, -8
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    mv s0, a0
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    mv s1, a0
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    mv s2, a0
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    add s0, s0, s1
+; RV64-ZCMP-NEXT:    add a0, a0, s2
+; RV64-ZCMP-NEXT:    addw a0, a0, s0
+; RV64-ZCMP-NEXT:    cm.pop {ra, s0-s2}, 32
+; RV64-ZCMP-NEXT:    ld ra, -8(gp)
+; RV64-ZCMP-NEXT:    addi gp, gp, -8
+; RV64-ZCMP-NEXT:    .cfi_restore gp
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f4:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    addi gp, gp, 4
+; RV32-ZCMP-NOZCMOP-NEXT:    sw ra, -4(gp)
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_escape 0x16, 0x03, 0x02, 0x73, 0x7c #
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.push {ra, s0-s2}, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_def_cfa_offset 16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset ra, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset s0, -12
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset s1, -8
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset s2, -4
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    mv s0, a0
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    mv s1, a0
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    mv s2, a0
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    add s0, s0, s1
+; RV32-ZCMP-NOZCMOP-NEXT:    add a0, a0, s2
+; RV32-ZCMP-NOZCMOP-NEXT:    add a0, a0, s0
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.pop {ra, s0-s2}, 16
+; RV32-ZCMP-NOZCMOP-NEXT:    lw ra, -4(gp)
+; RV32-ZCMP-NOZCMOP-NEXT:    addi gp, gp, -4
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_restore gp
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f4:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    addi gp, gp, 4
@@ -459,6 +563,28 @@ define i32 @f5() shadowcallstack nounwind {
 ; RV64-NEXT:    addi gp, gp, -8
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f5:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    addi gp, gp, 8
+; RV64-ZCMP-NEXT:    sd ra, -8(gp)
+; RV64-ZCMP-NEXT:    cm.push {ra}, -16
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    cm.pop {ra}, 16
+; RV64-ZCMP-NEXT:    ld ra, -8(gp)
+; RV64-ZCMP-NEXT:    addi gp, gp, -8
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f5:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    addi gp, gp, 4
+; RV32-ZCMP-NOZCMOP-NEXT:    sw ra, -4(gp)
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.push {ra}, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.pop {ra}, 16
+; RV32-ZCMP-NOZCMOP-NEXT:    lw ra, -4(gp)
+; RV32-ZCMP-NOZCMOP-NEXT:    addi gp, gp, -4
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f5:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    addi gp, gp, 4
@@ -524,6 +650,14 @@ define void @f1_hw() "hw-shadow-stack" {
 ; RV64:       # %bb.0:
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f1_hw:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f1_hw:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f1_hw:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    ret
@@ -551,6 +685,14 @@ define void @f2_hw() "hw-shadow-stack" {
 ; RV64:       # %bb.0:
 ; RV64-NEXT:    tail foo
 ;
+; RV64-ZCMP-LABEL: f2_hw:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    tail foo
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f2_hw:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    tail foo
+;
 ; RV32-ZICFISS-LABEL: f2_hw:
 ; RV32-ZICFISS:       # %bb.0:
 ; RV32-ZICFISS-NEXT:    tail foo
@@ -589,6 +731,28 @@ define i32 @f3_hw() "hw-shadow-stack" {
 ; RV64-NEXT:    .cfi_def_cfa_offset 0
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f3_hw:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    sspush ra
+; RV64-ZCMP-NEXT:    cm.push {ra}, -16
+; RV64-ZCMP-NEXT:    .cfi_def_cfa_offset 16
+; RV64-ZCMP-NEXT:    .cfi_offset ra, -8
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    cm.pop {ra}, 16
+; RV64-ZCMP-NEXT:    sspopchk ra
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f3_hw:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    sspush ra
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.push {ra}, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_def_cfa_offset 16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset ra, -4
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.pop {ra}, 16
+; RV32-ZCMP-NOZCMOP-NEXT:    sspopchk ra
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f3_hw:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    sspush ra
@@ -722,6 +886,52 @@ define i32 @f4_hw() "hw-shadow-stack" {
 ; RV64-NEXT:    .cfi_def_cfa_offset 0
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f4_hw:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    sspush ra
+; RV64-ZCMP-NEXT:    cm.push {ra, s0-s2}, -32
+; RV64-ZCMP-NEXT:    .cfi_def_cfa_offset 32
+; RV64-ZCMP-NEXT:    .cfi_offset ra, -32
+; RV64-ZCMP-NEXT:    .cfi_offset s0, -24
+; RV64-ZCMP-NEXT:    .cfi_offset s1, -16
+; RV64-ZCMP-NEXT:    .cfi_offset s2, -8
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    mv s0, a0
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    mv s1, a0
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    mv s2, a0
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    add s0, s0, s1
+; RV64-ZCMP-NEXT:    add a0, a0, s2
+; RV64-ZCMP-NEXT:    addw a0, a0, s0
+; RV64-ZCMP-NEXT:    cm.pop {ra, s0-s2}, 32
+; RV64-ZCMP-NEXT:    sspopchk ra
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f4_hw:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    sspush ra
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.push {ra, s0-s2}, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_def_cfa_offset 16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset ra, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset s0, -12
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset s1, -8
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset s2, -4
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    mv s0, a0
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    mv s1, a0
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    mv s2, a0
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    add s0, s0, s1
+; RV32-ZCMP-NOZCMOP-NEXT:    add a0, a0, s2
+; RV32-ZCMP-NOZCMOP-NEXT:    add a0, a0, s0
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.pop {ra, s0-s2}, 16
+; RV32-ZCMP-NOZCMOP-NEXT:    sspopchk ra
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f4_hw:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    sspush ra
@@ -894,6 +1104,24 @@ define i32 @f5_hw() "hw-shadow-stack" nounwind {
 ; RV64-NEXT:    addi sp, sp, 16
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f5_hw:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    sspush ra
+; RV64-ZCMP-NEXT:    cm.push {ra}, -16
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    cm.pop {ra}, 16
+; RV64-ZCMP-NEXT:    sspopchk ra
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f5_hw:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    sspush ra
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.push {ra}, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.pop {ra}, 16
+; RV32-ZCMP-NOZCMOP-NEXT:    sspopchk ra
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f5_hw:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    sspush ra
@@ -951,6 +1179,14 @@ define void @f1_both() "hw-shadow-stack" shadowcallstack {
 ; RV64:       # %bb.0:
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f1_both:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f1_both:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f1_both:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    ret
@@ -978,6 +1214,14 @@ define void @f2_both() "hw-shadow-stack" shadowcallstack {
 ; RV64:       # %bb.0:
 ; RV64-NEXT:    tail foo
 ;
+; RV64-ZCMP-LABEL: f2_both:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    tail foo
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f2_both:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    tail foo
+;
 ; RV32-ZICFISS-LABEL: f2_both:
 ; RV32-ZICFISS:       # %bb.0:
 ; RV32-ZICFISS-NEXT:    tail foo
@@ -1028,6 +1272,28 @@ define i32 @f3_both() "hw-shadow-stack" shadowcallstack {
 ; RV64-NEXT:    .cfi_restore gp
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f3_both:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    sspush ra
+; RV64-ZCMP-NEXT:    cm.push {ra}, -16
+; RV64-ZCMP-NEXT:    .cfi_def_cfa_offset 16
+; RV64-ZCMP-NEXT:    .cfi_offset ra, -8
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    cm.pop {ra}, 16
+; RV64-ZCMP-NEXT:    sspopchk ra
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f3_both:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    sspush ra
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.push {ra}, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_def_cfa_offset 16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset ra, -4
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.pop {ra}, 16
+; RV32-ZCMP-NOZCMOP-NEXT:    sspopchk ra
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f3_both:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    sspush ra
@@ -1173,6 +1439,52 @@ define i32 @f4_both() "hw-shadow-stack" shadowcallstack {
 ; RV64-NEXT:    .cfi_restore gp
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f4_both:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    sspush ra
+; RV64-ZCMP-NEXT:    cm.push {ra, s0-s2}, -32
+; RV64-ZCMP-NEXT:    .cfi_def_cfa_offset 32
+; RV64-ZCMP-NEXT:    .cfi_offset ra, -32
+; RV64-ZCMP-NEXT:    .cfi_offset s0, -24
+; RV64-ZCMP-NEXT:    .cfi_offset s1, -16
+; RV64-ZCMP-NEXT:    .cfi_offset s2, -8
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    mv s0, a0
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    mv s1, a0
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    mv s2, a0
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    add s0, s0, s1
+; RV64-ZCMP-NEXT:    add a0, a0, s2
+; RV64-ZCMP-NEXT:    addw a0, a0, s0
+; RV64-ZCMP-NEXT:    cm.pop {ra, s0-s2}, 32
+; RV64-ZCMP-NEXT:    sspopchk ra
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f4_both:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    sspush ra
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.push {ra, s0-s2}, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_def_cfa_offset 16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset ra, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset s0, -12
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset s1, -8
+; RV32-ZCMP-NOZCMOP-NEXT:    .cfi_offset s2, -4
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    mv s0, a0
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    mv s1, a0
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    mv s2, a0
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    add s0, s0, s1
+; RV32-ZCMP-NOZCMOP-NEXT:    add a0, a0, s2
+; RV32-ZCMP-NOZCMOP-NEXT:    add a0, a0, s0
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.pop {ra, s0-s2}, 16
+; RV32-ZCMP-NOZCMOP-NEXT:    sspopchk ra
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f4_both:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    sspush ra
@@ -1353,6 +1665,24 @@ define i32 @f5_both() "hw-shadow-stack" shadowcallstack nounwind {
 ; RV64-NEXT:    addi gp, gp, -8
 ; RV64-NEXT:    ret
 ;
+; RV64-ZCMP-LABEL: f5_both:
+; RV64-ZCMP:       # %bb.0:
+; RV64-ZCMP-NEXT:    sspush ra
+; RV64-ZCMP-NEXT:    cm.push {ra}, -16
+; RV64-ZCMP-NEXT:    call bar
+; RV64-ZCMP-NEXT:    cm.pop {ra}, 16
+; RV64-ZCMP-NEXT:    sspopchk ra
+; RV64-ZCMP-NEXT:    ret
+;
+; RV32-ZCMP-NOZCMOP-LABEL: f5_both:
+; RV32-ZCMP-NOZCMOP:       # %bb.0:
+; RV32-ZCMP-NOZCMOP-NEXT:    sspush ra
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.push {ra}, -16
+; RV32-ZCMP-NOZCMOP-NEXT:    call bar
+; RV32-ZCMP-NOZCMOP-NEXT:    cm.pop {ra}, 16
+; RV32-ZCMP-NOZCMOP-NEXT:    sspopchk ra
+; RV32-ZCMP-NOZCMOP-NEXT:    ret
+;
 ; RV32-NOZCMOP-LABEL: f5_both:
 ; RV32-NOZCMOP:       # %bb.0:
 ; RV32-NOZCMOP-NEXT:    sspush ra

>From bd035e0ddc98d2988c951dbc6b517c3c86bbb5ef Mon Sep 17 00:00:00 2001
From: Nemanja Ivanovic <nemanja.i.llvm at gmail.com>
Date: Wed, 29 Jul 2026 17:53:36 +0200
Subject: [PATCH 2/2] [RISC-V] Fix assert after 255162a (#212791)

The iterator passed-in may point to the end of the block which causes an
assertion failure when attempting to inspect the MI it points to. Guard
against this.

(cherry picked from commit f24da9d03ba5d25c523e050da2ca3c4060ca23d5)
---
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp |  3 +-
 llvm/test/CodeGen/RISCV/epilog-crash.ll      | 37 ++++++++++++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/RISCV/epilog-crash.ll

diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 516cb557feaba6..8eb20927fb8a08 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -195,7 +195,8 @@ static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB,
     return;
 
   // The shadow call stack popchk needs to happen after cm.pop that loads ra.
-  if (MI->getOpcode() == RISCV::CM_POP || MI->getOpcode() == RISCV::QC_CM_POP)
+  if (MI != MBB.end() &&
+      (MI->getOpcode() == RISCV::CM_POP || MI->getOpcode() == RISCV::QC_CM_POP))
     ++MI;
   const RISCVInstrInfo *TII = STI.getInstrInfo();
   if (HasHWShadowStack) {
diff --git a/llvm/test/CodeGen/RISCV/epilog-crash.ll b/llvm/test/CodeGen/RISCV/epilog-crash.ll
new file mode 100644
index 00000000000000..ad5c8a7068b9f0
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/epilog-crash.ll
@@ -0,0 +1,37 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64 < %s | FileCheck %s
+
+define double @test() #0 {
+; CHECK-LABEL: test:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi gp, gp, 8
+; CHECK-NEXT:    sd ra, -8(gp)
+; CHECK-NEXT:    .cfi_escape 0x16, 0x03, 0x02, 0x73, 0x78 #
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_offset ra, -8
+; CHECK-NEXT:    li a0, 0
+; CHECK-NEXT:    li a1, 0
+; CHECK-NEXT:    jalr a0
+; CHECK-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    .cfi_restore ra
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    .cfi_def_cfa_offset 0
+; CHECK-NEXT:    ld ra, -8(gp)
+; CHECK-NEXT:    addi gp, gp, -8
+; CHECK-NEXT:    .cfi_restore gp
+; CHECK-NEXT:    li a0, 0
+; CHECK-NEXT:    ret
+entry:
+  br label %if.end.i
+
+if.end.i:                                         ; preds = %entry
+  %call.i.i = tail call i32 null(i128 0)
+  br label %exit
+
+exit:                               ; preds = %if.end.i
+  ret double 0.000000e+00
+}
+
+attributes #0 = { shadowcallstack }



More information about the llvm-branch-commits mailing list