[llvm] [Hexagon] Disable restore stubs when ShadowCallStack is active (PR #206302)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 27 21:43:17 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-hexagon
Author: Brian Cain (androm3da)
<details>
<summary>Changes</summary>
The returning restore stubs (e.g. __restore_r16_through_r17_and_deallocframe) perform deallocframe+jumpr r31 internally, returning via the on-stack return address. This is incompatible with ShadowCallStack, which must restore r31 from the shadow stack before returning.
Fix by having useRestoreFunction() return false when the ShadowCallStack attribute is present, forcing inline callee-saved restores so the SCS epilogue is properly emitted.
---
Full diff: https://github.com/llvm/llvm-project/pull/206302.diff
2 Files Affected:
- (modified) llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp (+3)
- (modified) llvm/test/CodeGen/Hexagon/shadow-call-stack.ll (+69)
``````````diff
diff --git a/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp b/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp
index 9a02bfda4b4a9..41e422a8ac362 100644
--- a/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp
@@ -3000,6 +3000,9 @@ bool HexagonFrameLowering::useRestoreFunction(const MachineFunction &MF,
const CSIVect &CSI) const {
if (shouldInlineCSR(MF, CSI))
return false;
+ // The returning restore stubs do jumpr r31, this breaks ShadowCallStack:
+ if (MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack))
+ return false;
// The restore functions do a bit more than just restoring registers.
// The non-returning versions will go back directly to the caller's
// caller, others will clean up the stack frame in preparation for
diff --git a/llvm/test/CodeGen/Hexagon/shadow-call-stack.ll b/llvm/test/CodeGen/Hexagon/shadow-call-stack.ll
index ae6b254f5ed8e..b4e03f17e00a3 100644
--- a/llvm/test/CodeGen/Hexagon/shadow-call-stack.ll
+++ b/llvm/test/CodeGen/Hexagon/shadow-call-stack.ll
@@ -71,6 +71,43 @@
; CHECK-NOT: r19 = add(r19,#-4)
; CHECK-LABEL: nonleaf_cfi:
+;; Minsize + multiple callee-saved registers: the restore stub
+;; (__restore_r16_through_r17_and_deallocframe) must NOT be used when SCS is
+;; active because it performs deallocframe+jumpr without the SCS epilogue.
+; CHECK-LABEL: minsize_multicall:
+; CHECK: r19 = add(r19,#4)
+; CHECK: memw(r19+#-4) = r31
+; CHECK-NOT: __restore_r16_through_r17_and_deallocframe
+; CHECK: {
+; CHECK-DAG: r19 = add(r19,#-4)
+; CHECK-DAG: r31 = memw(r19+#-4)
+; CHECK: }
+; CHECK: jumpr r31
+
+;; Minsize + tail call + multiple callee-saved registers: the tailcall restore
+;; stub (__restore_r16_through_r17_and_deallocframe_before_tailcall) must NOT be
+;; used when SCS is active. The SCS epilogue and tail jump are fused together.
+; CHECK-LABEL: minsize_tailcall:
+; CHECK: r19 = add(r19,#4)
+; CHECK: memw(r19+#-4) = r31
+; CHECK-NOT: __restore_r16_through_r17_and_deallocframe_before_tailcall
+; CHECK: {
+; CHECK-DAG: r19 = add(r19,#-4)
+; CHECK-DAG: r31 = memw(r19+#-4)
+; CHECK-DAG: jump bar
+; CHECK: }
+
+;; Multiple return paths - each exit block gets its own SCS epilogue.
+; CHECK-LABEL: multi_return:
+; CHECK: r19 = add(r19,#4)
+; CHECK: memw(r19+#-4) = r31
+; CHECK: r31 = memw(r19+#-4)
+; CHECK: r19 = add(r19,#-4)
+; CHECK: jumpr r31
+; CHECK: r31 = memw(r19+#-4)
+; CHECK: r19 = add(r19,#-4)
+; CHECK: jumpr r31
+
;; Without r19 reserved, SCS should report an error.
; ERR: Must reserve r19 to use shadow call stack on Hexagon
@@ -97,7 +134,9 @@
; MUSL: }
; MUSL: jumpr r31
+declare i32 @foo(i32)
declare void @bar()
+declare void @baz(i32)
define void @leaf() shadowcallstack nounwind {
ret void
@@ -143,3 +182,33 @@ define void @vararg_musl(i32 %a, ...) shadowcallstack nounwind {
call void @bar()
ret void
}
+
+define i32 @minsize_multicall(i32 %x) shadowcallstack nounwind minsize
+ "disable-tail-calls"="true" {
+ %call = call i32 @foo(i32 %x)
+ %call1 = call i32 @foo(i32 %x)
+ %sum = add i32 %call, %call1
+ ret i32 %sum
+}
+
+define void @minsize_tailcall(i32 %x) shadowcallstack nounwind minsize {
+ call void @baz(i32 %x)
+ call void @baz(i32 %x)
+ tail call void @bar()
+ ret void
+}
+
+define i32 @multi_return(i32 %x) shadowcallstack nounwind optnone noinline {
+entry:
+ %call = call i32 @foo(i32 %x)
+ %cmp = icmp sgt i32 %call, 0
+ br i1 %cmp, label %pos, label %neg
+
+pos:
+ %r1 = call i32 @foo(i32 %call)
+ ret i32 %r1
+
+neg:
+ %r2 = call i32 @foo(i32 0)
+ ret i32 %r2
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/206302
More information about the llvm-commits
mailing list