From c0c1745cd843555612349aa6511e1580474d08e3 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 3 Jul 2026 20:53:14 -0700 Subject: [PATCH] Use FxHashMap/FxHashSet rather than std HashMap everywhere. Significant performance improvement in some workloads that compile many large functions. --- src/backend/localify.rs | 6 +++--- src/backend/reducify.rs | 3 ++- src/backend/stackify.rs | 8 ++++---- src/interp.rs | 4 ++-- src/ir/debug.rs | 2 +- src/ir/display.rs | 4 ++-- src/ir/func.rs | 2 +- src/passes/maxssa.rs | 5 +++-- 8 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/backend/localify.rs b/src/backend/localify.rs index 82e9122..62d8790 100644 --- a/src/backend/localify.rs +++ b/src/backend/localify.rs @@ -5,8 +5,8 @@ use crate::backend::treeify::Trees; use crate::cfg::CFGInfo; use crate::entity::{EntityVec, PerEntity}; use crate::ir::{Block, FunctionBody, Local, Type, Value, ValueDef}; +use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use smallvec::{smallvec, SmallVec}; -use std::collections::{HashMap, HashSet}; use std::ops::Range; #[derive(Clone, Debug, Default)] @@ -242,13 +242,13 @@ impl<'a> Context<'a> { ranges.sort_unstable_by_key(|(val, range)| (range.start, *val)); // Keep a list of expiring Locals by expiry point. - let mut expiring: HashMap> = HashMap::new(); + let mut expiring: HashMap> = HashMap::default(); // Iterate over allocation space, processing range starts (at // which point we allocate) and ends (at which point we add to // the freelist). let mut range_idx = 0; - let mut freelist: HashMap> = HashMap::new(); + let mut freelist: HashMap> = HashMap::default(); for i in 0..self.points { // Process ends. (Ends are exclusive, so we do them diff --git a/src/backend/reducify.rs b/src/backend/reducify.rs index b522fb5..d235fdb 100644 --- a/src/backend/reducify.rs +++ b/src/backend/reducify.rs @@ -150,10 +150,11 @@ use crate::entity::EntityRef; use crate::{cfg::CFGInfo, cfg::RPOIndex, entity::PerEntity, Block, FunctionBody, Value, ValueDef}; +use fxhash::FxHashSet as HashSet; use fxhash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; use std::borrow::Cow; -use std::collections::{HashSet, VecDeque}; +use std::collections::VecDeque; pub struct Reducifier<'a> { body: &'a FunctionBody, diff --git a/src/backend/stackify.rs b/src/backend/stackify.rs index a4229ab..7788ebe 100644 --- a/src/backend/stackify.rs +++ b/src/backend/stackify.rs @@ -12,7 +12,7 @@ use crate::cfg::CFGInfo; use crate::entity::EntityRef; use crate::ir::{Block, BlockTarget, FunctionBody, Terminator, Type, Value}; -use std::collections::HashSet; +use fxhash::FxHashSet as HashSet; use std::convert::TryFrom; #[derive(Clone, Debug)] @@ -134,9 +134,9 @@ impl<'a, 'b> Context<'a, 'b> { body: &FunctionBody, cfg: &CFGInfo, ) -> anyhow::Result<(HashSet, HashSet)> { - let mut loop_headers = HashSet::new(); - let mut branched_once = HashSet::new(); - let mut merge_nodes = HashSet::new(); + let mut loop_headers = HashSet::default(); + let mut branched_once = HashSet::default(); + let mut merge_nodes = HashSet::default(); for (block_rpo, &block) in cfg.rpo.entries() { for &succ in &body.blocks[block].succs { diff --git a/src/interp.rs b/src/interp.rs index 4952c78..824a175 100644 --- a/src/interp.rs +++ b/src/interp.rs @@ -5,7 +5,7 @@ use crate::ir::*; use crate::ops::Operator; use smallvec::{smallvec, SmallVec}; -use std::collections::HashMap; +use fxhash::FxHashMap as HashMap; /// How large do we allow a Wasm memory to be when interpreting? Limit /// the size somewhat (apply an implementation limit) so we do not @@ -156,7 +156,7 @@ impl InterpContext { let mut frame = InterpStackFrame { func, cur_block: body.entry, - values: HashMap::new(), + values: HashMap::default(), }; for (&arg, &(_, blockparam)) in args.iter().zip(body.blocks[body.entry].params.iter()) { diff --git a/src/ir/debug.rs b/src/ir/debug.rs index bfcc5df..f12d0ac 100644 --- a/src/ir/debug.rs +++ b/src/ir/debug.rs @@ -4,8 +4,8 @@ use crate::declare_entity; use crate::entity::EntityVec; #[cfg(feature = "dwarf")] use addr2line::gimli; +use fxhash::FxHashMap as HashMap; use std::collections::hash_map::Entry as HashEntry; -use std::collections::HashMap; declare_entity!(SourceFile, "file"); declare_entity!(SourceLoc, "loc"); diff --git a/src/ir/display.rs b/src/ir/display.rs index fb53a40..1eda466 100644 --- a/src/ir/display.rs +++ b/src/ir/display.rs @@ -2,7 +2,7 @@ use super::{Func, FuncDecl, FunctionBody, Module, SourceLoc, ValueDef}; use crate::entity::EntityRef; -use std::collections::HashMap; +use fxhash::FxHashMap as HashMap; use std::fmt::{self, Display, Formatter, Result as FmtResult}; /// Hooks to print information after instruction, before and after blocks @@ -253,7 +253,7 @@ impl<'a, PD: PrintDecorator> Display for ModuleDisplay<'a, PD> { if let Some(func) = self.module.start_func { writeln!(f, " start = {}", func)?; } - let mut sig_strs = HashMap::new(); + let mut sig_strs = HashMap::default(); for (sig, sig_data) in self.module.signatures.entries() { let arg_tys = sig_data .params diff --git a/src/ir/func.rs b/src/ir/func.rs index 0dccfba..953c65a 100644 --- a/src/ir/func.rs +++ b/src/ir/func.rs @@ -12,7 +12,7 @@ use crate::pool::{ListPool, ListRef}; use crate::Operator; use anyhow::Result; use fxhash::FxHashMap; -use std::collections::HashSet; +use fxhash::FxHashSet as HashSet; /// A declaration of a function: there is one `FuncDecl` per `Func` /// index. diff --git a/src/passes/maxssa.rs b/src/passes/maxssa.rs index 4b844b6..551edb8 100644 --- a/src/passes/maxssa.rs +++ b/src/passes/maxssa.rs @@ -7,7 +7,8 @@ use crate::cfg::CFGInfo; use crate::entity::PerEntity; use crate::ir::{Block, FunctionBody, Value, ValueDef}; -use std::collections::{BTreeSet, HashMap, HashSet}; +use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; +use std::collections::BTreeSet; pub(crate) fn run(body: &mut FunctionBody, cut_blocks: Option>, cfg: &CFGInfo) { MaxSSAPass::new(cut_blocks).run(body, cfg); @@ -30,7 +31,7 @@ impl MaxSSAPass { Self { cut_blocks, new_args: PerEntity::default(), - value_map: HashMap::new(), + value_map: HashMap::default(), } }