// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// compiler_intrinsics.hpp
// Compatibility layer for compiler intrinsics between MSVC and GCC

#ifndef SANGI_COMPILER_INTRINSICS_HPP
#define SANGI_COMPILER_INTRINSICS_HPP

#include <cstdint>

#ifdef _MSC_VER
#include <intrin.h>
#else

// --- _umul128: 64x64 -> 128-bit multiplication ---
static inline uint64_t _umul128(uint64_t a, uint64_t b, uint64_t* hi) {
    __uint128_t r = (__uint128_t)a * b;
    *hi = (uint64_t)(r >> 64);
    return (uint64_t)r;
}

// --- _addcarry_u64: addition with carry ---
static inline unsigned char _addcarry_u64(unsigned char carry_in,
                                           uint64_t a, uint64_t b, uint64_t* out) {
    __uint128_t sum = (__uint128_t)a + b + carry_in;
    *out = (uint64_t)sum;
    return (unsigned char)(sum >> 64);
}

// --- _subborrow_u64: subtraction with borrow ---
static inline unsigned char _subborrow_u64(unsigned char borrow_in,
                                            uint64_t a, uint64_t b, uint64_t* out) {
    __uint128_t diff = (__uint128_t)a - b - borrow_in;
    *out = (uint64_t)diff;
    return (unsigned char)(diff >> 127); // borrow = sign bit
}

// --- _BitScanReverse64: position of the most significant bit ---
static inline unsigned char _BitScanReverse64(unsigned long* index, uint64_t mask) {
    if (mask == 0) return 0;
    *index = 63 - __builtin_clzll(mask);
    return 1;
}

// --- _udiv128: 128/64 -> 64 division ---
static inline uint64_t _udiv128(uint64_t hi, uint64_t lo, uint64_t divisor, uint64_t* remainder) {
    __uint128_t dividend = ((__uint128_t)hi << 64) | lo;
    *remainder = (uint64_t)(dividend % divisor);
    return (uint64_t)(dividend / divisor);
}

// --- __shiftleft128: 128-bit left shift ---
static inline uint64_t __shiftleft128(uint64_t lo, uint64_t hi, unsigned char shift) {
    if (shift == 0) return hi;
    return (hi << shift) | (lo >> (64 - shift));
}

// --- __shiftright128: 128-bit right shift ---
static inline uint64_t __shiftright128(uint64_t lo, uint64_t hi, unsigned char shift) {
    if (shift == 0) return lo;
    return (lo >> shift) | (hi << (64 - shift));
}

// --- __lzcnt64: leading-zero count ---
static inline uint64_t __lzcnt64(uint64_t x) {
    return x ? __builtin_clzll(x) : 64;
}

// --- _mulx_u64: MULX (carry-less multiply) ---
static inline uint64_t _mulx_u64(uint64_t a, uint64_t b, uint64_t* hi) {
    return _umul128(a, b, hi);
}

// --- _BitScanForward64: position of the least significant bit ---
static inline unsigned char _BitScanForward64(unsigned long* index, uint64_t mask) {
    if (mask == 0) return 0;
    *index = __builtin_ctzll(mask);
    return 1;
}

// --- _tzcnt_u64: trailing-zero count ---
static inline uint64_t _tzcnt_u64(uint64_t x) {
    return x ? __builtin_ctzll(x) : 64;
}

// --- _alloca → alloca ---
#include <alloca.h>
#define _alloca alloca

#endif // _MSC_VER

#endif // SANGI_COMPILER_INTRINSICS_HPP
