# UNDF: UNDF-2026-000001155 --- a/core/src/avm2/optimizer/type_aware.rs +++ b/core/src/avm2/optimizer/type_aware.rs @@ -1,6 +1,7 @@ +use std::collections::HashSet; + // ... existing imports ... - // Block #0 is the entry block - let mut worklist = vec![0]; + // Block #0 is the entry block. Companion HashSet for O(1) dedup (was O(B) Vec::contains). + let mut worklist = vec![0usize]; + let mut worklist_set: HashSet = [0].into_iter().collect(); while let Some(block_idx) = worklist.pop() { + worklist_set.remove(&block_idx); // In every call to process_jump, pass &mut worklist_set as the new argument. @@ -702,7 +702,8 @@ fn process_jump<'gc>( abstract_states: &mut [Option>], current_state: &AbstractStateRef<'_, 'gc>, op_index_to_block_index_table: &HashMap, - worklist: &mut Vec, + worklist: &mut Vec, + worklist_set: &mut HashSet, do_optimize: bool, ) -> Result<(), Error<'gc>> { if do_optimize { @@ -722,9 +722,8 @@ fn process_jump<'gc>( abstract_states[target_block_id] = Some(current_state.to_owned()); }; - // FP reschedules blocks to the front of queue (for us, it'd be back of the vec). - // I don't know if there's any good reason for that, but not doing it is faster. - if !worklist.contains(&target_block_id) { + // O(1) dedup via companion HashSet instead of O(B) Vec::contains scan. + if worklist_set.insert(target_block_id) { worklist.push(target_block_id); }