Skip to content

Commit 90bc7bc

Browse files
authored
Optimize numeric array raw payload helpers (#4957)
* Optimize numeric array raw payload helpers * Optimize dense numeric array fill loops (#4958) * Optimize class method field fast paths (#4959)
1 parent 3fbeea9 commit 90bc7bc

10 files changed

Lines changed: 762 additions & 39 deletions

File tree

crates/perry-codegen/src/expr/proxy_reflect.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,35 @@ fn put_value_static_property_fast_path(
117117
.scalar_replaced
118118
.get(id)
119119
.is_some_and(|fields| fields.contains_key(property));
120-
(pod_field || scalar_field).then(|| property.clone())
120+
if pod_field || scalar_field {
121+
return Some(property.clone());
122+
}
123+
receiver_class_name(ctx, target)
124+
.and_then(|class_name| {
125+
crate::type_analysis::class_field_global_index(ctx, &class_name, property)
126+
})
127+
.map(|_| property.clone())
128+
}
129+
(Expr::This, Expr::This) => {
130+
if ctx
131+
.scalar_ctor_target
132+
.last()
133+
.and_then(|tid| ctx.scalar_replaced.get(tid))
134+
.is_some_and(|fields| fields.contains_key(property))
135+
{
136+
return Some(property.clone());
137+
}
138+
receiver_class_name(ctx, target)
139+
.and_then(|class_name| {
140+
crate::type_analysis::class_field_global_index(ctx, &class_name, property)
141+
})
142+
.map(|_| property.clone())
143+
}
144+
_ if same_side_effect_free_receiver(target, receiver) => {
145+
let class_name = receiver_class_name(ctx, target)?;
146+
crate::type_analysis::class_field_global_index(ctx, &class_name, property)
147+
.map(|_| property.clone())
121148
}
122-
(Expr::This, Expr::This) => ctx
123-
.scalar_ctor_target
124-
.last()
125-
.and_then(|tid| ctx.scalar_replaced.get(tid))
126-
.and_then(|fields| fields.contains_key(property).then(|| property.clone())),
127149
_ => None,
128150
}
129151
}

crates/perry-codegen/src/lower_call/method_override.rs

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -139,24 +139,29 @@ pub(super) fn emit_guarded_direct_method_call(
139139
direct_fn: &str,
140140
direct_arg_slices: &[(crate::types::LlvmType, &str)],
141141
fallback_user_args: &[String],
142+
shape_only_guard: bool,
142143
) -> Option<String> {
143144
let expected_class_id = *ctx.class_ids.get(receiver_class_name)?;
144145
let keys_global_name = ctx.class_keys_globals.get(receiver_class_name)?.clone();
145146

147+
let expected_class_id_str = expected_class_id.to_string();
148+
let expected_keys_slot = ctx.func.entry_init_load_global(&keys_global_name, I64);
149+
let expected_keys = ctx.block().load(I64, &expected_keys_slot);
150+
146151
let key_idx = ctx.strings.intern(property);
147152
let entry = ctx.strings.entry(key_idx);
148153
let bytes_global = format!("@{}", entry.bytes_global);
149154
let name_len_str = entry.byte_len.to_string();
150-
let expected_class_id_str = expected_class_id.to_string();
151-
152-
let site_id = emit_typed_feedback_register_site(
153-
ctx,
154-
TypedFeedbackKind::MethodCall,
155-
property,
156-
TypedFeedbackContract::method_direct_call(),
157-
);
158-
let expected_keys_slot = ctx.func.entry_init_load_global(&keys_global_name, I64);
159-
let expected_keys = ctx.block().load(I64, &expected_keys_slot);
155+
let site_id = if shape_only_guard {
156+
None
157+
} else {
158+
Some(emit_typed_feedback_register_site(
159+
ctx,
160+
TypedFeedbackKind::MethodCall,
161+
property,
162+
TypedFeedbackContract::method_direct_call(),
163+
))
164+
};
160165

161166
let guard_idx = ctx.new_block("method_direct.guard");
162167
let fast_idx = ctx.new_block("method_direct.fast");
@@ -169,19 +174,34 @@ pub(super) fn emit_guarded_direct_method_call(
169174
ctx.block().br(&guard_label);
170175

171176
ctx.current_block = guard_idx;
172-
let guard_ok = ctx.block().call(
173-
I32,
174-
"js_typed_feedback_method_direct_call_guard",
175-
&[
176-
(I64, &site_id),
177-
(DOUBLE, recv_box),
178-
(I32, &expected_class_id_str),
179-
(I64, &expected_keys),
180-
(crate::types::PTR, &bytes_global),
181-
(I64, &name_len_str),
182-
(crate::types::PTR, &format!("@{}", direct_fn)),
183-
],
184-
);
177+
let guard_ok = if shape_only_guard {
178+
ctx.block().call(
179+
I32,
180+
"js_method_direct_shape_guard",
181+
&[
182+
(DOUBLE, recv_box),
183+
(I32, &expected_class_id_str),
184+
(I64, &expected_keys),
185+
],
186+
)
187+
} else {
188+
ctx.block().call(
189+
I32,
190+
"js_typed_feedback_method_direct_call_guard",
191+
&[
192+
(
193+
I64,
194+
site_id.as_deref().expect("typed-feedback method site id"),
195+
),
196+
(DOUBLE, recv_box),
197+
(I32, &expected_class_id_str),
198+
(I64, &expected_keys),
199+
(crate::types::PTR, &bytes_global),
200+
(I64, &name_len_str),
201+
(crate::types::PTR, &format!("@{}", direct_fn)),
202+
],
203+
)
204+
};
185205
let guard_pass = ctx.block().icmp_ne(I32, &guard_ok, "0");
186206
ctx.block()
187207
.cond_br(&guard_pass, &fast_label, &fallback_label);
@@ -212,8 +232,10 @@ pub(super) fn emit_guarded_direct_method_call(
212232
));
213233
(ptr_reg, n.to_string())
214234
};
215-
ctx.block()
216-
.call_void("js_typed_feedback_record_fallback_call", &[(I64, &site_id)]);
235+
if let Some(site_id) = site_id {
236+
ctx.block()
237+
.call_void("js_typed_feedback_record_fallback_call", &[(I64, &site_id)]);
238+
}
217239
let fallback_value = ctx.block().call(
218240
DOUBLE,
219241
"js_native_call_method",

crates/perry-codegen/src/lower_call/property_get.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,24 @@ fn is_inherited_object_prototype_method(name: &str) -> bool {
6262
)
6363
}
6464

