use super::{TypeBuilder, WithSelf};
use crate::{
error::Result,
metadata::{
drop_overrides::DropOverridesMeta, dup_overrides::DupOverridesMeta,
realloc_bindings::ReallocBindingsMeta, MetadataStorage,
},
utils::ProgramRegistryExt,
};
use cairo_lang_sierra::{
extensions::{
core::{CoreLibfunc, CoreType},
types::InfoAndTypeConcreteType,
},
program_registry::ProgramRegistry,
};
use melior::{
dialect::{cf, func},
helpers::{ArithBlockExt, BuiltinBlockExt, LlvmBlockExt},
ir::{BlockLike, Region},
};
use melior::{
dialect::{llvm, ods},
ir::{attribute::IntegerAttribute, r#type::IntegerType, Block, Location, Module, 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 src_is_null = entry.append_op_result(
ods::llvm::icmp(
context,
IntegerType::new(context, 1).into(),
src_value,
null_ptr,
IntegerAttribute::new(IntegerType::new(context, 64).into(), 0).into(),
location,
)
.into(),
)?;
let block_realloc = region.append_block(Block::new(&[]));
let block_finish =
region.append_block(Block::new(&[(llvm::r#type::pointer(context, 0), location)]));
entry.append_operation(cf::cond_br(
context,
src_is_null,
&block_finish,
&block_realloc,
&[null_ptr],
&[],
location,
));
{
let dst_value = block_realloc.append_op_result(ReallocBindingsMeta::realloc(
context,
null_ptr,
inner_len_val,
location,
)?)?;
if DupOverridesMeta::is_overriden(metadata, &info.ty) {
let value = block_realloc.load(context, location, src_value, inner_ty)?;
let values = DupOverridesMeta::invoke_override(
context,
registry,
module,
&block_realloc,
&block_realloc,
location,
metadata,
&info.ty,
value,
)?;
block_realloc.store(context, location, src_value, values.0)?;
block_realloc.store(context, location, dst_value, values.1)?;
} else {
block_realloc.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(),
);
}
block_realloc.append_operation(cf::br(&block_finish, &[dst_value], location));
}
block_finish.append_operation(func::r#return(&[src_value, block_finish.arg(0)?], 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 null_ptr =
entry.append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?;
let value = entry.arg(0)?;
let is_null = entry.append_op_result(
ods::llvm::icmp(
context,
IntegerType::new(context, 1).into(),
value,
null_ptr,
IntegerAttribute::new(IntegerType::new(context, 64).into(), 0).into(),
location,
)
.into(),
)?;
let block_free = region.append_block(Block::new(&[]));
let block_finish =
region.append_block(Block::new(&[(llvm::r#type::pointer(context, 0), location)]));
entry.append_operation(cf::cond_br(
context,
is_null,
&block_finish,
&block_free,
&[null_ptr],
&[],
location,
));
{
if DropOverridesMeta::is_overriden(metadata, &info.ty) {
let value = block_free.load(context, location, value, inner_ty)?;
DropOverridesMeta::invoke_override(
context,
registry,
module,
&block_free,
&block_free,
location,
metadata,
&info.ty,
value,
)?;
}
block_free.append_operation(ReallocBindingsMeta::free(context, value, location)?);
block_free.append_operation(func::r#return(&[], location));
}
block_finish.append_operation(func::r#return(&[], location));
Ok(region)
}
#[cfg(test)]
mod test {
use crate::{
jit_enum, jit_struct,
utils::testing::{get_compiled_program, run_program},
values::Value,
};
use pretty_assertions_sorted::assert_eq;
#[test]
fn test_nullable_deep_clone() {
let program =
get_compiled_program("test_data_artifacts/programs/types/nullable_deep_clone");
let result = run_program(&program, "run_test", &[]).return_value;
assert_eq!(
result,
jit_enum!(
0,
jit_struct!(Value::Array(vec![
Value::Felt252(1.into()),
Value::Felt252(2.into()),
Value::Felt252(3.into()),
]))
),
);
}
}