[sancov] Introduce optional callback for stack-depth tracking (#138323)
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.
This commit is contained in:
@@ -385,6 +385,50 @@ Users need to implement a single function to capture the CF table at startup:
|
||||
// the collected control flow.
|
||||
}
|
||||
|
||||
Tracing Stack Depth
|
||||
===================
|
||||
|
||||
With ``-fsanitize-coverage=stack-depth`` the compiler will track how much
|
||||
stack space has been used for a function call chain. Leaf functions are
|
||||
not included in this tracing.
|
||||
|
||||
The maximum depth of a function call graph is stored in the thread-local
|
||||
``__sancov_lowest_stack`` variable. Instrumentation is inserted in every
|
||||
non-leaf function to check the frame pointer against this variable,
|
||||
and if it is lower, store the current frame pointer. This effectively
|
||||
inserts the following:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
extern thread_local uintptr_t __sancov_lowest_stack;
|
||||
|
||||
uintptr_t stack = (uintptr_t)__builtin_frame_address(0);
|
||||
if (stack < __sancov_lowest_stack)
|
||||
__sancov_lowest_stack = stack;
|
||||
|
||||
If ``-fsanitize-coverage-stack-depth-callback-min=N`` (where
|
||||
``N > 0``) is also used, the tracking is delegated to a callback,
|
||||
``__sanitizer_cov_stack_depth``, instead of adding instrumentation to
|
||||
update ``__sancov_lowest_stack``. The ``N`` of the argument is used
|
||||
to determine which functions to instrument. Only functions estimated
|
||||
to be using ``N`` bytes or more of stack space will be instrumented to
|
||||
call the tracing callback. In the case of a dynamically sized stack,
|
||||
the callback is unconditionally added.
|
||||
|
||||
The callback takes no arguments and is responsible for determining
|
||||
the stack usage and doing any needed comparisons and storage. A roughly
|
||||
equivalent implementation of ``__sancov_lowest_stack`` using the callback
|
||||
would look like this:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void __sanitizer_cov_stack_depth(void) {
|
||||
uintptr_t stack = (uintptr_t)__builtin_frame_address(0);
|
||||
|
||||
if (stack < __sancov_lowest_stack)
|
||||
__sancov_lowest_stack = stack;
|
||||
}
|
||||
|
||||
Gated Trace Callbacks
|
||||
=====================
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -2365,6 +2365,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">;
|
||||
|
||||
@@ -34,6 +34,7 @@ class SanitizerArgs {
|
||||
std::vector<std::string> CoverageIgnorelistFiles;
|
||||
std::vector<std::string> BinaryMetadataIgnorelistFiles;
|
||||
int CoverageFeatures = 0;
|
||||
int CoverageStackDepthCallbackMin = 0;
|
||||
int BinaryMetadataFeatures = 0;
|
||||
int OverflowPatternExclusions = 0;
|
||||
int MsanTrackOrigins = 0;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -751,6 +751,17 @@ 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, CoverageStackDepthCallbackMin) ||
|
||||
CoverageStackDepthCallbackMin < 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 +1280,11 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
|
||||
addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-coverage-ignorelist=",
|
||||
CoverageIgnorelistFiles);
|
||||
|
||||
if (CoverageStackDepthCallbackMin)
|
||||
CmdArgs.push_back(
|
||||
Args.MakeArgString("-fsanitize-coverage-stack-depth-callback-min=" +
|
||||
Twine(CoverageStackDepthCallbackMin)));
|
||||
|
||||
if (!GPUSanitize) {
|
||||
// Translate available BinaryMetadataFeatures to corresponding clang-cc1
|
||||
// flags. Does not depend on any other sanitizers. Unsupported on GPUs.
|
||||
|
||||
@@ -93,6 +93,20 @@
|
||||
// CHECK-STACK-DEPTH-PC-GUARD: -fsanitize-coverage-trace-pc-guard
|
||||
// CHECK-STACK-DEPTH-PC-GUARD: -fsanitize-coverage-stack-depth
|
||||
|
||||
// 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: not %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-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' in '-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:
|
||||
// CHECK-NO-TYPE-NECESSARY: -fsanitize-coverage-indirect-calls
|
||||
|
||||
@@ -162,6 +162,7 @@ struct SanitizerCoverageOptions {
|
||||
bool TraceStores = false;
|
||||
bool CollectControlFlow = false;
|
||||
bool GatedCallbacks = false;
|
||||
int StackDepthCallbackMin = 0;
|
||||
|
||||
SanitizerCoverageOptions() = default;
|
||||
};
|
||||
|
||||
@@ -86,6 +86,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 +153,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 +209,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 +280,7 @@ private:
|
||||
DomTreeCallback DTCallback;
|
||||
PostDomTreeCallback PDTCallback;
|
||||
|
||||
FunctionCallee SanCovStackDepthCallback;
|
||||
FunctionCallee SanCovTracePCIndir;
|
||||
FunctionCallee SanCovTracePC, SanCovTracePCGuard;
|
||||
std::array<FunctionCallee, 4> SanCovTraceCmpFunction;
|
||||
@@ -514,6 +524,9 @@ bool ModuleSanitizerCoverage::instrumentModule() {
|
||||
SanCovTracePCGuard =
|
||||
M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, PtrTy);
|
||||
|
||||
SanCovStackDepthCallback =
|
||||
M.getOrInsertFunction(SanCovStackDepthCallbackName, VoidTy);
|
||||
|
||||
for (auto &F : M)
|
||||
instrumentFunction(F);
|
||||
|
||||
@@ -1078,22 +1091,65 @@ 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();
|
||||
const DataLayout &DL = M->getDataLayout();
|
||||
|
||||
if (Options.StackDepthCallbackMin) {
|
||||
// In callback mode, only add call when stack depth reaches minimum.
|
||||
uint32_t EstimatedStackSize = 0;
|
||||
// If dynamic alloca found, always add call.
|
||||
bool HasDynamicAlloc = false;
|
||||
// Find an insertion point after last "alloca".
|
||||
llvm::Instruction *InsertBefore = nullptr;
|
||||
|
||||
// Examine all allocas in the basic block. since we're too early
|
||||
// to have results from Intrinsic::frameaddress, we have to manually
|
||||
// estimate the stack size.
|
||||
for (auto &I : BB) {
|
||||
if (auto *AI = dyn_cast<AllocaInst>(&I)) {
|
||||
// Move potential insertion point past the "alloca".
|
||||
InsertBefore = AI->getNextNode();
|
||||
|
||||
// Make an estimate on the stack usage.
|
||||
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();
|
||||
} else {
|
||||
HasDynamicAlloc = true;
|
||||
}
|
||||
}
|
||||
EstimatedStackSize += Bytes;
|
||||
} else {
|
||||
HasDynamicAlloc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (HasDynamicAlloc ||
|
||||
EstimatedStackSize >= Options.StackDepthCallbackMin) {
|
||||
if (InsertBefore)
|
||||
IRB.SetInsertPoint(InsertBefore);
|
||||
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(DL.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
; 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:
|
||||
; COMMON-NEXT: [[VAR:%.*]] = alloca i8, i32 %input, 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-NEXT: call void @__sanitizer_cov_stack_depth()
|
||||
; 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 {
|
||||
%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
|
||||
; COMMON-LABEL: %7 = call ptr @llvm.stacksave
|
||||
; COMMON-NEXT: store ptr %7, ptr %3, align 8
|
||||
; COMMON-NEXT: [[VAR:%.*]] = alloca i32, i64 %6, align 16
|
||||
; 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()
|
||||
%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
|
||||
}
|
||||
Reference in New Issue
Block a user