Skip to content

Commit a0e7762

Browse files
committed
Cache indexed realm binding access
1 parent 39fb188 commit a0e7762

6 files changed

Lines changed: 258 additions & 101 deletions

File tree

crates/qjs-runtime/src/bytecode/vm.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ pub(super) struct Vm<'a> {
8181
/// binding authority. The common first 128 slots require no allocation;
8282
/// larger slot indices conservatively use the full binding path.
8383
pub(super) authoritative_slots: u128,
84+
/// Inline per-slot cache for locals backed by this realm's shared binding
85+
/// cells. This turns ordinary global-var reads into a direct cell load;
86+
/// the cell's uninitialized marker still deoptimizes deleted/accessor
87+
/// globals through the observable global-object path.
88+
pub(super) realm_binding_slots: u128,
8489
pub(super) upvalues: Vec<Upvalue>,
8590
/// Shared realm plus this frame's internal/caller-scope bindings.
8691
pub(super) env: CallEnv,
@@ -230,6 +235,8 @@ impl<'a> Vm<'a> {
230235
};
231236
let authoritative_slots =
232237
Self::initial_authoritative_slots(bytecode, &local_upvalues, &env);
238+
let realm_binding_slots =
239+
Self::initial_realm_binding_slots(bytecode, &local_upvalues, &env);
233240
let numeric_loop_plans = bytecode
234241
.numeric_loop_plans
235242
.get_or_init(|| super::vm_numeric_loop::NumericLoopPlan::compile_all(bytecode))
@@ -254,6 +261,7 @@ impl<'a> Vm<'a> {
254261
locals,
255262
local_upvalues,
256263
authoritative_slots,
264+
realm_binding_slots,
257265
upvalues,
258266
env,
259267
direct_this,
@@ -579,7 +587,7 @@ impl<'a> Vm<'a> {
579587
let result = if self.direct_eval_with_stack {
580588
self.store_ident_with(name, None, true, value)
581589
} else {
582-
self.store_global_strict(name.clone(), value)
590+
self.store_global_strict(name, value)
583591
};
584592
self.handle_runtime_result(result)?;
585593
}
@@ -588,13 +596,13 @@ impl<'a> Vm<'a> {
588596
let result = if self.direct_eval_with_stack {
589597
self.store_ident_with(name, None, false, value)
590598
} else {
591-
self.store_global_sloppy(name.clone(), value)
599+
self.store_global_sloppy(name, value)
592600
};
593601
self.handle_runtime_result(result)?;
594602
}
595603
Op::StoreLocalOrGlobalSloppy { slot, name } => {
596604
let value = self.pop()?;
597-
let result = self.store_local_or_global_sloppy(*slot, name.clone(), value);
605+
let result = self.store_local_or_global_sloppy(*slot, name, value);
598606
self.handle_runtime_result(result)?;
599607
}
600608
Op::TypeofGlobal(name) => {

crates/qjs-runtime/src/bytecode/vm_bindings.rs

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,31 @@ impl Vm<'_> {
417417
.fold(0, |slots, slot| slots | slot)
418418
}
419419

420+
pub(super) fn initial_realm_binding_slots(
421+
bytecode: &Bytecode,
422+
local_upvalues: &[Option<Upvalue>],
423+
env: &CallEnv,
424+
) -> u128 {
425+
bytecode
426+
.locals
427+
.iter()
428+
.enumerate()
429+
.take(u128::BITS as usize)
430+
.filter_map(|(slot, local)| {
431+
local_upvalues
432+
.get(slot)
433+
.and_then(Option::as_ref)
434+
.is_some_and(|cell| env.is_realm_binding_cell(&local.name, cell))
435+
.then_some(1_u128 << slot)
436+
})
437+
.fold(0, |slots, slot| slots | slot)
438+
}
439+
420440
pub(super) fn refresh_authoritative_slots(&mut self) {
421441
self.authoritative_slots =
422442
Self::initial_authoritative_slots(self.bytecode, &self.local_upvalues, &self.env);
443+
self.realm_binding_slots =
444+
Self::initial_realm_binding_slots(self.bytecode, &self.local_upvalues, &self.env);
423445
}
424446

425447
/// Keeps a module's exported binding cell current for name-based writes.
@@ -756,9 +778,9 @@ impl Vm<'_> {
756778
}
757779
match slot {
758780
Some(slot) => self.assign_local(slot, value),
759-
None if is_strict => self.store_global_strict(name.to_owned(), value),
781+
None if is_strict => self.store_global_strict(name, value),
760782
None => {
761-
self.store_global_sloppy(name.to_owned(), value)?;
783+
self.store_global_sloppy(name, value)?;
762784
self.record_sloppy_global_name(name);
763785
Ok(())
764786
}
@@ -818,9 +840,9 @@ impl Vm<'_> {
818840
match self.load_local(object_slot)? {
819841
Value::Undefined => match slot {
820842
Some(slot) => self.assign_local(slot, value),
821-
None if is_strict => self.store_global_strict(name.to_owned(), value),
843+
None if is_strict => self.store_global_strict(name, value),
822844
None => {
823-
self.store_global_sloppy(name.to_owned(), value)?;
845+
self.store_global_sloppy(name, value)?;
824846
self.record_sloppy_global_name(name);
825847
Ok(())
826848
}
@@ -910,12 +932,14 @@ impl Vm<'_> {
910932

911933
#[inline(never)]
912934
fn load_local_slow(&mut self, slot: usize) -> Result<Value, RuntimeError> {
913-
if let Some(cell) = self.local_upvalues.get(slot).and_then(Option::as_ref)
914-
&& let Some(local) = self.bytecode.locals.get(slot)
915-
&& self.env.is_realm_binding_cell(&local.name, cell)
916-
&& !self.realm.borrow().contains_key(&local.name)
935+
if self.slot_is_realm_binding(slot)
936+
&& let Some(cell) = self.local_upvalues.get(slot).and_then(Option::as_ref)
917937
{
918-
let name = local.name.clone();
938+
let value = cell.get();
939+
if !value.is_uninitialized_lexical_marker() {
940+
return Ok(value);
941+
}
942+
let name = self.bytecode.locals[slot].name.clone();
919943
if let Some(value) = self.global_this_own_value(&name)? {
920944
return Ok(value);
921945
}
@@ -1253,21 +1277,26 @@ impl Vm<'_> {
12531277
slot < u128::BITS as usize && self.authoritative_slots & (1_u128 << slot) != 0
12541278
}
12551279

1280+
#[inline(always)]
1281+
pub(super) fn slot_is_realm_binding(&self, slot: usize) -> bool {
1282+
slot < u128::BITS as usize && self.realm_binding_slots & (1_u128 << slot) != 0
1283+
}
1284+
12561285
pub(super) fn store_local_or_global_sloppy(
12571286
&mut self,
12581287
slot: usize,
1259-
name: String,
1288+
name: &str,
12601289
value: Value,
12611290
) -> Result<(), RuntimeError> {
1262-
if self.env.has_module_import(&name) {
1291+
if self.env.has_module_import(name) {
12631292
return Err(RuntimeError {
12641293
thrown: None,
12651294
message: "TypeError: assignment to constant variable".to_owned(),
12661295
});
12671296
}
12681297
// The inner name of a named function expression is immutable; a sloppy
12691298
// assignment to it is a silent no-op.
1270-
if self.env.is_immutable_function_name(&name) {
1299+
if self.env.is_immutable_function_name(name) {
12711300
return Ok(());
12721301
}
12731302
let is_sloppy_global_fallback = self
@@ -1279,14 +1308,14 @@ impl Vm<'_> {
12791308
&& self.locals.get(slot).is_some_and(Option::is_some)
12801309
&& let Some(Value::Object(global_this)) = self.env.global_this()
12811310
{
1282-
match global_this.write_existing_own_data_property(&name, &value) {
1311+
match global_this.write_existing_own_data_property(name, &value) {
12831312
OwnDataPropertyWrite::Written => {
1284-
self.invalidate_array_prototype_cache(&name);
1285-
if !self.env.replace_existing_realm(&name, value.clone()) {
1286-
self.env.insert_realm(name.clone(), value.clone());
1313+
self.invalidate_array_prototype_cache(name);
1314+
if !self.env.replace_existing_realm(name, value.clone()) {
1315+
self.env.insert_realm(name.to_owned(), value.clone());
12871316
}
12881317
self.locals[slot] = Some(value);
1289-
self.sync_marked_dynamic_global(&name);
1318+
self.sync_marked_dynamic_global(name);
12901319
return Ok(());
12911320
}
12921321
OwnDataPropertyWrite::ReadOnly => return Ok(()),
@@ -1295,19 +1324,19 @@ impl Vm<'_> {
12951324
}
12961325
match self.locals.get(slot) {
12971326
Some(Some(_)) => {
1298-
if self.local_slot_targets_non_writable_global(slot, &name) {
1327+
if self.local_slot_targets_non_writable_global(slot, name) {
12991328
return Ok(());
13001329
}
1301-
if is_sloppy_global_fallback || self.has_realm_or_global_this_binding(&name) {
1330+
if is_sloppy_global_fallback || self.has_realm_or_global_this_binding(name) {
13021331
let syncs_global_snapshot = is_sloppy_global_fallback
1303-
&& self.captured_or_local_matches_global_this(&name);
1332+
&& self.captured_or_local_matches_global_this(name);
13041333
if syncs_global_snapshot {
1305-
self.record_sloppy_global_name(&name);
1334+
self.record_sloppy_global_name(name);
13061335
}
1307-
self.store_realm_or_global_this_sloppy(name.clone(), value.clone())?;
1336+
self.store_realm_or_global_this_sloppy(name.to_owned(), value.clone())?;
13081337
self.store_local(slot, value)?;
13091338
if syncs_global_snapshot && let Some(value) = self.locals[slot].clone() {
1310-
self.sync_global_this_own_property(&name, value);
1339+
self.sync_global_this_own_property(name, value);
13111340
}
13121341
} else {
13131342
self.store_local(slot, value)?;
@@ -1316,23 +1345,23 @@ impl Vm<'_> {
13161345
}
13171346
Some(None) => {
13181347
if is_sloppy_global_fallback {
1319-
if self.local_slot_targets_non_writable_global(slot, &name) {
1348+
if self.local_slot_targets_non_writable_global(slot, name) {
13201349
return Ok(());
13211350
}
1322-
let syncs_global_snapshot = self.captured_or_local_matches_global_this(&name);
1351+
let syncs_global_snapshot = self.captured_or_local_matches_global_this(name);
13231352
if syncs_global_snapshot {
1324-
self.record_sloppy_global_name(&name);
1353+
self.record_sloppy_global_name(name);
13251354
}
1326-
self.store_realm_or_global_this_sloppy(name.clone(), value.clone())?;
1355+
self.store_realm_or_global_this_sloppy(name.to_owned(), value.clone())?;
13271356
self.store_local(slot, value)?;
13281357
if syncs_global_snapshot && let Some(value) = self.locals[slot].clone() {
1329-
self.sync_global_this_own_property(&name, value);
1358+
self.sync_global_this_own_property(name, value);
13301359
}
13311360
return Ok(());
13321361
}
1333-
self.store_global_sloppy(name.clone(), value)?;
1334-
self.record_sloppy_global_name(&name);
1335-
let global_value = self.load_global(&name)?;
1362+
self.store_global_sloppy(name, value)?;
1363+
self.record_sloppy_global_name(name);
1364+
let global_value = self.load_global(name)?;
13361365
let local = self.locals.get_mut(slot).ok_or_else(|| RuntimeError {
13371366
thrown: None,
13381367
message: "bytecode local index out of bounds".to_owned(),

0 commit comments

Comments
 (0)