Skip to main content

wasmtime_test_util/
wast.rs

1use serde::de::DeserializeOwned;
2use serde_derive::Deserialize;
3use std::fmt;
4use std::fs;
5use std::path::Path;
6use std::path::PathBuf;
7use wasmtime_environ::prelude::*;
8
9/// Limits for running wast tests.
10///
11/// This is useful for sharing between `tests/wast.rs` and fuzzing, for
12/// example, and is used as the minimum threshold for configuration when
13/// fuzzing.
14///
15/// Note that it's ok to increase these numbers if a test comes along and needs
16/// it, they're just here as empirically found minimum thresholds so far and
17/// they're not too scientific.
18pub mod limits {
19    pub const MEMORY_SIZE: usize = 805 << 16;
20    pub const MEMORIES: u32 = 450;
21    pub const GC_HEAP_SIZE: usize = 10 << 16;
22    pub const TABLES: u32 = 200;
23    pub const MEMORIES_PER_MODULE: u32 = 9;
24    pub const TABLES_PER_MODULE: u32 = 5;
25    pub const COMPONENT_INSTANCES: u32 = 50;
26    pub const CORE_INSTANCES: u32 = 900;
27    pub const TABLE_ELEMENTS: usize = 1000;
28    pub const CORE_INSTANCE_SIZE: usize = 64 * 1024;
29    pub const TOTAL_STACKS: u32 = 20;
30}
31
32/// Local all `*.wast` tests under `root` which should be the path to the root
33/// of the wasmtime repository.
34pub fn find_tests(root: &Path) -> Result<Vec<WastTest>> {
35    let mut tests = Vec::new();
36
37    let spec_tests = root.join("tests/spec_testsuite");
38    add_tests(
39        &mut tests,
40        &spec_tests,
41        &FindConfig::Infer(spec_test_config),
42    )
43    .context("Do you need to `git submodule update --init`?")
44    .with_context(|| format!("failed to add tests from `{}`", spec_tests.display()))?;
45
46    let misc_tests = root.join("tests/misc_testsuite");
47    add_tests(&mut tests, &misc_tests, &FindConfig::InTest)
48        .with_context(|| format!("failed to add tests from `{}`", misc_tests.display()))?;
49
50    let cm_tests = root.join("tests/component-model/test");
51    add_tests(
52        &mut tests,
53        &cm_tests,
54        &FindConfig::Infer(component_test_config),
55    )
56    .context("Do you need to `git submodule update --init`?")
57    .with_context(|| format!("failed to add tests from `{}`", cm_tests.display()))?;
58
59    // Temporarily work around upstream tests that fail in unexpected ways (e.g.
60    // panics, loops, etc).
61    {
62        let skip_list = &[
63            // .. empty currently ..
64        ];
65        tests.retain(|test| {
66            test.path
67                .file_name()
68                .and_then(|name| name.to_str())
69                .map(|name| !skip_list.contains(&name))
70                .unwrap_or(true)
71        });
72    }
73
74    Ok(tests)
75}
76
77enum FindConfig {
78    InTest,
79    Infer(fn(&Path) -> TestConfig),
80}
81
82fn add_tests(tests: &mut Vec<WastTest>, path: &Path, config: &FindConfig) -> Result<()> {
83    for entry in path.read_dir().context("failed to read directory")? {
84        let entry = entry.context("failed to read directory entry")?;
85        let path = entry.path();
86        if entry
87            .file_type()
88            .context("failed to get file type")?
89            .is_dir()
90        {
91            add_tests(tests, &path, config).context("failed to read sub-directory")?;
92            continue;
93        }
94
95        if path.extension().and_then(|s| s.to_str()) != Some("wast") {
96            continue;
97        }
98
99        // These tests use `*.wast` directives not yet supported by Wasmtime, so
100        // wait for a `wasm-tools` update to ungate these.
101        if path.ends_with("spec_testsuite/custom/custom_annot.wast")
102            || path.ends_with("spec_testsuite/custom/branch_hint.wast")
103            || path.ends_with("spec_testsuite/custom/name_annot.wast")
104        {
105            continue;
106        }
107
108        let contents =
109            fs::read_to_string(&path).with_context(|| format!("failed to read test: {path:?}"))?;
110        let config = match config {
111            FindConfig::InTest => parse_test_config(&contents, ";;!")
112                .with_context(|| format!("failed to parse test configuration: {path:?}"))?,
113            FindConfig::Infer(f) => f(&path),
114        };
115        tests.push(WastTest {
116            path,
117            contents,
118            config,
119        })
120    }
121    Ok(())
122}
123
124fn spec_test_config(test: &Path) -> TestConfig {
125    let mut ret = TestConfig::default();
126    ret.spec_test = Some(true);
127    ret.bulk_memory = Some(true);
128    match spec_proposal_from_path(test) {
129        Some("wide-arithmetic") => {
130            ret.wide_arithmetic = Some(true);
131        }
132        Some("threads") => {
133            ret.threads = Some(true);
134            ret.reference_types = Some(false);
135        }
136        Some("custom-page-sizes") => {
137            ret.custom_page_sizes = Some(true);
138            ret.multi_memory = Some(true);
139            ret.memory64 = Some(true);
140            ret.reference_types = Some(true);
141
142            // See commentary below in `wasm-3.0` case for why these "hog
143            // memory"
144            if test.ends_with("memory_max.wast") || test.ends_with("memory_max_i64.wast") {
145                ret.hogs_memory = Some(true);
146            }
147        }
148        Some("custom-descriptors") => {
149            ret.custom_descriptors = Some(true);
150        }
151        Some(proposal) => panic!("unsupported proposal {proposal:?}"),
152        None => {
153            ret.reference_types = Some(true);
154            ret.simd = Some(true);
155            ret.simd = Some(true);
156            ret.relaxed_simd = Some(true);
157            ret.multi_memory = Some(true);
158            ret.gc = Some(true);
159            ret.reference_types = Some(true);
160            ret.memory64 = Some(true);
161            ret.tail_call = Some(true);
162            ret.extended_const = Some(true);
163            ret.exceptions = Some(true);
164
165            if test.parent().unwrap().ends_with("legacy") {
166                ret.legacy_exceptions = Some(true);
167            }
168
169            // These tests technically don't actually hog any memory but they
170            // do have a module definition with a table/memory that is the
171            // maximum size. These modules fail to compile in the pooling
172            // allocator which has limits on the minimum size of
173            // memories/tables by default.
174            //
175            // Pretend that these hog memory to avoid running the tests in the
176            // pooling allocator.
177            if test.ends_with("memory.wast")
178                || test.ends_with("table.wast")
179                || test.ends_with("memory64.wast")
180                || test.ends_with("table64.wast")
181            {
182                ret.hogs_memory = Some(true);
183            }
184        }
185    }
186
187    ret
188}
189
190fn component_test_config(test: &Path) -> TestConfig {
191    let mut ret = TestConfig::default();
192    ret.spec_test = Some(true);
193    ret.reference_types = Some(true);
194    ret.multi_memory = Some(true);
195
196    if let Some(parent) = test.parent() {
197        if parent.ends_with("async")
198            || [
199                "trap-in-post-return.wast",
200                "resources.wast",
201                "multiple-resources.wast",
202            ]
203            .into_iter()
204            .any(|name| Some(name) == test.file_name().and_then(|s| s.to_str()))
205        {
206            ret.component_model_async = Some(true);
207            ret.component_model_async_stackful = Some(true);
208            ret.component_model_more_async_builtins = Some(true);
209            ret.component_model_threading = Some(true);
210        }
211        if parent.ends_with("wasm-tools") {
212            ret.memory64 = Some(true);
213            ret.threads = Some(true);
214            ret.exceptions = Some(true);
215            ret.gc = Some(true);
216        }
217        if parent.ends_with("wasmtime") {
218            ret.exceptions = Some(true);
219            ret.gc = Some(true);
220        }
221    }
222
223    ret
224}
225
226/// Parse test configuration from the specified test, comments starting with
227/// `;;!`.
228pub fn parse_test_config<T>(wat: &str, comment: &'static str) -> Result<T>
229where
230    T: DeserializeOwned,
231{
232    // The test config source is the leading lines of the WAT file that are
233    // prefixed with `;;!`.
234    let config_lines: Vec<_> = wat
235        .lines()
236        .take_while(|l| l.starts_with(comment))
237        .map(|l| &l[comment.len()..])
238        .collect();
239    let config_text = config_lines.join("\n");
240
241    toml::from_str(&config_text).context("failed to parse the test configuration")
242}
243
244/// A `*.wast` test with its path, contents, and configuration.
245#[derive(Clone)]
246pub struct WastTest {
247    pub path: PathBuf,
248    pub contents: String,
249    pub config: TestConfig,
250}
251
252impl fmt::Debug for WastTest {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        f.debug_struct("WastTest")
255            .field("path", &self.path)
256            .field("contents", &"...")
257            .field("config", &self.config)
258            .finish()
259    }
260}
261
262macro_rules! foreach_config_option {
263    ($m:ident) => {
264        $m! {
265            bulk_memory
266            memory64
267            custom_page_sizes
268            multi_memory
269            threads
270            shared_everything_threads
271            gc
272            function_references
273            relaxed_simd
274            reference_types
275            tail_call
276            extended_const
277            wide_arithmetic
278            branch_hinting
279            hogs_memory
280            nan_canonicalization
281            component_model_async
282            component_model_more_async_builtins
283            component_model_async_stackful
284            component_model_threading
285            component_model_error_context
286            component_model_gc
287            component_model_map
288            component_model_fixed_length_lists
289            component_model_implements
290            simd
291            gc_types
292            exceptions
293            legacy_exceptions
294            stack_switching
295            spec_test
296            custom_descriptors
297        }
298    };
299}
300
301macro_rules! define_test_config {
302    ($($option:ident)*) => {
303        /// Per-test configuration which is written down in the test file itself for
304        /// `misc_testsuite/**/*.wast` or in `spec_test_config` above for spec tests.
305        #[derive(Debug, PartialEq, Default, Deserialize, Clone)]
306        #[serde(deny_unknown_fields)]
307        pub struct TestConfig {
308            $(pub $option: Option<bool>,)*
309        }
310
311        impl TestConfig {
312            $(
313                pub fn $option(&self) -> bool {
314                    self.$option.unwrap_or(false)
315                }
316            )*
317        }
318    }
319}
320
321foreach_config_option!(define_test_config);
322
323impl TestConfig {
324    /// Returns an iterator over each option.
325    pub fn options_mut(&mut self) -> impl Iterator<Item = (&'static str, &mut Option<bool>)> {
326        macro_rules! mk {
327            ($($option:ident)*) => {
328                [
329                    $((stringify!($option), &mut self.$option),)*
330                ].into_iter()
331            }
332        }
333        foreach_config_option!(mk)
334    }
335}
336
337/// Configuration that spec tests can run under.
338#[derive(Debug)]
339pub struct WastConfig {
340    /// Compiler chosen to run this test.
341    pub compiler: Compiler,
342    /// Whether or not the pooling allocator is enabled.
343    pub pooling: bool,
344    /// What garbage collector is being used.
345    pub collector: Collector,
346}
347
348/// Different compilers that can be tested in Wasmtime.
349#[derive(PartialEq, Debug, Copy, Clone)]
350pub enum Compiler {
351    /// Cranelift backend.
352    ///
353    /// This tests the Cranelift code generator for native platforms. This
354    /// notably excludes Pulley since that's listed separately below even though
355    /// Pulley is a backend of Cranelift. This is only used for native code
356    /// generation such as x86_64.
357    CraneliftNative,
358
359    /// Winch backend.
360    ///
361    /// This tests the Winch backend for native platforms. Currently Winch
362    /// primarily supports x86_64.
363    Winch,
364
365    /// Pulley interpreter.
366    ///
367    /// This tests the Cranelift pulley backend plus the pulley execution
368    /// environment of the output bytecode. Note that this is separate from
369    /// `Cranelift` above to be able to test both on platforms where Cranelift
370    /// has native codegen support.
371    CraneliftPulley,
372}
373
374impl Compiler {
375    /// Returns whether this compiler is known to fail for the provided
376    /// `TestConfig`.
377    ///
378    /// This function will determine if the configuration of the test provided
379    /// is known to guarantee fail. This effectively tracks the proposal support
380    /// for each compiler backend/runtime and tests whether `config` enables or
381    /// disables features that aren't supported.
382    ///
383    /// Note that this is closely aligned with
384    /// `Config::compiler_panicking_wasm_features`.
385    pub fn should_fail(&self, config: &TestConfig) -> bool {
386        match self {
387            Compiler::CraneliftNative => {
388                if config.legacy_exceptions() {
389                    return true;
390                }
391
392                // Stack-switching is only implemented on x86_64 for unix
393                // platforms right now.
394                if config.stack_switching() && !(cfg!(target_arch = "x86_64") && cfg!(unix)) {
395                    return true;
396                }
397
398                false
399            }
400
401            Compiler::Winch => {
402                if config.gc()
403                    || config.tail_call()
404                    || config.function_references()
405                    || config.gc()
406                    || config.relaxed_simd()
407                    || config.gc_types()
408                    || config.exceptions()
409                    || config.legacy_exceptions()
410                    || config.stack_switching()
411                    || config.legacy_exceptions()
412                    || config.component_model_async()
413                {
414                    return true;
415                }
416
417                if cfg!(target_arch = "aarch64") {
418                    return (config.simd() && !config.spec_test()) || config.threads();
419                }
420
421                !cfg!(target_arch = "x86_64")
422            }
423
424            Compiler::CraneliftPulley => {
425                config.threads() || config.legacy_exceptions() || config.stack_switching()
426            }
427        }
428    }
429
430    /// Returns whether this compiler configuration supports the current host
431    /// architecture.
432    pub fn supports_host(&self) -> bool {
433        match self {
434            Compiler::CraneliftNative => {
435                cfg!(target_arch = "x86_64")
436                    || cfg!(target_arch = "aarch64")
437                    || cfg!(target_arch = "riscv64")
438                    || cfg!(target_arch = "s390x")
439            }
440            Compiler::Winch => cfg!(target_arch = "x86_64") || cfg!(target_arch = "aarch64"),
441            Compiler::CraneliftPulley => true,
442        }
443    }
444}
445
446#[derive(PartialEq, Debug, Copy, Clone)]
447pub enum Collector {
448    Auto,
449    Null,
450    DeferredReferenceCounting,
451    Copying,
452}
453
454impl WastTest {
455    /// Returns whether this test exercises the GC types and might want to use
456    /// multiple different garbage collectors.
457    pub fn test_uses_gc_types(&self) -> bool {
458        self.config.gc() || self.config.function_references()
459    }
460
461    /// Returns the optional spec proposal that this test is associated with.
462    pub fn spec_proposal(&self) -> Option<&str> {
463        spec_proposal_from_path(&self.path)
464    }
465
466    /// Returns whether this test should fail under the specified extra
467    /// configuration.
468    pub fn should_fail(&self, config: &WastConfig) -> bool {
469        if !config.compiler.supports_host() {
470            return true;
471        }
472
473        let unsupported = [
474            // These tests in the `component-model` submodule have not yet been
475            // updated to account for the recent threading-related intrinsic
476            // changes.
477            "test/async/trap-if-block-and-sync.wast",
478            // Wasmtime doesn't expose the component-model `cm64` feature toggle
479            // yet, so this parser-only test can't be enabled here.
480            "test/wasm-tools/memory64.wast",
481        ];
482        if unsupported.iter().any(|part| self.path.ends_with(part)) {
483            return true;
484        }
485
486        // Some tests are known to fail with the pooling allocator
487        if config.pooling {
488            // allocates too much memory for the pooling configuration here
489            if self.config.hogs_memory() {
490                return true;
491            }
492            let unsupported = [
493                // shared memories + pooling allocator aren't supported yet
494                "misc_testsuite/memory-combos.wast",
495                "misc_testsuite/threads/atomics-end-of-memory.wast",
496                "misc_testsuite/threads/LB.wast",
497                "misc_testsuite/threads/LB_atomic.wast",
498                "misc_testsuite/threads/MP.wast",
499                "misc_testsuite/threads/MP_atomic.wast",
500                "misc_testsuite/threads/MP_wait.wast",
501                "misc_testsuite/threads/SB.wast",
502                "misc_testsuite/threads/SB_atomic.wast",
503                "misc_testsuite/threads/atomics_notify.wast",
504                "misc_testsuite/threads/atomics_wait_address.wast",
505                "misc_testsuite/threads/wait_notify.wast",
506                "spec_testsuite/proposals/threads/atomic.wast",
507                "spec_testsuite/proposals/threads/exports.wast",
508                "spec_testsuite/proposals/threads/memory.wast",
509                "misc_testsuite/memory64/threads.wast",
510                "misc_testsuite/winch/rmw32_cmpxchg_u_wrap.wast",
511            ];
512
513            if unsupported.iter().any(|part| self.path.ends_with(part)) {
514                return true;
515            }
516        }
517
518        if config.compiler.should_fail(&self.config) {
519            return true;
520        }
521
522        // Disable spec tests per target for proposals that Winch does not implement yet.
523        if config.compiler == Compiler::Winch {
524            // Common list for tests that fail in all targets supported by Winch.
525            let unsupported = [
526                "extended-const/elem.wast",
527                "extended-const/global.wast",
528                "misc_testsuite/component-model/modules.wast",
529                "misc_testsuite/externref-id-function.wast",
530                "misc_testsuite/externref-segment.wast",
531                "misc_testsuite/externref-segments.wast",
532                "misc_testsuite/externref-table-dropped-segment-issue-8281.wast",
533                "misc_testsuite/linking-errors.wast",
534                "misc_testsuite/many_table_gets_lead_to_gc.wast",
535                "misc_testsuite/mutable_externref_globals.wast",
536                "misc_testsuite/no-mixup-stack-maps.wast",
537                "misc_testsuite/no-panic.wast",
538                "misc_testsuite/simple_ref_is_null.wast",
539            ];
540
541            if unsupported.iter().any(|part| self.path.ends_with(part)) {
542                return true;
543            }
544
545            #[cfg(target_arch = "aarch64")]
546            {
547                let unsupported = [
548                    "misc_testsuite/int-to-float-splat.wast",
549                    "misc_testsuite/issue6562.wast",
550                    "misc_testsuite/memory64/simd.wast",
551                    "misc_testsuite/simd/almost-extmul.wast",
552                    "misc_testsuite/simd/canonicalize-nan.wast",
553                    "misc_testsuite/simd/cvt-from-uint.wast",
554                    "misc_testsuite/simd/edge-of-memory.wast",
555                    "misc_testsuite/simd/interesting-float-splat.wast",
556                    "misc_testsuite/simd/issue4807.wast",
557                    "misc_testsuite/simd/issue6725-no-egraph-panic.wast",
558                    "misc_testsuite/simd/issue_3173_select_v128.wast",
559                    "misc_testsuite/simd/issue_3327_bnot_lowering.wast",
560                    "misc_testsuite/simd/load_splat_out_of_bounds.wast",
561                    "misc_testsuite/simd/replace-lane-preserve.wast",
562                    "misc_testsuite/simd/spillslot-size-fuzzbug.wast",
563                    "misc_testsuite/simd/sse-cannot-fold-unaligned-loads.wast",
564                    "misc_testsuite/simd/unaligned-load.wast",
565                    "misc_testsuite/simd/v128-select.wast",
566                    "misc_testsuite/winch/issue-10331.wast",
567                    "misc_testsuite/winch/issue-10357.wast",
568                    "misc_testsuite/winch/issue-10460.wast",
569                    "misc_testsuite/winch/replace_lane.wast",
570                    "misc_testsuite/winch/simd_multivalue.wast",
571                    "misc_testsuite/winch/v128_load_lane_invalid_address.wast",
572                    "spec_testsuite/proposals/annotations/simd_lane.wast",
573                    "spec_testsuite/proposals/multi-memory/simd_memory-multi.wast",
574                    "spec_testsuite/simd_address.wast",
575                    "spec_testsuite/simd_align.wast",
576                    "spec_testsuite/simd_bit_shift.wast",
577                    "spec_testsuite/simd_bitwise.wast",
578                    "spec_testsuite/simd_boolean.wast",
579                    "spec_testsuite/simd_const.wast",
580                    "spec_testsuite/simd_conversions.wast",
581                    "spec_testsuite/simd_f32x4.wast",
582                    "spec_testsuite/simd_f32x4_arith.wast",
583                    "spec_testsuite/simd_f32x4_cmp.wast",
584                    "spec_testsuite/simd_f32x4_pmin_pmax.wast",
585                    "spec_testsuite/simd_f32x4_rounding.wast",
586                    "spec_testsuite/simd_f64x2.wast",
587                    "spec_testsuite/simd_f64x2_arith.wast",
588                    "spec_testsuite/simd_f64x2_cmp.wast",
589                    "spec_testsuite/simd_f64x2_pmin_pmax.wast",
590                    "spec_testsuite/simd_f64x2_rounding.wast",
591                    "spec_testsuite/simd_i16x8_arith.wast",
592                    "spec_testsuite/simd_i16x8_arith2.wast",
593                    "spec_testsuite/simd_i16x8_cmp.wast",
594                    "spec_testsuite/simd_i16x8_extadd_pairwise_i8x16.wast",
595                    "spec_testsuite/simd_i16x8_extmul_i8x16.wast",
596                    "spec_testsuite/simd_i16x8_q15mulr_sat_s.wast",
597                    "spec_testsuite/simd_i16x8_sat_arith.wast",
598                    "spec_testsuite/simd_i32x4_arith.wast",
599                    "spec_testsuite/simd_i32x4_arith2.wast",
600                    "spec_testsuite/simd_i32x4_cmp.wast",
601                    "spec_testsuite/simd_i32x4_dot_i16x8.wast",
602                    "spec_testsuite/simd_i32x4_extadd_pairwise_i16x8.wast",
603                    "spec_testsuite/simd_i32x4_extmul_i16x8.wast",
604                    "spec_testsuite/simd_i32x4_trunc_sat_f32x4.wast",
605                    "spec_testsuite/simd_i32x4_trunc_sat_f64x2.wast",
606                    "spec_testsuite/simd_i64x2_arith.wast",
607                    "spec_testsuite/simd_i64x2_arith2.wast",
608                    "spec_testsuite/simd_i64x2_cmp.wast",
609                    "spec_testsuite/simd_i64x2_extmul_i32x4.wast",
610                    "spec_testsuite/simd_i8x16_arith.wast",
611                    "spec_testsuite/simd_i8x16_arith2.wast",
612                    "spec_testsuite/simd_i8x16_cmp.wast",
613                    "spec_testsuite/simd_i8x16_sat_arith.wast",
614                    "spec_testsuite/simd_int_to_int_extend.wast",
615                    "spec_testsuite/simd_lane.wast",
616                    "spec_testsuite/simd_load.wast",
617                    "spec_testsuite/simd_load16_lane.wast",
618                    "spec_testsuite/simd_load32_lane.wast",
619                    "spec_testsuite/simd_load64_lane.wast",
620                    "spec_testsuite/simd_load8_lane.wast",
621                    "spec_testsuite/simd_load_extend.wast",
622                    "spec_testsuite/simd_load_splat.wast",
623                    "spec_testsuite/simd_load_zero.wast",
624                    "spec_testsuite/simd_select.wast",
625                    "spec_testsuite/simd_splat.wast",
626                    "spec_testsuite/simd_store.wast",
627                    "spec_testsuite/simd_store16_lane.wast",
628                    "spec_testsuite/simd_store32_lane.wast",
629                    "spec_testsuite/simd_store64_lane.wast",
630                    "spec_testsuite/simd_store8_lane.wast",
631                ];
632
633                if unsupported.iter().any(|part| self.path.ends_with(part)) {
634                    return true;
635                }
636            }
637
638            #[cfg(target_arch = "x86_64")]
639            {
640                // SIMD on Winch requires AVX instructions.
641                #[cfg(target_arch = "x86_64")]
642                if !(std::is_x86_feature_detected!("avx") && std::is_x86_feature_detected!("avx2"))
643                {
644                    let unsupported = [
645                        "annotations/simd_lane.wast",
646                        "memory64/simd.wast",
647                        "misc_testsuite/int-to-float-splat.wast",
648                        "misc_testsuite/issue6562.wast",
649                        "misc_testsuite/simd/almost-extmul.wast",
650                        "misc_testsuite/simd/canonicalize-nan.wast",
651                        "misc_testsuite/simd/cvt-from-uint.wast",
652                        "misc_testsuite/simd/edge-of-memory.wast",
653                        "misc_testsuite/simd/issue_3327_bnot_lowering.wast",
654                        "misc_testsuite/simd/issue6725-no-egraph-panic.wast",
655                        "misc_testsuite/simd/replace-lane-preserve.wast",
656                        "misc_testsuite/simd/spillslot-size-fuzzbug.wast",
657                        "misc_testsuite/simd/sse-cannot-fold-unaligned-loads.wast",
658                        "misc_testsuite/winch/issue-10331.wast",
659                        "misc_testsuite/winch/replace_lane.wast",
660                        "misc_testsuite/simd/riscv64-replicated-imm5-works.wast",
661                        "misc_testsuite/simd/v128-equal.wast",
662                        "spec_testsuite/simd_align.wast",
663                        "spec_testsuite/simd_boolean.wast",
664                        "spec_testsuite/simd_conversions.wast",
665                        "spec_testsuite/simd_f32x4.wast",
666                        "spec_testsuite/simd_f32x4_arith.wast",
667                        "spec_testsuite/simd_f32x4_cmp.wast",
668                        "spec_testsuite/simd_f32x4_pmin_pmax.wast",
669                        "spec_testsuite/simd_f32x4_rounding.wast",
670                        "spec_testsuite/simd_f64x2.wast",
671                        "spec_testsuite/simd_f64x2_arith.wast",
672                        "spec_testsuite/simd_f64x2_cmp.wast",
673                        "spec_testsuite/simd_f64x2_pmin_pmax.wast",
674                        "spec_testsuite/simd_f64x2_rounding.wast",
675                        "spec_testsuite/simd_i16x8_cmp.wast",
676                        "spec_testsuite/simd_i32x4_cmp.wast",
677                        "spec_testsuite/simd_i64x2_arith2.wast",
678                        "spec_testsuite/simd_i64x2_cmp.wast",
679                        "spec_testsuite/simd_i8x16_arith2.wast",
680                        "spec_testsuite/simd_i8x16_cmp.wast",
681                        "spec_testsuite/simd_int_to_int_extend.wast",
682                        "spec_testsuite/simd_load.wast",
683                        "spec_testsuite/simd_load_extend.wast",
684                        "spec_testsuite/simd_load_splat.wast",
685                        "spec_testsuite/simd_load_zero.wast",
686                        "spec_testsuite/simd_splat.wast",
687                        "spec_testsuite/simd_store16_lane.wast",
688                        "spec_testsuite/simd_store32_lane.wast",
689                        "spec_testsuite/simd_store64_lane.wast",
690                        "spec_testsuite/simd_store8_lane.wast",
691                        "spec_testsuite/simd_load16_lane.wast",
692                        "spec_testsuite/simd_load32_lane.wast",
693                        "spec_testsuite/simd_load64_lane.wast",
694                        "spec_testsuite/simd_load8_lane.wast",
695                        "spec_testsuite/simd_bitwise.wast",
696                        "misc_testsuite/simd/load_splat_out_of_bounds.wast",
697                        "misc_testsuite/simd/unaligned-load.wast",
698                        "multi-memory/simd_memory-multi.wast",
699                        "misc_testsuite/simd/issue4807.wast",
700                        "spec_testsuite/simd_const.wast",
701                        "spec_testsuite/simd_i8x16_sat_arith.wast",
702                        "spec_testsuite/simd_i64x2_arith.wast",
703                        "spec_testsuite/simd_i16x8_arith.wast",
704                        "spec_testsuite/simd_i16x8_arith2.wast",
705                        "spec_testsuite/simd_i16x8_q15mulr_sat_s.wast",
706                        "spec_testsuite/simd_i16x8_sat_arith.wast",
707                        "spec_testsuite/simd_i32x4_arith.wast",
708                        "spec_testsuite/simd_i32x4_dot_i16x8.wast",
709                        "spec_testsuite/simd_i32x4_trunc_sat_f32x4.wast",
710                        "spec_testsuite/simd_i32x4_trunc_sat_f64x2.wast",
711                        "spec_testsuite/simd_i8x16_arith.wast",
712                        "spec_testsuite/simd_bit_shift.wast",
713                        "spec_testsuite/simd_lane.wast",
714                        "spec_testsuite/simd_i16x8_extmul_i8x16.wast",
715                        "spec_testsuite/simd_i32x4_extmul_i16x8.wast",
716                        "spec_testsuite/simd_i64x2_extmul_i32x4.wast",
717                        "spec_testsuite/simd_i16x8_extadd_pairwise_i8x16.wast",
718                        "spec_testsuite/simd_i32x4_extadd_pairwise_i16x8.wast",
719                        "spec_testsuite/simd_i32x4_arith2.wast",
720                    ];
721
722                    if unsupported.iter().any(|part| self.path.ends_with(part)) {
723                        return true;
724                    }
725                }
726            }
727        }
728
729        // Not implemented in Wasmtime anywhere yet.
730        if self.config.custom_descriptors() {
731            let happens_to_work =
732                ["spec_testsuite/proposals/custom-descriptors/binary-leb128.wast"];
733
734            if happens_to_work.iter().any(|part| self.path.ends_with(part)) {
735                return false;
736            }
737            return true;
738        }
739
740        false
741    }
742}
743
744fn spec_proposal_from_path(path: &Path) -> Option<&str> {
745    let mut iter = path.iter();
746    loop {
747        match iter.next()?.to_str()? {
748            "proposals" => break,
749            _ => {}
750        }
751    }
752    Some(iter.next()?.to_str()?)
753}