mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-14 20:21:07 +00:00
Compare commits
3 Commits
v0.4.1
...
cl/outline
| Author | SHA1 | Date | |
|---|---|---|---|
| 0685df37a1 | |||
| 3204b61ea0 | |||
| 4028f7a985 |
@@ -19,10 +19,22 @@ 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::intrinsics::Intrinsics as PolkaVMIntrinsicFunction;
|
||||||
pub use self::polkavm::context::function::llvm_runtime::LLVMRuntime as PolkaVMLLVMRuntime;
|
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::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::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::Remainder as PolkaVMRemainderFunction;
|
||||||
pub use self::polkavm::context::function::runtime::arithmetics::SignedDivision as PolkaVMSignedDivisionFunction;
|
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::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::call::Call as PolkaVMCall;
|
||||||
|
pub use self::polkavm::context::function::runtime::call::CallReentrancyProtector as PolkaVMCallReentrancyProtector;
|
||||||
pub use self::polkavm::context::function::runtime::deploy_code::DeployCode as PolkaVMDeployCodeFunction;
|
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::entry::Entry as PolkaVMEntryFunction;
|
||||||
pub use self::polkavm::context::function::runtime::revive::Exit as PolkaVMExitFunction;
|
pub use self::polkavm::context::function::runtime::revive::Exit as PolkaVMExitFunction;
|
||||||
|
|||||||
@@ -7,6 +7,141 @@ use crate::polkavm::context::Context;
|
|||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
use crate::polkavm::WriteLLVM;
|
use crate::polkavm::WriteLLVM;
|
||||||
|
|
||||||
|
/// Implements the ADD operator according to the EVM specification.
|
||||||
|
pub struct Addition;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for Addition
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the SUB operator according to the EVM specification.
|
||||||
|
pub struct Subtraction;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for Subtraction
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the MUL operator according to the EVM specification.
|
||||||
|
pub struct Multiplication;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for Multiplication
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Implements the division operator according to the EVM specification.
|
/// Implements the division operator according to the EVM specification.
|
||||||
pub struct Division;
|
pub struct Division;
|
||||||
|
|
||||||
|
|||||||
@@ -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<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for Or
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the XOR operator according to the EVM specification.
|
||||||
|
pub struct Xor;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for Xor
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the AND operator according to the EVM specification.
|
||||||
|
pub struct And;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for And
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Implements the SHL operator according to the EVM specification.
|
||||||
|
pub struct Shl;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for Shl
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the SHR operator according to the EVM specification.
|
||||||
|
pub struct Shr;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for Shr
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the SAR operator according to the EVM specification.
|
||||||
|
pub struct Sar;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for Sar
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Implements the BYTE operator according to the EVM specification.
|
||||||
|
pub struct Byte;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> 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<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
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<D> WriteLLVM<D> for Byte
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
//! Translates the arithmetic operations.
|
||||||
|
|
||||||
|
use inkwell::values::BasicValue;
|
||||||
|
|
||||||
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||||
|
use crate::polkavm::context::Context;
|
||||||
|
use crate::polkavm::context::Pointer;
|
||||||
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::polkavm::WriteLLVM;
|
||||||
|
|
||||||
|
const SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD: u64 = 2300;
|
||||||
|
|
||||||
|
/// The Solidity `address.transfer` and `address.send` call detection heuristic.
|
||||||
|
///
|
||||||
|
/// # Why
|
||||||
|
/// This heuristic is an additional security feature to guard against re-entrancy attacks
|
||||||
|
/// in case contract authors violate Solidity best practices and use `address.transfer` or
|
||||||
|
/// `address.send`.
|
||||||
|
/// While contract authors are supposed to never use `address.transfer` or `address.send`,
|
||||||
|
/// for a small cost we can be extra defensive about it.
|
||||||
|
///
|
||||||
|
/// # How
|
||||||
|
/// The gas stipend emitted by solc for `transfer` and `send` is not static, thus:
|
||||||
|
/// - Dynamically allow re-entrancy only for calls considered not transfer or send.
|
||||||
|
/// - Detected balance transfers will supply 0 deposit limit instead of `u256::MAX`.
|
||||||
|
///
|
||||||
|
/// Calls are considered transfer or send if:
|
||||||
|
/// - (Input length | Output lenght) == 0;
|
||||||
|
/// - Gas <= 2300;
|
||||||
|
///
|
||||||
|
/// # Arguments:
|
||||||
|
/// - The deposit value pointer.
|
||||||
|
/// - The gas value.
|
||||||
|
/// - `input_length | output_length`.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// # Returns:
|
||||||
|
/// The call flags xlen `IntValue`
|
||||||
|
pub struct CallReentrancyProtector;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for CallReentrancyProtector
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_call_reentrancy_protector";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.xlen_type().fn_type(
|
||||||
|
&[
|
||||||
|
context.llvm().ptr_type(Default::default()).into(),
|
||||||
|
context.word_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let deposit_pointer = Self::paramater(context, 0).into_pointer_value();
|
||||||
|
let gas = Self::paramater(context, 1).into_int_value();
|
||||||
|
let input_length_or_output_length = Self::paramater(context, 2).into_int_value();
|
||||||
|
|
||||||
|
// Branch-free SSA implementation: First derive the heuristic boolean (int1) value.
|
||||||
|
let is_no_input_no_output = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::EQ,
|
||||||
|
context.xlen_type().const_zero(),
|
||||||
|
input_length_or_output_length,
|
||||||
|
"is_no_input_no_output",
|
||||||
|
)?;
|
||||||
|
let gas_stipend = context
|
||||||
|
.word_type()
|
||||||
|
.const_int(SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD, false);
|
||||||
|
let is_gas_stipend_for_transfer_or_send = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::ULE,
|
||||||
|
gas,
|
||||||
|
gas_stipend,
|
||||||
|
"is_gas_stipend_for_transfer_or_send",
|
||||||
|
)?;
|
||||||
|
let is_balance_transfer = context.builder().build_and(
|
||||||
|
is_no_input_no_output,
|
||||||
|
is_gas_stipend_for_transfer_or_send,
|
||||||
|
"is_balance_transfer",
|
||||||
|
)?;
|
||||||
|
let is_regular_call = context
|
||||||
|
.builder()
|
||||||
|
.build_not(is_balance_transfer, "is_balance_transfer_inverted")?;
|
||||||
|
|
||||||
|
// Call flag: Left shift the heuristic boolean value.
|
||||||
|
let is_regular_call_xlen = context.builder().build_int_z_extend(
|
||||||
|
is_regular_call,
|
||||||
|
context.xlen_type(),
|
||||||
|
"is_balance_transfer_xlen",
|
||||||
|
)?;
|
||||||
|
let call_flags = context.builder().build_left_shift(
|
||||||
|
is_regular_call_xlen,
|
||||||
|
context.xlen_type().const_int(3, false),
|
||||||
|
"flags",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Deposit limit value: Sign-extended the heuristic boolean value.
|
||||||
|
let deposit_limit_value = context.builder().build_int_s_extend(
|
||||||
|
is_regular_call,
|
||||||
|
context.word_type(),
|
||||||
|
"deposit_limit_value",
|
||||||
|
)?;
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_store(deposit_pointer, deposit_limit_value)?;
|
||||||
|
|
||||||
|
Ok(Some(call_flags.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for CallReentrancyProtector
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the CALL operator according to the EVM specification.
|
||||||
|
///
|
||||||
|
/// # Arguments:
|
||||||
|
/// - The address value.
|
||||||
|
/// - The value value.
|
||||||
|
/// - The input offset.
|
||||||
|
/// - The input length.
|
||||||
|
/// - The output offset.
|
||||||
|
/// - The output length.
|
||||||
|
/// - The deposit limit pointer.
|
||||||
|
/// - The call flags.
|
||||||
|
///
|
||||||
|
/// # Returns:
|
||||||
|
/// - The success value (as xlen)
|
||||||
|
pub struct Call;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Call
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_call";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.register_type().fn_type(
|
||||||
|
&[
|
||||||
|
context.word_type().into(),
|
||||||
|
context.word_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
context.llvm().ptr_type(Default::default()).into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let address = Self::paramater(context, 0).into_int_value();
|
||||||
|
let value = Self::paramater(context, 1).into_int_value();
|
||||||
|
let input_offset = Self::paramater(context, 2).into_int_value();
|
||||||
|
let input_length = Self::paramater(context, 3).into_int_value();
|
||||||
|
let output_offset = Self::paramater(context, 4).into_int_value();
|
||||||
|
let output_length = Self::paramater(context, 5).into_int_value();
|
||||||
|
let depsit_limit_pointer = Self::paramater(context, 6).into_pointer_value();
|
||||||
|
let flags = Self::paramater(context, 7).into_int_value();
|
||||||
|
|
||||||
|
let address_pointer = context.build_address_argument_store(address)?;
|
||||||
|
|
||||||
|
let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer");
|
||||||
|
context.build_store(value_pointer, value)?;
|
||||||
|
|
||||||
|
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
|
||||||
|
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
|
||||||
|
|
||||||
|
let output_length_pointer =
|
||||||
|
context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
||||||
|
context.build_store(output_length_pointer, output_length)?;
|
||||||
|
|
||||||
|
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
flags,
|
||||||
|
address_pointer.to_int(context),
|
||||||
|
"address_and_callee",
|
||||||
|
)?;
|
||||||
|
let deposit_limit_pointer = Pointer::new(
|
||||||
|
context.word_type(),
|
||||||
|
Default::default(),
|
||||||
|
depsit_limit_pointer,
|
||||||
|
);
|
||||||
|
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
deposit_limit_pointer.to_int(context),
|
||||||
|
value_pointer.to_int(context),
|
||||||
|
"deposit_and_value",
|
||||||
|
)?;
|
||||||
|
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
input_length,
|
||||||
|
input_pointer.to_int(context),
|
||||||
|
"input_data",
|
||||||
|
)?;
|
||||||
|
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
output_length_pointer.to_int(context),
|
||||||
|
output_pointer.to_int(context),
|
||||||
|
"output_data",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let name = revive_runtime_api::polkavm_imports::CALL;
|
||||||
|
let success = context
|
||||||
|
.build_runtime_call(
|
||||||
|
name,
|
||||||
|
&[
|
||||||
|
flags_and_callee.into(),
|
||||||
|
context.register_type().const_all_ones().into(),
|
||||||
|
context.register_type().const_all_ones().into(),
|
||||||
|
deposit_and_value.into(),
|
||||||
|
input_data.into(),
|
||||||
|
output_data.into(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| panic!("{name} should return a value"))
|
||||||
|
.into_int_value();
|
||||||
|
Ok(Some(success.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Call
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
//! The front-end runtime functions.
|
//! The front-end runtime functions.
|
||||||
|
|
||||||
pub mod arithmetics;
|
pub mod arithmetics;
|
||||||
|
pub mod bitwise;
|
||||||
|
pub mod call;
|
||||||
pub mod deploy_code;
|
pub mod deploy_code;
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
pub mod revive;
|
pub mod revive;
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
//! Translates the arithmetic operations.
|
//! Translates the arithmetic operations.
|
||||||
|
|
||||||
use inkwell::values::BasicValue;
|
|
||||||
|
|
||||||
use crate::polkavm::context::runtime::RuntimeFunction;
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::PolkaVMAdditionFunction;
|
||||||
use crate::PolkaVMDivisionFunction;
|
use crate::PolkaVMDivisionFunction;
|
||||||
|
use crate::PolkaVMMultiplicationFunction;
|
||||||
use crate::PolkaVMRemainderFunction;
|
use crate::PolkaVMRemainderFunction;
|
||||||
use crate::PolkaVMSignedDivisionFunction;
|
use crate::PolkaVMSignedDivisionFunction;
|
||||||
use crate::PolkaVMSignedRemainderFunction;
|
use crate::PolkaVMSignedRemainderFunction;
|
||||||
|
use crate::PolkaVMSubstractionFunction;
|
||||||
|
|
||||||
/// Translates the arithmetic addition.
|
/// Translates the arithmetic addition.
|
||||||
pub fn addition<'ctx, D>(
|
pub fn addition<'ctx, D>(
|
||||||
@@ -19,10 +20,11 @@ pub fn addition<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMAdditionFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMAdditionFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "SUB")
|
||||||
.build_int_add(operand_1, operand_2, "addition_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the arithmetic subtraction.
|
/// Translates the arithmetic subtraction.
|
||||||
@@ -34,10 +36,11 @@ pub fn subtraction<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMSubstractionFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMSubstractionFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "SUB")
|
||||||
.build_int_sub(operand_1, operand_2, "subtraction_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the arithmetic multiplication.
|
/// Translates the arithmetic multiplication.
|
||||||
@@ -49,10 +52,11 @@ pub fn multiplication<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMMultiplicationFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMMultiplicationFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "MUL")
|
||||||
.build_int_mul(operand_1, operand_2, "multiplication_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the arithmetic division.
|
/// Translates the arithmetic division.
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
//! Translates the bitwise operations.
|
//! Translates the bitwise operations.
|
||||||
|
|
||||||
use inkwell::values::BasicValue;
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||||
|
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::{
|
||||||
|
PolkaVMAndFunction, PolkaVMByteFunction, PolkaVMOrFunction, PolkaVMSarFunction,
|
||||||
|
PolkaVMShlFunction, PolkaVMShrFunction, PolkaVMXorFunction,
|
||||||
|
};
|
||||||
|
|
||||||
/// Translates the bitwise OR.
|
/// Translates the bitwise OR.
|
||||||
pub fn or<'ctx, D>(
|
pub fn or<'ctx, D>(
|
||||||
@@ -14,10 +17,11 @@ pub fn or<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMOrFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMOrFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "OR")
|
||||||
.build_or(operand_1, operand_2, "or_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the bitwise XOR.
|
/// Translates the bitwise XOR.
|
||||||
@@ -29,10 +33,11 @@ pub fn xor<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMXorFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMXorFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "XOR")
|
||||||
.build_xor(operand_1, operand_2, "xor_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the bitwise AND.
|
/// Translates the bitwise AND.
|
||||||
@@ -44,10 +49,11 @@ pub fn and<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMAndFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMAndFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "AND")
|
||||||
.build_and(operand_1, operand_2, "and_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the bitwise shift left.
|
/// Translates the bitwise shift left.
|
||||||
@@ -59,37 +65,11 @@ pub fn shift_left<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
let overflow_block = context.append_basic_block("shift_left_overflow");
|
let name = <PolkaVMShlFunction as RuntimeFunction<D>>::NAME;
|
||||||
let non_overflow_block = context.append_basic_block("shift_left_non_overflow");
|
let declaration = <PolkaVMShlFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
let join_block = context.append_basic_block("shift_left_join");
|
Ok(context
|
||||||
|
.build_call(declaration, &[shift.into(), value.into()], "SHL")
|
||||||
let condition_is_overflow = context.builder().build_int_compare(
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the bitwise shift right.
|
/// Translates the bitwise shift right.
|
||||||
@@ -101,39 +81,11 @@ pub fn shift_right<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
let overflow_block = context.append_basic_block("shift_right_overflow");
|
let name = <PolkaVMShrFunction as RuntimeFunction<D>>::NAME;
|
||||||
let non_overflow_block = context.append_basic_block("shift_right_non_overflow");
|
let declaration = <PolkaVMShrFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
let join_block = context.append_basic_block("shift_right_join");
|
Ok(context
|
||||||
|
.build_call(declaration, &[shift.into(), value.into()], "SHR")
|
||||||
let condition_is_overflow = context.builder().build_int_compare(
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the arithmetic bitwise shift right.
|
/// Translates the arithmetic bitwise shift right.
|
||||||
@@ -145,68 +97,11 @@ pub fn shift_right_arithmetic<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
let overflow_block = context.append_basic_block("shift_right_arithmetic_overflow");
|
let name = <PolkaVMSarFunction as RuntimeFunction<D>>::NAME;
|
||||||
let overflow_positive_block =
|
let declaration = <PolkaVMSarFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
context.append_basic_block("shift_right_arithmetic_overflow_positive");
|
Ok(context
|
||||||
let overflow_negative_block =
|
.build_call(declaration, &[shift.into(), value.into()], "SHR")
|
||||||
context.append_basic_block("shift_right_arithmetic_overflow_negative");
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the `byte` instruction, extracting the byte of `operand_2`
|
/// Translates the `byte` instruction, extracting the byte of `operand_2`
|
||||||
@@ -225,61 +120,9 @@ pub fn byte<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
const MAX_INDEX_BYTES: u64 = 31;
|
let name = <PolkaVMByteFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMByteFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
let is_overflow_bit = context.builder().build_int_compare(
|
Ok(context
|
||||||
inkwell::IntPredicate::ULE,
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "BYTE")
|
||||||
operand_1,
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
use inkwell::values::BasicValue;
|
use inkwell::values::BasicValue;
|
||||||
|
|
||||||
use crate::polkavm::context::argument::Argument;
|
use crate::polkavm::context::argument::Argument;
|
||||||
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::{PolkaVMCall, PolkaVMCallReentrancyProtector};
|
||||||
|
|
||||||
const STATIC_CALL_FLAG: u32 = 0b0001_0000;
|
const STATIC_CALL_FLAG: u32 = 0b0001_0000;
|
||||||
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
|
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
|
||||||
const SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD: u64 = 2300;
|
|
||||||
|
|
||||||
/// Translates a contract call.
|
/// Translates a contract call.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@@ -27,79 +28,45 @@ pub fn call<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
let address_pointer = context.build_address_argument_store(address)?;
|
|
||||||
|
|
||||||
let value = value.unwrap_or_else(|| context.word_const(0));
|
|
||||||
let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer");
|
|
||||||
context.build_store(value_pointer, value)?;
|
|
||||||
|
|
||||||
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
||||||
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
|
||||||
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
|
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
|
||||||
|
|
||||||
|
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
||||||
let output_length = context.safe_truncate_int_to_xlen(output_length)?;
|
let output_length = context.safe_truncate_int_to_xlen(output_length)?;
|
||||||
|
|
||||||
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
|
let deposit_limit_pointer =
|
||||||
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
|
context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
|
||||||
|
let flags = if static_call {
|
||||||
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
|
||||||
context.build_store(output_length_pointer, output_length)?;
|
|
||||||
|
|
||||||
let (flags, deposit_limit_value) = if static_call {
|
|
||||||
let flags = REENTRANT_CALL_FLAG | STATIC_CALL_FLAG;
|
let flags = REENTRANT_CALL_FLAG | STATIC_CALL_FLAG;
|
||||||
(
|
context.build_store(deposit_limit_pointer, context.word_type().const_zero())?;
|
||||||
context.xlen_type().const_int(flags as u64, false),
|
context.xlen_type().const_int(flags as u64, false)
|
||||||
context.word_type().const_zero(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
call_reentrancy_heuristic(context, gas, input_length, output_length)?
|
call_reentrancy_heuristic(
|
||||||
|
context,
|
||||||
|
deposit_limit_pointer.value,
|
||||||
|
gas,
|
||||||
|
input_length,
|
||||||
|
output_length,
|
||||||
|
)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
|
let value = value.unwrap_or_else(|| context.word_const(0));
|
||||||
context.build_store(deposit_pointer, deposit_limit_value)?;
|
|
||||||
|
|
||||||
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
let name = <PolkaVMCall as RuntimeFunction<D>>::NAME;
|
||||||
context.builder(),
|
let declaration = <PolkaVMCall as RuntimeFunction<D>>::declaration(context);
|
||||||
context.llvm(),
|
let arguments = &[
|
||||||
flags,
|
address.into(),
|
||||||
address_pointer.to_int(context),
|
value.into(),
|
||||||
"address_and_callee",
|
input_offset.into(),
|
||||||
)?;
|
input_length.into(),
|
||||||
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
output_offset.into(),
|
||||||
context.builder(),
|
output_length.into(),
|
||||||
context.llvm(),
|
deposit_limit_pointer.value.into(),
|
||||||
deposit_pointer.to_int(context),
|
flags.into(),
|
||||||
value_pointer.to_int(context),
|
];
|
||||||
"deposit_and_value",
|
|
||||||
)?;
|
|
||||||
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
|
||||||
context.builder(),
|
|
||||||
context.llvm(),
|
|
||||||
input_length,
|
|
||||||
input_pointer.to_int(context),
|
|
||||||
"input_data",
|
|
||||||
)?;
|
|
||||||
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
|
||||||
context.builder(),
|
|
||||||
context.llvm(),
|
|
||||||
output_length_pointer.to_int(context),
|
|
||||||
output_pointer.to_int(context),
|
|
||||||
"output_data",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let name = revive_runtime_api::polkavm_imports::CALL;
|
|
||||||
let success = context
|
let success = context
|
||||||
.build_runtime_call(
|
.build_call(declaration, arguments, "call")
|
||||||
name,
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))
|
||||||
&[
|
|
||||||
flags_and_callee.into(),
|
|
||||||
context.register_type().const_all_ones().into(),
|
|
||||||
context.register_type().const_all_ones().into(),
|
|
||||||
deposit_and_value.into(),
|
|
||||||
input_data.into(),
|
|
||||||
output_data.into(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|| panic!("{name} should return a value"))
|
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
|
|
||||||
let is_success = context.builder().build_int_compare(
|
let is_success = context.builder().build_int_compare(
|
||||||
@@ -216,85 +183,28 @@ where
|
|||||||
.as_basic_value_enum())
|
.as_basic_value_enum())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Solidity `address.transfer` and `address.send` call detection heuristic.
|
|
||||||
///
|
|
||||||
/// # Why
|
|
||||||
/// This heuristic is an additional security feature to guard against re-entrancy attacks
|
|
||||||
/// in case contract authors violate Solidity best practices and use `address.transfer` or
|
|
||||||
/// `address.send`.
|
|
||||||
/// While contract authors are supposed to never use `address.transfer` or `address.send`,
|
|
||||||
/// for a small cost we can be extra defensive about it.
|
|
||||||
///
|
|
||||||
/// # How
|
|
||||||
/// The gas stipend emitted by solc for `transfer` and `send` is not static, thus:
|
|
||||||
/// - Dynamically allow re-entrancy only for calls considered not transfer or send.
|
|
||||||
/// - Detected balance transfers will supply 0 deposit limit instead of `u256::MAX`.
|
|
||||||
///
|
|
||||||
/// Calls are considered transfer or send if:
|
|
||||||
/// - (Input length | Output lenght) == 0;
|
|
||||||
/// - Gas <= 2300;
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// The call flags xlen `IntValue` and the deposit limit word `IntValue`.
|
|
||||||
fn call_reentrancy_heuristic<'ctx, D>(
|
fn call_reentrancy_heuristic<'ctx, D>(
|
||||||
context: &mut Context<'ctx, D>,
|
context: &mut Context<'ctx, D>,
|
||||||
|
deposit_limit_pointer: inkwell::values::PointerValue<'ctx>,
|
||||||
gas: inkwell::values::IntValue<'ctx>,
|
gas: inkwell::values::IntValue<'ctx>,
|
||||||
input_length: inkwell::values::IntValue<'ctx>,
|
input_length: inkwell::values::IntValue<'ctx>,
|
||||||
output_length: inkwell::values::IntValue<'ctx>,
|
output_length: inkwell::values::IntValue<'ctx>,
|
||||||
) -> anyhow::Result<(
|
) -> anyhow::Result<inkwell::values::IntValue<'ctx>>
|
||||||
inkwell::values::IntValue<'ctx>,
|
|
||||||
inkwell::values::IntValue<'ctx>,
|
|
||||||
)>
|
|
||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
// Branch-free SSA implementation: First derive the heuristic boolean (int1) value.
|
let name = <PolkaVMCallReentrancyProtector as RuntimeFunction<D>>::NAME;
|
||||||
let input_length_or_output_length =
|
let declaration = <PolkaVMCallReentrancyProtector as RuntimeFunction<D>>::declaration(context);
|
||||||
|
let arguments = &[
|
||||||
|
deposit_limit_pointer.into(),
|
||||||
|
gas.into(),
|
||||||
context
|
context
|
||||||
.builder()
|
.builder()
|
||||||
.build_or(input_length, output_length, "input_length_or_output_length")?;
|
.build_or(input_length, output_length, "input_length_or_output_length")?
|
||||||
let is_no_input_no_output = context.builder().build_int_compare(
|
.into(),
|
||||||
inkwell::IntPredicate::EQ,
|
];
|
||||||
context.xlen_type().const_zero(),
|
Ok(context
|
||||||
input_length_or_output_length,
|
.build_call(declaration, arguments, "call_flags")
|
||||||
"is_no_input_no_output",
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))
|
||||||
)?;
|
.into_int_value())
|
||||||
let gas_stipend = context
|
|
||||||
.word_type()
|
|
||||||
.const_int(SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD, false);
|
|
||||||
let is_gas_stipend_for_transfer_or_send = context.builder().build_int_compare(
|
|
||||||
inkwell::IntPredicate::ULE,
|
|
||||||
gas,
|
|
||||||
gas_stipend,
|
|
||||||
"is_gas_stipend_for_transfer_or_send",
|
|
||||||
)?;
|
|
||||||
let is_balance_transfer = context.builder().build_and(
|
|
||||||
is_no_input_no_output,
|
|
||||||
is_gas_stipend_for_transfer_or_send,
|
|
||||||
"is_balance_transfer",
|
|
||||||
)?;
|
|
||||||
let is_regular_call = context
|
|
||||||
.builder()
|
|
||||||
.build_not(is_balance_transfer, "is_balance_transfer_inverted")?;
|
|
||||||
|
|
||||||
// Call flag: Left shift the heuristic boolean value.
|
|
||||||
let is_regular_call_xlen = context.builder().build_int_z_extend(
|
|
||||||
is_regular_call,
|
|
||||||
context.xlen_type(),
|
|
||||||
"is_balance_transfer_xlen",
|
|
||||||
)?;
|
|
||||||
let call_flags = context.builder().build_left_shift(
|
|
||||||
is_regular_call_xlen,
|
|
||||||
context.xlen_type().const_int(3, false),
|
|
||||||
"flags",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Deposit limit value: Sign-extended the heuristic boolean value.
|
|
||||||
let deposit_limit_value = context.builder().build_int_s_extend(
|
|
||||||
is_regular_call,
|
|
||||||
context.word_type(),
|
|
||||||
"deposit_limit_value",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok((call_flags, deposit_limit_value))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,11 +204,25 @@ where
|
|||||||
revive_llvm_context::PolkaVMEventLogFunction::<3>.declare(context)?;
|
revive_llvm_context::PolkaVMEventLogFunction::<3>.declare(context)?;
|
||||||
revive_llvm_context::PolkaVMEventLogFunction::<4>.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::PolkaVMDivisionFunction.declare(context)?;
|
||||||
revive_llvm_context::PolkaVMSignedDivisionFunction.declare(context)?;
|
revive_llvm_context::PolkaVMSignedDivisionFunction.declare(context)?;
|
||||||
revive_llvm_context::PolkaVMRemainderFunction.declare(context)?;
|
revive_llvm_context::PolkaVMRemainderFunction.declare(context)?;
|
||||||
revive_llvm_context::PolkaVMSignedRemainderFunction.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::PolkaVMCall.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMCallReentrancyProtector.declare(context)?;
|
||||||
|
|
||||||
revive_llvm_context::PolkaVMSbrkFunction.declare(context)?;
|
revive_llvm_context::PolkaVMSbrkFunction.declare(context)?;
|
||||||
|
|
||||||
let mut entry = revive_llvm_context::PolkaVMEntryFunction::default();
|
let mut entry = revive_llvm_context::PolkaVMEntryFunction::default();
|
||||||
@@ -258,11 +272,25 @@ where
|
|||||||
revive_llvm_context::PolkaVMEventLogFunction::<3>.into_llvm(context)?;
|
revive_llvm_context::PolkaVMEventLogFunction::<3>.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMEventLogFunction::<4>.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::PolkaVMDivisionFunction.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMSignedDivisionFunction.into_llvm(context)?;
|
revive_llvm_context::PolkaVMSignedDivisionFunction.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMRemainderFunction.into_llvm(context)?;
|
revive_llvm_context::PolkaVMRemainderFunction.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMSignedRemainderFunction.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::PolkaVMCall.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMCallReentrancyProtector.into_llvm(context)?;
|
||||||
|
|
||||||
revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?;
|
revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user