diff --git a/src/context.rs b/src/context.rs index e5aa3c80..26e0223f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -348,19 +348,14 @@ pub struct EntityOrientedDenseMap, V> { // since the ideal state is one chunk per map, the slow case might never be hit, // unless one `EntityOrientedDenseMap` is used with more than one `EntityDefs`, // which could still maybe be implemented more efficiently than `FxHashMap`. -#[derive(Clone)] +#[derive(Clone, Default)] enum SmallFxHashMap { + #[default] Empty, One(K, V), More(FxHashMap), } -impl Default for SmallFxHashMap { - fn default() -> Self { - Self::Empty - } -} - impl SmallFxHashMap { fn get_mut_or_insert_default(&mut self, k: K) -> &mut V { // HACK(eddyb) to avoid borrowing issues, this is done in two stages: diff --git a/src/lib.rs b/src/lib.rs index 4f1b084b..c8290a81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ clippy::dbg_macro, clippy::debug_assert_with_mut_call, clippy::doc_markdown, - clippy::empty_enum, + clippy::empty_enums, clippy::enum_glob_use, clippy::exit, clippy::expl_impl_clone_on_copy, @@ -121,7 +121,6 @@ clippy::string_add_assign, clippy::string_add, clippy::string_lit_as_bytes, - clippy::string_to_string, clippy::todo, clippy::trait_duplication_in_bounds, clippy::unimplemented, diff --git a/src/spv/lift.rs b/src/spv/lift.rs index 5bbb6551..b088e3a6 100644 --- a/src/spv/lift.rs +++ b/src/spv/lift.rs @@ -863,22 +863,70 @@ impl<'a> FuncLifting<'a> { // HACK(eddyb) this takes advantage of `blocks` being an `IndexMap`, // to iterate at the same time as mutating other entries. for block_idx in (0..blocks.len()).rev() { - let BlockLifting { terminator: original_terminator, .. } = &blocks[block_idx]; + // HACK(eddyb) elide empty cases of an `if`-`else`/`switch`, as + // SPIR-V allows their targets to just be the whole merge block + // (the same one that `OpSelectionMerge` describes). + let block = &blocks[block_idx]; + if let (cfg::ControlInstKind::SelectBranch(_), Some(Merge::Selection(merge_point))) = + (&*block.terminator.kind, block.terminator.merge) + { + for target_idx in 0..block.terminator.targets.len() { + let block = &blocks[block_idx]; + let target = block.terminator.targets[target_idx]; + if !block + .terminator + .target_phi_values + .get(&target) + .copied() + .unwrap_or_default() + .is_empty() + { + continue; + } + + let target_is_trivial_branch = { + let BlockLifting { + phis, + insts, + terminator: + Terminator { attrs, kind, inputs, targets, target_phi_values, merge }, + } = &blocks[&target]; + + (phis.is_empty() + && insts.iter().all(|insts| insts.is_empty()) + && *attrs == AttrSet::default() + && matches!(**kind, cfg::ControlInstKind::Branch) + && inputs.is_empty() + && targets.len() == 1 + && target_phi_values.is_empty() + && merge.is_none()) + .then(|| targets[0]) + }; + if let Some(target_of_target) = target_is_trivial_branch + && target_of_target == merge_point + { + blocks[block_idx].terminator.targets[target_idx] = target_of_target; + *use_counts.get_mut(&target).unwrap() -= 1; + *use_counts.get_mut(&target_of_target).unwrap() += 1; + } + } + } + let block = &blocks[block_idx]; let is_trivial_branch = { let Terminator { attrs, kind, inputs, targets, target_phi_values, merge } = - original_terminator; + &block.terminator; - *attrs == AttrSet::default() + (*attrs == AttrSet::default() && matches!(**kind, cfg::ControlInstKind::Branch) && inputs.is_empty() && targets.len() == 1 && target_phi_values.is_empty() - && merge.is_none() + && merge.is_none()) + .then(|| targets[0]) }; - if is_trivial_branch { - let target = original_terminator.targets[0]; + if let Some(target) = is_trivial_branch { let target_use_count = use_counts.get_mut(&target).unwrap(); if *target_use_count == 1 {