Repository: lambdaclass/cairo_native
Pull request #1174: Refactor DebugUtils with support for both AOT and JIT executors
Author: login: JulianGCalderon
Created: 2025-04-10T20:50:27Z | Merged: 2025-04-25T14:45:41Z

Debug utils (prints, breakpoints) used to work only for JIT executors. This is because the implementation of this functions was actually in Rust, and we loaded the symbols directly in the JIT engine.

This PR refactors the debug utils to work both for AOT and JIT executors. The approach taken is similar (identical) to how we manage the Runtime. See: https://github.com/lambdaclass/cairo_native/pull/1051

TLDR: We declare globals for each rust function we intend to call, and when loading a contract we set the global values to point to the address of the rust function.



Diff:
diff --git a/src/executor/aot.rs b/src/executor/aot.rs
index ab16c33ae2..f88ea582bc 100644
--- a/src/executor/aot.rs
+++ b/src/executor/aot.rs
@@ -54,6 +54,9 @@ impl AotNativeExecutor {
 
         setup_runtime(|name| executor.find_symbol_ptr(name));
 
+        #[cfg(feature = "with-debug-utils")]
+        crate::metadata::debug_utils::setup_runtime(|name| executor.find_symbol_ptr(name));
+
         executor
     }
 
diff --git a/src/executor/contract.rs b/src/executor/contract.rs
index a46bf35998..61ec5e155d 100644
--- a/src/executor/contract.rs
+++ b/src/executor/contract.rs
@@ -279,6 +279,9 @@ impl AotContractExecutor {
 
         setup_runtime(|x| executor.find_symbol_ptr(x));
 
+        #[cfg(feature = "with-debug-utils")]
+        crate::metadata::debug_utils::setup_runtime(|name| executor.find_symbol_ptr(name));
+
         Ok(Some(executor))
     }
 
diff --git a/src/executor/jit.rs b/src/executor/jit.rs
index b9f059c371..13edfab1df 100644
--- a/src/executor/jit.rs
+++ b/src/executor/jit.rs
@@ -65,6 +65,9 @@ impl<'m> JitNativeExecutor<'m> {
 
         setup_runtime(|name| executor.find_symbol_ptr(name));
 
+        #[cfg(feature = "with-debug-utils")]
+        crate::metadata::debug_utils::setup_runtime(|name| executor.find_symbol_ptr(name));
+
         Ok(executor)
     }
 
