Browse Source

Add debug information to LLVM Instructions (#949)

Signed-off-by: thanhtoantnt <thanhtoantnt@gmail.com>
Nguyen Thanh Toan 3 years ago
parent
commit
da03458023

+ 12 - 0
src/bin/solang.rs

@@ -180,6 +180,13 @@ fn main() {
                         .help("Enable math overflow checking")
                         .long("math-overflow")
                         .display_order(6),
+                )
+                .arg(
+                    Arg::new("GENERATEDEBUGINFORMATION")
+                        .help("Enable generating debug information for LLVM IR")
+                        .short('g')
+                        .long("generate-debug-info")
+                        .display_order(5),
                 ),
         )
         .subcommand(
@@ -345,6 +352,8 @@ fn compile(matches: &ArgMatches) {
 
     let math_overflow_check = matches.contains_id("MATHOVERFLOW");
 
+    let generate_debug_info = matches.contains_id("GENERATEDEBUGINFORMATION");
+
     let mut resolver = imports_arg(matches);
 
     let opt_level = match matches.get_one::<String>("OPT").unwrap().as_str() {
@@ -361,6 +370,7 @@ fn compile(matches: &ArgMatches) {
         strength_reduce: *matches.get_one::<bool>("STRENGTHREDUCE").unwrap(),
         vector_to_slice: *matches.get_one::<bool>("VECTORTOSLICE").unwrap(),
         math_overflow_check,
+        generate_debug_information: generate_debug_info,
         common_subexpression_elimination: *matches
             .get_one::<bool>("COMMONSUBEXPRESSIONELIMINATION")
             .unwrap(),
@@ -405,6 +415,7 @@ fn compile(matches: &ArgMatches) {
             "bundle.sol",
             opt_level.into(),
             math_overflow_check,
+            generate_debug_info,
         );
 
         if !save_intermediates(&binary, matches) {
@@ -586,6 +597,7 @@ fn process_file(
             &filename_string,
             opt.opt_level.into(),
             opt.math_overflow_check,
+            opt.generate_debug_information,
         );
 
         if save_intermediates(&binary, matches) {

+ 22 - 32
src/codegen/cfg.rs

@@ -459,6 +459,7 @@ impl ControlFlowGraph {
     /// The operands of the add/sub instruction are the temp variable, and +/- 1.
     pub fn modify_temp_array_length(
         &mut self,
+        loc: pt::Loc,
         minus: bool,      // If the function is called from pushMemory or popMemory
         array_pos: usize, // The res of array that push/pop is performed on
         vartab: &mut Vartable,
@@ -468,32 +469,24 @@ impl ControlFlowGraph {
             let to_add = self.array_lengths_temps[&array_pos];
             let add_expr = if minus {
                 Expression::Subtract(
-                    pt::Loc::Codegen,
+                    loc,
                     Type::Uint(32),
                     false,
-                    Box::new(Expression::Variable(
-                        pt::Loc::Codegen,
-                        Type::Uint(32),
-                        to_add,
-                    )),
+                    Box::new(Expression::Variable(loc, Type::Uint(32), to_add)),
                     Box::new(Expression::NumberLiteral(
-                        pt::Loc::Codegen,
+                        loc,
                         Type::Uint(32),
                         BigInt::one(),
                     )),
                 )
             } else {
                 Expression::Add(
-                    pt::Loc::Codegen,
+                    loc,
                     Type::Uint(32),
                     false,
-                    Box::new(Expression::Variable(
-                        pt::Loc::Codegen,
-                        Type::Uint(32),
-                        to_add,
-                    )),
+                    Box::new(Expression::Variable(loc, Type::Uint(32), to_add)),
                     Box::new(Expression::NumberLiteral(
-                        pt::Loc::Codegen,
+                        loc,
                         Type::Uint(32),
                         BigInt::one(),
                     )),
@@ -504,7 +497,7 @@ impl ControlFlowGraph {
             self.add(
                 vartab,
                 Instr::Set {
-                    loc: pt::Loc::Codegen,
+                    loc,
                     res: to_add,
                     expr: add_expr,
                 },
@@ -1602,6 +1595,10 @@ fn function_cfg(
         .map(|stmt| stmt.reachable())
         .unwrap_or(true)
     {
+        let loc = match func.body.last() {
+            Some(ins) => ins.loc(),
+            None => pt::Loc::Codegen,
+        };
         // add implicit return
         cfg.add(
             &mut vartab,
@@ -1610,13 +1607,7 @@ fn function_cfg(
                     .symtable
                     .returns
                     .iter()
-                    .map(|pos| {
-                        Expression::Variable(
-                            pt::Loc::Codegen,
-                            func.symtable.vars[pos].ty.clone(),
-                            *pos,
-                        )
-                    })
+                    .map(|pos| Expression::Variable(loc, func.symtable.vars[pos].ty.clone(), *pos))
                     .collect::<Vec<_>>(),
             },
         );
@@ -1711,7 +1702,7 @@ pub fn generate_modifier_dispatch(
             cfg.add(
                 &mut vartab,
                 Instr::Set {
-                    loc: pt::Loc::Codegen,
+                    loc: var.id.loc,
                     res: *pos,
                     expr: Expression::FunctionArg(var.id.loc, var.ty.clone(), i),
                 },
@@ -1734,7 +1725,7 @@ pub fn generate_modifier_dispatch(
             cfg.add(
                 &mut vartab,
                 Instr::Set {
-                    loc: pt::Loc::Codegen,
+                    loc: expr.loc(),
                     res: *pos,
                     expr,
                 },
@@ -1792,6 +1783,10 @@ pub fn generate_modifier_dispatch(
         .map(|stmt| stmt.reachable())
         .unwrap_or(true)
     {
+        let loc = match func.body.last() {
+            Some(ins) => ins.loc(),
+            None => pt::Loc::Codegen,
+        };
         // add implicit return
         cfg.add(
             &mut vartab,
@@ -1800,13 +1795,7 @@ pub fn generate_modifier_dispatch(
                     .symtable
                     .returns
                     .iter()
-                    .map(|pos| {
-                        Expression::Variable(
-                            pt::Loc::Codegen,
-                            func.symtable.vars[pos].ty.clone(),
-                            *pos,
-                        )
-                    })
+                    .map(|pos| Expression::Variable(loc, func.symtable.vars[pos].ty.clone(), *pos))
                     .collect::<Vec<_>>(),
             },
         );
@@ -1867,6 +1856,7 @@ impl Contract {
     /// Get the storage slot for a variable, possibly from base contract
     pub fn get_storage_slot(
         &self,
+        loc: pt::Loc,
         var_contract_no: usize,
         var_no: usize,
         ns: &Namespace,
@@ -1878,7 +1868,7 @@ impl Contract {
             .find(|l| l.contract_no == var_contract_no && l.var_no == var_no)
         {
             Expression::NumberLiteral(
-                pt::Loc::Codegen,
+                loc,
                 ty.unwrap_or_else(|| ns.storage_type()),
                 layout.slot.clone(),
             )

+ 19 - 18
src/codegen/expression.rs

@@ -40,9 +40,9 @@ pub fn expression(
     opt: &Options,
 ) -> Expression {
     match expr {
-        ast::Expression::StorageVariable(_, _, var_contract_no, var_no) => {
+        ast::Expression::StorageVariable(loc, _, var_contract_no, var_no) => {
             // base storage variables should precede contract variables, not overlap
-            ns.contracts[contract_no].get_storage_slot(*var_contract_no, *var_no, ns, None)
+            ns.contracts[contract_no].get_storage_slot(*loc, *var_contract_no, *var_no, ns, None)
         }
         ast::Expression::StorageLoad(loc, ty, expr) => {
             let storage = expression(expr, cfg, contract_no, func, ns, vartab, opt);
@@ -651,7 +651,7 @@ pub fn expression(
                         array: array_pos,
                     },
                 );
-                cfg.modify_temp_array_length(true, array_pos, vartab);
+                cfg.modify_temp_array_length(*loc, true, array_pos, vartab);
 
                 Expression::Variable(*loc, ty[0].clone(), address_res)
             }
@@ -795,7 +795,7 @@ fn memory_array_push(
             value: Box::new(value),
         },
     );
-    cfg.modify_temp_array_length(false, array_pos, vartab);
+    cfg.modify_temp_array_length(*loc, false, array_pos, vartab);
 
     Expression::Variable(*loc, ty.clone(), address_res)
 }
@@ -823,7 +823,7 @@ fn post_incdec(
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: v.loc(),
             res,
             expr: v,
         },
@@ -847,11 +847,11 @@ fn post_incdec(
         _ => unreachable!(),
     };
     match var {
-        ast::Expression::Variable(loc, _, pos) => {
+        ast::Expression::Variable(_, _, pos) => {
             cfg.add(
                 vartab,
                 Instr::Set {
-                    loc: *loc,
+                    loc: expr.loc(),
                     res: *pos,
                     expr,
                 },
@@ -863,7 +863,7 @@ fn post_incdec(
             cfg.add(
                 vartab,
                 Instr::Set {
-                    loc: pt::Loc::Codegen,
+                    loc: expr.loc(),
                     res,
                     expr,
                 },
@@ -929,7 +929,7 @@ fn pre_incdec(
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: expr.loc(),
             res,
             expr,
         },
@@ -1000,7 +1000,7 @@ fn expr_or(
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: *loc,
             res: pos,
             expr: Expression::BoolLiteral(*loc, true),
         },
@@ -1018,7 +1018,7 @@ fn expr_or(
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: r.loc(),
             res: pos,
             expr: r,
         },
@@ -1054,7 +1054,7 @@ fn and(
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: *loc,
             res: pos,
             expr: Expression::BoolLiteral(*loc, false),
         },
@@ -1072,7 +1072,7 @@ fn and(
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: r.loc(),
             res: pos,
             expr: r,
         },
@@ -1772,7 +1772,7 @@ fn checking_trunc(
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: expr.loc(),
             res: pos,
             expr,
         },
@@ -1873,7 +1873,7 @@ fn ternary(
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: expr.loc(),
             res: pos,
             expr,
         },
@@ -1888,7 +1888,7 @@ fn ternary(
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: expr.loc(),
             res: pos,
             expr,
         },
@@ -2669,12 +2669,13 @@ fn array_subscript(
         &coerced_ty,
     );
 
+    let expr = index.cast(&coerced_ty, ns);
     cfg.add(
         vartab,
         Instr::Set {
-            loc: pt::Loc::Codegen,
+            loc: expr.loc(),
             res: pos,
-            expr: index.cast(&coerced_ty, ns),
+            expr,
         },
     );
 

+ 4 - 0
src/codegen/mod.rs

@@ -84,6 +84,7 @@ pub struct Options {
     pub vector_to_slice: bool,
     pub math_overflow_check: bool,
     pub common_subexpression_elimination: bool,
+    pub generate_debug_information: bool,
     pub opt_level: OptimizationLevel,
 }
 
@@ -96,6 +97,7 @@ impl Default for Options {
             vector_to_slice: true,
             math_overflow_check: false,
             common_subexpression_elimination: true,
+            generate_debug_information: false,
             opt_level: OptimizationLevel::Default,
         }
     }
@@ -155,6 +157,7 @@ pub fn codegen(ns: &mut Namespace, opt: &Options) {
                         &filename,
                         opt.opt_level.into(),
                         opt.math_overflow_check,
+                        opt.generate_debug_information,
                     );
 
                     let code = binary.code(Generate::Linked).expect("llvm build");
@@ -251,6 +254,7 @@ fn storage_initializer(contract_no: usize, ns: &mut Namespace, opt: &Options) ->
 
         if let Some(init) = &var.initializer {
             let storage = ns.contracts[contract_no].get_storage_slot(
+                pt::Loc::Codegen,
                 layout.contract_no,
                 layout.var_no,
                 ns,

+ 1 - 1
src/codegen/statements.rs

@@ -1043,7 +1043,7 @@ fn cast_and_try_load(
             return casted_expr;
         }
 
-        let anonymous_no = vartab.temp_anonymous(&*ty);
+        let anonymous_no = vartab.temp_anonymous(&ty);
         cfg.add(
             vartab,
             Instr::LoadStorage {

+ 1 - 1
src/codegen/subexpression_elimination/available_expression_set.rs

@@ -273,7 +273,7 @@ impl AvailableExpressionSet {
 
     /// Remove from the set all children from a node
     fn kill_child(&mut self, child_node: &Rc<RefCell<BasicExpression>>, parent_id: &NodeId) {
-        self.kill_recursive(&*child_node.borrow(), parent_id);
+        self.kill_recursive(&child_node.borrow(), parent_id);
         child_node.borrow_mut().children.clear();
     }
 

+ 2 - 1
src/codegen/yul/expression.rs

@@ -104,8 +104,9 @@ fn process_suffix_access(
 ) -> Expression {
     match suffix {
         YulSuffix::Slot => match expr {
-            ast::YulExpression::StorageVariable(_, _, contract_no, var_no) => {
+            ast::YulExpression::StorageVariable(loc, _, contract_no, var_no) => {
                 return ns.contracts[*contract_no].get_storage_slot(
+                    *loc,
                     *contract_no,
                     *var_no,
                     ns,

+ 1 - 1
src/codegen/yul/tests/expression.rs

@@ -273,7 +273,7 @@ fn slot_suffix() {
     let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(
         res,
-        Expression::NumberLiteral(Loc::Codegen, Type::Uint(256), BigInt::from(2))
+        Expression::NumberLiteral(Loc::File(1, 2, 3), Type::Uint(256), BigInt::from(2))
     );
 
     let expr = ast::YulExpression::SuffixAccess(

+ 42 - 1
src/emit/binary.rs

@@ -16,6 +16,8 @@ use crate::linker::link;
 use crate::Target;
 use inkwell::builder::Builder;
 use inkwell::context::Context;
+use inkwell::debug_info::DICompileUnit;
+use inkwell::debug_info::DebugInfoBuilder;
 use inkwell::memory_buffer::MemoryBuffer;
 use inkwell::module::{Linkage, Module};
 use inkwell::passes::PassManager;
@@ -39,7 +41,10 @@ pub struct Binary<'a> {
     pub(crate) function_abort_value_transfers: bool,
     pub(crate) constructor_abort_value_transfers: bool,
     pub(crate) math_overflow_check: bool,
+    pub(crate) generate_debug_info: bool,
     pub builder: Builder<'a>,
+    pub dibuilder: DebugInfoBuilder<'a>,
+    pub compile_unit: DICompileUnit<'a>,
     pub(crate) context: &'a Context,
     pub(crate) functions: HashMap<usize, FunctionValue<'a>>,
     code: RefCell<Vec<u8>>,
@@ -63,6 +68,7 @@ impl<'a> Binary<'a> {
         filename: &'a str,
         opt: OptimizationLevel,
         math_overflow_check: bool,
+        generate_debug_info: bool,
     ) -> Self {
         let std_lib = load_stdlib(context, &ns.target);
         match ns.target {
@@ -74,6 +80,7 @@ impl<'a> Binary<'a> {
                 filename,
                 opt,
                 math_overflow_check,
+                generate_debug_info,
             ),
             Target::Ewasm => ewasm::EwasmTarget::build(
                 context,
@@ -83,6 +90,7 @@ impl<'a> Binary<'a> {
                 filename,
                 opt,
                 math_overflow_check,
+                generate_debug_info,
             ),
             Target::Solana => solana::SolanaTarget::build(
                 context,
@@ -92,6 +100,7 @@ impl<'a> Binary<'a> {
                 filename,
                 opt,
                 math_overflow_check,
+                generate_debug_info,
             ),
         }
     }
@@ -103,6 +112,7 @@ impl<'a> Binary<'a> {
         filename: &str,
         opt: OptimizationLevel,
         math_overflow_check: bool,
+        generate_debug_info: bool,
     ) -> Self {
         assert!(namespaces.iter().all(|ns| ns.target == Target::Solana));
 
@@ -114,6 +124,7 @@ impl<'a> Binary<'a> {
             filename,
             opt,
             math_overflow_check,
+            generate_debug_info,
         )
     }
 
@@ -249,6 +260,7 @@ impl<'a> Binary<'a> {
         math_overflow_check: bool,
         std_lib: &Module<'a>,
         runtime: Option<Box<Binary<'a>>>,
+        generate_debug_info: bool,
     ) -> Self {
         LLVM_INIT.get_or_init(|| {
             inkwell::targets::Target::initialize_webassembly(&Default::default());
@@ -258,6 +270,32 @@ impl<'a> Binary<'a> {
         let triple = target.llvm_target_triple();
         let module = context.create_module(name);
 
+        let debug_metadata_version = context.i32_type().const_int(3, false);
+        module.add_basic_value_flag(
+            "Debug Info Version",
+            inkwell::module::FlagBehavior::Warning,
+            debug_metadata_version,
+        );
+
+        let builder = context.create_builder();
+        let (dibuilder, compile_unit) = module.create_debug_info_builder(
+            true,
+            inkwell::debug_info::DWARFSourceLanguage::C,
+            filename,
+            ".",
+            "Solang",
+            false,
+            "",
+            0,
+            "",
+            inkwell::debug_info::DWARFEmissionKind::Full,
+            0,
+            false,
+            false,
+            "",
+            "",
+        );
+
         module.set_triple(&triple);
         module.set_source_file_name(filename);
 
@@ -308,7 +346,10 @@ impl<'a> Binary<'a> {
             function_abort_value_transfers: false,
             constructor_abort_value_transfers: false,
             math_overflow_check,
-            builder: context.create_builder(),
+            generate_debug_info,
+            builder,
+            dibuilder,
+            compile_unit,
             context,
             target,
             functions: HashMap::new(),

+ 4 - 0
src/emit/ewasm.rs

@@ -36,6 +36,7 @@ impl EwasmTarget {
         filename: &'a str,
         opt: OptimizationLevel,
         math_overflow_check: bool,
+        generate_debug_info: bool,
     ) -> Binary<'a> {
         // first emit runtime code
         let mut b = EwasmTarget {
@@ -50,6 +51,7 @@ impl EwasmTarget {
             math_overflow_check,
             std_lib,
             None,
+            generate_debug_info,
         );
 
         runtime_code.set_early_value_aborts(contract, ns);
@@ -80,6 +82,7 @@ impl EwasmTarget {
             math_overflow_check,
             std_lib,
             Some(Box::new(runtime_code)),
+            generate_debug_info,
         );
 
         deploy_code.set_early_value_aborts(contract, ns);
@@ -1309,6 +1312,7 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
             "",
             binary.opt,
             binary.math_overflow_check,
+            binary.generate_debug_info,
         );
 
         // wasm

+ 91 - 0
src/emit/mod.rs

@@ -7,6 +7,7 @@ use crate::sema::ast::{
     StructType, Type,
 };
 use solang_parser::pt;
+// use solang_parser::pt::Loc;
 use std::convert::TryFrom;
 use std::fmt;
 use std::str;
@@ -17,6 +18,9 @@ use num_traits::ToPrimitive;
 use std::collections::{HashMap, VecDeque};
 
 use crate::Target;
+use inkwell::debug_info::AsDIScope;
+use inkwell::debug_info::DISubprogram;
+use inkwell::debug_info::DIType;
 use inkwell::module::Linkage;
 use inkwell::targets::TargetTriple;
 use inkwell::types::{BasicType, IntType, StringRadix};
@@ -26,6 +30,7 @@ use inkwell::values::{
 };
 use inkwell::AddressSpace;
 use inkwell::IntPredicate;
+use solang_parser::pt::{CodeLocation, Loc};
 
 pub mod binary;
 mod ethabiencoder;
@@ -1233,6 +1238,7 @@ pub trait TargetRuntime<'a> {
                     "",
                     bin.opt,
                     bin.math_overflow_check,
+                    bin.generate_debug_info,
                 );
 
                 let code = if *runtime && target_bin.runtime.is_some() {
@@ -3182,6 +3188,75 @@ pub trait TargetRuntime<'a> {
         function: FunctionValue<'a>,
         ns: &Namespace,
     ) {
+        let dibuilder = &bin.dibuilder;
+        let compile_unit = &bin.compile_unit;
+        let file = compile_unit.get_file();
+        let mut di_func_scope: Option<DISubprogram<'_>> = None;
+
+        if bin.generate_debug_info {
+            let return_type = function.get_type().get_return_type();
+            match return_type {
+                None => {}
+                Some(return_type) => {
+                    let return_type_size = return_type.size_of().unwrap();
+                    let size = return_type_size.get_type().get_bit_width();
+                    let mut type_name = "size_".to_owned();
+                    type_name.push_str(&size.to_string());
+                    let di_flags = if cfg.public {
+                        inkwell::debug_info::DIFlagsConstants::PUBLIC
+                    } else {
+                        inkwell::debug_info::DIFlagsConstants::PRIVATE
+                    };
+
+                    let di_return_type = dibuilder
+                        .create_basic_type(&type_name, size as u64, 0x00, di_flags)
+                        .unwrap();
+                    let param_types = function.get_type().get_param_types();
+                    let di_param_types: Vec<DIType<'_>> = param_types
+                        .iter()
+                        .map(|typ| {
+                            let mut param_tname = "size_".to_owned();
+                            let param_size = typ.size_of().unwrap().get_type().get_bit_width();
+                            param_tname.push_str(&size.to_string());
+                            dibuilder
+                                .create_basic_type(&param_tname, param_size as u64, 0x00, di_flags)
+                                .unwrap()
+                                .as_type()
+                        })
+                        .collect();
+                    let di_func_type = dibuilder.create_subroutine_type(
+                        file,
+                        Some(di_return_type.as_type()),
+                        di_param_types.as_slice(),
+                        di_flags,
+                    );
+
+                    let func_loc = cfg.blocks[0].instr.first().unwrap().loc();
+                    let line_num = if let Loc::File(file_offset, offset, _) = func_loc {
+                        let (line, _) = ns.files[file_offset].offset_to_line_column(offset);
+                        line
+                    } else {
+                        0
+                    };
+
+                    di_func_scope = Some(dibuilder.create_function(
+                        compile_unit.as_debug_info_scope(),
+                        function.get_name().to_str().unwrap(),
+                        None,
+                        file,
+                        line_num.try_into().unwrap(),
+                        di_func_type,
+                        true,
+                        true,
+                        line_num.try_into().unwrap(),
+                        di_flags,
+                        false,
+                    ));
+                    function.set_subprogram(di_func_scope.unwrap());
+                }
+            }
+        }
+
         // recurse through basic blocks
         struct BasicBlock<'a> {
             bb: inkwell::basic_block::BasicBlock<'a>,
@@ -3297,6 +3372,21 @@ pub trait TargetRuntime<'a> {
             }
 
             for ins in &cfg.blocks[w.block_no].instr {
+                if bin.generate_debug_info {
+                    let debug_loc = ins.loc();
+                    if let Loc::File(file_offset, offset, _) = debug_loc {
+                        let (line, col) = ns.files[file_offset].offset_to_line_column(offset);
+                        let debug_loc = dibuilder.create_debug_location(
+                            bin.context,
+                            line as u32,
+                            col as u32,
+                            di_func_scope.unwrap().as_debug_info_scope(),
+                            None,
+                        );
+                        bin.builder
+                            .set_current_debug_location(bin.context, debug_loc);
+                    }
+                }
                 match ins {
                     Instr::Nop => (),
                     Instr::Return { value } if value.is_empty() => {
@@ -4514,6 +4604,7 @@ pub trait TargetRuntime<'a> {
                         }
                     }
                 }
+                bin.builder.unset_current_debug_location();
             }
         }
     }

+ 4 - 0
src/emit/solana.rs

@@ -46,6 +46,7 @@ impl SolanaTarget {
         filename: &'a str,
         opt: OptimizationLevel,
         math_overflow_check: bool,
+        generate_debug_info: bool,
     ) -> Binary<'a> {
         let mut target = SolanaTarget {
             abi: ethabiencoder::EthAbiDecoder { bswap: true },
@@ -61,6 +62,7 @@ impl SolanaTarget {
             math_overflow_check,
             std_lib,
             None,
+            generate_debug_info,
         );
 
         binary
@@ -123,6 +125,7 @@ impl SolanaTarget {
         filename: &str,
         opt: OptimizationLevel,
         math_overflow_check: bool,
+        generate_debug_info: bool,
     ) -> Binary<'a> {
         let mut target = SolanaTarget {
             abi: ethabiencoder::EthAbiDecoder { bswap: true },
@@ -138,6 +141,7 @@ impl SolanaTarget {
             math_overflow_check,
             std_lib,
             None,
+            generate_debug_info,
         );
 
         binary

+ 2 - 0
src/emit/substrate.rs

@@ -35,6 +35,7 @@ impl SubstrateTarget {
         filename: &'a str,
         opt: OptimizationLevel,
         math_overflow_check: bool,
+        generate_debug_info: bool,
     ) -> Binary<'a> {
         let mut binary = Binary::new(
             context,
@@ -45,6 +46,7 @@ impl SubstrateTarget {
             math_overflow_check,
             std_lib,
             None,
+            generate_debug_info,
         );
 
         binary.set_early_value_aborts(contract, ns);

+ 9 - 1
src/lib.rs

@@ -157,8 +157,16 @@ pub fn compile_many<'a>(
     filename: &str,
     opt: inkwell::OptimizationLevel,
     math_overflow_check: bool,
+    generate_debug_info: bool,
 ) -> emit::binary::Binary<'a> {
-    emit::binary::Binary::build_bundle(context, namespaces, filename, opt, math_overflow_check)
+    emit::binary::Binary::build_bundle(
+        context,
+        namespaces,
+        filename,
+        opt,
+        math_overflow_check,
+        generate_debug_info,
+    )
 }
 
 /// Parse and resolve the Solidity source code provided in src, for the target chain as specified in target.

+ 72 - 4
src/sema/ast.rs

@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use super::symtable::Symtable;
-use crate::codegen::cfg::ControlFlowGraph;
+use crate::codegen::cfg::{ControlFlowGraph, Instr};
 use crate::diagnostics::Diagnostics;
 use crate::sema::yul::ast::{InlineAssembly, YulFunction};
 use crate::sema::Recurse;
@@ -282,11 +282,11 @@ impl FunctionAttributes for Function {
     }
 
     fn get_parameters(&self) -> &Vec<Parameter> {
-        &*self.params
+        &self.params
     }
 
     fn get_returns(&self) -> &Vec<Parameter> {
-        &*self.returns
+        &self.returns
     }
 }
 
@@ -988,7 +988,75 @@ impl CodeLocation for Expression {
     }
 }
 
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+impl CodeLocation for Statement {
+    fn loc(&self) -> pt::Loc {
+        match self {
+            Statement::Block { loc, .. }
+            | Statement::VariableDecl(loc, ..)
+            | Statement::If(loc, ..)
+            | Statement::While(loc, ..)
+            | Statement::For { loc, .. }
+            | Statement::DoWhile(loc, ..)
+            | Statement::Expression(loc, ..)
+            | Statement::Delete(loc, ..)
+            | Statement::Destructure(loc, ..)
+            | Statement::Continue(loc, ..)
+            | Statement::Break(loc, ..)
+            | Statement::Return(loc, ..)
+            | Statement::Emit { loc, .. }
+            | Statement::TryCatch(loc, ..)
+            | Statement::Underscore(loc, ..) => *loc,
+            Statement::Assembly(..) => pt::Loc::Codegen,
+        }
+    }
+}
+
+impl CodeLocation for Instr {
+    fn loc(&self) -> pt::Loc {
+        match self {
+            Instr::Set { loc, expr, .. } => match loc {
+                pt::Loc::File(_, _, _) => *loc,
+                _ => expr.loc(),
+            },
+            Instr::Call { args, .. } if args.is_empty() => pt::Loc::Codegen,
+            Instr::Call { args, .. } => args[0].loc(),
+            Instr::Return { value } if value.is_empty() => pt::Loc::Codegen,
+            Instr::Return { value } => value[0].loc(),
+            Instr::EmitEvent { data, .. } if data.is_empty() => pt::Loc::Codegen,
+            Instr::EmitEvent { data, .. } => data[0].loc(),
+            Instr::BranchCond { cond, .. } => cond.loc(),
+            Instr::Store { dest, .. } => dest.loc(),
+            Instr::SetStorageBytes { storage, .. }
+            | Instr::PushStorage { storage, .. }
+            | Instr::PopStorage { storage, .. }
+            | Instr::LoadStorage { storage, .. }
+            | Instr::ClearStorage { storage, .. } => storage.loc(),
+            Instr::ExternalCall { value, .. } | Instr::SetStorage { value, .. } => value.loc(),
+            Instr::PushMemory { value, .. } => value.loc(),
+            Instr::Constructor { gas, .. } => gas.loc(),
+            Instr::ValueTransfer { address, .. } => address.loc(),
+            Instr::AbiDecode { data, .. } => data.loc(),
+            Instr::SelfDestruct { recipient } => recipient.loc(),
+            Instr::WriteBuffer { buf, .. } => buf.loc(),
+            Instr::Print { expr } => expr.loc(),
+            Instr::MemCopy {
+                source,
+                destination,
+                ..
+            } => match source.loc() {
+                pt::Loc::File(_, _, _) => source.loc(),
+                _ => destination.loc(),
+            },
+            Instr::Branch { .. }
+            | Instr::Unreachable
+            | Instr::Nop
+            | Instr::AssertFailure { .. }
+            | Instr::PopMemory { .. } => pt::Loc::Codegen,
+        }
+    }
+}
+
+#[derive(PartialEq, Clone, Copy, Debug, Eq)]
 pub enum FormatArg {
     StringLiteral,
     Default,

+ 10 - 1
src/sema/contracts.rs

@@ -57,8 +57,17 @@ impl ast::Contract {
         filename: &'a str,
         opt: inkwell::OptimizationLevel,
         math_overflow_check: bool,
+        generate_debug_info: bool,
     ) -> emit::binary::Binary {
-        emit::binary::Binary::build(context, self, ns, filename, opt, math_overflow_check)
+        emit::binary::Binary::build(
+            context,
+            self,
+            ns,
+            filename,
+            opt,
+            math_overflow_check,
+            generate_debug_info,
+        )
     }
 
     /// Selector for this contract. This is used by Solana contract bundle

+ 1 - 1
src/sema/statements.rs

@@ -702,7 +702,7 @@ fn statement(
 
             for offset in symtable.returns.iter() {
                 let elem = symtable.vars.get_mut(offset).unwrap();
-                (*elem).assigned = true;
+                elem.assigned = true;
             }
 
             res.push(Statement::Return(*loc, Some(expr)));

+ 2 - 2
src/sema/unused_variable.rs

@@ -15,7 +15,7 @@ pub fn assigned_variable(ns: &mut Namespace, exp: &Expression, symtable: &mut Sy
 
         Expression::Variable(_, _, offset) => {
             let var = symtable.vars.get_mut(offset).unwrap();
-            (*var).assigned = true;
+            var.assigned = true;
         }
 
         Expression::StructMember(_, _, str, _) => {
@@ -51,7 +51,7 @@ pub fn used_variable(ns: &mut Namespace, exp: &Expression, symtable: &mut Symtab
 
         Expression::Variable(_, _, offset) => {
             let var = symtable.vars.get_mut(offset).unwrap();
-            (*var).read = true;
+            var.read = true;
         }
 
         Expression::ConstantVariable(_, _, Some(contract_no), offset) => {

+ 2 - 2
src/sema/yul/ast.rs

@@ -138,11 +138,11 @@ impl FunctionAttributes for YulFunction {
     }
 
     fn get_parameters(&self) -> &Vec<Parameter> {
-        &*self.params
+        &self.params
     }
 
     fn get_returns(&self) -> &Vec<Parameter> {
-        &*self.returns
+        &self.returns
     }
 }
 

+ 2 - 2
src/sema/yul/unused_variable.rs

@@ -10,7 +10,7 @@ pub(crate) fn assigned_variable(ns: &mut Namespace, exp: &YulExpression, symtabl
         YulExpression::SolidityLocalVariable(_, _, _, var_no)
         | YulExpression::YulLocalVariable(_, _, var_no) => {
             let var = symtable.vars.get_mut(var_no).unwrap();
-            (*var).assigned = true;
+            var.assigned = true;
         }
 
         YulExpression::StorageVariable(_, _, contract_no, var_no) => {
@@ -30,7 +30,7 @@ pub(crate) fn used_variable(ns: &mut Namespace, exp: &YulExpression, symtable: &
         YulExpression::SolidityLocalVariable(_, _, _, var_no)
         | YulExpression::YulLocalVariable(_, _, var_no) => {
             let var = symtable.vars.get_mut(var_no).unwrap();
-            (*var).read = true;
+            var.read = true;
         }
 
         YulExpression::StorageVariable(_, _, contract_no, var_no) => {

+ 1 - 1
tests/codegen_testcases/solidity/borsh_encoding_simple_types.sol

@@ -247,4 +247,4 @@ contract EncodingTest {
         bytes memory b = abi.encode(fPtr, pr);
         return b;
     }
-}
+}

+ 1 - 1
tests/codegen_testcases/yul/expression.sol

@@ -188,4 +188,4 @@ contract testing {
             }
         }
     }
-}
+}

+ 2 - 0
tests/contract.rs

@@ -86,6 +86,7 @@ fn parse_file(path: PathBuf, target: Target) -> io::Result<()> {
                 &filename,
                 Default::default(),
                 false,
+                false,
             );
         } else {
             for contract in &ns.contracts {
@@ -97,6 +98,7 @@ fn parse_file(path: PathBuf, target: Target) -> io::Result<()> {
                         &filename,
                         Default::default(),
                         false,
+                        false,
                     );
                 }
             }

+ 1 - 0
tests/solana.rs

@@ -145,6 +145,7 @@ fn build_solidity(src: &str) -> VirtualMachine {
         "bundle.sol",
         inkwell::OptimizationLevel::Default,
         false,
+        false,
     );
 
     let code = binary

+ 1 - 0
tests/undefined_variable_detection.rs

@@ -19,6 +19,7 @@ fn parse_and_codegen(src: &'static str) -> Namespace {
         common_subexpression_elimination: false,
         opt_level: OptimizationLevel::Default,
         math_overflow_check: false,
+        generate_debug_information: false,
     };
 
     codegen(&mut ns, &opt);