diff --git a/changelog.d/8012-async-control-cells.md b/changelog.d/8012-async-control-cells.md new file mode 100644 index 0000000000..d189799a8d --- /dev/null +++ b/changelog.d/8012-async-control-cells.md @@ -0,0 +1,18 @@ +### Faster compiler-private async control cells (#8008) + +Async and generator state machines now access their compiler-minted state, +pending-type, done, and executing cells with direct typed loads and stores. +Those cells are preallocated as single-field `I32Box` or `BoolBox` values, so +their pointer provenance and representation are already proven; routing every +access through the general runtime helpers redundantly checked the same pointer +against a thread-local box registry. + +Ordinary user captures keep the checked runtime path, including its invalid-box +protection. Primitive allocation and registry insertion also remain unchanged; +only access to the four private controls bypasses repeated validation. + +On the refreshed async probe set this removes 7.08% of retired instructions +from the pure async/await topology, 5.92% when plain objects flow through the +same topology, and 2.10% from the full `asyncpipe` workload. The synchronous +and Promise.all-only controls remain flat. An IR regression locks down both +the narrow eligibility boundary and the direct typed accesses. diff --git a/crates/perry-codegen/src/expr/mod.rs b/crates/perry-codegen/src/expr/mod.rs index d870e47ba1..0364432f92 100644 --- a/crates/perry-codegen/src/expr/mod.rs +++ b/crates/perry-codegen/src/expr/mod.rs @@ -2297,6 +2297,40 @@ pub(crate) fn load_boxed_local_pointer(ctx: &mut FnCtx<'_>, id: u32) -> Result, cell: &str) -> String { + let ptr = ctx.block().inttoptr(I64, cell); + ctx.block().load(I32, &ptr) +} + +/// Store a compiler-private async i32 control cell directly. See +/// `load_async_i32_control_cell` for the allocation/provenance proof. +pub(crate) fn store_async_i32_control_cell(ctx: &mut FnCtx<'_>, cell: &str, value: &str) { + let ptr = ctx.block().inttoptr(I64, cell); + ctx.block().store(I32, value, &ptr); +} + +/// Load a compiler-private async boolean control cell directly. `BoolBox`'s +/// value is a Rust `bool`, represented as LLVM i1 at the FFI boundary. +pub(crate) fn load_async_i1_control_cell(ctx: &mut FnCtx<'_>, cell: &str) -> String { + let ptr = ctx.block().inttoptr(I64, cell); + ctx.block().load(I1, &ptr) +} + +/// Store a compiler-private async boolean control cell directly. See +/// `load_async_i1_control_cell` for the representation proof. +pub(crate) fn store_async_i1_control_cell(ctx: &mut FnCtx<'_>, cell: &str, value: &str) { + let ptr = ctx.block().inttoptr(I64, cell); + ctx.block().store(I1, value, &ptr); +} + pub(crate) fn box_i1_for_compat_shadow(ctx: &mut FnCtx<'_>, value: &str) -> String { let bits = ctx.block().select( I1, @@ -2381,7 +2415,7 @@ fn lower_async_i32_control_const_compare( let Some(ptr) = load_boxed_local_pointer(ctx, id)? else { return Ok(None); }; - let value = ctx.block().call(I32, "js_i32_box_get", &[(I64, &ptr)]); + let value = load_async_i32_control_cell(ctx, &ptr); let constant_s = constant.to_string(); let (lhs, rhs) = if local_on_left { (value.as_str(), constant_s.as_str()) @@ -2921,7 +2955,7 @@ pub(crate) fn lower_expr_value(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result, expr: &Expr) -> Result, expr: &Expr) -> Result, expr: &Expr) -> Result