// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.


#include "ThreadLocal.h"

#include "Common/Runtime.h"
#include "schedule.h"
#include "Base/Globals.h"

namespace MapleRuntime {
RwLock ThreadLocal::tlEnableLock;
MRT_EXPORT thread_local uint64_t threadLocalData[sizeof(ThreadLocalData) / sizeof(uint64_t)];
thread_local CleanThreadLocalData cleaner;

ThreadLocalData* ThreadLocal::GetThreadLocalData()
{
    return reinterpret_cast<ThreadLocalData*>(threadLocalData);
}

void ThreadLocal::InitializeCleaner()
{
    (void)cleaner;
}

CleanThreadLocalData::CleanThreadLocalData()
{
    // Add a side effect to make sure the constructor wont be optimized out.
    std::atomic_thread_fence(std::memory_order_seq_cst);
    static volatile bool isInit = false;
    if (!isInit) {
        isInit = true;
    }
}

CleanThreadLocalData::~CleanThreadLocalData()
{
    if (!ThreadLocal::TryGetRdLock()) {
        return;
    }

    ThreadLocalData* local = ThreadLocal::GetThreadLocalData();
    if (Runtime::CurrentRef() == nullptr ||
        local->isCJProcessor || local->foreignCJThread == nullptr) {
        ThreadLocal::UnlockRdLock();
        return;
    }

    CJForeignThreadExit(reinterpret_cast<CJThreadHandle>(local->foreignCJThread));
    ThreadLocal::UnlockRdLock();
}

extern "C" void MCC_CheckThreadLocalDataOffset()
{
    static_assert(offsetof(ThreadLocalData, buffer) == 0,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(offsetof(ThreadLocalData, mutator) == sizeof(void*),
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(offsetof(ThreadLocalData, cjthread) == sizeof(void*) * 2,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(offsetof(ThreadLocalData, schedule) == sizeof(void*) * 3,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(offsetof(ThreadLocalData, preemptFlag) == sizeof(void*) * 4,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(offsetof(ThreadLocalData, protectAddr) == sizeof(void*) * 5,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(offsetof(ThreadLocalData, safepointState) == sizeof(void*) * 6,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
#if defined(__arm__)
    static_assert(offsetof(ThreadLocalData, tid) == sizeof(void*) * 6 + sizeof(uint64_t),
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(offsetof(ThreadLocalData, foreignCJThread) == sizeof(void*) * 6 + sizeof(uint64_t) * 2,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(sizeof(ThreadLocalData) == sizeof(void*) * 10 + sizeof(uint64_t) * 2,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
#else   
    static_assert(offsetof(ThreadLocalData, tid) == sizeof(void*) * 7,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(offsetof(ThreadLocalData, foreignCJThread) == sizeof(void*) * 8,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
    static_assert(sizeof(ThreadLocalData) == sizeof(void*) * 11,
                  "need to modify the offset of this value in llvm-project and cjthread at the same time");
#endif
}

#ifdef __APPLE__
extern "C" MRT_EXPORT void MRT_CheckThreadLocalDataOffset();
__asm__(
    ".global _MRT_CheckThreadLocalDataOffset\n\t.set _MRT_CheckThreadLocalDataOffset, _MCC_CheckThreadLocalDataOffset");
extern "C" MRT_EXPORT void CJ_MCC_CheckThreadLocalDataOffset();
__asm__(
    ".global _CJ_MCC_CheckThreadLocalDataOffset\n\t.set _CJ_MCC_CheckThreadLocalDataOffset, "
    "_MCC_CheckThreadLocalDataOffset");
#else
extern "C" MRT_EXPORT void MRT_CheckThreadLocalDataOffset() __attribute__((alias("MCC_CheckThreadLocalDataOffset")));
extern "C" MRT_EXPORT void CJ_MCC_CheckThreadLocalDataOffset() __attribute__((alias("MCC_CheckThreadLocalDataOffset")));
#endif
} // namespace MapleRuntime