simplify wrapped divisions and sdiv cfg

Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2024-05-10 10:16:40 +02:00
parent 6af889c1ff
commit 5f5ec1a539
+89 -114
View File
@@ -59,7 +59,7 @@ pub fn division<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
WrappedDivision::new(context, operand_2)?.with(|| { wrapped_division(context, operand_2, || {
Ok(context Ok(context
.builder() .builder()
.build_int_unsigned_div(operand_1, operand_2, "DIV")?) .build_int_unsigned_div(operand_1, operand_2, "DIV")?)
@@ -75,7 +75,7 @@ pub fn remainder<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
WrappedDivision::new(context, operand_2)?.with(|| { wrapped_division(context, operand_2, || {
Ok(context Ok(context
.builder() .builder()
.build_int_unsigned_rem(operand_1, operand_2, "MOD")?) .build_int_unsigned_rem(operand_1, operand_2, "MOD")?)
@@ -94,59 +94,54 @@ pub fn division_signed<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
WrappedDivision::new(context, operand_2)?.with(|| { assert_eq!(
let block_no_overflow = context.append_basic_block("no_overflow"); operand_2.get_type().get_bit_width(),
let block_operand_1_overflow = context.append_basic_block("operand_1_overflow"); revive_common::BIT_LENGTH_WORD as u32
let block_select_quotient = context.append_basic_block("block_select_quotient"); );
let max_uint = context.builder().build_int_z_extend( let block_calculate = context.append_basic_block("calculate");
context let block_overflow = context.append_basic_block("overflow");
.integer_type(revive_common::BIT_LENGTH_WORD - 1) let block_select = context.append_basic_block("select_result");
.const_all_ones(), let block_origin = context.basic_block();
context.word_type(), context.builder().build_switch(
"constant_zext_max_uint", operand_2,
)?; block_calculate,
let is_operand_1_overflow = context.builder().build_int_compare( &[
inkwell::IntPredicate::EQ, (context.word_type().const_zero(), block_select),
operand_1, (context.word_type().const_all_ones(), block_overflow),
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,
)?;
context.set_basic_block(block_operand_1_overflow); context.set_basic_block(block_calculate);
let is_operand_2_overflow = context.builder().build_int_compare( let quotient = context
inkwell::IntPredicate::EQ, .builder()
operand_2, .build_int_signed_div(operand_1, operand_2, "SDIV")?;
context.word_type().const_all_ones(), context.build_unconditional_branch(block_select);
"is_operand_2_overflow",
)?;
context.build_conditional_branch(
is_operand_2_overflow,
block_select_quotient,
block_no_overflow,
)?;
context.set_basic_block(block_no_overflow); context.set_basic_block(block_overflow);
let quotient = context let max_uint = context.builder().build_int_z_extend(
.builder() context
.build_int_signed_div(operand_1, operand_2, "SDIV")?; .integer_type(revive_common::BIT_LENGTH_WORD - 1)
context.build_unconditional_branch(block_select_quotient); .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); context.set_basic_block(block_select);
let phi_value = context let result = context.builder().build_phi(context.word_type(), "result")?;
.builder() result.add_incoming(&[
.build_phi(context.word_type(), "phi_quotient")?; (&operand_1, block_overflow),
phi_value.add_incoming(&[ (&context.word_const(0), block_origin),
(&quotient.as_basic_value_enum(), block_no_overflow), (&quotient.as_basic_value_enum(), block_calculate),
(&operand_1, block_operand_1_overflow), ]);
]); Ok(result.as_basic_value())
Ok(phi_value.as_basic_value().into_int_value())
})
} }
/// Translates the signed arithmetic remainder. /// Translates the signed arithmetic remainder.
@@ -158,73 +153,53 @@ pub fn remainder_signed<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
WrappedDivision::new(context, operand_2)?.with(|| { wrapped_division(context, operand_2, || {
Ok(context Ok(context
.builder() .builder()
.build_int_signed_rem(operand_1, operand_2, "SMOD")?) .build_int_signed_rem(operand_1, operand_2, "SMOD")?)
}) })
} }
/// Helper to wrap division operations so that zero will be returned /// Wrap division operations so that zero will be returned if the
/// if the denominator is zero (see also Ethereum YP Appendix H.2). /// denominator is zero (see also Ethereum YP Appendix H.2).
struct WrappedDivision<'a, 'ctx, D: Dependency + Clone> { ///
context: &'a Context<'ctx, D>, /// The closure is expected to calculate and return the quotient.
block_origin: inkwell::basic_block::BasicBlock<'ctx>, ///
block_calculate: inkwell::basic_block::BasicBlock<'ctx>, /// The result is either the calculated quotient or zero,
block_select: inkwell::basic_block::BasicBlock<'ctx>, /// selected at runtime.
} fn wrapped_division<'ctx, D, F, T>(
context: &Context<'ctx, D>,
impl<'a, 'ctx, D: Dependency + Clone> WrappedDivision<'a, 'ctx, D> { denominator: inkwell::values::IntValue<'ctx>,
/// Create a new wrapped division (inserts a switch on the denominator). f: F,
fn new( ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
context: &'a Context<'ctx, D>, where
denominator: inkwell::values::IntValue<'ctx>, D: Dependency + Clone,
) -> anyhow::Result<Self> { F: FnOnce() -> anyhow::Result<T>,
assert_eq!( T: inkwell::values::IntMathValue<'ctx>,
denominator.get_type().get_bit_width(), {
revive_common::BIT_LENGTH_WORD as u32 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( let block_calculate = context.append_basic_block("calculate");
denominator, let block_select = context.append_basic_block("select");
block_calculate, let block_origin = context.basic_block();
&[(context.word_const(0), block_select)], context.builder().build_switch(
)?; denominator,
block_calculate,
Ok(Self { &[(context.word_const(0), block_select)],
context, )?;
block_origin: context.basic_block(),
block_calculate, context.set_basic_block(block_calculate);
block_select, let calculated_value = f()?.as_basic_value_enum();
}) context.build_unconditional_branch(block_select);
}
context.set_basic_block(block_select);
/// Insert code to calculate the operation. let result = context.builder().build_phi(context.word_type(), "result")?;
/// result.add_incoming(&[
/// The closure is expected to calculate and return the quotient. (&context.word_const(0), block_origin),
/// (&calculated_value, block_calculate),
/// The returned value is either the calculated quotient or zero, selected at runtime. ]);
fn with<T, F>(self, f: F) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> Ok(result.as_basic_value())
where
F: FnOnce() -> anyhow::Result<T>,
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())
}
} }