Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segmented Stack Tests #3

Merged
merged 6 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/actions/build/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,19 @@ runs:
category: task
sub-category: unwind
test-name: deferred_nested_drop

# *** Tests for task - segmented stack ***

- name: Build test test-task-segmented_stack-function_arguments
uses: ./.github/workflows/actions/build-test
with:
category: task
sub-category: segmented_stack
test-name: function_arguments

- name: Build test test-task-segmented_stack-return_values
uses: ./.github/workflows/actions/build-test
with:
category: task
sub-category: segmented_stack
test-name: return_values
39 changes: 39 additions & 0 deletions .github/workflows/segmented_stack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Run Tests for Segmented Stack

on:
workflow_call:
secrets:
cookie:
required: true

env:
CARGO_TERM_COLOR: always

jobs:
function_arguments:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run test function_arguments
uses: ./.github/workflows/actions/run-test
with:
cookie: ${{ secrets.cookie }}
category: task
sub-category: segmented_stack
test-name: function_arguments

return_values:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run test return_values
uses: ./.github/workflows/actions/run-test
with:
cookie: ${{ secrets.cookie }}
category: task
sub-category: segmented_stack
test-name: return_values
5 changes: 5 additions & 0 deletions .github/workflows/task.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ jobs:
uses: ./.github/workflows/unwind.yaml
secrets:
cookie: ${{ secrets.cookie }}

segmented_stack:
uses: ./.github/workflows/segmented_stack.yaml
secrets:
cookie: ${{ secrets.cookie }}
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,15 @@ path = "examples/tests/task/unwind/deferred_indirect_drop.rs"
[[example]]
name = "test-task-unwind-deferred_nested_drop"
path = "examples/tests/task/unwind/deferred_nested_drop.rs"

[[example]]
name = "test-task-segmented_stack-function_arguments"
path = "examples/tests/task/segmented_stack/function_arguments.rs"

[[example]]
name = "test-task-segmented_stack-nested_functions"
path = "examples/tests/task/segmented_stack/nested_functions.rs"

[[example]]
name = "test-task-segmented_stack-return_values"
path = "examples/tests/task/segmented_stack/return_values.rs"
133 changes: 133 additions & 0 deletions examples/tests/task/segmented_stack/function_arguments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! Test function arguments passing via both registers and the stack when a new
//! stacklet is allocated for a function call.

#![no_std]
#![no_main]
#![feature(naked_functions)]

extern crate alloc;
use core::arch::asm;
use hopter::{boot::main, debug::semihosting, hprintln, task};

#[naked]
extern "C" fn prepare_regs_stack() {
unsafe {
asm!(
// Segmented stack prologue.
// Stacklet allocation size 36, 20 for preserved values plus 16 for
// stack arguments.
// Stack argument size 0.
"mov.w r12, #0x20000000",
"ldr.w r12, [r12]",
"subs.w r12, sp, r12",
"cmp.w r12, #36",
"bge.n 0f",
"svc #255",
".short 9",
".short 0",

// Preserve callee-saved registers and return address.
"0:",
"push {{r4-r7, lr}}",

// Prepare arguments in registers r0-r3.
"mov r0, #1",
"mov r1, #2",
"mov r2, #3",
"mov r3, #4",

// Prepare arguments on stack.
"mov r4, #5",
"mov r5, #6",
"mov r6, #7",
"mov r7, #8",
"push {{r4-r7}}",

// Call `verify_arguments` function.
"bl {verify_arguments}",

// Discard stack arguments.
"add sp, #16",

// Restore callee-saved registers and return.
"pop {{r4-r7, pc}}",

verify_arguments = sym verify_arguments,
options(noreturn)
)
}
}

