diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index 24e36e58f..a7cb46517 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -6,8 +6,8 @@ use crate::{ resolver::FuncResolverBuilder, signal::Caller, trampoline::Trampolines, }; -use cranelift_codegen::entity::{EntityRef, PrimaryMap}; -use cranelift_codegen::ir::{self, Ebb, Function, InstBuilder}; +use cranelift_codegen::entity::EntityRef; +use cranelift_codegen::ir::{self, Ebb, Function, InstBuilder, ValueLabel}; use cranelift_codegen::isa::CallConv; use cranelift_codegen::{cursor::FuncCursor, isa}; use cranelift_frontend::{FunctionBuilder, Position, Variable}; @@ -71,6 +71,7 @@ impl ModuleCodeGenerator fn next_function( &mut self, module_info: Arc>, + loc: (u32, u32), ) -> Result<&mut CraneliftFunctionCodeGenerator, CodegenError> { // define_function_body( @@ -101,8 +102,12 @@ impl ModuleCodeGenerator target_config: self.isa.frontend_config().clone(), clif_signatures: self.clif_signatures.clone(), }, + start: loc.0, + end: loc.1, }; + func_env.func.collect_debug_info(); + debug_assert_eq!(func_env.func.dfg.num_ebbs(), 0, "Function must be empty"); debug_assert_eq!(func_env.func.dfg.num_insts(), 0, "Function must be empty"); @@ -112,8 +117,7 @@ impl ModuleCodeGenerator &mut func_env.position, ); - // TODO srcloc - //builder.set_srcloc(cur_srcloc(&reader)); + builder.set_srcloc(ir::SourceLoc::new(loc.0)); let entry_block = builder.create_ebb(); builder.append_ebb_params_for_function_params(entry_block); @@ -141,37 +145,19 @@ impl ModuleCodeGenerator fn finalize( self, module_info: &ModuleInfo, - ) -> Result<((Caller, Option), Box), CodegenError> { - - use wasm_debug::types::{CompiledFunctionData}; - let mut debug_metadata = wasmer_runtime_core::codegen::DebugMetadata { - func_info: PrimaryMap::new(), - inst_info: PrimaryMap::new(), - }; - let mut func_bodies: Map = Map::new(); + ) -> Result< + ( + (Caller, Option), + Box, + ), + CodegenError, + > { + let mut func_bodies: Map = Map::new(); for f in self.functions.into_iter() { - // TODO: review - // if container is stable, then first and last probably makes more sense here, - let min_srcloc = f.func.srclocs.iter().map(|x| x.1.bits()).min().unwrap_or_default(); - let max_srcloc = f.func.srclocs.iter().map(|x| x.1.bits()).max().unwrap_or_default(); - let entry = CompiledFunctionData { - instructions: vec![], - start: wasm_debug::types::SourceLoc::new(min_srcloc), - end: wasm_debug::types::SourceLoc::new(max_srcloc), - compiled_offset: 0, - compiled_size: 0, - }; - debug_metadata.func_info.push(entry); - /*let mut map = std::collections::HashMap::new(); - for (k, v) in f.func.srclocs { - map. - } - debug_metadata.inst_info.push(map);*/ - - func_bodies.push(f.func); + func_bodies.push((f.func, (f.start, f.end))); } - let (func_resolver_builder, handler_data) = + let (func_resolver_builder, debug_metadata, handler_data) = FuncResolverBuilder::new(&*self.isa, func_bodies, module_info)?; let trampolines = Arc::new(Trampolines::new(&*self.isa, module_info)); @@ -195,7 +181,10 @@ impl ModuleCodeGenerator )); Ok(( - (Caller::new(handler_data, trampolines, func_resolver), Some(debug_metadata)), + ( + Caller::new(handler_data, trampolines, func_resolver), + debug_metadata, + ), cache_gen, )) } @@ -265,6 +254,8 @@ pub struct CraneliftFunctionCodeGenerator { next_local: usize, position: Position, func_env: FunctionEnvironment, + start: u32, + end: u32, } pub struct FunctionEnvironment { @@ -467,7 +458,7 @@ impl FuncEnvironment for FunctionEnvironment { let local_memory_bound = func.create_global_value(ir::GlobalValueData::Load { base: local_memory_ptr, offset: (vm::LocalMemory::offset_bound() as i32).into(), - global_type: ptr_type, + global_type: ir::types::I32, readonly: false, }); @@ -575,7 +566,7 @@ impl FuncEnvironment for FunctionEnvironment { let table_count = func.create_global_value(ir::GlobalValueData::Load { base: table_struct_ptr, offset: (vm::LocalTable::offset_count() as i32).into(), - global_type: ptr_type, + global_type: ir::types::I32, // The table length can change, so it can't be readonly. readonly: false, }); @@ -1092,13 +1083,14 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { Ok(()) } - fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { + fn feed_local(&mut self, ty: WpType, n: usize, loc: u32) -> Result<(), CodegenError> { let mut next_local = self.next_local; let mut builder = FunctionBuilder::new( &mut self.func, &mut self.func_translator.func_ctx, &mut self.position, ); + builder.set_srcloc(ir::SourceLoc::new(loc)); cranelift_wasm::declare_locals( &mut builder, n as u32, @@ -1114,7 +1106,7 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { Ok(()) } - fn feed_event(&mut self, event: Event, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + fn feed_event(&mut self, event: Event, _module_info: &ModuleInfo, loc: u32) -> Result<(), CodegenError> { let op = match event { Event::Wasm(x) => x, Event::WasmOwned(ref x) => x, @@ -1136,6 +1128,8 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { &mut self.func_translator.func_ctx, &mut self.position, ); + builder.func.collect_debug_info(); + builder.set_srcloc(ir::SourceLoc::new(loc)); let module_state = ModuleTranslationState::new(); let func_state = &mut self.func_translator.state; translate_operator( @@ -1145,6 +1139,7 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { func_state, &mut self.func_env, )?; + Ok(()) } @@ -1173,6 +1168,8 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { } } + builder.finalize(); + // Discard any remaining values on the stack. Either we just returned them, // or the end of the function is unreachable. state.stack.clear(); @@ -1249,9 +1246,11 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Ebb) -> u // This is a normal WebAssembly signature parameter, so create a local for it. let local = Variable::new(next_local); builder.declare_var(local, param_type.value_type); + let value_label = ValueLabel::from_u32(next_local as u32); next_local += 1; let param_value = builder.ebb_params(entry_block)[i]; + builder.set_val_label(param_value, value_label); builder.def_var(local, param_value); } if param_type.purpose == ir::ArgumentPurpose::VMContext { diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index 06bffc791..5eccdcb2d 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -42,7 +42,7 @@ fn get_isa() -> Box { builder.set("opt_level", "speed_and_size").unwrap(); builder.set("jump_tables_enabled", "false").unwrap(); - if cfg!(not(test)) { + if cfg!(not(test)) && cfg!(not(debug)) { builder.set("enable_verifier", "false").unwrap(); } diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index 11999ba02..1fb15b5f6 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -45,7 +45,6 @@ impl Module { runnable_module: Arc::new(Box::new(runnable_module)), cache_gen, info, - debug_info: None, }) } } diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index efc038d58..e039af433 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -10,7 +10,9 @@ use crate::{ }; use byteorder::{ByteOrder, LittleEndian}; use cranelift_codegen::{ + ValueLabelsRanges, binemit::{Stackmap, StackmapSink}, + entity::PrimaryMap, ir, isa, Context, }; use rayon::prelude::*; @@ -96,54 +98,159 @@ impl FuncResolverBuilder { pub fn new( isa: &dyn isa::TargetIsa, - function_bodies: Map, + function_bodies: Map, info: &ModuleInfo, - ) -> CompileResult<(Self, HandlerData)> { + ) -> CompileResult<( + Self, + Option, + HandlerData, + )> { let num_func_bodies = function_bodies.len(); let mut local_relocs = Map::with_capacity(num_func_bodies); let mut external_relocs = Map::with_capacity(num_func_bodies); let mut trap_sink = TrapSink::new(); - let compiled_functions: Result, (RelocSink, LocalTrapSink))>, CompileError> = - function_bodies - .into_vec() - .par_iter() - .map_init( - || Context::new(), - |ctx, func| { - let mut code_buf = Vec::new(); - ctx.func = func.to_owned(); - let mut reloc_sink = RelocSink::new(); - let mut local_trap_sink = LocalTrapSink::new(); - let mut stackmap_sink = NoopStackmapSink {}; - ctx.compile_and_emit( - isa, - &mut code_buf, - &mut reloc_sink, - &mut local_trap_sink, - &mut stackmap_sink, - ) - .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; - ctx.clear(); - Ok((code_buf, (reloc_sink, local_trap_sink))) - }, - ) - .collect(); + let fb = function_bodies.iter().collect::>(); + rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap(); - let compiled_functions = compiled_functions?; + let compiled_functions: Result< + Vec<(Vec, + ( + LocalFuncIndex, + CompiledFunctionData, + ValueLabelsRanges, + Vec>, + RelocSink, + LocalTrapSink, + ), + )>, + CompileError, + > = fb + .par_iter() + .map_init( + || Context::new(), + |ctx, (lfi, (func, (start, end)))| { + let mut code_buf = Vec::new(); + ctx.func = func.to_owned(); + let mut reloc_sink = RelocSink::new(); + let mut local_trap_sink = LocalTrapSink::new(); + let mut stackmap_sink = NoopStackmapSink {}; + + //ctx.eliminate_unreachable_code(isa).unwrap(); + /*ctx.dce(isa).unwrap(); + ctx.shrink_instructions(isa).unwrap(); + ctx.redundant_reload_remover(isa).unwrap(); + ctx.preopt(isa).unwrap(); + ctx.legalize(isa).unwrap(); + ctx.postopt(isa).unwrap();*/ + ctx.verify(isa).unwrap(); + + ctx.compile_and_emit( + isa, + &mut code_buf, + &mut reloc_sink, + &mut local_trap_sink, + &mut stackmap_sink, + ) + .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; + + // begin debug stuff + let func = &ctx.func; + let encinfo = isa.encoding_info(); + let mut ebbs = func.layout.ebbs().collect::>(); + ebbs.sort_by_key(|ebb| func.offsets[*ebb]); + let instructions = /*func + .layout + .ebbs()*/ + ebbs.into_iter() + .flat_map(|ebb| { + func.inst_offsets(ebb, &encinfo) + .map(|(offset, inst, length)| { + let srcloc = func.srclocs[inst]; + let val = srcloc.bits(); + wasm_debug::types::CompiledInstructionData { + // we write this data later + loc: wasm_debug::types::SourceLoc::new(val),//srcloc.bits()), + offset: offset as usize, + length: length as usize, + } + }) + }) + .collect::>(); + + /*let mut unwind = vec![]; + ctx.emit_unwind_info(isa, &mut unwind); + dbg!(unwind.len());*/ + + let stack_slots = ctx.func.stack_slots.iter().map(|(_, ssd)| ssd.offset).collect::>>(); + let labels_ranges = ctx.build_value_labels_ranges(isa).unwrap_or_default(); + if labels_ranges.len() == 24 || labels_ranges.len() == 25 { + let mut vec = labels_ranges.iter().map(|(a,b)| (*a, b.clone())).collect::>(); + vec.sort_by(|a, b| a.0.as_u32().cmp(&b.0.as_u32())); + dbg!(labels_ranges.len(), &vec); + } + + let entry = CompiledFunctionData { + instructions, + start: wasm_debug::types::SourceLoc::new(*start), + end: wasm_debug::types::SourceLoc::new(*end), + // this not being 0 breaks inst-level debugging + compiled_offset: 0, + compiled_size: code_buf.len(), + }; + + // end debug stuff + + ctx.clear(); + Ok((code_buf, (*lfi, entry, labels_ranges, stack_slots, reloc_sink, local_trap_sink))) + }, + ) + .collect(); + + + use wasm_debug::types::CompiledFunctionData; + let mut debug_metadata = wasmer_runtime_core::codegen::DebugMetadata { + func_info: PrimaryMap::new(), + inst_info: PrimaryMap::new(), + pointers: vec![], + stack_slot_offsets: PrimaryMap::new(), + }; + + let mut compiled_functions = compiled_functions?; + compiled_functions.sort_by(|a, b| (a.1).0.cmp(&(b.1).0)); + let compiled_functions = compiled_functions; let mut total_size = 0; // We separate into two iterators, one iterable and one into iterable - let (code_bufs, sinks): (Vec>, Vec<(RelocSink, LocalTrapSink)>) = - compiled_functions.into_iter().unzip(); - for (code_buf, (reloc_sink, mut local_trap_sink)) in code_bufs.iter().zip(sinks.into_iter()) + let (code_bufs, sinks): ( + Vec>, + Vec<( + LocalFuncIndex, + CompiledFunctionData, + ValueLabelsRanges, + Vec>, + RelocSink, + LocalTrapSink, + )>, + ) = compiled_functions.into_iter().unzip(); + for ( + code_buf, + (_, entry, vlr, stackslots, reloc_sink, mut local_trap_sink), + ) in code_bufs + .iter() + .zip(sinks.into_iter()) { + let rounded_size = round_up(code_buf.len(), mem::size_of::()); + debug_metadata.func_info.push(entry); + debug_metadata.inst_info.push(unsafe {std::mem::transmute(vlr)}); + debug_metadata.stack_slot_offsets.push(stackslots); + // Clear the local trap sink and consolidate all trap info // into a single location. trap_sink.drain_local(total_size, &mut local_trap_sink); // Round up each function's size to pointer alignment. - total_size += round_up(code_buf.len(), mem::size_of::()); + total_size += rounded_size; local_relocs.push(reloc_sink.local_relocs.into_boxed_slice()); external_relocs.push(reloc_sink.external_relocs.into_boxed_slice()); @@ -175,7 +282,12 @@ impl FuncResolverBuilder { let mut previous_end = 0; for compiled in code_bufs.iter() { - let new_end = previous_end + round_up(compiled.len(), mem::size_of::()); + let length = round_up(compiled.len(), mem::size_of::()); + debug_metadata.pointers.push(( + (memory.as_ptr() as usize + previous_end) as *const u8, + length, + )); + let new_end = previous_end + length; unsafe { memory.as_slice_mut()[previous_end..previous_end + compiled.len()] .copy_from_slice(&compiled[..]); @@ -197,7 +309,7 @@ impl FuncResolverBuilder { func_resolver_builder.relocate_locals(); - Ok((func_resolver_builder, handler_data)) + Ok((func_resolver_builder, Some(debug_metadata), handler_data)) } fn relocate_locals(&mut self) { diff --git a/lib/debug-writer/src/lib.rs b/lib/debug-writer/src/lib.rs index 66a2b2513..655a5a6dc 100644 --- a/lib/debug-writer/src/lib.rs +++ b/lib/debug-writer/src/lib.rs @@ -1,13 +1,15 @@ // TODO: add attribution to LLVM for data definitions and WasmTime for code structure -use std::str::FromStr; -use std::ptr; use std::ffi::c_void; +use std::ptr; +use std::str::FromStr; -pub use wasm_debug::{read_debuginfo, DebugInfoData, WasmFileInfo}; pub use wasm_debug::{emit_dwarf, ResolvedSymbol, SymbolResolver}; +pub use wasm_debug::{read_debuginfo, DebugInfoData, WasmFileInfo}; -use target_lexicon::{Triple, Architecture, Vendor, OperatingSystem, Environment, BinaryFormat}; -use gimli::write::{self, DwarfUnit, Sections, Address, RangeList, EndianVec, AttributeValue, Range}; +use gimli::write::{ + self, Address, AttributeValue, DwarfUnit, EndianVec, Range, RangeList, Sections, +}; +use target_lexicon::{Architecture, BinaryFormat, Environment, OperatingSystem, Triple, Vendor}; use wasmer_runtime_core::{module::ModuleInfo, state::CodeVersion}; @@ -138,8 +140,8 @@ pub fn generate_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, // `DW_AT_low_pc` and `DW_AT_high_pc` are good for continuous // `DW_AT_low_pc` alone can work for a single address, but we can probably not // worry about that for now. These attribtues associate machine code with the DIE - // - - + // - + match platform { X86_64_GNU_LINUX => unimplemented!("in progress"), X86_64_OSX => unimplemented!("in progress"), @@ -151,7 +153,12 @@ pub fn generate_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, */ // converts existing dwarf into a usable form with metadata from the JIT -fn reprocess_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, code_version: &CodeVersion, platform: Triple) -> Option { +fn reprocess_dwarf( + module_info: &ModuleInfo, + debug_info_data: &DebugInfoData, + code_version: &CodeVersion, + platform: Triple, +) -> Option { None } diff --git a/lib/middleware-common/src/block_trace.rs b/lib/middleware-common/src/block_trace.rs index f104ab086..1d539e44a 100644 --- a/lib/middleware-common/src/block_trace.rs +++ b/lib/middleware-common/src/block_trace.rs @@ -25,6 +25,7 @@ impl FunctionMiddleware for BlockTrace { op: Event<'a, 'b>, _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + _loc: u32, ) -> Result<(), Self::Error> { match op { Event::Internal(InternalEvent::FunctionBegin(_)) => { diff --git a/lib/middleware-common/src/call_trace.rs b/lib/middleware-common/src/call_trace.rs index 5cb77534c..5f7f32032 100644 --- a/lib/middleware-common/src/call_trace.rs +++ b/lib/middleware-common/src/call_trace.rs @@ -26,6 +26,7 @@ impl FunctionMiddleware for CallTrace { op: Event<'a, 'b>, _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + _loc: u32, ) -> Result<(), Self::Error> { let counter = self.counter.clone(); diff --git a/lib/middleware-common/src/metering.rs b/lib/middleware-common/src/metering.rs index a7d10aece..a66fea509 100644 --- a/lib/middleware-common/src/metering.rs +++ b/lib/middleware-common/src/metering.rs @@ -44,6 +44,7 @@ impl FunctionMiddleware for Metering { op: Event<'a, 'b>, _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + _loc: u32, ) -> Result<(), Self::Error> { match op { Event::Internal(InternalEvent::FunctionBegin(_)) => { diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index c72ca25df..835bc4501 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -112,9 +112,12 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, /// Checks the precondition for a module. fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>; /// Creates a new function and returns the function-scope code generator for it. - fn next_function(&mut self, module_info: Arc>) -> Result<&mut FCG, E>; + fn next_function(&mut self, module_info: Arc>, loc: (u32, u32)) -> Result<&mut FCG, E>; /// Finalizes this module. - fn finalize(self, module_info: &ModuleInfo) -> Result<((RM, Option), Box), E>; + fn finalize( + self, + module_info: &ModuleInfo, + ) -> Result<((RM, Option), Box), E>; /// Creates a module from cache. unsafe fn from_cache(cache: Artifact, _: Token) -> Result; @@ -127,6 +130,10 @@ pub struct DebugMetadata { pub func_info: PrimaryMap, /// inst_info pub inst_info: PrimaryMap, + /// stack slot offsets! + pub stack_slot_offsets: PrimaryMap>>, + /// function pointers and their lengths + pub pointers: Vec<(*const u8, usize)>, } /// A streaming compiler which is designed to generated code for a module based on a stream @@ -241,7 +248,6 @@ impl< validate_with_features(wasm, &compiler_config.features)?; } - let mut mcg = match MCG::backend_id() { "llvm" => MCG::new_with_target( compiler_config.triple.clone(), @@ -252,13 +258,15 @@ impl< }; let mut chain = (self.middleware_chain_generator)(); let info = crate::parse::read_module(wasm, &mut mcg, &mut chain, &compiler_config)?; - let ((exec_context, debug_metadata), cache_gen) = - mcg.finalize(&info.read().unwrap()) - .map_err(|x| CompileError::InternalError { - msg: format!("{:?}", x), - })?; + let ((exec_context, debug_metadata), cache_gen) = mcg + .finalize(&info.read().unwrap()) + .map_err(|x| CompileError::InternalError { + msg: format!("{:?}", x), + })?; - use target_lexicon::{Triple, Architecture, Vendor, OperatingSystem, Environment, BinaryFormat}; + use target_lexicon::{ + Architecture, BinaryFormat, Environment, OperatingSystem, Triple, Vendor, + }; const X86_64_OSX: Triple = Triple { architecture: Architecture::X86_64, vendor: Vendor::Apple, @@ -266,34 +274,37 @@ impl< environment: Environment::Unknown, binary_format: BinaryFormat::Macho, }; - + if compiler_config.generate_debug_info { let debug_metadata = debug_metadata.expect("debug metadata"); let debug_info = wasm_debug::read_debuginfo(wasm); - let extra_info = wasm_debug::types::ModuleVmctxInfo { - memory_offset: 0, - stack_slot_offsets: cranelift_entity::PrimaryMap::new(), - }; + let extra_info = wasm_debug::types::ModuleVmctxInfo::new(14 * 8, debug_metadata.stack_slot_offsets.values()); // lazy type hack (TODO:) - let compiled_fn_map = unsafe { std::mem::transmute(debug_metadata.func_info) }; - let range_map = unsafe { std::mem::transmute(debug_metadata.inst_info) }; - let raw_func_slice = vec![];//exec_context.get_local_function_pointers_and_lengths().expect("raw func slice"); - dbg!("DEBUG INFO GENERATED"); - let debug_image = wasm_debug::emit_debugsections_image(X86_64_OSX, - std::mem::size_of::() as u8, - &debug_info, - &extra_info, - &compiled_fn_map, - &range_map, - &raw_func_slice).expect("make debug image"); + let compiled_fn_map = wasm_debug::types::create_module_address_map(debug_metadata.func_info.values()); + let range_map = wasm_debug::types::build_values_ranges(debug_metadata.inst_info.values()); + let raw_func_slice = debug_metadata.pointers; - crate::jit_debug::register_new_jit_code_entry(&debug_image, crate::jit_debug::JITAction::JIT_REGISTER_FN); - } + dbg!("DEBUG INFO GENERATED"); + let debug_image = wasm_debug::emit_debugsections_image( + X86_64_OSX, + std::mem::size_of::() as u8, + &debug_info, + &extra_info, + &compiled_fn_map, + &range_map, + &raw_func_slice, + ) + .expect("make debug image"); + + crate::jit_debug::register_new_jit_code_entry( + &debug_image, + crate::jit_debug::JITAction::JIT_REGISTER_FN, + ); + } Ok(ModuleInner { cache_gen, runnable_module: Arc::new(Box::new(exec_context)), info: Arc::try_unwrap(info).unwrap().into_inner().unwrap(), - debug_info: None, }) } @@ -340,6 +351,7 @@ impl MiddlewareChain { fcg: Option<&mut FCG>, ev: Event, module_info: &ModuleInfo, + loc: u32, ) -> Result<(), String> { let mut sink = EventSink { buffer: SmallVec::new(), @@ -348,12 +360,12 @@ impl MiddlewareChain { for m in &mut self.chain { let prev: SmallVec<[Event; 2]> = sink.buffer.drain().collect(); for ev in prev { - m.feed_event(ev, module_info, &mut sink)?; + m.feed_event(ev, module_info, &mut sink, loc)?; } } if let Some(fcg) = fcg { for ev in sink.buffer { - fcg.feed_event(ev, module_info) + fcg.feed_event(ev, module_info, loc) .map_err(|x| format!("{:?}", x))?; } } @@ -372,6 +384,7 @@ pub trait FunctionMiddleware { op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + loc: u32, ) -> Result<(), Self::Error>; } @@ -381,6 +394,7 @@ pub(crate) trait GenericFunctionMiddleware { op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + loc: u32, ) -> Result<(), String>; } @@ -390,8 +404,9 @@ impl> GenericFunctionMiddleware for T op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + loc: u32, ) -> Result<(), String> { - ::feed_event(self, op, module_info, sink) + ::feed_event(self, op, module_info, sink, loc) .map_err(|x| format!("{:?}", x)) } } @@ -405,13 +420,13 @@ pub trait FunctionCodeGenerator { fn feed_param(&mut self, ty: WpType) -> Result<(), E>; /// Adds `n` locals to the function. - fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), E>; + fn feed_local(&mut self, ty: WpType, n: usize, loc: u32) -> Result<(), E>; /// Called before the first call to `feed_opcode`. fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), E>; /// Called for each operator. - fn feed_event(&mut self, op: Event, module_info: &ModuleInfo) -> Result<(), E>; + fn feed_event(&mut self, op: Event, module_info: &ModuleInfo, loc: u32) -> Result<(), E>; /// Finalizes the function. fn finalize(&mut self) -> Result<(), E>; diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index d012ae8d6..bb8f29ad0 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -86,9 +86,14 @@ impl Instance { None => vm::Ctx::new(backing, import_backing, &module), }; vmctx.as_mut_ptr().write(real_ctx); + for (_, memory) in backing.vm_memories.iter_mut() { + let mem: &mut vm::LocalMemory = &mut **memory; + mem.vmctx = dbg!(vmctx.as_mut_ptr()); + } }; Box::leak(vmctx); + let instance = Instance { module, inner, diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index 8a56fcd7f..f00d07bf6 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -8,15 +8,25 @@ use std::ptr; // // see also https://sourceware.org/gdb/current/onlinedocs/gdb.html#JIT-Interface +#[no_mangle] #[inline(never)] -pub extern "C" fn __jit_debug_register_code() { - +extern "C" fn __jit_debug_register_code() { + // implementation of this function copied from wasmtime (TODO: link and attribution) + // prevent optimization of this function + let x = 3; + unsafe { + std::ptr::read_volatile(&x); + } } #[allow(non_camel_case_types)] #[derive(Debug)] #[repr(u32)] -pub enum JITAction { JIT_NOACTION = 0, JIT_REGISTER_FN = 1, JIT_UNREGISTER_FN = 2 } +pub enum JITAction { + JIT_NOACTION = 0, + JIT_REGISTER_FN = 1, + JIT_UNREGISTER_FN = 2, +} #[no_mangle] #[repr(C)] @@ -76,9 +86,15 @@ unsafe fn push_front(jce: *mut JITCodeEntry) { // deleted static (added and deleted by Mark): TODO: pub fn register_new_jit_code_entry(bytes: &[u8], action: JITAction) -> *mut JITCodeEntry { + let owned_bytes = bytes.iter().cloned().collect::>(); + let ptr = owned_bytes.as_ptr(); + let len = owned_bytes.len(); + + std::mem::forget(bytes); + let entry: *mut JITCodeEntry = Box::into_raw(Box::new(JITCodeEntry { - symfile_addr: bytes.as_ptr(), - symfile_size: bytes.len() as _, + symfile_addr: ptr, + symfile_size: len as _, ..JITCodeEntry::default() })); @@ -86,6 +102,9 @@ pub fn register_new_jit_code_entry(bytes: &[u8], action: JITAction) -> *mut JITC push_front(entry); __jit_debug_descriptor.relevant_entry = entry; __jit_debug_descriptor.action_flag = action as u32; + __jit_debug_register_code(); + __jit_debug_descriptor.relevant_entry = ptr::null_mut(); + __jit_debug_descriptor.action_flag = JITAction::JIT_NOACTION as _; } entry diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 46f46f34a..8f808be74 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -66,10 +66,10 @@ pub mod vmcalls; pub use trampoline_x64 as trampoline; #[cfg(unix)] pub mod fault; +pub mod jit_debug; pub mod state; #[cfg(feature = "managed")] pub mod tiering; -pub mod jit_debug; use self::error::CompileResult; #[doc(inline)] diff --git a/lib/runtime-core/src/memory/mod.rs b/lib/runtime-core/src/memory/mod.rs index 75a02e227..95458eda4 100644 --- a/lib/runtime-core/src/memory/mod.rs +++ b/lib/runtime-core/src/memory/mod.rs @@ -227,6 +227,7 @@ impl UnsharedMemory { base: std::ptr::null_mut(), bound: 0, memory: std::ptr::null_mut(), + vmctx: std::ptr::null_mut(), }; let storage = match desc.memory_type() { @@ -314,6 +315,7 @@ impl SharedMemory { base: std::ptr::null_mut(), bound: 0, memory: std::ptr::null_mut(), + vmctx: std::ptr::null_mut(), }; let memory = StaticMemory::new(desc, &mut local)?; diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index cfe9aaa3c..4b04a6d6b 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -20,18 +20,12 @@ use indexmap::IndexMap; use std::collections::HashMap; use std::sync::Arc; -#[doc(hidden)] -pub struct DebugInfo { - -} - /// This is used to instantiate a new WebAssembly module. #[doc(hidden)] pub struct ModuleInner { pub runnable_module: Arc>, pub cache_gen: Box, pub info: ModuleInfo, - pub debug_info: Option, } /// Container for module data including memories, globals, tables, imports, and exports. @@ -83,6 +77,18 @@ pub struct ModuleInfo { /// Custom sections. pub custom_sections: HashMap>, + + /// Debug info for funcs + pub func_debug_info: Map, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +/// info about functions for debugging +pub struct FuncDebugInfo { + /// byte offset from start of code section where the function starts at + pub start: u32, + /// byte offset from start of code section where the function starts at + pub end: u32, } impl ModuleInfo { diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index ad91afb77..b6a7ff6d5 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -6,8 +6,8 @@ use crate::{ backend::{CompilerConfig, RunnableModule}, error::CompileError, module::{ - DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, - TableInitializer, + DataInitializer, ExportIndex, FuncDebugInfo, ImportName, ModuleInfo, StringTable, + StringTableBuilder, TableInitializer, }, structures::{Map, TypedIndex}, types::{ @@ -90,6 +90,8 @@ pub fn read_module< em_symbol_map: compiler_config.symbol_map.clone(), custom_sections: HashMap::new(), + + func_debug_info: Map::new(), })); let mut parser = wasmparser::ValidatingParser::new( @@ -206,91 +208,113 @@ pub fn read_module< ParserState::StartSectionEntry(start_index) => { info.write().unwrap().start_func = Some(FuncIndex::new(start_index as usize)); } - ParserState::BeginFunctionBody { .. } => { + ParserState::BeginFunctionBody { range } => { let id = func_count; if !mcg_info_fed { mcg_info_fed = true; - info.write().unwrap().namespace_table = - namespace_builder.take().unwrap().finish(); - info.write().unwrap().name_table = name_builder.take().unwrap().finish(); - mcg.feed_signatures(info.read().unwrap().signatures.clone()) + { + let mut info_write = info.write().unwrap(); + info_write.namespace_table = namespace_builder.take().unwrap().finish(); + info_write.name_table = name_builder.take().unwrap().finish(); + } + let info_read = info.read().unwrap(); + mcg.feed_signatures(info_read.signatures.clone()) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - mcg.feed_function_signatures(info.read().unwrap().func_assoc.clone()) + mcg.feed_function_signatures(info_read.func_assoc.clone()) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - mcg.check_precondition(&info.read().unwrap()) + mcg.check_precondition(&info_read) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; } + let fcg = mcg - .next_function(Arc::clone(&info)) + .next_function(Arc::clone(&info), (range.start as u32, range.end as u32)) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - let info_read = info.read().unwrap(); - let sig = info_read - .signatures - .get( - *info - .read() - .unwrap() - .func_assoc - .get(FuncIndex::new( - id as usize + info.read().unwrap().imported_functions.len(), - )) - .unwrap(), - ) - .unwrap(); - for ret in sig.returns() { - fcg.feed_return(type_to_wp_type(*ret)) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - } - for param in sig.params() { - fcg.feed_param(type_to_wp_type(*param)) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - } - - let mut body_begun = false; - - loop { - let state = parser.read(); - match state { - ParserState::Error(err) => return Err(LoadError::Parse(*err)), - ParserState::FunctionBodyLocals { ref locals } => { - for &(count, ty) in locals.iter() { - fcg.feed_local(ty, count as usize) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - } - } - ParserState::CodeOperator(op) => { - if !body_begun { - body_begun = true; - fcg.begin_body(&info.read().unwrap()) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - middlewares - .run( - Some(fcg), - Event::Internal(InternalEvent::FunctionBegin(id as u32)), - &info.read().unwrap(), - ) - .map_err(LoadError::Codegen)?; - } - middlewares - .run(Some(fcg), Event::Wasm(op), &info.read().unwrap()) - .map_err(LoadError::Codegen)?; - } - ParserState::EndFunctionBody => break, - _ => unreachable!(), + { + let info_read = info.read().unwrap(); + let sig = info_read + .signatures + .get( + *info + .read() + .unwrap() + .func_assoc + .get(FuncIndex::new( + id as usize + info_read.imported_functions.len(), + )) + .unwrap(), + ) + .unwrap(); + for ret in sig.returns() { + fcg.feed_return(type_to_wp_type(*ret)) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + for param in sig.params() { + fcg.feed_param(type_to_wp_type(*param)) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; } } - middlewares - .run( - Some(fcg), - Event::Internal(InternalEvent::FunctionEnd), - &info.read().unwrap(), - ) - .map_err(LoadError::Codegen)?; + + let mut operator_parser = parser.create_binary_reader(); + + // read locals in function body + { + let local_count = operator_parser.read_local_count().unwrap(); + let mut total = 0; + for _ in 0..local_count { + let cur_pos = range.start as u32 + operator_parser.current_position() as u32; + let (count, ty) = operator_parser + .read_local_decl(&mut total) + .expect("Expected local"); + fcg.feed_local(ty, count as usize, cur_pos) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + } + + // read instruction locations into vector for debug purposes + { + let info_read = info.read().unwrap(); + let mut cur_pos = range.start as u32 + operator_parser.current_position() as u32; + fcg.begin_body(&info_read) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionBegin(id as u32)), + &info_read, + cur_pos, + ) + .map_err(LoadError::Codegen)?; + + while let Ok(op) = operator_parser.read_operator() { + middlewares + .run(Some(fcg), Event::WasmOwned(op), &info_read, cur_pos) + .map_err(LoadError::Codegen)?; + cur_pos = range.start as u32 + operator_parser.current_position() as u32; + } + + cur_pos = range.start as u32 + operator_parser.current_position() as u32; + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionEnd), + &info_read, + cur_pos, + ) + .map_err(LoadError::Codegen)?; + } + fcg.finalize() .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; func_count = func_count.wrapping_add(1); + + // debug info + let debug_entry = FuncDebugInfo { + start: range.start as u32, + end: range.end as u32, + }; + info.write().unwrap().func_debug_info.push(debug_entry); } ParserState::BeginActiveElementSectionEntry(table_index) => { let table_index = TableIndex::new(table_index as usize); diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 2a39bdec1..ceb7e3114 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -8,7 +8,7 @@ use crate::{ module::{ModuleInfo, ModuleInner}, sig_registry::SigRegistry, structures::TypedIndex, - types::{LocalOrImport, MemoryIndex, TableIndex, Value}, + types::{LocalOrImport, MemoryIndex, TableIndex, Value, LocalMemoryIndex}, vmcalls, }; use std::{ @@ -134,6 +134,9 @@ pub struct InternalCtx { /// Interrupt signal mem. pub interrupt_signal_mem: *mut u8, + + /// hmm + pub first_mem: *mut LocalMemory, } static INTERNAL_FIELDS: AtomicUsize = AtomicUsize::new(0); @@ -283,6 +286,7 @@ impl Ctx { }; ((*mem).base, (*mem).bound) }; + dbg!(mem_bound); Self { internal: InternalCtx { memories: local_backing.vm_memories.as_mut_ptr(), @@ -306,6 +310,7 @@ impl Ctx { internals: &mut local_backing.internals.0, interrupt_signal_mem: get_interrupt_signal_mem(), + first_mem: local_backing.vm_memories[LocalMemoryIndex::new(0)], }, local_functions: local_backing.local_functions.as_ptr(), @@ -336,6 +341,8 @@ impl Ctx { }; ((*mem).base, (*mem).bound) }; + + dbg!(mem_bound); Self { internal: InternalCtx { memories: local_backing.vm_memories.as_mut_ptr(), @@ -359,6 +366,8 @@ impl Ctx { internals: &mut local_backing.internals.0, interrupt_signal_mem: get_interrupt_signal_mem(), + + first_mem: local_backing.vm_memories[LocalMemoryIndex::new(0)], }, local_functions: local_backing.local_functions.as_ptr(), @@ -537,7 +546,7 @@ impl Ctx { } pub const fn offset_local_functions() -> u8 { - 14 * (mem::size_of::() as u8) + 15 * (mem::size_of::() as u8) } } @@ -666,6 +675,9 @@ pub struct LocalMemory { /// This is either `*mut DynamicMemory`, `*mut StaticMemory`, /// or `*mut SharedStaticMemory`. pub memory: *mut (), + + /// wat + pub vmctx: *mut Ctx, } // manually implemented because LocalMemory contains raw pointers