// Reference: Section 7.4 "Hints" of ACLE

// CP15 instruction
#[cfg(not(any(
    // v8
    target_arch = "aarch64",
    // v7
    target_feature = "v7",
    // v6-M
    target_feature = "mclass"
)))]
mod cp15;

#[cfg(not(any(
    target_arch = "aarch64",
    target_feature = "v7",
    target_feature = "mclass"
)))]
#[unstable(feature = "stdarch_arm_barrier", issue = "117219")]
pub use self::cp15::*;

// Dedicated instructions
#[cfg(any(
    target_arch = "aarch64",
    target_feature = "v7",
    target_feature = "mclass"
))]
macro_rules! dmb_dsb {
    ($A:ident) => {
        #[unstable(feature = "stdarch_arm_barrier", issue = "117219")]
        impl super::super::sealed::Dmb for $A {
            #[inline(always)]
            unsafe fn __dmb(&self) {
                super::dmb(super::arg::$A)
            }
        }

        #[unstable(feature = "stdarch_arm_barrier", issue = "117219")]
        impl super::super::sealed::Dsb for $A {
            #[inline(always)]
            unsafe fn __dsb(&self) {
                super::dsb(super::arg::$A)
            }
        }
    };
}

#[cfg(any(
    target_arch = "aarch64",
    target_feature = "v7",
    target_feature = "mclass"
))]
mod common;

#[cfg(any(
    target_arch = "aarch64",
    target_feature = "v7",
    target_feature = "mclass"
))]
#[unstable(feature = "stdarch_arm_barrier", issue = "117219")]
pub use self::common::*;

#[cfg(any(target_arch = "aarch64", target_feature = "v7",))]
mod not_mclass;

#[cfg(any(target_arch = "aarch64", target_feature = "v7",))]
#[unstable(feature = "stdarch_arm_barrier", issue = "117219")]
pub use self::not_mclass::*;

#[cfg(target_arch = "aarch64")]
mod v8;

#[cfg(target_arch = "aarch64")]
#[unstable(feature = "stdarch_arm_barrier", issue = "117219")]
pub use self::v8::*;

/// Generates a DMB (data memory barrier) instruction or equivalent CP15 instruction.
///
/// DMB ensures the observed ordering of memory accesses. Memory accesses of the specified type
/// issued before the DMB are guaranteed to be observed (in the specified scope) before memory
/// accesses issued after the DMB.
///
/// For example, DMB should be used between storing data, and updating a flag variable that makes
/// that data available to another core.
///
/// The __dmb() intrinsic also acts as a compiler memory barrier of the appropriate type.
#[inline(always)]
#[unstable(feature = "stdarch_arm_barrier", issue = "117219")]
pub unsafe fn __dmb<A>(arg: A)
where
    A: super::sealed::Dmb,
{
    arg.__dmb()
}

/// Generates a DSB (data synchronization barrier) instruction or equivalent CP15 instruction.
///
/// DSB ensures the completion of memory accesses. A DSB behaves as the equivalent DMB and has
/// additional properties. After a DSB instruction completes, all memory accesses of the specified
/// type issued before the DSB are guaranteed to have completed.
///
/// The __dsb() intrinsic also acts as a compiler memory barrier of the appropriate type.
#[inline(always)]
#[unstable(feature = "stdarch_arm_barrier", issue = "117219")]
pub unsafe fn __dsb<A>(arg: A)
where
    A: super::sealed::Dsb,
{
    arg.__dsb()
}

/// Generates an ISB (instruction synchronization barrier) instruction or equivalent CP15
/// instruction.
///
/// This instruction flushes the processor pipeline fetch buffers, so that following instructions
/// are fetched from cache or memory.
///
/// An ISB is needed after some system maintenance operations. An ISB is also needed before
/// transferring control to code that has been loaded or modified in memory, for example by an
/// overlay mechanism or just-in-time code generator.  (Note that if instruction and data caches are
/// separate, privileged cache maintenance operations would be needed in order to unify the caches.)
///
/// The only supported argument for the __isb() intrinsic is 15, corresponding to the SY (full
/// system) scope of the ISB instruction.
#[inline(always)]
#[unstable(feature = "stdarch_arm_barrier", issue = "117219")]
pub unsafe fn __isb<A>(arg: A)
where
    A: super::sealed::Isb,
{
    arg.__isb()
}

extern "unadjusted" {
    #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.dmb")]
    #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.dmb")]
    fn dmb(_: i32);

    #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.dsb")]
    #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.dsb")]
    fn dsb(_: i32);

    #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.isb")]
    #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.isb")]
    fn isb(_: i32);
}

// we put these in a module to prevent weirdness with glob re-exports
mod arg {
    // See Section 7.3  Memory barriers of ACLE
    pub const SY: i32 = 15;
    pub const ST: i32 = 14;
    pub const LD: i32 = 13;
    pub const ISH: i32 = 11;
    pub const ISHST: i32 = 10;
    pub const ISHLD: i32 = 9;
    pub const NSH: i32 = 7;
    pub const NSHST: i32 = 6;
    pub const NSHLD: i32 = 5;
    pub const OSH: i32 = 3;
    pub const OSHST: i32 = 2;
    pub const OSHLD: i32 = 1;
}
