This also paves the way for unifying TargetIsa and MachBackend, since now they map one to one. In theory the two traits could be merged, which would be nice to limit the number of total concepts. Also they have quite different responsibilities, so it might be fine to keep them separate. Interestingly, this PR started as removing RegInfo from the TargetIsa trait since the adapter returned a dummy value there. From the fallout, noticed that all Display implementations didn't needed an ISA anymore (since these were only used to render ISA specific registers). Also the whole family of RegInfo / ValueLoc / RegUnit was exclusively used for the old backend, and these could be removed. Notably, some IR instructions needed to be removed, because they were using RegUnit too: this was the oddball of regfill / regmove / regspill / copy_special, which were IR instructions inserted by the old regalloc. Fare thee well!
192 lines
5.5 KiB
Rust
192 lines
5.5 KiB
Rust
use cranelift_codegen::isa::{CallConv, TargetFrontendConfig};
|
|
use cranelift_codegen::print_errors::pretty_verifier_error;
|
|
use cranelift_codegen::settings::{self, Flags};
|
|
use cranelift_codegen::verifier;
|
|
use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode};
|
|
use std::fs;
|
|
use std::path::Path;
|
|
use target_lexicon::PointerWidth;
|
|
|
|
#[test]
|
|
fn testsuite() {
|
|
let mut paths: Vec<_> = fs::read_dir("./wasmtests")
|
|
.unwrap()
|
|
.map(|r| r.unwrap())
|
|
.filter(|p| {
|
|
// Ignore files starting with `.`, which could be editor temporary files
|
|
if let Some(stem) = p.path().file_stem() {
|
|
if let Some(stemstr) = stem.to_str() {
|
|
return !stemstr.starts_with('.');
|
|
}
|
|
}
|
|
false
|
|
})
|
|
.collect();
|
|
paths.sort_by_key(|dir| dir.path());
|
|
let flags = Flags::new(settings::builder());
|
|
for path in paths {
|
|
let path = path.path();
|
|
println!("=== {} ===", path.display());
|
|
let data = read_module(&path);
|
|
handle_module(data, &flags, ReturnMode::NormalReturns);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn use_fallthrough_return() {
|
|
let flags = Flags::new(settings::builder());
|
|
let path = Path::new("./wasmtests/use_fallthrough_return.wat");
|
|
let data = read_module(&path);
|
|
handle_module(data, &flags, ReturnMode::FallthroughReturn);
|
|
}
|
|
|
|
#[test]
|
|
fn use_name_section() {
|
|
let data = wat::parse_str(
|
|
r#"
|
|
(module $module_name
|
|
(func $func_name (local $loc_name i32)
|
|
)
|
|
)"#,
|
|
)
|
|
.unwrap();
|
|
|
|
let return_mode = ReturnMode::NormalReturns;
|
|
let mut dummy_environ = DummyEnvironment::new(
|
|
TargetFrontendConfig {
|
|
default_call_conv: CallConv::SystemV,
|
|
pointer_width: PointerWidth::U32,
|
|
},
|
|
return_mode,
|
|
false,
|
|
);
|
|
|
|
translate_module(data.as_ref(), &mut dummy_environ).unwrap();
|
|
|
|
assert_eq!(
|
|
dummy_environ.get_func_name(FuncIndex::from_u32(0)).unwrap(),
|
|
"func_name"
|
|
);
|
|
}
|
|
|
|
fn read_module(path: &Path) -> Vec<u8> {
|
|
match path.extension() {
|
|
None => {
|
|
panic!("the file extension is not wasm or wat");
|
|
}
|
|
Some(ext) => match ext.to_str() {
|
|
Some("wasm") => std::fs::read(path).expect("error reading wasm file"),
|
|
Some("wat") => wat::parse_file(path)
|
|
.map_err(|e| e.to_string())
|
|
.expect("failed to parse wat"),
|
|
None | Some(&_) => panic!("the file extension for {:?} is not wasm or wat", path),
|
|
},
|
|
}
|
|
}
|
|
|
|
fn handle_module(data: Vec<u8>, flags: &Flags, return_mode: ReturnMode) {
|
|
let mut dummy_environ = DummyEnvironment::new(
|
|
TargetFrontendConfig {
|
|
default_call_conv: CallConv::SystemV,
|
|
pointer_width: PointerWidth::U64,
|
|
},
|
|
return_mode,
|
|
false,
|
|
);
|
|
|
|
translate_module(&data, &mut dummy_environ).unwrap();
|
|
|
|
for func in dummy_environ.info.function_bodies.values() {
|
|
verifier::verify_function(func, flags)
|
|
.map_err(|errors| panic!("{}", pretty_verifier_error(func, None, errors)))
|
|
.unwrap();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn reachability_is_correct() {
|
|
let tests = vec![
|
|
(
|
|
ReturnMode::NormalReturns,
|
|
r#"
|
|
(module (func (param i32)
|
|
(loop
|
|
(block
|
|
local.get 0
|
|
br_if 0
|
|
br 1))))"#,
|
|
vec![
|
|
(true, true), // Loop
|
|
(true, true), // Block
|
|
(true, true), // LocalGet
|
|
(true, true), // BrIf
|
|
(true, false), // Br
|
|
(false, true), // End
|
|
(true, true), // End
|
|
(true, true), // End
|
|
],
|
|
),
|
|
(
|
|
ReturnMode::NormalReturns,
|
|
r#"
|
|
(module (func (param i32)
|
|
(loop
|
|
(block
|
|
br 1
|
|
nop))))"#,
|
|
vec![
|
|
(true, true), // Loop
|
|
(true, true), // Block
|
|
(true, false), // Br
|
|
(false, false), // Nop
|
|
(false, false), // Nop
|
|
(false, false), // Nop
|
|
(false, false), // End
|
|
],
|
|
),
|
|
(
|
|
ReturnMode::NormalReturns,
|
|
r#"
|
|
(module (func (param i32) (result i32)
|
|
i32.const 1
|
|
return
|
|
i32.const 42))"#,
|
|
vec![
|
|
(true, true), // I32Const
|
|
(true, false), // Return
|
|
(false, false), // I32Const
|
|
(false, false), // End
|
|
],
|
|
),
|
|
(
|
|
ReturnMode::FallthroughReturn,
|
|
r#"
|
|
(module (func (param i32) (result i32)
|
|
i32.const 1
|
|
return
|
|
i32.const 42))"#,
|
|
vec![
|
|
(true, true), // I32Const
|
|
(true, false), // Return
|
|
(false, false), // I32Const
|
|
(false, true), // End
|
|
],
|
|
),
|
|
];
|
|
|
|
for (return_mode, wat, expected_reachability) in tests {
|
|
println!("testing wat:\n{}", wat);
|
|
let mut env = DummyEnvironment::new(
|
|
TargetFrontendConfig {
|
|
default_call_conv: CallConv::SystemV,
|
|
pointer_width: PointerWidth::U64,
|
|
},
|
|
return_mode,
|
|
false,
|
|
);
|
|
env.test_expected_reachability(expected_reachability);
|
|
let data = wat::parse_str(wat).unwrap();
|
|
translate_module(data.as_ref(), &mut env).unwrap();
|
|
}
|
|
}
|