#![macro_use]
macro_rules! intrinsics_aliases {
(
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
) => {};
(
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
) => {};
(
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
$alias:ident
$($rest:ident)*
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
intrinsics! {
extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
$name($($argname),*)
}
}
intrinsics_aliases! {
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
$($rest)*
}
};
(
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
$alias:ident
$($rest:ident)*
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
intrinsics! {
unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
$name($($argname),*)
}
}
intrinsics_aliases! {
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
$($rest)*
}
};
}
macro_rules! intrinsics {
() => {};
(
#[slower_than_default]
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
#[allow(dead_code)]
fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
intrinsics!($($rest)*);
};
(
#[bootrom_v2]
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
#[cfg(not(feature = "rom-v2-intrinsics"))]
#[allow(dead_code)]
fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
#[cfg(feature = "rom-v2-intrinsics")]
intrinsics! {
$(#[$($attr)*])*
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
}
intrinsics!($($rest)*);
};
(
#[alias = $($alias:ident),*]
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
intrinsics! {
$(#[$($attr)*])*
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
}
intrinsics_aliases! {
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
$($alias) *
}
intrinsics!($($rest)*);
};
(
#[alias = $($alias:ident),*]
$(#[$($attr:tt)*])*
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
intrinsics! {
$(#[$($attr)*])*
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
}
intrinsics_aliases! {
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
$($alias) *
}
intrinsics!($($rest)*);
};
(
#[aeabi = $($alias:ident),*]
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
intrinsics! {
$(#[$($attr)*])*
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
}
intrinsics_aliases! {
extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret,
$($alias) *
}
intrinsics!($($rest)*);
};
(
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
$(#[$($attr)*])*
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
mod $name {
#[no_mangle]
$(#[$($attr)*])*
pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
super::$name($($argname),*)
}
}
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
#[allow(dead_code)]
fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
intrinsics!($($rest)*);
};
(
$(#[$($attr:tt)*])*
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
$(#[$($attr)*])*
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
mod $name {
#[no_mangle]
$(#[$($attr)*])*
pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
super::$name($($argname),*)
}
}
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
#[allow(dead_code)]
unsafe fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
intrinsics!($($rest)*);
};
}
#[cfg(target_arch = "arm")]
core::arch::global_asm!(
".macro hwdivider_head",
"ldr r2, =(0xd0000000)", "ldr r3, [r2, #0x078]", "lsrs r3, #2", "bcs 2f",
"1:",
".endm",
".macro hwdivider_tail",
"b 3f",
"3: b 3f",
"3: b 3f",
"3: b 3f",
"3:",
"ldr r1, [r2, #0x074]", "ldr r0, [r2, #0x070]", "bx lr",
"2:",
"push {{r4-r6, lr}}",
"ldr r3, [r2, #0x060]", "ldr r4, [r2, #0x064]", "ldr r5, [r2, #0x074]", "ldr r6, [r2, #0x070]", "bl 1b",
"str r3, [r2, #0x060]", "str r4, [r2, #0x064]", "str r5, [r2, #0x074]", "str r6, [r2, #0x070]", "pop {{r4-r6, pc}}",
".endm",
);
macro_rules! division_function {
(
$name:ident $($intrinsic:ident)* ( $argty:ty ) {
$($begin:literal),+
}
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
core::arch::global_asm!(
// Mangle the name slightly, since this is a global symbol.
concat!(".section .text._erphal_", stringify!($name)),
concat!(".global _erphal_", stringify!($name)),
concat!(".type _erphal_", stringify!($name), ", %function"),
".align 2",
concat!("_erphal_", stringify!($name), ":"),
$(
concat!(".global ", stringify!($intrinsic)),
concat!(".type ", stringify!($intrinsic), ", %function"),
concat!(stringify!($intrinsic), ":"),
)*
"hwdivider_head",
$($begin),+ ,
"hwdivider_tail",
);
#[cfg(all(target_arch = "arm", not(feature = "intrinsics")))]
core::arch::global_asm!(
// Mangle the name slightly, since this is a global symbol.
concat!(".section .text._erphal_", stringify!($name)),
concat!(".global _erphal_", stringify!($name)),
concat!(".type _erphal_", stringify!($name), ", %function"),
".align 2",
concat!("_erphal_", stringify!($name), ":"),
"hwdivider_head",
$($begin),+ ,
"hwdivider_tail",
);
#[cfg(target_arch = "arm")]
extern "aapcs" {
#[link_name = concat!("_erphal_", stringify!($name)) ]
fn $name(n: $argty, d: $argty) -> u64;
}
#[cfg(not(target_arch = "arm"))]
#[allow(unused_variables)]
unsafe fn $name(n: $argty, d: $argty) -> u64 { 0 }
};
}
division_function! {
unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) {
"str r0, [r2, #0x060]", "str r1, [r2, #0x064]" }
}
division_function! {
signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) {
"str r0, [r2, #0x068]", "str r1, [r2, #0x06c]" }
}
fn divider_unsigned(n: u32, d: u32) -> DivResult<u32> {
let packed = unsafe { unsigned_divmod(n, d) };
DivResult {
quotient: packed as u32,
remainder: (packed >> 32) as u32,
}
}
fn divider_signed(n: i32, d: i32) -> DivResult<i32> {
let packed = unsafe { signed_divmod(n, d) };
DivResult {
quotient: packed as u32 as i32,
remainder: (packed >> 32) as u32 as i32,
}
}
struct DivResult<T> {
pub quotient: T,
pub remainder: T,
}
intrinsics! {
extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
divider_unsigned(n, d).quotient
}
extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
divider_unsigned(n, d).remainder
}
extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
let quo_rem = divider_unsigned(n, d);
if let Some(rem) = rem {
*rem = quo_rem.remainder;
}
quo_rem.quotient
}
extern "C" fn __divsi3(n: i32, d: i32) -> i32 {
divider_signed(n, d).quotient
}
extern "C" fn __modsi3(n: i32, d: i32) -> i32 {
divider_signed(n, d).remainder
}
extern "C" fn __divmodsi4(n: i32, d: i32, rem: &mut i32) -> i32 {
let quo_rem = divider_signed(n, d);
*rem = quo_rem.remainder;
quo_rem.quotient
}
}