diff --git a/src/metadata/debug_utils.rs b/src/metadata/debug_utils.rs
index d7fb3161a8..2872aef07c 100644
--- a/src/metadata/debug_utils.rs
+++ b/src/metadata/debug_utils.rs
@@ -89,25 +89,25 @@ use crate::{
 };
 use melior::{
     dialect::{
-        arith, func,
-        llvm::{self, r#type::pointer},
+        arith,
+        llvm::{self},
         ods,
     },
     ir::{
         attribute::{FlatSymbolRefAttribute, IntegerAttribute, StringAttribute, TypeAttribute},
         operation::OperationBuilder,
-        r#type::{FunctionType, IntegerType},
-        Block, BlockLike, Identifier, Location, Module, Region, Value,
+        r#type::IntegerType,
+        Attribute, Block, BlockLike, Location, Module, Region, Value,
     },
-    Context, ExecutionEngine,
+    Context,
 };
 use num_bigint::BigUint;
-use std::collections::HashSet;
+use std::{collections::HashSet, ffi::c_void};
 
 #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
 enum DebugBinding {
     BreakpointMarker,
-    DebugPrint,
+    PrintStr,
     PrintI1,
     PrintI8,
     PrintI32,
@@ -118,12 +118,88 @@ enum DebugBinding {
     DumpMemRegion,
 }
 
+impl DebugBinding {
+    const fn symbol(self) -> &'static str {
+        match self {
+            DebugBinding::BreakpointMarker => "cairo_native__debug__breakpoint_marker_impl",
+            DebugBinding::PrintStr => "cairo_native__debug__print_str_impl",
+            DebugBinding::PrintI1 => "cairo_native__debug__print_i1_impl",
+            DebugBinding::PrintI8 => "cairo_native__debug__print_i8_impl",
+            DebugBinding::PrintI32 => "cairo_native__debug__print_i32_impl",
+            DebugBinding::PrintI64 => "cairo_native__debug__print_i64_impl",
+            DebugBinding::PrintI128 => "cairo_native__debug__print_i128_impl",
+            DebugBinding::PrintPointer => "cairo_native__debug__print_pointer_impl",
+            DebugBinding::PrintFelt252 => "cairo_native__debug__print_felt252_impl",
+            DebugBinding::DumpMemRegion => "cairo_native__debug__dump_mem_region_impl",
+        }
+    }
+    const fn function_ptr(self) -> *const () {
+        match self {
+            DebugBinding::BreakpointMarker => breakpoint_marker_impl as *const (),
+            DebugBinding::PrintStr => print_str_impl as *const (),
+            DebugBinding::PrintI1 => print_i1_impl as *const (),
+            DebugBinding::PrintI8 => print_i8_impl as *const (),
+            DebugBinding::PrintI32 => print_i32_impl as *const (),
+            DebugBinding::PrintI64 => print_i64_impl as *const (),
+            DebugBinding::PrintI128 => print_i128_impl as *const (),
+            DebugBinding::PrintPointer => print_pointer_impl as *const (),
+            DebugBinding::PrintFelt252 => print_felt252_impl as *const (),
+            DebugBinding::DumpMemRegion => dump_mem_region_impl as *const (),
+        }
+    }
+}
+
 #[derive(Debug, Default)]
 pub struct DebugUtils {
     active_map: HashSet<DebugBinding>,
 }
 
 impl DebugUtils {
+    /// Register the global for the given binding, if not yet registered, and return
+    /// a pointer to the stored function.
+    ///
+    /// For the function to be available, `setup_runtime` must be called before running the module
+    fn build_function<'c, 'a>(
+        &mut self,
+        context: &'c Context,
+        module: &Module,
+        block: &'a Block<'c>,
+        location: Location<'c>,
+        binding: DebugBinding,
+    ) -> Result<Value<'c, 'a>> {
+        if self.active_map.insert(binding) {
+            module.body().append_operation(
+                ods::llvm::mlir_global(
+                    context,
+                    Region::new(),
+                    TypeAttribute::new(llvm::r#type::pointer(context, 0)),
+                    StringAttribute::new(context, binding.symbol()),
+                    Attribute::parse(context, "#llvm.linkage<weak>")
+                        .ok_or(Error::ParseAttributeError)?,
+                    location,
+                )
+                .into(),
+            );
+        }
+
+        let global_address = block.append_op_result(
+            ods::llvm::mlir_addressof(
+                context,
+                llvm::r#type::pointer(context, 0),
+                FlatSymbolRefAttribute::new(context, binding.symbol()),
+                location,
+            )
+            .into(),
+        )?;
+
+        block.load(
+            context,
+            location,
+            global_address,
+            llvm::r#type::pointer(context, 0),
+        )
+    }
+
     pub fn breakpoint_marker(
         &mut self,
         context: &Context,
@@ -131,31 +207,28 @@ impl DebugUtils {
         block: &Block,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::BreakpointMarker) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__breakpoint_marker"),
-                TypeAttribute::new(FunctionType::new(context, &[], &[]).into()),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                Location::unknown(context),
-            ));
-        }
-
-        block.append_operation(func::call(
+        let function = self.build_function(
             context,
-            FlatSymbolRefAttribute::new(context, "__debug__breakpoint_marker"),
-            &[],
-            &[],
+            module,
+            block,
             location,
-        ));
+            DebugBinding::BreakpointMarker,
+        )?;
+
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .build()?,
+        );
 
         Ok(())
     }
 
