revive-llvm-context 1.3.0

Shared front end code of the revive PolkaVM compilers
//! Translates the value and balance operations.

use crate::polkavm::context::Context;

/// Translates the `gas` instruction.
pub fn gas<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let ref_time_left_value = context
        .build_runtime_call(revive_runtime_api::polkavm_imports::REF_TIME_LEFT, &[])
        .expect("the ref_time_left syscall method should return a value")
        .into_int_value();

    Ok(context
        .builder()
        .build_int_z_extend(ref_time_left_value, context.word_type(), "gas_left")?
        .into())
}

/// Translates the `value` instruction.
pub fn value<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let output_pointer = context.build_alloca_at_entry(context.value_type(), "value_transferred");
    context.build_store(output_pointer, context.word_const(0))?;
    context.build_runtime_call(
        revive_runtime_api::polkavm_imports::VALUE_TRANSFERRED,
        &[output_pointer.to_int(context).into()],
    );
    context.build_load(output_pointer, "value_transferred")
}

/// Calls the outlined `__revive_callvalue() -> i256` runtime function.
///
/// This is functionally identical to [`value`] but calls a shared outlined function
/// instead of inlining the alloca+store+call+load sequence at every call site.
/// For contracts with many non-payable checks (e.g. OpenZeppelin ERC20: 37 sites),
/// this significantly reduces code size.
pub fn value_outlined<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    use crate::polkavm::context::function::runtime::revive::CallValue;
    use crate::polkavm::context::runtime::RuntimeFunction;
    let function = context
        .get_function(CallValue::NAME, false)
        .expect("ICE: __revive_callvalue should be declared");
    let result = context
        .build_call(function.borrow().declaration(), &[], "callvalue_result")
        .expect("ICE: __revive_callvalue should return a value");
    Ok(result)
}

/// Calls the outlined `__revive_callvalue_nonzero() -> i1` runtime function.
///
/// Returns true (i1) if callvalue is nonzero. This is more efficient than
/// `value_outlined()` followed by `icmp ne i256 %cv, 0` because the
/// 256-bit comparison is done once inside the outlined function body,
/// and each call site only receives a single boolean flag.
pub fn value_nonzero_outlined<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    use crate::polkavm::context::function::runtime::revive::CallValueNonzero;
    use crate::polkavm::context::runtime::RuntimeFunction;
    let function = context
        .get_function(CallValueNonzero::NAME, false)
        .expect("ICE: __revive_callvalue_nonzero should be declared");
    let result = context
        .build_call(function.borrow().declaration(), &[], "callvalue_nonzero")
        .expect("ICE: __revive_callvalue_nonzero should return a value");
    Ok(result)
}

/// Translates the `balance` instructions.
pub fn balance<'ctx>(
    context: &mut Context<'ctx>,
    address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let address_pointer = context.build_address_argument_store(address)?;
    let balance_pointer = context.build_alloca_at_entry(context.word_type(), "balance_pointer");
    let balance = context.builder().build_ptr_to_int(
        balance_pointer.value,
        context.xlen_type(),
        "balance",
    )?;

    context.build_runtime_call(
        revive_runtime_api::polkavm_imports::BALANCE_OF,
        &[address_pointer.to_int(context).into(), balance.into()],
    );

    context.build_load(balance_pointer, "balance")
}

/// Translates the `selfbalance` instructions.
pub fn self_balance<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let balance_pointer = context.build_alloca_at_entry(context.word_type(), "balance_pointer");
    let balance = context.builder().build_ptr_to_int(
        balance_pointer.value,
        context.xlen_type(),
        "balance",
    )?;

    context.build_runtime_call(
        revive_runtime_api::polkavm_imports::BALANCE,
        &[balance.into()],
    );

    context.build_load(balance_pointer, "balance")
}