https://github.com/amosher-nvidia created 
https://github.com/llvm/llvm-project/pull/231651

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.

This is useful on architectures which have hardware features that can act like 
stack protectors like Zicfiss on RISC-V, where any return addresses spilled to 
the stack effectively act as stack cookies.

Changes to 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.

Changes to 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.

>From 42f9153ab11172ff65310de3946b0f3eb230fce9 Mon Sep 17 00:00:00 2001
From: Avery Mosher <[email protected]>
Date: Fri, 18 Sep 2026 18:13:38 -0700
Subject: [PATCH] [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 1dc1764978ea2..23f30097f5985 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -290,6 +290,12 @@ features cannot lower the translation-unit ABI level;
 - 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 96d1c4e009c71..a310890a58fcb 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 10d9a4e3a6a82..fbe34acf667e5 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 1adc5f8399e4b..7d0ea985436c5 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4626,6 +4626,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 bce2106a74988..a7caf8abf7e04 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3186,6 +3186,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 fb6effb65d71f..20adb4d67f30f 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 9b93f775ffe79..b8996c02ff33b 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 e082b3361d058..f258ada07d162 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 84af4a727c223..82ddd5c6db533 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -2841,6 +2841,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 cec21851f0d76..90c76bb84e62d 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
@@ -387,6 +391,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 ab1c91d9f0de5..2d09ad29aedac 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 30699c3c18f1c..67308abe03667 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 2d6dc76a65fe8..d627ae111322d 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 9cf268ce8ce4f..33b99587ce027 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -1561,7 +1561,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 a388413525c23..697b9bae707b3 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 26af6ef073082..1c441f074a666 100644
--- a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
@@ -958,6 +958,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 def751e6df6bb..87bf7af1cddf4 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 49fa0f89d616c..ef0c36398fbc4 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 9ec1d0630952d..f03518999b10e 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 acff5e19b06d7..8fb2d08c5c0be 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 0000000000000..1f6c92fd0895d
--- /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 0000000000000..533c03d06aeeb
--- /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 9879559e5e85a..175bc56453154 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 0000000000000..a8253555d6522
--- /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 0000000000000..41af3da7ed3a4
--- /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 0000000000000..f96c909ce4a4d
--- /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 0000000000000..809481bfa7d62
--- /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 0000000000000..f9269463b0679
--- /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 0000000000000..c63f2084c7efe
--- /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 0000000000000..eb82ab9f8798f
--- /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 6b202aec3e8f8..ec960361125d7 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());

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to