+    pub fn debug_breakpoint_trap(&self, block: &Block, location: Location) -> Result<()> {
+        block.append_operation(OperationBuilder::new("llvm.intr.debugtrap", location).build()?);
+        Ok(())
+    }
+
     /// Prints the given &str.
     pub fn debug_print(
         &mut self,
@@ -165,26 +238,8 @@ impl DebugUtils {
         message: &str,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::DebugPrint) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__debug_print_impl"),
-                TypeAttribute::new(
-                    FunctionType::new(
-                        context,
-                        &[pointer(context, 0), IntegerType::new(context, 64).into()],
-                        &[],
-                    )
-                    .into(),
-                ),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                Location::unknown(context),
-            ));
-        }
+        let function =
+            self.build_function(context, module, block, location, DebugBinding::PrintStr)?;
 
         let ty = llvm::r#type::array(
             IntegerType::new(context, 8).into(),
@@ -231,19 +286,13 @@ impl DebugUtils {
             .result(0)?
             .into();
 
-        block.append_operation(func::call(
-            context,
-            FlatSymbolRefAttribute::new(context, "__debug__debug_print_impl"),
-            &[ptr, len],
-            &[],
-            location,
-        ));
-
-        Ok(())
-    }
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .add_operands(&[ptr, len])
+                .build()?,
+        );
 
-    pub fn debug_breakpoint_trap(&self, block: &Block, location: Location) -> Result<()> {
-        block.append_operation(OperationBuilder::new("llvm.intr.debugtrap", location).build()?);
         Ok(())
     }
 
@@ -255,27 +304,15 @@ impl DebugUtils {
         value: Value,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::PrintPointer) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__print_pointer"),
-                TypeAttribute::new(FunctionType::new(context, &[pointer(context, 0)], &[]).into()),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                Location::unknown(context),
-            ));
-        }
-
-        block.append_operation(func::call(
-            context,
-            FlatSymbolRefAttribute::new(context, "__debug__print_pointer"),
-            &[value],
-            &[],
-            location,
-        ));
+        let function =
+            self.build_function(context, module, block, location, DebugBinding::PrintPointer)?;
+
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .add_operands(&[value])
+                .build()?,
+        );
 
         Ok(())
     }
@@ -288,29 +325,15 @@ impl DebugUtils {
         value: Value,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::PrintI1) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__print_i1"),
-                TypeAttribute::new(
-                    FunctionType::new(context, &[IntegerType::new(context, 1).into()], &[]).into(),
-                ),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                Location::unknown(context),
-            ));
-        }
-
-        block.append_operation(func::call(
-            context,
-            FlatSymbolRefAttribute::new(context, "__debug__print_i1"),
-            &[value],
-            &[],
-            location,
-        ));
+        let function =
+            self.build_function(context, module, block, location, DebugBinding::PrintI1)?;
+
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .add_operands(&[value])
+                .build()?,
+        );
 
         Ok(())
     }
@@ -323,31 +346,8 @@ impl DebugUtils {
         value: Value,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::PrintFelt252) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__print_felt252"),
-                TypeAttribute::new(
-                    FunctionType::new(
-                        context,
-                        &[
-                            IntegerType::new(context, 64).into(),
-                            IntegerType::new(context, 64).into(),
-                            IntegerType::new(context, 64).into(),
-                            IntegerType::new(context, 64).into(),
-                        ],
-                        &[],
-                    )
-                    .into(),
-                ),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                Location::unknown(context),
-            ));
-        }
+        let function =
+            self.build_function(context, module, block, location, DebugBinding::PrintFelt252)?;
 
         let k64 = block
             .append_operation(arith::constant(
@@ -403,13 +403,12 @@ impl DebugUtils {
             .result(0)?
             .into();
 
-        block.append_operation(func::call(
-            context,
-            FlatSymbolRefAttribute::new(context, "__debug__print_felt252"),
-            &[l0, l1, l2, l3],
-            &[],
-            location,
-        ));
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .add_operands(&[l0, l1, l2, l3])
+                .build()?,
+        );
 
         Ok(())
     }
@@ -422,29 +421,15 @@ impl DebugUtils {
         value: Value,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::PrintI8) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__print_i8"),
-                TypeAttribute::new(
-                    FunctionType::new(context, &[IntegerType::new(context, 8).into()], &[]).into(),
-                ),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                Location::unknown(context),
-            ));
-        }
-
-        block.append_operation(func::call(
-            context,
-            FlatSymbolRefAttribute::new(context, "__debug__print_i8"),
-            &[value],
-            &[],
-            location,
-        ));
+        let function =
+            self.build_function(context, module, block, location, DebugBinding::PrintI8)?;
+
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .add_operands(&[value])
+                .build()?,
+        );
 
         Ok(())
     }
