diff --git a/Cargo.lock b/Cargo.lock index 3212364f7c..7c4f4fb106 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2513,6 +2513,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", + "log", "more-asserts", "region", "target-lexicon", diff --git a/crates/api/src/trampoline/func.rs b/crates/api/src/trampoline/func.rs index 76d29d2e8d..2e15c6b741 100644 --- a/crates/api/src/trampoline/func.rs +++ b/crates/api/src/trampoline/func.rs @@ -289,13 +289,14 @@ pub fn create_handle_with_function( // ... and then we also need a trampoline with the standard "trampoline ABI" // which enters into the ABI specified by `ft`. Note that this is only used // if `Func::call` is called on an object created by `Func::new`. - let trampoline = wasmtime_jit::make_trampoline( + let (trampoline, relocations) = wasmtime_jit::make_trampoline( &*isa, &mut code_memory, &mut fn_builder_ctx, &sig, mem::size_of::(), )?; + assert!(relocations.is_empty()); let sig_id = store.compiler().signatures().register(&sig); trampolines.insert(sig_id, trampoline); diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml index fa0614eb57..f90142c8cc 100644 --- a/crates/jit/Cargo.toml +++ b/crates/jit/Cargo.toml @@ -27,6 +27,7 @@ wasmparser = "0.51.2" more-asserts = "0.2.1" anyhow = "1.0" cfg-if = "0.1.9" +log = "0.4" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3.7", features = ["winnt", "impl-default"] } diff --git a/crates/jit/src/compiler.rs b/crates/jit/src/compiler.rs index 7bfdbb17d3..0329320a76 100644 --- a/crates/jit/src/compiler.rs +++ b/crates/jit/src/compiler.rs @@ -3,6 +3,7 @@ use crate::code_memory::CodeMemory; use crate::instantiate::SetupError; use crate::target_tunables::target_tunables; +use cranelift_codegen::ir::ExternalName; use cranelift_codegen::ir::InstBuilder; use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::Context; @@ -15,10 +16,11 @@ use wasmtime_debug::{emit_debugsections_image, DebugInfoData}; use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa}; use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex}; +use wasmtime_environ::RelocationTarget; use wasmtime_environ::{ - CacheConfig, Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, - Compiler as _C, FunctionBodyData, Module, ModuleMemoryOffset, ModuleVmctxInfo, Relocations, - Traps, Tunables, VMOffsets, + CacheConfig, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Compiler as _C, + FunctionBodyData, Module, ModuleMemoryOffset, ModuleVmctxInfo, Relocation, Relocations, Traps, + Tunables, VMOffsets, }; use wasmtime_profiling::ProfilingAgent; use wasmtime_runtime::{ @@ -76,6 +78,17 @@ impl Compiler { } } +#[allow(missing_docs)] +pub struct Compilation { + pub finished_functions: PrimaryMap, + pub relocations: Relocations, + pub trampolines: HashMap, + pub trampoline_relocations: HashMap>, + pub jt_offsets: PrimaryMap, + pub dbg_image: Option>, + pub trap_registration: TrapRegistration, +} + impl Compiler { /// Return the target's frontend configuration settings. pub fn frontend_config(&self) -> TargetFrontendConfig { @@ -94,17 +107,7 @@ impl Compiler { module_translation: &ModuleTranslationState, function_body_inputs: PrimaryMap>, debug_data: Option, - ) -> Result< - ( - PrimaryMap, - HashMap, - PrimaryMap, - Relocations, - Option>, - TrapRegistration, - ), - SetupError, - > { + ) -> Result { let ( compilation, relocations, @@ -142,7 +145,7 @@ impl Compiler { // Allocate all of the compiled functions into executable memory, // copying over their contents. - let allocated_functions = + let finished_functions = allocate_functions(&mut self.code_memory, &compilation).map_err(|message| { SetupError::Instantiate(InstantiationError::Resource(format!( "failed to allocate memory for functions: {}", @@ -153,7 +156,7 @@ impl Compiler { // Create a registration value for all traps in our allocated // functions. This registration will allow us to map a trapping PC // value to what the trap actually means if it came from JIT code. - let trap_registration = register_traps(&allocated_functions, &traps, &self.trap_registry); + let trap_registration = register_traps(&finished_functions, &traps, &self.trap_registry); // Eagerly generate a entry trampoline for every type signature in the // module. This should be "relatively lightweight" for most modules and @@ -161,38 +164,37 @@ impl Compiler { // tables) have a trampoline when invoked through the wasmtime API. let mut cx = FunctionBuilderContext::new(); let mut trampolines = HashMap::new(); + let mut trampoline_relocations = HashMap::new(); for sig in module.local.signatures.values() { let index = self.signatures.register(sig); if trampolines.contains_key(&index) { continue; } - // FIXME(#1322) we should be generating a trampoline for all - // functions in a module, not just those with less than 40 - // arguments. Currently there is no relocation support for - // trampoline compilation; when that is added this check can - // go away. - if sig.params.len() > 40 { - continue; + let (trampoline, relocations) = make_trampoline( + &*self.isa, + &mut self.code_memory, + &mut cx, + sig, + std::mem::size_of::(), + )?; + trampolines.insert(index, trampoline); + + // Typically trampolines do not have relocations, so if one does + // show up be sure to log it in case anyone's listening and there's + // an accidental bug. + if relocations.len() > 0 { + log::info!("relocations found in trampoline for {:?}", sig); + trampoline_relocations.insert(index, relocations); } - trampolines.insert( - index, - make_trampoline( - &*self.isa, - &mut self.code_memory, - &mut cx, - sig, - std::mem::size_of::(), - )?, - ); } // Translate debug info (DWARF) only if at least one function is present. - let dbg = if debug_data.is_some() && !allocated_functions.is_empty() { + let dbg_image = if debug_data.is_some() && !finished_functions.is_empty() { let target_config = self.isa.frontend_config(); let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local); let mut funcs = Vec::new(); - for (i, allocated) in allocated_functions.into_iter() { + for (i, allocated) in finished_functions.into_iter() { let ptr = (*allocated) as *const u8; let body_len = compilation.get(i).body.len(); funcs.push((ptr, body_len)); @@ -228,14 +230,15 @@ impl Compiler { let jt_offsets = compilation.get_jt_offsets(); - Ok(( - allocated_functions, - trampolines, - jt_offsets, + Ok(Compilation { + finished_functions, relocations, - dbg, + trampolines, + trampoline_relocations, + jt_offsets, + dbg_image, trap_registration, - )) + }) } /// Make memory containing compiled code executable. @@ -271,7 +274,7 @@ pub fn make_trampoline( fn_builder_ctx: &mut FunctionBuilderContext, signature: &ir::Signature, value_size: usize, -) -> Result { +) -> Result<(VMTrampoline, Vec), SetupError> { let pointer_type = isa.pointer_type(); let mut wrapper_sig = ir::Signature::new(isa.frontend_config().default_call_conv); @@ -352,7 +355,7 @@ pub fn make_trampoline( } let mut code_buf = Vec::new(); - let mut reloc_sink = RelocSink {}; + let mut reloc_sink = RelocSink::default(); let mut trap_sink = binemit::NullTrapSink {}; let mut stackmap_sink = binemit::NullStackmapSink {}; context @@ -381,12 +384,15 @@ pub fn make_trampoline( }) .map_err(|message| SetupError::Instantiate(InstantiationError::Resource(message)))? .as_ptr(); - Ok(unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) }) + Ok(( + unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) }, + reloc_sink.relocs, + )) } fn allocate_functions( code_memory: &mut CodeMemory, - compilation: &Compilation, + compilation: &wasmtime_environ::Compilation, ) -> Result, String> { let fat_ptrs = code_memory.allocate_for_compilation(compilation)?; @@ -419,9 +425,13 @@ fn register_traps( registry.register_traps(traps) } -/// We don't expect trampoline compilation to produce any relocations, so -/// this `RelocSink` just asserts that it doesn't recieve any. -struct RelocSink {} +/// We don't expect trampoline compilation to produce many relocations, so +/// this `RelocSink` just asserts that it doesn't recieve most of them, but +/// handles libcall ones. +#[derive(Default)] +struct RelocSink { + relocs: Vec, +} impl binemit::RelocSink for RelocSink { fn reloc_block( @@ -434,12 +444,22 @@ impl binemit::RelocSink for RelocSink { } fn reloc_external( &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _name: &ir::ExternalName, - _addend: binemit::Addend, + offset: binemit::CodeOffset, + reloc: binemit::Reloc, + name: &ir::ExternalName, + addend: binemit::Addend, ) { - panic!("trampoline compilation should not produce external symbol relocs"); + let reloc_target = if let ExternalName::LibCall(libcall) = *name { + RelocationTarget::LibCall(libcall) + } else { + panic!("unrecognized external name") + }; + self.relocs.push(Relocation { + reloc, + reloc_target, + offset, + addend, + }); } fn reloc_constant( &mut self, diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index aa6d80438a..9fbef727b7 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -80,26 +80,14 @@ impl<'data> RawCompiledModule<'data> { None }; - let ( - finished_functions, - trampolines, - jt_offsets, - relocations, - dbg_image, - trap_registration, - ) = compiler.compile( + let compilation = compiler.compile( &translation.module, translation.module_translation.as_ref().unwrap(), translation.function_body_inputs, debug_data, )?; - link_module( - &translation.module, - &finished_functions, - &jt_offsets, - relocations, - ); + link_module(&translation.module, &compilation); // Compute indices into the shared signature table. let signatures = { @@ -121,7 +109,7 @@ impl<'data> RawCompiledModule<'data> { Some(_) => { let region_name = String::from("wasm_module"); let mut profiler = profiler.unwrap().lock().unwrap(); - match &dbg_image { + match &compilation.dbg_image { Some(dbg) => { compiler.profiler_module_load(&mut profiler, ®ion_name, Some(&dbg)) } @@ -131,7 +119,7 @@ impl<'data> RawCompiledModule<'data> { _ => (), }; - let dbg_jit_registration = if let Some(img) = dbg_image { + let dbg_jit_registration = if let Some(img) = compilation.dbg_image { let mut bytes = Vec::new(); bytes.write_all(&img).expect("all written"); let reg = GdbJitImageRegistration::register(bytes); @@ -142,12 +130,12 @@ impl<'data> RawCompiledModule<'data> { Ok(Self { module: translation.module, - finished_functions: finished_functions.into_boxed_slice(), - trampolines, + finished_functions: compilation.finished_functions.into_boxed_slice(), + trampolines: compilation.trampolines, data_initializers: translation.data_initializers.into_boxed_slice(), signatures: signatures.into_boxed_slice(), dbg_jit_registration, - trap_registration, + trap_registration: compilation.trap_registration, }) } } diff --git a/crates/jit/src/lib.rs b/crates/jit/src/lib.rs index 4f672cfbd0..c524b31a09 100644 --- a/crates/jit/src/lib.rs +++ b/crates/jit/src/lib.rs @@ -34,7 +34,7 @@ pub mod native; pub mod trampoline; pub use crate::code_memory::CodeMemory; -pub use crate::compiler::{make_trampoline, CompilationStrategy, Compiler}; +pub use crate::compiler::{make_trampoline, Compilation, CompilationStrategy, Compiler}; pub use crate::instantiate::{instantiate, CompiledModule, SetupError}; pub use crate::link::link_module; pub use crate::resolver::{NullResolver, Resolver}; diff --git a/crates/jit/src/link.rs b/crates/jit/src/link.rs index fcd715589a..c8313b5d86 100644 --- a/crates/jit/src/link.rs +++ b/crates/jit/src/link.rs @@ -1,96 +1,107 @@ //! Linking for JIT-compiled code. +use crate::Compilation; use cranelift_codegen::binemit::Reloc; -use cranelift_codegen::ir::JumpTableOffsets; use std::ptr::write_unaligned; -use wasmtime_environ::entity::PrimaryMap; -use wasmtime_environ::wasm::DefinedFuncIndex; -use wasmtime_environ::{Module, RelocationTarget, Relocations}; +use wasmtime_environ::{Module, Relocation, RelocationTarget}; use wasmtime_runtime::libcalls; use wasmtime_runtime::VMFunctionBody; /// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`. /// /// Performs all required relocations inside the function code, provided the necessary metadata. -pub fn link_module( - module: &Module, - allocated_functions: &PrimaryMap, - jt_offsets: &PrimaryMap, - relocations: Relocations, -) { - for (i, function_relocs) in relocations.into_iter() { - for r in function_relocs { - use self::libcalls::*; - let target_func_address: usize = match r.reloc_target { - RelocationTarget::UserFunc(index) => match module.local.defined_func_index(index) { - Some(f) => { - let fatptr: *const [VMFunctionBody] = allocated_functions[f]; - fatptr as *const VMFunctionBody as usize - } - None => panic!("direct call to import"), - }, - RelocationTarget::LibCall(libcall) => { - use cranelift_codegen::ir::LibCall::*; - match libcall { - CeilF32 => wasmtime_f32_ceil as usize, - FloorF32 => wasmtime_f32_floor as usize, - TruncF32 => wasmtime_f32_trunc as usize, - NearestF32 => wasmtime_f32_nearest as usize, - CeilF64 => wasmtime_f64_ceil as usize, - FloorF64 => wasmtime_f64_floor as usize, - TruncF64 => wasmtime_f64_trunc as usize, - NearestF64 => wasmtime_f64_nearest as usize, - Probestack => PROBESTACK as usize, - other => panic!("unexpected libcall: {}", other), - } - } - RelocationTarget::JumpTable(func_index, jt) => { - match module.local.defined_func_index(func_index) { - Some(f) => { - let offset = *jt_offsets - .get(f) - .and_then(|ofs| ofs.get(jt)) - .expect("func jump table"); - let fatptr: *const [VMFunctionBody] = allocated_functions[f]; - fatptr as *const VMFunctionBody as usize + offset as usize - } - None => panic!("func index of jump table"), - } - } - }; - - let fatptr: *const [VMFunctionBody] = allocated_functions[i]; +pub fn link_module(module: &Module, compilation: &Compilation) { + for (i, function_relocs) in compilation.relocations.iter() { + for r in function_relocs.iter() { + let fatptr: *const [VMFunctionBody] = compilation.finished_functions[i]; let body = fatptr as *const VMFunctionBody; - match r.reloc { - #[cfg(target_pointer_width = "64")] - Reloc::Abs8 => unsafe { - let reloc_address = body.add(r.offset as usize) as usize; - let reloc_addend = r.addend as isize; - let reloc_abs = (target_func_address as u64) - .checked_add(reloc_addend as u64) - .unwrap(); - write_unaligned(reloc_address as *mut u64, reloc_abs); - }, - #[cfg(target_pointer_width = "32")] - Reloc::X86PCRel4 => unsafe { - let reloc_address = body.add(r.offset as usize) as usize; - let reloc_addend = r.addend as isize; - let reloc_delta_u32 = (target_func_address as u32) - .wrapping_sub(reloc_address as u32) - .checked_add(reloc_addend as u32) - .unwrap(); - write_unaligned(reloc_address as *mut u32, reloc_delta_u32); - }, - #[cfg(target_pointer_width = "32")] - Reloc::X86CallPCRel4 => { - // ignore - } - Reloc::X86PCRelRodata4 => { - // ignore - } - _ => panic!("unsupported reloc kind"), + apply_reloc(module, compilation, body, r); + } + } + + for (i, function_relocs) in compilation.trampoline_relocations.iter() { + for r in function_relocs.iter() { + println!("tramopline relocation"); + let body = compilation.trampolines[&i] as *const VMFunctionBody; + apply_reloc(module, compilation, body, r); + } + } +} + +fn apply_reloc( + module: &Module, + compilation: &Compilation, + body: *const VMFunctionBody, + r: &Relocation, +) { + use self::libcalls::*; + let target_func_address: usize = match r.reloc_target { + RelocationTarget::UserFunc(index) => match module.local.defined_func_index(index) { + Some(f) => { + let fatptr: *const [VMFunctionBody] = compilation.finished_functions[f]; + fatptr as *const VMFunctionBody as usize + } + None => panic!("direct call to import"), + }, + RelocationTarget::LibCall(libcall) => { + use cranelift_codegen::ir::LibCall::*; + match libcall { + CeilF32 => wasmtime_f32_ceil as usize, + FloorF32 => wasmtime_f32_floor as usize, + TruncF32 => wasmtime_f32_trunc as usize, + NearestF32 => wasmtime_f32_nearest as usize, + CeilF64 => wasmtime_f64_ceil as usize, + FloorF64 => wasmtime_f64_floor as usize, + TruncF64 => wasmtime_f64_trunc as usize, + NearestF64 => wasmtime_f64_nearest as usize, + Probestack => PROBESTACK as usize, + other => panic!("unexpected libcall: {}", other), } } + RelocationTarget::JumpTable(func_index, jt) => { + match module.local.defined_func_index(func_index) { + Some(f) => { + let offset = *compilation + .jt_offsets + .get(f) + .and_then(|ofs| ofs.get(jt)) + .expect("func jump table"); + let fatptr: *const [VMFunctionBody] = compilation.finished_functions[f]; + fatptr as *const VMFunctionBody as usize + offset as usize + } + None => panic!("func index of jump table"), + } + } + }; + + match r.reloc { + #[cfg(target_pointer_width = "64")] + Reloc::Abs8 => unsafe { + let reloc_address = body.add(r.offset as usize) as usize; + let reloc_addend = r.addend as isize; + let reloc_abs = (target_func_address as u64) + .checked_add(reloc_addend as u64) + .unwrap(); + write_unaligned(reloc_address as *mut u64, reloc_abs); + }, + #[cfg(target_pointer_width = "32")] + Reloc::X86PCRel4 => unsafe { + let reloc_address = body.add(r.offset as usize) as usize; + let reloc_addend = r.addend as isize; + let reloc_delta_u32 = (target_func_address as u32) + .wrapping_sub(reloc_address as u32) + .checked_add(reloc_addend as u32) + .unwrap(); + write_unaligned(reloc_address as *mut u32, reloc_delta_u32); + }, + #[cfg(target_pointer_width = "32")] + Reloc::X86CallPCRel4 => { + // ignore + } + Reloc::X86PCRelRodata4 => { + // ignore + } + _ => panic!("unsupported reloc kind"), } } diff --git a/tests/misc_testsuite/func-400-params.wast b/tests/misc_testsuite/func-400-params.wast new file mode 100644 index 0000000000..71e81cdf87 --- /dev/null +++ b/tests/misc_testsuite/func-400-params.wast @@ -0,0 +1,134 @@ +(module + (type (;0;) (func (param + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 + ) + + (result i32) + )) + (func (export "x") (type 0) local.get 0) +) + +(assert_return + (invoke "x" + (i32.const 1) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + ) + (i32.const 1) +)