From 939a8f2f78247a5f791f18cd7431569c2cf4a2f5 Mon Sep 17 00:00:00 2001 From: Cyrill Leutwiler Date: Fri, 4 Jul 2025 15:45:59 +0200 Subject: [PATCH] add simulated evm math opcode syscalls Signed-off-by: Cyrill Leutwiler --- .../llvm-context/src/polkavm/context/mod.rs | 24 +--- crates/llvm-context/src/polkavm/evm/math.rs | 110 ++++++++++++------ crates/runtime-api/src/polkavm_imports.c | 16 +++ crates/runtime-api/src/polkavm_imports.rs | 20 +++- 4 files changed, 109 insertions(+), 61 deletions(-) diff --git a/crates/llvm-context/src/polkavm/context/mod.rs b/crates/llvm-context/src/polkavm/context/mod.rs index 368c390..2510574 100644 --- a/crates/llvm-context/src/polkavm/context/mod.rs +++ b/crates/llvm-context/src/polkavm/context/mod.rs @@ -46,7 +46,6 @@ use self::code_type::CodeType; use self::debug_info::DebugInfo; use self::function::declaration::Declaration as FunctionDeclaration; use self::function::intrinsics::Intrinsics; -use self::function::llvm_runtime::LLVMRuntime; use self::function::r#return::Return as FunctionReturn; use self::function::runtime::revive::Exit; use self::function::runtime::revive::WordToPointer; @@ -79,8 +78,6 @@ where globals: HashMap>, /// The LLVM intrinsic functions, defined on the LLVM side. intrinsics: Intrinsics<'ctx>, - /// The LLVM runtime functions, defined on the LLVM side. - llvm_runtime: LLVMRuntime<'ctx>, /// The declared functions. functions: HashMap>>>, /// The current active function. @@ -122,16 +119,6 @@ where /// The loop stack default capacity. const LOOP_STACK_INITIAL_CAPACITY: usize = 16; - /// Link in the stdlib module. - fn link_stdlib_module( - llvm: &'ctx inkwell::context::Context, - module: &inkwell::module::Module<'ctx>, - ) { - module - .link_in_module(revive_stdlib::module(llvm, "revive_stdlib").unwrap()) - .expect("the stdlib module should be linkable"); - } - /// Link in the PolkaVM imports module, containing imported functions, /// and marking them as external (they need to be relocatable as too). fn link_polkavm_imports( @@ -215,7 +202,8 @@ where llvm: &'ctx inkwell::context::Context, module: &inkwell::module::Module<'ctx>, ) { - let source_module = revive_stdlib::module(llvm, "revive_stdlib").unwrap(); + let source_module = + revive_runtime_api::polkavm_imports::module(llvm, "revive_runtime").unwrap(); let data_layout = source_module.get_data_layout(); module.set_data_layout(&data_layout); } @@ -233,7 +221,6 @@ where memory_config: SolcStandardJsonInputSettingsPolkaVMMemory, ) -> Self { Self::set_data_layout(llvm, &module); - Self::link_stdlib_module(llvm, &module); Self::link_polkavm_imports(llvm, &module); Self::set_polkavm_stack_size( llvm, @@ -245,7 +232,6 @@ where Self::set_module_flags(llvm, &module); let intrinsics = Intrinsics::new(llvm, &module); - let llvm_runtime = LLVMRuntime::new(llvm, &module, &optimizer); let debug_info = debug_config.emit_debug_info.then(|| { let debug_info = DebugInfo::new(&module); debug_info.initialize_module(llvm, &module); @@ -260,7 +246,6 @@ where code_type: None, globals: HashMap::with_capacity(Self::GLOBALS_HASHMAP_INITIAL_CAPACITY), intrinsics, - llvm_runtime, functions: HashMap::with_capacity(Self::FUNCTIONS_HASHMAP_INITIAL_CAPACITY), current_function: None, loop_stack: Vec::with_capacity(Self::LOOP_STACK_INITIAL_CAPACITY), @@ -451,11 +436,6 @@ where &self.intrinsics } - /// Returns the LLVM runtime function collection reference. - pub fn llvm_runtime(&self) -> &LLVMRuntime<'ctx> { - &self.llvm_runtime - } - /// Appends a function to the current module. pub fn add_function( &mut self, diff --git a/crates/llvm-context/src/polkavm/evm/math.rs b/crates/llvm-context/src/polkavm/evm/math.rs index 68f59ef..a292ec6 100644 --- a/crates/llvm-context/src/polkavm/evm/math.rs +++ b/crates/llvm-context/src/polkavm/evm/math.rs @@ -1,7 +1,5 @@ //! Translates the mathematical operations. -use inkwell::values::BasicValue; - use crate::polkavm::context::Context; use crate::polkavm::Dependency; @@ -15,17 +13,26 @@ pub fn add_mod<'ctx, D>( where D: Dependency + Clone, { - Ok(context - .build_call( - context.llvm_runtime().add_mod, - &[ - operand_1.as_basic_value_enum(), - operand_2.as_basic_value_enum(), - modulo.as_basic_value_enum(), - ], - "add_mod_call", - ) - .expect("Always exists")) + let result_pointer = + context.build_alloca_at_entry(context.word_type(), "addmod_result_pointer"); + let operand_1_pointer = context.build_alloca_at_entry(context.word_type(), "addmod_operand_1"); + let operand_2_pointer = context.build_alloca_at_entry(context.word_type(), "addmod_operand_2"); + let modulo_pointer = + context.build_alloca_at_entry(context.word_type(), "addmod_modulo_operand"); + + context.build_store(operand_2_pointer, operand_2)?; + context.build_store(operand_1_pointer, operand_1)?; + context.build_store(modulo_pointer, modulo)?; + + let arguments = &[ + result_pointer.to_int(context).into(), + operand_1_pointer.to_int(context).into(), + operand_2_pointer.to_int(context).into(), + modulo_pointer.to_int(context).into(), + ]; + + context.build_runtime_call(revive_runtime_api::polkavm_imports::ADDMOD, arguments); + context.build_load(result_pointer, "addmod_result") } /// Translates the `mulmod` instruction. @@ -38,17 +45,26 @@ pub fn mul_mod<'ctx, D>( where D: Dependency + Clone, { - Ok(context - .build_call( - context.llvm_runtime().mul_mod, - &[ - operand_1.as_basic_value_enum(), - operand_2.as_basic_value_enum(), - modulo.as_basic_value_enum(), - ], - "mul_mod_call", - ) - .expect("Always exists")) + let result_pointer = + context.build_alloca_at_entry(context.word_type(), "mulmod_result_pointer"); + let operand_1_pointer = context.build_alloca_at_entry(context.word_type(), "mulmod_operand_1"); + let operand_2_pointer = context.build_alloca_at_entry(context.word_type(), "mulmod_operand_2"); + let modulo_pointer = + context.build_alloca_at_entry(context.word_type(), "mulmod_modulo_operand"); + + context.build_store(operand_2_pointer, operand_2)?; + context.build_store(operand_1_pointer, operand_1)?; + context.build_store(modulo_pointer, modulo)?; + + let arguments = &[ + result_pointer.to_int(context).into(), + operand_1_pointer.to_int(context).into(), + operand_2_pointer.to_int(context).into(), + modulo_pointer.to_int(context).into(), + ]; + + context.build_runtime_call(revive_runtime_api::polkavm_imports::MULMOD, arguments); + context.build_load(result_pointer, "addmod_result") } /// Translates the `exp` instruction. @@ -60,13 +76,22 @@ pub fn exponent<'ctx, D>( where D: Dependency + Clone, { - Ok(context - .build_call( - context.llvm_runtime().exp, - &[value.as_basic_value_enum(), exponent.as_basic_value_enum()], - "exp_call", - ) - .expect("Always exists")) + let result_pointer = context.build_alloca_at_entry(context.word_type(), "exp_result_pointer"); + let value_pointer = context.build_alloca_at_entry(context.word_type(), "exp_value_pointer"); + let exponent_pointer = + context.build_alloca_at_entry(context.word_type(), "exp_exponent_pointer"); + + context.build_store(value_pointer, value)?; + context.build_store(exponent_pointer, exponent)?; + + let arguments = &[ + result_pointer.to_int(context).into(), + value_pointer.to_int(context).into(), + exponent_pointer.to_int(context).into(), + ]; + + context.build_runtime_call(revive_runtime_api::polkavm_imports::EXP, arguments); + context.build_load(result_pointer, "exponent_result") } /// Translates the `signextend` instruction. @@ -78,11 +103,20 @@ pub fn sign_extend<'ctx, D>( where D: Dependency + Clone, { - Ok(context - .build_call( - context.llvm_runtime().sign_extend, - &[bytes.as_basic_value_enum(), value.as_basic_value_enum()], - "sign_extend_call", - ) - .expect("Always exists")) + let result_pointer = + context.build_alloca_at_entry(context.word_type(), "signext_result_pointer"); + let bytes_pointer = context.build_alloca_at_entry(context.word_type(), "bytes_pointer"); + let value_pointer = context.build_alloca_at_entry(context.word_type(), "signext_value_pointer"); + + context.build_store(bytes_pointer, bytes)?; + context.build_store(value_pointer, value)?; + + let arguments = &[ + result_pointer.to_int(context).into(), + bytes_pointer.to_int(context).into(), + value_pointer.to_int(context).into(), + ]; + + context.build_runtime_call(revive_runtime_api::polkavm_imports::EXP, arguments); + context.build_load(result_pointer, "signext_mod_result") } diff --git a/crates/runtime-api/src/polkavm_imports.c b/crates/runtime-api/src/polkavm_imports.c index 10f974d..af3402c 100644 --- a/crates/runtime-api/src/polkavm_imports.c +++ b/crates/runtime-api/src/polkavm_imports.c @@ -104,3 +104,19 @@ POLKAVM_IMPORT(uint64_t, set_storage, uint32_t, uint32_t, uint32_t, uint32_t, ui POLKAVM_IMPORT(void, value_transferred, uint32_t) POLKAVM_IMPORT(void, weight_to_fee, uint64_t, uint64_t, uint32_t); + +POLKAVM_IMPORT(void, div, uint32_t, uint32_t, uint32_t); + +POLKAVM_IMPORT(void, sdiv, uint32_t, uint32_t, uint32_t); + +POLKAVM_IMPORT(void, addmod, uint32_t, uint32_t, uint32_t, uint32_t); + +POLKAVM_IMPORT(void, mulmod, uint32_t, uint32_t, uint32_t, uint32_t); + +POLKAVM_IMPORT(void, mod, uint32_t, uint32_t, uint32_t); + +POLKAVM_IMPORT(void, smod, uint32_t, uint32_t, uint32_t); + +POLKAVM_IMPORT(void, exp, uint32_t, uint32_t, uint32_t); + +POLKAVM_IMPORT(void, signext, uint32_t, uint32_t, uint32_t); diff --git a/crates/runtime-api/src/polkavm_imports.rs b/crates/runtime-api/src/polkavm_imports.rs index 42ca88a..6b232a3 100644 --- a/crates/runtime-api/src/polkavm_imports.rs +++ b/crates/runtime-api/src/polkavm_imports.rs @@ -68,9 +68,27 @@ pub static VALUE_TRANSFERRED: &str = "value_transferred"; pub static WEIGHT_TO_FEE: &str = "weight_to_fee"; +pub static DIV: &str = "div"; +pub static SDIV: &str = "sdiv"; +pub static ADDMOD: &str = "addmod"; +pub static MULMOD: &str = "mulmod"; +pub static MOD: &str = "mod"; +pub static SMOD: &str = "smod"; +pub static EXP: &str = "exp"; +pub static SIGNEXT: &str = "signext"; + /// All imported runtime API symbols. /// Useful for configuring common attributes and linkage. -pub static IMPORTS: [&str; 33] = [ +pub static IMPORTS: [&str; 41] = [ + DIV, + SDIV, + ADDMOD, + MULMOD, + MOD, + SMOD, + EXP, + SIGNEXT, + // ADDRESS, BALANCE, BALANCE_OF,