diff --git a/crates/llvm-context/src/polkavm/evm/arithmetic.rs b/crates/llvm-context/src/polkavm/evm/arithmetic.rs index ed808bd..49aaaab 100644 --- a/crates/llvm-context/src/polkavm/evm/arithmetic.rs +++ b/crates/llvm-context/src/polkavm/evm/arithmetic.rs @@ -59,7 +59,7 @@ pub fn division<'ctx, D>( where D: Dependency + Clone, { - WrappedDivision::new(context, operand_2)?.with(|| { + wrapped_division(context, operand_2, || { Ok(context .builder() .build_int_unsigned_div(operand_1, operand_2, "DIV")?) @@ -75,7 +75,7 @@ pub fn remainder<'ctx, D>( where D: Dependency + Clone, { - WrappedDivision::new(context, operand_2)?.with(|| { + wrapped_division(context, operand_2, || { Ok(context .builder() .build_int_unsigned_rem(operand_1, operand_2, "MOD")?) @@ -94,59 +94,54 @@ pub fn division_signed<'ctx, D>( where D: Dependency + Clone, { - WrappedDivision::new(context, operand_2)?.with(|| { - let block_no_overflow = context.append_basic_block("no_overflow"); - let block_operand_1_overflow = context.append_basic_block("operand_1_overflow"); - let block_select_quotient = context.append_basic_block("block_select_quotient"); + assert_eq!( + operand_2.get_type().get_bit_width(), + revive_common::BIT_LENGTH_WORD as u32 + ); - let max_uint = context.builder().build_int_z_extend( - context - .integer_type(revive_common::BIT_LENGTH_WORD - 1) - .const_all_ones(), - context.word_type(), - "constant_zext_max_uint", - )?; - let is_operand_1_overflow = context.builder().build_int_compare( - inkwell::IntPredicate::EQ, - operand_1, - context.builder().build_int_neg(max_uint, "min_uint")?, - "is_operand_1_overflow", - )?; - context.build_conditional_branch( - is_operand_1_overflow, - block_operand_1_overflow, - block_no_overflow, - )?; + let block_calculate = context.append_basic_block("calculate"); + let block_overflow = context.append_basic_block("overflow"); + let block_select = context.append_basic_block("select_result"); + let block_origin = context.basic_block(); + context.builder().build_switch( + operand_2, + block_calculate, + &[ + (context.word_type().const_zero(), block_select), + (context.word_type().const_all_ones(), block_overflow), + ], + )?; - context.set_basic_block(block_operand_1_overflow); - let is_operand_2_overflow = context.builder().build_int_compare( - inkwell::IntPredicate::EQ, - operand_2, - context.word_type().const_all_ones(), - "is_operand_2_overflow", - )?; - context.build_conditional_branch( - is_operand_2_overflow, - block_select_quotient, - block_no_overflow, - )?; + context.set_basic_block(block_calculate); + let quotient = context + .builder() + .build_int_signed_div(operand_1, operand_2, "SDIV")?; + context.build_unconditional_branch(block_select); - context.set_basic_block(block_no_overflow); - let quotient = context - .builder() - .build_int_signed_div(operand_1, operand_2, "SDIV")?; - context.build_unconditional_branch(block_select_quotient); + context.set_basic_block(block_overflow); + let max_uint = context.builder().build_int_z_extend( + context + .integer_type(revive_common::BIT_LENGTH_WORD - 1) + .const_all_ones(), + context.word_type(), + "max_uint", + )?; + let is_operand_1_overflow = context.builder().build_int_compare( + inkwell::IntPredicate::EQ, + operand_1, + context.builder().build_int_neg(max_uint, "min_uint")?, + "is_operand_1_overflow", + )?; + context.build_conditional_branch(is_operand_1_overflow, block_select, block_calculate)?; - context.set_basic_block(block_select_quotient); - let phi_value = context - .builder() - .build_phi(context.word_type(), "phi_quotient")?; - phi_value.add_incoming(&[ - ("ient.as_basic_value_enum(), block_no_overflow), - (&operand_1, block_operand_1_overflow), - ]); - Ok(phi_value.as_basic_value().into_int_value()) - }) + context.set_basic_block(block_select); + let result = context.builder().build_phi(context.word_type(), "result")?; + result.add_incoming(&[ + (&operand_1, block_overflow), + (&context.word_const(0), block_origin), + ("ient.as_basic_value_enum(), block_calculate), + ]); + Ok(result.as_basic_value()) } /// Translates the signed arithmetic remainder. @@ -158,73 +153,53 @@ pub fn remainder_signed<'ctx, D>( where D: Dependency + Clone, { - WrappedDivision::new(context, operand_2)?.with(|| { + wrapped_division(context, operand_2, || { Ok(context .builder() .build_int_signed_rem(operand_1, operand_2, "SMOD")?) }) } -/// Helper to wrap division operations so that zero will be returned -/// if the denominator is zero (see also Ethereum YP Appendix H.2). -struct WrappedDivision<'a, 'ctx, D: Dependency + Clone> { - context: &'a Context<'ctx, D>, - block_origin: inkwell::basic_block::BasicBlock<'ctx>, - block_calculate: inkwell::basic_block::BasicBlock<'ctx>, - block_select: inkwell::basic_block::BasicBlock<'ctx>, -} - -impl<'a, 'ctx, D: Dependency + Clone> WrappedDivision<'a, 'ctx, D> { - /// Create a new wrapped division (inserts a switch on the denominator). - fn new( - context: &'a Context<'ctx, D>, - denominator: inkwell::values::IntValue<'ctx>, - ) -> anyhow::Result { - assert_eq!( - denominator.get_type().get_bit_width(), - revive_common::BIT_LENGTH_WORD as u32 - ); - - let block_calculate = context.append_basic_block("calculate"); - let block_select = context.append_basic_block("select"); - context.builder().build_switch( - denominator, - block_calculate, - &[(context.word_const(0), block_select)], - )?; - - Ok(Self { - context, - block_origin: context.basic_block(), - block_calculate, - block_select, - }) - } - - /// Insert code to calculate the operation. - /// - /// The closure is expected to calculate and return the quotient. - /// - /// The returned value is either the calculated quotient or zero, selected at runtime. - fn with(self, f: F) -> anyhow::Result> - where - F: FnOnce() -> anyhow::Result, - T: inkwell::values::IntMathValue<'ctx>, - { - self.context.set_basic_block(self.block_calculate); - let calculated_value = f()?.as_basic_value_enum(); - let calculated_value_incoming_block = self.context.basic_block(); - self.context.build_unconditional_branch(self.block_select); - - self.context.set_basic_block(self.block_select); - let phi_value = self - .context - .builder() - .build_phi(self.context.word_type(), "phi_result")?; - phi_value.add_incoming(&[ - (&self.context.word_const(0), self.block_origin), - (&calculated_value, calculated_value_incoming_block), - ]); - Ok(phi_value.as_basic_value()) - } +/// Wrap division operations so that zero will be returned if the +/// denominator is zero (see also Ethereum YP Appendix H.2). +/// +/// The closure is expected to calculate and return the quotient. +/// +/// The result is either the calculated quotient or zero, +/// selected at runtime. +fn wrapped_division<'ctx, D, F, T>( + context: &Context<'ctx, D>, + denominator: inkwell::values::IntValue<'ctx>, + f: F, +) -> anyhow::Result> +where + D: Dependency + Clone, + F: FnOnce() -> anyhow::Result, + T: inkwell::values::IntMathValue<'ctx>, +{ + assert_eq!( + denominator.get_type().get_bit_width(), + revive_common::BIT_LENGTH_WORD as u32 + ); + + let block_calculate = context.append_basic_block("calculate"); + let block_select = context.append_basic_block("select"); + let block_origin = context.basic_block(); + context.builder().build_switch( + denominator, + block_calculate, + &[(context.word_const(0), block_select)], + )?; + + context.set_basic_block(block_calculate); + let calculated_value = f()?.as_basic_value_enum(); + context.build_unconditional_branch(block_select); + + context.set_basic_block(block_select); + let result = context.builder().build_phi(context.word_type(), "result")?; + result.add_incoming(&[ + (&context.word_const(0), block_origin), + (&calculated_value, block_calculate), + ]); + Ok(result.as_basic_value()) }