[clang] [llvm] [CodeGen][Clang] Add -fstack-protector-layout-only (PR #231651)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 23 18:04:06 PDT 2026
https://github.com/amosher-nvidia updated https://github.com/llvm/llvm-project/pull/231651
>From 368cf146e6003c6d6d863615e9b29bacb283b6a3 Mon Sep 17 00:00:00 2001
From: Avery Mosher <amosher at nvidia.com>
Date: Fri, 18 Sep 2026 18:13:38 -0700
Subject: [PATCH 1/3] [CodeGen][Clang] Add -fstack-protector-layout-only
Add a way to apply only the changes to the stack frame layout that are
made when stack protectors are enabled, without actually inserting a
stack protector.
LLVM:
Add a new "stack-protector-layout-only" function attribute.
When specified, the StackProtector pass still identifies the objects
that are likely to be overflowed, and adjusts the stack frame layout
accordingly, but does not insert a stack protector.
Add a new MachineFrameInfo flag, stackProtectorLayoutOnly. It tells
PrologEpilogInserter and LocalStackSlotAllocation to apply the stack
protector's frame layout changes for those objects although no stack
protector object exists.
Clang:
Add a new flag -fstack-protector-layout-only which adds the layout-only
attribute to every function that gets a stack protector attribute. At a
minimum -fstack-protector is enabled so that the layout changes can be
made; if stack protection would otherwise be off, by default or via an
explicit -fno-stack-protector, a warning is emitted.
---
clang/docs/ReleaseNotes.md | 6 +
clang/docs/UsersManual.md | 11 ++
clang/include/clang/Basic/CodeGenOptions.def | 1 +
clang/include/clang/Options/Options.td | 7 +
clang/lib/CodeGen/CodeGenModule.cpp | 2 +
clang/lib/Driver/ToolChains/Clang.cpp | 19 +++
clang/test/CodeGen/stack-protector.c | 9 ++
clang/test/Driver/stack-protector.c | 32 ++++
llvm/docs/LangRef.md | 9 ++
llvm/docs/ReleaseNotes.md | 8 +
llvm/include/llvm/CodeGen/MIRYamlMapping.h | 4 +
llvm/include/llvm/CodeGen/MachineFrameInfo.h | 11 ++
llvm/include/llvm/CodeGen/StackProtector.h | 3 +
llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 3 +-
llvm/lib/CodeGen/LocalStackSlotAllocation.cpp | 31 ++--
llvm/lib/CodeGen/MIRParser/MIRParser.cpp | 1 +
llvm/lib/CodeGen/MIRPrinter.cpp | 1 +
llvm/lib/CodeGen/PrologEpilogInserter.cpp | 43 +++---
llvm/lib/CodeGen/StackProtector.cpp | 32 ++++
.../CodeGen/TargetLoweringObjectFileImpl.cpp | 3 +-
.../stack-protector-layout-only-codegen.ll | 140 ++++++++++++++++++
.../AArch64/stack-protector-layout-only.ll | 93 ++++++++++++
llvm/test/CodeGen/MIR/Generic/frame-info.mir | 3 +
.../stack-protector-layout-only-codegen.ll | 120 +++++++++++++++
.../RISCV/stack-protector-layout-only.ll | 94 ++++++++++++
.../stack-protector-layout-only-funclet.ll | 37 +++++
.../X86/stack-protector-layout-only.ll | 94 ++++++++++++
.../Inline/inline_ssp_layout_only.ll | 86 +++++++++++
.../StackProtector/layout-only-no-analysis.ll | 14 ++
.../layout-only-no-protector.ll | 54 +++++++
llvm/tools/llvm-reduce/ReducerWorkItem.cpp | 2 +
31 files changed, 938 insertions(+), 35 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll
create mode 100644 llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll
create mode 100644 llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll
create mode 100644 llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll
create mode 100644 llvm/test/CodeGen/X86/stack-protector-layout-only-funclet.ll
create mode 100644 llvm/test/CodeGen/X86/stack-protector-layout-only.ll
create mode 100644 llvm/test/Transforms/Inline/inline_ssp_layout_only.ll
create mode 100644 llvm/test/Transforms/StackProtector/layout-only-no-analysis.ll
create mode 100644 llvm/test/Transforms/StackProtector/layout-only-no-protector.ll
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 1ae4ec0d6b6d5a..b80502c826c148 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -349,6 +349,12 @@ documentation](CIR/index.md) for more information regarding ClangIR's status and
- New option `-fdefined-pointer-subtraction` added to preserve stable semantics
when subtracting pointers to unrelated objects.
+- Added `-fstack-protector-layout-only`. When specified, changes to the layout
+ of a stack frame that occur when `-fstack-protector` is specified are applied
+ but a stack protector is not actually inserted. This allows targets that have
+ features that can act as pseudo-stack-protectors, like RISC-V's shadow stack,
+ to benefit from the protection added by reordering stack variables.
+
- Added `--print-cxx-stdlib` and `--print-cxx-stdlib-include-dirs` to print
the C++ standard library selected by the driver and the include directories
added for it.
diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 96d1c4e009c71a..a310890a58fcb0 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -3037,6 +3037,17 @@ at block entry, which is more expensive and no more useful for the jumps this
feature exists to guard against. Clang applies the C++ rule in both languages.
:::
+:::{option} -f[no-]stack-protector-layout-only
+
+When specified, stack protectors will not be inserted, but stack variables will
+continue to be rearranged as if they were. This allows targets that have
+features that can act as pseudo-stack-protectors, like RISC-V's shadow stack,
+to benefit from the protection added by reordering stack variables.
+
+This option depends on at least `-fstack-protector`, so it will be overridden to
+being enabled with a warning, even if `-fno-stack-protector` is given.
+:::
+
(strict_aliasing)=
(strict-aliasing)=
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index c8ad0eb3f302e3..32016f588f374c 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -167,6 +167,7 @@ CODEGENOPT(CallFEntry , 1, 0, Benign) ///< Set when -mfentry is enabled.
CODEGENOPT(MNopMCount , 1, 0, Benign) ///< Set when -mnop-mcount is enabled.
CODEGENOPT(RecordMCount , 1, 0, Benign) ///< Set when -mrecord-mcount is enabled.
CODEGENOPT(StackProtectorGuardRecord, 1, 0, Benign) ///< Set when -mstack-protector-guard-record is enabled.
+CODEGENOPT(StackProtectorLayoutOnly, 1, 0, Benign) ///< Set when -fstack-protector-layout-only is enabled.
CODEGENOPT(PackedStack , 1, 0, Benign) ///< Set when -mpacked-stack is enabled.
CODEGENOPT(LessPreciseFPMAD , 1, 0, Benign) ///< Enable less precise MAD instructions to
///< be generated.
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index bc2d305fc45242..acf73079e4d152 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4633,6 +4633,13 @@ defm split_stack : BoolFOption<"split-stack",
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use segmented stack">>;
def fstack_protector_all : Flag<["-"], "fstack-protector-all">, Group<f_Group>,
HelpText<"Enable stack protectors for all functions">;
+defm stack_protector_layout_only : BoolFOption<"stack-protector-layout-only",
+ CodeGenOpts<"StackProtectorLayoutOnly">, DefaultFalse,
+ PosFlag<SetTrue, [], [ClangOption, CC1Option],
+ "Apply only the stack frame layout changes as part of stack protection, "
+ "without inserting stack protectors">,
+ NegFlag<SetFalse, [], [ClangOption],
+ "Insert stack protectors when stack protection is enabled">>;
defm stack_clash_protection : BoolFOption<"stack-clash-protection",
CodeGenOpts<"StackClashProtector">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index eec4d9e714abe6..f1edd8e12cf180 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3203,6 +3203,8 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
if (std::optional<llvm::Attribute::AttrKind> Attr =
StackProtectorAttribute(D)) {
B.addAttribute(*Attr);
+ if (CodeGenOpts.StackProtectorLayoutOnly)
+ B.addAttribute("stack-protector-layout-only");
}
if (!D) {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 454673dd20f9ad..2eb0b782743d7d 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3673,10 +3673,29 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
StackProtectorLevel = DefaultStackProtectorLevel;
}
+ bool LayoutOnly = false;
+ if (Arg *A = Args.getLastArg(options::OPT_fstack_protector_layout_only,
+ options::OPT_fno_stack_protector_layout_only)) {
+ LayoutOnly =
+ A->getOption().matches(options::OPT_fstack_protector_layout_only);
+ if (LayoutOnly && EffectiveTriple.isBPF()) {
+ D.Diag(diag::warn_drv_unsupported_option_for_target)
+ << A->getSpelling() << EffectiveTriple.getTriple();
+ LayoutOnly = false;
+ }
+ if (LayoutOnly && StackProtectorLevel == LangOptions::SSPOff) {
+ D.Diag(diag::warn_drv_overriding_option)
+ << "-fno-stack-protector" << A->getSpelling();
+ StackProtectorLevel = LangOptions::SSPOn;
+ }
+ }
+
if (StackProtectorLevel) {
CmdArgs.push_back("-stack-protector");
CmdArgs.push_back(Args.MakeArgString(Twine(StackProtectorLevel)));
}
+ if (LayoutOnly)
+ CmdArgs.push_back("-fstack-protector-layout-only");
// --param ssp-buffer-size=
for (const Arg *A : Args.filtered(options::OPT__param)) {
diff --git a/clang/test/CodeGen/stack-protector.c b/clang/test/CodeGen/stack-protector.c
index 9b93f775ffe799..b8996c02ff33ba 100644
--- a/clang/test/CodeGen/stack-protector.c
+++ b/clang/test/CodeGen/stack-protector.c
@@ -9,6 +9,11 @@
// RUN: %clang_cc1 -emit-llvm -o - %s -fsanitize=safe-stack -stack-protector 2 | FileCheck -check-prefix=DEF -check-prefix=SAFESTACK-SSPSTRONG %s
// RUN: %clang_cc1 -emit-llvm -o - %s -fsanitize=safe-stack -stack-protector 3 | FileCheck -check-prefix=DEF -check-prefix=SAFESTACK-SSPREQ %s
+// RUN: %clang_cc1 -emit-llvm -o - %s -stack-protector 0 -fstack-protector-layout-only | FileCheck -check-prefix=DEF -check-prefix=NOSSP -check-prefix=NO-LAYOUT-ONLY %s
+// RUN: %clang_cc1 -emit-llvm -o - %s -stack-protector 1 -fstack-protector-layout-only | FileCheck -check-prefix=DEF -check-prefix=SSP -check-prefix=LAYOUT-ONLY %s
+// RUN: %clang_cc1 -emit-llvm -o - %s -stack-protector 2 -fstack-protector-layout-only | FileCheck -check-prefix=DEF -check-prefix=SSPSTRONG -check-prefix=LAYOUT-ONLY %s
+// RUN: %clang_cc1 -emit-llvm -o - %s -stack-protector 3 -fstack-protector-layout-only | FileCheck -check-prefix=DEF -check-prefix=SSPREQ -check-prefix=LAYOUT-ONLY %s
+
typedef __SIZE_TYPE__ size_t;
int printf(const char * _Format, ...);
@@ -34,6 +39,7 @@ void test2(const char *msg) {
// SSP: attributes #[[A]] = {{.*}} ssp{{ }}
// SSPSTRONG: attributes #[[A]] = {{.*}} sspstrong
// SSPREQ: attributes #[[A]] = {{.*}} sspreq
+// LAYOUT-ONLY-SAME: "stack-protector-layout-only"
// SAFESTACK-NOSSP: attributes #[[A]] = {{.*}} safestack
// SAFESTACK-NOSSP-NOT: ssp
@@ -53,3 +59,6 @@ void test2(const char *msg) {
// SAFESTACK-SSPSTRONG-NOT: attributes #[[B]] = {{.*}} safestack sspstrong
// SAFESTACK-SSPREQ: attributes #[[B]] = {{.*}} safestack
// SAFESTACK-SSPREQ-NOT: attributes #[[B]] = {{.*}} safestack sspreq
+
+// LAYOUT-ONLY-NOT: attributes #[[B]] = {{.*}} "stack-protector-layout-only"
+// NO-LAYOUT-ONLY-NOT: "stack-protector-layout-only"
diff --git a/clang/test/Driver/stack-protector.c b/clang/test/Driver/stack-protector.c
index e082b3361d058a..f258ada07d1625 100644
--- a/clang/test/Driver/stack-protector.c
+++ b/clang/test/Driver/stack-protector.c
@@ -55,3 +55,35 @@
// RUN: %clang -ffreestanding -target x86_64-apple-darwin10 -mmacos-version-min=10.6 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_MACOSX_10_6_KERNEL
// SSP_MACOSX_10_6_KERNEL: "-stack-protector" "1"
+// Test -fstack-protector-layout-only with the different stack protector levels
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY
+// SSP-LAYOUT-ONLY: warning: overriding '-fno-stack-protector' option with '-fstack-protector-layout-only'
+// SSP-LAYOUT-ONLY: "-stack-protector" "1" "-fstack-protector-layout-only"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -fno-stack-protector -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-NO-SSP
+// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-layout-only -fno-stack-protector -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-NO-SSP
+// SSP-LAYOUT-ONLY-NO-SSP: warning: overriding '-fno-stack-protector' option with '-fstack-protector-layout-only'
+// SSP-LAYOUT-ONLY-NO-SSP: "-stack-protector" "1" "-fstack-protector-layout-only"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-layout-only -fstack-protector-strong -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-STRONG
+// SSP-LAYOUT-ONLY-STRONG-NOT: warning:
+// SSP-LAYOUT-ONLY-STRONG: "-stack-protector" "2" "-fstack-protector-layout-only"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-all -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-ALL
+// SSP-LAYOUT-ONLY-ALL-NOT: warning:
+// SSP-LAYOUT-ONLY-ALL: "-stack-protector" "3" "-fstack-protector-layout-only"
+
+// RUN: %clang --target=i386-pc-openbsd -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-OPENBSD
+// SSP-LAYOUT-ONLY-OPENBSD-NOT: warning:
+// SSP-LAYOUT-ONLY-OPENBSD: "-stack-protector" "2" "-fstack-protector-layout-only"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-layout-only -fno-stack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=NO-SSP-LAYOUT-ONLY
+// NO-SSP-LAYOUT-ONLY-NOT: "-stack-protector"
+// NO-SSP-LAYOUT-ONLY-NOT: "-fstack-protector-layout-only"
+
+// RUN: %clang --target=bpf -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-BPF
+// SSP-LAYOUT-ONLY-BPF: warning: ignoring '-fstack-protector-layout-only' option as it is not currently supported for target 'bpf'
+// SSP-LAYOUT-ONLY-BPF-NOT: overriding
+// SSP-LAYOUT-ONLY-BPF-NOT: "-stack-protector"
+// SSP-LAYOUT-ONLY-BPF-NOT: "-fstack-protector-layout-only"
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 1e8342fd119be9..3e39948f2e2928 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -2849,6 +2849,15 @@ fn -> other_fn -> other_fn ; fn is norecurse
function which has an `ssp` or `sspstrong` attribute, the calling
function's attribute will be upgraded to `sspreq`.
+`"stack-protector-layout-only"`
+: This attribute is only meaningful in conjunction with one of the stack
+ protector attributes above. It indicates that only the changes to the
+ stack frame layout that are made for a stack protector are applied,
+ and no stack protectors are actually inserted.
+
+ If a function with this attribute is inlined into a calling function, the
+ attribute will not be added to the calling function.
+
(strictfp)=
`strictfp`
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index a32825e3855a64..af66271967733b 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -172,6 +172,10 @@ Makes programs 10x faster by doing Special New Thing.
* Added the `bitinsert` and `bitextract` instructions for bit-range
manipulation on byte type values.
+* Added the `"stack-protector-layout-only"` attribute. When specified, the
+ `ssp`/`sspstrong`/`sspreq` attributes still make changes to the layout of the
+ stack frame, but do not actually insert any stack protectors.
+
### Changes to LLVM infrastructure
* Removed `TargetOptions::FloatABIType`. The soft float ABI should be
@@ -421,6 +425,10 @@ Makes programs 10x faster by doing Special New Thing.
### Changes to the CodeGen infrastructure
+* `MachineFrameInfo` gained a `stackProtectorLayoutOnly` flag. It tells the
+ prolog/epilog inserter and local stack slot allocation to lay out the stack
+ objects as if the function had a stack protector, even though it does not.
+
* Fixed a crash
([#214750](https://github.com/llvm/llvm-project/issues/214750)) when
compiling a function containing a static alloca of `(size_t)-1` bytes, whose
diff --git a/llvm/include/llvm/CodeGen/MIRYamlMapping.h b/llvm/include/llvm/CodeGen/MIRYamlMapping.h
index 61b4005e269247..f53b41f72e5a6a 100644
--- a/llvm/include/llvm/CodeGen/MIRYamlMapping.h
+++ b/llvm/include/llvm/CodeGen/MIRYamlMapping.h
@@ -736,6 +736,7 @@ struct MachineFrameInfo {
bool HasCalls = false;
FramePointerKind FramePointerPolicy = FramePointerKind::None;
StringValue StackProtector;
+ bool StackProtectorLayoutOnly = false;
StringValue FunctionContext;
unsigned MaxCallFrameSize = ~0u; ///< ~0u means: not computed yet.
unsigned CVBytesOfCalleeSavedRegisters = 0;
@@ -759,6 +760,7 @@ struct MachineFrameInfo {
AdjustsStack == Other.AdjustsStack && HasCalls == Other.HasCalls &&
FramePointerPolicy == Other.FramePointerPolicy &&
StackProtector == Other.StackProtector &&
+ StackProtectorLayoutOnly == Other.StackProtectorLayoutOnly &&
FunctionContext == Other.FunctionContext &&
MaxCallFrameSize == Other.MaxCallFrameSize &&
CVBytesOfCalleeSavedRegisters ==
@@ -788,6 +790,8 @@ template <> struct MappingTraits<MachineFrameInfo> {
YamlIO.mapOptional("framePointerPolicy", MFI.FramePointerPolicy);
YamlIO.mapOptional("stackProtector", MFI.StackProtector,
StringValue()); // Don't print it out when it's empty.
+ YamlIO.mapOptional("stackProtectorLayoutOnly", MFI.StackProtectorLayoutOnly,
+ false);
YamlIO.mapOptional("functionContext", MFI.FunctionContext,
StringValue()); // Don't print it out when it's empty.
YamlIO.mapOptional("maxCallFrameSize", MFI.MaxCallFrameSize, (unsigned)~0);
diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
index 30699c3c18f1c4..67308abe03667d 100644
--- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
@@ -291,6 +291,10 @@ class MachineFrameInfo {
/// The frame index for the stack protector.
int StackProtectorIdx = -1;
+ /// True if the stack objects should be laid out as if the function had a
+ /// stack protector, even though it does not.
+ bool StackProtectorLayoutOnly = false;
+
/// The frame index for the function context. Used for SjLj exceptions.
int FunctionContextIdx = -1;
@@ -379,6 +383,13 @@ class MachineFrameInfo {
void setStackProtectorIndex(int I) { StackProtectorIdx = I; }
bool hasStackProtectorIndex() const { return StackProtectorIdx != -1; }
+ /// Return true if the stack objects are laid out as if the function had a
+ /// stack protector, even though it does not.
+ bool hasStackProtectorLayoutOnly() const { return StackProtectorLayoutOnly; }
+ void setStackProtectorLayoutOnly(bool B = true) {
+ StackProtectorLayoutOnly = B;
+ }
+
/// Return the index for the function context object.
/// This object is used for SjLj exceptions.
int getFunctionContextIndex() const { return FunctionContextIdx; }
diff --git a/llvm/include/llvm/CodeGen/StackProtector.h b/llvm/include/llvm/CodeGen/StackProtector.h
index 2d6dc76a65fe8d..d627ae111322df 100644
--- a/llvm/include/llvm/CodeGen/StackProtector.h
+++ b/llvm/include/llvm/CodeGen/StackProtector.h
@@ -52,6 +52,9 @@ class SSPLayoutInfo {
bool RequireStackProtector = false;
+ // Changes to frame layout are made, but no stack protector is inserted.
+ bool LayoutOnly = false;
+
// A prologue is generated.
bool HasPrologue = false;
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index cfdcd96d35cff6..273de13d5309e1 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -1570,7 +1570,8 @@ void CodeViewDebug::beginFunctionImpl(const MachineFunction *MF) {
GV.hasFnAttribute(Attribute::StackProtectReq)) {
FPO |= FrameProcedureOptions::StrictSecurityChecks;
}
- } else if (!GV.hasStackProtectorFnAttr()) {
+ } else if (!GV.hasStackProtectorFnAttr() ||
+ GV.hasFnAttribute("stack-protector-layout-only")) {
// __declspec(safebuffers) disables stack guards.
FPO |= FrameProcedureOptions::SafeBuffers;
}
diff --git a/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp b/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp
index a388413525c23d..697b9bae707b30 100644
--- a/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp
+++ b/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp
@@ -218,27 +218,30 @@ void LocalStackSlotImpl::calculateFrameObjectOffsets(MachineFunction &Fn) {
Align MaxAlign;
// Make sure that the stack protector comes before the local variables on the
- // stack.
+ // stack. With StackProtectorLayoutOnly these objects are ordered the same
+ // way although no stack protector is inserted.
SmallSet<int, 16> ProtectedObjs;
- if (MFI.hasStackProtectorIndex()) {
+ if (MFI.hasStackProtectorIndex() || MFI.hasStackProtectorLayoutOnly()) {
int StackProtectorFI = MFI.getStackProtectorIndex();
- // We need to make sure we didn't pre-allocate the stack protector when
- // doing this.
- // If we already have a stack protector, this will re-assign it to a slot
- // that is **not** covering the protected objects.
- assert(!MFI.isObjectPreAllocated(StackProtectorFI) &&
- "Stack protector pre-allocated in LocalStackSlotAllocation");
-
StackObjSet LargeArrayObjs;
StackObjSet SmallArrayObjs;
StackObjSet AddrOfObjs;
- // Only place the stack protector in the local stack area if the target
- // allows it.
- if (TFI.isStackIdSafeForLocalArea(MFI.getStackID(StackProtectorFI)))
- AdjustStackOffset(MFI, StackProtectorFI, Offset, StackGrowsDown,
- MaxAlign);
+ if (MFI.hasStackProtectorIndex()) {
+ // We need to make sure we didn't pre-allocate the stack protector when
+ // doing this.
+ // If we already have a stack protector, this will re-assign it to a slot
+ // that is **not** covering the protected objects.
+ assert(!MFI.isObjectPreAllocated(StackProtectorFI) &&
+ "Stack protector pre-allocated in LocalStackSlotAllocation");
+
+ // Only place the stack protector in the local stack area if the target
+ // allows it.
+ if (TFI.isStackIdSafeForLocalArea(MFI.getStackID(StackProtectorFI)))
+ AdjustStackOffset(MFI, StackProtectorFI, Offset, StackGrowsDown,
+ MaxAlign);
+ }
// Assign large stack objects first.
for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) {
diff --git a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
index f2af6ccefea00d..5cbabeba725f0d 100644
--- a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
@@ -957,6 +957,7 @@ bool MIRParserImpl::initializeFrameInfo(PerFunctionMIParsingState &PFS,
MFI.setHasCalls(YamlMFI.HasCalls);
if (YamlMFI.FramePointerPolicy != FramePointerKind::None)
MFI.setFramePointerPolicy(YamlMFI.FramePointerPolicy);
+ MFI.setStackProtectorLayoutOnly(YamlMFI.StackProtectorLayoutOnly);
if (YamlMFI.MaxCallFrameSize != ~0u)
MFI.setMaxCallFrameSize(YamlMFI.MaxCallFrameSize);
MFI.setCVBytesOfCalleeSavedRegisters(YamlMFI.CVBytesOfCalleeSavedRegisters);
diff --git a/llvm/lib/CodeGen/MIRPrinter.cpp b/llvm/lib/CodeGen/MIRPrinter.cpp
index 1a4d6754e88ddf..d4aa2537f6b192 100644
--- a/llvm/lib/CodeGen/MIRPrinter.cpp
+++ b/llvm/lib/CodeGen/MIRPrinter.cpp
@@ -384,6 +384,7 @@ static void convertMFI(ModuleSlotTracker &MST, yaml::MachineFrameInfo &YamlMFI,
YamlMFI.AdjustsStack = MFI.adjustsStack();
YamlMFI.HasCalls = MFI.hasCalls();
YamlMFI.FramePointerPolicy = MFI.getFramePointerPolicy();
+ YamlMFI.StackProtectorLayoutOnly = MFI.hasStackProtectorLayoutOnly();
YamlMFI.MaxCallFrameSize = MFI.isMaxCallFrameSizeComputed()
? MFI.getMaxCallFrameSize() : ~0u;
YamlMFI.CVBytesOfCalleeSavedRegisters =
diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
index 49fa0f89d616c1..ef0c36398fbc44 100644
--- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -972,9 +972,12 @@ void PEIImpl::calculateFrameObjectOffsets(MachineFunction &MF) {
EHRegNodeFrameIndex = FuncInfo->EHRegNodeFrameIndex;
// Make sure that the stack protector comes before the local variables on the
- // stack.
+ // stack. With StackProtectorLayoutOnly these objects are ordered the same
+ // way although no stack protector is inserted.
SmallSet<int, 16> ProtectedObjs;
- if (MFI.hasStackProtectorIndex()) {
+ bool DoSSPLayout =
+ MFI.hasStackProtectorIndex() || MFI.hasStackProtectorLayoutOnly();
+ if (DoSSPLayout) {
int StackProtectorFI = MFI.getStackProtectorIndex();
StackObjSet LargeArrayObjs;
StackObjSet SmallArrayObjs;
@@ -984,21 +987,23 @@ void PEIImpl::calculateFrameObjectOffsets(MachineFunction &MF) {
// LocalStackSlotPass didn't already allocate a slot for it.
// If we are told to use the LocalStackAllocationBlock, the stack protector
// is expected to be already pre-allocated.
- if (MFI.getStackID(StackProtectorFI) != TargetStackID::Default) {
- // If the stack protector isn't on the default stack then it's up to the
- // target to set the stack offset.
- assert(MFI.getObjectOffset(StackProtectorFI) != 0 &&
- "Offset of stack protector on non-default stack expected to be "
- "already set.");
- assert(!MFI.isObjectPreAllocated(MFI.getStackProtectorIndex()) &&
- "Stack protector on non-default stack expected to not be "
- "pre-allocated by LocalStackSlotPass.");
- } else if (!MFI.getUseLocalStackAllocationBlock()) {
- AdjustStackOffset(MFI, StackProtectorFI, StackGrowsDown, Offset,
- MaxAlign);
- } else if (!MFI.isObjectPreAllocated(MFI.getStackProtectorIndex())) {
- llvm_unreachable(
- "Stack protector not pre-allocated by LocalStackSlotPass.");
+ if (MFI.hasStackProtectorIndex()) {
+ if (MFI.getStackID(StackProtectorFI) != TargetStackID::Default) {
+ // If the stack protector isn't on the default stack then it's up to the
+ // target to set the stack offset.
+ assert(MFI.getObjectOffset(StackProtectorFI) != 0 &&
+ "Offset of stack protector on non-default stack expected to be "
+ "already set.");
+ assert(!MFI.isObjectPreAllocated(MFI.getStackProtectorIndex()) &&
+ "Stack protector on non-default stack expected to not be "
+ "pre-allocated by LocalStackSlotPass.");
+ } else if (!MFI.getUseLocalStackAllocationBlock()) {
+ AdjustStackOffset(MFI, StackProtectorFI, StackGrowsDown, Offset,
+ MaxAlign);
+ } else if (!MFI.isObjectPreAllocated(MFI.getStackProtectorIndex())) {
+ llvm_unreachable(
+ "Stack protector not pre-allocated by LocalStackSlotPass.");
+ }
}
// Assign large stack objects first.
@@ -1091,8 +1096,8 @@ void PEIImpl::calculateFrameObjectOffsets(MachineFunction &MF) {
// optimizing.
BitVector StackBytesFree;
if (!ObjectsToAllocate.empty() &&
- MF.getTarget().getOptLevel() != CodeGenOptLevel::None &&
- MFI.getStackProtectorIndex() < 0 && TFI.enableStackSlotScavenging(MF))
+ MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !DoSSPLayout &&
+ TFI.enableStackSlotScavenging(MF))
computeFreeStackSlots(MFI, StackGrowsDown, FixedCSEnd, StackBytesFree);
// Now walk the objects and actually assign base offsets to them.
diff --git a/llvm/lib/CodeGen/StackProtector.cpp b/llvm/lib/CodeGen/StackProtector.cpp
index ec5144a73d97f7..6a61137c1c718b 100644
--- a/llvm/lib/CodeGen/StackProtector.cpp
+++ b/llvm/lib/CodeGen/StackProtector.cpp
@@ -55,6 +55,9 @@ using namespace llvm;
#define DEBUG_TYPE "stack-protector"
STATISTIC(NumFunProtected, "Number of functions protected");
+STATISTIC(NumFunLayoutOnly,
+ "Number of functions that have their layouts modified, but no "
+ "protector inserted");
STATISTIC(NumAddrTaken, "Number of local variables that have their address"
" taken.");
@@ -87,6 +90,7 @@ void SSPLayoutInfo::copyToMachineFrameInfo(MachineFrameInfo &MFI) const {
if (Layout.empty())
return;
+ bool HasLayout = false;
for (int I = 0, E = MFI.getObjectIndexEnd(); I != E; ++I) {
if (MFI.isDeadObjectIndex(I))
continue;
@@ -100,7 +104,23 @@ void SSPLayoutInfo::copyToMachineFrameInfo(MachineFrameInfo &MFI) const {
continue;
MFI.setObjectSSPLayout(I, LI->second);
+ HasLayout = true;
}
+
+ if (HasLayout && LayoutOnly)
+ MFI.setStackProtectorLayoutOnly();
+}
+
+/// Return true if F's stack objects should be laid out as if it had a stack
+/// protector, but without inserting one. Functions with funclets are not
+/// correctly supported, so are excluded.
+static bool isLayoutOnly(const Function &F) {
+ if (!F.hasFnAttribute("stack-protector-layout-only"))
+ return false;
+ if (F.hasPersonalityFn() &&
+ isFuncletEHPersonality(classifyEHPersonality(F.getPersonalityFn())))
+ return false;
+ return true;
}
SSPLayoutInfo SSPLayoutAnalysis::run(Function &F,
@@ -109,6 +129,7 @@ SSPLayoutInfo SSPLayoutAnalysis::run(Function &F,
SSPLayoutInfo Info;
Info.RequireStackProtector =
SSPLayoutAnalysis::requiresStackProtector(&F, &Info.Layout);
+ Info.LayoutOnly = isLayoutOnly(F);
Info.SSPBufferSize = F.getFnAttributeAsParsedInteger(
"stack-protector-buffer-size", SSPLayoutInfo::DefaultSSPBufferSize);
return Info;
@@ -125,6 +146,11 @@ PreservedAnalyses StackProtectorPass::run(Function &F,
if (!Info.RequireStackProtector)
return PreservedAnalyses::all();
+ if (Info.LayoutOnly) {
+ ++NumFunLayoutOnly;
+ return PreservedAnalyses::all();
+ }
+
// TODO(etienneb): Functions with funclets are not correctly supported now.
// Do nothing if this is funclet-based personality.
if (F.hasPersonalityFn()) {
@@ -191,6 +217,7 @@ bool StackProtector::runOnFunction(Function &Fn) {
if (auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>())
DTU.emplace(DTWP->getDomTree(), DomTreeUpdater::UpdateStrategy::Lazy);
TM = &getAnalysis<TargetPassConfig>().getTM<TargetMachine>();
+ LayoutInfo.LayoutOnly = isLayoutOnly(Fn);
LayoutInfo.HasPrologue = false;
LayoutInfo.HasIRCheck = false;
@@ -199,6 +226,11 @@ bool StackProtector::runOnFunction(Function &Fn) {
if (!requiresStackProtector(F, &LayoutInfo.Layout))
return false;
+ if (LayoutInfo.LayoutOnly) {
+ ++NumFunLayoutOnly;
+ return false;
+ }
+
// TODO(etienneb): Functions with funclets are not correctly supported now.
// Do nothing if this is funclet-based personality.
if (Fn.hasPersonalityFn()) {
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index acff5e19b06d79..8fb2d08c5c0be3 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2454,7 +2454,8 @@ bool TargetLoweringObjectFileXCOFF::ShouldEmitEHBlock(
bool TargetLoweringObjectFileXCOFF::ShouldSetSSPCanaryBitInTB(
const MachineFunction *MF) {
const Function &F = MF->getFunction();
- if (!F.hasStackProtectorFnAttr())
+ if (!F.hasStackProtectorFnAttr() ||
+ F.hasFnAttribute("stack-protector-layout-only"))
return false;
// FIXME: check presence of canary word
// There are cases that the stack protectors are not really inserted even if
diff --git a/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll b/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll
new file mode 100644
index 00000000000000..1f6c92fd0895d9
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll
@@ -0,0 +1,140 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -o - %s | FileCheck %s --check-prefixes=CHECK,OPT
+; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -O0 -o - %s | FileCheck %s --check-prefixes=CHECK,O0
+
+; Check that no stack protector is emitted for functions with
+; "stack-protector-layout-only", with and without GlobalISel.
+
+declare void @use(ptr, ptr, ptr)
+declare void @sink(i32)
+
+define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" {
+; CHECK-LABEL: layout_strong:
+; CHECK: // %bb.0:
+; CHECK-NEXT: sub sp, sp, #48
+; CHECK-NEXT: str x30, [sp, #32] // 8-byte Spill
+; CHECK-NEXT: .cfi_def_cfa_offset 48
+; CHECK-NEXT: .cfi_offset w30, -16
+; CHECK-NEXT: str w0, [sp, #4]
+; CHECK-NEXT: add x0, sp, #16
+; CHECK-NEXT: add x1, sp, #12
+; CHECK-NEXT: add x2, sp, #8
+; CHECK-NEXT: bl use
+; CHECK-NEXT: ldr w0, [sp, #4]
+; CHECK-NEXT: bl sink
+; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Reload
+; CHECK-NEXT: add sp, sp, #48
+; CHECK-NEXT: ret
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
+; CHECK-LABEL: layout_ssp:
+; CHECK: // %bb.0:
+; CHECK-NEXT: sub sp, sp, #48
+; CHECK-NEXT: str x30, [sp, #32] // 8-byte Spill
+; CHECK-NEXT: .cfi_def_cfa_offset 48
+; CHECK-NEXT: .cfi_offset w30, -16
+; CHECK-NEXT: str w0, [sp, #12]
+; CHECK-NEXT: add x0, sp, #16
+; CHECK-NEXT: add x1, sp, #4
+; CHECK-NEXT: add x2, sp, #8
+; CHECK-NEXT: bl use
+; CHECK-NEXT: ldr w0, [sp, #12]
+; CHECK-NEXT: bl sink
+; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Reload
+; CHECK-NEXT: add sp, sp, #48
+; CHECK-NEXT: ret
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+define void @req_empty() sspreq "stack-protector-layout-only" {
+; CHECK-LABEL: req_empty:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ret
+ ret void
+}
+
+define void @cookie(i32 %n) sspstrong {
+; OPT-LABEL: cookie:
+; OPT: // %bb.0:
+; OPT-NEXT: sub sp, sp, #64
+; OPT-NEXT: str x30, [sp, #48] // 8-byte Spill
+; OPT-NEXT: .cfi_def_cfa_offset 64
+; OPT-NEXT: .cfi_offset w30, -16
+; OPT-NEXT: adrp x8, __stack_chk_guard
+; OPT-NEXT: add x1, sp, #12
+; OPT-NEXT: add x2, sp, #8
+; OPT-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard]
+; OPT-NEXT: str x8, [sp, #40]
+; OPT-NEXT: str w0, [sp, #4]
+; OPT-NEXT: add x0, sp, #16
+; OPT-NEXT: bl use
+; OPT-NEXT: ldr w0, [sp, #4]
+; OPT-NEXT: bl sink
+; OPT-NEXT: adrp x8, __stack_chk_guard
+; OPT-NEXT: ldr x9, [sp, #40]
+; OPT-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard]
+; OPT-NEXT: cmp x8, x9
+; OPT-NEXT: b.ne .LBB3_2
+; OPT-NEXT: // %bb.1:
+; OPT-NEXT: ldr x30, [sp, #48] // 8-byte Reload
+; OPT-NEXT: add sp, sp, #64
+; OPT-NEXT: ret
+; OPT-NEXT: .LBB3_2:
+; OPT-NEXT: bl __stack_chk_fail
+;
+; O0-LABEL: cookie:
+; O0: // %bb.0:
+; O0-NEXT: sub sp, sp, #64
+; O0-NEXT: str x30, [sp, #48] // 8-byte Spill
+; O0-NEXT: .cfi_def_cfa_offset 64
+; O0-NEXT: .cfi_offset w30, -16
+; O0-NEXT: adrp x8, __stack_chk_guard
+; O0-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard]
+; O0-NEXT: str x8, [sp, #40]
+; O0-NEXT: str w0, [sp, #4]
+; O0-NEXT: add x0, sp, #16
+; O0-NEXT: add x1, sp, #12
+; O0-NEXT: add x2, sp, #8
+; O0-NEXT: bl use
+; O0-NEXT: ldr w0, [sp, #4]
+; O0-NEXT: bl sink
+; O0-NEXT: ldr x9, [sp, #40]
+; O0-NEXT: adrp x8, __stack_chk_guard
+; O0-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard]
+; O0-NEXT: subs x8, x8, x9
+; O0-NEXT: b.ne .LBB3_1
+; O0-NEXT: b .LBB3_2
+; O0-NEXT: .LBB3_1:
+; O0-NEXT: bl __stack_chk_fail
+; O0-NEXT: .LBB3_2:
+; O0-NEXT: ldr x30, [sp, #48] // 8-byte Reload
+; O0-NEXT: add sp, sp, #64
+; O0-NEXT: ret
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
diff --git a/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll b/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll
new file mode 100644
index 00000000000000..533c03d06aeeb3
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll
@@ -0,0 +1,93 @@
+; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -stop-after=prolog-epilog -o - %s | FileCheck %s
+; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -O0 -stop-after=prolog-epilog -o - %s | FileCheck %s
+
+; Check that with "stack-protector-layout-only" the stack objects are ordered
+; as if there were a stack protector, but one is not inserted.
+
+declare void @use(ptr, ptr, ptr)
+declare void @sink(i32)
+
+; CHECK-LABEL: name: layout_strong
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]],
+; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR+4]],
+; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR+8]],
+; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+12]],
+define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+; CHECK-LABEL: name: layout_ssp
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]],
+; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]],
+; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]],
+; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+4]],
+define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+; CHECK-LABEL: name: req_empty
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+define void @req_empty() sspreq "stack-protector-layout-only" {
+ ret void
+}
+
+; Control: without a stack protector attribute the objects keep their order; at
+; -O2 the small ones may be scavenged into callee-save holes.
+; CHECK-LABEL: name: control
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]],
+; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]],
+; CHECK: - { id: 2, name: small, type: default, offset: -{{[0-9]+}},
+; CHECK: - { id: 3, name: large, type: default, offset: -{{[0-9]+}},
+define void @control(i32 %n) {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+; Control: without the attribute a stack protector is still inserted.
+; CHECK-LABEL: name: cookie
+; CHECK: stackProtector: '%stack.{{[0-9]+}}.StackGuardSlot'
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+define void @cookie(i32 %n) sspstrong {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
diff --git a/llvm/test/CodeGen/MIR/Generic/frame-info.mir b/llvm/test/CodeGen/MIR/Generic/frame-info.mir
index 9879559e5e85a2..175bc564531544 100644
--- a/llvm/test/CodeGen/MIR/Generic/frame-info.mir
+++ b/llvm/test/CodeGen/MIR/Generic/frame-info.mir
@@ -58,6 +58,7 @@ tracksRegLiveness: true
# CHECK-NEXT: hasCalls: false
# CHECK-NEXT: framePointerPolicy: none
# CHECK-NEXT: stackProtector: ''
+# CHECK-NEXT: stackProtectorLayoutOnly: false
# CHECK-NEXT: functionContext: ''
# CHECK-NEXT: maxCallFrameSize:
# CHECK-NEXT: cvBytesOfCalleeSavedRegisters: 0
@@ -93,6 +94,7 @@ stack:
# CHECK-NEXT: hasCalls: true
# CHECK-NEXT: framePointerPolicy: none
# CHECK-NEXT: stackProtector: ''
+# CHECK-NEXT: stackProtectorLayoutOnly: true
# CHECK-NEXT: functionContext: '%stack.0'
# CHECK-NEXT: maxCallFrameSize: 4
# CHECK-NEXT: cvBytesOfCalleeSavedRegisters: 8
@@ -111,6 +113,7 @@ frameInfo:
maxAlignment: 4
adjustsStack: true
hasCalls: true
+ stackProtectorLayoutOnly: true
functionContext: '%stack.0'
maxCallFrameSize: 4
cvBytesOfCalleeSavedRegisters: 8
diff --git a/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll b/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll
new file mode 100644
index 00000000000000..a8253555d6522e
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll
@@ -0,0 +1,120 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64 -o - %s | FileCheck %s
+
+; Check that no stack protector is emitted for functions with
+; "stack-protector-layout-only".
+
+declare void @use(ptr, ptr, ptr)
+declare void @sink(i32)
+
+define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" {
+; CHECK-LABEL: layout_strong:
+; CHECK: # %bb.0:
+; CHECK-NEXT: addi sp, sp, -48
+; CHECK-NEXT: .cfi_def_cfa_offset 48
+; CHECK-NEXT: sd ra, 40(sp) # 8-byte Folded Spill
+; CHECK-NEXT: .cfi_offset ra, -8
+; CHECK-NEXT: sw a0, 4(sp)
+; CHECK-NEXT: addi a0, sp, 16
+; CHECK-NEXT: addi a1, sp, 12
+; CHECK-NEXT: addi a2, sp, 8
+; CHECK-NEXT: call use
+; CHECK-NEXT: lw a0, 4(sp)
+; CHECK-NEXT: call sink
+; CHECK-NEXT: ld ra, 40(sp) # 8-byte Folded Reload
+; CHECK-NEXT: .cfi_restore ra
+; CHECK-NEXT: addi sp, sp, 48
+; CHECK-NEXT: .cfi_def_cfa_offset 0
+; CHECK-NEXT: ret
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
+; CHECK-LABEL: layout_ssp:
+; CHECK: # %bb.0:
+; CHECK-NEXT: addi sp, sp, -48
+; CHECK-NEXT: .cfi_def_cfa_offset 48
+; CHECK-NEXT: sd ra, 40(sp) # 8-byte Folded Spill
+; CHECK-NEXT: .cfi_offset ra, -8
+; CHECK-NEXT: sw a0, 12(sp)
+; CHECK-NEXT: addi a0, sp, 16
+; CHECK-NEXT: addi a1, sp, 4
+; CHECK-NEXT: addi a2, sp, 8
+; CHECK-NEXT: call use
+; CHECK-NEXT: lw a0, 12(sp)
+; CHECK-NEXT: call sink
+; CHECK-NEXT: ld ra, 40(sp) # 8-byte Folded Reload
+; CHECK-NEXT: .cfi_restore ra
+; CHECK-NEXT: addi sp, sp, 48
+; CHECK-NEXT: .cfi_def_cfa_offset 0
+; CHECK-NEXT: ret
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+define void @req_empty() sspreq "stack-protector-layout-only" {
+; CHECK-LABEL: req_empty:
+; CHECK: # %bb.0:
+; CHECK-NEXT: ret
+ ret void
+}
+
+define void @cookie(i32 %n) sspstrong {
+; CHECK-LABEL: cookie:
+; CHECK: # %bb.0:
+; CHECK-NEXT: addi sp, sp, -64
+; CHECK-NEXT: .cfi_def_cfa_offset 64
+; CHECK-NEXT: sd ra, 56(sp) # 8-byte Folded Spill
+; CHECK-NEXT: sd s0, 48(sp) # 8-byte Folded Spill
+; CHECK-NEXT: .cfi_offset ra, -8
+; CHECK-NEXT: .cfi_offset s0, -16
+; CHECK-NEXT: .cfi_remember_state
+; CHECK-NEXT: lui s0, %hi(__stack_chk_guard)
+; CHECK-NEXT: ld a1, %lo(__stack_chk_guard)(s0)
+; CHECK-NEXT: sd a1, 40(sp)
+; CHECK-NEXT: sw a0, 4(sp)
+; CHECK-NEXT: addi a0, sp, 16
+; CHECK-NEXT: addi a1, sp, 12
+; CHECK-NEXT: addi a2, sp, 8
+; CHECK-NEXT: call use
+; CHECK-NEXT: lw a0, 4(sp)
+; CHECK-NEXT: call sink
+; CHECK-NEXT: ld a0, %lo(__stack_chk_guard)(s0)
+; CHECK-NEXT: ld a1, 40(sp)
+; CHECK-NEXT: bne a0, a1, .LBB3_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: ld ra, 56(sp) # 8-byte Folded Reload
+; CHECK-NEXT: ld s0, 48(sp) # 8-byte Folded Reload
+; CHECK-NEXT: .cfi_restore ra
+; CHECK-NEXT: .cfi_restore s0
+; CHECK-NEXT: addi sp, sp, 64
+; CHECK-NEXT: .cfi_def_cfa_offset 0
+; CHECK-NEXT: ret
+; CHECK-NEXT: .LBB3_2:
+; CHECK-NEXT: .cfi_restore_state
+; CHECK-NEXT: call __stack_chk_fail
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
diff --git a/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll b/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll
new file mode 100644
index 00000000000000..41af3da7ed3a4e
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll
@@ -0,0 +1,94 @@
+; RUN: llc -mtriple=riscv64 -stop-after=prolog-epilog -o - %s | FileCheck %s
+; RUN: llc -mtriple=riscv64 -O0 -global-isel -stop-after=prolog-epilog -o - %s | FileCheck %s
+
+; Check that with "stack-protector-layout-only" the stack objects are ordered
+; as if there were a stack protector, but one is not inserted.
+
+declare void @use(ptr, ptr, ptr)
+declare void @sink(i32)
+
+; CHECK-LABEL: name: layout_strong
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]],
+; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR+4]],
+; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR+8]],
+; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+12]],
+define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+; With ssp only the large array moves; the rest keep their order.
+; CHECK-LABEL: name: layout_ssp
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]],
+; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]],
+; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]],
+; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+4]],
+define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+; Nothing to lay out, so stackProtectorLayoutOnly stays false.
+; CHECK-LABEL: name: req_empty
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+define void @req_empty() sspreq "stack-protector-layout-only" {
+ ret void
+}
+
+; Control: without a stack protector attribute the objects keep their order.
+; CHECK-LABEL: name: control
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]],
+; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]],
+; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]],
+; CHECK: - { id: 3, name: large, type: default, offset: -{{[0-9]+}},
+define void @control(i32 %n) {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+; Control: without the attribute a stack protector is still inserted.
+; CHECK-LABEL: name: cookie
+; CHECK: stackProtector: '%stack.{{[0-9]+}}.StackGuardSlot'
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+define void @cookie(i32 %n) sspstrong {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
diff --git a/llvm/test/CodeGen/X86/stack-protector-layout-only-funclet.ll b/llvm/test/CodeGen/X86/stack-protector-layout-only-funclet.ll
new file mode 100644
index 00000000000000..f96c909ce4a4de
--- /dev/null
+++ b/llvm/test/CodeGen/X86/stack-protector-layout-only-funclet.ll
@@ -0,0 +1,37 @@
+; RUN: llc -mtriple=x86_64-pc-windows-msvc -stop-after=prolog-epilog -o - %s | FileCheck %s
+
+; Check that "stack-protector-layout-only" is ignored for functions with
+; funclets, which StackProtector does not support.
+
+declare void @may_throw()
+declare void @use(ptr)
+declare i32 @__CxxFrameHandler3(...)
+
+; CHECK-LABEL: name: funclet
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+define void @funclet() sspstrong "stack-protector-layout-only" personality ptr @__CxxFrameHandler3 {
+entry:
+ %buf = alloca [16 x i8], align 1
+ call void @use(ptr %buf)
+ invoke void @may_throw()
+ to label %cont unwind label %lpad
+cont:
+ ret void
+lpad:
+ %cs = catchswitch within none [label %catch] unwind to caller
+catch:
+ %p = catchpad within %cs [ptr null, i32 64, ptr null]
+ catchret from %p to label %cont
+}
+
+; Control: the same function without funclets.
+; CHECK-LABEL: name: no_funclet
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+define void @no_funclet() sspstrong "stack-protector-layout-only" {
+entry:
+ %buf = alloca [16 x i8], align 1
+ call void @use(ptr %buf)
+ ret void
+}
diff --git a/llvm/test/CodeGen/X86/stack-protector-layout-only.ll b/llvm/test/CodeGen/X86/stack-protector-layout-only.ll
new file mode 100644
index 00000000000000..809481bfa7d62d
--- /dev/null
+++ b/llvm/test/CodeGen/X86/stack-protector-layout-only.ll
@@ -0,0 +1,94 @@
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu -stack-symbol-ordering=0 -stop-after=prolog-epilog -o - %s | FileCheck %s
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu -stack-symbol-ordering=0 -O0 -stop-after=prolog-epilog -o - %s | FileCheck %s
+
+; Check that with "stack-protector-layout-only" the stack objects are ordered
+; as if there were a stack protector, but one is not inserted.
+
+declare void @use(ptr, ptr, ptr)
+declare void @sink(i32)
+
+; CHECK-LABEL: name: layout_strong
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]],
+; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR+4]],
+; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR+8]],
+; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+12]],
+define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+; With ssp only the large array moves; the rest keep their order.
+; CHECK-LABEL: name: layout_ssp
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]],
+; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]],
+; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]],
+; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+4]],
+define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+; Nothing to lay out, so stackProtectorLayoutOnly stays false.
+; CHECK-LABEL: name: req_empty
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+define void @req_empty() sspreq "stack-protector-layout-only" {
+ ret void
+}
+
+; Control: without a stack protector attribute the objects keep their order.
+; CHECK-LABEL: name: control
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]],
+; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]],
+; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]],
+; CHECK: - { id: 3, name: large, type: default, offset: -{{[0-9]+}},
+define void @control(i32 %n) {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
+
+; Control: without the attribute a stack protector is still inserted.
+; CHECK-LABEL: name: cookie
+; CHECK: stackProtector: '%stack.{{[0-9]+}}.StackGuardSlot'
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+define void @cookie(i32 %n) sspstrong {
+ %scalar = alloca i32, align 4
+ %addrof = alloca i32, align 4
+ %small = alloca [4 x i8], align 4
+ %large = alloca [16 x i8], align 16
+ store i32 %n, ptr %scalar
+ call void @use(ptr %large, ptr %small, ptr %addrof)
+ %v = load i32, ptr %scalar
+ call void @sink(i32 %v)
+ ret void
+}
diff --git a/llvm/test/Transforms/Inline/inline_ssp_layout_only.ll b/llvm/test/Transforms/Inline/inline_ssp_layout_only.ll
new file mode 100644
index 00000000000000..f9269463b0679d
--- /dev/null
+++ b/llvm/test/Transforms/Inline/inline_ssp_layout_only.ll
@@ -0,0 +1,86 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt -passes=inline -S -o - %s | FileCheck %s
+; Ensure "stack-protector-layout-only" is neither added to nor removed from the
+; caller when inlining, while the SSP attributes are still merged.
+
+declare void @sink()
+
+define internal void @callee_nossp() alwaysinline {
+ call void @sink()
+ ret void
+}
+
+define internal void @callee_sspstrong() sspstrong alwaysinline {
+ call void @sink()
+ ret void
+}
+
+define internal void @callee_ssp_layout_only() ssp "stack-protector-layout-only" alwaysinline {
+ call void @sink()
+ ret void
+}
+
+define internal void @callee_sspreq_layout_only() sspreq "stack-protector-layout-only" alwaysinline {
+ call void @sink()
+ ret void
+}
+
+define internal void @callee_sspstrong_layout_only() sspstrong "stack-protector-layout-only" alwaysinline {
+ call void @sink()
+ ret void
+}
+
+define void @caller_ssp_layout_only_callee_sspstrong() ssp "stack-protector-layout-only" {
+; CHECK-LABEL: define void @caller_ssp_layout_only_callee_sspstrong(
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: call void @sink()
+; CHECK-NEXT: ret void
+;
+ call void @callee_sspstrong()
+ ret void
+}
+
+define void @caller_sspstrong_callee_ssp_layout_only() sspstrong {
+; CHECK-LABEL: define void @caller_sspstrong_callee_ssp_layout_only(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: call void @sink()
+; CHECK-NEXT: ret void
+;
+ call void @callee_ssp_layout_only()
+ ret void
+}
+
+define void @caller_ssp_callee_sspreq_layout_only() ssp {
+; CHECK-LABEL: define void @caller_ssp_callee_sspreq_layout_only(
+; CHECK-SAME: ) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: call void @sink()
+; CHECK-NEXT: ret void
+;
+ call void @callee_sspreq_layout_only()
+ ret void
+}
+
+define void @caller_nossp_callee_sspstrong_layout_only() {
+; CHECK-LABEL: define void @caller_nossp_callee_sspstrong_layout_only() {
+; CHECK-NEXT: call void @sink()
+; CHECK-NEXT: ret void
+;
+ call void @callee_sspstrong_layout_only()
+ ret void
+}
+
+define void @caller_sspreq_layout_only_callee_nossp() sspreq "stack-protector-layout-only" {
+; CHECK-LABEL: define void @caller_sspreq_layout_only_callee_nossp(
+; CHECK-SAME: ) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT: call void @sink()
+; CHECK-NEXT: ret void
+;
+ call void @callee_nossp()
+ ret void
+}
+;.
+; CHECK: attributes #[[ATTR0]] = { sspstrong "stack-protector-layout-only" }
+; CHECK: attributes #[[ATTR1]] = { sspstrong }
+; CHECK: attributes #[[ATTR2]] = { sspreq }
+; CHECK: attributes #[[ATTR3]] = { sspreq "stack-protector-layout-only" }
+;.
diff --git a/llvm/test/Transforms/StackProtector/layout-only-no-analysis.ll b/llvm/test/Transforms/StackProtector/layout-only-no-analysis.ll
new file mode 100644
index 00000000000000..c63f2084c7efef
--- /dev/null
+++ b/llvm/test/Transforms/StackProtector/layout-only-no-analysis.ll
@@ -0,0 +1,14 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+;; StackProtector inserts no __stack_chk_fail call for functions with
+;; "stack-protector-layout-only", so it must not require
+;; LibcallLoweringModuleAnalysis for them.
+; REQUIRES: x86-registered-target
+; RUN: opt -mtriple=x86_64-- -passes=stack-protector -S -o - %s | FileCheck %s
+
+define void @empty() sspreq "stack-protector-layout-only" {
+; CHECK-LABEL: define void @empty(
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: ret void
+;
+ ret void
+}
diff --git a/llvm/test/Transforms/StackProtector/layout-only-no-protector.ll b/llvm/test/Transforms/StackProtector/layout-only-no-protector.ll
new file mode 100644
index 00000000000000..eb82ab9f8798f5
--- /dev/null
+++ b/llvm/test/Transforms/StackProtector/layout-only-no-protector.ll
@@ -0,0 +1,54 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+;; Check that no stack protector is inserted for functions with
+;; "stack-protector-layout-only", whether one is required by a buffer
+;; (sspstrong) or unconditionally (sspreq).
+; REQUIRES: x86-registered-target
+; RUN: opt -mtriple=x86_64-pc-linux-gnu -passes='require<libcall-lowering-info>,stack-protector' -S -o - %s | FileCheck %s
+
+declare void @use(ptr)
+
+define void @layout_only_strong() sspstrong "stack-protector-layout-only" {
+; CHECK-LABEL: define void @layout_only_strong(
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[BUF:%.*]] = alloca [16 x i8], align 1
+; CHECK-NEXT: call void @use(ptr [[BUF]])
+; CHECK-NEXT: ret void
+;
+ %buf = alloca [16 x i8], align 1
+ call void @use(ptr %buf)
+ ret void
+}
+
+define void @layout_only_req() sspreq "stack-protector-layout-only" {
+; CHECK-LABEL: define void @layout_only_req(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: ret void
+;
+ ret void
+}
+
+define void @cookie() sspstrong {
+; CHECK-LABEL: define void @cookie(
+; CHECK-SAME: ) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)), align 8
+; CHECK-NEXT: call void @llvm.stackprotector(ptr [[STACKGUARD]], ptr [[STACKGUARDSLOT]])
+; CHECK-NEXT: [[BUF:%.*]] = alloca [16 x i8], align 1
+; CHECK-NEXT: call void @use(ptr [[BUF]])
+; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)), align 8
+; CHECK-NEXT: [[TMP1:%.*]] = load volatile ptr, ptr [[STACKGUARDSLOT]], align 8
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[STACKGUARD1]], [[TMP1]]
+; CHECK-NEXT: br i1 [[TMP2]], label %[[SP_RETURN:.*]], label %[[CALLSTACKCHECKFAILBLK:.*]], !prof [[PROF0:![0-9]+]]
+; CHECK: [[SP_RETURN]]:
+; CHECK-NEXT: ret void
+; CHECK: [[CALLSTACKCHECKFAILBLK]]:
+; CHECK-NEXT: call void @__stack_chk_fail() #[[ATTR4:[0-9]+]]
+; CHECK-NEXT: unreachable
+;
+ %buf = alloca [16 x i8], align 1
+ call void @use(ptr %buf)
+ ret void
+}
+;.
+; CHECK: [[PROF0]] = !{!"branch_weights", i32 2147481600, i32 2048}
+;.
diff --git a/llvm/tools/llvm-reduce/ReducerWorkItem.cpp b/llvm/tools/llvm-reduce/ReducerWorkItem.cpp
index 4d8a570d699d66..472955d5a8fda9 100644
--- a/llvm/tools/llvm-reduce/ReducerWorkItem.cpp
+++ b/llvm/tools/llvm-reduce/ReducerWorkItem.cpp
@@ -170,6 +170,8 @@ static void cloneFrameInfo(
DstMFI.setStackProtectorIndex(SrcMFI.getStackProtectorIndex());
}
+ DstMFI.setStackProtectorLayoutOnly(SrcMFI.hasStackProtectorLayoutOnly());
+
// FIXME: Needs test, missing MIR serialization.
if (SrcMFI.hasFunctionContextIndex()) {
DstMFI.setFunctionContextIndex(SrcMFI.getFunctionContextIndex());
>From aa57027ab03292f14e895e1523c6cee69b4c753a Mon Sep 17 00:00:00 2001
From: Avery Mosher <amosher at nvidia.com>
Date: Thu, 22 Oct 2026 15:49:12 -0700
Subject: [PATCH 2/3] [CodeGen] Regenerate AArch64
stack-protector-layout-only-codegen.ll checks
---
.../stack-protector-layout-only-codegen.ll | 113 ++++++++++++------
1 file changed, 75 insertions(+), 38 deletions(-)
diff --git a/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll b/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll
index 1f6c92fd0895d9..ac6b189e310e56 100644
--- a/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll
+++ b/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll
@@ -9,22 +9,40 @@ declare void @use(ptr, ptr, ptr)
declare void @sink(i32)
define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" {
-; CHECK-LABEL: layout_strong:
-; CHECK: // %bb.0:
-; CHECK-NEXT: sub sp, sp, #48
-; CHECK-NEXT: str x30, [sp, #32] // 8-byte Spill
-; CHECK-NEXT: .cfi_def_cfa_offset 48
-; CHECK-NEXT: .cfi_offset w30, -16
-; CHECK-NEXT: str w0, [sp, #4]
-; CHECK-NEXT: add x0, sp, #16
-; CHECK-NEXT: add x1, sp, #12
-; CHECK-NEXT: add x2, sp, #8
-; CHECK-NEXT: bl use
-; CHECK-NEXT: ldr w0, [sp, #4]
-; CHECK-NEXT: bl sink
-; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Reload
-; CHECK-NEXT: add sp, sp, #48
-; CHECK-NEXT: ret
+; OPT-LABEL: layout_strong:
+; OPT: // %bb.0:
+; OPT-NEXT: sub sp, sp, #48
+; OPT-NEXT: str x30, [sp, #32] // 8-byte Spill
+; OPT-NEXT: .cfi_def_cfa_offset 48
+; OPT-NEXT: .cfi_offset w30, -16
+; OPT-NEXT: str w0, [sp, #4]
+; OPT-NEXT: add x0, sp, #16
+; OPT-NEXT: add x1, sp, #12
+; OPT-NEXT: add x2, sp, #8
+; OPT-NEXT: bl use
+; OPT-NEXT: ldr w0, [sp, #4]
+; OPT-NEXT: bl sink
+; OPT-NEXT: ldr x30, [sp, #32] // 8-byte Reload
+; OPT-NEXT: add sp, sp, #48
+; OPT-NEXT: ret
+;
+; O0-LABEL: layout_strong:
+; O0: // %bb.0:
+; O0-NEXT: sub sp, sp, #48
+; O0-NEXT: str x30, [sp, #32] // 8-byte Spill
+; O0-NEXT: .cfi_def_cfa_offset 48
+; O0-NEXT: .cfi_offset w30, -16
+; O0-NEXT: mov w8, w0
+; O0-NEXT: add x2, sp, #8
+; O0-NEXT: add x1, sp, #12
+; O0-NEXT: add x0, sp, #16
+; O0-NEXT: str w8, [sp, #4]
+; O0-NEXT: bl use
+; O0-NEXT: ldr w0, [sp, #4]
+; O0-NEXT: bl sink
+; O0-NEXT: ldr x30, [sp, #32] // 8-byte Reload
+; O0-NEXT: add sp, sp, #48
+; O0-NEXT: ret
%scalar = alloca i32, align 4
%addrof = alloca i32, align 4
%small = alloca [4 x i8], align 4
@@ -37,22 +55,40 @@ define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" {
}
define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
-; CHECK-LABEL: layout_ssp:
-; CHECK: // %bb.0:
-; CHECK-NEXT: sub sp, sp, #48
-; CHECK-NEXT: str x30, [sp, #32] // 8-byte Spill
-; CHECK-NEXT: .cfi_def_cfa_offset 48
-; CHECK-NEXT: .cfi_offset w30, -16
-; CHECK-NEXT: str w0, [sp, #12]
-; CHECK-NEXT: add x0, sp, #16
-; CHECK-NEXT: add x1, sp, #4
-; CHECK-NEXT: add x2, sp, #8
-; CHECK-NEXT: bl use
-; CHECK-NEXT: ldr w0, [sp, #12]
-; CHECK-NEXT: bl sink
-; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Reload
-; CHECK-NEXT: add sp, sp, #48
-; CHECK-NEXT: ret
+; OPT-LABEL: layout_ssp:
+; OPT: // %bb.0:
+; OPT-NEXT: sub sp, sp, #48
+; OPT-NEXT: str x30, [sp, #32] // 8-byte Spill
+; OPT-NEXT: .cfi_def_cfa_offset 48
+; OPT-NEXT: .cfi_offset w30, -16
+; OPT-NEXT: str w0, [sp, #12]
+; OPT-NEXT: add x0, sp, #16
+; OPT-NEXT: add x1, sp, #4
+; OPT-NEXT: add x2, sp, #8
+; OPT-NEXT: bl use
+; OPT-NEXT: ldr w0, [sp, #12]
+; OPT-NEXT: bl sink
+; OPT-NEXT: ldr x30, [sp, #32] // 8-byte Reload
+; OPT-NEXT: add sp, sp, #48
+; OPT-NEXT: ret
+;
+; O0-LABEL: layout_ssp:
+; O0: // %bb.0:
+; O0-NEXT: sub sp, sp, #48
+; O0-NEXT: str x30, [sp, #32] // 8-byte Spill
+; O0-NEXT: .cfi_def_cfa_offset 48
+; O0-NEXT: .cfi_offset w30, -16
+; O0-NEXT: mov w8, w0
+; O0-NEXT: add x2, sp, #8
+; O0-NEXT: add x1, sp, #4
+; O0-NEXT: add x0, sp, #16
+; O0-NEXT: str w8, [sp, #12]
+; O0-NEXT: bl use
+; O0-NEXT: ldr w0, [sp, #12]
+; O0-NEXT: bl sink
+; O0-NEXT: ldr x30, [sp, #32] // 8-byte Reload
+; O0-NEXT: add sp, sp, #48
+; O0-NEXT: ret
%scalar = alloca i32, align 4
%addrof = alloca i32, align 4
%small = alloca [4 x i8], align 4
@@ -106,13 +142,14 @@ define void @cookie(i32 %n) sspstrong {
; O0-NEXT: str x30, [sp, #48] // 8-byte Spill
; O0-NEXT: .cfi_def_cfa_offset 64
; O0-NEXT: .cfi_offset w30, -16
-; O0-NEXT: adrp x8, __stack_chk_guard
-; O0-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard]
-; O0-NEXT: str x8, [sp, #40]
-; O0-NEXT: str w0, [sp, #4]
-; O0-NEXT: add x0, sp, #16
-; O0-NEXT: add x1, sp, #12
+; O0-NEXT: mov w8, w0
+; O0-NEXT: adrp x9, __stack_chk_guard
+; O0-NEXT: ldr x9, [x9, :lo12:__stack_chk_guard]
+; O0-NEXT: str x9, [sp, #40]
; O0-NEXT: add x2, sp, #8
+; O0-NEXT: add x1, sp, #12
+; O0-NEXT: add x0, sp, #16
+; O0-NEXT: str w8, [sp, #4]
; O0-NEXT: bl use
; O0-NEXT: ldr w0, [sp, #4]
; O0-NEXT: bl sink
>From d4233245ff4d3804aa4e507cc5cea533876e076b Mon Sep 17 00:00:00 2001
From: Avery Mosher <amosher at nvidia.com>
Date: Fri, 23 Oct 2026 18:03:02 -0700
Subject: [PATCH 3/3] [CodeGen] Force ra to be spilled with
"stack-protector-layout-only"
---
clang/docs/ReleaseNotes.md | 7 +-
clang/docs/UsersManual.md | 9 ++-
llvm/docs/LangRef.md | 4 +-
llvm/docs/ReleaseNotes.md | 4 +-
llvm/include/llvm/CodeGen/MachineFrameInfo.h | 6 +-
llvm/lib/CodeGen/StackProtector.cpp | 12 ++--
llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp | 11 ++++
.../stack-protector-layout-only-codegen.ll | 64 +++++++++++++++++--
.../AArch64/stack-protector-layout-only.ll | 34 +++++++++-
.../stack-protector-layout-only-codegen.ll | 46 ++++++++++++-
.../RISCV/stack-protector-layout-only.ll | 36 ++++++++++-
.../X86/stack-protector-layout-only.ll | 7 +-
12 files changed, 207 insertions(+), 33 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index b80502c826c148..2737f7c43125ad 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -351,9 +351,10 @@ documentation](CIR/index.md) for more information regarding ClangIR's status and
- Added `-fstack-protector-layout-only`. When specified, changes to the layout
of a stack frame that occur when `-fstack-protector` is specified are applied
- but a stack protector is not actually inserted. This allows targets that have
- features that can act as pseudo-stack-protectors, like RISC-V's shadow stack,
- to benefit from the protection added by reordering stack variables.
+ but a stack protector is not actually inserted, and the return address is
+ forced to be spilled to the stack. This allows targets that have features
+ that can act as pseudo-stack-protectors, like RISC-V's shadow stack, to
+ benefit from the protection added by reordering stack variables.
- Added `--print-cxx-stdlib` and `--print-cxx-stdlib-include-dirs` to print
the C++ standard library selected by the driver and the include directories
diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index a310890a58fcb0..c85beabcac19bd 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -3040,9 +3040,12 @@ feature exists to guard against. Clang applies the C++ rule in both languages.
:::{option} -f[no-]stack-protector-layout-only
When specified, stack protectors will not be inserted, but stack variables will
-continue to be rearranged as if they were. This allows targets that have
-features that can act as pseudo-stack-protectors, like RISC-V's shadow stack,
-to benefit from the protection added by reordering stack variables.
+continue to be rearranged as if they were, and the return address will be
+spilled to the stack in every function that would have had a stack protector.
+This allows targets that have features that can act as pseudo-stack-protectors,
+like RISC-V's shadow stack, to benefit from the protection added by reordering
+stack variables, with the spilled return address taking the place of the stack
+protector.
This option depends on at least `-fstack-protector`, so it will be overridden to
being enabled with a warning, even if `-fno-stack-protector` is given.
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 3e39948f2e2928..53c4a2ca7afc37 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -2853,7 +2853,9 @@ fn -> other_fn -> other_fn ; fn is norecurse
: This attribute is only meaningful in conjunction with one of the stack
protector attributes above. It indicates that only the changes to the
stack frame layout that are made for a stack protector are applied,
- and no stack protectors are actually inserted.
+ and no stack protectors are actually inserted. The return address is
+ also forced to be spilled to the stack in functions in which a stack
+ protector would have been inserted.
If a function with this attribute is inlined into a calling function, the
attribute will not be added to the calling function.
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index af66271967733b..6e7d248c83b1a3 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -174,7 +174,8 @@ Makes programs 10x faster by doing Special New Thing.
* Added the `"stack-protector-layout-only"` attribute. When specified, the
`ssp`/`sspstrong`/`sspreq` attributes still make changes to the layout of the
- stack frame, but do not actually insert any stack protectors.
+ stack frame and force the return address to be spilled to the stack, but do
+ not actually insert any stack protectors.
### Changes to LLVM infrastructure
@@ -428,6 +429,7 @@ Makes programs 10x faster by doing Special New Thing.
* `MachineFrameInfo` gained a `stackProtectorLayoutOnly` flag. It tells the
prolog/epilog inserter and local stack slot allocation to lay out the stack
objects as if the function had a stack protector, even though it does not.
+ It also forces the return address to be spilled to the stack.
* Fixed a crash
([#214750](https://github.com/llvm/llvm-project/issues/214750)) when
diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
index 67308abe03667d..d7e4c48a1b7225 100644
--- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
@@ -292,7 +292,8 @@ class MachineFrameInfo {
int StackProtectorIdx = -1;
/// True if the stack objects should be laid out as if the function had a
- /// stack protector, even though it does not.
+ /// stack protector, even though it does not. Also forces the return address
+ /// to be spilled to the stack.
bool StackProtectorLayoutOnly = false;
/// The frame index for the function context. Used for SjLj exceptions.
@@ -384,7 +385,8 @@ class MachineFrameInfo {
bool hasStackProtectorIndex() const { return StackProtectorIdx != -1; }
/// Return true if the stack objects are laid out as if the function had a
- /// stack protector, even though it does not.
+ /// stack protector, even though it does not. Also forces the return address
+ /// to be spilled to the stack.
bool hasStackProtectorLayoutOnly() const { return StackProtectorLayoutOnly; }
void setStackProtectorLayoutOnly(bool B = true) {
StackProtectorLayoutOnly = B;
diff --git a/llvm/lib/CodeGen/StackProtector.cpp b/llvm/lib/CodeGen/StackProtector.cpp
index 6a61137c1c718b..681207af3ecb35 100644
--- a/llvm/lib/CodeGen/StackProtector.cpp
+++ b/llvm/lib/CodeGen/StackProtector.cpp
@@ -87,10 +87,12 @@ bool SSPLayoutInfo::shouldEmitSDCheck(const BasicBlock &BB) const {
}
void SSPLayoutInfo::copyToMachineFrameInfo(MachineFrameInfo &MFI) const {
+ if (RequireStackProtector && LayoutOnly)
+ MFI.setStackProtectorLayoutOnly();
+
if (Layout.empty())
return;
- bool HasLayout = false;
for (int I = 0, E = MFI.getObjectIndexEnd(); I != E; ++I) {
if (MFI.isDeadObjectIndex(I))
continue;
@@ -104,11 +106,7 @@ void SSPLayoutInfo::copyToMachineFrameInfo(MachineFrameInfo &MFI) const {
continue;
MFI.setObjectSSPLayout(I, LI->second);
- HasLayout = true;
}
-
- if (HasLayout && LayoutOnly)
- MFI.setStackProtectorLayoutOnly();
}
/// Return true if F's stack objects should be laid out as if it had a stack
@@ -223,7 +221,9 @@ bool StackProtector::runOnFunction(Function &Fn) {
LayoutInfo.SSPBufferSize = Fn.getFnAttributeAsParsedInteger(
"stack-protector-buffer-size", SSPLayoutInfo::DefaultSSPBufferSize);
- if (!requiresStackProtector(F, &LayoutInfo.Layout))
+ LayoutInfo.RequireStackProtector =
+ requiresStackProtector(F, &LayoutInfo.Layout);
+ if (!LayoutInfo.RequireStackProtector)
return false;
if (LayoutInfo.LayoutOnly) {
diff --git a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
index dc275b41e8943d..cdedf3e6c35c0f 100644
--- a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
+++ b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
@@ -135,6 +135,17 @@ TargetFrameLowering::getMustPreserveRegisters(const MachineFunction &MF) const {
void TargetFrameLowering::determineUncondPrologCalleeSaves(
MachineFunction &MF, const MCPhysReg *CSRegs, BitVector &SavedRegs) const {
+ // We always spill ra when StackProtectorLayoutOnly is specified
+ if (MF.getFrameInfo().hasStackProtectorLayoutOnly()) {
+ MCRegister RA = MF.getSubtarget().getRegisterInfo()->getRARegister();
+ for (unsigned i = 0; CSRegs[i]; ++i) {
+ if (RA == CSRegs[i]) {
+ SavedRegs.set(RA);
+ break;
+ }
+ }
+ }
+
// Functions which call __builtin_unwind_init get all their registers saved.
if (!MF.callsUnwindInit())
return;
diff --git a/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll b/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll
index ac6b189e310e56..38266d72151ed7 100644
--- a/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll
+++ b/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll
@@ -3,7 +3,8 @@
; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -O0 -o - %s | FileCheck %s --check-prefixes=CHECK,O0
; Check that no stack protector is emitted for functions with
-; "stack-protector-layout-only", with and without GlobalISel.
+; "stack-protector-layout-only" and that the return address is spilled, with
+; and without GlobalISel.
declare void @use(ptr, ptr, ptr)
declare void @sink(i32)
@@ -100,13 +101,62 @@ define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
ret void
}
+define void @layout_leaf(i8 %c) sspstrong "stack-protector-layout-only" {
+; OPT-LABEL: layout_leaf:
+; OPT: // %bb.0:
+; OPT-NEXT: sub sp, sp, #32
+; OPT-NEXT: str x30, [sp, #16] // 8-byte Spill
+; OPT-NEXT: .cfi_def_cfa_offset 32
+; OPT-NEXT: .cfi_offset w30, -16
+; OPT-NEXT: strb w0, [sp], #32
+; OPT-NEXT: ret
+;
+; O0-LABEL: layout_leaf:
+; O0: // %bb.0:
+; O0-NEXT: sub sp, sp, #32
+; O0-NEXT: str x30, [sp, #16] // 8-byte Spill
+; O0-NEXT: .cfi_def_cfa_offset 32
+; O0-NEXT: .cfi_offset w30, -16
+; O0-NEXT: strb w0, [sp]
+; O0-NEXT: ldr x30, [sp, #16] // 8-byte Reload
+; O0-NEXT: add sp, sp, #32
+; O0-NEXT: ret
+ %large = alloca [16 x i8], align 16
+ store volatile i8 %c, ptr %large
+ ret void
+}
+
define void @req_empty() sspreq "stack-protector-layout-only" {
; CHECK-LABEL: req_empty:
; CHECK: // %bb.0:
+; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: .cfi_offset w30, -16
+; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
ret void
}
+define void @control_leaf(i8 %c) {
+; OPT-LABEL: control_leaf:
+; OPT: // %bb.0:
+; OPT-NEXT: sub sp, sp, #16
+; OPT-NEXT: .cfi_def_cfa_offset 16
+; OPT-NEXT: strb w0, [sp], #16
+; OPT-NEXT: ret
+;
+; O0-LABEL: control_leaf:
+; O0: // %bb.0:
+; O0-NEXT: sub sp, sp, #16
+; O0-NEXT: .cfi_def_cfa_offset 16
+; O0-NEXT: strb w0, [sp]
+; O0-NEXT: add sp, sp, #16
+; O0-NEXT: ret
+ %large = alloca [16 x i8], align 16
+ store volatile i8 %c, ptr %large
+ ret void
+}
+
define void @cookie(i32 %n) sspstrong {
; OPT-LABEL: cookie:
; OPT: // %bb.0:
@@ -128,12 +178,12 @@ define void @cookie(i32 %n) sspstrong {
; OPT-NEXT: ldr x9, [sp, #40]
; OPT-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard]
; OPT-NEXT: cmp x8, x9
-; OPT-NEXT: b.ne .LBB3_2
+; OPT-NEXT: b.ne .LBB5_2
; OPT-NEXT: // %bb.1:
; OPT-NEXT: ldr x30, [sp, #48] // 8-byte Reload
; OPT-NEXT: add sp, sp, #64
; OPT-NEXT: ret
-; OPT-NEXT: .LBB3_2:
+; OPT-NEXT: .LBB5_2:
; OPT-NEXT: bl __stack_chk_fail
;
; O0-LABEL: cookie:
@@ -157,11 +207,11 @@ define void @cookie(i32 %n) sspstrong {
; O0-NEXT: adrp x8, __stack_chk_guard
; O0-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard]
; O0-NEXT: subs x8, x8, x9
-; O0-NEXT: b.ne .LBB3_1
-; O0-NEXT: b .LBB3_2
-; O0-NEXT: .LBB3_1:
+; O0-NEXT: b.ne .LBB5_1
+; O0-NEXT: b .LBB5_2
+; O0-NEXT: .LBB5_1:
; O0-NEXT: bl __stack_chk_fail
-; O0-NEXT: .LBB3_2:
+; O0-NEXT: .LBB5_2:
; O0-NEXT: ldr x30, [sp, #48] // 8-byte Reload
; O0-NEXT: add sp, sp, #64
; O0-NEXT: ret
diff --git a/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll b/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll
index 533c03d06aeeb3..c6d63b2ad2a42a 100644
--- a/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll
+++ b/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll
@@ -2,7 +2,8 @@
; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -O0 -stop-after=prolog-epilog -o - %s | FileCheck %s
; Check that with "stack-protector-layout-only" the stack objects are ordered
-; as if there were a stack protector, but one is not inserted.
+; as if there were a stack protector, but one is not inserted, and the return
+; address is spilled.
declare void @use(ptr, ptr, ptr)
declare void @sink(i32)
@@ -47,9 +48,25 @@ define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
ret void
}
+; CHECK-LABEL: name: layout_leaf
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: large, type: default,
+; CHECK: - { id: 1, name: '', type: spill-slot,
+; CHECK: callee-saved-register: '$lr'
+define void @layout_leaf(i8 %c) sspstrong "stack-protector-layout-only" {
+ %large = alloca [16 x i8], align 16
+ store volatile i8 %c, ptr %large
+ ret void
+}
+
; CHECK-LABEL: name: req_empty
; CHECK: stackProtector: ''
-; CHECK-NEXT: stackProtectorLayoutOnly: false
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: '', type: spill-slot,
+; CHECK: callee-saved-register: '$lr'
define void @req_empty() sspreq "stack-protector-layout-only" {
ret void
}
@@ -76,6 +93,19 @@ define void @control(i32 %n) {
ret void
}
+; Control: without a stack protector attribute the return address is not
+; spilled.
+; CHECK-LABEL: name: control_leaf
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+; CHECK-NOT: callee-saved-register: '$lr'
+; CHECK: body:
+define void @control_leaf(i8 %c) {
+ %large = alloca [16 x i8], align 16
+ store volatile i8 %c, ptr %large
+ ret void
+}
+
; Control: without the attribute a stack protector is still inserted.
; CHECK-LABEL: name: cookie
; CHECK: stackProtector: '%stack.{{[0-9]+}}.StackGuardSlot'
diff --git a/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll b/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll
index a8253555d6522e..cb4fc3299a5f04 100644
--- a/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll
+++ b/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll
@@ -2,7 +2,7 @@
; RUN: llc -mtriple=riscv64 -o - %s | FileCheck %s
; Check that no stack protector is emitted for functions with
-; "stack-protector-layout-only".
+; "stack-protector-layout-only", and that the return address is spilled.
declare void @use(ptr, ptr, ptr)
declare void @sink(i32)
@@ -67,13 +67,53 @@ define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
ret void
}
+define void @layout_leaf(i8 %c) sspstrong "stack-protector-layout-only" {
+; CHECK-LABEL: layout_leaf:
+; CHECK: # %bb.0:
+; CHECK-NEXT: addi sp, sp, -32
+; CHECK-NEXT: .cfi_def_cfa_offset 32
+; CHECK-NEXT: sd ra, 24(sp) # 8-byte Folded Spill
+; CHECK-NEXT: .cfi_offset ra, -8
+; CHECK-NEXT: sb a0, 0(sp)
+; CHECK-NEXT: ld ra, 24(sp) # 8-byte Folded Reload
+; CHECK-NEXT: .cfi_restore ra
+; CHECK-NEXT: addi sp, sp, 32
+; CHECK-NEXT: .cfi_def_cfa_offset 0
+; CHECK-NEXT: ret
+ %large = alloca [16 x i8], align 16
+ store volatile i8 %c, ptr %large
+ ret void
+}
+
define void @req_empty() sspreq "stack-protector-layout-only" {
; CHECK-LABEL: req_empty:
; CHECK: # %bb.0:
+; 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: 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: ret
ret void
}
+define void @control_leaf(i8 %c) {
+; CHECK-LABEL: control_leaf:
+; CHECK: # %bb.0:
+; CHECK-NEXT: addi sp, sp, -16
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: sb a0, 0(sp)
+; CHECK-NEXT: addi sp, sp, 16
+; CHECK-NEXT: .cfi_def_cfa_offset 0
+; CHECK-NEXT: ret
+ %large = alloca [16 x i8], align 16
+ store volatile i8 %c, ptr %large
+ ret void
+}
+
define void @cookie(i32 %n) sspstrong {
; CHECK-LABEL: cookie:
; CHECK: # %bb.0:
@@ -96,7 +136,7 @@ define void @cookie(i32 %n) sspstrong {
; CHECK-NEXT: call sink
; CHECK-NEXT: ld a0, %lo(__stack_chk_guard)(s0)
; CHECK-NEXT: ld a1, 40(sp)
-; CHECK-NEXT: bne a0, a1, .LBB3_2
+; CHECK-NEXT: bne a0, a1, .LBB5_2
; CHECK-NEXT: # %bb.1:
; CHECK-NEXT: ld ra, 56(sp) # 8-byte Folded Reload
; CHECK-NEXT: ld s0, 48(sp) # 8-byte Folded Reload
@@ -105,7 +145,7 @@ define void @cookie(i32 %n) sspstrong {
; CHECK-NEXT: addi sp, sp, 64
; CHECK-NEXT: .cfi_def_cfa_offset 0
; CHECK-NEXT: ret
-; CHECK-NEXT: .LBB3_2:
+; CHECK-NEXT: .LBB5_2:
; CHECK-NEXT: .cfi_restore_state
; CHECK-NEXT: call __stack_chk_fail
%scalar = alloca i32, align 4
diff --git a/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll b/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll
index 41af3da7ed3a4e..7fe1c57696425d 100644
--- a/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll
+++ b/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll
@@ -2,7 +2,8 @@
; RUN: llc -mtriple=riscv64 -O0 -global-isel -stop-after=prolog-epilog -o - %s | FileCheck %s
; Check that with "stack-protector-layout-only" the stack objects are ordered
-; as if there were a stack protector, but one is not inserted.
+; as if there were a stack protector, but one is not inserted, and the return
+; address is spilled.
declare void @use(ptr, ptr, ptr)
declare void @sink(i32)
@@ -48,10 +49,26 @@ define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
ret void
}
-; Nothing to lay out, so stackProtectorLayoutOnly stays false.
+; CHECK-LABEL: name: layout_leaf
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: large, type: default,
+; CHECK: - { id: 1, name: '', type: spill-slot,
+; CHECK: callee-saved-register: '$x1'
+define void @layout_leaf(i8 %c) sspstrong "stack-protector-layout-only" {
+ %large = alloca [16 x i8], align 16
+ store volatile i8 %c, ptr %large
+ ret void
+}
+
+; Nothing to lay out, but a stack protector would have been inserted.
; CHECK-LABEL: name: req_empty
; CHECK: stackProtector: ''
-; CHECK-NEXT: stackProtectorLayoutOnly: false
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK: stack:
+; CHECK-NEXT: - { id: 0, name: '', type: spill-slot,
+; CHECK: callee-saved-register: '$x1'
define void @req_empty() sspreq "stack-protector-layout-only" {
ret void
}
@@ -77,6 +94,19 @@ define void @control(i32 %n) {
ret void
}
+; Control: without a stack protector attribute the return address is not
+; spilled.
+; CHECK-LABEL: name: control_leaf
+; CHECK: stackProtector: ''
+; CHECK-NEXT: stackProtectorLayoutOnly: false
+; CHECK-NOT: callee-saved-register: '$x1'
+; CHECK: body:
+define void @control_leaf(i8 %c) {
+ %large = alloca [16 x i8], align 16
+ store volatile i8 %c, ptr %large
+ ret void
+}
+
; Control: without the attribute a stack protector is still inserted.
; CHECK-LABEL: name: cookie
; CHECK: stackProtector: '%stack.{{[0-9]+}}.StackGuardSlot'
diff --git a/llvm/test/CodeGen/X86/stack-protector-layout-only.ll b/llvm/test/CodeGen/X86/stack-protector-layout-only.ll
index 809481bfa7d62d..0f677f89941d6f 100644
--- a/llvm/test/CodeGen/X86/stack-protector-layout-only.ll
+++ b/llvm/test/CodeGen/X86/stack-protector-layout-only.ll
@@ -48,10 +48,13 @@ define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" {
ret void
}
-; Nothing to lay out, so stackProtectorLayoutOnly stays false.
+; Nothing to lay out, but a stack protector would have been inserted. x86 has
+; no return address register to spill.
; CHECK-LABEL: name: req_empty
; CHECK: stackProtector: ''
-; CHECK-NEXT: stackProtectorLayoutOnly: false
+; CHECK-NEXT: stackProtectorLayoutOnly: true
+; CHECK-NOT: callee-saved-register
+; CHECK: body:
define void @req_empty() sspreq "stack-protector-layout-only" {
ret void
}
More information about the cfe-commits
mailing list