aarch32_cpu/stacks.rs
1//! Code for checking stack usage
2
3/// Reports stack usage as a count of bytes
4///
5/// It starts at the lower bound, and looks for values that are set to 0,
6/// concluding that those values have never been used. It returns `(total,
7/// used)` in bytes.
8///
9/// # Safety
10///
11/// Pass a range of valid, readable, memory with 32-bit aligned addresses.
12pub unsafe fn stack_used_bytes(stack: core::ops::Range<*const u32>) -> (usize, usize) {
13 let size_words = unsafe { stack.end.offset_from(stack.start) } as usize;
14 let unused_words = unsafe { stack_unused_bytes_asm(stack.start, size_words) };
15 let used_words = size_words - unused_words;
16 (
17 size_words * core::mem::size_of::<u32>(),
18 used_words * core::mem::size_of::<u32>(),
19 )
20}
21
22/// Counts number of words that are equal to zero
23///
24/// Written in Arm assembly to avoid any issues with pointing at things that are
25/// not validly initialised integers (as far as Rust is concerned).
26///
27/// Returns a count of the number of contiguous words equal to 0x0 at `start`, with a
28/// maximum of `size` words
29///
30/// # Safety
31///
32/// The address `start` must be correctly aligned, and point to a region of memory
33/// of at least `size` words in length.
34unsafe fn stack_unused_bytes_asm(start: *const u32, size: usize) -> usize {
35 let result: usize;
36 unsafe {
37 core::arch::asm!(
38 r#"
39 // skip out if size is zero
40 movs {result}, #0
41 cmp {size}, #0
42 beq 3f
43 2: // loop
44 ldr {scratch}, [{start}]
45 cmp {scratch}, #0
46 // break out if value is non-zero
47 bne 3f
48 // otherwise increment counter
49 adds {result}, {result}, #1
50 adds {start}, {start}, #4
51 // loop if not finished yet
52 cmp {result}, {size}
53 bne 2b
54 // all finished
55 3:
56 "#,
57 size = in(reg) size,
58 start = inout(reg) start => _,
59 result = out(reg) result,
60 scratch = out(reg) _,
61 );
62 }
63 result
64}