mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-14 19:11:04 +00:00
94ec34c4d5
Separate between compilation and linker phases to allow deploy time linking and back-porting era compiler changes to fix #91. Unlinked contract binaries (caused by missing libraries or missing factory dependencies in turn) are emitted as raw ELF object. Few drive by fixes: - #98 - A compiler panic on missing libraries definitions. - Fixes some incosistent type forwarding in JSON output (empty string vs. null object). - Remove the unused fallback for size optimization setting. - Remove the broken `--lvm-ir` mode. - CI workflow fixes. --------- Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com> Signed-off-by: xermicus <bigcyrill@hotmail.com> Signed-off-by: xermicus <cyrill@parity.io>
264 lines
8.9 KiB
Rust
264 lines
8.9 KiB
Rust
//! Translates the bitwise operations.
|
|
|
|
use inkwell::values::BasicValue;
|
|
|
|
use revive_common::BIT_LENGTH_BYTE;
|
|
use revive_common::BIT_LENGTH_WORD;
|
|
|
|
use crate::polkavm::context::Context;
|
|
|
|
/// Translates the bitwise OR.
|
|
pub fn or<'ctx>(
|
|
context: &mut Context<'ctx>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
|
Ok(context
|
|
.builder()
|
|
.build_or(operand_1, operand_2, "or_result")?
|
|
.as_basic_value_enum())
|
|
}
|
|
|
|
/// Translates the bitwise XOR.
|
|
pub fn xor<'ctx>(
|
|
context: &mut Context<'ctx>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
|
Ok(context
|
|
.builder()
|
|
.build_xor(operand_1, operand_2, "xor_result")?
|
|
.as_basic_value_enum())
|
|
}
|
|
|
|
/// Translates the bitwise AND.
|
|
pub fn and<'ctx>(
|
|
context: &mut Context<'ctx>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
|
Ok(context
|
|
.builder()
|
|
.build_and(operand_1, operand_2, "and_result")?
|
|
.as_basic_value_enum())
|
|
}
|
|
|
|
/// Translates the bitwise shift left.
|
|
pub fn shift_left<'ctx>(
|
|
context: &mut Context<'ctx>,
|
|
shift: inkwell::values::IntValue<'ctx>,
|
|
value: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
|
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((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())
|
|
}
|
|
|
|
/// Translates the bitwise shift right.
|
|
pub fn shift_right<'ctx>(
|
|
context: &mut Context<'ctx>,
|
|
shift: inkwell::values::IntValue<'ctx>,
|
|
value: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
|
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((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())
|
|
}
|
|
|
|
/// Translates the arithmetic bitwise shift right.
|
|
pub fn shift_right_arithmetic<'ctx>(
|
|
context: &mut Context<'ctx>,
|
|
shift: inkwell::values::IntValue<'ctx>,
|
|
value: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
|
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((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((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())
|
|
}
|
|
|
|
/// Translates the `byte` instruction, extracting the byte of `operand_2`
|
|
/// found at index `operand_1`, starting from the most significant bit.
|
|
///
|
|
/// Builds a logical `and` with a corresponding bit mask.
|
|
///
|
|
/// Because this opcode returns zero on overflows, the index `operand_1`
|
|
/// is checked for overflow. On overflow, the mask will be all zeros,
|
|
/// resulting in a branchless implementation.
|
|
pub fn byte<'ctx>(
|
|
context: &mut Context<'ctx>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
|
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(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 * 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())
|
|
}
|