diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index d27b9b306d..c930fbb4f5 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -20,7 +20,7 @@ use cranelift_wasm::{ }; use std::any::Any; use std::cmp; -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet}; use std::convert::TryFrom; use std::mem; use std::sync::Mutex; @@ -306,7 +306,7 @@ impl wasmtime_environ::Compiler for Compiler { self.isa.triple() } - fn flags(&self) -> HashMap { + fn flags(&self) -> BTreeMap { self.isa .flags() .iter() @@ -314,7 +314,7 @@ impl wasmtime_environ::Compiler for Compiler { .collect() } - fn isa_flags(&self) -> HashMap { + fn isa_flags(&self) -> BTreeMap { self.isa .isa_flags() .iter() diff --git a/crates/environ/src/compilation.rs b/crates/environ/src/compilation.rs index 9037fedd3f..607eccc2b9 100644 --- a/crates/environ/src/compilation.rs +++ b/crates/environ/src/compilation.rs @@ -9,7 +9,7 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use std::any::Any; use std::borrow::Cow; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::fmt; use thiserror::Error; @@ -204,10 +204,10 @@ pub trait Compiler: Send + Sync { fn triple(&self) -> &target_lexicon::Triple; /// Returns a list of configured settings for this compiler. - fn flags(&self) -> HashMap; + fn flags(&self) -> BTreeMap; /// Same as [`Compiler::flags`], but ISA-specific (a cranelift-ism) - fn isa_flags(&self) -> HashMap; + fn isa_flags(&self) -> BTreeMap; } /// Value of a configured setting for a [`Compiler`] diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index 11814afb14..d2f109a329 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -3,7 +3,7 @@ use crate::{EntityRef, PrimaryMap, Tunables}; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet}; use std::convert::TryFrom; use std::sync::Arc; use wasmtime_types::*; @@ -349,17 +349,17 @@ pub struct Module { pub passive_elements: Vec>, /// The map from passive element index (element segment index space) to index in `passive_elements`. - pub passive_elements_map: HashMap, + pub passive_elements_map: BTreeMap, /// WebAssembly passive data segments. #[serde(with = "passive_data_serde")] pub passive_data: Vec>, /// The map from passive data index (data segment index space) to index in `passive_data`. - pub passive_data_map: HashMap, + pub passive_data_map: BTreeMap, /// WebAssembly function names. - pub func_names: HashMap, + pub func_names: BTreeMap, /// Types declared in the wasm module. pub types: PrimaryMap, @@ -396,7 +396,7 @@ pub struct Module { /// The set of defined functions within this module which are located in /// element segments. - pub possibly_exported_funcs: HashSet, + pub possibly_exported_funcs: BTreeSet, } /// Initialization routines for creating an instance, encompassing imports, diff --git a/crates/lightbeam/wasmtime/src/lib.rs b/crates/lightbeam/wasmtime/src/lib.rs index c32e5ec5a0..4850e3ffc0 100644 --- a/crates/lightbeam/wasmtime/src/lib.rs +++ b/crates/lightbeam/wasmtime/src/lib.rs @@ -9,7 +9,7 @@ use anyhow::Result; use cranelift_codegen::binemit; use cranelift_codegen::ir::{self, ExternalName}; use std::any::Any; -use std::collections::HashMap; +use std::collections::BTreeMap; use wasmtime_environ::{ BuiltinFunctionIndex, CompileError, Compiler, FlagValue, FunctionBodyData, FunctionInfo, Module, ModuleTranslation, PrimaryMap, TrapInformation, Tunables, TypeTables, VMOffsets, @@ -96,11 +96,11 @@ impl Compiler for Lightbeam { unimplemented!() } - fn flags(&self) -> HashMap { + fn flags(&self) -> BTreeMap { unimplemented!() } - fn isa_flags(&self) -> HashMap { + fn isa_flags(&self) -> BTreeMap { unimplemented!() } } diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index f735cb0198..c2c98fd712 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -16,7 +16,7 @@ use memoffset::offset_of; use more_asserts::assert_lt; use std::alloc::Layout; use std::any::Any; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::convert::TryFrom; use std::hash::Hash; use std::ptr::NonNull; @@ -511,13 +511,13 @@ impl Instance { fn find_passive_segment<'a, I, D, T>( index: I, - index_map: &HashMap, + index_map: &BTreeMap, data: &'a Vec, dropped: &EntitySet, ) -> &'a [T] where D: AsRef<[T]>, - I: EntityRef + Hash, + I: EntityRef + Ord, { match index_map.get(&index) { Some(index) if !dropped.contains(I::new(*index)) => data[*index].as_ref(), diff --git a/crates/wasmtime/src/module/serialization.rs b/crates/wasmtime/src/module/serialization.rs index 7e7432cb27..2a971663f8 100644 --- a/crates/wasmtime/src/module/serialization.rs +++ b/crates/wasmtime/src/module/serialization.rs @@ -4,7 +4,7 @@ use crate::{Engine, Module}; use anyhow::{anyhow, bail, Context, Result}; use bincode::Options; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::str::FromStr; use std::sync::Arc; use wasmtime_environ::{Compiler, FlagValue, Tunables}; @@ -152,8 +152,8 @@ impl SerializedModuleUpvar { #[derive(Serialize, Deserialize)] pub struct SerializedModule<'a> { target: String, - shared_flags: HashMap, - isa_flags: HashMap, + shared_flags: BTreeMap, + isa_flags: BTreeMap, tunables: Tunables, features: WasmFeatures, artifacts: Vec>, diff --git a/tests/all/module.rs b/tests/all/module.rs index e8c2327929..73143ea00c 100644 --- a/tests/all/module.rs +++ b/tests/all/module.rs @@ -78,3 +78,46 @@ fn aot_compiles() -> Result<()> { Ok(()) } + +#[test] +fn serialize_deterministic() { + let engine = Engine::default(); + + let assert_deterministic = |wasm: &str| { + let p1 = engine.precompile_module(wasm.as_bytes()).unwrap(); + let p2 = engine.precompile_module(wasm.as_bytes()).unwrap(); + if p1 != p2 { + panic!("precompile_module not determinisitc for:\n{}", wasm); + } + + let module1 = Module::new(&engine, wasm).unwrap(); + let a1 = module1.serialize().unwrap(); + let a2 = module1.serialize().unwrap(); + if a1 != a2 { + panic!("Module::serialize not determinisitc for:\n{}", wasm); + } + + let module2 = Module::new(&engine, wasm).unwrap(); + let b1 = module2.serialize().unwrap(); + let b2 = module2.serialize().unwrap(); + if b1 != b2 { + panic!("Module::serialize not determinisitc for:\n{}", wasm); + } + + if a1 != b2 { + panic!("not matching across modules:\n{}", wasm); + } + if b1 != p2 { + panic!("not matching across engine/module:\n{}", wasm); + } + }; + + assert_deterministic("(module)"); + assert_deterministic("(module (func))"); + assert_deterministic("(module (func nop))"); + assert_deterministic("(module (func) (func (param i32)))"); + assert_deterministic("(module (func (export \"f\")) (func (export \"y\")))"); + assert_deterministic("(module (func $f) (func $g))"); + assert_deterministic("(module (data \"\") (data \"\"))"); + assert_deterministic("(module (elem) (elem))"); +}