From 17f1889f3f4f70ecd6d72fa00739704a70395c15 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Thu, 24 Jan 2019 20:45:24 -0600 Subject: [PATCH] Produce better object file --- lib/obj/src/lib.rs | 2 + lib/obj/src/stubs.rs | 219 +++++++++++++++++++++++++++++++++++++++++++ src/wasm2obj.rs | 35 ++++++- 3 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 lib/obj/src/stubs.rs diff --git a/lib/obj/src/lib.rs b/lib/obj/src/lib.rs index f4d9dc2d00f5..fba807cab75b 100644 --- a/lib/obj/src/lib.rs +++ b/lib/obj/src/lib.rs @@ -30,9 +30,11 @@ mod context; mod data_segment; mod function; mod module; +mod stubs; mod table; pub use crate::module::emit_module; +pub use crate::stubs::generate_c_stubs; /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/obj/src/stubs.rs b/lib/obj/src/stubs.rs new file mode 100644 index 000000000000..6b9350a82e73 --- /dev/null +++ b/lib/obj/src/stubs.rs @@ -0,0 +1,219 @@ +use cranelift_codegen::ir; +use cranelift_codegen::isa::TargetFrontendConfig; +use cranelift_entity::EntityRef; +use cranelift_wasm::FuncIndex; +use std::io::{Result, Write}; +use wasmtime_environ::{DataInitializer, Module, VMOffsets}; + +/// Generates C code based on module metadata. +pub fn generate_c_stubs( + header: &mut Vec, + code: &mut Vec, + name: &str, + module: &Module, + data_initializers: &[DataInitializer], + target_config: &TargetFrontendConfig, +) -> Result<()> { + let ofs = VMOffsets::new(target_config.pointer_bytes(), &module); + + let uppercase_name = String::from(name).to_uppercase(); + writeln!(header, "#ifndef __{}_H", uppercase_name)?; + writeln!(header, "#define __{}_H", uppercase_name)?; + writeln!(header, "#include ")?; + writeln!(header, "#include ")?; + writeln!(header)?; + + if !module.memory_plans.is_empty() { + if module.memory_plans.len() > 1 { + panic!("multiple memories not supported yet"); + } + let (_, memory_plan) = module + .memory_plans + .iter() + .next() + .expect("at least one memory"); + let size = memory_plan.memory.minimum << 16; + writeln!(header, "#define MEMORY_INIT_SIZE {}", size)?; + } + + writeln!(header)?; + writeln!(header, "struct VMContext {{")?; + if ofs.num_signature_ids > 0 { + writeln!(header, "\tuint32_t\tsignatures[{}];", ofs.num_signature_ids)?; + } + if ofs.num_imported_functions > 0 { + writeln!(header, "\tstruct {{")?; + writeln!(header, "\t\tvoid\t*body;")?; + writeln!(header, "\t\tvoid\t*vmctx;")?; + writeln!( + header, + "\t}}\timported_functions[{}];", + ofs.num_imported_functions + )?; + } + if ofs.num_imported_tables > 0 { + writeln!(header, "\tstruct {{")?; + writeln!(header, "\t\tvoid\t*from;")?; + writeln!(header, "\t\tvoid\t*vmctx;")?; + writeln!( + header, + "\t}}\timported_tables[{}];", + ofs.num_imported_tables + )?; + } + if ofs.num_imported_memories > 0 { + writeln!(header, "\tstruct {{")?; + writeln!(header, "\t\tvoid\t*from;")?; + writeln!(header, "\t\tvoid\t*vmctx;")?; + writeln!( + header, + "\t}}\timported_memories[{}];", + ofs.num_imported_memories + )?; + } + if ofs.num_imported_globals > 0 { + writeln!( + header, + "\tvoid\t*imported_globals[{}];", + ofs.num_imported_globals + )?; + } + if ofs.num_defined_tables > 0 { + writeln!(header, "\tstruct {{")?; + writeln!(header, "\t\tvoid\t**base;")?; + writeln!(header, "\t\tsize_t\tcurrent_elements;")?; + writeln!(header, "\t}}\tdefined_tables[{}];", ofs.num_defined_tables)?; + } + if ofs.num_defined_memories > 0 { + writeln!(header, "\tstruct {{")?; + writeln!(header, "\t\tvoid\t*base;")?; + writeln!(header, "\t\tsize_t\tcurrent_length;")?; + writeln!( + header, + "\t}}\tdefined_memories[{}];", + ofs.num_defined_memories + )?; + } + if ofs.num_defined_globals > 0 { + writeln!(header, "\tstruct {{")?; + writeln!(header, "\t\tunion {{")?; + writeln!(header, "\t\t\tuint32_t\tu32;")?; + writeln!(header, "\t\t\tuint64_t\tu64;")?; + writeln!(header, "\t\t\tfloat\tf32;")?; + writeln!(header, "\t\t\tdouble\tf64;")?; + writeln!(header, "\t\t}};")?; + writeln!( + header, + "\t}}\tdefined_globals[{}];", + ofs.num_defined_globals + )?; + } + writeln!(header, "}};")?; + writeln!(header)?; + for i in 0..module.functions.len() { + if i < ofs.num_imported_functions as usize { + continue; + } + let signature = &module.signatures[module.functions[FuncIndex::from_u32(i as u32)]]; + let get_type = |x: &ir::AbiParam| -> &str { + match x.value_type { + ir::types::I32 => "uint32_t", + ir::types::I64 => "uint64_t", + _ => panic!("unsupported type"), + } + }; + let mut first = true; + for p in signature.returns.iter() { + if p.purpose != ir::ArgumentPurpose::Normal { + continue; + } + assert!(first); + first = false; + write!(header, "{}", get_type(p))?; + } + if first { + write!(header, "void")?; + } + write!(header, " _wasm_function_{}(", i)?; + let mut first = true; + for p in signature.params.iter() { + if first { + first = false; + } else { + write!(header, ", ")?; + } + match p.purpose { + ir::ArgumentPurpose::VMContext => { + write!(header, "struct VMContext*")?; + } + ir::ArgumentPurpose::Normal => { + write!(header, "{}", get_type(p))?; + } + _ => panic!("unsupported param type: {:?}", p.purpose), + } + } + writeln!(header, ");")? + } + if data_initializers.len() > 0 { + writeln!(header)?; + for i in 0..data_initializers.len() { + writeln!(header, "extern void *_memory_{};", i)?; + } + } + writeln!(header)?; + writeln!( + header, + "extern char _vmcontext_init[{}];", + ofs.size_of_vmctx() + )?; + writeln!(header, "void init_vm(struct VMContext *, void *, size_t);")?; + writeln!(header, "#endif // __{}_H", uppercase_name)?; + + writeln!(code, "#include \"{}.h\"", name)?; + writeln!(code)?; + writeln!(code, "#include ")?; + writeln!(code)?; + writeln!( + code, + "void init_vm(struct VMContext *vmctx, void *memory, size_t memory_len) {{" + )?; + writeln!( + code, + "\tmemcpy(vmctx, _vmcontext_init, sizeof(_vmcontext_init));" + )?; + if ofs.num_defined_memories > 0 { + writeln!(code, "\tvmctx->defined_memories[0].base = memory;")?; + writeln!( + code, + "\tvmctx->defined_memories[0].current_length = memory_len;" + )?; + } + + if data_initializers.len() > 0 { + writeln!(code)?; + for i in 0..data_initializers.len() { + let data_initializer = &data_initializers[i]; + let offset = { + let addend = data_initializer.location.offset; + if let Some(global_index) = data_initializer.location.base { + format!( + "vmctx->defined_globals[{}].u32 + {}", + global_index.index(), + addend + ) + } else { + format!("{}", addend) + } + }; + let len = data_initializer.data.len(); + writeln!( + code, + "\tmemcpy(memory + {}, _memory_{}, {});", + offset, i, len + )?; + } + } + + writeln!(code, "}};")?; + Ok(()) +} diff --git a/src/wasm2obj.rs b/src/wasm2obj.rs index c68ddb50b767..e22d29de5d73 100644 --- a/src/wasm2obj.rs +++ b/src/wasm2obj.rs @@ -50,7 +50,7 @@ use std::str::FromStr; use target_lexicon::Triple; use wasmtime_debug::{emit_debugsections, read_debuginfo}; use wasmtime_environ::{cranelift, ModuleEnvironment, Tunables}; -use wasmtime_obj::emit_module; +use wasmtime_obj::{emit_module, generate_c_stubs}; const USAGE: &str = " Wasm to native object translation utility. @@ -59,7 +59,7 @@ The translation is dependent on the environment chosen. The default is a dummy environment that produces placeholder values. Usage: - wasm2obj [--target TARGET] [-g] -o + wasm2obj [--target TARGET] [-g] [--generate-c] -o wasm2obj --help | --version Options: @@ -67,6 +67,7 @@ Options: -h, --help print this help message --target build for the target triple; default is the host machine -g generate debug information + -c, --generate-c generate C code/wrapper --version print the Cranelift version "; @@ -76,6 +77,7 @@ struct Args { arg_output: String, arg_target: Option, flag_g: bool, + flag_generate_c: bool, } fn read_wasm_file(path: PathBuf) -> Result, io::Error> { @@ -100,6 +102,7 @@ fn main() { &args.arg_target, &args.arg_output, args.flag_g, + args.flag_generate_c, ) { Ok(()) => {} Err(message) => { @@ -114,6 +117,7 @@ fn handle_module( target: &Option, output: &str, generate_debug_info: bool, + generate_c: bool, ) -> Result<(), String> { let data = match read_wasm_file(path) { Ok(data) => data, @@ -187,5 +191,32 @@ fn handle_module( ::std::fs::File::create(Path::new(output)).map_err(|x| format(format_args!("{}", x)))?; obj.write(file).map_err(|e| e.to_string())?; + if generate_c { + let base_name = Path::new(output) + .file_stem() + .expect("filename") + .to_str() + .expect("name"); + let mut c_header = Vec::new(); + let mut c_code = Vec::new(); + generate_c_stubs( + &mut c_header, + &mut c_code, + &base_name, + &module, + &lazy_data_initializers, + &target_config, + ) + .map_err(|e| e.to_string())?; + File::create(Path::new(output).with_extension("h")) + .map_err(|x| format(format_args!("{}", x)))? + .write_all(&c_header) + .map_err(|e| e.to_string())?; + File::create(Path::new(output).with_extension("c")) + .map_err(|x| format(format_args!("{}", x)))? + .write_all(&c_code) + .map_err(|e| e.to_string())?; + } + Ok(()) }