From 0685df37a18c7616c18c5783353cbf21667bbeb5 Mon Sep 17 00:00:00 2001 From: Cyrill Leutwiler Date: Fri, 4 Jul 2025 12:31:25 +0200 Subject: [PATCH] the revive call function Signed-off-by: Cyrill Leutwiler --- crates/llvm-context/src/lib.rs | 1 + .../polkavm/context/function/runtime/call.rs | 130 ++++++++++++++++++ crates/llvm-context/src/polkavm/evm/call.rs | 74 +++------- crates/yul/src/parser/statement/object.rs | 2 + 4 files changed, 151 insertions(+), 56 deletions(-) diff --git a/crates/llvm-context/src/lib.rs b/crates/llvm-context/src/lib.rs index a51fd4e..dc6638b 100644 --- a/crates/llvm-context/src/lib.rs +++ b/crates/llvm-context/src/lib.rs @@ -33,6 +33,7 @@ pub use self::polkavm::context::function::runtime::bitwise::Sar as PolkaVMSarFun pub use self::polkavm::context::function::runtime::bitwise::Shl as PolkaVMShlFunction; pub use self::polkavm::context::function::runtime::bitwise::Shr as PolkaVMShrFunction; pub use self::polkavm::context::function::runtime::bitwise::Xor as PolkaVMXorFunction; +pub use self::polkavm::context::function::runtime::call::Call as PolkaVMCall; pub use self::polkavm::context::function::runtime::call::CallReentrancyProtector as PolkaVMCallReentrancyProtector; pub use self::polkavm::context::function::runtime::deploy_code::DeployCode as PolkaVMDeployCodeFunction; pub use self::polkavm::context::function::runtime::entry::Entry as PolkaVMEntryFunction; diff --git a/crates/llvm-context/src/polkavm/context/function/runtime/call.rs b/crates/llvm-context/src/polkavm/context/function/runtime/call.rs index 3fd0f7a..d3de8ba 100644 --- a/crates/llvm-context/src/polkavm/context/function/runtime/call.rs +++ b/crates/llvm-context/src/polkavm/context/function/runtime/call.rs @@ -4,6 +4,7 @@ use inkwell::values::BasicValue; use crate::polkavm::context::runtime::RuntimeFunction; use crate::polkavm::context::Context; +use crate::polkavm::context::Pointer; use crate::polkavm::Dependency; use crate::polkavm::WriteLLVM; @@ -125,3 +126,132 @@ where >::emit(&self, context) } } + +/// Implements the CALL operator according to the EVM specification. +/// +/// # Arguments: +/// - The address value. +/// - The value value. +/// - The input offset. +/// - The input length. +/// - The output offset. +/// - The output length. +/// - The deposit limit pointer. +/// - The call flags. +/// +/// # Returns: +/// - The success value (as xlen) +pub struct Call; + +impl RuntimeFunction for Call +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_call"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.register_type().fn_type( + &[ + context.word_type().into(), + context.word_type().into(), + context.xlen_type().into(), + context.xlen_type().into(), + context.xlen_type().into(), + context.xlen_type().into(), + context.llvm().ptr_type(Default::default()).into(), + context.xlen_type().into(), + ], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let address = Self::paramater(context, 0).into_int_value(); + let value = Self::paramater(context, 1).into_int_value(); + let input_offset = Self::paramater(context, 2).into_int_value(); + let input_length = Self::paramater(context, 3).into_int_value(); + let output_offset = Self::paramater(context, 4).into_int_value(); + let output_length = Self::paramater(context, 5).into_int_value(); + let depsit_limit_pointer = Self::paramater(context, 6).into_pointer_value(); + let flags = Self::paramater(context, 7).into_int_value(); + + let address_pointer = context.build_address_argument_store(address)?; + + let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer"); + context.build_store(value_pointer, value)?; + + let input_pointer = context.build_heap_gep(input_offset, input_length)?; + let output_pointer = context.build_heap_gep(output_offset, output_length)?; + + let output_length_pointer = + context.build_alloca_at_entry(context.xlen_type(), "output_length"); + context.build_store(output_length_pointer, output_length)?; + + let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg( + context.builder(), + context.llvm(), + flags, + address_pointer.to_int(context), + "address_and_callee", + )?; + let deposit_limit_pointer = Pointer::new( + context.word_type(), + Default::default(), + depsit_limit_pointer, + ); + let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg( + context.builder(), + context.llvm(), + deposit_limit_pointer.to_int(context), + value_pointer.to_int(context), + "deposit_and_value", + )?; + let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg( + context.builder(), + context.llvm(), + input_length, + input_pointer.to_int(context), + "input_data", + )?; + let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg( + context.builder(), + context.llvm(), + output_length_pointer.to_int(context), + output_pointer.to_int(context), + "output_data", + )?; + + let name = revive_runtime_api::polkavm_imports::CALL; + let success = context + .build_runtime_call( + name, + &[ + flags_and_callee.into(), + context.register_type().const_all_ones().into(), + context.register_type().const_all_ones().into(), + deposit_and_value.into(), + input_data.into(), + output_data.into(), + ], + ) + .unwrap_or_else(|| panic!("{name} should return a value")) + .into_int_value(); + Ok(Some(success.into())) + } +} + +impl WriteLLVM for Call +where + D: Dependency + Clone, +{ + fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> { + >::declare(self, context) + } + + fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> { + >::emit(&self, context) + } +} diff --git a/crates/llvm-context/src/polkavm/evm/call.rs b/crates/llvm-context/src/polkavm/evm/call.rs index dd0c3ea..891706b 100644 --- a/crates/llvm-context/src/polkavm/evm/call.rs +++ b/crates/llvm-context/src/polkavm/evm/call.rs @@ -6,7 +6,7 @@ use crate::polkavm::context::argument::Argument; use crate::polkavm::context::runtime::RuntimeFunction; use crate::polkavm::context::Context; use crate::polkavm::Dependency; -use crate::PolkaVMCallReentrancyProtector; +use crate::{PolkaVMCall, PolkaVMCallReentrancyProtector}; const STATIC_CALL_FLAG: u32 = 0b0001_0000; const REENTRANT_CALL_FLAG: u32 = 0b0000_1000; @@ -28,6 +28,9 @@ pub fn call<'ctx, D>( where D: Dependency + Clone, { + let input_offset = context.safe_truncate_int_to_xlen(input_offset)?; + let output_offset = context.safe_truncate_int_to_xlen(output_offset)?; + let input_length = context.safe_truncate_int_to_xlen(input_length)?; let output_length = context.safe_truncate_int_to_xlen(output_length)?; @@ -47,64 +50,23 @@ where )? }; - let address_pointer = context.build_address_argument_store(address)?; - let value = value.unwrap_or_else(|| context.word_const(0)); - let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer"); - context.build_store(value_pointer, value)?; - let input_offset = context.safe_truncate_int_to_xlen(input_offset)?; - let output_offset = context.safe_truncate_int_to_xlen(output_offset)?; - - let input_pointer = context.build_heap_gep(input_offset, input_length)?; - let output_pointer = context.build_heap_gep(output_offset, output_length)?; - - let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length"); - context.build_store(output_length_pointer, output_length)?; - - let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg( - context.builder(), - context.llvm(), - flags, - address_pointer.to_int(context), - "address_and_callee", - )?; - let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg( - context.builder(), - context.llvm(), - deposit_limit_pointer.to_int(context), - value_pointer.to_int(context), - "deposit_and_value", - )?; - let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg( - context.builder(), - context.llvm(), - input_length, - input_pointer.to_int(context), - "input_data", - )?; - let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg( - context.builder(), - context.llvm(), - output_length_pointer.to_int(context), - output_pointer.to_int(context), - "output_data", - )?; - - let name = revive_runtime_api::polkavm_imports::CALL; + let name = >::NAME; + let declaration = >::declaration(context); + let arguments = &[ + address.into(), + value.into(), + input_offset.into(), + input_length.into(), + output_offset.into(), + output_length.into(), + deposit_limit_pointer.value.into(), + flags.into(), + ]; let success = context - .build_runtime_call( - name, - &[ - flags_and_callee.into(), - context.register_type().const_all_ones().into(), - context.register_type().const_all_ones().into(), - deposit_and_value.into(), - input_data.into(), - output_data.into(), - ], - ) - .unwrap_or_else(|| panic!("{name} should return a value")) + .build_call(declaration, arguments, "call") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value")) .into_int_value(); let is_success = context.builder().build_int_compare( diff --git a/crates/yul/src/parser/statement/object.rs b/crates/yul/src/parser/statement/object.rs index 2b11171..22dfbb9 100644 --- a/crates/yul/src/parser/statement/object.rs +++ b/crates/yul/src/parser/statement/object.rs @@ -220,6 +220,7 @@ where revive_llvm_context::PolkaVMSarFunction.declare(context)?; revive_llvm_context::PolkaVMByteFunction.declare(context)?; + revive_llvm_context::PolkaVMCall.declare(context)?; revive_llvm_context::PolkaVMCallReentrancyProtector.declare(context)?; revive_llvm_context::PolkaVMSbrkFunction.declare(context)?; @@ -287,6 +288,7 @@ where revive_llvm_context::PolkaVMSarFunction.into_llvm(context)?; revive_llvm_context::PolkaVMByteFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMCall.into_llvm(context)?; revive_llvm_context::PolkaVMCallReentrancyProtector.into_llvm(context)?; revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?;