65+
fn class_chain_has_field_named(ctx: &FnCtx<'_>, class_name: &str, property: &str) -> bool {
66+
let mut current = Some(class_name.to_string());
67+
while let Some(name) = current {
68+
let Some(class) = ctx.classes.get(&name) else {
69+
return true;
70+
};
71+
if class
72+
.fields
73+
.iter()
74+
.any(|field| field.key_expr.is_some() || (!field.is_private && field.name == property))
75+
{
76+
return true;
77+
}
78+
current = class.extends_name.clone();
79+
}
80+
false
81+
}
82+
6583
/// Try to lower a `Call { callee: PropertyGet { .. } }` via the
6684
/// string/array/class/Map/Set/Promise/fetch/static/instance dispatch tower.
6785
pub fn try_lower_property_get_method_call(
@@ -1906,6 +1924,8 @@ pub fn try_lower_property_get_method_call(
19061924
lowered_args.iter().map(|s| (DOUBLE, s.as_str())).collect();
19071925

19081926
if !method_has_rest {
1927+
let shape_only_guard =
1928+
!class_chain_has_field_named(ctx, &class_name, property.as_str());
19091929
if let Some(guarded) = emit_guarded_direct_method_call(
19101930
ctx,
19111931
&recv_box,
@@ -1914,6 +1934,7 @@ pub fn try_lower_property_get_method_call(
19141934
&fallback_fn,
19151935
&arg_slices,
19161936
&fallback_user_args,
1937+
shape_only_guard,
19171938
) {
19181939
return Ok(Some(guarded));
19191940
}

crates/perry-codegen/src/runtime_decls/arrays.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ pub fn declare_phase_b_arrays(module: &mut LlModule) {
4747
// Extending variant: returns a possibly-realloc'd pointer that the
4848
// caller must write back to the local slot.
4949
module.declare_function("js_array_set_f64_extend", I64, &[I64, I32, DOUBLE]);
50+
module.declare_function("js_array_fill_f64_const_extend", I64, &[I64, I32, DOUBLE]);
51+
module.declare_function("js_array_fill_f64_iota_extend", I64, &[I64, I32]);
52+
module.declare_function("js_array_fill_f64_const_len_extend", I64, &[I64, DOUBLE]);
53+
module.declare_function("js_array_fill_f64_iota_len_extend", I64, &[I64]);
5054
module.declare_function("js_array_set_string_key", I64, &[I64, I64, DOUBLE]);
5155
module.declare_function("js_array_set_index_or_string", I64, &[I64, DOUBLE, DOUBLE]);
5256
module.declare_function("js_array_mark_arguments_object", I64, &[I64]);

crates/perry-codegen/src/runtime_decls/objects.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ pub fn declare_phase_b_objects(module: &mut LlModule) {
123123
I32,
124124
&[I64, DOUBLE, I32, I64, PTR, I64, PTR],
125125
);
126+
module.declare_function("js_method_direct_shape_guard", I32, &[DOUBLE, I32, I64]);
126127
module.declare_function(
127128
"js_typed_feedback_closure_direct_call_guard",
128129
I32,

0 commit comments

Comments
 (0)