//===--- ProgramStack.h -----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_SUPPORT_PROGRAMSTACK_H
#define LLVM_SUPPORT_PROGRAMSTACK_H

#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/Support/Compiler.h"

// LLVM_HAS_SPLIT_STACKS is exposed in the header because CrashRecoveryContext
// needs to know if it's running on another thread or not.
//
// Currently only Apple AArch64 is known to support split stacks in the debugger
// and other tooling.
#if defined(__APPLE__) && defined(__MACH__) && defined(__aarch64__) &&         \
    __has_extension(gnu_asm)
# define LLVM_HAS_SPLIT_STACKS
# define LLVM_HAS_SPLIT_STACKS_AARCH64
#endif

namespace llvm {

/// \returns an address close to the current value of the stack pointer.
///
/// The value is not guaranteed to point to anything specific. It can be used to
/// estimate how much stack space has been used since the previous call.
LLVM_ABI uintptr_t getStackPointer();

/// \returns the default stack size for this platform.
///
/// Based on \p RLIMIT_STACK or the equivalent.
LLVM_ABI unsigned getDefaultStackSize();

/// Runs Fn on a new stack of at least the given size.
///
/// \param StackSize requested stack size. A size of 0 uses the default stack
///                  size of the platform.
///
/// The preferred implementation is split stacks on platforms that have a good
/// debugging experience for them. On other platforms a new thread is used.
LLVM_ABI void runOnNewStack(unsigned StackSize, function_ref<void()> Fn);

template <typename R, typename... Ts>
auto runOnNewStack(unsigned StackSize, function_ref<R(Ts...)> Fn,
                   Ts &&...Args) {
  if constexpr (std::is_same_v<R, void>) {
    runOnNewStack(StackSize, [&]() { Fn(std::forward<Ts>(Args)...); });
  } else {
    std::optional<R> Ret;
    runOnNewStack(StackSize, [&]() { Ret = Fn(std::forward<Ts>(Args)...); });
    return std::move(*Ret);
  }
}

} // namespace llvm

#endif // LLVM_SUPPORT_PROGRAMSTACK_H
