//! The LLVM debug information. use std::cell::RefCell; use revive_common::BIT_LENGTH_WORD; use inkwell::debug_info::AsDIScope; use inkwell::debug_info::DIScope; /// Debug info scope stack #[derive(Clone, Debug, PartialEq, Eq)] pub struct ScopeStack<'ctx> { stack: Vec>, } // Abstract the type of the DIScope stack. impl<'ctx> ScopeStack<'ctx> { pub fn from(item: DIScope<'ctx>) -> Self { Self { stack: vec![item] } } /// Return the top of the scope stack, or None if the stack is empty. pub fn top(&self) -> Option> { self.stack.last().copied() } /// Push a scope onto the stack. pub fn push(&mut self, scope: DIScope<'ctx>) { self.stack.push(scope) } /// Pop the scope at the top of the stack and return it. /// Return None if the stack is empty. pub fn pop(&mut self) -> Option> { self.stack.pop() } /// Return the number of scopes on the stack. pub fn len(&self) -> usize { self.stack.len() } } /// The LLVM debug information. pub struct DebugInfo<'ctx> { /// The compile unit. compile_unit: inkwell::debug_info::DICompileUnit<'ctx>, /// The debug info builder. builder: inkwell::debug_info::DebugInfoBuilder<'ctx>, /// Enclosing debug info scopes. scope_stack: RefCell>, } impl<'ctx> DebugInfo<'ctx> { /// A shortcut constructor. pub fn new( module: &inkwell::module::Module<'ctx>, debug_config: &crate::debug_config::DebugConfig, ) -> Self { let module_name = module.get_name().to_string_lossy(); let yul_name = debug_config .contract_path .as_ref() .map(|path| path.display().to_string()); let (builder, compile_unit) = module.create_debug_info_builder( true, inkwell::debug_info::DWARFSourceLanguage::C, yul_name.as_deref().unwrap_or_else(|| module_name.as_ref()), "", "", false, "", 0, "", inkwell::debug_info::DWARFEmissionKind::Full, 0, false, false, "", "", ); Self { compile_unit, builder, scope_stack: RefCell::new(ScopeStack::from(compile_unit.as_debug_info_scope())), } } /// Prepare an LLVM-IR module for debug-info generation pub fn initialize_module( &self, llvm: &'ctx inkwell::context::Context, module: &inkwell::module::Module<'ctx>, ) { let debug_metadata_value = llvm .i32_type() .const_int(inkwell::debug_info::debug_metadata_version() as u64, false); module.add_basic_value_flag( "Debug Info Version", inkwell::module::FlagBehavior::Warning, debug_metadata_value, ); self.push_scope(self.compilation_unit().get_file().as_debug_info_scope()); } /// Finalize debug-info for an LLVM-IR module. pub fn finalize_module(&self) { self.builder().finalize() } /// Creates a function info. pub fn create_function( &self, name: &str, ) -> anyhow::Result> { let flags = inkwell::debug_info::DIFlagsConstants::ZERO; let subroutine_type = self.builder.create_subroutine_type( self.compile_unit.get_file(), Some(self.create_word_type(Some(flags))?.as_type()), &[], flags, ); let function = self.builder.create_function( self.compile_unit.get_file().as_debug_info_scope(), name, None, self.compile_unit.get_file(), 42, subroutine_type, true, false, 1, flags, false, ); self.builder.create_lexical_block( function.as_debug_info_scope(), self.compile_unit.get_file(), 1, 1, ); Ok(function) } /// Creates primitive integer type debug-info. pub fn create_primitive_type( &self, bit_length: usize, flags: Option, ) -> anyhow::Result> { let di_flags = flags.unwrap_or(inkwell::debug_info::DIFlagsConstants::ZERO); let di_encoding: u32 = 0; let type_name = String::from("U") + bit_length.to_string().as_str(); self.builder .create_basic_type(type_name.as_str(), bit_length as u64, di_encoding, di_flags) .map_err(|error| anyhow::anyhow!("Debug info error: {}", error)) } /// Returns the debug-info model of word-sized integer types. pub fn create_word_type( &self, flags: Option, ) -> anyhow::Result> { self.create_primitive_type(BIT_LENGTH_WORD, flags) } /// Return the DIBuilder. pub fn builder(&self) -> &inkwell::debug_info::DebugInfoBuilder<'ctx> { &self.builder } /// Return the compilation unit. { pub fn compilation_unit(&self) -> &inkwell::debug_info::DICompileUnit<'ctx> { &self.compile_unit } /// Push a debug-info scope onto the stack. pub fn push_scope(&self, scope: DIScope<'ctx>) { self.scope_stack.borrow_mut().push(scope) } /// Pop the top of the debug-info scope stack and return it. pub fn pop_scope(&self) -> Option> { self.scope_stack.borrow_mut().pop() } /// Return the top of the debug-info scope stack. pub fn top_scope(&self) -> Option> { self.scope_stack.borrow().top() } /// Return the number of debug-info scopes on the scope stack. pub fn num_scopes(&self) -> usize { self.scope_stack.borrow().len() } }