@@ -457,29 +442,15 @@ impl DebugUtils {
         value: Value,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::PrintI32) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__print_i32"),
-                TypeAttribute::new(
-                    FunctionType::new(context, &[IntegerType::new(context, 32).into()], &[]).into(),
-                ),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                Location::unknown(context),
-            ));
-        }
-
-        block.append_operation(func::call(
-            context,
-            FlatSymbolRefAttribute::new(context, "__debug__print_i32"),
-            &[value],
-            &[],
-            location,
-        ));
+        let function =
+            self.build_function(context, module, block, location, DebugBinding::PrintI32)?;
+
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .add_operands(&[value])
+                .build()?,
+        );
 
         Ok(())
     }
@@ -492,29 +463,15 @@ impl DebugUtils {
         value: Value,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::PrintI64) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__print_i64"),
-                TypeAttribute::new(
-                    FunctionType::new(context, &[IntegerType::new(context, 64).into()], &[]).into(),
-                ),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                Location::unknown(context),
-            ));
-        }
-
-        block.append_operation(func::call(
-            context,
-            FlatSymbolRefAttribute::new(context, "__debug__print_i64"),
-            &[value],
-            &[],
-            location,
-        ));
+        let function =
+            self.build_function(context, module, block, location, DebugBinding::PrintI64)?;
+
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .add_operands(&[value])
+                .build()?,
+        );
 
         Ok(())
     }
@@ -527,29 +484,8 @@ impl DebugUtils {
         value: Value,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::PrintI128) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__print_i128"),
-                TypeAttribute::new(
-                    FunctionType::new(
-                        context,
-                        &[
-                            IntegerType::new(context, 64).into(),
-                            IntegerType::new(context, 64).into(),
-                        ],
-                        &[],
-                    )
-                    .into(),
-                ),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                Location::unknown(context),
-            ));
-        }
+        let function =
+            self.build_function(context, module, block, location, DebugBinding::PrintI128)?;
 
         let i64_ty = IntegerType::new(context, 64).into();
         let k64 = block
@@ -574,13 +510,12 @@ impl DebugUtils {
             .result(0)?
             .into();
 
-        block.append_operation(func::call(
-            context,
-            FlatSymbolRefAttribute::new(context, "__debug__print_i128"),
-            &[value_lo, value_hi],
-            &[],
-            location,
-        ));
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .add_operands(&[value_lo, value_hi])
+                .build()?,
+        );
 
         Ok(())
     }
@@ -597,131 +532,43 @@ impl DebugUtils {
         len: usize,
         location: Location,
     ) -> Result<()> {
-        if self.active_map.insert(DebugBinding::DumpMemRegion) {
-            module.body().append_operation(func::func(
-                context,
-                StringAttribute::new(context, "__debug__dump_mem"),
-                TypeAttribute::new(
-                    FunctionType::new(
-                        context,
-                        &[
-                            llvm::r#type::pointer(context, 0),
-                            IntegerType::new(context, 64).into(),
-                        ],
-                        &[],
-                    )
-                    .into(),
-                ),
-                Region::new(),
-                &[(
-                    Identifier::new(context, "sym_visibility"),
-                    StringAttribute::new(context, "private").into(),
-                )],
-                location,
-            ));
-        }
-
-        let len = block.const_int(context, location, len, 64)?;
-        block.append_operation(func::call(
+        let function = self.build_function(
             context,
-            FlatSymbolRefAttribute::new(context, "__debug__dump_mem"),
-            &[ptr, len],
-            &[],
+            module,
+            block,
             location,
-        ));
-
-        Ok(())
-    }
+            DebugBinding::DumpMemRegion,
+        )?;
 
