      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135

    // # References
    //
    // - Section 7.4 "Hints" of ACLE
    // - Section 7.7 "NOP" of ACLE

    /// Generates a WFI (wait for interrupt) hint instruction, or nothing.
    ///
    /// The WFI instruction allows (but does not require) the processor to enter a
    /// low-power state until one of a number of asynchronous events occurs.
    // Section 10.1 of ACLE says that the supported arches are: 8, 6K, 6-M
    // LLVM says "instruction requires: armv6k"
    #[cfg(any(target_feature = "v6", target_arch = "aarch64"))]
    #[inline(always)]
    pub unsafe fn __wfi() {
        hint(HINT_WFI);
    }

    /// Generates a WFE (wait for event) hint instruction, or nothing.
    ///
    /// The WFE instruction allows (but does not require) the processor to enter a
    /// low-power state until some event occurs such as a SEV being issued by
    /// another processor.
    // Section 10.1 of ACLE says that the supported arches are: 8, 6K, 6-M
    // LLVM says "instruction requires: armv6k"
    #[cfg(any(target_feature = "v6", target_arch = "aarch64"))]
    #[inline(always)]
    pub unsafe fn __wfe() {
        hint(HINT_WFE);
    }

    /// Generates a SEV (send a global event) hint instruction.
    ///
    /// This causes an event to be signaled to all processors in a multiprocessor
    /// system. It is a NOP on a uniprocessor system.
    // Section 10.1 of ACLE says that the supported arches are: 8, 6K, 6-M, 7-M
    // LLVM says "instruction requires: armv6k"
    #[cfg(any(target_feature = "v6", target_arch = "aarch64"))]
    #[inline(always)]
    pub unsafe fn __sev() {
        hint(HINT_SEV);
    }

    /// Generates a send a local event hint instruction.
    ///
    /// This causes an event to be signaled to only the processor executing this
    /// instruction. In a multiprocessor system, it is not required to affect the
    /// other processors.
    // LLVM says "instruction requires: armv8"
    #[cfg(any(
        target_feature = "v8", // 32-bit ARMv8
        target_arch = "aarch64", // AArch64
    ))]
    #[inline(always)]
    pub unsafe fn __sevl() {
        hint(HINT_SEVL);
    }

    /// Generates a YIELD hint instruction.
    ///
    /// This enables multithreading software to indicate to the hardware that it is
    /// performing a task, for example a spin-lock, that could be swapped out to
    /// improve overall system performance.
    // Section 10.1 of ACLE says that the supported arches are: 8, 6K, 6-M
    // LLVM says "instruction requires: armv6k"
    #[cfg(any(target_feature = "v6", target_arch = "aarch64"))]
    #[inline(always)]
    pub unsafe fn __yield() {
        hint(HINT_YIELD);
    }

    /// Generates a DBG instruction.
    ///
    /// This provides a hint to debugging and related systems. The argument must be
    /// a constant integer from 0 to 15 inclusive. See implementation documentation
    /// for the effect (if any) of this instruction and the meaning of the
    /// argument. This is available only when compliling for AArch32.
    // Section 10.1 of ACLE says that the supported arches are: 7, 7-M
    // "The DBG hint instruction is added in ARMv7. It is UNDEFINED in the ARMv6 base architecture, and
    // executes as a NOP instruction in ARMv6K and ARMv6T2." - ARM Architecture Reference Manual ARMv7-A
    // and ARMv7-R edition (ARM DDI 0406C.c) sections D12.4.1 "ARM instruction set support" and D12.4.2
    // "Thumb instruction set support"
    #[cfg(target_feature = "v7")]
    #[inline(always)]
    #[rustc_args_required_const(0)]
    pub unsafe fn __dbg(imm4: u32) {
        macro_rules! call {
            ($imm4:expr) => {
                llvm_asm!(concat!("DBG ", stringify!($imm4)) : : : : "volatile")
            }
        }

        match imm4 & 0b1111 {
            0 => call!(0),
            1 => call!(1),
            2 => call!(2),
            3 => call!(3),
            4 => call!(4),
            5 => call!(5),
            6 => call!(6),
            7 => call!(7),
            8 => call!(8),
            9 => call!(9),
            10 => call!(10),
            11 => call!(11),
            12 => call!(12),
            13 => call!(13),
            14 => call!(14),
            _ => call!(15),
        }
    }

    /// Generates an unspecified no-op instruction.
    ///
    /// Note that not all architectures provide a distinguished NOP instruction. On
    /// those that do, it is unspecified whether this intrinsic generates it or
    /// another instruction. It is not guaranteed that inserting this instruction
    /// will increase execution time.
    #[inline(always)]
    pub unsafe fn __nop() {
        llvm_asm!("NOP" : : : : "volatile")
    }

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

    // from LLVM 7.0.1's lib/Target/ARM/{ARMInstrThumb,ARMInstrInfo,ARMInstrThumb2}.td
    const HINT_NOP: i32 = 0;
    const HINT_YIELD: i32 = 1;
    const HINT_WFE: i32 = 2;
    const HINT_WFI: i32 = 3;
    const HINT_SEV: i32 = 4;
    const HINT_SEVL: i32 = 5;