perf(context): pass actor sp through registers, not TLS

switch_to_actor takes the target sp in rdi and returns the actor's
next saved sp in rax (handed over by switch_to_scheduler's shim).
Deletes the ACTOR_SP thread-local and halves the helper calls per
one-way switch (2 -> 1); the scheduler loop also drops its
set_actor_sp/get_actor_sp TLS round-trips. SCHEDULER_SP stays: a
yielding actor at arbitrary call depth has no argument channel back.

asm before/after in outputs/history.md session 1-2. Tests: 354 pass.
This commit is contained in:
claude-asm-audit
2026-08-21 12:22:45 +00:00
parent 741c10337b
commit d7082eb266
3 changed files with 54 additions and 58 deletions
+10 -22
View File
@@ -1,9 +1,7 @@
//! Low-level context-switch tests. These poke `init_actor_stack` and the
//! naked asm shims directly — no scheduler involved.
use smarm::context::{
get_actor_sp, init_actor_stack, set_actor_sp, switch_to_actor, switch_to_scheduler,
};
use smarm::context::{init_actor_stack, switch_to_actor, switch_to_scheduler};
use smarm::stack::Stack;
use std::cell::Cell;
@@ -31,8 +29,7 @@ fn actor_runs_and_returns_to_scheduler() {
reset_log();
let stack = Stack::new(64 * 1024, 4096).unwrap();
let sp = init_actor_stack(stack.top(), actor_simple);
set_actor_sp(sp);
unsafe { switch_to_actor() };
let _ = unsafe { switch_to_actor(sp) };
assert_eq!(get_log(), 0x1);
}
@@ -48,12 +45,11 @@ fn actor_yields_and_resumes() {
reset_log();
let stack = Stack::new(64 * 1024, 4096).unwrap();
let sp = init_actor_stack(stack.top(), actor_two_steps);
set_actor_sp(sp);
unsafe { switch_to_actor() };
let sp = unsafe { switch_to_actor(sp) };
assert_eq!(get_log(), 0x1, "after first resume");
unsafe { switch_to_actor() };
let _ = unsafe { switch_to_actor(sp) };
assert_eq!(get_log(), 0x1 | 0x2, "after second resume");
}
@@ -96,10 +92,9 @@ extern "C-unwind" fn actor_reg_check() {
fn callee_saved_registers_survive_yield() {
let stack = Stack::new(64 * 1024, 4096).unwrap();
let sp = init_actor_stack(stack.top(), actor_reg_check);
set_actor_sp(sp);
unsafe {
switch_to_actor();
switch_to_actor();
let sp = switch_to_actor(sp);
let _ = switch_to_actor(sp);
}
assert_eq!(
REG_BEFORE.get().copied().unwrap(),
@@ -138,18 +133,11 @@ fn two_actors_dont_corrupt_each_other() {
let sp_a = init_actor_stack(stack_a.top(), actor_a);
let sp_b = init_actor_stack(stack_b.top(), actor_b);
set_actor_sp(sp_a);
unsafe { switch_to_actor() };
let sp_a = get_actor_sp();
let sp_a = unsafe { switch_to_actor(sp_a) };
let sp_b = unsafe { switch_to_actor(sp_b) };
set_actor_sp(sp_b);
unsafe { switch_to_actor() };
let sp_b = get_actor_sp();
set_actor_sp(sp_a);
unsafe { switch_to_actor() };
set_actor_sp(sp_b);
unsafe { switch_to_actor() };
let _ = unsafe { switch_to_actor(sp_a) };
let _ = unsafe { switch_to_actor(sp_b) };
assert_eq!(A_VAL.with(|c| c.get()), 0xA00D);
assert_eq!(B_VAL.with(|c| c.get()), 0xB00D);