diff --git a/crates/integration/codesize.json b/crates/integration/codesize.json index dd2771a0..9e4f0fdb 100644 --- a/crates/integration/codesize.json +++ b/crates/integration/codesize.json @@ -2,7 +2,7 @@ "Baseline": 934, "Computation": 4360, "DivisionArithmetics": 39448, - "ERC20": 52461, + "ERC20": 52333, "Events": 1749, "FibonacciIterative": 2973, "Flipper": 3368, diff --git a/crates/integration/contracts/MCopy.sol b/crates/integration/contracts/MCopy.sol new file mode 100644 index 00000000..03431fad --- /dev/null +++ b/crates/integration/contracts/MCopy.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8; + +contract MCopy { + function memcpy(bytes memory payload) public pure returns (bytes memory) { + return payload; + } +} diff --git a/crates/integration/src/cases.rs b/crates/integration/src/cases.rs index 19c1f751..911d2e49 100644 --- a/crates/integration/src/cases.rs +++ b/crates/integration/src/cases.rs @@ -129,6 +129,12 @@ sol!( } ); +sol!( + contract MCopy { + function memcpy(bytes memory payload) public pure returns (bytes memory); + } +); + impl Contract { /// Execute the contract. /// @@ -423,6 +429,18 @@ impl Contract { calldata: ExtCode::ExtCodeSizeCall::new((address,)).abi_encode(), } } + + pub fn memcpy(payload: Vec) -> Self { + let code = include_str!("../contracts/MCopy.sol"); + let name = "MCopy"; + + Self { + name, + evm_runtime: crate::compile_evm_bin_runtime(name, code), + pvm_runtime: crate::compile_blob(name, code), + calldata: MCopy::memcpyCall::new((payload,)).abi_encode(), + } + } } #[cfg(test)] diff --git a/crates/integration/src/tests.rs b/crates/integration/src/tests.rs index 96916c6e..d8d91f5f 100644 --- a/crates/integration/src/tests.rs +++ b/crates/integration/src/tests.rs @@ -1,5 +1,5 @@ use alloy_primitives::{keccak256, Address, FixedBytes, B256, I256, U256}; -use alloy_sol_types::{sol, SolCall}; +use alloy_sol_types::{sol, SolCall, SolValue}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use sha1::Digest; @@ -522,3 +522,15 @@ fn ext_code_size() { let expected = U256::ZERO; assert_eq!(received, expected); } + +#[test] +fn mcopy() { + let expected = vec![1, 2, 3]; + + let (_, output) = assert_success(&Contract::memcpy(expected.clone()), false); + + let received = alloy_primitives::Bytes::abi_decode(&output.data, true) + .unwrap() + .to_vec(); + assert_eq!(expected, received); +} diff --git a/crates/llvm-context/src/polkavm/context/function/intrinsics.rs b/crates/llvm-context/src/polkavm/context/function/intrinsics.rs index c136b4c8..03523c06 100644 --- a/crates/llvm-context/src/polkavm/context/function/intrinsics.rs +++ b/crates/llvm-context/src/polkavm/context/function/intrinsics.rs @@ -2,7 +2,6 @@ use inkwell::types::BasicType; -use crate::polkavm::context::address_space::AddressSpace; use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration; /// The LLVM intrinsic functions, implemented in the LLVM back-end. @@ -11,10 +10,6 @@ use crate::polkavm::context::function::declaration::Declaration as FunctionDecla pub struct Intrinsics<'ctx> { /// The trap. pub trap: FunctionDeclaration<'ctx>, - /// The memory copy within the heap. - pub memory_copy: FunctionDeclaration<'ctx>, - /// The memory copy from a generic page. - pub memory_copy_from_generic: FunctionDeclaration<'ctx>, /// Performs endianness swaps on i256 values pub byte_swap_word: FunctionDeclaration<'ctx>, /// Performs endianness swaps on i160 values @@ -25,12 +20,6 @@ impl<'ctx> Intrinsics<'ctx> { /// The corresponding intrinsic function name. pub const FUNCTION_TRAP: &'static str = "llvm.trap"; - /// The corresponding intrinsic function name. - pub const FUNCTION_MEMORY_COPY: &'static str = "llvm.memcpy.p1.p1.i256"; - - /// The corresponding intrinsic function name. - pub const FUNCTION_MEMORY_COPY_FROM_GENERIC: &'static str = "llvm.memcpy.p3.p1.i256"; - /// The corresponding intrinsic function name. pub const FUNCTION_BYTE_SWAP_WORD: &'static str = "llvm.bswap.i256"; @@ -43,12 +32,8 @@ impl<'ctx> Intrinsics<'ctx> { module: &inkwell::module::Module<'ctx>, ) -> Self { let void_type = llvm.void_type(); - let bool_type = llvm.bool_type(); let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32); let address_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_ETH_ADDRESS as u32); - let _stack_field_pointer_type = llvm.ptr_type(AddressSpace::Stack.into()); - let heap_field_pointer_type = llvm.ptr_type(AddressSpace::Heap.into()); - let generic_byte_pointer_type = llvm.ptr_type(AddressSpace::Generic.into()); let trap = Self::declare( llvm, @@ -56,34 +41,6 @@ impl<'ctx> Intrinsics<'ctx> { Self::FUNCTION_TRAP, void_type.fn_type(&[], false), ); - let memory_copy = Self::declare( - llvm, - module, - Self::FUNCTION_MEMORY_COPY, - void_type.fn_type( - &[ - heap_field_pointer_type.as_basic_type_enum().into(), - heap_field_pointer_type.as_basic_type_enum().into(), - word_type.as_basic_type_enum().into(), - bool_type.as_basic_type_enum().into(), - ], - false, - ), - ); - let memory_copy_from_generic = Self::declare( - llvm, - module, - Self::FUNCTION_MEMORY_COPY_FROM_GENERIC, - void_type.fn_type( - &[ - heap_field_pointer_type.as_basic_type_enum().into(), - generic_byte_pointer_type.as_basic_type_enum().into(), - word_type.as_basic_type_enum().into(), - bool_type.as_basic_type_enum().into(), - ], - false, - ), - ); let byte_swap_word = Self::declare( llvm, module, @@ -99,8 +56,6 @@ impl<'ctx> Intrinsics<'ctx> { Self { trap, - memory_copy, - memory_copy_from_generic, byte_swap_word, byte_swap_eth_address, } @@ -130,20 +85,6 @@ impl<'ctx> Intrinsics<'ctx> { let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32); match name { - name if name == Self::FUNCTION_MEMORY_COPY => vec![ - llvm.ptr_type(AddressSpace::Heap.into()) - .as_basic_type_enum(), - llvm.ptr_type(AddressSpace::Heap.into()) - .as_basic_type_enum(), - word_type.as_basic_type_enum(), - ], - name if name == Self::FUNCTION_MEMORY_COPY_FROM_GENERIC => vec![ - llvm.ptr_type(AddressSpace::Heap.into()) - .as_basic_type_enum(), - llvm.ptr_type(AddressSpace::Generic.into()) - .as_basic_type_enum(), - word_type.as_basic_type_enum(), - ], name if name == Self::FUNCTION_BYTE_SWAP_WORD => vec![word_type.as_basic_type_enum()], name if name == Self::FUNCTION_BYTE_SWAP_ETH_ADDRESS => { vec![llvm diff --git a/crates/llvm-context/src/polkavm/context/mod.rs b/crates/llvm-context/src/polkavm/context/mod.rs index 0c5ab661..a5af6a67 100644 --- a/crates/llvm-context/src/polkavm/context/mod.rs +++ b/crates/llvm-context/src/polkavm/context/mod.rs @@ -1060,63 +1060,35 @@ where /// Sets the alignment to `1`, since all non-stack memory pages have such alignment. pub fn build_memcpy( &self, - _function: FunctionDeclaration<'ctx>, destination: Pointer<'ctx>, source: Pointer<'ctx>, size: inkwell::values::IntValue<'ctx>, - _name: &str, + name: &str, ) -> anyhow::Result<()> { - let _ = self - .builder() - .build_memcpy(destination.value, 1, source.value, 1, size)?; + let size = self.safe_truncate_int_to_xlen(size)?; - Ok(()) - } + let destination = if destination.address_space == AddressSpace::Heap { + self.build_heap_gep( + self.builder() + .build_ptr_to_int(destination.value, self.xlen_type(), name)?, + size, + )? + } else { + destination + }; - /// Builds a memory copy call for the return data. - /// Sets the output length to `min(output_length, return_data_size` and calls the default - /// generic page memory copy builder. - pub fn build_memcpy_return_data( - &self, - function: FunctionDeclaration<'ctx>, - destination: Pointer<'ctx>, - source: Pointer<'ctx>, - size: inkwell::values::IntValue<'ctx>, - name: &str, - ) -> anyhow::Result<()> { - let pointer_casted = self.builder.build_ptr_to_int( - source.value, - self.word_type(), - format!("{name}_pointer_casted").as_str(), - )?; - let return_data_size_shifted = self.builder.build_right_shift( - pointer_casted, - self.word_const((revive_common::BIT_LENGTH_X32 * 3) as u64), - false, - format!("{name}_return_data_size_shifted").as_str(), - )?; - let return_data_size_truncated = self.builder.build_and( - return_data_size_shifted, - self.word_const(u32::MAX as u64), - format!("{name}_return_data_size_truncated").as_str(), - )?; - let is_return_data_size_lesser = self.builder.build_int_compare( - inkwell::IntPredicate::ULT, - return_data_size_truncated, - size, - format!("{name}_is_return_data_size_lesser").as_str(), - )?; - let min_size = self - .builder - .build_select( - is_return_data_size_lesser, - return_data_size_truncated, + let source = if source.address_space == AddressSpace::Heap { + self.build_heap_gep( + self.builder() + .build_ptr_to_int(source.value, self.xlen_type(), name)?, size, - format!("{name}_min_size").as_str(), )? - .into_int_value(); + } else { + source + }; - self.build_memcpy(function, destination, source, min_size, name)?; + self.builder() + .build_memmove(destination.value, 1, source.value, 1, size)?; Ok(()) } diff --git a/crates/llvm-context/src/polkavm/evm/calldata.rs b/crates/llvm-context/src/polkavm/evm/calldata.rs index 1540076d..d28bd679 100644 --- a/crates/llvm-context/src/polkavm/evm/calldata.rs +++ b/crates/llvm-context/src/polkavm/evm/calldata.rs @@ -66,11 +66,5 @@ where "calldata_pointer_with_offset", ); - context.build_memcpy( - context.intrinsics().memory_copy_from_generic, - destination, - source, - size, - "calldata_copy_memcpy_from_child", - ) + context.build_memcpy(destination, source, size, "calldata_copy_memcpy_from_child") } diff --git a/crates/llvm-context/src/polkavm/evm/return_data.rs b/crates/llvm-context/src/polkavm/evm/return_data.rs index 6d0f6b10..3090bb8c 100644 --- a/crates/llvm-context/src/polkavm/evm/return_data.rs +++ b/crates/llvm-context/src/polkavm/evm/return_data.rs @@ -76,7 +76,6 @@ where context.set_basic_block(block_copy); context.build_memcpy( - context.intrinsics().memory_copy_from_generic, context.build_heap_gep(destination_offset, size)?, context.build_gep( context diff --git a/crates/pallet-contracts-pvm-llapi/src/polkavm_guest.c b/crates/pallet-contracts-pvm-llapi/src/polkavm_guest.c index 0317ba3d..1ce3b01c 100644 --- a/crates/pallet-contracts-pvm-llapi/src/polkavm_guest.c +++ b/crates/pallet-contracts-pvm-llapi/src/polkavm_guest.c @@ -21,6 +21,22 @@ void * memcpy(void *dst, const void *_src, size_t len) { return dst; } +void * memmove(void *dst, const void *src, size_t n) { + char *d = dst; + const char *s = src; + + if (d==s) return d; + if ((uintptr_t)s-(uintptr_t)d-n <= -2*n) return memcpy(d, s, n); + + if (d