From b8ebc37a72bedd14a35dd1486e3e4d87888b4f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 9 Aug 2026 01:36:31 +0200 Subject: [PATCH 1/4] perf(repsel): resolve object-literal element types in the element-shape loop clone (#7480) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #7480's own kernel (`keep: {v, w}[]`, 200k x 50 sweeps) went from 408 ms to 12 ms on the pinned quiet mini — parity with node (12 ms) and bun (12 ms), down from 34x node. The named-class arm #7612 already covered is unchanged at 13 ms. Checksums identical across every arm and runtime. `element_class_name` resolved `Array(Named(C))` only, so an object-literal element type never reached the clone. It now also resolves the declared object type to the `__AnonShape_` class its literals allocate, by matching the declared property order against the module's anon shapes (ambiguity declines rather than guessing, so the answer does not depend on `ctx.classes` iteration order). `receiver_class_name` is deliberately NOT widened — that is the #6377 blast radius #7612 refused. The clone is made self-contained instead: its `ElementShapeLoopFact` already carried the class name and packed slot index, and the three sites that would otherwise re-derive the class from the receiver now consult that fact through one predicate. Claude-Session: https://claude.ai/code/session_01Y1QZ5wUP9gRSwpiweT4Wix --- crates/perry-codegen/src/expr/binary.rs | 14 +- crates/perry-codegen/src/expr/mod.rs | 61 +++- .../src/expr/property_get/helpers.rs | 149 ++++---- .../src/stmt/element_shape_loop.rs | 178 +++++++-- .../src/stmt/element_shape_loop_tests.rs | 342 +++++++++++++++++- .../src/type_analysis/numeric.rs | 19 + docs/engine-plan.md | 105 +++--- ...est_gap_repsel_element_shape_loop_clone.ts | 184 ++++++++++ 8 files changed, 889 insertions(+), 163 deletions(-) diff --git a/crates/perry-codegen/src/expr/binary.rs b/crates/perry-codegen/src/expr/binary.rs index 7cfe31b07c..5ea824f5dc 100644 --- a/crates/perry-codegen/src/expr/binary.rs +++ b/crates/perry-codegen/src/expr/binary.rs @@ -58,7 +58,19 @@ fn lower_arithmetic_operand(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<(String, return Ok((value, true)); } } - if expr_may_return_boxed_value_from_raw_f64_fallback(ctx, expr) { + // repsel #7480 step 3: a tracked `arr[i].field` read inside an + // element-shape fast clone routes to the raw-f64 lowering WITHOUT the + // boxed-fallback test below. That test asks `receiver_class_name`, which + // by design does not resolve an object-literal element type, so the read + // would otherwise fall through to `lower_expr` — a generic diamond, whose + // calls then fail the clone's call-free admission and cost the clone + // entirely. The predicate is left alone rather than widened: this read has + // no boxed fallback at all (the residual per-element check proves the slot + // is a raw double before the load), so claiming one here would be a lie + // that other consumers of that predicate would read. + let in_element_shape_clone = matches!(expr, Expr::PropertyGet { object, property, .. } + if crate::expr::element_shape_loop_fact_for_property_get(ctx, object, property).is_some()); + if in_element_shape_clone || expr_may_return_boxed_value_from_raw_f64_fallback(ctx, expr) { if let Some(value) = super::property_get::lower_raw_f64_class_field_get_for_number_context(ctx, expr)? { diff --git a/crates/perry-codegen/src/expr/mod.rs b/crates/perry-codegen/src/expr/mod.rs index 1900983249..1417a83c58 100644 --- a/crates/perry-codegen/src/expr/mod.rs +++ b/crates/perry-codegen/src/expr/mod.rs @@ -1605,21 +1605,56 @@ pub(crate) struct ElementShapeLoopFact { pub max_field_index: u32, } -/// Find the innermost active element-shape loop fact covering -/// `(array_local_id, index_local_id, class_name, property)`. Returns the fact -/// and the packed slot index of the field. -pub(crate) fn element_shape_loop_fact_lookup<'f>( - facts: &'f [ElementShapeLoopFact], - array_local_id: u32, - index_local_id: u32, - class_name: &str, +/// Find the innermost active element-shape loop fact covering a +/// `PropertyGet`'s receiver: answers `Some((fact, packed_slot_index))` exactly +/// when `object.property` is a tracked `arr[counter].field` read inside an +/// element-shape fast clone. +/// +/// The single entry point for the three sites that must agree about that read +/// — the field lowering itself +/// (`expr::property_get::lower_raw_f64_class_field_get_for_number_context`), +/// `type_analysis::is_numeric_expr`, and `expr::binary`'s arithmetic-operand +/// router. #7480 step 3 made the clone self-contained by routing all three +/// through the fact instead of through `receiver_class_name`, which by design +/// does not resolve an object-literal element type; the fact's own +/// `class_name` is therefore the authoritative answer rather than a filter on +/// one the caller supplies. +/// +/// `(array, counter)` already identifies one loop — a counter local is minted +/// per `for`, and the matcher admits exactly one array per loop. Cheap +/// early-out first: outside a fast clone the fact vector is empty. +/// +/// The **canonical-i32 counter slot is part of the predicate**, not a +/// precondition the caller re-checks. Answering `Some` is a promise that the +/// read really does take the bare-load lowering, and `is_numeric_expr` bets a +/// raw `double` on that promise: if the field lowering declined for want of an +/// i32 slot while the numeric predicate still said yes, the operand would be +/// consumed as a real double while the generic lowering handed back a NaN-boxed +/// value. The matcher declines the whole loop without that slot +/// (`lower_element_shape_versioned_for`), so today the two can't disagree — +/// asking here keeps them unable to disagree if the matcher is ever widened. +pub(crate) fn element_shape_loop_fact_for_property_get<'f>( + ctx: &'f FnCtx<'_>, + object: &perry_hir::Expr, property: &str, ) -> Option<(&'f ElementShapeLoopFact, u32)> { - facts.iter().rev().find_map(|fact| { - if fact.array_local_id != array_local_id - || fact.index_local_id != index_local_id - || fact.class_name != class_name - { + use perry_hir::Expr; + if ctx.element_shape_loop_facts.is_empty() { + return None; + } + let Expr::IndexGet { object, index } = object else { + return None; + }; + let (Expr::LocalGet(array_local_id), Expr::LocalGet(index_local_id)) = + (object.as_ref(), index.as_ref()) + else { + return None; + }; + if !ctx.i32_counter_slots.contains_key(index_local_id) { + return None; + } + ctx.element_shape_loop_facts.iter().rev().find_map(|fact| { + if fact.array_local_id != *array_local_id || fact.index_local_id != *index_local_id { return None; } fact.fields.get(property).map(|idx| (fact, *idx)) diff --git a/crates/perry-codegen/src/expr/property_get/helpers.rs b/crates/perry-codegen/src/expr/property_get/helpers.rs index fc6c84f2c3..36ea0727c8 100644 --- a/crates/perry-codegen/src/expr/property_get/helpers.rs +++ b/crates/perry-codegen/src/expr/property_get/helpers.rs @@ -331,6 +331,81 @@ pub(crate) fn lower_raw_f64_class_field_get_for_number_context( } } + // repsel #7480 / #5093: inside the fast clone of an ELEMENT-shape + // versioned loop, `arr[i].field` in number context lowers to a bare + // element load plus the residual per-element check, with no element-read + // tier and no guard call (see stmt/element_shape_loop.rs). + // + // #7480 step 3: this sits ABOVE the `receiver_class_name` gate on purpose. + // The clone's element class can be one that resolver does not answer for — + // an object-literal element type (`keep: {v: number}[]`) resolves to its + // `__AnonShape_` only inside the matcher, which is where that + // resolution is kept so it cannot un-gate anything else (#6377). Every + // fact this consults was validated by the matcher when the fact was built: + // the class has no computed members and no base, the property is not an + // accessor and is not denylisted, and its declared type is a raw-f64 + // candidate at the packed slot index carried here. So the lowering needs + // nothing from the receiver's static type, and asking for it would have + // made the whole clone dead IR. + if let Some((fact, field_index)) = + crate::expr::element_shape_loop_fact_for_property_get(ctx, object, property) + .map(|(fact, idx)| (fact.clone(), idx)) + { + if let Expr::IndexGet { object: array, .. } = object.as_ref() { + if let Expr::LocalGet(arr_id) = array.as_ref() { + // The counter's canonical i32 slot is what the matcher + // required; without it there is nothing to index with. + if let Some(slot) = ctx.i32_counter_slots.get(&fact.index_local_id).cloned() { + let idx_i32 = ctx.block().load(I32, &slot); + let value = crate::expr::element_shape_guard::emit_element_shape_field_load( + ctx, + &fact, + &idx_i32, + field_index, + ); + let lowered = LoweredValue { + semantic: SemanticKind::JsNumber, + rep: NativeRep::F64, + llvm_ty: DOUBLE, + value: value.clone(), + }; + ctx.record_lowered_value_with_access_mode_and_facts( + "ElementShapeFieldGet", + Some(*arr_id), + "element_shape_loop.raw_f64_load", + &lowered, + Some(BoundsState::Guarded { + guard_id: "element_shape_loop_preheader_check".to_string(), + }), + None, + Some(BufferAccessMode::CheckedNative), + None, + None, + None, + vec![raw_f64_layout_fact( + Some(*arr_id), + "consumed", + "element_shape_loop_preheader_check", + None, + )], + Vec::new(), + false, + false, + vec![ + format!("field={property}"), + format!("class={}", fact.class_name), + "loop_versioning=element_shape".to_string(), + "index_range=nonnegative_i32".to_string(), + "length_range=guarded_i32".to_string(), + "element_shape=homogeneous_class".to_string(), + ], + ); + return Ok(Some(value)); + } + } + } + } + let Some(class_name) = receiver_class_name(ctx, object) else { return Ok(None); }; @@ -368,80 +443,6 @@ pub(crate) fn lower_raw_f64_class_field_get_for_number_context( return Ok(None); }; - // repsel #7480 / #5093: inside the fast clone of an ELEMENT-shape - // versioned loop, `arr[i].field` in number context lowers to a bare - // element load plus the residual per-element check, with no element-read - // tier and no guard call (see stmt/element_shape_loop.rs). Checked before - // the class-field fact because the receiver shapes are disjoint - // (`IndexGet` vs `LocalGet`) and this one is the cheaper lowering. - if !ctx.element_shape_loop_facts.is_empty() { - if let Expr::IndexGet { object, index } = object.as_ref() { - if let (Expr::LocalGet(arr_id), Expr::LocalGet(idx_id)) = - (object.as_ref(), index.as_ref()) - { - let hit = crate::expr::element_shape_loop_fact_lookup( - &ctx.element_shape_loop_facts, - *arr_id, - *idx_id, - &class_name, - property, - ) - .filter(|(_, loop_idx)| *loop_idx == field_index) - .map(|(fact, _)| fact.clone()); - if let Some(fact) = hit { - // The counter's canonical i32 slot is what the matcher - // required; without it there is nothing to index with. - if let Some(slot) = ctx.i32_counter_slots.get(idx_id).cloned() { - let idx_i32 = ctx.block().load(I32, &slot); - let value = crate::expr::element_shape_guard::emit_element_shape_field_load( - ctx, - &fact, - &idx_i32, - field_index, - ); - let lowered = LoweredValue { - semantic: SemanticKind::JsNumber, - rep: NativeRep::F64, - llvm_ty: DOUBLE, - value: value.clone(), - }; - ctx.record_lowered_value_with_access_mode_and_facts( - "ElementShapeFieldGet", - Some(*arr_id), - "element_shape_loop.raw_f64_load", - &lowered, - Some(BoundsState::Guarded { - guard_id: "element_shape_loop_preheader_check".to_string(), - }), - None, - Some(BufferAccessMode::CheckedNative), - None, - None, - None, - vec![raw_f64_layout_fact( - Some(*arr_id), - "consumed", - "element_shape_loop_preheader_check", - None, - )], - Vec::new(), - false, - false, - vec![ - format!("field={property}"), - "loop_versioning=element_shape".to_string(), - "index_range=nonnegative_i32".to_string(), - "length_range=guarded_i32".to_string(), - "element_shape=homogeneous_class".to_string(), - ], - ); - return Ok(Some(value)); - } - } - } - } - } - // #5093 loop versioning: inside the fast clone of a class-field versioned // loop, a tracked number-context field read on the proven receiver lowers // to a bare slot load on the preheader-cached object pointer — no shape diff --git a/crates/perry-codegen/src/stmt/element_shape_loop.rs b/crates/perry-codegen/src/stmt/element_shape_loop.rs index 3bc269463e..0bf0f466da 100644 --- a/crates/perry-codegen/src/stmt/element_shape_loop.rs +++ b/crates/perry-codegen/src/stmt/element_shape_loop.rs @@ -176,9 +176,24 @@ fn element_shape_loop_pure_expr_collect( array.is_none_or(|a| a != *id) && crate::type_analysis::is_numeric_expr(ctx, expr) } Expr::Number(_) | Expr::Integer(_) => true, + // NOTE (#7480 step 3): deliberately NOT gated on + // `is_numeric_expr(ctx, expr)`. `BinaryOp` is arithmetic/bitwise only + // (no `in`/`instanceof`), so the sole hazard the whole-expression test + // covered was `+` on a possibly-string operand — and every leaf this + // walk admits is numeric by the time the match is ACCEPTED: numeric + // locals and literals by their own arms, and tracked `arr[j].field` + // reads because the caller rejects the whole loop unless every + // collected property is a declared raw-f64 candidate on the resolved + // element class. + // + // The gate had to go for the object-literal kernel: at match time no + // fact is installed yet, so `is_numeric_expr` cannot see through + // `keep[j].v` (its `PropertyGet` arm resolves the owner through + // `receiver_class_name`, which by design does not type an + // object-literal element). Keeping it would have declined #7480's own + // kernel before the class resolver was ever consulted. Expr::Binary { left, right, .. } => { - crate::type_analysis::is_numeric_expr(ctx, expr) - && element_shape_loop_pure_expr_collect(ctx, left, counter_id, array, props) + element_shape_loop_pure_expr_collect(ctx, left, counter_id, array, props) && element_shape_loop_pure_expr_collect(ctx, right, counter_id, array, props) } Expr::NumberCoerce(operand) => { @@ -208,35 +223,150 @@ fn element_shape_loop_pure_expr_collect( /// Resolve the class every element of `array_id` must have for the clone to /// fire. /// -/// Exactly one source: `receiver_class_name` on the `IndexGet`, i.e. a -/// declared element type (`keep: Node[]`) — the same path Perry already uses -/// to resolve `items[2].display()`. It does not have to be *right*: the -/// preheader compares the class id the runtime invariant reports against this -/// one, so a wrong answer costs the clone, never correctness. +/// Two sources, tried in order: +/// +/// 1. `receiver_class_name` on the `IndexGet`, i.e. a declared *named* element +/// type (`keep: Node[]`) — the same path Perry already uses to resolve +/// `items[2].display()`. +/// 2. #7480 step 3: an **object-literal element type** (`keep: {v: number}[]`), +/// resolved to the `__AnonShape_` class the literals actually +/// allocate ([`anon_shape_class_for_element_type`]). This is #7480's own +/// kernel and the whole measured gap — 414 ms vs node's 12 on 200k × 50, +/// where the named-class arm is already 13 ms. /// -/// **Deliberately NOT resolved: object-literal element types** -/// (`keep: {v: number}[]`), which is #7480's own kernel — an object literal -/// lowers to `New { __AnonShape_ }`, so a class id genuinely exists, and -/// matching the declared object type's property order against this module's -/// `__AnonShape_*` classes would find it. It is left out on purpose, because -/// the class name alone is not enough: `receiver_class_name` returning `None` -/// is also what makes `lower_raw_f64_class_field_get_for_number_context` -/// decline, so the read would still lower through the generic diamond, the -/// clone would fail its call-free check, and the only thing the wider matcher -/// would buy is a block of dead fast-clone IR. Reaching that kernel means -/// teaching `static_type_of` / `receiver_class_name` to type an `Object`-typed -/// property read — which is precisely the #6377 "more type visibility un-gates -/// latent fast paths" change, and belongs in its own PR with its own gap-suite -/// A/B. Measured cost of the omission: the object-literal kernel stays at -/// ~88 ms while the named-class kernel goes 43 → 16 ms. +/// Neither has to be *right*: the preheader compares the class id the runtime +/// invariant reports against this one, so a wrong answer costs the clone, +/// never correctness. The annotation stays a hint, never layout. fn element_class_name(ctx: &FnCtx<'_>, array_id: u32, counter_id: u32) -> Option { - crate::type_analysis::receiver_class_name( + if let Some(named) = crate::type_analysis::receiver_class_name( ctx, &perry_hir::Expr::IndexGet { object: Box::new(perry_hir::Expr::LocalGet(array_id)), index: Box::new(perry_hir::Expr::LocalGet(counter_id)), }, - ) + ) { + return Some(named); + } + anon_shape_class_for_element_type(ctx, array_id) +} + +/// Content-addressed synthetic class every closed-shape object literal lowers +/// to (`perry-hir/src/lower/context.rs::mint_anon_shape_class`). +const ANON_SHAPE_PREFIX: &str = "__AnonShape_"; + +/// #7480 step 3: resolve `keep: {v: number, w: number}[]` to the +/// `__AnonShape_` class its literals allocate. +/// +/// **Why not widen `receiver_class_name`.** That is the #6377 blast radius +/// #7612 deliberately refused — every consumer of the receiver-class resolver +/// would start seeing a class for an `Object`-typed read, un-gating latent +/// fast paths this change never measured. The resolver therefore lives here, +/// in the matcher, and the fast clone is made self-contained instead: its +/// field read carries its own `class_name` + packed slot index on +/// `ElementShapeLoopFact`, and the three predicates that would otherwise have +/// re-derived the class from the receiver +/// (`lower_raw_f64_class_field_get_for_number_context`, `is_numeric_expr`, +/// `lower_arithmetic_operand`'s routing test) consult that fact instead. All +/// three are scoped to the fast clone, where the guard has already proven the +/// element's class *and* — via the residual check's +/// `GC_OBJ_TYPED_LAYOUT_INTACT` bit — that the slot really holds a raw double. +/// +/// **Why the hash cannot be recomputed.** `mint_anon_shape_class` keys the +/// FNV hash on the literal's *inferred value* types (`{v: 1}` tags `i`, not +/// `n`), while the annotation says `number`. So the class is found by matching +/// the declared property order against the module's anon shapes, not by +/// recomputing the name. +/// +/// Ambiguity declines rather than guesses: two anon shapes can share a field +/// name list (`{v: n, w: n}` vs `{v: s, w: s}`), so candidates are narrowed by +/// field-type compatibility and a still-ambiguous set returns `None`. That +/// keeps the answer independent of `ctx.classes` iteration order, which is a +/// `HashMap`'s. +fn anon_shape_class_for_element_type(ctx: &FnCtx<'_>, array_id: u32) -> Option { + use perry_hir::types::Type as HirType; + + let elem = match ctx.local_types.get(&array_id)? { + HirType::Array(elem) => elem.as_ref(), + // `new Array<{v: number}>(n)` locals carry the generic spelling. + HirType::Generic { base, type_args } if base == "Array" && type_args.len() == 1 => { + &type_args[0] + } + _ => return None, + }; + let HirType::Object(obj) = elem else { + return None; + }; + // Only a CLOSED shape names a layout: an index signature, a method + // signature (which `property_order` does not record) or an optional + // property all mean the runtime object may not have exactly these slots. + if obj.index_signature.is_some() { + return None; + } + let order = obj.property_order.as_ref()?; + if order.is_empty() || order.len() != obj.properties.len() { + return None; + } + if obj.properties.values().any(|p| p.optional) { + return None; + } + + let mut candidates: Vec<&str> = ctx + .classes + .iter() + .filter(|(name, class)| { + name.starts_with(ANON_SHAPE_PREFIX) + // The clone's packed slot indices describe ONE class's own + // fields; an inherited layout or a computed key would not be + // self-describing. Anon shapes never have either, so this is + // a belt-and-braces check that keeps the invariant local. + && class.extends_name.is_none() + && class.computed_members.is_empty() + && class.fields.len() == order.len() + && class + .fields + .iter() + .zip(order) + .all(|(f, want)| f.key_expr.is_none() && f.name == *want) + }) + .map(|(name, _)| name.as_str()) + .collect(); + if candidates.len() > 1 { + candidates.retain(|name| { + ctx.classes.get(*name).is_some_and(|class| { + class.fields.iter().all(|f| { + obj.properties + .get(&f.name) + .is_some_and(|p| anon_shape_field_type_is_compatible(&p.ty, &f.ty)) + }) + }) + }); + } + match candidates.as_slice() { + [only] => Some((*only).to_string()), + _ => None, + } +} + +/// Disambiguator for [`anon_shape_class_for_element_type`]: is a synthesized +/// anon-shape field type (inferred from the literal's VALUES) consistent with +/// the declared property type (an annotation)? +/// +/// Only used to break a tie between same-named shapes, so it is deliberately +/// coarse — `Number`/`Int32` are one bucket (`{v: 1}` infers `Int32` for a +/// `number`-declared property), as are `String`/`StringLiteral`. +fn anon_shape_field_type_is_compatible( + declared: &perry_hir::types::Type, + actual: &perry_hir::types::Type, +) -> bool { + use perry_hir::types::Type as T; + match (declared, actual) { + (T::Number | T::Int32, T::Number | T::Int32) => true, + (T::String | T::StringLiteral(_), T::String | T::StringLiteral(_)) => true, + // An `any`/`unknown` annotation names no layout, so it rules nothing + // out; every other pair must agree exactly. + (T::Any | T::Unknown, _) => true, + (d, a) => d == a, + } } /// Match `for (let j = k0; j < B; j++) acc = `. diff --git a/crates/perry-codegen/src/stmt/element_shape_loop_tests.rs b/crates/perry-codegen/src/stmt/element_shape_loop_tests.rs index 7f2c67a881..87027b109e 100644 --- a/crates/perry-codegen/src/stmt/element_shape_loop_tests.rs +++ b/crates/perry-codegen/src/stmt/element_shape_loop_tests.rs @@ -122,6 +122,51 @@ fn node_class(extends_name: Option<&str>) -> Class { } } +/// A closed-shape object literal's synthesized class, exactly as +/// `perry-hir`'s `mint_anon_shape_class` builds it: content-addressed name, no +/// base, no computed members, fields in source order with `init: None`. +fn anon_shape_class(id: u32, name: &str, fields: &[(&str, Type)]) -> Class { + let mut class = node_class(None); + class.id = id; + class.name = name.to_string(); + class.fields = fields + .iter() + .map(|(field, ty)| ClassField { + name: (*field).to_string(), + key_expr: None, + ty: ty.clone(), + init: None, + is_private: false, + is_readonly: false, + decorators: Vec::new(), + }) + .collect(); + class +} + +/// The declared element type of #7480's own kernel: `{ v: number; w: number }`. +fn object_element_type(fields: &[(&str, Type)], optional: bool) -> Type { + let mut properties = std::collections::HashMap::new(); + let mut property_order = Vec::new(); + for (name, ty) in fields { + property_order.push((*name).to_string()); + properties.insert( + (*name).to_string(), + perry_hir::types::PropertyInfo { + ty: ty.clone(), + optional, + readonly: false, + }, + ); + } + Type::Object(perry_hir::types::ObjectType { + name: None, + properties, + property_order: Some(property_order), + index_signature: None, + }) +} + /// `keep[].v` fn elem_field(array_id: u32, index: Expr) -> Expr { Expr::PropertyGet { @@ -194,6 +239,25 @@ fn element_shape_module(body: Vec, extends_name: Option<&str>) -> Module { m } +/// `const keep: [] = []; let sum = 0; for (let j = 0; j < N; j++) sum += +/// keep[j].v` with an explicit set of module classes — the object-literal +/// twin of [`element_shape_module`] (#7480 step 3). +fn object_element_module(elem: Type, classes: Vec) -> Module { + let mut m = element_shape_module( + vec![accumulate_stmt( + SUM_ID, + ARRAY_ID, + Expr::LocalGet(COUNTER_ID), + )], + None, + ); + m.classes = classes; + if let Some(Stmt::Let { ty, .. }) = m.init.first_mut() { + *ty = Type::Array(Box::new(elem)); + } + m +} + fn emit(m: &Module) -> String { String::from_utf8(compile_module(m, ir_opts()).unwrap()).expect("LLVM IR should be UTF-8") } @@ -220,17 +284,47 @@ fn block_slice<'a>(ir: &'a str, label: &str) -> &'a str { &body[..end] } -/// The emitted text the fast clone owns: from its cond block to the slow -/// clone's. +/// Byte offset of the DEFINITION of block `label` — a line that begins at +/// column 0 with `