Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[llvm-context,solidity] Generate debug-info location information. #43

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/llvm-context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub use self::polkavm::context::argument::Argument as PolkaVMArgument;
pub use self::polkavm::context::attribute::Attribute as PolkaVMAttribute;
pub use self::polkavm::context::build::Build as PolkaVMBuild;
pub use self::polkavm::context::code_type::CodeType as PolkaVMCodeType;
pub use self::polkavm::context::debug_info::DebugInfo;
pub use self::polkavm::context::evmla_data::EVMLAData as PolkaVMContextEVMLAData;
pub use self::polkavm::context::function::block::evmla_data::key::Key as PolkaVMFunctionBlockKey;
pub use self::polkavm::context::function::block::evmla_data::EVMLAData as PolkaVMFunctionBlockEVMLAData;
Expand Down
16 changes: 15 additions & 1 deletion crates/llvm-context/src/optimizer/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use itertools::Itertools;

use self::size_level::SizeLevel;

/// The LLVM optimizer settings.
/// The LLVM optimizer and code-gen settings.
#[derive(Debug, Serialize, Deserialize, Clone, Eq)]
pub struct Settings {
/// The middle-end optimization level.
Expand All @@ -28,6 +28,9 @@ pub struct Settings {
pub is_verify_each_enabled: bool,
/// Whether the LLVM `debug logging` option is enabled.
pub is_debug_logging_enabled: bool,

/// Whether to generate source-level debug information.
pub emit_debug_info: bool,
}

impl Settings {
Expand All @@ -47,6 +50,8 @@ impl Settings {

is_verify_each_enabled: false,
is_debug_logging_enabled: false,

emit_debug_info: false,
}
}

Expand All @@ -58,6 +63,8 @@ impl Settings {

is_verify_each_enabled: bool,
is_debug_logging_enabled: bool,

emit_debug_info: bool,
) -> Self {
Self {
level_middle_end,
Expand All @@ -69,6 +76,8 @@ impl Settings {

is_verify_each_enabled,
is_debug_logging_enabled,

emit_debug_info,
}
}

Expand Down Expand Up @@ -220,6 +229,11 @@ impl Settings {
pub fn is_system_request_memoization_disabled(&self) -> bool {
self.is_system_request_memoization_disabled
}

/// Whether source-level debug information should be emitted.
pub fn emit_debug_info(&self) -> bool {
return self.emit_debug_info;
}
}

impl PartialEq for Settings {
Expand Down
199 changes: 163 additions & 36 deletions crates/llvm-context/src/polkavm/context/debug_info.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,54 @@
//! The LLVM debug information.

use std::cell::RefCell;

use inkwell::debug_info::AsDIScope;
use num::Zero;
use inkwell::debug_info::DIScope;

/// Debug info scope stack
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ScopeStack<'ctx> {
stack: Vec<DIScope<'ctx>>,
}

// 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<DIScope<'ctx>> {
self.stack.last().map(|rc| rc.clone())
}

/// 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<DIScope<'ctx>> {
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<ScopeStack<'ctx>>,
// Names of enclosing objects, functions and other namespaces.
namespace_stack: RefCell<Vec<String>>,
}

impl<'ctx> DebugInfo<'ctx> {
Expand All @@ -35,63 +75,150 @@ impl<'ctx> DebugInfo<'ctx> {
Self {
compile_unit,
builder,
scope_stack: RefCell::new(ScopeStack::from(compile_unit.as_debug_info_scope())),
namespace_stack: RefCell::new(vec![]),
}
}

/// Creates a function info.
/// 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,
);
}

/// Create debug-info for a function.
pub fn create_function(
&self,
scope: inkwell::debug_info::DIScope<'ctx>,
name: &str,
linkage_name: Option<&str>,
return_type: Option<inkwell::debug_info::DIType<'ctx>>,
parameter_types: &[inkwell::debug_info::DIType<'ctx>],
file: inkwell::debug_info::DIFile<'ctx>,
line_num: u32,
is_definition: bool,
is_local: bool,
is_optimized: bool,
flags: Option<inkwell::debug_info::DIFlags>,
) -> anyhow::Result<inkwell::debug_info::DISubprogram<'ctx>> {
let subroutine_type = self.builder.create_subroutine_type(
self.compile_unit.get_file(),
Some(self.create_type(revive_common::BIT_LENGTH_FIELD)?),
&[],
inkwell::debug_info::DIFlags::zero(),
);
let di_flags = flags.unwrap_or(inkwell::debug_info::DIFlagsConstants::ZERO);
let ret_type = return_type.unwrap_or(self.create_word_type(flags)?.as_type());
let subroutine_type =
self.builder
.create_subroutine_type(file, Some(ret_type), parameter_types, di_flags);

let function = self.builder.create_function(
self.compile_unit.get_file().as_debug_info_scope(),
scope,
name,
None,
self.compile_unit.get_file(),
42,
linkage_name,
file,
line_num,
subroutine_type,
true,
false,
1,
inkwell::debug_info::DIFlags::zero(),
false,
);

self.builder.create_lexical_block(
function.as_debug_info_scope(),
self.compile_unit.get_file(),
1,
is_local,
is_definition,
1,
di_flags,
is_optimized,
);

Ok(function)
}

/// Creates a primitive type info.
pub fn create_type(
/// Creates primitive integer type debug-info.
pub fn create_primitive_type(
&self,
bit_length: usize,
) -> anyhow::Result<inkwell::debug_info::DIType<'ctx>> {
flags: Option<inkwell::debug_info::DIFlags>,
) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> {
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(
"U256",
bit_length as u64,
0,
inkwell::debug_info::DIFlags::zero(),
)
.map(|basic_type| basic_type.as_type())
.create_basic_type(type_name.as_str(), bit_length as u64, di_encoding, di_flags)
.map_err(|error| anyhow::anyhow!("Debug info error: {}", error))
}

/// Finalizes the builder.
pub fn finalize(&self) {
self.builder.finalize();
/// Returns the debug-info model of word-sized integer types.
pub fn create_word_type(
&self,
flags: Option<inkwell::debug_info::DIFlags>,
) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> {
self.create_primitive_type(revive_common::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<DIScope<'ctx>> {
self.scope_stack.borrow_mut().pop()
}

/// Return the top of the debug-info scope stack.
pub fn top_scope(&self) -> Option<DIScope<'ctx>> {
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()
}

/// Push a name onto the namespace stack.
pub fn push_namespace(&self, name: String) -> () {
self.namespace_stack.borrow_mut().push(name);
}

/// Pop the top name off the namespace stack and return it.
pub fn pop_namespace(&self) -> Option<String> {
self.namespace_stack.borrow_mut().pop()
}

/// Return the top of the namespace stack.
pub fn top_namespace(&self) -> Option<String> {
self.namespace_stack.borrow().last().map(|rc| rc.clone())
}

// Get a string representation of the namespace stack. Optionally append the given name.
pub fn namespace_as_identifier(&self, name: Option<&str>) -> String {
let separator = "::";
let mut ret = String::new();
let mut sep = false;
for s in self.namespace_stack.borrow().iter() {
if sep {
ret.push_str(separator);
};
sep = true;
ret.push_str(s)
}
if let Some(n) = name {
if sep {
ret.push_str(separator);
};
ret.push_str(n);
}
ret
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ impl<'ctx> Declaration<'ctx> {
) -> Self {
Self { r#type, value }
}

pub fn function_value(&self) -> inkwell::values::FunctionValue<'ctx> {
self.value
}
}
Loading