revive-llvm-context 1.3.0

Shared front end code of the revive PolkaVM compilers
//! The LLVM context library.

#![allow(clippy::too_many_arguments)]

use std::ffi::CString;
use std::sync::OnceLock;

pub use self::debug_config::ir_type::IRType as DebugConfigIR;
pub use self::debug_config::DebugConfig;
pub use self::optimizer::settings::size_level::SizeLevel as OptimizerSettingsSizeLevel;
pub use self::optimizer::settings::Settings as OptimizerSettings;
pub use self::optimizer::Optimizer;
pub use self::polkavm::build as polkavm_build;
pub use self::polkavm::context::address_space::AddressSpace as PolkaVMAddressSpace;
pub use self::polkavm::context::argument::Argument as PolkaVMArgument;
pub use self::polkavm::context::attribute::Attribute as PolkaVMAttribute;
pub use self::polkavm::context::attribute::MemoryEffect as PolkaVMMemoryEffect;
pub use self::polkavm::context::build::Build as PolkaVMBuild;
pub use self::polkavm::context::code_type::CodeType as PolkaVMCodeType;
pub use self::polkavm::context::debug_info::DebugInfo;
pub use self::polkavm::context::function::declaration::Declaration as PolkaVMFunctionDeclaration;
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::r#return::Return as PolkaVMFunctionReturn;
pub use self::polkavm::context::function::runtime::arithmetics::Division as PolkaVMDivisionFunction;
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::SignedRemainder as PolkaVMSignedRemainderFunction;
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::revive::CallDataLoad as PolkaVMCallDataLoadFunction;
pub use self::polkavm::context::function::runtime::revive::CallValue as PolkaVMCallValueFunction;
pub use self::polkavm::context::function::runtime::revive::CallValueNonzero as PolkaVMCallValueNonzeroFunction;
pub use self::polkavm::context::function::runtime::revive::Caller as PolkaVMCallerFunction;
pub use self::polkavm::context::function::runtime::revive::Exit as PolkaVMExitFunction;
pub use self::polkavm::context::function::runtime::revive::Revert as PolkaVMRevertFunction;
pub use self::polkavm::context::function::runtime::revive::RevertEmpty as PolkaVMRevertEmptyFunction;
pub use self::polkavm::context::function::runtime::revive::RevertPanic as PolkaVMRevertPanicFunction;
pub use self::polkavm::context::function::runtime::revive::WordToPointer as PolkaVMWordToPointerFunction;
pub use self::polkavm::context::function::runtime::runtime_code::RuntimeCode as PolkaVMRuntimeCodeFunction;
pub use self::polkavm::context::function::runtime::sbrk::Sbrk as PolkaVMSbrkFunction;
pub use self::polkavm::context::function::runtime::FUNCTION_DEPLOY_CODE as PolkaVMFunctionDeployCode;
pub use self::polkavm::context::function::runtime::FUNCTION_ENTRY as PolkaVMFunctionEntry;
pub use self::polkavm::context::function::runtime::FUNCTION_RUNTIME_CODE as PolkaVMFunctionRuntimeCode;
pub use self::polkavm::context::function::yul_data::YulData as PolkaVMFunctionYulData;
pub use self::polkavm::context::function::Function as PolkaVMFunction;
pub use self::polkavm::context::global::Global as PolkaVMGlobal;
pub use self::polkavm::context::pointer::heap::Keccak256OneWord as PolkaVMKeccak256OneWordFunction;
pub use self::polkavm::context::pointer::heap::Keccak256TwoWords as PolkaVMKeccak256TwoWordsFunction;
pub use self::polkavm::context::pointer::heap::LoadWord as PolkaVMLoadHeapWordFunction;
pub use self::polkavm::context::pointer::heap::LoadWordNative as PolkaVMLoadHeapWordNativeFunction;
pub use self::polkavm::context::pointer::heap::StoreWord as PolkaVMStoreHeapWordFunction;
pub use self::polkavm::context::pointer::heap::StoreWordNative as PolkaVMStoreHeapWordNativeFunction;
pub use self::polkavm::context::pointer::storage::LoadTransientWord as PolkaVMLoadTransientStorageWordFunction;
pub use self::polkavm::context::pointer::storage::LoadWord as PolkaVMLoadStorageWordFunction;
pub use self::polkavm::context::pointer::storage::StoreTransientWord as PolkaVMStoreTransientStorageWordFunction;
pub use self::polkavm::context::pointer::storage::StoreWord as PolkaVMStoreStorageWordFunction;
pub use self::polkavm::context::pointer::Pointer as PolkaVMPointer;
pub use self::polkavm::context::r#loop::Loop as PolkaVMLoop;
pub use self::polkavm::context::solidity_data::SolidityData as PolkaVMContextSolidityData;
pub use self::polkavm::context::yul_data::YulData as PolkaVMContextYulData;
pub use self::polkavm::context::Context as PolkaVMContext;
pub use self::polkavm::disassemble as polkavm_disassemble;
pub use self::polkavm::evm::arithmetic as polkavm_evm_arithmetic;
pub use self::polkavm::evm::bitwise as polkavm_evm_bitwise;
pub use self::polkavm::evm::call as polkavm_evm_call;
pub use self::polkavm::evm::calldata as polkavm_evm_calldata;
pub use self::polkavm::evm::comparison as polkavm_evm_comparison;
pub use self::polkavm::evm::context as polkavm_evm_contract_context;
pub use self::polkavm::evm::create as polkavm_evm_create;
pub use self::polkavm::evm::crypto as polkavm_evm_crypto;
pub use self::polkavm::evm::ether_gas as polkavm_evm_ether_gas;
pub use self::polkavm::evm::event as polkavm_evm_event;
pub use self::polkavm::evm::event::EventLog as PolkaVMEventLogFunction;
pub use self::polkavm::evm::ext_code as polkavm_evm_ext_code;
pub use self::polkavm::evm::immutable as polkavm_evm_immutable;
pub use self::polkavm::evm::immutable::Load as PolkaVMLoadImmutableDataFunction;
pub use self::polkavm::evm::immutable::Store as PolkaVMStoreImmutableDataFunction;
pub use self::polkavm::evm::math as polkavm_evm_math;
pub use self::polkavm::evm::memory as polkavm_evm_memory;
pub use self::polkavm::evm::r#return as polkavm_evm_return;
pub use self::polkavm::evm::return_data as polkavm_evm_return_data;
pub use self::polkavm::evm::storage as polkavm_evm_storage;
pub use self::polkavm::hash as polkavm_hash;
pub use self::polkavm::link as polkavm_link;
pub use self::polkavm::r#const as polkavm_const;
pub use self::polkavm::DummyLLVMWritable as PolkaVMDummyLLVMWritable;
pub use self::polkavm::WriteLLVM as PolkaVMWriteLLVM;
pub use self::target_machine::target::Target as PolkaVMTarget;
pub use self::target_machine::TargetMachine as PolkaVMTargetMachine;

