Skip to content

Commit 82ac816

Browse files
committed
ZJIT: Branch on the ivar shape for attr_accessor sends in polymorphic arms
[zjit/min port note] Hand-ported af5bc8f, 98f1b7a and 2f92871, adapted to min's ivar dispatch, plus afe2f67 ("Branch on the ivar shape in an attr_reader's final version"). The previous commit handed a dispatch arm its shape only when every bucket profiled for the class agreed on it, because a consumer would *guard* that shape and side-exit on a miss. Upstream instead marks such a type (`ProfiledType::as_polymorphic_arm`) and has consumers branch on it: a receiver of the right class but another shape takes the generic C helper instead of side-exiting out of a version that never promised the shape. With that, an arm can hand over every shape its class was profiled with, which is what actually moves getivar_fallback_no_profile_missing_ic. * `Flags::IS_POLYMORPHIC_ARM` marks a type synthesized for an arm rather than observed at that program point. * `emit_polymorphic_send` hands each arm every bucket for its class, not just the one that named the arm. A sibling has to carry at least 1/`ARM_SHAPE_MIN_SHARE` of the class's samples to earn an arm: the arms live in the caller and share its inline budget, so specializing a shape almost nobody has can push a hot callee out of the budget and cost more in dynamic sends than it saves in ivar reads. * The `VM_METHOD_TYPE_IVAR` and `VM_METHOD_TYPE_ATTRSET` send paths route a polymorphic-arm profile through `dispatch_getivar`/`dispatch_setivar` instead of a shape guard, over every shape the arm carries (`profiled_shape_variants`). attr_writer's shape transition is not itself speculative: the destination shape is a function of the source shape and the ivar, and the branch has already established the source shape. * attr_reader's final version (`no_side_exits`) previously emitted an unconditional `rb_ivar_get`; it now branches the same way `getinstancevariable` already did. Adaptations: min's `dispatch_ivar` already ends every shape chain in a branch to a generic fallback rather than a side exit, so upstream's three-valued `ShapeMiss` collapses onto min's existing `covers_profile` flag (false asks for the branchy form) plus a new `reprofile_on_miss` flag. The latter is upstream's `CallFallbackWithoutReprofile`: sampling an arm's fallback miss would earn the ISEQ a re-profile and throw away the type profile the arms were built from. `try_emit_optimized_setivar` is deleted, as upstream does, now that the attr_writer path drives `prepare_optimized_setivar` directly.
1 parent 1ee1ff0 commit 82ac816

4 files changed

Lines changed: 223 additions & 46 deletions

File tree

zjit/src/distribution.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,23 @@ impl<T: Copy + PartialEq + Default + std::fmt::Debug, const N: usize> Distributi
278278
/// already been split into per-type branches: within a branch, the receiver is known to have
279279
/// one specific profiled type, so downstream specialization can treat it as monomorphic.
280280
pub fn monomorphic(profiled_type: T) -> Self {
281+
Self::monomorphic_variants(&[profiled_type])
282+
}
283+
284+
/// Build a monomorphic summary out of several items that a consumer treats as one. Dispatch
285+
/// arms use this: the arm has already branched on the Ruby class, so every item in it is the
286+
/// same class as far as method lookup is concerned (hence `Monomorphic`), but the items still
287+
/// differ in the shape they carry, and a consumer that specializes on shape wants all of them.
288+
/// `items` beyond the bucket count are dropped, most significant first.
289+
pub fn monomorphic_variants(items: &[T]) -> Self {
290+
assert!(N > 0);
291+
assert!(!items.is_empty(), "a monomorphic summary needs at least one item");
281292
let mut buckets = [Default::default(); N];
282-
buckets[0] = profiled_type;
283293
let mut counts = [0; N];
284-
counts[0] = 1;
294+
for (i, &item) in items.iter().take(N).enumerate() {
295+
buckets[i] = item;
296+
counts[i] = 1;
297+
}
285298
Self { kind: DistributionKind::Monomorphic, buckets, counts, other: 0 }
286299
}
287300

0 commit comments

Comments
 (0)