outline all arithmetic and bitwise functions

Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2025-07-03 16:03:40 +02:00
parent ed608699af
commit 4028f7a985
7 changed files with 714 additions and 205 deletions
+37 -194
View File
@@ -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 = <PolkaVMOrFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMOrFunction as RuntimeFunction<D>>::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 = <PolkaVMXorFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMXorFunction as RuntimeFunction<D>>::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 = <PolkaVMAndFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMAndFunction as RuntimeFunction<D>>::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 = <PolkaVMShlFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMShlFunction as RuntimeFunction<D>>::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 = <PolkaVMShrFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMShrFunction as RuntimeFunction<D>>::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 = <PolkaVMSarFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMSarFunction as RuntimeFunction<D>>::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 = <PolkaVMByteFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMByteFunction as RuntimeFunction<D>>::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")))
}