[clang] [llvm] [sancov] Introduce optional callback for stack-depth tracking (PR #138323)
Kees Cook via cfe-commits
cfe-commits at lists.llvm.org
Sun May 4 20:08:21 PDT 2025
https://github.com/kees updated https://github.com/llvm/llvm-project/pull/138323
>From 729d249d3848c2e417174ed935bfefde5c048dc7 Mon Sep 17 00:00:00 2001
From: Kees Cook <kees at kernel.org>
Date: Fri, 2 May 2025 10:51:02 -0700
Subject: [PATCH] [sancov] Introduce optional callback for stack-depth tracking
Normally -fsanitize-coverage=stack-depth inserts inline arithmetic to
update thread_local __sancov_lowest_stack. To support stack depth
tracking in the Linux kernel, which does not implement traditional
thread_local storage, provide the option to call a function instead.
This matches the existing "stackleak" implementation that is supported
in Linux via a GCC plugin. To make this coverage more performant, a
minimum estimated stack depth can be chosen to enable the callback mode,
skipping instrumentation of functions with smaller stacks.
With -fsanitize-coverage-stack-depth-callback-min set greater than 0,
the __sanitize_cov_stack_depth() callback will be injected when the
estimated stack depth is greater than or equal to the given minimum.
---
clang/include/clang/Basic/CodeGenOptions.def | 1 +
clang/include/clang/Driver/Options.td | 7 +
clang/include/clang/Driver/SanitizerArgs.h | 1 +
clang/lib/CodeGen/BackendUtil.cpp | 1 +
clang/lib/Driver/SanitizerArgs.cpp | 15 ++
clang/test/Driver/fsanitize-coverage.c | 13 +
.../llvm/Transforms/Utils/Instrumentation.h | 1 +
.../Instrumentation/SanitizerCoverage.cpp | 74 +++--
.../SanitizerCoverage/stack-depth-callback.ll | 254 ++++++++++++++++++
9 files changed, 352 insertions(+), 15 deletions(-)
create mode 100644 llvm/test/Instrumentation/SanitizerCoverage/stack-depth-callback.ll
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 927972015c3dc..452b1e325afb2 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -305,6 +305,7 @@ CODEGENOPT(SanitizeCoveragePCTable, 1, 0) ///< Create a PC Table.
CODEGENOPT(SanitizeCoverageControlFlow, 1, 0) ///< Collect control flow
CODEGENOPT(SanitizeCoverageNoPrune, 1, 0) ///< Disable coverage pruning.
CODEGENOPT(SanitizeCoverageStackDepth, 1, 0) ///< Enable max stack depth tracing
+VALUE_CODEGENOPT(SanitizeCoverageStackDepthCallbackMin , 32, 0) ///< Enable stack depth tracing callbacks.
CODEGENOPT(SanitizeCoverageTraceLoads, 1, 0) ///< Enable tracing of loads.
CODEGENOPT(SanitizeCoverageTraceStores, 1, 0) ///< Enable tracing of stores.
CODEGENOPT(SanitizeBinaryMetadataCovered, 1, 0) ///< Emit PCs for covered functions.
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 561b0498c549c..251d20d0b1984 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2361,6 +2361,13 @@ def fsanitize_coverage_ignorelist : Joined<["-"], "fsanitize-coverage-ignorelist
HelpText<"Disable sanitizer coverage instrumentation for modules and functions "
"that match the provided special case list, even the allowed ones">,
MarshallingInfoStringVector<CodeGenOpts<"SanitizeCoverageIgnorelistFiles">>;
+def fsanitize_coverage_stack_depth_callback_min_EQ
+ : Joined<["-"], "fsanitize-coverage-stack-depth-callback-min=">,
+ Group<f_clang_Group>,
+ MetaVarName<"<M>">,
+ HelpText<"Use callback for max stack depth tracing with minimum stack "
+ "depth M">,
+ MarshallingInfoInt<CodeGenOpts<"SanitizeCoverageStackDepthCallbackMin">>;
def fexperimental_sanitize_metadata_EQ : CommaJoined<["-"], "fexperimental-sanitize-metadata=">,
Group<f_Group>,
HelpText<"Specify the type of metadata to emit for binary analysis sanitizers">;
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 528e3b400f3dc..1213cd8dcd3b6 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -34,6 +34,7 @@ class SanitizerArgs {
std::vector<std::string> CoverageIgnorelistFiles;
std::vector<std::string> BinaryMetadataIgnorelistFiles;
int CoverageFeatures = 0;
+ int StackDepthCallbackMin = 0;
int BinaryMetadataFeatures = 0;
int OverflowPatternExclusions = 0;
int MsanTrackOrigins = 0;
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index c9ceb49ce5ceb..42c59377688b2 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -255,6 +255,7 @@ getSancovOptsFromCGOpts(const CodeGenOptions &CGOpts) {
Opts.InlineBoolFlag = CGOpts.SanitizeCoverageInlineBoolFlag;
Opts.PCTable = CGOpts.SanitizeCoveragePCTable;
Opts.StackDepth = CGOpts.SanitizeCoverageStackDepth;
+ Opts.StackDepthCallbackMin = CGOpts.SanitizeCoverageStackDepthCallbackMin;
Opts.TraceLoads = CGOpts.SanitizeCoverageTraceLoads;
Opts.TraceStores = CGOpts.SanitizeCoverageTraceStores;
Opts.CollectControlFlow = CGOpts.SanitizeCoverageControlFlow;
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index ff08bffdbde1f..da4ea6293f2ba 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -751,6 +751,16 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
options::OPT_fno_sanitize_ignorelist,
clang::diag::err_drv_malformed_sanitizer_ignorelist, DiagnoseErrors);
+ // Verify that -fsanitize-coverage-stack-depth-callback-min is >= 0.
+ if (Arg *A = Args.getLastArg(
+ options::OPT_fsanitize_coverage_stack_depth_callback_min_EQ)) {
+ StringRef S = A->getValue();
+ if (S.getAsInteger(0, StackDepthCallbackMin) || StackDepthCallbackMin < 0) {
+ if (DiagnoseErrors)
+ D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
+ }
+ }
+
// Parse -f[no-]sanitize-memory-track-origins[=level] options.
if (AllAddedKinds & SanitizerKind::Memory) {
if (Arg *A =
@@ -1269,6 +1279,11 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-coverage-ignorelist=",
CoverageIgnorelistFiles);
+ if (StackDepthCallbackMin)
+ CmdArgs.push_back(
+ Args.MakeArgString("-fsanitize-coverage-stack-depth-callback-min=" +
+ Twine(StackDepthCallbackMin)));
+
if (!GPUSanitize) {
// Translate available BinaryMetadataFeatures to corresponding clang-cc1
// flags. Does not depend on any other sanitizers. Unsupported on GPUs.
diff --git a/clang/test/Driver/fsanitize-coverage.c b/clang/test/Driver/fsanitize-coverage.c
index dc4c39396d45c..a0da4c7b3454c 100644
--- a/clang/test/Driver/fsanitize-coverage.c
+++ b/clang/test/Driver/fsanitize-coverage.c
@@ -87,11 +87,24 @@
// RUN: %clang --target=x86_64-linux-gnu \
// RUN: -fsanitize-coverage=trace-pc-guard,stack-depth %s -### 2>&1 | \
// RUN: FileCheck %s --check-prefix=CHECK-STACK-DEPTH-PC-GUARD
+// RUN: %clang --target=x86_64-linux-gnu \
+// RUN: -fsanitize-coverage-stack-depth-callback-min=100 %s -### 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CHECK-STACK-DEPTH-CALLBACK
+// RUN: %clang --target=x86_64-linux-gnu \
+// RUN: -fsanitize-coverage-stack-depth-callback-min=0 %s -### 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CHECK-STACK-DEPTH-CALLBACK-ZERO
+// RUN: %clang --target=x86_64-linux-gnu \
+// RUN: -fsanitize-coverage-stack-depth-callback-min=-10 %s -### 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CHECK-STACK-DEPTH-CALLBACK-NEGATIVE
// CHECK-STACK-DEPTH: -fsanitize-coverage-type=1
// CHECK-STACK-DEPTH: -fsanitize-coverage-stack-depth
// CHECK-STACK-DEPTH-PC-GUARD: -fsanitize-coverage-type=3
// CHECK-STACK-DEPTH-PC-GUARD: -fsanitize-coverage-trace-pc-guard
// CHECK-STACK-DEPTH-PC-GUARD: -fsanitize-coverage-stack-depth
+// CHECK-STACK-DEPTH-CALLBACK-NOT: error:
+// CHECK-STACK-DEPTH-CALLBACK: -fsanitize-coverage-stack-depth-callback-min=100
+// CHECK-STACK-DEPTH-CALLBACK-ZERO-NOT: -fsanitize-coverage-stack-depth-callback-min=0
+// CHECK-STACK-DEPTH-CALLBACK-NEGATIVE: error: invalid value '-10' not allowed with '-fsanitize-coverage-stack-depth-callback-min=-10'
// RUN: %clang --target=x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=trace-cmp,indirect-calls %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-TYPE-NECESSARY
// CHECK-NO-TYPE-NECESSARY-NOT: error:
diff --git a/llvm/include/llvm/Transforms/Utils/Instrumentation.h b/llvm/include/llvm/Transforms/Utils/Instrumentation.h
index 0e2c0d9bfa605..f2bc6854ae5b6 100644
--- a/llvm/include/llvm/Transforms/Utils/Instrumentation.h
+++ b/llvm/include/llvm/Transforms/Utils/Instrumentation.h
@@ -158,6 +158,7 @@ struct SanitizerCoverageOptions {
bool PCTable = false;
bool NoPrune = false;
bool StackDepth = false;
+ int StackDepthCallbackMin = 0;
bool TraceLoads = false;
bool TraceStores = false;
bool CollectControlFlow = false;
diff --git a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
index e52269637b92d..e54afb1fdc8a7 100644
--- a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
+++ b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
@@ -33,6 +33,7 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/SpecialCaseList.h"
#include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
@@ -86,6 +87,7 @@ const char SanCovPCsSectionName[] = "sancov_pcs";
const char SanCovCFsSectionName[] = "sancov_cfs";
const char SanCovCallbackGateSectionName[] = "sancov_gate";
+const char SanCovStackDepthCallbackName[] = "__sanitizer_cov_stack_depth";
const char SanCovLowestStackName[] = "__sancov_lowest_stack";
const char SanCovCallbackGateName[] = "__sancov_should_track";
@@ -152,6 +154,12 @@ static cl::opt<bool> ClStackDepth("sanitizer-coverage-stack-depth",
cl::desc("max stack depth tracing"),
cl::Hidden);
+static cl::opt<int> ClStackDepthCallbackMin(
+ "sanitizer-coverage-stack-depth-callback-min",
+ cl::desc("max stack depth tracing should use callback and only when "
+ "stack depth more than specified"),
+ cl::Hidden);
+
static cl::opt<bool>
ClCollectCF("sanitizer-coverage-control-flow",
cl::desc("collect control flow for each function"), cl::Hidden);
@@ -202,6 +210,8 @@ SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) {
Options.PCTable |= ClCreatePCTable;
Options.NoPrune |= !ClPruneBlocks;
Options.StackDepth |= ClStackDepth;
+ Options.StackDepthCallbackMin = std::max(Options.StackDepthCallbackMin,
+ ClStackDepthCallbackMin.getValue());
Options.TraceLoads |= ClLoadTracing;
Options.TraceStores |= ClStoreTracing;
Options.GatedCallbacks |= ClGatedCallbacks;
@@ -271,6 +281,7 @@ class ModuleSanitizerCoverage {
DomTreeCallback DTCallback;
PostDomTreeCallback PDTCallback;
+ FunctionCallee SanCovStackDepthCallback;
FunctionCallee SanCovTracePCIndir;
FunctionCallee SanCovTracePC, SanCovTracePCGuard;
std::array<FunctionCallee, 4> SanCovTraceCmpFunction;
@@ -514,6 +525,9 @@ bool ModuleSanitizerCoverage::instrumentModule() {
SanCovTracePCGuard =
M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, PtrTy);
+ SanCovStackDepthCallback =
+ M.getOrInsertFunction(SanCovStackDepthCallbackName, VoidTy);
+
for (auto &F : M)
instrumentFunction(F);
@@ -1029,6 +1043,8 @@ void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
if (IsEntryBB) {
if (auto SP = F.getSubprogram())
EntryLoc = DILocation::get(SP->getContext(), SP->getScopeLine(), 0, SP);
+ // FIXME: stack-depth does not correctly instrument dynamic allocas.
+ //
// Keep static allocas and llvm.localescape calls in the entry block. Even
// if we aren't splitting the block, it's nice for allocas to be before
// calls.
@@ -1078,22 +1094,50 @@ void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
Store->setNoSanitizeMetadata();
}
if (Options.StackDepth && IsEntryBB && !IsLeafFunc) {
- // Check stack depth. If it's the deepest so far, record it.
Module *M = F.getParent();
- auto FrameAddrPtr = IRB.CreateIntrinsic(
- Intrinsic::frameaddress,
- IRB.getPtrTy(M->getDataLayout().getAllocaAddrSpace()),
- {Constant::getNullValue(Int32Ty)});
- auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy);
- auto LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack);
- auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack);
- auto ThenTerm = SplitBlockAndInsertIfThen(
- IsStackLower, &*IP, false,
- MDBuilder(IRB.getContext()).createUnlikelyBranchWeights());
- IRBuilder<> ThenIRB(ThenTerm);
- auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack);
- LowestStack->setNoSanitizeMetadata();
- Store->setNoSanitizeMetadata();
+ if (Options.StackDepthCallbackMin) {
+ // In callback mode, only add call when stack depth reaches minimum.
+ const DataLayout &DL = M->getDataLayout();
+ uint32_t EstimatedStackSize = 0;
+
+ // Make an estimate on the stack usage.
+ for (auto &I : F.getEntryBlock()) {
+ if (auto *AI = dyn_cast<AllocaInst>(&I)) {
+ if (AI->isStaticAlloca()) {
+ uint32_t Bytes = DL.getTypeAllocSize(AI->getAllocatedType());
+ if (AI->isArrayAllocation()) {
+ if (const ConstantInt *arraySize =
+ dyn_cast<ConstantInt>(AI->getArraySize()))
+ Bytes *= arraySize->getZExtValue();
+ }
+ EstimatedStackSize += Bytes;
+ } else {
+ // Dynamic alloca: require we always perform callback.
+ EstimatedStackSize = Options.StackDepthCallbackMin;
+ break;
+ }
+ }
+ }
+
+ if (EstimatedStackSize >= Options.StackDepthCallbackMin)
+ IRB.CreateCall(SanCovStackDepthCallback)->setCannotMerge();
+ } else {
+ // Check stack depth. If it's the deepest so far, record it.
+ auto FrameAddrPtr = IRB.CreateIntrinsic(
+ Intrinsic::frameaddress,
+ IRB.getPtrTy(M->getDataLayout().getAllocaAddrSpace()),
+ {Constant::getNullValue(Int32Ty)});
+ auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy);
+ auto LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack);
+ auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack);
+ auto ThenTerm = SplitBlockAndInsertIfThen(
+ IsStackLower, &*IP, false,
+ MDBuilder(IRB.getContext()).createUnlikelyBranchWeights());
+ IRBuilder<> ThenIRB(ThenTerm);
+ auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack);
+ LowestStack->setNoSanitizeMetadata();
+ Store->setNoSanitizeMetadata();
+ }
}
}
diff --git a/llvm/test/Instrumentation/SanitizerCoverage/stack-depth-callback.ll b/llvm/test/Instrumentation/SanitizerCoverage/stack-depth-callback.ll
new file mode 100644
index 0000000000000..6c9eabbfef894
--- /dev/null
+++ b/llvm/test/Instrumentation/SanitizerCoverage/stack-depth-callback.ll
@@ -0,0 +1,254 @@
+; This check verifies that stack depth callback instrumentation works correctly.
+; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-stack-depth -sanitizer-coverage-stack-depth-callback-min=1 -S | FileCheck %s --check-prefixes=COMMON,CB1
+; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-stack-depth -sanitizer-coverage-stack-depth-callback-min=8 -S | FileCheck %s --check-prefixes=COMMON,CB8
+; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-stack-depth -sanitizer-coverage-stack-depth-callback-min=16 -S | FileCheck %s --check-prefixes=COMMON,CB16
+; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-stack-depth -sanitizer-coverage-stack-depth-callback-min=32 -S | FileCheck %s --check-prefixes=COMMON,CB32
+; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-stack-depth -sanitizer-coverage-stack-depth-callback-min=64 -S | FileCheck %s --check-prefixes=COMMON,CB64
+; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-stack-depth -sanitizer-coverage-stack-depth-callback-min=128 -S | FileCheck %s --check-prefixes=COMMON,CB128
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; No stack, just return: our leaf function
+define i32 @foo() {
+; COMMON-LABEL: define i32 @foo() {
+; COMMON-NEXT: entry:
+; CB1-NOT: call void @__sanitizer_cov_stack_depth()
+; CB8-NOT: call void @__sanitizer_cov_stack_depth()
+; CB16-NOT: call void @__sanitizer_cov_stack_depth()
+; CB32-NOT: call void @__sanitizer_cov_stack_depth()
+; CB64-NOT: call void @__sanitizer_cov_stack_depth()
+; CB128-NOT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: ret i32 7
+;
+entry:
+
+ ret i32 7
+}
+
+; No stack, just function call
+define i32 @retcall() {
+; COMMON-LABEL: define i32 @retcall() {
+; COMMON-NEXT: entry:
+; CB1-NOT: call void @__sanitizer_cov_stack_depth()
+; CB8-NOT: call void @__sanitizer_cov_stack_depth()
+; CB16-NOT: call void @__sanitizer_cov_stack_depth()
+; CB32-NOT: call void @__sanitizer_cov_stack_depth()
+; CB64-NOT: call void @__sanitizer_cov_stack_depth()
+; CB128-NOT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: [[CALL:%.*]] = call i32 @foo()
+; COMMON-NEXT: ret i32 [[CALL]]
+entry:
+
+ %call = call i32 @foo()
+ ret i32 %call
+}
+
+; No stack, just function call, with argument
+define i32 @witharg(i32 %input) {
+; COMMON-LABEL: define i32 @witharg(i32 %input) {
+; COMMON-NEXT: entry:
+; CB1-NOT: call void @__sanitizer_cov_stack_depth()
+; CB8-NOT: call void @__sanitizer_cov_stack_depth()
+; CB16-NOT: call void @__sanitizer_cov_stack_depth()
+; CB32-NOT: call void @__sanitizer_cov_stack_depth()
+; CB64-NOT: call void @__sanitizer_cov_stack_depth()
+; CB128-NOT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: [[CALL:%.*]] = call i32 @foo()
+; COMMON-NEXT: ret i32 [[CALL]]
+entry:
+
+ %call = call i32 @foo()
+ ret i32 %call
+}
+
+; 4 byte stack of scalars
+define i32 @alloc4_0() {
+; COMMON-LABEL: define i32 @alloc4_0() {
+; COMMON-NEXT: entry:
+; COMMON-NEXT: [[VAR:%.*]] = alloca i32, align 4
+; CB1-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB8-NOT: call void @__sanitizer_cov_stack_depth()
+; CB16-NOT: call void @__sanitizer_cov_stack_depth()
+; CB32-NOT: call void @__sanitizer_cov_stack_depth()
+; CB64-NOT: call void @__sanitizer_cov_stack_depth()
+; CB128-NOT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: [[CALL:%.*]] = call i32 @foo()
+; COMMON-NEXT: ret i32 [[CALL]]
+entry:
+ %var1 = alloca i32, align 4
+
+ %call = call i32 @foo()
+ ret i32 %call
+}
+
+; 16 byte stack of scalars
+define i32 @alloc16_0() {
+; COMMON-LABEL: define i32 @alloc16_0() {
+; COMMON-NEXT: entry:
+; COMMON-NEXT: [[VAR:%.*]] = alloca i32, align 4
+; COMMON-NEXT: [[VAR:%.*]] = alloca i32, align 4
+; COMMON-NEXT: [[VAR:%.*]] = alloca i32, align 4
+; COMMON-NEXT: [[VAR:%.*]] = alloca i32, align 4
+; CB1-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB8-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB16-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB32-NOT: call void @__sanitizer_cov_stack_depth()
+; CB64-NOT: call void @__sanitizer_cov_stack_depth()
+; CB128-NOT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: [[CALL:%.*]] = call i32 @foo()
+; COMMON-NEXT: ret i32 [[CALL]]
+entry:
+ %var1 = alloca i32, align 4
+ %var2 = alloca i32, align 4
+ %var3 = alloca i32, align 4
+ %var4 = alloca i32, align 4
+
+ %call = call i32 @foo()
+ ret i32 %call
+}
+
+; 32 byte stack of scalars
+define i32 @alloc32_0() {
+; COMMON-LABEL: define i32 @alloc32_0() {
+; COMMON-NEXT: entry:
+; COMMON-NEXT: [[VAR:%.*]] = alloca i64, align 8
+; COMMON-NEXT: [[VAR:%.*]] = alloca i64, align 8
+; COMMON-NEXT: [[VAR:%.*]] = alloca i64, align 8
+; COMMON-NEXT: [[VAR:%.*]] = alloca i64, align 8
+; CB1-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB8-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB16-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB32-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB64-NOT: call void @__sanitizer_cov_stack_depth()
+; CB128-NOT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: [[CALL:%.*]] = call i32 @foo()
+; COMMON-NEXT: ret i32 [[CALL]]
+entry:
+ %var1 = alloca i64, align 8
+ %var2 = alloca i64, align 8
+ %var3 = alloca i64, align 8
+ %var4 = alloca i64, align 8
+
+ %call = call i32 @foo()
+ ret i32 %call
+}
+
+; 36 byte stack of 1 4 byte scalar and 1 32 byte array
+define i32 @alloc4_32x1() {
+; COMMON-LABEL: define i32 @alloc4_32x1() {
+; COMMON-NEXT: entry:
+; COMMON-NEXT: [[VAR:%.*]] = alloca i8, i32 32, align 4
+; COMMON-NEXT: [[VAR:%.*]] = alloca i32, align 4
+; CB1-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB8-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB16-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB32-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB64-NOT: call void @__sanitizer_cov_stack_depth()
+; CB128-NOT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: [[CALL:%.*]] = call i32 @foo()
+; COMMON-NEXT: ret i32 [[CALL]]
+entry:
+ %stack_array1 = alloca i8, i32 32, align 4
+ %var1 = alloca i32, align 4
+
+ %call = call i32 @foo()
+ ret i32 %call
+}
+
+; 64 byte stack of 2 32 byte arrays
+define i32 @alloc0_32x2() {
+; COMMON-LABEL: define i32 @alloc0_32x2() {
+; COMMON-NEXT: entry:
+; COMMON-NEXT: [[VAR:%.*]] = alloca i8, i32 32, align 4
+; COMMON-NEXT: [[VAR:%.*]] = alloca i8, i32 32, align 4
+; CB1-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB8-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB16-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB32-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB64-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB128-NOT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: [[CALL:%.*]] = call i32 @foo()
+; COMMON-NEXT: ret i32 [[CALL]]
+entry:
+ %stack_array1 = alloca i8, i32 32, align 4
+ %stack_array2 = alloca i8, i32 32, align 4
+
+ %call = call i32 @foo()
+ ret i32 %call
+}
+
+; 64 byte stack of 1 64 byte array
+define i32 @alloc0_64x1() {
+; COMMON-LABEL: define i32 @alloc0_64x1() {
+; COMMON-NEXT: entry:
+; COMMON-NEXT: [[VAR:%.*]] = alloca i8, i32 64, align 4
+; CB1-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB8-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB16-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB32-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB64-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB128-NOT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: [[CALL:%.*]] = call i32 @foo()
+; COMMON-NEXT: ret i32 [[CALL]]
+entry:
+ %stack_array = alloca i8, i32 64, align 4
+
+ %call = call i32 @foo()
+ ret i32 %call
+}
+
+; dynamic stack sized by i32
+define i32 @alloc0_32xDyn(i32 %input) {
+; COMMON-LABEL: define i32 @alloc0_32xDyn(i32 %input) {
+; COMMON-NEXT: entry:
+; CB1-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB8-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB16-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB32-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB64-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB128-NEXT: call void @__sanitizer_cov_stack_depth()
+; COMMON-NEXT: [[VAR:%.*]] = alloca i8, i32 %input, align 4
+; COMMON-NEXT: [[CALL:%.*]] = call i32 @foo()
+; COMMON-NEXT: ret i32 [[CALL]]
+entry:
+ %stack_array1 = alloca i8, i32 %input, align 4
+
+ %call = call i32 @foo()
+ ret i32 %call
+}
+
+; true dynamic stack sized by i32, from C:
+; static int dyamic_alloca(int size)
+; {
+; int array[size];
+; return foo();
+; }
+define dso_local i32 @dynamic_alloca(i32 noundef %0) #0 {
+; COMMON-LABEL: define dso_local i32 @dynamic_alloca(i32 noundef %0) {
+; COMMON-NEXT: [[VAR:%.*]] = alloca i32, align 4
+; COMMON-NEXT: [[VAR:%.*]] = alloca ptr, align 8
+; COMMON-NEXT: [[VAR:%.*]] = alloca i64, align 8
+; CB1-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB8-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB16-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB32-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB64-NEXT: call void @__sanitizer_cov_stack_depth()
+; CB128-NEXT: call void @__sanitizer_cov_stack_depth()
+ %2 = alloca i32, align 4
+ %3 = alloca ptr, align 8
+ %4 = alloca i64, align 8
+ store i32 %0, ptr %2, align 4
+ %5 = load i32, ptr %2, align 4
+ %6 = zext i32 %5 to i64
+ %7 = call ptr @llvm.stacksave.p0()
+ store ptr %7, ptr %3, align 8
+ %8 = alloca i32, i64 %6, align 16
+ store i64 %6, ptr %4, align 8
+ %9 = call i32 @foo()
+ %10 = load ptr, ptr %3, align 8
+; COMMON-LABEL: call void @llvm.stackrestore
+; COMMON-NEXT: ret i32 %9
+ call void @llvm.stackrestore.p0(ptr %10)
+ ret i32 %9
+}
More information about the cfe-commits
mailing list