-    pub fn register_impls(&self, engine: &ExecutionEngine) {
-        if self.active_map.contains(&DebugBinding::BreakpointMarker) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__breakpoint_marker",
-                    breakpoint_marker_impl as *const fn() -> () as *mut (),
-                );
-            }
-        }
-
-        if self.active_map.contains(&DebugBinding::DebugPrint) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__debug_print_impl",
-                    debug_print_impl as *const fn(*const std::ffi::c_char) -> () as *mut (),
-                );
-            }
-        }
-
-        if self.active_map.contains(&DebugBinding::PrintI1) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__print_i1",
-                    print_i1_impl as *const fn(bool) -> () as *mut (),
-                );
-            }
-        }
-
-        if self.active_map.contains(&DebugBinding::PrintI8) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__print_i8",
-                    print_i8_impl as *const fn(u8) -> () as *mut (),
-                );
-            }
-        }
-
-        if self.active_map.contains(&DebugBinding::PrintI32) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__print_i32",
-                    print_i32_impl as *const fn(u8) -> () as *mut (),
-                );
-            }
-        }
-
-        if self.active_map.contains(&DebugBinding::PrintI64) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__print_i64",
-                    print_i64_impl as *const fn(u8) -> () as *mut (),
-                );
-            }
-        }
-
-        if self.active_map.contains(&DebugBinding::PrintI128) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__print_i128",
-                    print_i128_impl as *const fn(u64, u64) -> () as *mut (),
-                );
-            }
-        }
+        let len = block.const_int(context, location, len, 64)?;
 
-        if self.active_map.contains(&DebugBinding::PrintPointer) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__print_pointer",
-                    print_pointer_impl as *const fn(*const ()) -> () as *mut (),
-                );
-            }
-        }
+        block.append_operation(
+            OperationBuilder::new("llvm.call", location)
+                .add_operands(&[function])
+                .add_operands(&[ptr, len])
+                .build()?,
+        );
 
-        if self.active_map.contains(&DebugBinding::PrintFelt252) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__print_felt252",
-                    print_felt252 as *const fn(u64, u64, u64, u64) -> () as *mut (),
-                );
-            }
-        }
+        Ok(())
+    }
+}
 
