diff --git a/language/solana/move-to-solana/src/stackless/dwarf.rs b/language/solana/move-to-solana/src/stackless/dwarf.rs index c355d61bb0..3e17380018 100644 --- a/language/solana/move-to-solana/src/stackless/dwarf.rs +++ b/language/solana/move-to-solana/src/stackless/dwarf.rs @@ -13,7 +13,8 @@ //! - Hides weirdly mutable array pointers. //! - Provides high-level instruction builders compatible with the stackless bytecode model. -use crate::stackless::{extensions::FunctionEnvExt, FunctionContext, Module, TargetData}; +use crate::stackless::{extensions::FunctionEnvExt, llvm::Module, FunctionContext, TargetData}; +use anyhow::{Context, Result}; use codespan::Location; use itertools::enumerate; use llvm_sys::{ @@ -35,12 +36,15 @@ use llvm_sys::{ }; use log::{debug, error, warn}; -use move_model::model::{ModuleId, StructId}; +use move_model::model::{GlobalEnv, ModuleId, StructId}; +use move_stackless_bytecode::stackless_bytecode::Bytecode; use std::{ cell::RefCell, collections::{HashMap, HashSet}, env, ffi::CStr, + fs::File, + io::{BufRead, BufReader, Read, Seek, SeekFrom}, ptr, }; @@ -97,6 +101,43 @@ pub enum UnresolvedPrintLogLevel { Error, } +pub struct Instruction<'up> { + bc: &'up Bytecode, + func_ctx: &'up FunctionContext<'up, 'up>, + loc: move_model::model::Loc, +} + +impl<'up> Instruction<'up> { + fn new(bc: &'up Bytecode, func_ctx: &'up FunctionContext<'_, '_>) -> Instruction<'up> { + let loc = func_ctx + .env + .get_bytecode_loc(bc.get_attr_id().as_usize() as u16); + Instruction { bc, func_ctx, loc } + } + fn debug(&self) { + let bc = self.bc; + let loc = &self.loc; + let (file, _line, _column, start, end) = self.loc_display(); + + let (start_line, start_column) = find_line_column(&file, start as usize).unwrap_or((-1, 0)); + let (end_line, end_column) = find_line_column(&file, end as usize).unwrap_or((-1, 0)); + debug!(target: "bytecode", "{:#?} loc {:#?}", bc, loc); + let substring = read_substring(&file, start as usize, end as usize) + .unwrap_or("String not found".to_string()); + debug!(target: "bytecode", "file {:#?}[{start}-{end}]:{start_line}.{start_column}-{end_line}.{end_column} {substring}", file); + } + fn loc_display(&self) -> (String, u32, u32, u32, u32) { + let g_env = self.func_ctx.module_cx.env.env; + let loc = &self.loc; + loc_display(loc, g_env) + } + fn create_load_store(&self, _load: *mut LLVMValue, _store: *mut LLVMValue, mty: &mty::Type) { + let bc = self.bc; + let loc = &self.loc; + debug!(target: "bytecode", "create_load_store {:#?} loc {:#?} mty {:#?}", bc, loc, mty); + } +} + pub fn type_get_name(x: LLVMMetadataRef) -> String { let mut length: ::libc::size_t = 0; let name_c_str = unsafe { LLVMDITypeGetName(x, &mut length) }; @@ -722,6 +763,11 @@ impl<'up> DIBuilder<'up> { ) }; + let llvm_builder = &mod_cx.llvm_builder; + + let entry_bb = llvm_builder.get_entry_basic_block(ll_fn); + dbg!(entry_bb); + let module_di = &self.module_di().unwrap(); let module_ctx = unsafe { LLVMGetModuleContext(*module_di) }; let meta_as_value = unsafe { LLVMMetadataAsValue(module_ctx, function) }; @@ -967,9 +1013,98 @@ impl<'up> DIBuilder<'up> { } } + pub fn create_instruction<'a>( + &self, + bc: &'a Bytecode, + func_ctx: &'a FunctionContext<'_, '_>, + ) -> Option> { + if let Some(_di_builder_core) = &self.0 { + let instr = Instruction::new(bc, func_ctx); + instr.debug(); + return Some(instr); + } + None + } + + pub fn create_load_store( + &self, + instr: Option, + load: *mut LLVMValue, + store: *mut LLVMValue, + mty: &mty::Type, + ) { + if let Some(_di_builder_core) = &self.0 { + if let Some(instruction) = instr { + instruction.create_load_store(load, store, mty); + } + } + } + pub fn finalize(&self) { if let Some(x) = &self.0 { unsafe { LLVMDIBuilderFinalize(x.builder_ref) }; } } } + +fn loc_display(loc: &move_model::model::Loc, env: &GlobalEnv) -> (String, u32, u32, u32, u32) { + if let Some((fname, pos)) = env.get_file_and_location(loc) { + ( + fname, + (pos.line + codespan::LineOffset(1)).0, + (pos.column + codespan::ColumnOffset(1)).0, + loc.span().start().0, + loc.span().end().0, + ) + } else { + ("unknown source".to_string(), 0, 0, 0, 0) + } +} + +fn find_line_column(file_path: &str, find_offset: usize) -> Result<(i32, usize)> { + // Open the file + let file = File::open(file_path)?; + let reader = BufReader::new(file); + + // Read the file line by line + let mut current_offset = 0; + let mut line_number = 1; + + for line in reader.lines() { + let line = line?; + let line_length = line.len() + 1; // Add 1 for the newline character + + if current_offset + line_length > find_offset { + // Found the line where the substring starts + let start_column = find_offset - current_offset; + debug!(target: "bytecode", "{:#?} offset {find_offset} @ {line_number}:{start_column}", file_path); + return Ok((line_number, start_column)); + } + + // Move to the next line + current_offset += line_length; + line_number += 1; + } + + // Substring spans multiple lines. Unable to determine end position. + let err_msg = "Substring spans multiple lines. Unable to determine end position."; + Err(anyhow::anyhow!(err_msg)).with_context(|| format!("Error processing file: {}", file_path)) +} + +// for debugging: for a given ascii file returns substring in the range file[begin, end] +fn read_substring(file_path: &str, begin: usize, end: usize) -> anyhow::Result { + // Open the file + let mut file = File::open(file_path)?; + + // Seek to the beginning of the substring + file.seek(SeekFrom::Start(begin as u64))?; + + // Read the substring into a buffer + let mut buffer = Vec::with_capacity(end - begin); + file.take((end - begin) as u64).read_to_end(&mut buffer)?; + + // Convert the buffer to a String + let substring = String::from_utf8(buffer)?; + + Ok(substring) +} diff --git a/language/solana/move-to-solana/src/stackless/llvm.rs b/language/solana/move-to-solana/src/stackless/llvm.rs index 0d7c2dda9e..74919dbded 100644 --- a/language/solana/move-to-solana/src/stackless/llvm.rs +++ b/language/solana/move-to-solana/src/stackless/llvm.rs @@ -527,10 +527,16 @@ impl Builder { } /// Load an alloca and store in another. - pub fn load_store(&self, ty: Type, src: Alloca, dst: Alloca) { + pub fn load_store( + &self, + ty: Type, + src: Alloca, + dst: Alloca, + ) -> (*mut LLVMValue, *mut LLVMValue) { unsafe { let tmp_reg = LLVMBuildLoad2(self.0, ty.0, src.0, "load_store_tmp".cstr()); - LLVMBuildStore(self.0, tmp_reg, dst.0); + let store = LLVMBuildStore(self.0, tmp_reg, dst.0); + (tmp_reg, store) } } @@ -1227,6 +1233,9 @@ impl BasicBlock { pub fn get_basic_block_parent(&self) -> Function { unsafe { Function(LLVMGetBasicBlockParent(self.0)) } } + pub fn get_basic_block_ref(&self) -> &LLVMBasicBlockRef { + &self.0 + } } #[derive(Copy, Clone, Debug)] diff --git a/language/solana/move-to-solana/src/stackless/translate.rs b/language/solana/move-to-solana/src/stackless/translate.rs index 2ca12f20f3..61d03b543f 100644 --- a/language/solana/move-to-solana/src/stackless/translate.rs +++ b/language/solana/move-to-solana/src/stackless/translate.rs @@ -41,7 +41,11 @@ use codespan::Location; use llvm_sys::core::LLVMGetModuleContext; use log::debug; use move_core_types::{account_address, u256::U256, vm_status::StatusCode::ARITHMETIC_ERROR}; -use move_model::{ast as mast, model as mm, ty as mty}; +use move_model::{ + ast as mast, + model::{self as mm}, + ty as mty, +}; use move_stackless_bytecode::{ function_target::FunctionData, stackless_bytecode as sbc, stackless_bytecode_generator::StacklessBytecodeGenerator, @@ -221,7 +225,10 @@ impl<'mm, 'up> FunctionContext<'mm, 'up> { let map_node_to_type: BTreeMap = g_env .get_nodes() .iter() - .map(|nd| (*nd, g_env.get_node_type(*nd))) + .map(|nd| { + debug!(target: "nodes", "{:#?} {:#?}", nd, g_env.get_node_loc(*nd)); + (*nd, g_env.get_node_type(*nd)) + }) .collect(); debug!(target: "nodes", "\n{:#?}", &map_node_to_type); @@ -354,12 +361,16 @@ impl<'mm, 'up> FunctionContext<'mm, 'up> { fn translate_instruction(&mut self, instr: &sbc::Bytecode) { let builder = &self.module_cx.llvm_builder; + let builder_di = &self.module_cx.llvm_di_builder; + let instr_dbg = builder_di.create_instruction(instr, self); + match instr { sbc::Bytecode::Assign(_, dst, src, sbc::AssignKind::Move) => { let mty = &self.locals[*dst].mty; let llty = self.locals[*dst].llty; let dst_llval = self.locals[*dst].llval; let src_llval = self.locals[*src].llval; + match mty { mty::Type::Primitive( mty::PrimitiveType::Bool @@ -370,7 +381,8 @@ impl<'mm, 'up> FunctionContext<'mm, 'up> { | mty::PrimitiveType::U128 | mty::PrimitiveType::U256, ) => { - builder.load_store(llty, src_llval, dst_llval); + let (load, store) = builder.load_store(llty, src_llval, dst_llval); + builder_di.create_load_store(instr_dbg, load, store, mty); } mty::Type::Reference(_, _) => { builder.load_store(llty, src_llval, dst_llval);