//! The revive compiler runtime function interface definition. //! //! Common routines should not be inlined but extracted into smaller functions. //! This benefits contract code size. use crate::optimizer::settings::size_level::SizeLevel; use crate::polkavm::context::function::declaration::Declaration; use crate::polkavm::context::function::Function; use crate::polkavm::context::Attribute; use crate::polkavm::context::Context; /// The revive runtime function interface simplifies declaring runtime functions /// and code emitting by providing helpful default implementations. pub trait RuntimeFunction { /// The function name. const NAME: &'static str; const ATTRIBUTES: &'static [Attribute] = &[ Attribute::NoFree, Attribute::NoRecurse, Attribute::WillReturn, ]; /// The function type. fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx>; /// Declare the function. fn declare(&self, context: &mut Context) -> anyhow::Result<()> { let function = context.add_function( Self::NAME, Self::r#type(context), 0, Some(inkwell::module::Linkage::LinkOnceODR), None, false, )?; let mut attributes = Self::ATTRIBUTES.to_vec(); attributes.extend_from_slice(match context.optimizer_settings().level_middle_end_size { SizeLevel::Zero => &[], _ => &[Attribute::OptimizeForSize, Attribute::MinSize], }); Function::set_attributes( context.llvm(), function.borrow().declaration(), &attributes, true, ); let function = function.borrow().declaration().function_value(); let comdat = context .module() .get_or_insert_comdat(&format!("{}_comdat", Self::NAME)); comdat.set_selection_kind(inkwell::comdat::ComdatSelectionKind::NoDuplicates); function.as_global_value().set_comdat(comdat); Ok(()) } /// Get the function declaration. fn declaration<'ctx>(context: &Context<'ctx>) -> Declaration<'ctx> { context .get_function(Self::NAME, false) .unwrap_or_else(|| panic!("runtime function {} should be declared", Self::NAME)) .borrow() .declaration() } /// Emit the function. fn emit(&self, context: &mut Context) -> anyhow::Result<()> { context.set_current_function(Self::NAME, None, false)?; context.set_basic_block(context.current_function().borrow().entry_block()); let return_value = self.emit_body(context)?; self.emit_epilogue(context, return_value); context.pop_debug_scope(); Ok(()) } /// Emit the function body. fn emit_body<'ctx>( &self, context: &mut Context<'ctx>, ) -> anyhow::Result>>; /// Emit the function return instructions. fn emit_epilogue<'ctx>( &self, context: &mut Context<'ctx>, return_value: Option>, ) { let return_block = context.current_function().borrow().return_block(); context.build_unconditional_branch(return_block); context.set_basic_block(return_block); match return_value { Some(value) => context.build_return(Some(&value)), None => context.build_return(None), } } /// Get the nth function paramater. fn paramater<'ctx>( context: &Context<'ctx>, index: usize, ) -> inkwell::values::BasicValueEnum<'ctx> { let name = Self::NAME; context .get_function(name, false) .unwrap_or_else(|| panic!("runtime function {name} should be declared")) .borrow() .declaration() .function_value() .get_nth_param(index as u32) .unwrap_or_else(|| panic!("runtime function {name} should have parameter #{index}")) } }