-        if self.active_map.contains(&DebugBinding::DumpMemRegion) {
-            unsafe {
-                engine.register_symbol(
-                    "__debug__dump_mem",
-                    dump_mem_impl as *const fn(*const (), u64) as *mut (),
-                );
-            }
+pub fn setup_runtime(find_symbol_ptr: impl Fn(&str) -> Option<*mut c_void>) {
+    for binding in [
+        DebugBinding::BreakpointMarker,
+        DebugBinding::PrintStr,
+        DebugBinding::PrintI1,
+        DebugBinding::PrintI8,
+        DebugBinding::PrintI32,
+        DebugBinding::PrintI64,
+        DebugBinding::PrintI128,
+        DebugBinding::PrintPointer,
+        DebugBinding::PrintFelt252,
+        DebugBinding::DumpMemRegion,
+    ] {
+        if let Some(global) = find_symbol_ptr(binding.symbol()) {
+            let global = global.cast::<*const ()>();
+            unsafe { *global = binding.function_ptr() };
         }
     }
 }
@@ -730,15 +577,15 @@ extern "C" fn breakpoint_marker_impl() {
     println!("[DEBUG] Breakpoint marker.");
 }
 
-extern "C" fn debug_print_impl(message: *const std::ffi::c_char, len: u64) {
+extern "C" fn print_str_impl(message: *const std::ffi::c_char, len: u64) {
     // llvm constant strings are not zero terminated
     let slice = unsafe { std::slice::from_raw_parts(message as *const u8, len as usize) };
     let message = std::str::from_utf8(slice);
 
     if let Ok(message) = message {
-        println!("[DEBUG] Message: {}", message);
+        println!("[DEBUG] {}", message);
     } else {
-        println!("[DEBUG] Message: {:?}", message);
+        println!("[DEBUG] {:?}", message);
     }
 }
 
@@ -767,7 +614,7 @@ extern "C" fn print_pointer_impl(value: *const ()) {
     println!("[DEBUG] {value:018x?}");
 }
 
-unsafe extern "C" fn dump_mem_impl(ptr: *const (), len: u64) {
+unsafe extern "C" fn dump_mem_region_impl(ptr: *const (), len: u64) {
     println!("[DEBUG] Memory dump at {ptr:?}:");
     for chunk in (0..len).step_by(8) {
         print!("  {:?}:", ptr.byte_add(chunk as usize));
@@ -778,7 +625,7 @@ unsafe extern "C" fn dump_mem_impl(ptr: *const (), len: u64) {
     }
 }
 
-extern "C" fn print_felt252(l0: u64, l1: u64, l2: u64, l3: u64) {
+extern "C" fn print_felt252_impl(l0: u64, l1: u64, l2: u64, l3: u64) {
     println!(
         "[DEBUG] {}",
         BigUint::from_bytes_le(
diff --git a/src/utils.rs b/src/utils.rs
index 35ffbe8260..ecfecf7a89 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -303,11 +303,6 @@ pub fn create_engine(
 ) -> ExecutionEngine {
     // Create the JIT engine.
     let engine = ExecutionEngine::new(module, opt_level.into(), &[], false);
-    #[cfg(feature = "with-debug-utils")]
-    _metadata
-        .get::<crate::metadata::debug_utils::DebugUtils>()
-        .unwrap()
-        .register_impls(&engine);
 
     #[cfg(feature = "with-mem-tracing")]
     self::mem_tracing::register_bindings(&engine);


Reviews:
user: login: FrancoGiachetta
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T21:14:12Z
user: login: FrancoGiachetta
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T13:51:29Z
user: login: gabrielbosio
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T22:01:19Z
user: login: JulianGCalderon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T17:39:26Z
user: login: gabrielbosio
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T14:00:06Z

Review comments:
diff_hunk: @@ -118,44 +118,117 @@ enum DebugBinding {
     DumpMemRegion,
 }
 
+impl DebugBinding {
+    const fn symbol(self) -> &'static str {
+        match self {
+            DebugBinding::BreakpointMarker => "cairo_native__debug__breakpoint_marker_impl",
path: src/metadata/debug_utils.rs
user: login: gabrielbosio
body: Does using `cairo_native__debug__breakpoint_marker_impl` instead of `__debug__breakpoint_marker` improve legibility?
The same for the rest of the enum variants
created_at: 2025-04-23T22:01:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,44 +118,117 @@ enum DebugBinding {
     DumpMemRegion,
 }
 
+impl DebugBinding {
+    const fn symbol(self) -> &'static str {
+        match self {
+            DebugBinding::BreakpointMarker => "cairo_native__debug__breakpoint_marker_impl",
path: src/metadata/debug_utils.rs
user: login: JulianGCalderon
body: The reason I renamed it is to keep consistency with the other runtime symbols we define:

https://github.com/lambdaclass/cairo_native/blob/9815958c6eb70dbaaabffd4df1de6920c8dc0b39/src/metadata/runtime_bindings.rs#L62-L66

The name is purely internal, and not referenced in any other place. As long as the chosen name doesn't clash with any other symbol, then there should be no problem.

I think that by prefixing the symbol name with `cairo_native`, we ensure that the name will not clash with any other.
created_at: 2025-04-24T17:39:26Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-10T21:17:05Z
body: ## **Benchmark results Main vs HEAD.**
### Base

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `base dict_insert.cairo (JIT)` | 4.806 ± 0.036 | 4.759 | 4.881 | 1.00 |
| `base dict_insert.cairo (AOT)` | 4.840 ± 0.088 | 4.733 | 5.030 | 1.01 ± 0.02 |

### Head

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `head dict_insert.cairo (JIT)` | 4.804 ± 0.015 | 4.771 | 4.820 | 1.02 ± 0.01 |
| `head dict_insert.cairo (AOT)` | 4.729 ± 0.038 | 4.689 | 4.823 | 1.00 |

### Base

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `base dict_snapshot.cairo (JIT)` | 4.933 ± 0.070 | 4.811 | 5.027 | 1.05 ± 0.03 |
| `base dict_snapshot.cairo (AOT)` | 4.720 ± 0.105 | 4.574 | 4.869 | 1.00 |

### Head

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `head dict_snapshot.cairo (JIT)` | 4.721 ± 0.036 | 4.681 | 4.790 | 1.03 ± 0.01 |
| `head dict_snapshot.cairo (AOT)` | 4.590 ± 0.029 | 4.549 | 4.627 | 1.00 |

### Base

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `base factorial_2M.cairo (JIT)` | 5.117 ± 0.047 | 5.038 | 5.209 | 1.01 ± 0.01 |
| `base factorial_2M.cairo (AOT)` | 5.078 ± 0.049 | 5.021 | 5.158 | 1.00 |

### Head

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `head factorial_2M.cairo (JIT)` | 5.030 ± 0.026 | 4.978 | 5.073 | 1.00 ± 0.01 |
| `head factorial_2M.cairo (AOT)` | 5.011 ± 0.026 | 4.976 | 5.044 | 1.00 |

### Base

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `base fib_2M.cairo (JIT)` | 4.656 ± 0.028 | 4.597 | 4.697 | 1.02 ± 0.01 |
| `base fib_2M.cairo (AOT)` | 4.549 ± 0.035 | 4.491 | 4.631 | 1.00 |

### Head

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `head fib_2M.cairo (JIT)` | 4.582 ± 0.017 | 4.552 | 4.607 | 1.01 ± 0.01 |
| `head fib_2M.cairo (AOT)` | 4.531 ± 0.026 | 4.487 | 4.574 | 1.00 |

### Base

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `base linear_search.cairo (JIT)` | 4.756 ± 0.033 | 4.681 | 4.792 | 1.02 ± 0.01 |
| `base linear_search.cairo (AOT)` | 4.646 ± 0.029 | 4.608 | 4.697 | 1.00 |

### Head

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `head linear_search.cairo (JIT)` | 4.782 ± 0.027 | 4.751 | 4.847 | 1.03 ± 0.01 |
| `head linear_search.cairo (AOT)` | 4.652 ± 0.053 | 4.610 | 4.794 | 1.00 |

### Base

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `base logistic_map.cairo (JIT)` | 4.880 ± 0.055 | 4.813 | 4.964 | 1.05 ± 0.01 |
| `base logistic_map.cairo (AOT)` | 4.657 ± 0.022 | 4.625 | 4.689 | 1.00 |

### Head

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `head logistic_map.cairo (JIT)` | 4.859 ± 0.040 | 4.795 | 4.945 | 1.03 ± 0.01 |
| `head logistic_map.cairo (AOT)` | 4.712 ± 0.021 | 4.676 | 4.741 | 1.00 |


author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-10T21:21:38Z
body: # Benchmarking results
## Benchmark for program `dict_insert`

<details><summary>Open benchmarks</summary>

<br>

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `Cairo-vm (Rust, Cairo 1)` | 13.069 ± 0.060 | 12.984 | 13.154 | 2.74 ± 0.02 |
| `cairo-native (embedded AOT)` | 4.762 ± 0.030 | 4.713 | 4.809 | 1.00 |
| `cairo-native (embedded JIT using LLVM's ORC Engine)` | 4.833 ± 0.008 | 4.823 | 4.847 | 1.01 ± 0.01 |

</details>

## Benchmark for program `dict_snapshot`

<details><summary>Open benchmarks</summary>

<br>

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `Cairo-vm (Rust, Cairo 1)` | 674.3 ± 6.7 | 666.7 | 688.5 | 1.00 |
| `cairo-native (embedded AOT)` | 4625.2 ± 34.4 | 4555.9 | 4661.9 | 6.86 ± 0.09 |
| `cairo-native (embedded JIT using LLVM's ORC Engine)` | 4705.5 ± 36.9 | 4634.2 | 4751.0 | 6.98 ± 0.09 |

</details>

## Benchmark for program `factorial_2M`

<details><summary>Open benchmarks</summary>

<br>

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `Cairo-vm (Rust, Cairo 1)` | 5.590 ± 0.054 | 5.520 | 5.689 | 1.11 ± 0.01 |
| `cairo-native (embedded AOT)` | 5.026 ± 0.033 | 4.986 | 5.090 | 1.00 |
| `cairo-native (embedded JIT using LLVM's ORC Engine)` | 5.051 ± 0.033 | 5.007 | 5.125 | 1.01 ± 0.01 |

</details>

## Benchmark for program `fib_2M`

<details><summary>Open benchmarks</summary>

<br>

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `Cairo-vm (Rust, Cairo 1)` | 5.488 ± 0.057 | 5.440 | 5.634 | 1.20 ± 0.01 |
| `cairo-native (embedded AOT)` | 4.558 ± 0.021 | 4.516 | 4.594 | 1.00 |
| `cairo-native (embedded JIT using LLVM's ORC Engine)` | 4.606 ± 0.043 | 4.568 | 4.712 | 1.01 ± 0.01 |

</details>

## Benchmark for program `linear_search`

<details><summary>Open benchmarks</summary>

<br>

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `Cairo-vm (Rust, Cairo 1)` | 690.3 ± 4.9 | 684.1 | 698.9 | 1.00 |
| `cairo-native (embedded AOT)` | 4677.6 ± 24.9 | 4640.2 | 4728.9 | 6.78 ± 0.06 |
| `cairo-native (embedded JIT using LLVM's ORC Engine)` | 4815.1 ± 21.9 | 4787.9 | 4847.2 | 6.98 ± 0.06 |

</details>

## Benchmark for program `logistic_map`

<details><summary>Open benchmarks</summary>

<br>

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `Cairo-vm (Rust, Cairo 1)` | 430.3 ± 2.1 | 427.2 | 434.6 | 1.00 |
| `cairo-native (embedded AOT)` | 4731.3 ± 45.2 | 4676.0 | 4829.6 | 11.00 ± 0.12 |
| `cairo-native (embedded JIT using LLVM's ORC Engine)` | 4901.2 ± 19.9 | 4850.9 | 4921.5 | 11.39 ± 0.07 |

</details>


author_association: NONE
user: login: codecov-commenter
created_at: 2025-04-10T21:28:33Z
body: ## [Codecov](https://app.codecov.io/gh/lambdaclass/cairo_native/pull/1174?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lambdaclass) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 80.34%. Comparing base [(`9815958`)](https://app.codecov.io/gh/lambdaclass/cairo_native/commit/9815958c6eb70dbaaabffd4df1de6920c8dc0b39?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lambdaclass) to head [(`a3c3744`)](https://app.codecov.io/gh/lambdaclass/cairo_native/commit/a3c37448bae6e23cb181228f16bccc9c5ffc3b48?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lambdaclass).

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #1174   +/-   ##
=======================================
  Coverage   80.34%   80.34%           
=======================================
  Files         110      110           
  Lines       29554    29558    +4     
=======================================
+ Hits        23745    23749    +4     
  Misses       5809     5809           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/lambdaclass/cairo_native/pull/1174?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lambdaclass).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lambdaclass).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE