diff --git a/CHANGELOG.md b/CHANGELOG.md index 96c2afb..967bb91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ This is a development pre-release. Supported `polkadot-sdk` rev: `2503.0.1` +## v0.4.0 + +This is a development pre-release. + +Supported `polkadot-sdk` rev: `2503.0.1` + +### Added +- Line debug information per YUL builtin and for `if` statements. + +### Fixed +- The debug info source file matches the YUL path in `--debug-output-dir`, allowing tools to display the source line. + ## v0.3.0 This is a development pre-release. diff --git a/crates/llvm-context/src/debug_config/mod.rs b/crates/llvm-context/src/debug_config/mod.rs index e85c14d..af32a65 100644 --- a/crates/llvm-context/src/debug_config/mod.rs +++ b/crates/llvm-context/src/debug_config/mod.rs @@ -2,6 +2,7 @@ pub mod ir_type; +use std::path::Path; use std::path::PathBuf; use serde::Deserialize; @@ -16,6 +17,14 @@ pub struct DebugConfig { pub output_directory: Option, /// Whether debug info should be emitted. pub emit_debug_info: bool, + /// The YUL debug output file path. + /// + /// Is expected to be configured when running in YUL mode. + pub contract_path: Option, + /// The YUL input file path. + /// + /// Is expected to be configured when not running in YUL mode. + pub yul_path: Option, } impl DebugConfig { @@ -24,15 +33,41 @@ impl DebugConfig { Self { output_directory, emit_debug_info, + contract_path: None, + yul_path: None, } } + /// Set the current YUL path. + pub fn set_yul_path(&mut self, yul_path: &Path) { + self.yul_path = yul_path.to_path_buf().into(); + } + + /// Set the current contract path. + pub fn set_contract_path(&mut self, contract_path: &str) { + self.contract_path = self.yul_source_path(contract_path); + } + + /// Returns with the following precedence: + /// 1. The YUL source path if it was configured. + /// 2. The source YUL path from the debug output dir if it was configured. + /// 3. `None` if there is no debug output directory. + pub fn yul_source_path(&self, contract_path: &str) -> Option { + if let Some(path) = self.yul_path.as_ref() { + return Some(path.clone()); + } + + self.output_directory.as_ref().map(|output_directory| { + let mut file_path = output_directory.to_owned(); + let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul); + file_path.push(full_file_name); + file_path + }) + } + /// Dumps the Yul IR. pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> { - if let Some(output_directory) = self.output_directory.as_ref() { - let mut file_path = output_directory.to_owned(); - let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul); - file_path.push(full_file_name); + if let Some(file_path) = self.yul_source_path(contract_path) { std::fs::write(file_path, code)?; } diff --git a/crates/llvm-context/src/polkavm/context/debug_info.rs b/crates/llvm-context/src/polkavm/context/debug_info.rs index 5c61f04..595b788 100644 --- a/crates/llvm-context/src/polkavm/context/debug_info.rs +++ b/crates/llvm-context/src/polkavm/context/debug_info.rs @@ -51,11 +51,20 @@ pub struct DebugInfo<'ctx> { impl<'ctx> DebugInfo<'ctx> { /// A shortcut constructor. - pub fn new(module: &inkwell::module::Module<'ctx>) -> Self { + 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, - module.get_name().to_string_lossy().as_ref(), + yul_name.as_deref().unwrap_or_else(|| module_name.as_ref()), "", "", false, diff --git a/crates/llvm-context/src/polkavm/context/mod.rs b/crates/llvm-context/src/polkavm/context/mod.rs index 368c390..4560bc2 100644 --- a/crates/llvm-context/src/polkavm/context/mod.rs +++ b/crates/llvm-context/src/polkavm/context/mod.rs @@ -247,7 +247,7 @@ where let intrinsics = Intrinsics::new(llvm, &module); let llvm_runtime = LLVMRuntime::new(llvm, &module, &optimizer); let debug_info = debug_config.emit_debug_info.then(|| { - let debug_info = DebugInfo::new(&module); + let debug_info = DebugInfo::new(&module, &debug_config); debug_info.initialize_module(llvm, &module); debug_info }); diff --git a/crates/resolc/src/lib.rs b/crates/resolc/src/lib.rs index 145f89f..698e8b9 100644 --- a/crates/resolc/src/lib.rs +++ b/crates/resolc/src/lib.rs @@ -54,7 +54,7 @@ pub fn yul( solc: &mut T, optimizer_settings: revive_llvm_context::OptimizerSettings, include_metadata_hash: bool, - debug_config: revive_llvm_context::DebugConfig, + mut debug_config: revive_llvm_context::DebugConfig, llvm_arguments: &[String], memory_config: SolcStandardJsonInputSettingsPolkaVMMemory, ) -> anyhow::Result { @@ -77,6 +77,7 @@ pub fn yul( let solc_validator = Some(&*solc); let project = Project::try_from_yul_path(path, solc_validator)?; + debug_config.set_yul_path(path); let build = project.compile( optimizer_settings, include_metadata_hash, diff --git a/crates/resolc/src/project/contract/mod.rs b/crates/resolc/src/project/contract/mod.rs index 334e0ad..c179afc 100644 --- a/crates/resolc/src/project/contract/mod.rs +++ b/crates/resolc/src/project/contract/mod.rs @@ -77,7 +77,7 @@ impl Contract { project: Project, optimizer_settings: revive_llvm_context::OptimizerSettings, include_metadata_hash: bool, - debug_config: revive_llvm_context::DebugConfig, + mut debug_config: revive_llvm_context::DebugConfig, llvm_arguments: &[String], memory_config: SolcStandardJsonInputSettingsPolkaVMMemory, ) -> anyhow::Result { @@ -117,6 +117,7 @@ impl Contract { _ => llvm.create_module(self.path.as_str()), }; + debug_config.set_contract_path(&self.path); let mut context = revive_llvm_context::PolkaVMContext::new( &llvm, module, diff --git a/crates/yul/src/parser/statement/expression/function_call/mod.rs b/crates/yul/src/parser/statement/expression/function_call/mod.rs index 44bbf98..b825ca4 100644 --- a/crates/yul/src/parser/statement/expression/function_call/mod.rs +++ b/crates/yul/src/parser/statement/expression/function_call/mod.rs @@ -123,6 +123,7 @@ impl FunctionCall { D: revive_llvm_context::PolkaVMDependency + Clone, { let location = self.location; + context.set_debug_location(location.line, 0, None)?; match self.name { Name::UserDefined(name) => { diff --git a/crates/yul/src/parser/statement/if_conditional.rs b/crates/yul/src/parser/statement/if_conditional.rs index 6703152..a833b8b 100644 --- a/crates/yul/src/parser/statement/if_conditional.rs +++ b/crates/yul/src/parser/statement/if_conditional.rs @@ -53,6 +53,7 @@ where D: revive_llvm_context::PolkaVMDependency + Clone, { fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext) -> anyhow::Result<()> { + context.set_debug_location(self.location.line, 0, None)?; let condition = self .condition .into_llvm(context)?