Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exec spectest #38

Open
wants to merge 3 commits into
base: update_testsuite
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "tests/testsuite"]
path = tests/testsuite
path = testsuite
url = [email protected]:WebAssembly/testsuite.git
[submodule "testsuite"]
path = testsuite
url = [email protected]:WebAssembly/testsuite.git
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ log = "0.4.20"
env_logger = "0.10.0"
pretty-hex = "0.3.0"
wast = "70.0.2"
wasi = "0.11.0+wasi-snapshot-preview1"
3 changes: 3 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("cargo:rustc-link-search=native=target/spectest");
}
3 changes: 1 addition & 2 deletions examples/wasi-wrapper/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ version = "0.1.0"
edition = "2021"
build = "build.rs"

[dependencies]
wasi = "0.11.0+wasi-snapshot-preview1"
[dependencies]
3 changes: 0 additions & 3 deletions examples/wasi-wrapper/rust/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
mod memory;
mod wasi;

extern "C" {
fn wasker_main();
}
Expand Down
46 changes: 0 additions & 46 deletions examples/wasi-wrapper/rust/src/memory.rs

This file was deleted.

201 changes: 196 additions & 5 deletions src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,32 @@
use crate::environment::Environment;
use crate::inkwell::init_inkwell;
use crate::section::translate_module;
use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, Context, Ok, Result};
use clap::Parser;
use inkwell::{context, module::Module, passes::PassManager, targets};
use std::path;
use std::fmt::format;
use std::io::{Read, Write};
use std::path::PathBuf;
use wast::{
core::WastArgCore, core::WastRetCore, lexer::Lexer, parser::ParseBuffer, Wast, WastDirective,
};
use wat;

/// Ahead-of-Time Wasm to ELF compiler.
#[derive(Parser, Debug)]
pub struct Args {
pub input_file: path::PathBuf,
/// Path to the input Wasm or WAT file.
#[arg(short, long, default_value = "./hello.wat")]
pub input_file: PathBuf,

/// Path to the output ELF file.
#[arg(short, long, default_value = "./wasm.o")]
pub output_file: path::PathBuf,
pub output_file: PathBuf,

/// SpecTest mode
/// If this flag is set, the compiler will output ELF file from spectest's *.wast file.
#[arg(short, long, default_value = "false")]
pub spectest: bool,
}

/// Receive a path to a Wasm binary or WAT and compile it into ELF binary.
Expand All @@ -31,6 +45,183 @@ pub fn compile_wasm_from_file(args: &Args) -> Result<()> {
compile_wasm(&wasm, args)
}

