1use super::{AsyncConfig, CodegenSettings, InstanceAllocationStrategy, MemoryConfig, ModuleConfig};
4use crate::oracles::{StoreLimits, Timeout};
5use arbitrary::{Arbitrary, Unstructured};
6use std::num::NonZeroU32;
7use std::time::Duration;
8use wasmtime::Result;
9use wasmtime::{Enabled, Engine, Module, Store};
10use wasmtime_test_util::wast::{WastConfig, WastTest, limits};
11
12#[derive(Debug, Clone)]
19pub struct Config {
20 pub wasmtime: WasmtimeConfig,
22 pub module_config: ModuleConfig,
24}
25
26impl Config {
27 pub fn set_differential_config(&mut self) {
35 let config = &mut self.module_config.config;
36
37 config.min_types = config.min_types.max(1);
40 config.max_types = config.max_types.max(1);
41
42 config.min_funcs = config.min_funcs.max(1);
44 config.max_funcs = config.max_funcs.max(1);
45
46 config.max_memory32_bytes = 10 << 16;
50 config.max_memory64_bytes = 10 << 16;
51 config.memory_max_size_required = true;
52
53 config.max_table_elements = 1_000;
61 config.table_max_size_required = true;
62
63 config.max_imports = 0;
65
66 config.export_everything = true;
68
69 config.canonicalize_nans = true;
72
73 if let InstanceAllocationStrategy::Pooling(pooling) = &mut self.wasmtime.strategy {
75 pooling.total_memories = config.max_memories as u32;
77 pooling.max_memory_size = 10 << 16;
78 pooling.max_memories_per_module = config.max_memories as u32;
79 if pooling.memory_protection_keys == Enabled::Auto
80 && pooling.max_memory_protection_keys > 1
81 {
82 pooling.total_memories =
83 pooling.total_memories * (pooling.max_memory_protection_keys as u32);
84 }
85
86 pooling.total_tables = config.max_tables as u32;
87 pooling.table_elements = 1_000;
88 pooling.max_tables_per_module = config.max_tables as u32;
89
90 pooling.core_instance_size = 1_000_000;
91
92 let cfg = &mut self.wasmtime.memory_config;
93 match &mut cfg.memory_reservation {
94 Some(size) => *size = (*size).max(pooling.max_memory_size as u64),
95 other @ None => *other = Some(pooling.max_memory_size as u64),
96 }
97 }
98
99 config.relaxed_simd_enabled = false;
102
103 self.wasmtime.make_internally_consistent();
104 }
105
106 pub fn generate(
113 &self,
114 input: &mut Unstructured<'_>,
115 default_fuel: Option<u32>,
116 ) -> arbitrary::Result<wasm_smith::Module> {
117 self.module_config.generate(input, default_fuel)
118 }
119
120 pub fn make_wast_test_compliant(&mut self, test: &WastTest) -> WastConfig {
127 let wasmtime_test_util::wast::TestConfig {
128 bulk_memory,
129 memory64,
130 custom_page_sizes,
131 multi_memory,
132 threads,
133 shared_everything_threads,
134 gc,
135 function_references,
136 relaxed_simd,
137 reference_types,
138 tail_call,
139 extended_const,
140 wide_arithmetic,
141 branch_hinting,
142 compact_imports,
143 component_model_async,
144 component_model_more_async_builtins,
145 component_model_async_stackful,
146 component_model_threading,
147 component_model_error_context,
148 component_model_gc,
149 component_model_map,
150 component_model_memory64,
151 component_model_fixed_length_lists,
152 component_model_implements,
153 component_model_canonical_names,
154 component_model_accessors,
155 simd,
156 exceptions,
157 legacy_exceptions: _,
158 custom_descriptors: _,
159
160 hogs_memory: _,
161 nan_canonicalization: _,
162 gc_types: _,
163 stack_switching,
164 spec_test: _,
165 } = test.config;
166
167 self.module_config.function_references_enabled =
170 function_references.or(gc).unwrap_or(false);
171 self.module_config.component_model_async = component_model_async.unwrap_or(false);
172 self.module_config.component_model_more_async_builtins =
173 component_model_more_async_builtins.unwrap_or(false);
174 self.module_config.component_model_async_stackful =
175 component_model_async_stackful.unwrap_or(false);
176 self.module_config.component_model_threading = component_model_threading.unwrap_or(false);
177 self.module_config.component_model_error_context =
178 component_model_error_context.unwrap_or(false);
179 self.module_config.component_model_gc = component_model_gc.unwrap_or(false);
180 self.module_config.component_model_map = component_model_map.unwrap_or(false);
181 self.module_config.component_model_memory64 = component_model_memory64.unwrap_or(false);
182 self.module_config.component_model_fixed_length_lists =
183 component_model_fixed_length_lists.unwrap_or(false);
184 self.module_config.component_model_implements = component_model_implements.unwrap_or(false);
185 self.module_config.component_model_canonical_names =
186 component_model_canonical_names.unwrap_or(false);
187 self.module_config.component_model_accessors = component_model_accessors.unwrap_or(false);
188 self.module_config.stack_switching = stack_switching.unwrap_or(false);
189 self.wasmtime.branch_hinting = branch_hinting.unwrap_or(false);
190
191 let config = &mut self.module_config.config;
194 config.bulk_memory_enabled = bulk_memory.unwrap_or(false);
195 config.multi_value_enabled = true;
196 config.wide_arithmetic_enabled = wide_arithmetic.unwrap_or(false);
197 config.compact_imports_enabled = compact_imports.unwrap_or(false);
198 config.memory64_enabled = memory64.unwrap_or(false);
199 config.relaxed_simd_enabled = relaxed_simd.unwrap_or(false);
200 config.simd_enabled = config.relaxed_simd_enabled || simd.unwrap_or(false);
201 config.tail_call_enabled = tail_call.unwrap_or(false);
202 config.custom_page_sizes_enabled = custom_page_sizes.unwrap_or(false);
203 config.threads_enabled = threads.unwrap_or(false);
204 config.shared_everything_threads_enabled = shared_everything_threads.unwrap_or(false);
205 config.gc_enabled = gc.unwrap_or(false);
206 config.reference_types_enabled = config.gc_enabled
207 || self.module_config.function_references_enabled
208 || self.module_config.component_model_async
209 || reference_types.unwrap_or(false);
210 config.extended_const_enabled = extended_const.unwrap_or(false);
211 config.exceptions_enabled =
212 self.module_config.stack_switching || exceptions.unwrap_or(false);
213 if multi_memory.unwrap_or(false) {
214 config.max_memories = limits::MEMORIES_PER_MODULE as usize;
215 } else {
216 config.max_memories = 1;
217 }
218
219 if self.module_config.stack_switching {
220 self.wasmtime.inlining = Some(Inlining::No);
221 }
222
223 if let Some(n) = &mut self.wasmtime.memory_config.memory_reservation {
224 *n = (*n).max(limits::MEMORY_SIZE as u64);
225 }
226 if let Some(n) = &mut self.wasmtime.memory_config.gc_heap_reservation {
227 *n = (*n).max(limits::GC_HEAP_SIZE as u64);
228 }
229
230 if let InstanceAllocationStrategy::Pooling(pooling) = &mut self.wasmtime.strategy {
237 pooling.max_memory_protection_keys = pooling.max_memory_protection_keys.max(1).min(2);
241 pooling.total_memories = pooling
242 .total_memories
243 .max(limits::MEMORIES * (pooling.max_memory_protection_keys as u32));
244
245 pooling.total_component_instances = pooling
248 .total_component_instances
249 .max(limits::COMPONENT_INSTANCES);
250 pooling.total_tables = pooling.total_tables.max(limits::TABLES);
251 pooling.max_tables_per_module =
252 pooling.max_tables_per_module.max(limits::TABLES_PER_MODULE);
253 pooling.max_tables_per_component = pooling
254 .max_tables_per_component
255 .max(limits::TABLES_PER_MODULE);
256 pooling.max_memories_per_module = pooling
257 .max_memories_per_module
258 .max(limits::MEMORIES_PER_MODULE);
259 pooling.max_memories_per_component = pooling
260 .max_memories_per_component
261 .max(limits::MEMORIES_PER_COMPONENT);
262 pooling.total_core_instances = pooling.total_core_instances.max(limits::CORE_INSTANCES);
263 pooling.max_memory_size = pooling
264 .max_memory_size
265 .max(limits::MEMORY_SIZE)
266 .max(limits::GC_HEAP_SIZE);
267 pooling.table_elements = pooling.table_elements.max(limits::TABLE_ELEMENTS);
268 pooling.core_instance_size = pooling.core_instance_size.max(limits::CORE_INSTANCE_SIZE);
269 pooling.component_instance_size = pooling
270 .component_instance_size
271 .max(limits::CORE_INSTANCE_SIZE);
272 pooling.total_stacks = pooling.total_stacks.max(limits::TOTAL_STACKS);
273 }
274
275 self.wasmtime.make_internally_consistent();
279
280 WastConfig {
284 collector: match self.wasmtime.collector {
285 Collector::Null => wasmtime_test_util::wast::Collector::Null,
286 Collector::DeferredReferenceCounting => {
287 wasmtime_test_util::wast::Collector::DeferredReferenceCounting
288 }
289 Collector::Copying => wasmtime_test_util::wast::Collector::Copying,
290 },
291 pooling: matches!(
292 self.wasmtime.strategy,
293 InstanceAllocationStrategy::Pooling(_)
294 ),
295 compiler: match self.wasmtime.compiler_strategy {
296 CompilerStrategy::CraneliftNative => {
297 wasmtime_test_util::wast::Compiler::CraneliftNative
298 }
299 CompilerStrategy::CraneliftPulley => {
300 wasmtime_test_util::wast::Compiler::CraneliftPulley
301 }
302 CompilerStrategy::Winch => wasmtime_test_util::wast::Compiler::Winch,
303 },
304 }
305 }
306
307 pub fn to_wasmtime(&self) -> wasmtime::Config {
309 crate::init_fuzzing();
310
311 let mut cfg = wasmtime_cli_flags::CommonOptions::default();
312 cfg.codegen.native_unwind_info =
313 Some(cfg!(target_os = "windows") || self.wasmtime.native_unwind_info);
314 cfg.codegen.parallel_compilation = Some(false);
315
316 cfg.debug.address_map = Some(self.wasmtime.generate_address_map);
317 cfg.debug.symbols = Some(self.wasmtime.debug_symbols);
318 cfg.opts.opt_level = Some(self.wasmtime.opt_level.to_wasmtime());
319 cfg.opts.regalloc_algorithm = Some(self.wasmtime.regalloc_algorithm.to_wasmtime());
320 cfg.opts.signals_based_traps = Some(self.wasmtime.signals_based_traps);
321 cfg.opts.memory_guaranteed_dense_image_size = Some(std::cmp::min(
322 16 << 20,
325 self.wasmtime.memory_guaranteed_dense_image_size,
326 ));
327 cfg.opts.gc_zeal_alloc_counter = self
328 .wasmtime
329 .gc_zeal_alloc_counter
330 .map(|c| c.clamp(NonZeroU32::new(1).unwrap(), NonZeroU32::new(1024).unwrap()));
331 cfg.wasm.async_stack_zeroing = Some(self.wasmtime.async_stack_zeroing);
332 cfg.wasm.bulk_memory = Some(self.module_config.config.bulk_memory_enabled);
333 cfg.wasm.component_model_async = Some(self.module_config.component_model_async);
334 cfg.wasm.component_model_more_async_builtins =
335 Some(self.module_config.component_model_more_async_builtins);
336 cfg.wasm.component_model_async_stackful =
337 Some(self.module_config.component_model_async_stackful);
338 cfg.wasm.component_model_threading = Some(self.module_config.component_model_threading);
339 cfg.wasm.component_model_error_context =
340 Some(self.module_config.component_model_error_context);
341 cfg.wasm.component_model_gc = Some(self.module_config.component_model_gc);
342 cfg.wasm.component_model_map = Some(self.module_config.component_model_map);
343 cfg.wasm.component_model_memory64 = Some(self.module_config.component_model_memory64);
344 cfg.wasm.component_model_fixed_length_lists =
345 Some(self.module_config.component_model_fixed_length_lists);
346 cfg.wasm.component_model_implements = Some(self.module_config.component_model_implements);
347 cfg.wasm.component_model_canonical_names =
348 Some(self.module_config.component_model_canonical_names);
349 cfg.wasm.component_model_accessors = Some(self.module_config.component_model_accessors);
350 cfg.wasm.custom_page_sizes = Some(self.module_config.config.custom_page_sizes_enabled);
351 cfg.wasm.compact_imports = Some(self.module_config.config.compact_imports_enabled);
352 cfg.wasm.epoch_interruption = Some(self.wasmtime.epoch_interruption);
353 cfg.wasm.extended_const = Some(self.module_config.config.extended_const_enabled);
354 cfg.wasm.fuel = self.wasmtime.consume_fuel.then(|| u64::MAX);
355 cfg.wasm.function_references = Some(self.module_config.function_references_enabled);
356 cfg.wasm.gc = Some(self.module_config.config.gc_enabled);
357 cfg.wasm.memory64 = Some(self.module_config.config.memory64_enabled);
358 cfg.wasm.multi_memory = Some(self.module_config.config.max_memories > 1);
359 cfg.wasm.multi_value = Some(self.module_config.config.multi_value_enabled);
360 cfg.wasm.nan_canonicalization = Some(self.wasmtime.canonicalize_nans);
361 cfg.wasm.reference_types = Some(self.module_config.config.reference_types_enabled);
362 cfg.wasm.simd = Some(self.module_config.config.simd_enabled);
363 cfg.wasm.tail_call = Some(self.module_config.config.tail_call_enabled);
364 cfg.wasm.threads = Some(self.module_config.config.threads_enabled);
365 cfg.wasm.shared_everything_threads =
366 Some(self.module_config.config.shared_everything_threads_enabled);
367 cfg.wasm.wide_arithmetic = Some(self.module_config.config.wide_arithmetic_enabled);
368 cfg.wasm.branch_hinting = Some(self.wasmtime.branch_hinting);
369 cfg.wasm.exceptions = Some(self.module_config.config.exceptions_enabled);
370 if self.wasmtime.compiler_strategy == CompilerStrategy::Winch {
372 cfg.wasm.gc_support = Some(true);
373 }
374 cfg.wasm.stack_switching = Some(self.module_config.stack_switching);
375 cfg.wasm.shared_memory = Some(self.module_config.shared_memory);
376 if !self.module_config.config.simd_enabled {
377 cfg.wasm.relaxed_simd = Some(false);
378 }
379 cfg.codegen.collector = Some(self.wasmtime.collector.to_wasmtime());
380 cfg.codegen.metadata_for_internal_asserts =
381 Some(self.wasmtime.metadata_for_internal_asserts);
382 cfg.codegen.metadata_for_gc_heap_corruption =
383 Some(self.wasmtime.metadata_for_gc_heap_corruption);
384
385 let compiler_strategy = &self.wasmtime.compiler_strategy;
386 let cranelift_strategy = match compiler_strategy {
387 CompilerStrategy::CraneliftNative | CompilerStrategy::CraneliftPulley => true,
388 CompilerStrategy::Winch => false,
389 };
390 self.wasmtime.compiler_strategy.configure(&mut cfg);
391
392 if *compiler_strategy != CompilerStrategy::CraneliftPulley {
395 self.wasmtime.codegen.configure(&mut cfg);
396 }
397
398 cfg.codegen.inlining = self.wasmtime.inlining.map(|i| i.into());
399
400 cfg.wasm.nan_canonicalization = Some(!self.module_config.config.canonicalize_nans);
404
405 if cranelift_strategy {
408 if let Some(size) = self.wasmtime.inlining_small_callee_size {
409 cfg.codegen.cranelift.push((
410 "wasmtime_inlining_small_callee_size".to_string(),
411 Some(std::cmp::min(1000, size).to_string()),
413 ));
414 }
415 if let Some(size) = self.wasmtime.inlining_sum_size_threshold {
416 cfg.codegen.cranelift.push((
417 "wasmtime_inlining_sum_size_threshold".to_string(),
418 Some(std::cmp::min(1000, size).to_string()),
420 ));
421 }
422
423 if self.module_config.config.max_funcs > 10 {
429 cfg.codegen.cranelift_debug_verifier = Some(false);
430 }
431
432 if self.wasmtime.force_jump_veneers {
433 cfg.codegen.cranelift.push((
434 "wasmtime_linkopt_force_jump_veneer".to_string(),
435 Some("true".to_string()),
436 ));
437 }
438
439 if let Some(pad) = self.wasmtime.padding_between_functions {
440 cfg.codegen.cranelift.push((
441 "wasmtime_linkopt_padding_between_functions".to_string(),
442 Some(pad.to_string()),
443 ));
444 }
445
446 cfg.opts.table_lazy_init = Some(self.wasmtime.table_lazy_init);
448 }
449
450 self.wasmtime.strategy.configure(&mut cfg);
451
452 if !self.module_config.config.threads_enabled {
463 let memory_config = self.wasmtime.memory_config.clone();
464 memory_config.configure(&mut cfg);
465 }
466
467 log::debug!("creating wasmtime config with CLI options:\n{cfg}");
468 let mut cfg = cfg.config(None).expect("failed to create wasmtime::Config");
469
470 if self.wasmtime.async_config != AsyncConfig::Disabled {
471 log::debug!("async config in use {:?}", self.wasmtime.async_config);
472 self.wasmtime.async_config.configure(&mut cfg);
473 }
474
475 return cfg;
476 }
477
478 pub fn to_store(&self) -> Store<StoreLimits> {
481 let engine = Engine::new(&self.to_wasmtime()).unwrap();
482 let mut store = Store::new(&engine, StoreLimits::new());
483 self.configure_store(&mut store);
484 store
485 }
486
487 pub fn configure_store(&self, store: &mut Store<StoreLimits>) {
489 store.limiter(|s| s as &mut dyn wasmtime::ResourceLimiter);
490 self.configure_store_epoch_and_fuel(store);
491 }
492
493 pub fn configure_store_epoch_and_fuel<T>(&self, store: &mut Store<T>) {
496 if self.wasmtime.consume_fuel {
503 store.set_fuel(u64::MAX).unwrap();
504 }
505 if self.wasmtime.epoch_interruption {
506 store.epoch_deadline_trap();
507 store.set_epoch_deadline(1);
508 }
509 match self.wasmtime.async_config {
510 AsyncConfig::Disabled => {}
511 AsyncConfig::YieldWithFuel(amt) => {
512 assert!(self.wasmtime.consume_fuel);
513 store.fuel_async_yield_interval(Some(amt)).unwrap();
514 }
515 AsyncConfig::YieldWithEpochs { ticks, .. } => {
516 assert!(self.wasmtime.epoch_interruption);
517 store.set_epoch_deadline(ticks);
518 store.epoch_deadline_async_yield_and_update(ticks);
519 }
520 }
521 }
522
523 pub fn generate_timeout(&mut self, u: &mut Unstructured<'_>) -> arbitrary::Result<Timeout> {
526 let time_duration = Duration::from_millis(100);
527 let timeout = u
528 .choose(&[Timeout::Fuel(100_000), Timeout::Epoch(time_duration)])?
529 .clone();
530 match &timeout {
531 Timeout::Fuel(..) => {
532 self.wasmtime.consume_fuel = true;
533 }
534 Timeout::Epoch(..) => {
535 self.wasmtime.epoch_interruption = true;
536 }
537 Timeout::None => unreachable!("Not an option given to choose()"),
538 }
539 Ok(timeout)
540 }
541
542 pub fn compile(&self, engine: &Engine, wasm: &[u8]) -> Result<Module> {
547 let module = Module::new(engine, wasm)?;
550 if !self.wasmtime.use_precompiled_cwasm {
551 return Ok(module);
552 }
553
554 let dir = tempfile::TempDir::new().unwrap();
558 let file = dir.path().join("module.wasm");
559 std::fs::write(&file, module.serialize().unwrap()).unwrap();
560 unsafe { Ok(Module::deserialize_file(engine, &file).unwrap()) }
561 }
562
563 pub fn enable_async(&mut self, u: &mut Unstructured<'_>) -> arbitrary::Result<()> {
566 if self.wasmtime.consume_fuel || u.arbitrary()? {
567 self.wasmtime.async_config =
568 AsyncConfig::YieldWithFuel(u.int_in_range(1000..=100_000)?);
569 self.wasmtime.consume_fuel = true;
570 } else {
571 self.wasmtime.async_config = AsyncConfig::YieldWithEpochs {
572 dur: Duration::from_millis(u.int_in_range(1..=10)?),
573 ticks: u.int_in_range(1..=10)?,
574 };
575 self.wasmtime.epoch_interruption = true;
576 }
577 Ok(())
578 }
579}
580
581impl<'a> Arbitrary<'a> for Config {
582 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
583 let mut config = Self {
584 wasmtime: u.arbitrary()?,
585 module_config: u.arbitrary()?,
586 };
587
588 config
589 .wasmtime
590 .update_module_config(&mut config.module_config, u)?;
591
592 Ok(config)
593 }
594}
595
596#[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)]
599pub struct WasmtimeConfig {
600 opt_level: OptLevel,
601 regalloc_algorithm: RegallocAlgorithm,
602 debug_info: bool,
603 debug_symbols: bool,
604 canonicalize_nans: bool,
605 interruptible: bool,
606 pub(crate) consume_fuel: bool,
607 pub(crate) epoch_interruption: bool,
608 pub memory_config: MemoryConfig,
610 force_jump_veneers: bool,
611 memory_init_cow: bool,
612 memory_guaranteed_dense_image_size: u64,
613 inlining: Option<Inlining>,
614 inlining_small_callee_size: Option<u32>,
615 inlining_sum_size_threshold: Option<u32>,
616 use_precompiled_cwasm: bool,
617 async_stack_zeroing: bool,
618 pub strategy: InstanceAllocationStrategy,
620 codegen: CodegenSettings,
621 padding_between_functions: Option<u16>,
622 generate_address_map: bool,
623 native_unwind_info: bool,
624 pub compiler_strategy: CompilerStrategy,
626 collector: Collector,
627 gc_zeal_alloc_counter: Option<NonZeroU32>,
628 table_lazy_init: bool,
629 metadata_for_internal_asserts: bool,
630 metadata_for_gc_heap_corruption: bool,
631 branch_hinting: bool,
635
636 pub async_config: AsyncConfig,
639
640 signals_based_traps: bool,
643}
644
645impl WasmtimeConfig {
646 pub fn make_compatible_with(&mut self, other: &Self) {
650 self.strategy = other.strategy.clone();
662 if let InstanceAllocationStrategy::Pooling { .. } = &other.strategy {
663 self.memory_config = other.memory_config.clone();
666 }
667
668 self.make_internally_consistent();
669 }
670
671 pub fn update_module_config(
674 &mut self,
675 config: &mut ModuleConfig,
676 _u: &mut Unstructured<'_>,
677 ) -> arbitrary::Result<()> {
678 match self.compiler_strategy {
679 CompilerStrategy::CraneliftNative => {}
680
681 CompilerStrategy::Winch => {
682 config.config.relaxed_simd_enabled = false;
687 config.config.gc_enabled = false;
688 config.config.reference_types_enabled = false;
689 config.function_references_enabled = false;
690 config.stack_switching = false;
691
692 if self
694 .codegen_flag("has_avx")
695 .is_some_and(|value| value == "false")
696 || self
697 .codegen_flag("has_avx2")
698 .is_some_and(|value| value == "false")
699 {
700 config.config.simd_enabled = false;
701 }
702
703 if cfg!(target_arch = "aarch64") {
705 config.config.wide_arithmetic_enabled = false;
706 config.config.threads_enabled = false;
707 }
708
709 self.signals_based_traps = true;
712 self.table_lazy_init = true;
713 self.debug_info = false;
714 }
715
716 CompilerStrategy::CraneliftPulley => {
717 config.config.threads_enabled = false;
718
719 self.signals_based_traps = false;
723 }
724 }
725
726 if cfg!(target_arch = "s390x") {
731 self.signals_based_traps = false;
732 }
733
734 if let InstanceAllocationStrategy::Pooling(pooling) = &mut self.strategy {
737 config.config.threads_enabled = false;
741
742 let min_bytes = config
745 .config
746 .max_memory32_bytes
747 .min(
750 config
751 .config
752 .max_memory64_bytes
753 .try_into()
754 .unwrap_or(u64::MAX),
755 );
756 let min = min_bytes
757 .min(pooling.max_memory_size as u64)
758 .min(self.memory_config.memory_reservation.unwrap_or(0));
759 pooling.max_memory_size = min as usize;
760 config.config.max_memory32_bytes = min;
761 config.config.max_memory64_bytes = min as u128;
762
763 if config.config.disallow_traps {
767 if pooling.max_memory_size < (1 << 16) {
768 pooling.max_memory_size = 1 << 16;
769 config.config.max_memory32_bytes = 1 << 16;
770 config.config.max_memory64_bytes = 1 << 16;
771 let cfg = &mut self.memory_config;
772 match &mut cfg.memory_reservation {
773 Some(size) => *size = (*size).max(pooling.max_memory_size as u64),
774 size @ None => *size = Some(pooling.max_memory_size as u64),
775 }
776 }
777 if pooling.table_elements == 0 {
779 pooling.table_elements = 1;
780 }
781 }
782
783 config.config.min_memories = config.config.min_memories.min(10);
786 config.config.max_memories = config.config.max_memories.min(10);
787
788 pooling.total_memories = config.config.max_memories as u32;
791 pooling.total_tables = config.config.max_tables as u32;
792 }
793
794 if !self.signals_based_traps {
795 config.config.threads_enabled = false;
802
803 self.memory_config
806 .cranelift_enable_heap_access_spectre_mitigations = None;
807 }
808
809 self.make_internally_consistent();
810
811 Ok(())
812 }
813
814 pub(crate) fn codegen_flag(&self, name: &str) -> Option<&str> {
816 self.codegen.flags().iter().find_map(|(n, value)| {
817 if n == name {
818 Some(value.as_str())
819 } else {
820 None
821 }
822 })
823 }
824
825 pub(crate) fn make_internally_consistent(&mut self) {
834 if !self.signals_based_traps {
835 self.memory_config
839 .cranelift_enable_heap_access_spectre_mitigations = None;
840 }
841
842 let is_pulley = self.compiler_strategy == CompilerStrategy::CraneliftPulley;
850 let mcfg = &mut self.memory_config;
851 if !self.signals_based_traps || is_pulley {
852 if (mcfg.memory_guard_size == Some(0) || is_pulley)
853 && mcfg.memory_reservation == Some(0)
854 && !mcfg.memory_init_cow
855 {
856 let growth = &mut mcfg.memory_reservation_for_growth;
857 let max = 1 << 20;
858 *growth = match *growth {
859 Some(n) => Some(n.min(max)),
860 None => Some(max),
861 };
862 }
863 if (mcfg.gc_heap_guard_size == Some(0) || is_pulley)
864 && mcfg.gc_heap_reservation == Some(0)
865 {
866 let growth = &mut mcfg.gc_heap_reservation_for_growth;
867 let max = 1 << 20;
868 *growth = match *growth {
869 Some(n) => Some(n.min(max)),
870 None => Some(max),
871 };
872 }
873 }
874
875 if let InstanceAllocationStrategy::Pooling(pcfg) = &self.strategy {
878 let reservation = mcfg.gc_heap_reservation.max(mcfg.memory_reservation);
879 mcfg.gc_heap_reservation = reservation;
880 mcfg.memory_reservation = reservation;
881
882 let guard_size = mcfg.gc_heap_guard_size.max(mcfg.memory_guard_size);
883 mcfg.gc_heap_guard_size = guard_size;
884 mcfg.memory_guard_size = guard_size;
885
886 let res_for_growth = mcfg
887 .gc_heap_reservation_for_growth
888 .max(mcfg.memory_reservation_for_growth);
889 mcfg.gc_heap_reservation_for_growth = res_for_growth;
890 mcfg.memory_reservation_for_growth = res_for_growth;
891
892 mcfg.gc_heap_may_move = None;
895
896 if let Some(amt) = mcfg.gc_heap_initial_size {
901 let page_size = 64 * 1024;
902 let amt = amt.next_multiple_of(page_size);
903 let max = (pcfg.max_memory_size as u64) / page_size * page_size;
906 mcfg.gc_heap_initial_size = Some(amt.min(max));
907 }
908 }
909
910 if !self.debug_symbols {
911 self.debug_info = false;
912 }
913 }
914}
915
916#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
917enum OptLevel {
918 None,
919 Speed,
920 SpeedAndSize,
921}
922
923impl OptLevel {
924 fn to_wasmtime(&self) -> wasmtime::OptLevel {
925 match self {
926 OptLevel::None => wasmtime::OptLevel::None,
927 OptLevel::Speed => wasmtime::OptLevel::Speed,
928 OptLevel::SpeedAndSize => wasmtime::OptLevel::SpeedAndSize,
929 }
930 }
931}
932
933#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
934enum RegallocAlgorithm {
935 Backtracking,
936 SinglePass,
937}
938
939impl RegallocAlgorithm {
940 fn to_wasmtime(&self) -> wasmtime::RegallocAlgorithm {
941 match self {
942 RegallocAlgorithm::Backtracking => wasmtime::RegallocAlgorithm::Backtracking,
943 RegallocAlgorithm::SinglePass => {
944 const SINGLE_PASS_KNOWN_BUGGY_AT_THIS_TIME: bool = false;
945 if SINGLE_PASS_KNOWN_BUGGY_AT_THIS_TIME {
946 wasmtime::RegallocAlgorithm::Backtracking
947 } else {
948 wasmtime::RegallocAlgorithm::SinglePass
949 }
950 }
951 }
952 }
953}
954
955#[derive(Arbitrary, Clone, Copy, Debug, PartialEq, Eq, Hash)]
956enum Inlining {
957 Yes,
958 InterModuleAndIntraGc,
959 InterModule,
960 Intrinsics,
961 No,
962}
963
964impl From<Inlining> for wasmtime::Inlining {
965 fn from(i: Inlining) -> Self {
966 let ret = match i {
967 Inlining::Yes => wasmtime::Inlining::Yes,
968 Inlining::InterModuleAndIntraGc => wasmtime::Inlining::InterModuleAndIntraGc,
969 Inlining::InterModule => wasmtime::Inlining::InterModule,
970 Inlining::Intrinsics => wasmtime::Inlining::Intrinsics,
971 Inlining::No => wasmtime::Inlining::No,
972 };
973
974 match ret {
975 wasmtime::Inlining::Yes
976 | wasmtime::Inlining::No
977 | wasmtime::Inlining::InterModuleAndIntraGc
978 | wasmtime::Inlining::InterModule
979 | wasmtime::Inlining::Intrinsics
980 => ret,
983 }
984 }
985}
986
987#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
988pub enum CompilerStrategy {
990 CraneliftNative,
992 Winch,
994 CraneliftPulley,
996}
997
998impl CompilerStrategy {
999 pub const CRANELIFT_NATIVE_SUPPORTED: bool = cfg!(any(
1001 target_arch = "x86_64",
1002 target_arch = "aarch64",
1003 target_arch = "riscv64",
1004 target_arch = "s390x",
1005 ));
1006
1007 pub const WINCH_SUPPORTED: bool = cfg!(any(target_arch = "x86_64", target_arch = "aarch64"));
1009
1010 pub fn configure(&self, config: &mut wasmtime_cli_flags::CommonOptions) {
1012 match self {
1013 CompilerStrategy::CraneliftNative => {
1014 config.codegen.compiler = Some(wasmtime::Strategy::Cranelift);
1015 }
1016 CompilerStrategy::Winch => {
1017 config.codegen.compiler = Some(wasmtime::Strategy::Winch);
1018 }
1019 CompilerStrategy::CraneliftPulley => {
1020 config.codegen.compiler = Some(wasmtime::Strategy::Cranelift);
1021 if cfg!(target_pointer_width = "32") {
1022 config.target = Some("pulley32".to_string());
1023 } else if cfg!(target_endian = "big") {
1024 config.target = Some("pulley64be".to_string());
1025 } else {
1026 config.target = Some("pulley64".to_string());
1027 }
1028 }
1029 }
1030 }
1031}
1032
1033impl Arbitrary<'_> for CompilerStrategy {
1034 fn arbitrary(u: &mut Unstructured<'_>) -> arbitrary::Result<Self> {
1035 let native = if Self::CRANELIFT_NATIVE_SUPPORTED {
1040 Self::CraneliftNative
1041 } else {
1042 Self::CraneliftPulley
1043 };
1044 let winch = if Self::WINCH_SUPPORTED {
1045 Self::Winch
1046 } else {
1047 native
1048 };
1049
1050 Ok(match u.int_in_range(0..=19)? {
1053 1 => Self::CraneliftPulley,
1054 2 => winch,
1055 _ => native,
1056 })
1057 }
1058}
1059
1060#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
1061pub enum Collector {
1062 DeferredReferenceCounting,
1063 Null,
1064 Copying,
1065}
1066
1067impl Collector {
1068 fn to_wasmtime(&self) -> wasmtime::Collector {
1069 match self {
1070 Collector::DeferredReferenceCounting => wasmtime::Collector::DeferredReferenceCounting,
1071 Collector::Null => wasmtime::Collector::Null,
1072 Collector::Copying => wasmtime::Collector::Copying,
1073 }
1074 }
1075}