use core::arch::{asm, global_asm};
use super::{allocate_obj_on_stack, push};
use crate::coroutine::adjusted_stack_base;
use crate::stack::{Stack, StackPointer};
use crate::unwind::{asm_may_unwind_root, InitialFunc, StackCallFunc, TrapHandler};
use crate::util::EncodedValue;
cfg_if::cfg_if! {
if #[cfg(fp_is_r7)] {
#[cfg(r9_reserved)]
macro_rules! asm_clobbers {
(options($($opt:ident),*), $($asm:tt)*) => {
asm!(
$($asm)*
lateout("r11") _,
lateout("r4") _, lateout("r5") _, lateout("r8") _, lateout("r10") _,
lateout("q4") _, lateout("q5") _, lateout("q6") _, lateout("q7") _,
clobber_abi("C"),
options($($opt),*)
)
};
}
#[cfg(not(r9_reserved))]
macro_rules! asm_clobbers {
(options($($opt:ident),*), $($asm:tt)*) => {
asm!(
$($asm)*
lateout("r11") _, lateout("r9") _,
lateout("r4") _, lateout("r5") _, lateout("r8") _, lateout("r10") _,
lateout("q4") _, lateout("q5") _, lateout("q6") _, lateout("q7") _,
clobber_abi("C"),
options($($opt),*)
)
};
}
macro_rules! fp {
() => {
"r7"
};
}
macro_rules! fp_idx {
() => {
"7"
};
}
} else {
#[cfg(r9_reserved)]
macro_rules! asm_clobbers {
(options($($opt:ident),*), $($asm:tt)*) => {
asm!(
$($asm)*
lateout("r7") _,
lateout("r4") _, lateout("r5") _, lateout("r8") _, lateout("r10") _,
lateout("q4") _, lateout("q5") _, lateout("q6") _, lateout("q7") _,
clobber_abi("C"),
options($($opt),*)
)
};
}
#[cfg(not(r9_reserved))]
macro_rules! asm_clobbers {
(options($($opt:ident),*), $($asm:tt)*) => {
asm!(
$($asm)*
lateout("r7") _, lateout("r9") _,
lateout("r4") _, lateout("r5") _, lateout("r8") _, lateout("r10") _,
lateout("q4") _, lateout("q5") _, lateout("q6") _, lateout("q7") _,
clobber_abi("C"),
options($($opt),*)
)
};
}
macro_rules! fp {
() => {
"r11"
};
}
macro_rules! fp_idx {
() => {
"11"
};
}
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "asm-unwind")] {
macro_rules! asm_may_unwind {
($($asm:tt)*) => {
asm_clobbers!(
options(may_unwind),
$($asm)*
)
};
}
} else {
macro_rules! asm_may_unwind {
($($asm:tt)*) => {
asm_clobbers!(
options(),
$($asm)*
)
};
}
}
}
cfg_if::cfg_if! {
if #[cfg(all(is_thumb, has_thumb2))] {
macro_rules! thumb_symbol_def {
() => {
".thumb\n.thumb_func"
};
}
macro_rules! thumb_symbol_adr {
($reg:expr, $sym:expr) => {
concat!("adr ", $reg, ", ", $sym, " + 1")
};
}
} else if #[cfg(is_thumb)] {
macro_rules! thumb_symbol_def {
() => {
".thumb\n.thumb_func"
};
}
macro_rules! thumb_symbol_adr {
($reg:expr, $sym:expr) => {
concat!("adr ", $reg, ", ", $sym, "\n", "adds ", $reg, ", ", $reg, ", #1")
};
}
} else {
macro_rules! thumb_symbol_def {
() => {
""
};
}
macro_rules! thumb_symbol_adr {
($reg:expr, $sym:expr) => {
concat!("adr ", $reg, ", ", $sym)
};
}
}
}
cfg_if::cfg_if! {
if #[cfg(any(not(is_thumb), has_thumb2))] {
macro_rules! thumb1 {
($asm:expr) => { "" };
}
macro_rules! thumb2 {
($asm:expr) => { $asm }
}
} else {
macro_rules! thumb1 {
($asm:expr) => { $asm };
}
macro_rules! thumb2 {
($asm:expr) => { "" };
}
}
}
pub const STACK_ALIGNMENT: usize = 8;
pub const PARENT_STACK_OFFSET: usize = 0;
pub const PARENT_LINK_OFFSET: usize = 8;
pub type StackWord = u32;
global_asm!(
".balign 4",
thumb_symbol_def!(),
asm_function_begin!("stack_init_trampoline"),
".cfi_sections .debug_frame",
".fnstart",
".cfi_startproc",
cfi_signal_frame!(),
concat!("push {{", fp!(), ", lr}}"),
thumb2!("str sp, [r1, #-8]!"),
thumb1!("mov r3, sp"),
thumb1!("subs r1, r1, #8"),
thumb1!("str r3, [r1]"),
"adds r2, r2, #12",
"mov sp, r2",
concat!("mov ", fp!(), ", r1"),
concat!(
".cfi_escape 0x0f, 5, 0x70 + ",
fp_idx!(),
", 0x00, 0x06, 0x23, 0x0c"
),
".cfi_offset r6, -4",
".cfi_offset lr, -8",
concat!(".cfi_offset ", fp!(), ", -12"),
".save {{r6}}",
concat!(".save {{", fp!(), ", pc}}"),
".save {{sp}}",
concat!(".movsp ", fp!()),
thumb2!(thumb_symbol_adr!("lr", "0f")),
thumb2!("ldr pc, [r1, #4]"),
thumb1!(thumb_symbol_adr!("r3", "0f")),
thumb1!("mov lr, r3"),
thumb1!("ldr r3, [r1, #4]"),
thumb1!("bx r3"),
".balign 4",
"0:",
thumb_symbol_def!(),
asm_function_alt_entry!("stack_init_trampoline_return"),
"udf #0",
".cfi_endproc",
".fnend",
asm_function_end!("stack_init_trampoline"),
);
global_asm!(
".balign 4",
thumb_symbol_def!(),
asm_function_begin!("stack_call_trampoline"),
".cfi_sections .debug_frame",
".fnstart",
".cfi_startproc",
cfi_signal_frame!(),
concat!("push {{", fp!(), ", lr}}"),
concat!("mov ", fp!(), ", sp"),
concat!(".save {{", fp!(), ", lr}}"),
concat!(".setfp ", fp!(), ", sp, #0"),
concat!(".cfi_def_cfa ", fp!(), ", 8"),
".cfi_offset lr, -4",
concat!(".cfi_offset ", fp!(), ", -8"),
"mov sp, r1",
"blx r2",
concat!("mov sp, ", fp!()),
concat!("pop {{", fp!(), ", pc}}"),
".cfi_endproc",
".fnend",
asm_function_end!("stack_call_trampoline"),
);
extern "C" {
fn stack_init_trampoline(arg: EncodedValue, stack_base: StackPointer, stack_ptr: StackPointer);
static stack_init_trampoline_return: [u8; 0];
#[allow(dead_code)]
fn stack_call_trampoline(arg: *mut u8, sp: StackPointer, f: StackCallFunc);
}
#[inline]
pub unsafe fn init_stack<T>(stack: &impl Stack, func: InitialFunc<T>, obj: T) -> StackPointer {
let mut sp = adjusted_stack_base(stack).get();
push(&mut sp, Some(func as StackWord));
push(&mut sp, None);
allocate_obj_on_stack(&mut sp, 8, obj);
debug_assert_eq!(sp % STACK_ALIGNMENT, 0);
push(
&mut sp,
Some(stack_init_trampoline as *const () as StackWord),
);
push(&mut sp, None);
push(&mut sp, None);
StackPointer::new_unchecked(sp)
}
#[inline]
pub unsafe fn switch_and_link(
arg: EncodedValue,
sp: StackPointer,
stack_base: StackPointer,
) -> (EncodedValue, Option<StackPointer>) {
let (ret_val, ret_sp);
asm_may_unwind!(
"push {{r6}}",
"ldr r3, [r2, #8]",
"blx r3",
"pop {{r6}}",
inlateout("r0") arg => ret_val,
lateout("r1") ret_sp,
in("r1") stack_base.get(),
in("r2") sp.get(),
);
(ret_val, StackPointer::new(ret_sp))
}
#[inline(always)]
pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) -> EncodedValue {
let ret_val;
asm_may_unwind!(
thumb2!(thumb_symbol_adr!("lr", "0f")),
thumb1!(thumb_symbol_adr!("r3", "0f")),
thumb1!("mov lr, r3"),
concat!("push {{r6, ", fp!(), ", lr}}"),
"mov r1, sp",
thumb2!("ldr sp, [r2]"),
thumb1!("ldr r3, [r2]"),
thumb1!("mov sp, r3"),
concat!("pop {{", fp!(), ", pc}}"),
".balign 4",
"0:",
concat!("push {{", fp!(), ", lr}}"),
thumb2!("str sp, [r1, #-8]"),
thumb1!("subs r1, r1, #8"),
thumb1!("mov r3, sp"),
thumb1!("str r3, [r1]"),
"mov sp, r2",
thumb2!(concat!("pop {{r6, ", fp!(), ", lr}}")),
thumb1!(concat!("pop {{r6, ", fp!(), "}}")),
thumb1!("add sp, sp, #4"),
inlateout("r0") arg => ret_val,
in("r2") parent_link,
);
ret_val
}
#[inline(always)]
pub unsafe fn switch_and_reset(arg: EncodedValue, parent_link: *mut StackPointer) -> ! {
asm!(
thumb2!("ldr sp, [{parent_link}]"),
thumb1!("ldr r3, [{parent_link}]"),
thumb1!("mov sp, r3"),
concat!("pop {{", fp!(), ", pc}}"),
parent_link = in(reg) parent_link,
in("r0") arg,
in("r1") 0,
options(noreturn),
);
}
#[inline]
#[cfg(feature = "asm-unwind")]
pub unsafe fn switch_and_throw(
forced_unwind: crate::unwind::ForcedUnwind,
sp: StackPointer,
stack_base: StackPointer,
) -> (EncodedValue, Option<StackPointer>) {
extern "C-unwind" fn throw(forced_unwind: crate::unwind::ForcedUnwind) -> ! {
extern crate std;
use std::boxed::Box;
std::panic::resume_unwind(Box::new(forced_unwind));
}
let (ret_val, ret_sp);
asm_may_unwind!(
thumb2!(thumb_symbol_adr!("lr", "0f")),
thumb1!(thumb_symbol_adr!("r3", "0f")),
thumb1!("mov lr, r3"),
"push {{r6}}",
concat!("push {{", fp!(), ", lr}}"),
"mov r3, sp",
"str r3, [r1, #-8]",
"mov sp, r2",
thumb2!(concat!("pop {{r6, ", fp!(), ", lr}}")),
thumb1!(concat!("pop {{r6, ", fp!(), "}}")),
thumb1!("pop {{r3}}"),
thumb1!("mov lr, r3"),
"b {throw}",
".balign 4",
"0:",
"pop {{r6}}",
throw = sym throw,
in("r0") forced_unwind.0.get(),
lateout("r0") ret_val,
lateout("r1") ret_sp,
in("r1") stack_base.get(),
in("r2") sp.get(),
);
(ret_val, StackPointer::new(ret_sp))
}
#[inline]
pub unsafe fn drop_initial_obj(
_stack_base: StackPointer,
stack_ptr: StackPointer,
drop_fn: unsafe fn(ptr: *mut u8),
) {
let ptr = (stack_ptr.get() as *mut u8).add(12);
drop_fn(ptr);
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub struct TrapHandlerRegs {
pub pc: u32,
pub r0: u32,
pub r1: u32,
pub r7: u32,
pub r11: u32,
pub r13: u32,
pub r14: u32,
pub cpsr_thumb: bool,
pub cpsr_endian: bool,
}
pub unsafe fn setup_trap_trampoline<T>(
stack_base: StackPointer,
val: T,
handler: TrapHandler<T>,
) -> TrapHandlerRegs {
let parent_link = stack_base.get() - PARENT_LINK_OFFSET;
let mut sp = parent_link;
allocate_obj_on_stack(&mut sp, 8, val);
let val_ptr = sp;
TrapHandlerRegs {
pc: handler as u32 & !1,
r0: val_ptr as u32,
r1: parent_link as u32,
r7: parent_link as u32,
r11: parent_link as u32,
r13: sp as u32,
r14: stack_init_trampoline_return.as_ptr() as u32,
cpsr_thumb: handler as u32 & 1 != 0,
cpsr_endian: cfg!(target_endian = "big"),
}
}
#[inline]
pub unsafe fn on_stack(arg: *mut u8, stack: impl Stack, f: StackCallFunc) {
asm_may_unwind_root!(
concat!("blx ", asm_mangle!("stack_call_trampoline")),
"nop",
in("r0") arg,
in("r1") adjusted_stack_base(&stack).get(),
in("r2") f,
clobber_abi("C"),
)
}