fn compile_spec_test(testname: &str, idx: usize) {
let project_root = std::env::var("CARGO_MANIFEST_DIR").expect("error get env");

// Read Wast
let mut wat = String::new();
let wat_path = std::path::Path::new(&project_root).join(format!("testsuite/{}.wast", testname));
log::info!("open file: {:?}", wat_path);
let mut file = std::fs::File::open(wat_path).expect("error open file");
file.read_to_string(&mut wat).expect("cannot read file");

// Parse Wast
let mut lexer = Lexer::new(&wat);
lexer.allow_confusing_unicode(true);
let parse_buffer = match ParseBuffer::new_with_lexer(lexer) {
core::result::Result::Ok(buffer) => buffer,
Err(error) => {
panic!("failed to create ParseBuffer : {}", error)
}
};
let wast = match wast::parser::parse::<Wast>(&parse_buffer) {
core::result::Result::Ok(wast) => wast,
Err(error) => {
panic!(
"failed to parse `.wast` spec test file {} for: {}",
testname, error
)
}
};

// Compile Wast
let target_dir = std::path::Path::new(&project_root).join(format!("target/spectest"));
if !target_dir.exists() {
std::fs::create_dir_all(&target_dir).expect("error create target dir");
}

let mut buff_externc = if idx == 0 {
String::from("#[link(name = \"spectest\")]\nextern \"C\" {\n")
} else {
String::new()
};
let mut buff_test = String::from(format!("#[test]\nfn test_{}()", testname));
buff_test.push_str("{\n");

for directive in wast.directives {
match directive {
WastDirective::Wat(mut wat) => {
// Get Wasm binary
let wasm = wat.encode().expect("failed to encode wat");
assert_eq!(wasm[0..4], [0, 97, 115, 109]);

// Compile with Wasker
let args = Args {
input_file: "dummy.wasm".into(),
output_file: format!("{}/{}.o", target_dir.display(), testname).into(),
spectest: false,
};
compile_wasm(&wasm, &args).expect("failed to Wasker compile");
}
WastDirective::AssertReturn {
span: _,
exec,
results,
} => match exec {
wast::WastExecute::Invoke(wast::WastInvoke {
span: _,
module: _,
name,
args,
}) => {
let mut test_strings = String::from("\tassert_eq!(");
if results.len() > 1 {
log::error!("TODO: results.len() > 1");
assert!(false);
}
match &results[0] {
wast::WastRet::Core(ret) => match ret {
WastRetCore::I32(i) => {
test_strings.push_str(&format!("{},", i));
}
WastRetCore::I64(i) => {
test_strings.push_str(&format!("{},", i));
}
WastRetCore::F32(f) => {
test_strings.push_str(&format!("{:?},", f));
}
WastRetCore::F64(f) => {
test_strings.push_str(&format!("{:?},", f));
}
_ => {}
},
_ => {
log::error!("TODO: results[0] is not Core");
assert!(false);
}
}
test_strings.push_str(format!("{}(", name).as_str());
for arg in args {
match arg {
wast::WastArg::Core(argcore) => match argcore {
WastArgCore::I32(i) => {
test_strings.push_str(&format!("{},", i));
}
WastArgCore::I64(i) => {
test_strings.push_str(&format!("{},", i));
}
WastArgCore::F32(f) => {
test_strings.push_str(&format!("{:?},", f));
}
WastArgCore::F64(f) => {
test_strings.push_str(&format!("{:?},", f));
}
_ => {}
},
_ => {}
}
}
// remove last comma
test_strings.pop();
test_strings.push_str("));\n");
buff_test.push_str(test_strings.as_str());
}

_ => {}
},
WastDirective::AssertInvalid {
span: _,
module: _,
message: _,
} => {}
WastDirective::AssertTrap {
span: _,
exec: _,
message: _,
} => {}
WastDirective::AssertMalformed {
span: _,
module: _,
message: _,
} => {}
_other => {}
}
}

buff_externc.push_str("\n}\n");
buff_test.push_str("}\n");
let rs_path = target_dir.join(format!("{}/target/spectest/spectest.rs", project_root));

if idx == 0 {
// remove file if exists
if rs_path.exists() {
std::fs::remove_file(&rs_path).expect("error remove file");
}

// write to new file
let buff = format!("{}{}", buff_externc, buff_test);
std::fs::write(&rs_path, &buff).expect("error write file");
} else {
let f = std::fs::OpenOptions::new()
.write(true)
.append(true)
.open(&rs_path)
.expect("error open file");
let mut bw = std::io::BufWriter::new(f);
bw.write_all(buff_test.as_bytes())
.expect("error write file");
}
}

pub const SPECTESTS: [&str; 2] = ["i32", "i64"];

pub fn compile_wast_from_spectest() -> Result<()> {
for (i, testname) in SPECTESTS.iter().enumerate() {
compile_spec_test(testname, i);
}
Ok(())
}

/// Receive a Wasm binary and compile it into ELF binary.
pub fn compile_wasm(wasm: &[u8], args: &Args) -> Result<()> {
// Prepare inkwell (Rust-wrapper of LLVM) instances
Expand Down Expand Up @@ -88,7 +279,7 @@ pub fn compile_wasm(wasm: &[u8], args: &Args) -> Result<()> {
}

fn output_elf(environment: Environment) -> Result<()> {
let obj_path = path::Path::new(environment.output_file);
let obj_path = PathBuf::from(environment.output_file);
let ll_path = obj_path.with_extension("ll");

log::info!("write to {}", ll_path.display());
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ pub mod environment;
pub mod inkwell;
pub mod insts;
pub mod section;
pub mod wasi_wrapper;
9 changes: 8 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ fn main() -> Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let args = compiler::Args::parse();

// If spectest mode is enabled, compile Wasm from spectest's *.wast file.
if args.spectest {
log::info!("SpecTest mode is enabled.");
compiler::compile_wast_from_spectest().expect("Failed to compile spectest");
return Ok(());
}

// Compile Wasm and output ELF
compiler::compile_wasm_from_file(&args)?;
compiler::compile_wasm_from_file(&args).expect("Failed to compile input Wasm");

Ok(())
}
Loading
Loading