Skip to content

Commit fd78f41

Browse files
author
git apple-llvm automerger
committed
Merge commit '606a7f316518' from swift/release/6.2 into stable/20240723
2 parents 0a184c2 + 606a7f3 commit fd78f41

File tree

12 files changed

+265
-30
lines changed

12 files changed

+265
-30
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,11 @@ Non-comprehensive list of changes in this release
451451
removed in Clang 20. ``__is_same(__remove_cv(T), decltype(nullptr))`` can be
452452
used instead to check whether a type ``T`` is a ``nullptr``.
453453

454+
- Clang itself now uses split stacks instead of threads for allocating more
455+
stack space when running on Apple AArch64 based platforms. This means that
456+
stack traces of Clang from debuggers, crashes, and profilers may look
457+
different than before.
458+
454459
New Compiler Flags
455460
------------------
456461
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and

clang/include/clang/Basic/Stack.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ namespace clang {
2727

2828
/// Call this once on each thread, as soon after starting the thread as
2929
/// feasible, to note the approximate address of the bottom of the stack.
30-
void noteBottomOfStack();
30+
///
31+
/// \param ForceSet set to true if you know the call is near the bottom of a
32+
/// new stack. Used for split stacks.
33+
void noteBottomOfStack(bool ForceSet = false);
3134

3235
/// Determine whether the stack is nearly exhausted.
3336
bool isStackNearlyExhausted();

clang/lib/Basic/Stack.cpp

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,13 @@
1313

1414
#include "clang/Basic/Stack.h"
1515
#include "llvm/Support/CrashRecoveryContext.h"
16+
#include "llvm/Support/ProgramStack.h"
1617

17-
#ifdef _MSC_VER
18-
#include <intrin.h> // for _AddressOfReturnAddress
19-
#endif
18+
static LLVM_THREAD_LOCAL uintptr_t BottomOfStack = 0;
2019

21-
static LLVM_THREAD_LOCAL void *BottomOfStack = nullptr;
22-
23-
static void *getStackPointer() {
24-
#if __GNUC__ || __has_builtin(__builtin_frame_address)
25-
return __builtin_frame_address(0);
26-
#elif defined(_MSC_VER)
27-
return _AddressOfReturnAddress();
28-
#else
29-
char CharOnStack = 0;
30-
// The volatile store here is intended to escape the local variable, to
31-
// prevent the compiler from optimizing CharOnStack into anything other
32-
// than a char on the stack.
33-
//
34-
// Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19.
35-
char *volatile Ptr = &CharOnStack;
36-
return Ptr;
37-
#endif
38-
}
39-
40-
void clang::noteBottomOfStack() {
41-
if (!BottomOfStack)
42-
BottomOfStack = getStackPointer();
20+
void clang::noteBottomOfStack(bool ForceSet) {
21+
if (!BottomOfStack || ForceSet)
22+
BottomOfStack = llvm::getStackPointer();
4323
}
4424

4525
bool clang::isStackNearlyExhausted() {
@@ -51,7 +31,8 @@ bool clang::isStackNearlyExhausted() {
5131
if (!BottomOfStack)
5232
return false;
5333

54-
intptr_t StackDiff = (intptr_t)getStackPointer() - (intptr_t)BottomOfStack;
34+
intptr_t StackDiff =
35+
(intptr_t)llvm::getStackPointer() - (intptr_t)BottomOfStack;
5536
size_t StackUsage = (size_t)std::abs(StackDiff);
5637

5738
// If the stack pointer has a surprising value, we do not understand this
@@ -66,9 +47,12 @@ bool clang::isStackNearlyExhausted() {
6647
void clang::runWithSufficientStackSpaceSlow(llvm::function_ref<void()> Diag,
6748
llvm::function_ref<void()> Fn) {
6849
llvm::CrashRecoveryContext CRC;
69-
CRC.RunSafelyOnThread([&] {
70-
noteBottomOfStack();
50+
// Preserve the BottomOfStack in case RunSafelyOnNewStack uses split stacks.
51+
uintptr_t PrevBottom = BottomOfStack;
52+
CRC.RunSafelyOnNewStack([&] {
53+
noteBottomOfStack(true);
7154
Diag();
7255
Fn();
7356
}, DesiredStackSize);
57+
BottomOfStack = PrevBottom;
7458
}

clang/lib/Frontend/CompilerInstance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1421,7 +1421,7 @@ compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc,
14211421

14221422
// Execute the action to actually build the module in-place. Use a separate
14231423
// thread so that we get a stack large enough.
1424-
bool Crashed = !llvm::CrashRecoveryContext().RunSafelyOnThread(
1424+
bool Crashed = !llvm::CrashRecoveryContext().RunSafelyOnNewStack(
14251425
[&]() {
14261426
std::unique_ptr<FrontendAction> Action(
14271427
new GenerateModuleFromModuleMapAction);

llvm/include/llvm/Support/CrashRecoveryContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ class CrashRecoveryContext {
9797
return RunSafelyOnThread([&]() { Fn(UserData); }, RequestedStackSize);
9898
}
9999

100+
bool RunSafelyOnNewStack(function_ref<void()>,
101+
unsigned RequestedStackSize = 0);
102+
100103
/// Explicitly trigger a crash recovery in the current process, and
101104
/// return failure from RunSafely(). This function does not return.
102105
[[noreturn]] void HandleExit(int RetCode);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===--- ProgramStack.h -----------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_SUPPORT_PROGRAMSTACK_H
10+
#define LLVM_SUPPORT_PROGRAMSTACK_H
11+
12+
#include "llvm/ADT/STLFunctionalExtras.h"
13+
#include "llvm/Support/Compiler.h"
14+
15+
// LLVM_HAS_SPLIT_STACKS is exposed in the header because CrashRecoveryContext
16+
// needs to know if it's running on another thread or not.
17+
//
18+
// Currently only Apple AArch64 is known to support split stacks in the debugger
19+
// and other tooling.
20+
#if defined(__APPLE__) && defined(__MACH__) && defined(__aarch64__) && \
21+
__has_extension(gnu_asm)
22+
# define LLVM_HAS_SPLIT_STACKS
23+
# define LLVM_HAS_SPLIT_STACKS_AARCH64
24+
#endif
25+
26+
namespace llvm {
27+
28+
/// \returns an address close to the current value of the stack pointer.
29+
///
30+
/// The value is not guaranteed to point to anything specific. It can be used to
31+
/// estimate how much stack space has been used since the previous call.
32+
uintptr_t getStackPointer();
33+
34+
/// \returns the default stack size for this platform.
35+
///
36+
/// Based on \p RLIMIT_STACK or the equivalent.
37+
unsigned getDefaultStackSize();
38+
39+
/// Runs Fn on a new stack of at least the given size.
40+
///
41+
/// \param StackSize requested stack size. A size of 0 uses the default stack
42+
/// size of the platform.
43+
///
44+
/// The preferred implementation is split stacks on platforms that have a good
45+
/// debugging experience for them. On other platforms a new thread is used.
46+
void runOnNewStack(unsigned StackSize, function_ref<void()> Fn);
47+
48+
template <typename R, typename... Ts>
49+
std::enable_if_t<!std::is_same_v<R, void>, R>
50+
runOnNewStack(unsigned StackSize, function_ref<R(Ts...)> Fn, Ts &&...Args) {
51+
std::optional<R> Ret;
52+
runOnNewStack(StackSize, [&]() { Ret = Fn(std::forward<Ts>(Args)...); });
53+
return std::move(*Ret);
54+
}
55+
56+
template <typename... Ts>
57+
void runOnNewStack(unsigned StackSize, function_ref<void(Ts...)> Fn,
58+
Ts &&...Args) {
59+
runOnNewStack(StackSize, [&]() { Fn(std::forward<Ts>(Args)...); });
60+
}
61+
62+
} // namespace llvm
63+
64+
#endif // LLVM_SUPPORT_PROGRAMSTACK_H

llvm/include/llvm/Support/thread.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ namespace this_thread {
211211

212212
#else // !LLVM_ENABLE_THREADS
213213

214+
#include "llvm/Support/ErrorHandling.h"
214215
#include <utility>
215216

216217
namespace llvm {

llvm/lib/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ add_llvm_component_library(LLVMSupport
285285
Path.cpp
286286
Process.cpp
287287
Program.cpp
288+
ProgramStack.cpp
288289
RWMutex.cpp
289290
Signals.cpp
290291
Threading.cpp

llvm/lib/Support/CrashRecoveryContext.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "llvm/Config/llvm-config.h"
1111
#include "llvm/Support/ErrorHandling.h"
1212
#include "llvm/Support/ExitCodes.h"
13+
#include "llvm/Support/ProgramStack.h"
1314
#include "llvm/Support/Signals.h"
1415
#include "llvm/Support/thread.h"
1516
#include <cassert>
@@ -523,3 +524,13 @@ bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn,
523524
CRC->setSwitchedThread();
524525
return Info.Result;
525526
}
527+
528+
bool CrashRecoveryContext::RunSafelyOnNewStack(function_ref<void()> Fn,
529+
unsigned RequestedStackSize) {
530+
#ifdef LLVM_HAS_SPLIT_STACKS
531+
return runOnNewStack(RequestedStackSize,
532+
function_ref<bool()>([&]() { return RunSafely(Fn); }));
533+
#else
534+
return RunSafelyOnThread(Fn, RequestedStackSize);
535+
#endif
536+
}

llvm/lib/Support/ProgramStack.cpp

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//===--- RunOnNewStack.cpp - Crash Recovery -------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Support/ProgramStack.h"
10+
#include "llvm/Config/config.h"
11+
#include "llvm/Support/Compiler.h"
12+
13+
#ifdef LLVM_ON_UNIX
14+
# include <sys/resource.h> // for getrlimit
15+
#endif
16+
17+
#ifdef _MSC_VER
18+
# include <intrin.h> // for _AddressOfReturnAddress
19+
#endif
20+
21+
#ifndef LLVM_HAS_SPLIT_STACKS
22+
# include "llvm/Support/thread.h"
23+
#endif
24+
25+
using namespace llvm;
26+
27+
uintptr_t llvm::getStackPointer() {
28+
#if __GNUC__ || __has_builtin(__builtin_frame_address)
29+
return (uintptr_t)__builtin_frame_address(0);
30+
#elif defined(_MSC_VER)
31+
return (uintptr_t)_AddressOfReturnAddress();
32+
#else
33+
volatile char CharOnStack = 0;
34+
// The volatile store here is intended to escape the local variable, to
35+
// prevent the compiler from optimizing CharOnStack into anything other
36+
// than a char on the stack.
37+
//
38+
// Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19.
39+
char *volatile Ptr = &CharOnStack;
40+
return (uintptr_t)Ptr;
41+
#endif
42+
}
43+
44+
unsigned llvm::getDefaultStackSize() {
45+
#ifdef LLVM_ON_UNIX
46+
rlimit RL;
47+
getrlimit(RLIMIT_STACK, &RL);
48+
return RL.rlim_cur;
49+
#else
50+
// Clang recursively parses, instantiates templates, and evaluates constant
51+
// expressions. We've found 8MiB to be a reasonable stack size given the way
52+
// Clang works and the way C++ is commonly written.
53+
return 8 << 20;
54+
#endif
55+
}
56+
57+
// Not an anonymous namespace to avoid warning about undefined local function.
58+
namespace llvm {
59+
#ifdef LLVM_HAS_SPLIT_STACKS_AARCH64
60+
void runOnNewStackImpl(void *Stack, void (*Fn)(void *), void *Ctx) __asm__(
61+
"_ZN4llvm17runOnNewStackImplEPvPFvS0_ES0_");
62+
63+
// This can't use naked functions because there is no way to know if cfi
64+
// directives are being emitted or not.
65+
//
66+
// When adding new platforms it may be better to move to a .S file with macros
67+
// for dealing with platform differences.
68+
__asm__ (
69+
".globl _ZN4llvm17runOnNewStackImplEPvPFvS0_ES0_\n\t"
70+
".p2align 2\n\t"
71+
"_ZN4llvm17runOnNewStackImplEPvPFvS0_ES0_:\n\t"
72+
".cfi_startproc\n\t"
73+
"mov x16, sp\n\t"
74+
"sub x0, x0, #0x20\n\t" // subtract space from stack
75+
"stp xzr, x16, [x0, #0x00]\n\t" // save old sp
76+
"stp x29, x30, [x0, #0x10]\n\t" // save fp, lr
77+
"mov sp, x0\n\t" // switch to new stack
78+
"add x29, x0, #0x10\n\t" // switch to new frame
79+
".cfi_def_cfa w29, 16\n\t"
80+
".cfi_offset w30, -8\n\t" // lr
81+
".cfi_offset w29, -16\n\t" // fp
82+
83+
"mov x0, x2\n\t" // Ctx is the only argument
84+
"blr x1\n\t" // call Fn
85+
86+
"ldp x29, x30, [sp, #0x10]\n\t" // restore fp, lr
87+
"ldp xzr, x16, [sp, #0x00]\n\t" // load old sp
88+
"mov sp, x16\n\t"
89+
"ret\n\t"
90+
".cfi_endproc"
91+
);
92+
#endif
93+
} // namespace llvm
94+
95+
namespace {
96+
#ifdef LLVM_HAS_SPLIT_STACKS
97+
void callback(void *Ctx) {
98+
(*reinterpret_cast<function_ref<void()> *>(Ctx))();
99+
}
100+
#endif
101+
} // namespace
102+
103+
#ifdef LLVM_HAS_SPLIT_STACKS
104+
void llvm::runOnNewStack(unsigned StackSize, function_ref<void()> Fn) {
105+
if (StackSize == 0)
106+
StackSize = getDefaultStackSize();
107+
108+
// We use malloc here instead of mmap because:
109+
// - it's simpler,
110+
// - many malloc implementations will reuse the allocation in cases where
111+
// we're bouncing accross the edge of a stack boundry, and
112+
// - many malloc implemenations will already provide guard pages for
113+
// allocations this large.
114+
void *Stack = malloc(StackSize);
115+
void *BottomOfStack = (char *)Stack + StackSize;
116+
117+
runOnNewStackImpl(BottomOfStack, callback, &Fn);
118+
119+
free(Stack);
120+
}
121+
#else
122+
void llvm::runOnNewStack(unsigned StackSize, function_ref<void()> Fn) {
123+
llvm::thread Thread(
124+
StackSize == 0 ? std::nullopt : std::optional<unsigned>(StackSize), Fn);
125+
Thread.join();
126+
}
127+
#endif

0 commit comments

Comments
 (0)