// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QV4STACKLIMITS_P_H
#define QV4STACKLIMITS_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <private/qtqmlglobal_p.h>

#ifndef Q_STACK_GROWTH_DIRECTION
#  ifdef Q_PROCESSOR_HPPA
#    define Q_STACK_GROWTH_DIRECTION (1)
#  else
#    define Q_STACK_GROWTH_DIRECTION (-1)
#  endif
#endif

QT_BEGIN_NAMESPACE

namespace QV4 {

// We may not be able to take the negative of the type
// used to represent stack size, but we can always add
// or subtract it to/from a quint8 pointer.

template<typename Size>
static const void *incrementStackPointer(const void *base, Size amount)
{
#if Q_STACK_GROWTH_DIRECTION > 0
    return static_cast<const quint8 *>(base) + amount;
#else
    return static_cast<const quint8 *>(base) - amount;
#endif
}

template<typename Size>
static void *decrementStackPointer(void *base, Size amount)
{
#if Q_STACK_GROWTH_DIRECTION > 0
    return static_cast<quint8 *>(base) - amount;
#else
    return static_cast<quint8 *>(base) + amount;
#endif
}

// Note: This does not return a completely accurate stack pointer.
//       Depending on whether this function is inlined or not, we may get the address of
//       this function's stack frame or the caller's stack frame.
//       Always use a safety margin when determining stack limits.
inline const void *currentStackPointer()
{
    // TODO: How often do we actually need the assembler mess below? Is that worth it?

    void *stackPointer;
#if defined(Q_CC_GNU) || __has_builtin(__builtin_frame_address)
    stackPointer = __builtin_frame_address(0);
#elif defined(Q_CC_MSVC)
    stackPointer = &stackPointer;
#elif defined(Q_PROCESSOR_X86_64)
    __asm__ __volatile__("movq %%rsp, %0" : "=r"(stackPointer) : :);
#elif defined(Q_PROCESSOR_X86)
    __asm__ __volatile__("movl %%esp, %0" : "=r"(stackPointer) : :);
#elif defined(Q_PROCESSOR_ARM_64) && defined(__ILP32__)
    quint64 stackPointerRegister = 0;
    __asm__ __volatile__("mov %0, sp" : "=r"(stackPointerRegister) : :);
    stackPointer = reinterpret_cast<void *>(stackPointerRegister);
#elif defined(Q_PROCESSOR_ARM_64) || defined(Q_PROCESSOR_ARM_32)
    __asm__ __volatile__("mov %0, sp" : "=r"(stackPointer) : :);
#else
    stackPointer = &stackPointer;
#endif
    return stackPointer;
}

struct StackProperties
{
    const void *base = nullptr;
    const void *softLimit = nullptr;
    const void *hardLimit = nullptr;
};

StackProperties stackProperties();

} // namespace QV4

QT_END_NAMESPACE

#endif // QV4STACKLIMITS_P_H
