diff --git a/src/arch.rs b/src/arch.rs new file mode 100644 index 000000000..cc46775ba --- /dev/null +++ b/src/arch.rs @@ -0,0 +1,179 @@ +use crate::{ + starknet::{ArrayAbi, U256}, + types::TypeBuilder, + values::JitValue, +}; +use bumpalo::Bump; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + starknet::{secp256::Secp256PointTypeConcrete, StarkNetTypeConcrete}, + }, + ids::ConcreteTypeId, + program_registry::ProgramRegistry, +}; +use std::ptr::NonNull; + +mod aarch64; +mod x86_64; + +/// Implemented by all supported argument types. +pub trait AbiArgument { + /// Serialize the argument into the buffer. This method should keep track of arch-dependent + /// stuff like register vs stack allocation. + fn to_bytes(&self, buffer: &mut Vec); +} + +/// A wrapper that implements `AbiArgument` for `JitValue`s. It contains all the required stuff to +/// serialize all possible `JitValue`s. +pub struct JitValueWithInfoWrapper<'a> { + pub value: &'a JitValue, + pub type_id: &'a ConcreteTypeId, + pub info: &'a CoreTypeConcrete, + + pub arena: &'a Bump, + pub registry: &'a ProgramRegistry, +} + +impl<'a> JitValueWithInfoWrapper<'a> { + fn map<'b>( + &'b self, + value: &'b JitValue, + type_id: &'b ConcreteTypeId, + ) -> JitValueWithInfoWrapper<'b> + where + 'b: 'a, + { + Self { + value, + type_id, + info: self.registry.get_type(type_id).unwrap(), + arena: self.arena, + registry: self.registry, + } + } +} + +impl<'a> AbiArgument for JitValueWithInfoWrapper<'a> { + fn to_bytes(&self, buffer: &mut Vec) { + match (self.value, self.info) { + ( + value, + CoreTypeConcrete::Box(info) + | CoreTypeConcrete::NonZero(info) + | CoreTypeConcrete::Nullable(info) + | CoreTypeConcrete::Snapshot(info), + ) => { + // TODO: Allocate and use to_jit(). + self.map(value, &info.ty).to_bytes(buffer) + } + + (JitValue::Array(_), CoreTypeConcrete::Array(_)) => { + // TODO: Assert that `info.ty` matches all the values' types. + + let abi_ptr = self + .value + .to_jit(self.arena, self.registry, self.type_id) + .unwrap(); + let abi = unsafe { abi_ptr.cast::>().as_ref() }; + + abi.ptr.to_bytes(buffer); + abi.since.to_bytes(buffer); + abi.until.to_bytes(buffer); + abi.capacity.to_bytes(buffer); + } + (JitValue::BoundedInt { .. }, CoreTypeConcrete::BoundedInt(_)) => todo!(), + (JitValue::Bytes31(value), CoreTypeConcrete::Bytes31(_)) => value.to_bytes(buffer), + (JitValue::EcPoint(x, y), CoreTypeConcrete::EcPoint(_)) => { + x.to_bytes(buffer); + y.to_bytes(buffer); + } + (JitValue::EcState(x, y, x0, y0), CoreTypeConcrete::EcState(_)) => { + x.to_bytes(buffer); + y.to_bytes(buffer); + x0.to_bytes(buffer); + y0.to_bytes(buffer); + } + (JitValue::Enum { tag, value, .. }, CoreTypeConcrete::Enum(info)) => { + if self.info.is_memory_allocated(self.registry) { + let abi_ptr = self + .value + .to_jit(self.arena, self.registry, self.type_id) + .unwrap(); + + let abi_ptr = unsafe { *abi_ptr.cast::>().as_ref() }; + abi_ptr.as_ptr().to_bytes(buffer); + } else { + match (info.variants.len().next_power_of_two().trailing_zeros() + 7) / 8 { + 0 => {} + _ => (*tag as u64).to_bytes(buffer), + } + + self.map(value, &info.variants[*tag]).to_bytes(buffer); + } + } + ( + JitValue::Felt252(value), + CoreTypeConcrete::Felt252(_) + | CoreTypeConcrete::StarkNet( + StarkNetTypeConcrete::ClassHash(_) + | StarkNetTypeConcrete::ContractAddress(_) + | StarkNetTypeConcrete::StorageAddress(_) + | StarkNetTypeConcrete::StorageBaseAddress(_), + ), + ) => value.to_bytes(buffer), + (JitValue::Felt252Dict { .. }, CoreTypeConcrete::Felt252Dict(_)) => { + #[cfg(not(feature = "with-runtime"))] + unimplemented!("enable the `with-runtime` feature to use felt252 dicts"); + + // TODO: Assert that `info.ty` matches all the values' types. + + self.value + .to_jit(self.arena, self.registry, self.type_id) + .unwrap() + .as_ptr() + .to_bytes(buffer) + } + ( + JitValue::Secp256K1Point { x, y }, + CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::Secp256Point( + Secp256PointTypeConcrete::K1(_), + )), + ) + | ( + JitValue::Secp256R1Point { x, y }, + CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::Secp256Point( + Secp256PointTypeConcrete::R1(_), + )), + ) => { + let x = U256 { lo: x.0, hi: x.1 }; + let y = U256 { lo: y.0, hi: y.1 }; + + x.to_bytes(buffer); + y.to_bytes(buffer); + } + (JitValue::Sint128(value), CoreTypeConcrete::Sint128(_)) => value.to_bytes(buffer), + (JitValue::Sint16(value), CoreTypeConcrete::Sint16(_)) => value.to_bytes(buffer), + (JitValue::Sint32(value), CoreTypeConcrete::Sint32(_)) => value.to_bytes(buffer), + (JitValue::Sint64(value), CoreTypeConcrete::Sint64(_)) => value.to_bytes(buffer), + (JitValue::Sint8(value), CoreTypeConcrete::Sint8(_)) => value.to_bytes(buffer), + (JitValue::Struct { fields, .. }, CoreTypeConcrete::Struct(info)) => { + fields + .iter() + .zip(&info.members) + .map(|(value, type_id)| self.map(value, type_id)) + .for_each(|wrapper| wrapper.to_bytes(buffer)); + } + (JitValue::Uint128(value), CoreTypeConcrete::Uint128(_)) => value.to_bytes(buffer), + (JitValue::Uint16(value), CoreTypeConcrete::Uint16(_)) => value.to_bytes(buffer), + (JitValue::Uint32(value), CoreTypeConcrete::Uint32(_)) => value.to_bytes(buffer), + (JitValue::Uint64(value), CoreTypeConcrete::Uint64(_)) => value.to_bytes(buffer), + (JitValue::Uint8(value), CoreTypeConcrete::Uint8(_)) => value.to_bytes(buffer), + _ => todo!( + "abi argument unimplemented for ({:?}, {:?})", + self.value, + self.type_id + ), + } + } +} diff --git a/src/arch/aarch64.rs b/src/arch/aarch64.rs new file mode 100644 index 000000000..c8b9f5827 --- /dev/null +++ b/src/arch/aarch64.rs @@ -0,0 +1,240 @@ +//! # Implementations of `AbiArgument` for the `aarch64` architecture. +//! +//! The aarch64 architecture uses 8 64-bit registers for arguments. This means that the first 64 +//! bytes of the buffer will go into registers while the rest will be on the stack. +//! +//! The values that span multiple registers may be split or moved into the stack completely in some +//! cases, having to pad a register. In those cases the amount of usable register space is reduced +//! to only 56 bytes. + +#![cfg(target_arch = "aarch64")] + +use super::AbiArgument; +use crate::{starknet::U256, utils::get_integer_layout}; +use num_traits::ToBytes; +use starknet_types_core::felt::Felt; + +fn align_to(buffer: &mut Vec, align: usize) { + buffer.resize(buffer.len().next_multiple_of(align), 0); +} + +impl AbiArgument for u8 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 64 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(8).align()); + buffer.push(*self); + } + } +} + +impl AbiArgument for i8 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 64 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(8).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for u16 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 64 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(16).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for i16 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 64 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(16).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for u32 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 64 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(32).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for i32 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 64 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(32).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for u64 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 64 { + buffer.extend_from_slice(&self.to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(64).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for i64 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 64 { + buffer.extend_from_slice(&self.to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(64).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for u128 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 56 { + buffer.extend_from_slice(&self.to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(128).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for i128 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 56 { + buffer.extend_from_slice(&self.to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(128).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for Felt { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 56 { + buffer.extend_from_slice(&self.to_bytes_le()); + } else { + align_to(buffer, get_integer_layout(252).align()); + buffer.extend_from_slice(&self.to_bytes_le()); + } + } +} + +impl AbiArgument for U256 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 56 { + buffer.extend_from_slice(&self.lo.to_le_bytes()); + buffer.extend_from_slice(&self.hi.to_le_bytes()); + } else { + align_to(buffer, get_integer_layout(256).align()); + buffer.extend_from_slice(&self.lo.to_le_bytes()); + buffer.extend_from_slice(&self.hi.to_le_bytes()); + } + } +} + +impl AbiArgument for [u8; 31] { + fn to_bytes(&self, buffer: &mut Vec) { + // The `bytes31` type is treated as a 248-bit integer, therefore it follows the same + // splitting rules as them. + if buffer.len() < 56 { + buffer.extend_from_slice(self); + buffer.push(0); + } else { + align_to(buffer, get_integer_layout(252).align()); + buffer.extend_from_slice(self); + buffer.push(0); + } + } +} + +impl AbiArgument for *const T { + fn to_bytes(&self, buffer: &mut Vec) { + ::to_bytes(&(*self as u64), buffer) + } +} + +impl AbiArgument for *mut T { + fn to_bytes(&self, buffer: &mut Vec) { + ::to_bytes(&(*self as u64), buffer) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn u128_stack_split() { + let mut buffer = vec![0; 56]; + u128::MAX.to_bytes(&mut buffer); + assert_eq!( + buffer, + [0; 64].into_iter().chain([0xFF; 16]).collect::>() + ); + } + + #[test] + fn felt_stack_split() { + // Only a single u64 spilled into the stack. + let mut buffer = vec![0; 40]; + Felt::from_hex("0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + .to_bytes(&mut buffer); + assert_eq!( + buffer, + [0; 40] + .into_iter() + .chain([0xFF; 31]) + .chain([0x00]) + .collect::>() + ); + + // Half the felt spilled into the stack. + let mut buffer = vec![0; 48]; + Felt::from_hex("0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + .to_bytes(&mut buffer); + assert_eq!( + buffer, + [0; 48] + .into_iter() + .chain([0xFF; 31]) + .chain([0x00]) + .collect::>() + ); + + // All the felt spilled into the stack (with padding). + let mut buffer = vec![0; 56]; + Felt::from_hex("0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + .to_bytes(&mut buffer); + assert_eq!( + buffer, + [0; 64] + .into_iter() + .chain([0xFF; 31]) + .chain([0x00]) + .collect::>() + ); + } +} diff --git a/src/arch/x86_64.rs b/src/arch/x86_64.rs new file mode 100644 index 000000000..3fab342f6 --- /dev/null +++ b/src/arch/x86_64.rs @@ -0,0 +1,240 @@ +//! # Implementations of `AbiArgument` for the `x86_64` architecture. +//! +//! The x86_64 architecture uses 6 64-bit registers for arguments. This means that the first 48 +//! bytes of the buffer will go into registers while the rest will be on the stack. +//! +//! The values that span multiple registers may be split or moved into the stack completely in some +//! cases, having to pad a register. In those cases the amount of usable register space is reduced +//! to only 40 bytes. + +#![cfg(target_arch = "x86_64")] + +use super::AbiArgument; +use crate::{starknet::U256, utils::get_integer_layout}; +use num_traits::ToBytes; +use starknet_types_core::felt::Felt; + +fn align_to(buffer: &mut Vec, align: usize) { + buffer.resize(buffer.len().next_multiple_of(align), 0); +} + +impl AbiArgument for u8 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 48 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(8).align()); + buffer.push(*self); + } + } +} + +impl AbiArgument for i8 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 48 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(8).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for u16 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 48 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(16).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for i16 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 48 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(16).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for u32 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 48 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(32).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for i32 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 48 { + buffer.extend_from_slice(&(*self as u64).to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(32).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for u64 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 48 { + buffer.extend_from_slice(&self.to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(64).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for i64 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 48 { + buffer.extend_from_slice(&self.to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(64).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for u128 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 40 { + buffer.extend_from_slice(&self.to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(128).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for i128 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 40 { + buffer.extend_from_slice(&self.to_ne_bytes()); + } else { + align_to(buffer, get_integer_layout(128).align()); + buffer.extend_from_slice(&self.to_ne_bytes()); + } + } +} + +impl AbiArgument for Felt { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 40 { + buffer.extend_from_slice(&self.to_bytes_le()); + } else { + align_to(buffer, get_integer_layout(252).align()); + buffer.extend_from_slice(&self.to_bytes_le()); + } + } +} + +impl AbiArgument for U256 { + fn to_bytes(&self, buffer: &mut Vec) { + if buffer.len() < 40 { + buffer.extend_from_slice(&self.lo.to_le_bytes()); + buffer.extend_from_slice(&self.hi.to_le_bytes()); + } else { + align_to(buffer, get_integer_layout(256).align()); + buffer.extend_from_slice(&self.lo.to_le_bytes()); + buffer.extend_from_slice(&self.hi.to_le_bytes()); + } + } +} + +impl AbiArgument for [u8; 31] { + fn to_bytes(&self, buffer: &mut Vec) { + // The `bytes31` type is treated as a 248-bit integer, therefore it follows the same + // splitting rules as them. + if buffer.len() < 40 { + buffer.extend_from_slice(self); + buffer.push(0); + } else { + align_to(buffer, get_integer_layout(252).align()); + buffer.extend_from_slice(self); + buffer.push(0); + } + } +} + +impl AbiArgument for *const T { + fn to_bytes(&self, buffer: &mut Vec) { + ::to_bytes(&(*self as u64), buffer) + } +} + +impl AbiArgument for *mut T { + fn to_bytes(&self, buffer: &mut Vec) { + ::to_bytes(&(*self as u64), buffer) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn u128_stack_split() { + let mut buffer = vec![0; 40]; + u128::MAX.to_bytes(&mut buffer); + assert_eq!( + buffer, + [0; 48].into_iter().chain([0xFF; 16]).collect::>() + ); + } + + #[test] + fn felt_stack_split() { + // Only a single u64 spilled into the stack. + let mut buffer = vec![0; 24]; + Felt::from_hex("0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + .to_bytes(&mut buffer); + assert_eq!( + buffer, + [0; 24] + .into_iter() + .chain([0xFF; 31]) + .chain([0x00]) + .collect::>() + ); + + // Half the felt spilled into the stack. + let mut buffer = vec![0; 32]; + Felt::from_hex("0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + .to_bytes(&mut buffer); + assert_eq!( + buffer, + [0; 32] + .into_iter() + .chain([0xFF; 31]) + .chain([0x00]) + .collect::>() + ); + + // All the felt spilled into the stack (with padding). + let mut buffer = vec![0; 40]; + Felt::from_hex("0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + .to_bytes(&mut buffer); + assert_eq!( + buffer, + [0; 48] + .into_iter() + .chain([0xFF; 31]) + .chain([0x00]) + .collect::>() + ); + } +} diff --git a/src/executor.rs b/src/executor.rs index 6c6284e16..ec966256a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,11 +5,11 @@ pub use self::{aot::AotNativeExecutor, jit::JitNativeExecutor}; use crate::{ + arch::{AbiArgument, JitValueWithInfoWrapper}, error::Error, execution_result::{BuiltinStats, ContractExecutionResult, ExecutionResult}, starknet::{handler::StarknetSyscallHandlerCallbacks, StarknetSyscallHandler}, types::TypeBuilder, - utils::get_integer_layout, values::JitValue, }; use bumpalo::Bump; @@ -20,14 +20,14 @@ use cairo_lang_sierra::{ }, ids::{ConcreteTypeId, FunctionId}, program::FunctionSignature, - program_registry::{ProgramRegistry, ProgramRegistryError}, + program_registry::ProgramRegistry, }; use libc::c_void; use starknet_types_core::felt::Felt; use std::{ alloc::Layout, arch::global_asm, - ptr::{addr_of_mut, null_mut, NonNull}, + ptr::{addr_of_mut, NonNull}, sync::Arc, }; @@ -152,7 +152,7 @@ fn invoke_dynamic( ) -> Result { tracing::info!("Invoking function with signature: {function_signature:?}."); let arena = Bump::new(); - let mut invoke_data = ArgumentMapper::new(&arena, registry); + let mut invoke_data = Vec::::new(); // Generate return pointer (if necessary). // @@ -185,10 +185,7 @@ fn invoke_dynamic( }); let return_ptr = arena.alloc_layout(layout).cast::<()>(); - invoke_data.push_aligned( - get_integer_layout(64).align(), - &[return_ptr.as_ptr() as u64], - ); + return_ptr.as_ptr().to_bytes(&mut invoke_data); Some(return_ptr) } else { @@ -219,34 +216,40 @@ fn invoke_dynamic( }) { // Process gas requirements and syscall handler. match registry.get_type(type_id).unwrap() { - CoreTypeConcrete::GasBuiltin(_) => invoke_data.push_aligned( - get_integer_layout(128).align(), - &[gas as u64, (gas >> 64) as u64], - ), + CoreTypeConcrete::GasBuiltin(_) => gas.to_bytes(&mut invoke_data), CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::System(_)) => { let syscall_handler = syscall_handler .as_mut() .expect("syscall handler is required"); - invoke_data.push_aligned( - get_integer_layout(64).align(), - &[syscall_handler as *mut _ as u64], - ); + (syscall_handler as *mut StarknetSyscallHandlerCallbacks<_>) + .to_bytes(&mut invoke_data); } - type_info => invoke_data - .push( - type_id, - type_info, - if type_info.is_builtin() { - &JitValue::Uint64(0) - } else { - iter.next().unwrap() - }, - ) - .unwrap(), + type_info if type_info.is_builtin() => 0u64.to_bytes(&mut invoke_data), + type_info => JitValueWithInfoWrapper { + value: iter.next().unwrap(), + type_id, + info: type_info, + + arena: &arena, + registry, + } + .to_bytes(&mut invoke_data), } } + // Pad invoke data to the 16 byte boundary avoid segfaults. + #[cfg(target_arch = "aarch64")] + const REGISTER_BYTES: usize = 64; + #[cfg(target_arch = "x86_64")] + const REGISTER_BYTES: usize = 48; + if invoke_data.len() > REGISTER_BYTES { + invoke_data.resize( + REGISTER_BYTES + (invoke_data.len() - REGISTER_BYTES).next_multiple_of(16), + 0, + ); + } + // Invoke the trampoline. #[cfg(target_arch = "x86_64")] let mut ret_registers = [0; 2]; @@ -256,8 +259,8 @@ fn invoke_dynamic( unsafe { invoke_trampoline( function_ptr, - invoke_data.invoke_data().as_ptr(), - invoke_data.invoke_data().len(), + invoke_data.as_ptr().cast(), + invoke_data.len() >> 3, ret_registers.as_mut_ptr(), ); } @@ -334,14 +337,7 @@ fn invoke_dynamic( if type_info.is_builtin() { None } else { - Some(parse_result( - ret_type, - registry, - return_ptr, - ret_registers, - // TODO: Consider returning an Option as return_value instead - // As cairo functions can not have a return value - )) + Some(parse_result(ret_type, registry, return_ptr, ret_registers)) } }) .unwrap_or_else(|| { @@ -358,271 +354,6 @@ fn invoke_dynamic( }) } -pub struct ArgumentMapper<'a> { - arena: &'a Bump, - registry: &'a ProgramRegistry, - - invoke_data: Vec, -} - -impl<'a> ArgumentMapper<'a> { - pub fn new(arena: &'a Bump, registry: &'a ProgramRegistry) -> Self { - Self { - arena, - registry, - invoke_data: Vec::new(), - } - } - - pub fn invoke_data(&self) -> &[u64] { - &self.invoke_data - } - - pub fn push_aligned(&mut self, align: usize, mut values: &[u64]) { - assert!(align.is_power_of_two()); - assert!(align <= 16); - - #[cfg(target_arch = "x86_64")] - const NUM_REGISTER_ARGS: usize = 6; - #[cfg(target_arch = "aarch64")] - const NUM_REGISTER_ARGS: usize = 8; - - if align == 16 { - // This works because on both aarch64 and x86_64 the stack is already aligned to - // 16 bytes when the trampoline starts pushing values. - - // Whenever a value spans across multiple registers, if it's in a position where it would be split between - // registers and the stack it must be padded so that the entire value is stored within the stack. - if self.invoke_data.len() >= NUM_REGISTER_ARGS { - if self.invoke_data.len() & 1 != 0 { - self.invoke_data.push(0); - } - } else if self.invoke_data.len() + 1 >= NUM_REGISTER_ARGS { - self.invoke_data.push(0); - } else { - let new_len = self.invoke_data.len() + values.len(); - if new_len >= NUM_REGISTER_ARGS && new_len % 2 != 0 { - let chunk; - (chunk, values) = if values.len() >= 4 { - values.split_at(4) - } else { - (values, [].as_slice()) - }; - self.invoke_data.extend(chunk); - self.invoke_data.push(0); - } - } - } - - self.invoke_data.extend(values); - } - - pub fn push( - &mut self, - type_id: &ConcreteTypeId, - type_info: &CoreTypeConcrete, - value: &JitValue, - ) -> Result<(), Box> { - match (type_info, value) { - (CoreTypeConcrete::Array(info), JitValue::Array(values)) => { - // TODO: Assert that `info.ty` matches all the values' types. - - let type_info = self.registry.get_type(&info.ty)?; - let type_layout = type_info.layout(self.registry).unwrap().pad_to_align(); - - // This needs to be a heap-allocated pointer because it's the actual array data. - let ptr = if values.is_empty() { - null_mut() - } else { - unsafe { libc::realloc(null_mut(), type_layout.size() * values.len()) } - }; - - for (idx, value) in values.iter().enumerate() { - unsafe { - std::ptr::copy_nonoverlapping( - value - .to_jit(self.arena, self.registry, &info.ty) - .unwrap() - .cast() - .as_ptr(), - (ptr as usize + type_layout.size() * idx) as *mut u8, - type_layout.size(), - ); - } - } - - self.push_aligned( - get_integer_layout(64).align(), - &[ptr as u64, 0, values.len() as u64, values.len() as u64], - ); - } - (CoreTypeConcrete::EcPoint(_), JitValue::EcPoint(a, b)) => { - let align = get_integer_layout(252).align(); - self.push_aligned(align, &a.to_le_digits()); - self.push_aligned(align, &b.to_le_digits()); - } - (CoreTypeConcrete::EcState(_), JitValue::EcState(a, b, c, d)) => { - let align = get_integer_layout(252).align(); - self.push_aligned(align, &a.to_le_digits()); - self.push_aligned(align, &b.to_le_digits()); - self.push_aligned(align, &c.to_le_digits()); - self.push_aligned(align, &d.to_le_digits()); - } - (CoreTypeConcrete::Enum(info), JitValue::Enum { tag, value, .. }) => { - if type_info.is_memory_allocated(self.registry) { - let (layout, tag_layout, variant_layouts) = - crate::types::r#enum::get_layout_for_variants( - self.registry, - &info.variants, - ) - .unwrap(); - - let ptr = self.arena.alloc_layout(layout); - unsafe { - match tag_layout.size() { - 0 => {} - 1 => *ptr.cast::().as_mut() = *tag as u8, - 2 => *ptr.cast::().as_mut() = *tag as u16, - 4 => *ptr.cast::().as_mut() = *tag as u32, - 8 => *ptr.cast::().as_mut() = *tag as u64, - _ => unreachable!(), - } - } - - let offset = tag_layout.extend(variant_layouts[*tag]).unwrap().1; - let payload_ptr = value - .to_jit(self.arena, self.registry, &info.variants[*tag]) - .unwrap(); - unsafe { - std::ptr::copy_nonoverlapping( - payload_ptr.cast::().as_ptr(), - ptr.cast::().as_ptr().add(offset), - variant_layouts[*tag].size(), - ); - } - - self.invoke_data.push(ptr.as_ptr() as u64); - } else { - // Write the tag. - match (info.variants.len().next_power_of_two().trailing_zeros() + 7) / 8 { - 0 => {} - _ => self.invoke_data.push(*tag as u64), - } - - // Write the payload. - let type_info = self.registry.get_type(&info.variants[*tag]).unwrap(); - self.push(&info.variants[*tag], type_info, value)?; - } - } - ( - CoreTypeConcrete::Felt252(_) - | CoreTypeConcrete::StarkNet( - StarkNetTypeConcrete::ClassHash(_) - | StarkNetTypeConcrete::ContractAddress(_) - | StarkNetTypeConcrete::StorageAddress(_) - | StarkNetTypeConcrete::StorageBaseAddress(_), - ), - JitValue::Felt252(value), - ) => { - self.push_aligned(get_integer_layout(252).align(), &value.to_le_digits()); - } - (CoreTypeConcrete::Bytes31(_), JitValue::Bytes31(value)) => { - self.push_aligned( - get_integer_layout(248).align(), - &Felt::from_bytes_be_slice(value).to_le_digits(), - ); - } - (CoreTypeConcrete::Felt252Dict(_), JitValue::Felt252Dict { .. }) => { - #[cfg(not(feature = "with-runtime"))] - unimplemented!("enable the `with-runtime` feature to use felt252 dicts"); - - // TODO: Assert that `info.ty` matches all the values' types. - - self.invoke_data.push( - value - .to_jit(self.arena, self.registry, type_id) - .unwrap() - .as_ptr() as u64, - ); - } - (CoreTypeConcrete::Struct(info), JitValue::Struct { fields, .. }) => { - for (field_type_id, field_value) in info.members.iter().zip(fields) { - self.push( - field_type_id, - self.registry.get_type(field_type_id)?, - field_value, - )?; - } - } - (CoreTypeConcrete::Uint128(_), JitValue::Uint128(value)) => self.push_aligned( - get_integer_layout(128).align(), - &[*value as u64, (value >> 64) as u64], - ), - (CoreTypeConcrete::Uint64(_), JitValue::Uint64(value)) => { - self.push_aligned(get_integer_layout(64).align(), &[*value]); - } - (CoreTypeConcrete::Uint32(_), JitValue::Uint32(value)) => { - self.push_aligned(get_integer_layout(32).align(), &[*value as u64]); - } - (CoreTypeConcrete::Uint16(_), JitValue::Uint16(value)) => { - self.push_aligned(get_integer_layout(16).align(), &[*value as u64]); - } - (CoreTypeConcrete::Uint8(_), JitValue::Uint8(value)) => { - self.push_aligned(get_integer_layout(8).align(), &[*value as u64]); - } - (CoreTypeConcrete::Sint128(_), JitValue::Sint128(value)) => { - self.push_aligned( - get_integer_layout(128).align(), - &[*value as u64, (value >> 64) as u64], - ); - } - (CoreTypeConcrete::Sint64(_), JitValue::Sint64(value)) => { - self.push_aligned(get_integer_layout(64).align(), &[*value as u64]); - } - (CoreTypeConcrete::Sint32(_), JitValue::Sint32(value)) => { - self.push_aligned(get_integer_layout(32).align(), &[*value as u64]); - } - (CoreTypeConcrete::Sint16(_), JitValue::Sint16(value)) => { - self.push_aligned(get_integer_layout(16).align(), &[*value as u64]); - } - (CoreTypeConcrete::Sint8(_), JitValue::Sint8(value)) => { - self.push_aligned(get_integer_layout(8).align(), &[*value as u64]); - } - (CoreTypeConcrete::NonZero(info), _) => { - // TODO: Check that the value is indeed non-zero. - let type_info = self.registry.get_type(&info.ty)?; - self.push(&info.ty, type_info, value)?; - } - (CoreTypeConcrete::Snapshot(info), _) => { - let type_info = self.registry.get_type(&info.ty)?; - self.push(&info.ty, type_info, value)?; - } - ( - CoreTypeConcrete::StarkNet(StarkNetTypeConcrete::Secp256Point(_)), - JitValue::Secp256K1Point { x, y } | JitValue::Secp256R1Point { x, y }, - ) => { - let x_data = unsafe { std::mem::transmute::<[u128; 2], [u64; 4]>([x.0, x.1]) }; - let y_data = unsafe { std::mem::transmute::<[u128; 2], [u64; 4]>([y.0, y.1]) }; - - self.push_aligned(get_integer_layout(252).align(), &x_data); - self.push_aligned(get_integer_layout(252).align(), &y_data); - } - (CoreTypeConcrete::Bitwise(_), JitValue::Uint64(value)) - | (CoreTypeConcrete::BuiltinCosts(_), JitValue::Uint64(value)) - | (CoreTypeConcrete::EcOp(_), JitValue::Uint64(value)) - | (CoreTypeConcrete::Pedersen(_), JitValue::Uint64(value)) - | (CoreTypeConcrete::Poseidon(_), JitValue::Uint64(value)) - | (CoreTypeConcrete::RangeCheck(_), JitValue::Uint64(value)) - | (CoreTypeConcrete::SegmentArena(_), JitValue::Uint64(value)) => { - self.push_aligned(get_integer_layout(64).align(), &[*value]) - } - (_, _) => todo!(), - } - - Ok(()) - } -} - /// Parses the result by reading from the return ptr the given type. fn parse_result( type_id: &ConcreteTypeId, @@ -867,379 +598,3 @@ fn parse_result( | CoreTypeConcrete::Uint128MulGuarantee(_) => todo!(), } } - -#[cfg(test)] -mod test { - use super::*; - use cairo_lang_sierra::extensions::types::InfoOnlyConcreteType; - use cairo_lang_sierra::extensions::types::TypeInfo; - use cairo_lang_sierra::program::ConcreteTypeLongId; - use cairo_lang_sierra::ProgramParser; - - #[test] - fn test_argument_mapper_push_sint8() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Sint8(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint8(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint8(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint8(-12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint8(i8::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint8(i8::MAX)); - - assert_eq!( - argument_mapper.invoke_data, - vec![12, 0, (-12_i8) as u64, i8::MIN as u64, i8::MAX as u64] - ); - } - - #[test] - fn test_argument_mapper_push_sint16() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Sint16(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint16(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint16(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint16(-12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint16(i16::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint16(i16::MAX)); - - assert_eq!( - argument_mapper.invoke_data, - vec![12, 0, (-12_i16) as u64, i16::MIN as u64, i16::MAX as u64] - ); - } - - #[test] - fn test_argument_mapper_push_sint32() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Sint32(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint32(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint32(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint32(-12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint32(i32::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint32(i32::MAX)); - - assert_eq!( - argument_mapper.invoke_data, - vec![12, 0, (-12_i32) as u64, i32::MIN as u64, i32::MAX as u64] - ); - } - - #[test] - fn test_argument_mapper_push_sint64() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Sint64(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint64(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint64(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint64(-12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint64(i64::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint64(i64::MAX)); - - assert_eq!( - argument_mapper.invoke_data, - vec![12, 0, (-12_i64) as u64, i64::MIN as u64, i64::MAX as u64] - ); - } - - #[test] - fn test_argument_mapper_push_sint128() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Sint128(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint128(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint128(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint128(-12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint128(i128::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Sint128(i128::MAX)); - - assert_eq!( - argument_mapper.invoke_data, - vec![ - 12, - 0, - 0, - 0, - (-12_i128) as u64, - ((-12_i128) as u128 >> 64) as u64, - i128::MIN as u64, - (i128::MIN as u128 >> 64) as u64, - i128::MAX as u64, - (i128::MAX as u128 >> 64) as u64, - ] - ); - } - - #[test] - fn test_argument_mapper_push_uint8() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Uint8(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint8(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint8(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint8(u8::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint8(u8::MAX)); - - assert_eq!(argument_mapper.invoke_data, vec![12, 0, 0, 0xFF]); - } - - #[test] - fn test_argument_mapper_push_uint16() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Uint16(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint16(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint16(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint16(u16::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint16(u16::MAX)); - - assert_eq!(argument_mapper.invoke_data, vec![12, 0, 0, 0xFFFF]); - } - - #[test] - fn test_argument_mapper_push_uint32() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Uint32(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint32(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint32(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint32(u32::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint32(u32::MAX)); - - assert_eq!(argument_mapper.invoke_data, vec![12, 0, 0, 0xFFFFFFFF]); - } - - #[test] - fn test_argument_mapper_push_uint64() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Uint64(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint64(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint64(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint64(u64::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint64(u64::MAX)); - - assert_eq!( - argument_mapper.invoke_data, - vec![12, 0, 0, 0xFFFFFFFFFFFFFFFF] - ); - } - - #[test] - fn test_argument_mapper_push_uint128() { - let program = ProgramParser::new().parse("").unwrap(); - let registry = ProgramRegistry::::new(&program).unwrap(); - let bump = Bump::new(); - let mut argument_mapper = ArgumentMapper::new(&bump, ®istry); - - let type_id = ConcreteTypeId { - debug_name: None, - id: 10, - }; - - let type_info = CoreTypeConcrete::Uint128(InfoOnlyConcreteType { - info: TypeInfo { - long_id: ConcreteTypeLongId { - generic_id: "generic_type_id".into(), - generic_args: vec![], - }, - storable: false, - droppable: false, - duplicatable: false, - zero_sized: false, - }, - }); - - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint128(12)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint128(0)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint128(u128::MIN)); - let _ = argument_mapper.push(&type_id, &type_info, &JitValue::Uint128(u128::MAX)); - - assert_eq!( - argument_mapper.invoke_data, - vec![12, 0, 0, 0, 0, 0, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF] - ); - } -} diff --git a/src/lib.rs b/src/lib.rs index b20cffc4b..92ce524e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,7 @@ pub use self::{ ffi::{module_to_object, object_to_shared_lib, LLVMCompileError, OptLevel}, }; +mod arch; pub(crate) mod block_ext; pub mod cache; mod compiler; diff --git a/src/metadata/debug_utils.rs b/src/metadata/debug_utils.rs index 0fe1ece66..c375df39a 100644 --- a/src/metadata/debug_utils.rs +++ b/src/metadata/debug_utils.rs @@ -362,7 +362,7 @@ impl DebugUtils { let k64 = block .append_operation(arith::constant( context, - IntegerAttribute::new(IntegerType::new(context, 64).into(), 64).into(), + IntegerAttribute::new(IntegerType::new(context, 252).into(), 64).into(), location, )) .result(0)? diff --git a/src/values.rs b/src/values.rs index f58fa2c86..c3bd2b126 100644 --- a/src/values.rs +++ b/src/values.rs @@ -30,11 +30,11 @@ use std::{alloc::Layout, collections::HashMap, ops::Neg, ptr::NonNull}; /// The debug_name field on some variants is `Some` when receiving a [`JitValue`] as a result. /// /// A Boxed value or a non-null Nullable value is returned with it's inner value. -#[derive(Debug, Clone, Educe)] +#[derive(Clone, Educe)] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] -#[educe(Eq, PartialEq)] +#[educe(Debug, Eq, PartialEq)] pub enum JitValue { - Felt252(Felt), + Felt252(#[educe(Debug(method(std::fmt::Display::fmt)))] Felt), Bytes31([u8; 31]), /// all elements need to be same type Array(Vec), @@ -237,12 +237,12 @@ impl JitValue { let ptr: *mut () = libc::malloc(elem_layout.size() * data.len()).cast(); let len: u32 = data.len().try_into().unwrap(); - for elem in data { + for (idx, elem) in data.iter().enumerate() { let elem = elem.to_jit(arena, registry, &info.ty)?; std::ptr::copy_nonoverlapping( elem.cast::().as_ptr(), - ptr.byte_add(len as usize * elem_layout.size()).cast::(), + ptr.byte_add(idx * elem_layout.size()).cast::(), elem_layout.size(), ); } diff --git a/tests/tests/trampoline.rs b/tests/tests/trampoline.rs index 143e77192..f5dcf678c 100644 --- a/tests/tests/trampoline.rs +++ b/tests/tests/trampoline.rs @@ -520,3 +520,45 @@ fn invoke1_enum2_u8_u16() { r(MyEnum::B(10)); r(MyEnum::B(u16::MAX)); } + +#[test] +fn test_deserialize_param_bug() { + let (module_name, program, _) = load_cairo! { + fn main( + b0: u64, // Pedersen + b1: u64, // RangeCheck + b2: u64, // Bitwise + b3: u128, // GasBuiltin + b4: u64, // System + arg0: Span // Arguments + ) -> (u64, u64, u64, u128, u64, Span) { + (b0, b1, b2, b3, b4, arg0) + } + }; + + let args = vec![ + JitValue::Uint64(0), + JitValue::Uint64(0), + JitValue::Uint64(0), + JitValue::Uint128(0), + JitValue::Uint64(0), + JitValue::Struct { + fields: vec![JitValue::Array(vec![ + JitValue::Felt252(1.into()), + JitValue::Felt252(2.into()), + ])], + debug_name: None, + }, + ]; + assert_eq!( + run_program(&program, &format!("{0}::{0}::main", module_name), &args), + ExecutionResult { + remaining_gas: None, + return_value: JitValue::Struct { + fields: args, + debug_name: None + }, + builtin_stats: BuiltinStats::default(), + }, + ); +}