pub(crate) mod debug_config;
pub(crate) mod optimizer;
pub(crate) mod polkavm;
pub(crate) mod target_machine;

static DID_INITIALIZE: OnceLock<()> = OnceLock::new();

/// The global LLVM options applied at [`OptimizerSettingsSizeLevel::Z`] **on the newyork path**.
///
/// These trade execution speed for code size: the machine outliner replaces repeated
/// instruction sequences with calls, disabling LICM avoids i256 register pressure on the
/// PVM target (rv64e) where hoisting causes excessive stack spills, and `ext-tsp-for-size`
/// optimizes block layout for size instead of speed. At any other optimization level the
/// upstream LLVM defaults apply: paying execution gas for smaller code is only acceptable
/// when the user explicitly asked for `-Oz`.
///
/// They are applied only for modules produced by the newyork IR generator. Like the
/// aggressive code-size pass pipeline in [`crate::optimizer::Optimizer::run`] and the
/// `+unaligned-scalar-mem` target feature, these knobs are tuned and validated against
/// newyork's outlined code; the stock Yul path keeps the upstream LLVM defaults it was
/// validated against (matching `main`).
const SIZE_LEVEL_Z_ARGUMENTS: &[&str] = &[
    "--disable-licm-promotion",
    "--disable-machine-licm",
    "--enable-machine-outliner",
    "--machine-outliner-reruns=2",
    "--disable-early-taildup=true",
    "--apply-ext-tsp-for-size=true",
    "--attributor-allow-deep-wrappers=true",
    "--hoist-common-insts=true",
];

/// Initializes the LLVM compiler backend.
///
/// This is a no-op if called subsequentially.
///
/// `llvm_arguments` are passed as-is to the LLVM CL options parser.
///
/// When `use_newyork` is set and the size level is [`OptimizerSettingsSizeLevel::Z`], the
/// `SIZE_LEVEL_Z_ARGUMENTS` are prepended. They are gated on the newyork path because they
/// are validated against its codegen; the stock Yul path must keep the upstream defaults.
/// LLVM parses command line options once per process, so the first caller wins; production
/// builds are unaffected because every contract is compiled in its own recursive process
/// with its own optimizer settings and IR generator.
pub fn initialize_llvm(
    target: PolkaVMTarget,
    name: &str,
    size_level: OptimizerSettingsSizeLevel,
    use_newyork: bool,
    llvm_arguments: &[String],
) {
    let Ok(_) = DID_INITIALIZE.set(()) else {
        return; // Tests don't go through a recursive process
    };

    let size_arguments = if use_newyork && size_level == OptimizerSettingsSizeLevel::Z {
        SIZE_LEVEL_Z_ARGUMENTS
    } else {
        &[]
    };
    let argv = std::iter::once(name)
        .chain(size_arguments.iter().copied())
        .chain(llvm_arguments.iter().map(String::as_str))
        .map(|arg| CString::new(arg.as_bytes()).unwrap())
        .collect::<Vec<_>>();
    let argv: Vec<*const libc::c_char> = argv.iter().map(|arg| arg.as_ptr()).collect();
    let overview = CString::new("").unwrap();
    unsafe {
        inkwell::llvm_sys::support::LLVMParseCommandLineOptions(
            argv.len() as i32,
            argv.as_ptr(),
            overview.as_ptr(),
        );
    }

    inkwell::support::enable_llvm_pretty_stack_trace();

    match target {
        PolkaVMTarget::PVM => inkwell::targets::Target::initialize_riscv(&Default::default()),
    }
}