use super::MetadataStorage;
use crate::{
error::{Error, Result},
types::TypeBuilder,
utils::ProgramRegistryExt,
};
use cairo_lang_sierra::{
extensions::core::{CoreLibfunc, CoreType},
ids::ConcreteTypeId,
program_registry::ProgramRegistry,
};
use melior::{
dialect::{cf, func, llvm},
helpers::{BuiltinBlockExt, LlvmBlockExt},
ir::{
attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute},
r#type::FunctionType,
Attribute, Block, BlockLike, Identifier, Location, Module, Region, Value,
},
Context,
};
use std::collections::HashSet;
#[derive(Debug, Default)]
pub struct DupOverridesMeta {
overriden_types: HashSet<ConcreteTypeId>,
}
impl DupOverridesMeta {
pub(crate) fn register_with<'ctx>(
context: &'ctx Context,
module: &Module<'ctx>,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
metadata: &mut MetadataStorage,
id: &ConcreteTypeId,
f: impl FnOnce(&mut MetadataStorage) -> Result<Option<Region<'ctx>>>,
) -> Result<()> {
{
let dup_override_meta = metadata.get_or_insert_with(Self::default);
if dup_override_meta.overriden_types.contains(id) {
return Ok(());
}
dup_override_meta.overriden_types.insert(id.clone());
}
match f(metadata)? {
Some(region) => {
let location = Location::unknown(context);
let ty = registry.build_type(context, module, metadata, id)?;
let ptr_ty = llvm::r#type::pointer(context, 0);
let sierra_ty = registry.get_type(id)?;
let is_memory_allocated = sierra_ty.is_memory_allocated(registry)?;
let signature_ty = if is_memory_allocated { ptr_ty } else { ty };
if is_memory_allocated {
let entry_block = region.first_block().unwrap();
let pre_entry_block =
region.insert_block_before(entry_block, Block::new(&[(ptr_ty, location)]));
pre_entry_block.append_operation(cf::br(
&entry_block,
&[pre_entry_block.load(context, location, pre_entry_block.arg(0)?, ty)?],
location,
));
}
module.body().append_operation(func::func(
context,
StringAttribute::new(context, &format!("dup${}", id.id)),
TypeAttribute::new(
FunctionType::new(context, &[signature_ty], &[ty, ty]).into(),
),
region,
&[
(
Identifier::new(context, "sym_visibility"),
StringAttribute::new(context, "public").into(),
),
(
Identifier::new(context, "llvm.CConv"),
Attribute::parse(context, "#llvm.cconv<fastcc>")
.ok_or(Error::ParseAttributeError)?,
),
(
Identifier::new(context, "llvm.linkage"),
Attribute::parse(context, "#llvm.linkage<private>")
.ok_or(Error::ParseAttributeError)?,
),
],
Location::unknown(context),
));
}
None => {
if let Some(dup_override_meta) = metadata.get_mut::<Self>() {
dup_override_meta.overriden_types.remove(id);
}
}
}
Ok(())
}
pub(crate) fn is_overriden(metadata: &mut MetadataStorage, id: &ConcreteTypeId) -> bool {
metadata
.get_or_insert_with(Self::default)
.overriden_types
.contains(id)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn invoke_override<'ctx, 'this>(
context: &'ctx Context,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
module: &Module<'ctx>,
init_block: &'this Block<'ctx>,
block: &'this Block<'ctx>,
location: Location<'ctx>,
metadata: &mut MetadataStorage,
id: &ConcreteTypeId,
value: Value<'ctx, 'this>,
) -> Result<(Value<'ctx, 'this>, Value<'ctx, 'this>)> {
Ok(if Self::is_overriden(metadata, id) {
let ty = registry.build_type(context, module, metadata, id)?;
let sierra_ty = registry.get_type(id)?;
let is_memory_allocated = sierra_ty.is_memory_allocated(registry)?;
let value = if is_memory_allocated {
let value_ptr = init_block.alloca1(
context,
location,
ty,
sierra_ty.layout(registry)?.align(),
)?;
block.store(context, location, value_ptr, value)?;
value_ptr
} else {
value
};
let result = block.append_operation(func::call(
context,
FlatSymbolRefAttribute::new(context, &format!("dup${}", id.id)),
&[value],
&[ty, ty],
location,
));
let original = result.result(0)?.into();
let copy = result.result(1)?.into();
(original, copy)
} else {
(value, value)
})
}
}