avm2: Fill in all ops in optimizer

This commit is contained in:
Lord-McSweeney 2024-04-04 21:49:04 -07:00 committed by Lord-McSweeney
parent efe878211a
commit daaf274245
1 changed files with 174 additions and 23 deletions

View File

@ -170,6 +170,7 @@ pub fn optimize<'gc>(
pub array: ClassObject<'gc>,
pub function: ClassObject<'gc>,
pub void: ClassObject<'gc>,
pub namespace: ClassObject<'gc>,
}
let types = Types {
object: activation.avm2().classes().object,
@ -182,6 +183,7 @@ pub fn optimize<'gc>(
array: activation.avm2().classes().array,
function: activation.avm2().classes().function,
void: activation.avm2().classes().void,
namespace: activation.avm2().classes().namespace,
};
let method_body = method
@ -306,6 +308,11 @@ pub fn optimize<'gc>(
last_op_was_block_terminating = false;
match op {
Op::CoerceA => {
// This does actually inhibit optimizations in FP
stack.pop();
stack.push_any();
}
Op::CoerceB => {
let stack_value = stack.pop_or_any();
if stack_value.class == Some(types.boolean) {
@ -331,18 +338,16 @@ pub fn optimize<'gc>(
}
stack.push_class_object(types.int);
}
Op::CoerceU => {
let stack_value = stack.pop_or_any();
// TODO: maybe the type check is safe here...?
if stack_value.contains_valid_unsigned {
*op = Op::Nop;
}
stack.push_class_object(types.uint);
}
Op::CoerceA => {
// This does actually inhibit optimizations in FP
Op::CoerceO => {
stack.pop();
stack.push_any();
stack.push_class_object(types.object);
}
Op::ConvertO => {
// This has no stack effect that code can notice:
// either it will push the value back on the stack
// (no difference) or it will throw an error (which
// will clear the stack).
}
Op::CoerceS => {
let stack_value = stack.pop_or_any();
@ -351,6 +356,18 @@ pub fn optimize<'gc>(
}
stack.push_class_object(types.string);
}
Op::ConvertS => {
stack.pop();
stack.push_class_object(types.string);
}
Op::CoerceU => {
let stack_value = stack.pop_or_any();
// TODO: maybe the type check is safe here...?
if stack_value.contains_valid_unsigned {
*op = Op::Nop;
}
stack.push_class_object(types.uint);
}
Op::Equals
| Op::StrictEquals
| Op::LessEquals
@ -406,6 +423,13 @@ pub fn optimize<'gc>(
}
stack.push(new_value);
}
Op::PushUint { value } => {
let mut new_value = OptValue::of_type(types.uint);
if *value < (1 << 28) {
new_value.contains_valid_unsigned = true;
}
stack.push(new_value);
}
Op::DecrementI => {
// TODO (same for other I ops): analyze what _exactly_ the type int implies
// and whether we can use Number or (u)int here
@ -419,9 +443,15 @@ pub fn optimize<'gc>(
Op::DecLocalI { index } => {
local_types.set_any(*index as usize);
}
Op::DecLocal { index } => {
local_types.set(*index as usize, OptValue::of_type(types.number));
}
Op::IncLocalI { index } => {
local_types.set_any(*index as usize);
}
Op::IncLocal { index } => {
local_types.set(*index as usize, OptValue::of_type(types.number));
}
Op::Increment => {
stack.pop();
stack.push_class_object(types.number);
@ -450,7 +480,6 @@ pub fn optimize<'gc>(
stack.push_any();
}
Op::NegateI => {
stack.pop();
stack.pop();
stack.push_any();
}
@ -526,6 +555,9 @@ pub fn optimize<'gc>(
Op::PushDouble { .. } => {
stack.push_class_object(types.number);
}
Op::PushNamespace { .. } => {
stack.push_class_object(types.namespace);
}
Op::PushString { .. } => {
stack.push_class_object(types.string);
}
@ -558,6 +590,11 @@ pub fn optimize<'gc>(
stack.pop();
stack.push_class_object(types.boolean);
}
Op::InstanceOf => {
stack.pop();
stack.pop();
stack.push_class_object(types.boolean);
}
Op::TypeOf => {
stack.pop();
stack.push_class_object(types.string);
@ -569,6 +606,27 @@ pub fn optimize<'gc>(
stack.push_any();
}
Op::CheckFilter => {
stack.pop();
stack.push_any();
}
Op::Dxns { .. } => {
// Dxns doesn't change stack or locals
}
Op::DxnsLate => {
stack.pop();
}
Op::EscXAttr | Op::EscXElem => {
stack.pop();
stack.push_class_object(types.string);
}
Op::GetDescendants { multiname } => {
stack.pop_for_multiname(*multiname);
stack.pop();
stack.push_any();
}
Op::AsTypeLate => {
stack.pop();
stack.pop();
@ -642,6 +700,10 @@ pub fn optimize<'gc>(
// Avoid handling for now
stack.push_any();
}
Op::GetOuterScope { .. } => {
// Avoid handling for now
stack.push_any();
}
Op::Pop => {
stack.pop();
}
@ -701,6 +763,11 @@ pub fn optimize<'gc>(
stack.pop();
stack.push_any();
}
Op::HasNext => {
stack.pop();
stack.pop();
stack.push_any();
}
Op::HasNext2 {
index_register,
object_register,
@ -862,6 +929,43 @@ pub fn optimize<'gc>(
// Avoid checking return value for now
stack.push_any();
}
Op::Call { num_args } => {
// Arguments
stack.popn(*num_args);
stack.pop();
stack.pop();
// Avoid checking return value for now
stack.push_any();
}
Op::CallMethod { .. } => unreachable!("CallMethod cannot be emitted by verifier"),
Op::CallPropLex {
multiname,
num_args,
} => {
// Arguments
stack.popn(*num_args);
stack.pop_for_multiname(*multiname);
// Then receiver.
stack.pop();
// Avoid checking return value for now
stack.push_any();
}
Op::CallStatic { num_args, .. } => {
// Arguments
stack.popn(*num_args);
// Then receiver.
stack.pop();
// Avoid checking return value for now
stack.push_any();
}
Op::CallProperty {
multiname,
num_args,
@ -925,27 +1029,70 @@ pub fn optimize<'gc>(
}
// `stack_pop_multiname` handled lazy
}
Op::Call { num_args } => {
Op::GetSuper { multiname } => {
stack.pop_for_multiname(*multiname);
// Receiver
stack.pop();
}
Op::SetSuper { multiname } => {
stack.pop();
stack.pop_for_multiname(*multiname);
// Receiver
stack.pop();
}
Op::CallSuper {
multiname,
num_args,
} => {
// Arguments
stack.popn(*num_args);
stack.pop_for_multiname(*multiname);
// Then receiver.
stack.pop();
// Avoid checking return value for now
stack.push_any();
}
Op::CallSuperVoid {
multiname,
num_args,
} => {
// Arguments
stack.popn(*num_args);
stack.pop_for_multiname(*multiname);
// Then receiver.
stack.pop();
}
Op::GetGlobalScope => {
// Avoid handling for now
stack.push_any();
}
Op::GetGlobalSlot { .. } => {
// Avoid handling for now
stack.push_any();
}
Op::SetGlobalSlot { .. } => {
// Avoid handling for now
stack.pop();
}
Op::NewActivation => {
// Avoid handling for now
stack.push_any();
}
Op::Nop => {}
Op::DebugFile { .. } => {}
Op::DebugLine { .. } => {}
Op::Debug { .. } => {}
Op::DebugFile { .. }
| Op::DebugLine { .. }
| Op::Debug { .. }
| Op::Bkpt
| Op::BkptLine { .. }
| Op::Timestamp => {}
Op::IfTrue { .. } | Op::IfFalse { .. } => {
stack.pop();
state_map.insert(i as i32, local_types.clone());
@ -976,15 +1123,23 @@ pub fn optimize<'gc>(
value.contains_valid_integer = true;
stack.push(value);
}
Op::Sxi8 | Op::Sxi16 => {
Op::Li32 => {
stack.pop();
stack.push_class_object(types.int);
}
Op::Sxi1 | Op::Sxi8 | Op::Sxi16 => {
stack.pop();
let mut value = OptValue::of_type(types.int);
value.contains_valid_integer = true;
stack.push(value);
}
Op::Li32 => {
Op::Sf32 | Op::Sf64 => {
stack.pop();
stack.push_class_object(types.int);
stack.pop();
}
Op::Lf32 | Op::Lf64 => {
stack.pop();
stack.push_class_object(types.number);
}
Op::ReturnVoid | Op::ReturnValue | Op::Throw | Op::LookupSwitch(_) => {
// End of block
@ -1002,10 +1157,6 @@ pub fn optimize<'gc>(
local_types = initial_local_types.clone();
last_op_was_block_terminating = true;
}
_ => {
stack.clear();
scope_stack.clear();
}
}
}
}