Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avm2: Various optimizations #15744

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 9 additions & 7 deletions core/src/avm2/activation.rs
Expand Up @@ -592,6 +592,11 @@ impl<'a, 'gc> Activation<'a, 'gc> {
*self.local_registers.get_unchecked_mut(id) = value.into();
}

/// Retrieve the outer scope of this activation
pub fn outer(&self) -> ScopeChain<'gc> {
self.outer
}

/// Sets the outer scope of this activation
pub fn set_outer(&mut self, new_outer: ScopeChain<'gc>) {
self.outer = new_outer;
Expand Down Expand Up @@ -872,7 +877,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
opcodes: &[Op<'gc>],
) -> Result<FrameControl<'gc>, Error<'gc>> {
self.actions_since_timeout_check += 1;
if self.actions_since_timeout_check >= 2000 {
if self.actions_since_timeout_check >= 64000 {
self.actions_since_timeout_check = 0;
if self.context.update_start.elapsed() >= self.context.max_execution_duration {
return Err(
Expand Down Expand Up @@ -1621,13 +1626,10 @@ impl<'a, 'gc> Activation<'a, 'gc> {
}

fn op_get_outer_scope(&mut self, index: u32) -> Result<FrameControl<'gc>, Error<'gc>> {
let scope = self.outer.get(index as usize);
// Verifier ensures that this points to a valid outer scope
let scope = self.outer.get_unchecked(index as usize);

if let Some(scope) = scope {
self.push_stack(scope.values());
} else {
self.push_stack(Value::Undefined);
};
self.push_stack(scope.values());

Ok(FrameControl::Continue)
}
Expand Down
157 changes: 99 additions & 58 deletions core/src/avm2/optimize.rs
Expand Up @@ -83,12 +83,8 @@ impl<'gc> Locals<'gc> {
self.0[index] = value;
}

fn at(&self, index: usize) -> Option<OptValue<'gc>> {
self.0.get(index).copied()
}

fn len(&self) -> usize {
self.0.len()
fn at(&self, index: usize) -> OptValue<'gc> {
self.0[index]
}
}

Expand Down Expand Up @@ -242,20 +238,14 @@ pub fn optimize<'gc>(
| Op::IncLocalI { index }
| Op::DecLocal { index }
| Op::DecLocalI { index } => {
if (*index as usize) < initial_local_types.len() {
initial_local_types.set_any(*index as usize);
}
initial_local_types.set_any(*index as usize);
}
Op::HasNext2 {
object_register,
index_register,
} => {
if (*object_register as usize) < initial_local_types.len() {
initial_local_types.set_any(*object_register as usize);
}
if (*index_register as usize) < initial_local_types.len() {
initial_local_types.set_any(*index_register as usize);
}
initial_local_types.set_any(*object_register as usize);
initial_local_types.set_any(*index_register as usize);
}
_ => {}
}
Expand Down Expand Up @@ -384,14 +374,10 @@ pub fn optimize<'gc>(
stack.push_any();
}
Op::DecLocalI { index } => {
if (*index as usize) < local_types.len() {
local_types.set_any(*index as usize);
}
local_types.set_any(*index as usize);
}
Op::IncLocalI { index } => {
if (*index as usize) < local_types.len() {
local_types.set_any(*index as usize);
}
local_types.set_any(*index as usize);
}
Op::Increment => {
stack.pop();
Expand Down Expand Up @@ -420,6 +406,11 @@ pub fn optimize<'gc>(
stack.pop();
stack.push_any();
}
Op::NegateI => {
stack.pop();
stack.pop();
stack.push_any();
}
Op::Add => {
let value2 = stack.pop_or_any();
let value1 = stack.pop_or_any();
Expand Down Expand Up @@ -511,6 +502,10 @@ pub fn optimize<'gc>(
Op::NewClass { .. } => {
stack.push_class_object(types.class);
}
Op::NewCatch { .. } => {
// Avoid handling for now
stack.push_any();
}
Op::IsType { .. } => {
stack.pop();
stack.push_class_object(types.boolean);
Expand All @@ -520,6 +515,10 @@ pub fn optimize<'gc>(
stack.pop();
stack.push_class_object(types.boolean);
}
Op::TypeOf => {
stack.pop();
stack.push_class_object(types.string);
}
Op::ApplyType { num_types } => {
stack.popn(*num_types);

Expand Down Expand Up @@ -594,53 +593,86 @@ pub fn optimize<'gc>(
Op::PopScope => {
scope_stack.pop();
}
Op::GetScopeObject { .. } => {
// Avoid handling for now
stack.push_any();
}
Op::Pop => {
stack.pop();
}
Op::Dup => {
let stack_value = stack.pop();
if let Some(stack_value) = stack_value {
stack.push(stack_value);
stack.push(stack_value);
}
let stack_value = stack.pop_or_any();
stack.push(stack_value);
stack.push(stack_value);
}
Op::Swap => {
let first = stack.pop_or_any();
let second = stack.pop_or_any();
stack.push(first);
stack.push(second);
}
Op::Kill { index } => {
if (*index as usize) < local_types.len() {
local_types.set_any(*index as usize);
}
local_types.set_any(*index as usize);
}
Op::SetLocal { index } => {
let stack_value = stack.pop();
if (*index as usize) < local_types.len() {
if let Some(stack_value) = stack_value {
local_types.set(*index as usize, stack_value);
} else {
local_types.set_any(*index as usize);
}
}
let stack_value = stack.pop_or_any();
local_types.set(*index as usize, stack_value);
}
Op::GetLocal { index } => {
let local_type = local_types.at(*index as usize);
if let Some(local_type) = local_type {
stack.push(local_type);
} else {
stack.push_any();
}
stack.push(local_type);
}
Op::GetLex { .. } => {
stack.push_any();
}
Op::FindPropStrict { multiname } => {
if !multiname.has_lazy_component() {
stack.push_any();
} else {
// Avoid handling lazy for now
stack.clear();
}
stack.pop_for_multiname(*multiname);

// Avoid handling for now
stack.push_any();
}
Op::FindProperty { .. } => {
Op::FindProperty { multiname } => {
stack.pop_for_multiname(*multiname);

// Avoid handling for now
stack.clear();
stack.push_any();
}
Op::FindDef { .. } => {
// Avoid handling for now
stack.push_any();
}
Op::In => {
stack.pop();
stack.pop();
stack.push_class_object(types.boolean);
}
Op::NextName => {
stack.pop();
stack.pop();
stack.push_any();
}
Op::NextValue => {
stack.pop();
stack.pop();
stack.push_any();
}
Op::HasNext2 {
index_register,
object_register,
} => {
stack.push_class_object(types.boolean);
local_types.set_any(*index_register as usize);
local_types.set_any(*object_register as usize);
}
Op::GetSlot { .. } => {
stack.pop();

// Avoid handling type for now
stack.push_any();
}
Op::SetSlot { .. } => {
stack.pop();
stack.pop();
}
Op::GetProperty { multiname } => {
let mut stack_push_done = false;
Expand Down Expand Up @@ -693,12 +725,6 @@ pub fn optimize<'gc>(
stack.push_any();
}
}
Op::GetSlot { .. } => {
stack.pop();

// Avoid handling type for now
stack.push_any();
}
Op::InitProperty { multiname } => {
stack.pop();
stack.pop_for_multiname(*multiname);
Expand Down Expand Up @@ -736,6 +762,11 @@ pub fn optimize<'gc>(
}
// `stack_pop_multiname` handled lazy
}
Op::DeleteProperty { multiname } => {
stack.pop_for_multiname(*multiname);

stack.pop();
}
Op::Construct { num_args } => {
// Arguments
stack.popn(*num_args);
Expand Down Expand Up @@ -842,14 +873,24 @@ pub fn optimize<'gc>(
stack.pop();
stack.pop();
}
Op::Si8 => {
Op::Si8 | Op::Si16 | Op::Si32 => {
stack.pop();
stack.pop();
}
Op::Li8 => {
Op::Li8 | Op::Li16 => {
stack.pop();
let mut value = OptValue::of_type(types.int);
value.contains_valid_integer = true;
stack.push(value);
}
Op::Sxi8 | Op::Sxi16 => {
stack.pop();
let mut value = OptValue::of_type(types.int);
value.contains_valid_integer = true;
stack.push(value);
}
Op::Li32 => {
stack.pop();
stack.push_class_object(types.int);
}
Op::ReturnVoid
Expand Down
11 changes: 11 additions & 0 deletions core/src/avm2/scope.rs
Expand Up @@ -72,6 +72,11 @@ impl<'gc> ScopeContainer<'gc> {
self.scopes.get(index).cloned()
}

/// Like `get`, but panics if the scope index is out of bounds.
pub fn get_unchecked(&self, index: usize) -> Scope<'gc> {
self.scopes[index]
}

fn is_empty(&self) -> bool {
self.scopes.is_empty()
}
Expand Down Expand Up @@ -147,6 +152,12 @@ impl<'gc> ScopeChain<'gc> {
self.container.and_then(|container| container.get(index))
}

/// Like `get`, but panics if the container doesn't exist or
/// the scope index is out of bounds.
pub fn get_unchecked(&self, index: usize) -> Scope<'gc> {
self.container.unwrap().get_unchecked(index)
}

pub fn is_empty(&self) -> bool {
self.container
.map(|container| container.is_empty())
Expand Down
10 changes: 10 additions & 0 deletions core/src/avm2/verify.rs
Expand Up @@ -288,6 +288,16 @@ pub fn verify_method<'gc>(
}
}

AbcOp::GetOuterScope { index } => {
if activation.outer().get(index as usize).is_none() {
return Err(Error::AvmError(verify_error(
activation,
"Error #1019: Getscopeobject is out of bounds.",
1019,
)?));
}
}

AbcOp::AsType {
type_name: name_index,
}
Expand Down