From 4028f7a9858c077795f21f29ee716fed06597855 Mon Sep 17 00:00:00 2001 From: xermicus Date: Thu, 3 Jul 2025 16:03:40 +0200 Subject: [PATCH] outline all arithmetic and bitwise functions Signed-off-by: xermicus --- crates/llvm-context/src/lib.rs | 10 + .../context/function/runtime/arithmetics.rs | 135 +++++ .../context/function/runtime/bitwise.rs | 494 ++++++++++++++++++ .../polkavm/context/function/runtime/mod.rs | 1 + .../src/polkavm/evm/arithmetic.rs | 26 +- .../llvm-context/src/polkavm/evm/bitwise.rs | 231 ++------ crates/yul/src/parser/statement/object.rs | 22 + 7 files changed, 714 insertions(+), 205 deletions(-) create mode 100644 crates/llvm-context/src/polkavm/context/function/runtime/bitwise.rs diff --git a/crates/llvm-context/src/lib.rs b/crates/llvm-context/src/lib.rs index c7a9ae7..98d21eb 100644 --- a/crates/llvm-context/src/lib.rs +++ b/crates/llvm-context/src/lib.rs @@ -19,10 +19,20 @@ pub use self::polkavm::context::function::declaration::Declaration as PolkaVMFun pub use self::polkavm::context::function::intrinsics::Intrinsics as PolkaVMIntrinsicFunction; pub use self::polkavm::context::function::llvm_runtime::LLVMRuntime as PolkaVMLLVMRuntime; pub use self::polkavm::context::function::r#return::Return as PolkaVMFunctionReturn; +pub use self::polkavm::context::function::runtime::arithmetics::Addition as PolkaVMAdditionFunction; pub use self::polkavm::context::function::runtime::arithmetics::Division as PolkaVMDivisionFunction; +pub use self::polkavm::context::function::runtime::arithmetics::Multiplication as PolkaVMMultiplicationFunction; pub use self::polkavm::context::function::runtime::arithmetics::Remainder as PolkaVMRemainderFunction; pub use self::polkavm::context::function::runtime::arithmetics::SignedDivision as PolkaVMSignedDivisionFunction; pub use self::polkavm::context::function::runtime::arithmetics::SignedRemainder as PolkaVMSignedRemainderFunction; +pub use self::polkavm::context::function::runtime::arithmetics::Subtraction as PolkaVMSubstractionFunction; +pub use self::polkavm::context::function::runtime::bitwise::And as PolkaVMAndFunction; +pub use self::polkavm::context::function::runtime::bitwise::Byte as PolkaVMByteFunction; +pub use self::polkavm::context::function::runtime::bitwise::Or as PolkaVMOrFunction; +pub use self::polkavm::context::function::runtime::bitwise::Sar as PolkaVMSarFunction; +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::deploy_code::DeployCode as PolkaVMDeployCodeFunction; pub use self::polkavm::context::function::runtime::entry::Entry as PolkaVMEntryFunction; pub use self::polkavm::context::function::runtime::revive::Exit as PolkaVMExitFunction; diff --git a/crates/llvm-context/src/polkavm/context/function/runtime/arithmetics.rs b/crates/llvm-context/src/polkavm/context/function/runtime/arithmetics.rs index ba9395f..f3be3ba 100644 --- a/crates/llvm-context/src/polkavm/context/function/runtime/arithmetics.rs +++ b/crates/llvm-context/src/polkavm/context/function/runtime/arithmetics.rs @@ -7,6 +7,141 @@ use crate::polkavm::context::Context; use crate::polkavm::Dependency; use crate::polkavm::WriteLLVM; +/// Implements the ADD operator according to the EVM specification. +pub struct Addition; + +impl RuntimeFunction for Addition +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_addition"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let operand_1 = Self::paramater(context, 0).into_int_value(); + let operand_2 = Self::paramater(context, 1).into_int_value(); + + Ok(Some( + context + .builder() + .build_int_add(operand_1, operand_2, "ADD") + .map(Into::into)?, + )) + } +} + +impl WriteLLVM for Addition +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) + } +} + +/// Implements the SUB operator according to the EVM specification. +pub struct Subtraction; + +impl RuntimeFunction for Subtraction +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_subtraction"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let operand_1 = Self::paramater(context, 0).into_int_value(); + let operand_2 = Self::paramater(context, 1).into_int_value(); + + Ok(Some( + context + .builder() + .build_int_sub(operand_1, operand_2, "SUB") + .map(Into::into)?, + )) + } +} + +impl WriteLLVM for Subtraction +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) + } +} + +/// Implements the MUL operator according to the EVM specification. +pub struct Multiplication; + +impl RuntimeFunction for Multiplication +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_multiplication"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let operand_1 = Self::paramater(context, 0).into_int_value(); + let operand_2 = Self::paramater(context, 1).into_int_value(); + + Ok(Some( + context + .builder() + .build_int_mul(operand_1, operand_2, "MUL") + .map(Into::into)?, + )) + } +} + +impl WriteLLVM for Multiplication +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) + } +} + /// Implements the division operator according to the EVM specification. pub struct Division; diff --git a/crates/llvm-context/src/polkavm/context/function/runtime/bitwise.rs b/crates/llvm-context/src/polkavm/context/function/runtime/bitwise.rs new file mode 100644 index 0000000..3eaeb8b --- /dev/null +++ b/crates/llvm-context/src/polkavm/context/function/runtime/bitwise.rs @@ -0,0 +1,494 @@ +//! Translates the arithmetic operations. + +use inkwell::values::BasicValue; + +use crate::polkavm::context::runtime::RuntimeFunction; +use crate::polkavm::context::Context; +use crate::polkavm::Dependency; +use crate::polkavm::WriteLLVM; + +/// Implements the OR operator according to the EVM specification. +pub struct Or; + +impl RuntimeFunction for Or +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_or"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let operand_1 = Self::paramater(context, 0).into_int_value(); + let operand_2 = Self::paramater(context, 1).into_int_value(); + + Ok(Some( + context + .builder() + .build_or(operand_1, operand_2, "OR") + .map(Into::into)?, + )) + } +} + +impl WriteLLVM for Or +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) + } +} + +/// Implements the XOR operator according to the EVM specification. +pub struct Xor; + +impl RuntimeFunction for Xor +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_xor"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let operand_1 = Self::paramater(context, 0).into_int_value(); + let operand_2 = Self::paramater(context, 1).into_int_value(); + + Ok(Some( + context + .builder() + .build_xor(operand_1, operand_2, "XOR") + .map(Into::into)?, + )) + } +} + +impl WriteLLVM for Xor +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) + } +} + +/// Implements the AND operator according to the EVM specification. +pub struct And; + +impl RuntimeFunction for And +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_and"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let operand_1 = Self::paramater(context, 0).into_int_value(); + let operand_2 = Self::paramater(context, 1).into_int_value(); + + Ok(Some( + context + .builder() + .build_and(operand_1, operand_2, "AND") + .map(Into::into)?, + )) + } +} + +impl WriteLLVM for And +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) + } +} +/// Implements the SHL operator according to the EVM specification. +pub struct Shl; + +impl RuntimeFunction for Shl +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_shl"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let shift = Self::paramater(context, 0).into_int_value(); + let value = Self::paramater(context, 1).into_int_value(); + + let overflow_block = context.append_basic_block("shift_left_overflow"); + let non_overflow_block = context.append_basic_block("shift_left_non_overflow"); + let join_block = context.append_basic_block("shift_left_join"); + + let condition_is_overflow = context.builder().build_int_compare( + inkwell::IntPredicate::UGT, + shift, + context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), + "shift_left_is_overflow", + )?; + context.build_conditional_branch( + condition_is_overflow, + overflow_block, + non_overflow_block, + )?; + + context.set_basic_block(overflow_block); + context.build_unconditional_branch(join_block); + + context.set_basic_block(non_overflow_block); + let value = + context + .builder() + .build_left_shift(value, shift, "shift_left_non_overflow_result")?; + context.build_unconditional_branch(join_block); + + context.set_basic_block(join_block); + let result = context + .builder() + .build_phi(context.word_type(), "shift_left_value")?; + result.add_incoming(&[ + (&value, non_overflow_block), + (&context.word_const(0), overflow_block), + ]); + Ok(Some(result.as_basic_value())) + } +} + +impl WriteLLVM for Shl +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) + } +} + +/// Implements the SHR operator according to the EVM specification. +pub struct Shr; + +impl RuntimeFunction for Shr +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_shr"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let shift = Self::paramater(context, 0).into_int_value(); + let value = Self::paramater(context, 1).into_int_value(); + + let overflow_block = context.append_basic_block("shift_right_overflow"); + let non_overflow_block = context.append_basic_block("shift_right_non_overflow"); + let join_block = context.append_basic_block("shift_right_join"); + + let condition_is_overflow = context.builder().build_int_compare( + inkwell::IntPredicate::UGT, + shift, + context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), + "shift_right_is_overflow", + )?; + context.build_conditional_branch( + condition_is_overflow, + overflow_block, + non_overflow_block, + )?; + + context.set_basic_block(overflow_block); + context.build_unconditional_branch(join_block); + + context.set_basic_block(non_overflow_block); + let value = context.builder().build_right_shift( + value, + shift, + false, + "shift_right_non_overflow_result", + )?; + context.build_unconditional_branch(join_block); + + context.set_basic_block(join_block); + let result = context + .builder() + .build_phi(context.word_type(), "shift_right_value")?; + result.add_incoming(&[ + (&value, non_overflow_block), + (&context.word_const(0), overflow_block), + ]); + Ok(Some(result.as_basic_value())) + } +} + +impl WriteLLVM for Shr +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) + } +} + +/// Implements the SAR operator according to the EVM specification. +pub struct Sar; + +impl RuntimeFunction for Sar +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_sar"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let shift = Self::paramater(context, 0).into_int_value(); + let value = Self::paramater(context, 1).into_int_value(); + + let overflow_block = context.append_basic_block("shift_right_arithmetic_overflow"); + let overflow_positive_block = + context.append_basic_block("shift_right_arithmetic_overflow_positive"); + let overflow_negative_block = + context.append_basic_block("shift_right_arithmetic_overflow_negative"); + let non_overflow_block = context.append_basic_block("shift_right_arithmetic_non_overflow"); + let join_block = context.append_basic_block("shift_right_arithmetic_join"); + + let condition_is_overflow = context.builder().build_int_compare( + inkwell::IntPredicate::UGT, + shift, + context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), + "shift_right_arithmetic_is_overflow", + )?; + context.build_conditional_branch( + condition_is_overflow, + overflow_block, + non_overflow_block, + )?; + + context.set_basic_block(overflow_block); + let sign_bit = context.builder().build_right_shift( + value, + context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), + false, + "shift_right_arithmetic_sign_bit", + )?; + let condition_is_negative = context.builder().build_int_truncate_or_bit_cast( + sign_bit, + context.bool_type(), + "shift_right_arithmetic_sign_bit_truncated", + )?; + context.build_conditional_branch( + condition_is_negative, + overflow_negative_block, + overflow_positive_block, + )?; + + context.set_basic_block(overflow_positive_block); + context.build_unconditional_branch(join_block); + + context.set_basic_block(overflow_negative_block); + context.build_unconditional_branch(join_block); + + context.set_basic_block(non_overflow_block); + let value = context.builder().build_right_shift( + value, + shift, + true, + "shift_right_arithmetic_non_overflow_result", + )?; + context.build_unconditional_branch(join_block); + + context.set_basic_block(join_block); + let result = context + .builder() + .build_phi(context.word_type(), "shift_arithmetic_right_value")?; + result.add_incoming(&[ + (&value, non_overflow_block), + ( + &context.word_type().const_all_ones(), + overflow_negative_block, + ), + (&context.word_const(0), overflow_positive_block), + ]); + Ok(Some(result.as_basic_value())) + } +} + +impl WriteLLVM for Sar +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) + } +} +/// Implements the BYTE operator according to the EVM specification. +pub struct Byte; + +impl RuntimeFunction for Byte +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_byte"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.word_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + let operand_1 = Self::paramater(context, 0).into_int_value(); + let operand_2 = Self::paramater(context, 1).into_int_value(); + const MAX_INDEX_BYTES: u64 = 31; + + let is_overflow_bit = context.builder().build_int_compare( + inkwell::IntPredicate::ULE, + operand_1, + context.word_const(MAX_INDEX_BYTES), + "is_overflow_bit", + )?; + let is_overflow_byte = context.builder().build_int_z_extend( + is_overflow_bit, + context.byte_type(), + "is_overflow_byte", + )?; + let mask_byte = context.builder().build_int_mul( + context.byte_type().const_all_ones(), + is_overflow_byte, + "mask_byte", + )?; + let mask_byte_word = context.builder().build_int_z_extend( + mask_byte, + context.word_type(), + "mask_byte_word", + )?; + + let index_truncated = context.builder().build_int_truncate( + operand_1, + context.byte_type(), + "index_truncated", + )?; + let index_in_bits = context.builder().build_int_mul( + index_truncated, + context + .byte_type() + .const_int(revive_common::BIT_LENGTH_BYTE as u64, false), + "index_in_bits", + )?; + let index_from_most_significant_bit = context.builder().build_int_sub( + context.byte_type().const_int( + MAX_INDEX_BYTES * revive_common::BIT_LENGTH_BYTE as u64, + false, + ), + index_in_bits, + "index_from_msb", + )?; + let index_extended = context.builder().build_int_z_extend( + index_from_most_significant_bit, + context.word_type(), + "index", + )?; + + let mask = context + .builder() + .build_left_shift(mask_byte_word, index_extended, "mask")?; + let masked_value = context.builder().build_and(operand_2, mask, "masked")?; + let byte = + context + .builder() + .build_right_shift(masked_value, index_extended, false, "byte")?; + + Ok(Some(byte.as_basic_value_enum())) + } +} + +impl WriteLLVM for Byte +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/context/function/runtime/mod.rs b/crates/llvm-context/src/polkavm/context/function/runtime/mod.rs index ec90f4b..089de6e 100644 --- a/crates/llvm-context/src/polkavm/context/function/runtime/mod.rs +++ b/crates/llvm-context/src/polkavm/context/function/runtime/mod.rs @@ -1,6 +1,7 @@ //! The front-end runtime functions. pub mod arithmetics; +pub mod bitwise; pub mod deploy_code; pub mod entry; pub mod revive; diff --git a/crates/llvm-context/src/polkavm/evm/arithmetic.rs b/crates/llvm-context/src/polkavm/evm/arithmetic.rs index 143cf1c..5b49cc4 100644 --- a/crates/llvm-context/src/polkavm/evm/arithmetic.rs +++ b/crates/llvm-context/src/polkavm/evm/arithmetic.rs @@ -1,14 +1,15 @@ //! Translates the arithmetic operations. -use inkwell::values::BasicValue; - use crate::polkavm::context::runtime::RuntimeFunction; use crate::polkavm::context::Context; use crate::polkavm::Dependency; +use crate::PolkaVMAdditionFunction; use crate::PolkaVMDivisionFunction; +use crate::PolkaVMMultiplicationFunction; use crate::PolkaVMRemainderFunction; use crate::PolkaVMSignedDivisionFunction; use crate::PolkaVMSignedRemainderFunction; +use crate::PolkaVMSubstractionFunction; /// Translates the arithmetic addition. pub fn addition<'ctx, D>( @@ -19,10 +20,11 @@ pub fn addition<'ctx, D>( where D: Dependency + Clone, { + let name = >::NAME; + let declaration = >::declaration(context); Ok(context - .builder() - .build_int_add(operand_1, operand_2, "addition_result")? - .as_basic_value_enum()) + .build_call(declaration, &[operand_1.into(), operand_2.into()], "SUB") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } /// Translates the arithmetic subtraction. @@ -34,10 +36,11 @@ pub fn subtraction<'ctx, D>( where D: Dependency + Clone, { + let name = >::NAME; + let declaration = >::declaration(context); Ok(context - .builder() - .build_int_sub(operand_1, operand_2, "subtraction_result")? - .as_basic_value_enum()) + .build_call(declaration, &[operand_1.into(), operand_2.into()], "SUB") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } /// Translates the arithmetic multiplication. @@ -49,10 +52,11 @@ pub fn multiplication<'ctx, D>( where D: Dependency + Clone, { + let name = >::NAME; + let declaration = >::declaration(context); Ok(context - .builder() - .build_int_mul(operand_1, operand_2, "multiplication_result")? - .as_basic_value_enum()) + .build_call(declaration, &[operand_1.into(), operand_2.into()], "MUL") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } /// Translates the arithmetic division. diff --git a/crates/llvm-context/src/polkavm/evm/bitwise.rs b/crates/llvm-context/src/polkavm/evm/bitwise.rs index b4a0217..aa2f1b5 100644 --- a/crates/llvm-context/src/polkavm/evm/bitwise.rs +++ b/crates/llvm-context/src/polkavm/evm/bitwise.rs @@ -1,9 +1,12 @@ //! Translates the bitwise operations. -use inkwell::values::BasicValue; - +use crate::polkavm::context::runtime::RuntimeFunction; use crate::polkavm::context::Context; use crate::polkavm::Dependency; +use crate::{ + PolkaVMAndFunction, PolkaVMByteFunction, PolkaVMOrFunction, PolkaVMSarFunction, + PolkaVMShlFunction, PolkaVMShrFunction, PolkaVMXorFunction, +}; /// Translates the bitwise OR. pub fn or<'ctx, D>( @@ -14,10 +17,11 @@ pub fn or<'ctx, D>( where D: Dependency + Clone, { + let name = >::NAME; + let declaration = >::declaration(context); Ok(context - .builder() - .build_or(operand_1, operand_2, "or_result")? - .as_basic_value_enum()) + .build_call(declaration, &[operand_1.into(), operand_2.into()], "OR") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } /// Translates the bitwise XOR. @@ -29,10 +33,11 @@ pub fn xor<'ctx, D>( where D: Dependency + Clone, { + let name = >::NAME; + let declaration = >::declaration(context); Ok(context - .builder() - .build_xor(operand_1, operand_2, "xor_result")? - .as_basic_value_enum()) + .build_call(declaration, &[operand_1.into(), operand_2.into()], "XOR") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } /// Translates the bitwise AND. @@ -44,10 +49,11 @@ pub fn and<'ctx, D>( where D: Dependency + Clone, { + let name = >::NAME; + let declaration = >::declaration(context); Ok(context - .builder() - .build_and(operand_1, operand_2, "and_result")? - .as_basic_value_enum()) + .build_call(declaration, &[operand_1.into(), operand_2.into()], "AND") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } /// Translates the bitwise shift left. @@ -59,37 +65,11 @@ pub fn shift_left<'ctx, D>( where D: Dependency + Clone, { - let overflow_block = context.append_basic_block("shift_left_overflow"); - let non_overflow_block = context.append_basic_block("shift_left_non_overflow"); - let join_block = context.append_basic_block("shift_left_join"); - - let condition_is_overflow = context.builder().build_int_compare( - inkwell::IntPredicate::UGT, - shift, - context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), - "shift_left_is_overflow", - )?; - context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; - - context.set_basic_block(overflow_block); - context.build_unconditional_branch(join_block); - - context.set_basic_block(non_overflow_block); - let value = - context - .builder() - .build_left_shift(value, shift, "shift_left_non_overflow_result")?; - context.build_unconditional_branch(join_block); - - context.set_basic_block(join_block); - let result = context - .builder() - .build_phi(context.word_type(), "shift_left_value")?; - result.add_incoming(&[ - (&value, non_overflow_block), - (&context.word_const(0), overflow_block), - ]); - Ok(result.as_basic_value()) + let name = >::NAME; + let declaration = >::declaration(context); + Ok(context + .build_call(declaration, &[shift.into(), value.into()], "SHL") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } /// Translates the bitwise shift right. @@ -101,39 +81,11 @@ pub fn shift_right<'ctx, D>( where D: Dependency + Clone, { - let overflow_block = context.append_basic_block("shift_right_overflow"); - let non_overflow_block = context.append_basic_block("shift_right_non_overflow"); - let join_block = context.append_basic_block("shift_right_join"); - - let condition_is_overflow = context.builder().build_int_compare( - inkwell::IntPredicate::UGT, - shift, - context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), - "shift_right_is_overflow", - )?; - context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; - - context.set_basic_block(overflow_block); - context.build_unconditional_branch(join_block); - - context.set_basic_block(non_overflow_block); - let value = context.builder().build_right_shift( - value, - shift, - false, - "shift_right_non_overflow_result", - )?; - context.build_unconditional_branch(join_block); - - context.set_basic_block(join_block); - let result = context - .builder() - .build_phi(context.word_type(), "shift_right_value")?; - result.add_incoming(&[ - (&value, non_overflow_block), - (&context.word_const(0), overflow_block), - ]); - Ok(result.as_basic_value()) + let name = >::NAME; + let declaration = >::declaration(context); + Ok(context + .build_call(declaration, &[shift.into(), value.into()], "SHR") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } /// Translates the arithmetic bitwise shift right. @@ -145,68 +97,11 @@ pub fn shift_right_arithmetic<'ctx, D>( where D: Dependency + Clone, { - let overflow_block = context.append_basic_block("shift_right_arithmetic_overflow"); - let overflow_positive_block = - context.append_basic_block("shift_right_arithmetic_overflow_positive"); - let overflow_negative_block = - context.append_basic_block("shift_right_arithmetic_overflow_negative"); - let non_overflow_block = context.append_basic_block("shift_right_arithmetic_non_overflow"); - let join_block = context.append_basic_block("shift_right_arithmetic_join"); - - let condition_is_overflow = context.builder().build_int_compare( - inkwell::IntPredicate::UGT, - shift, - context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), - "shift_right_arithmetic_is_overflow", - )?; - context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; - - context.set_basic_block(overflow_block); - let sign_bit = context.builder().build_right_shift( - value, - context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), - false, - "shift_right_arithmetic_sign_bit", - )?; - let condition_is_negative = context.builder().build_int_truncate_or_bit_cast( - sign_bit, - context.bool_type(), - "shift_right_arithmetic_sign_bit_truncated", - )?; - context.build_conditional_branch( - condition_is_negative, - overflow_negative_block, - overflow_positive_block, - )?; - - context.set_basic_block(overflow_positive_block); - context.build_unconditional_branch(join_block); - - context.set_basic_block(overflow_negative_block); - context.build_unconditional_branch(join_block); - - context.set_basic_block(non_overflow_block); - let value = context.builder().build_right_shift( - value, - shift, - true, - "shift_right_arithmetic_non_overflow_result", - )?; - context.build_unconditional_branch(join_block); - - context.set_basic_block(join_block); - let result = context - .builder() - .build_phi(context.word_type(), "shift_arithmetic_right_value")?; - result.add_incoming(&[ - (&value, non_overflow_block), - ( - &context.word_type().const_all_ones(), - overflow_negative_block, - ), - (&context.word_const(0), overflow_positive_block), - ]); - Ok(result.as_basic_value()) + let name = >::NAME; + let declaration = >::declaration(context); + Ok(context + .build_call(declaration, &[shift.into(), value.into()], "SHR") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } /// Translates the `byte` instruction, extracting the byte of `operand_2` @@ -225,61 +120,9 @@ pub fn byte<'ctx, D>( where D: Dependency + Clone, { - const MAX_INDEX_BYTES: u64 = 31; - - let is_overflow_bit = context.builder().build_int_compare( - inkwell::IntPredicate::ULE, - operand_1, - context.word_const(MAX_INDEX_BYTES), - "is_overflow_bit", - )?; - let is_overflow_byte = context.builder().build_int_z_extend( - is_overflow_bit, - context.byte_type(), - "is_overflow_byte", - )?; - let mask_byte = context.builder().build_int_mul( - context.byte_type().const_all_ones(), - is_overflow_byte, - "mask_byte", - )?; - let mask_byte_word = - context - .builder() - .build_int_z_extend(mask_byte, context.word_type(), "mask_byte_word")?; - - let index_truncated = - context - .builder() - .build_int_truncate(operand_1, context.byte_type(), "index_truncated")?; - let index_in_bits = context.builder().build_int_mul( - index_truncated, - context - .byte_type() - .const_int(revive_common::BIT_LENGTH_BYTE as u64, false), - "index_in_bits", - )?; - let index_from_most_significant_bit = context.builder().build_int_sub( - context.byte_type().const_int( - MAX_INDEX_BYTES * revive_common::BIT_LENGTH_BYTE as u64, - false, - ), - index_in_bits, - "index_from_msb", - )?; - let index_extended = context.builder().build_int_z_extend( - index_from_most_significant_bit, - context.word_type(), - "index", - )?; - - let mask = context - .builder() - .build_left_shift(mask_byte_word, index_extended, "mask")?; - let masked_value = context.builder().build_and(operand_2, mask, "masked")?; - let byte = context - .builder() - .build_right_shift(masked_value, index_extended, false, "byte")?; - - Ok(byte.as_basic_value_enum()) + let name = >::NAME; + let declaration = >::declaration(context); + Ok(context + .build_call(declaration, &[operand_1.into(), operand_2.into()], "BYTE") + .unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))) } diff --git a/crates/yul/src/parser/statement/object.rs b/crates/yul/src/parser/statement/object.rs index 7af755d..452bb21 100644 --- a/crates/yul/src/parser/statement/object.rs +++ b/crates/yul/src/parser/statement/object.rs @@ -204,11 +204,22 @@ where revive_llvm_context::PolkaVMEventLogFunction::<3>.declare(context)?; revive_llvm_context::PolkaVMEventLogFunction::<4>.declare(context)?; + revive_llvm_context::PolkaVMAdditionFunction.declare(context)?; + revive_llvm_context::PolkaVMSubstractionFunction.declare(context)?; + revive_llvm_context::PolkaVMMultiplicationFunction.declare(context)?; revive_llvm_context::PolkaVMDivisionFunction.declare(context)?; revive_llvm_context::PolkaVMSignedDivisionFunction.declare(context)?; revive_llvm_context::PolkaVMRemainderFunction.declare(context)?; revive_llvm_context::PolkaVMSignedRemainderFunction.declare(context)?; + revive_llvm_context::PolkaVMOrFunction.declare(context)?; + revive_llvm_context::PolkaVMXorFunction.declare(context)?; + revive_llvm_context::PolkaVMAndFunction.declare(context)?; + revive_llvm_context::PolkaVMShlFunction.declare(context)?; + revive_llvm_context::PolkaVMShrFunction.declare(context)?; + revive_llvm_context::PolkaVMSarFunction.declare(context)?; + revive_llvm_context::PolkaVMByteFunction.declare(context)?; + revive_llvm_context::PolkaVMSbrkFunction.declare(context)?; let mut entry = revive_llvm_context::PolkaVMEntryFunction::default(); @@ -258,11 +269,22 @@ where revive_llvm_context::PolkaVMEventLogFunction::<3>.into_llvm(context)?; revive_llvm_context::PolkaVMEventLogFunction::<4>.into_llvm(context)?; + revive_llvm_context::PolkaVMAdditionFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMSubstractionFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMMultiplicationFunction.into_llvm(context)?; revive_llvm_context::PolkaVMDivisionFunction.into_llvm(context)?; revive_llvm_context::PolkaVMSignedDivisionFunction.into_llvm(context)?; revive_llvm_context::PolkaVMRemainderFunction.into_llvm(context)?; revive_llvm_context::PolkaVMSignedRemainderFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMOrFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMXorFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMAndFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMShlFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMShrFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMSarFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMByteFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?; Ok(())