use std::collections::HashMap;
use inkwell::debug_info::AsDIScope;
use crate::optimizer::settings::size_level::SizeLevel;
use crate::optimizer::Optimizer;
use crate::polkavm::context::attribute::Attribute;
use crate::polkavm::context::pointer::Pointer;
use self::declaration::Declaration;
use self::r#return::Return;
use self::yul_data::YulData;
pub mod declaration;
pub mod intrinsics;
pub mod llvm_runtime;
pub mod r#return;
pub mod runtime;
pub mod yul_data;
#[derive(Debug)]
pub struct Function<'ctx> {
name: String,
declaration: Declaration<'ctx>,
stack: HashMap<String, Pointer<'ctx>>,
r#return: Return<'ctx>,
entry_block: inkwell::basic_block::BasicBlock<'ctx>,
return_block: inkwell::basic_block::BasicBlock<'ctx>,
yul_data: Option<YulData>,
}
impl<'ctx> Function<'ctx> {
const STACK_HASHMAP_INITIAL_CAPACITY: usize = 64;
pub fn new(
name: String,
declaration: Declaration<'ctx>,
r#return: Return<'ctx>,
entry_block: inkwell::basic_block::BasicBlock<'ctx>,
return_block: inkwell::basic_block::BasicBlock<'ctx>,
) -> Self {
Self {
name,
declaration,
stack: HashMap::with_capacity(Self::STACK_HASHMAP_INITIAL_CAPACITY),
r#return,
entry_block,
return_block,
yul_data: None,
}
}
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn declaration(&self) -> Declaration<'ctx> {
self.declaration
}
pub fn get_debug_scope(&self) -> Option<inkwell::debug_info::DIScope<'ctx>> {
self.declaration()
.function_value()
.get_subprogram()
.map(|scp| scp.as_debug_info_scope())
}
pub fn get_nth_param(&self, index: usize) -> inkwell::values::BasicValueEnum<'ctx> {
self.declaration()
.value
.get_nth_param(index as u32)
.expect("Always exists")
}
pub fn set_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
attributes: &[Attribute],
force: bool,
) {
for attribute_kind in attributes {
match attribute_kind {
Attribute::Memory => unimplemented!("`memory` attributes are not implemented"),
attribute_kind @ Attribute::AlwaysInline if force => {
declaration.value.remove_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
Attribute::NoInline as u32,
);
declaration.value.remove_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
Attribute::OptimizeNone as u32,
);
declaration.value.add_attribute(
inkwell::attributes::AttributeLoc::Function,
llvm.create_enum_attribute(*attribute_kind as u32, 0),
);
}
attribute_kind @ Attribute::NoInline if force => {
declaration.value.remove_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
Attribute::AlwaysInline as u32,
);
declaration.value.add_attribute(
inkwell::attributes::AttributeLoc::Function,
llvm.create_enum_attribute(*attribute_kind as u32, 0),
);
}
attribute_kind => declaration.value.add_attribute(
inkwell::attributes::AttributeLoc::Function,
llvm.create_enum_attribute(*attribute_kind as u32, 0),
),
}
}
}
pub fn remove_attributes(declaration: Declaration, attributes: &[Attribute]) {
for attribute in attributes.iter().filter(|attribute| {
declaration
.value
.get_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
**attribute as u32,
)
.is_some()
}) {
declaration.value.remove_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
*attribute as u32,
);
}
}
pub fn set_default_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
optimizer: &Optimizer,
) {
if optimizer.settings().level_middle_end_size == SizeLevel::Z {
Self::set_attributes(
llvm,
declaration,
&[Attribute::OptimizeForSize, Attribute::MinSize],
false,
);
}
if !optimizer.settings().is_middle_end_enabled() {
Self::set_attributes(
llvm,
declaration,
&[Attribute::NoInline, Attribute::OptimizeNone],
true,
);
}
Self::set_attributes(llvm, declaration, &[Attribute::NoFree], false);
}
pub fn set_frontend_runtime_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
optimizer: &Optimizer,
) {
if optimizer.settings().level_middle_end_size == SizeLevel::Z {
Self::set_attributes(llvm, declaration, &[Attribute::NoInline], false);
}
}
pub fn set_pure_function_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
) {
Self::set_attributes(
llvm,
declaration,
&[
Attribute::MustProgress,
Attribute::NoUnwind,
Attribute::WillReturn,
],
false,
);
}
pub fn insert_stack_pointer(
&mut self,
name: String,
pointer: Pointer<'ctx>,
) -> Option<Pointer<'ctx>> {
self.stack.insert(name, pointer)
}
pub fn get_stack_pointer(&self, name: &str) -> Option<Pointer<'ctx>> {
self.stack.get(name).copied()
}
pub fn r#return(&self) -> Return<'ctx> {
self.r#return
}
pub fn entry_block(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.entry_block
}
pub fn return_block(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.return_block
}
pub fn set_yul_data(&mut self, data: YulData) {
self.yul_data = Some(data);
}
pub fn yul(&self) -> &YulData {
self.yul_data
.as_ref()
.expect("The Yul data must have been initialized")
}
pub fn yul_mut(&mut self) -> &mut YulData {
self.yul_data
.as_mut()
.expect("The Yul data must have been initialized")
}
}