#[naked]
extern "C" fn verify_arguments() {
unsafe {
asm!(
// Segmented stack prologue. Request a huge stack frame of size
// 16384 bytes, which will very likely cause a new stacklet
// allocation.
//
// Stacklet allocation size 16384.
// Stack argument size 16.
"mov.w r12, #0x20000000",
"ldr.w r12, [r12]",
"subs.w r12, sp, r12",
"cmp.w r12, #16384",
"bge.n 0f",
"svc #255",
".short 4096",
".short 4",

"0:",
// Verify register arguments.
"cmp r0, #1",
"bne {error}",
"cmp r1, #2",
"bne {error}",
"cmp r2, #3",
"bne {error}",
"cmp r3, #4",
"bne {error}",

// Verify stack arguments.
"ldr r0, [sp, #0]",
"cmp r0, #5",
"bne {error}",
"ldr r0, [sp, #4]",
"cmp r0, #6",
"bne {error}",
"ldr r0, [sp, #8]",
"cmp r0, #7",
"bne {error}",
"ldr r0, [sp, #12]",
"cmp r0, #8",
"bne {error}",

// Print success message.
// This also tests tail call optimization.
"b {success}",

error = sym error,
success = sym success,
options(noreturn)
)
}
}

extern "C" fn success() {
hprintln!("Test Passed");
}

extern "C" fn error() {
hprintln!("Test Failed");
semihosting::terminate(false);
}

#[main]
fn main(_: cortex_m::Peripherals) {
task::build()
.set_entry(|| prepare_regs_stack())
.spawn()
.unwrap();
task::change_current_priority(10).unwrap();
semihosting::terminate(true);
}
1 change: 1 addition & 0 deletions examples/tests/task/segmented_stack/function_arguments.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test Passed
106 changes: 106 additions & 0 deletions examples/tests/task/segmented_stack/return_values.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! Test function return values via registers.
//!
//! FIXME: Does return value ever get passed through stack?

#![no_std]
#![no_main]
#![feature(naked_functions)]

extern crate alloc;
use core::arch::asm;
use hopter::{boot::main, debug::semihosting, hprintln, task};

#[naked]
extern "C" fn callee() {
unsafe {
asm!(
// Segmented stack prologue. Request a huge stack frame of size
// 16384 bytes, which will very likely cause a new stacklet
// allocation.
//
// Stacklet allocation size 16384.
// Stack argument size 16.
"mov.w r12, #0x20000000",
"ldr.w r12, [r12]",
"subs.w r12, sp, r12",
"cmp.w r12, #16384",
"bge.n 0f",
"svc #255",
".short 4096",
".short 4",
"0:",
// Set r0-r3 to known values as the return values.
"mov r0, #25",
"mov r1, #26",
"mov r2, #27",
"mov r3, #28",
// Return.
"bx lr",
options(noreturn)
)
}
}

#[naked]
extern "C" fn caller() {
unsafe {
asm!(
// Segmented stack prologue.
// Stacklet allocation size 4 for preserved return address.
// stack arguments.
// Stack argument size 0.
"mov.w r12, #0x20000000",
"ldr.w r12, [r12]",
"subs.w r12, sp, r12",
"cmp.w r12, #4",
"bge.n 0f",
"svc #255",
".short 1",
".short 0",

"0:",
// Preserve return address.
"push {{lr}}",

// Call the callee function.
"bl {callee}",

// verify return values in registers r0-r3
"cmp r0, #25",
"bne {error}",
"cmp r1, #26",
"bne {error}",
"cmp r2, #27",
"bne {error}",
"cmp r3, #28",
"bne {error}",

// Print success message.
"bl {success}",

// Return.
"pop {{pc}}",

callee = sym callee,
error = sym error,
success = sym success,
options(noreturn)
)
}
}

extern "C" fn success() {
hprintln!("Test Passed");
}

extern "C" fn error() {
hprintln!("Test Failed");
semihosting::terminate(false);
}

#[main]
fn main(_: cortex_m::Peripherals) {
task::build().set_entry(|| caller()).spawn().unwrap();
task::change_current_priority(10).unwrap();
semihosting::terminate(true);
}
1 change: 1 addition & 0 deletions examples/tests/task/segmented_stack/return_values.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test Passed