// SPDX-License-Identifier: Apache-2.0 OR MIT

/*
Run-time CPU feature detection on AArch64 Fuchsia by using zx_system_get_features.

As of nightly-2024-09-07, is_aarch64_feature_detected doesn't support run-time detection on Fuchsia.
https://github.com/rust-lang/stdarch/blob/d9466edb4c53cece8686ee6e17b028436ddf4151/crates/std_detect/src/detect/mod.rs

Refs:
- https://fuchsia.dev/reference/syscalls/system_get_features
- https://github.com/llvm/llvm-project/commit/4e731abc55681751b5d736b613f7720e50eb1ad4
*/

include!("common.rs");

#[allow(non_camel_case_types)]
mod ffi {
    // https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/zircon/system/public/zircon/types.h
    pub(crate) type zx_status_t = i32;

    sys_const!({
        // https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/zircon/system/public/zircon/errors.h
        pub(crate) const ZX_OK: zx_status_t = 0;

        // https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/zircon/system/public/zircon/features.h
        pub(crate) const ZX_FEATURE_KIND_CPU: u32 = 0;
        pub(crate) const ZX_ARM64_FEATURE_ISA_ATOMICS: u32 = 1 << 8;
    });

    // TODO: use sys_fn!
    #[link(name = "zircon")]
    extern "C" {
        // https://fuchsia.dev/reference/syscalls/system_get_features
        pub(crate) fn zx_system_get_features(kind: u32, features: *mut u32) -> zx_status_t;
    }
}

fn zx_system_get_features(kind: u32) -> u32 {
    let mut out = 0_u32;
    // SAFETY: the pointer is valid because we got it from a reference.
    let res = unsafe { ffi::zx_system_get_features(kind, &mut out) };
    if res != ffi::ZX_OK {
        return 0;
    }
    out
}

#[cold]
fn _detect(info: &mut CpuInfo) {
    let features = zx_system_get_features(ffi::ZX_FEATURE_KIND_CPU);
    if features & ffi::ZX_ARM64_FEATURE_ISA_ATOMICS != 0 {
        info.set(CpuInfo::HAS_LSE);
    }
}

#[allow(
    clippy::alloc_instead_of_core,
    clippy::std_instead_of_alloc,
    clippy::std_instead_of_core,
    clippy::undocumented_unsafe_blocks,
    clippy::wildcard_imports
)]
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_fuchsia() {
        let features = zx_system_get_features(ffi::ZX_FEATURE_KIND_CPU);
        assert_ne!(features, 0);
        std::eprintln!("features: {:b}", features);
    }

    // Static assertions for FFI bindings.
    // This checks that FFI bindings defined in this crate and FFI bindings
    // generated for the platform's latest header file using bindgen have
    // compatible signatures.
    // Since this is static assertion, we can detect problems with
    // `cargo check --tests --target <target>` run in CI (via TESTS=1 build.sh)
    // without actually running tests on these platforms.
    // As for constants, they are checked by static assertions generated by sys_const!.
    // See also https://github.com/taiki-e/test-helper/blob/HEAD/tools/codegen/src/ffi.rs.
    // TODO(codegen): auto-generate this test
    #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
    const _: fn() = || {
        use test_helper::sys;
        // TODO(codegen): zx_system_get_features
        let _: ffi::zx_status_t = 0 as sys::zx_status_t;
    };
}
