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;
pub trait RuntimeFunction {
const NAME: &'static str;
const ATTRIBUTES: &'static [Attribute] = &[
Attribute::NoFree,
Attribute::NoRecurse,
Attribute::WillReturn,
];
fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx>;
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(())
}
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()
}
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(())
}
fn emit_body<'ctx>(
&self,
context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>>;
fn emit_epilogue<'ctx>(
&self,
context: &mut Context<'ctx>,
return_value: Option<inkwell::values::BasicValueEnum<'ctx>>,
) {
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),
}
}
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}"))
}
}