use super::WithSelf;
use crate::{
error::Result,
metadata::{
drop_overrides::DropOverridesMeta, dup_overrides::DupOverridesMeta,
realloc_bindings::ReallocBindingsMeta, MetadataStorage,
},
types::TypeBuilder,
utils::ProgramRegistryExt,
};
use cairo_lang_sierra::{
extensions::{
core::{CoreLibfunc, CoreType},
types::InfoAndTypeConcreteType,
},
program_registry::ProgramRegistry,
};
use melior::{
dialect::{func, llvm, ods},
helpers::{ArithBlockExt, BuiltinBlockExt, LlvmBlockExt},
ir::{
attribute::IntegerAttribute, r#type::IntegerType, Block, BlockLike, Location, Module,
Region, Type,
},
Context,
};
pub fn build<'ctx>(
context: &'ctx Context,
module: &Module<'ctx>,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
metadata: &mut MetadataStorage,
info: WithSelf<InfoAndTypeConcreteType>,
) -> Result<Type<'ctx>> {
DupOverridesMeta::register_with(
context,
module,
registry,
metadata,
info.self_ty(),
|metadata| {
Ok(Some(build_dup(context, module, registry, metadata, &info)?))
},
)?;
DropOverridesMeta::register_with(
context,
module,
registry,
metadata,
info.self_ty(),
|metadata| {
Ok(Some(build_drop(
context, module, registry, metadata, &info,
)?))
},
)?;
Ok(llvm::r#type::pointer(context, 0))
}
fn build_dup<'ctx>(
context: &'ctx Context,
module: &Module<'ctx>,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
metadata: &mut MetadataStorage,
info: &WithSelf<InfoAndTypeConcreteType>,
) -> Result<Region<'ctx>> {
let location = Location::unknown(context);
if metadata.get::<ReallocBindingsMeta>().is_none() {
metadata.insert(ReallocBindingsMeta::new(context, module));
}
let inner_ty = registry.get_type(&info.ty)?;
let inner_len = inner_ty.layout(registry)?.pad_to_align().size();
let inner_ty = inner_ty.build(context, module, registry, metadata, &info.ty)?;
let region = Region::new();
let entry = region.append_block(Block::new(&[(llvm::r#type::pointer(context, 0), location)]));
let null_ptr =
entry.append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?;
let inner_len_val = entry.const_int(context, location, inner_len, 64)?;
let src_value = entry.arg(0)?;
let dst_value = entry.append_op_result(ReallocBindingsMeta::realloc(
context,
null_ptr,
inner_len_val,
location,
)?)?;
if DupOverridesMeta::is_overriden(metadata, &info.ty) {
let value = entry.load(context, location, src_value, inner_ty)?;
let values = DupOverridesMeta::invoke_override(
context, registry, module, &entry, &entry, location, metadata, &info.ty, value,
)?;
entry.store(context, location, src_value, values.0)?;
entry.store(context, location, dst_value, values.1)?;
} else {
entry.append_operation(
ods::llvm::intr_memcpy_inline(
context,
dst_value,
src_value,
IntegerAttribute::new(IntegerType::new(context, 64).into(), inner_len as i64),
IntegerAttribute::new(IntegerType::new(context, 1).into(), 0),
location,
)
.into(),
);
}
entry.append_operation(func::r#return(&[src_value, dst_value], location));
Ok(region)
}
fn build_drop<'ctx>(
context: &'ctx Context,
module: &Module<'ctx>,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
metadata: &mut MetadataStorage,
info: &WithSelf<InfoAndTypeConcreteType>,
) -> Result<Region<'ctx>> {
let location = Location::unknown(context);
if metadata.get::<ReallocBindingsMeta>().is_none() {
metadata.insert(ReallocBindingsMeta::new(context, module));
}
let inner_ty = registry.build_type(context, module, metadata, &info.ty)?;
let region = Region::new();
let entry = region.append_block(Block::new(&[(llvm::r#type::pointer(context, 0), location)]));
let value = entry.arg(0)?;
if DropOverridesMeta::is_overriden(metadata, &info.ty) {
let value = entry.load(context, location, value, inner_ty)?;
DropOverridesMeta::invoke_override(
context, registry, module, &entry, &entry, location, metadata, &info.ty, value,
)?;
}
entry.append_operation(ReallocBindingsMeta::free(context, value, location)?);
entry.append_operation(func::r#return(&[], location));
Ok(region)
}