Skip to content

Commit 1e0f128

Browse files
committed
avm1: Use Rc<[Cell<Value>]> instead of GcCell<SmallVec<Value>> for register sets
This removes RefCell checks (and one layer of indirection) on access, and trade GC pressure for one ref-count increment/decrement pair on each AVM1 scope.
1 parent 2b20cc9 commit 1e0f128

File tree

3 files changed

+16
-55
lines changed

3 files changed

+16
-55
lines changed

core/src/avm1/activation.rs

Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ use crate::string::{AvmString, HasStringContext, StringContext, SwfStrExt as _,
1616
use crate::tag_utils::SwfSlice;
1717
use crate::vminterface::Instantiator;
1818
use crate::{avm_error, avm_warn};
19-
use gc_arena::{Gc, GcCell, Mutation};
19+
use gc_arena::{Gc, Mutation};
2020
use indexmap::IndexMap;
2121
use rand::Rng;
2222
use ruffle_macros::istr;
23-
use smallvec::SmallVec;
2423
use std::borrow::Cow;
24+
use std::cell::Cell;
2525
use std::cmp::min;
2626
use std::fmt;
27+
use std::rc::Rc;
2728
use swf::avm1::read::Reader;
2829
use swf::avm1::types::*;
2930
use url::form_urlencoded;
@@ -39,44 +40,6 @@ macro_rules! avm_debug {
3940
)
4041
}
4142

42-
/// Represents a particular register set.
43-
///
44-
/// This type exists primarily because SmallVec isn't garbage-collectable.
45-
#[derive(Clone)]
46-
pub struct RegisterSet<'gc>(SmallVec<[Value<'gc>; 8]>);
47-
48-
unsafe impl<'gc> gc_arena::Collect<'gc> for RegisterSet<'gc> {
49-
#[inline]
50-
fn trace<C: gc_arena::collect::Trace<'gc>>(&self, cc: &mut C) {
51-
for register in &self.0 {
52-
cc.trace(register);
53-
}
54-
}
55-
}
56-
57-
impl<'gc> RegisterSet<'gc> {
58-
/// Create a new register set with a given number of specified registers.
59-
///
60-
/// The given registers will be set to `undefined`.
61-
pub fn new(num: u8) -> Self {
62-
Self(smallvec![Value::Undefined; num as usize])
63-
}
64-
65-
/// Return a reference to a given register, if it exists.
66-
pub fn get(&self, num: u8) -> Option<&Value<'gc>> {
67-
self.0.get(num as usize)
68-
}
69-
70-
/// Return a mutable reference to a given register, if it exists.
71-
pub fn get_mut(&mut self, num: u8) -> Option<&mut Value<'gc>> {
72-
self.0.get_mut(num as usize)
73-
}
74-
75-
pub fn len(&self) -> u8 {
76-
self.0.len() as u8
77-
}
78-
}
79-
8043
#[derive(Clone)]
8144
pub enum ReturnType<'gc> {
8245
Implicit,
@@ -206,9 +169,9 @@ pub struct Activation<'a, 'gc: 'a> {
206169
/// Registers are numbered from 1; r0 does not exist. Therefore this vec,
207170
/// while nominally starting from zero, actually starts from r1.
208171
///
209-
/// Registers are stored in a `GcCell` so that rescopes (e.g. with) use the
172+
/// Registers are stored in a `Rc` so that rescopes (e.g. with) use the
210173
/// same register set.
211-
local_registers: Option<GcCell<'gc, RegisterSet<'gc>>>,
174+
local_registers: Option<Rc<[Cell<Value<'gc>>]>>,
212175

213176
/// The base clip of this stack frame.
214177
/// This will be the MovieClip that contains the bytecode.
@@ -301,7 +264,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
301264
base_clip_unloaded: self.base_clip_unloaded,
302265
this: self.this,
303266
callee: self.callee,
304-
local_registers: self.local_registers,
267+
local_registers: self.local_registers.clone(),
305268
}
306269
}
307270

@@ -2268,7 +2231,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
22682231
self.callee,
22692232
);
22702233

2271-
activation.local_registers = self.local_registers;
2234+
activation.local_registers = self.local_registers.clone();
22722235

22732236
match catch_vars {
22742237
CatchVar::Var(name) => {
@@ -3061,21 +3024,22 @@ impl<'a, 'gc> Activation<'a, 'gc> {
30613024
/// Returns true if this activation has a given local register ID.
30623025
pub fn has_local_register(&self, id: u8) -> bool {
30633026
self.local_registers
3064-
.map(|rs| id < rs.read().len())
3027+
.as_deref()
3028+
.map(|rs| usize::from(id) < rs.len())
30653029
.unwrap_or(false)
30663030
}
30673031

3068-
pub fn allocate_local_registers(&mut self, num: u8, mc: &Mutation<'gc>) {
3032+
pub fn allocate_local_registers(&mut self, num: u8) {
30693033
self.local_registers = match num {
30703034
0 => None,
3071-
num => Some(GcCell::new(mc, RegisterSet::new(num))),
3035+
num => Some((0..num).map(|_| Cell::new(Value::Undefined)).collect()),
30723036
};
30733037
}
30743038

30753039
/// Retrieve a local register.
30763040
pub fn local_register(&self, id: u8) -> Option<Value<'gc>> {
3077-
if let Some(local_registers) = self.local_registers {
3078-
local_registers.read().get(id).cloned()
3041+
if let Some(local_registers) = self.local_registers.as_deref() {
3042+
local_registers.get(usize::from(id)).map(Cell::get)
30793043
} else {
30803044
None
30813045
}
@@ -3084,8 +3048,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
30843048
/// Set a local register.
30853049
pub fn set_local_register(&mut self, id: u8, value: Value<'gc>) {
30863050
if let Some(ref mut local_registers) = self.local_registers {
3087-
if let Some(r) = local_registers.write(self.context.gc()).get_mut(id) {
3088-
*r = value;
3051+
if let Some(r) = local_registers.get(usize::from(id)) {
3052+
r.set(value);
30893053
}
30903054
}
30913055
}

core/src/avm1/function.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ impl<'gc> Executable<'gc> {
432432
Some(callee),
433433
);
434434

435-
frame.allocate_local_registers(af.register_count(), frame.gc());
435+
frame.allocate_local_registers(af.register_count());
436436

437437
let mut preload_r = 1;
438438
af.load_this(&mut frame, this, &mut preload_r);

core/src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
mod display_object;
1010
pub use display_object::{StageAlign, StageDisplayState, StageScaleMode};
1111

12-
#[macro_use]
13-
extern crate smallvec;
14-
1512
#[macro_use]
1613
extern crate num_derive;
1714

0 commit comments

Comments
 (0)