use super::LibfuncHelper;
use crate::{
error::Result, metadata::MetadataStorage, native_panic, types::TypeBuilder,
utils::ProgramRegistryExt,
};
use cairo_lang_sierra::{
extensions::{
core::{CoreLibfunc, CoreType, CoreTypeConcrete},
lib_func::SignatureOnlyConcreteLibfunc,
structure::{ConcreteStructBoxedDeconstructLibfunc, StructConcreteLibfunc},
ConcreteLibfunc,
},
ids::ConcreteTypeId,
program_registry::ProgramRegistry,
};
use itertools::Itertools;
use melior::{
dialect::llvm::{self},
helpers::{BuiltinBlockExt, GepIndex, LlvmBlockExt},
ir::{Block, BlockLike, Location, Value},
Context,
};
pub fn build<'ctx, 'this>(
context: &'ctx Context,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
metadata: &mut MetadataStorage,
selector: &StructConcreteLibfunc,
) -> Result<()> {
match selector {
StructConcreteLibfunc::Construct(info) => {
build_construct(context, registry, entry, location, helper, metadata, info)
}
StructConcreteLibfunc::Deconstruct(info)
| StructConcreteLibfunc::SnapshotDeconstruct(info) => {
build_deconstruct(context, registry, entry, location, helper, metadata, info)
}
StructConcreteLibfunc::BoxedDeconstruct(info) => {
build_boxed_deconstruct(context, registry, entry, location, helper, metadata, info)
}
}
}
pub fn build_construct<'ctx, 'this>(
context: &'ctx Context,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
metadata: &mut MetadataStorage,
info: &SignatureOnlyConcreteLibfunc,
) -> Result<()> {
let mut fields = Vec::new();
for (i, _) in info.param_signatures().iter().enumerate() {
fields.push(entry.argument(i)?.into());
}
let value = build_struct_value(
context,
registry,
entry,
location,
helper,
metadata,
&info.branch_signatures()[0].vars[0].ty,
&fields,
)?;
helper.br(entry, 0, &[value], location)
}
#[allow(clippy::too_many_arguments)]
pub fn build_struct_value<'ctx, 'this>(
context: &'ctx Context,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
metadata: &mut MetadataStorage,
struct_type: &ConcreteTypeId,
fields: &[Value<'ctx, 'this>],
) -> Result<Value<'ctx, 'this>> {
let struct_ty = registry.build_type(context, helper, metadata, struct_type)?;
let struct_type = registry.get_type(struct_type)?;
let zst_fields = match struct_type {
CoreTypeConcrete::Struct(info) => {
info.members
.iter()
.map(|member| registry.get_type(member)?.is_zst(registry))
.try_collect()?
}
_ => {
vec![false; fields.len()]
}
};
let mut accumulator = entry.append_op_result(llvm::undef(struct_ty, location))?;
for (idx, field) in fields.iter().enumerate() {
if zst_fields[idx] {
continue;
}
accumulator = entry.insert_value(context, location, accumulator, *field, idx)?
}
Ok(accumulator)
}
pub fn build_deconstruct<'ctx, 'this>(
context: &'ctx Context,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
metadata: &mut MetadataStorage,
info: &SignatureOnlyConcreteLibfunc,
) -> Result<()> {
let container = entry.arg(0)?;
let mut fields = Vec::<Value>::with_capacity(info.branch_signatures()[0].vars.len());
for (i, var_info) in info.branch_signatures()[0].vars.iter().enumerate() {
let type_info = registry.get_type(&var_info.ty)?;
let field_ty = type_info.build(context, helper, registry, metadata, &var_info.ty)?;
let value = entry.extract_value(context, location, container, field_ty, i)?;
fields.push(value);
}
helper.br(entry, 0, &fields, location)
}
pub fn build_boxed_deconstruct<'ctx, 'this>(
context: &'ctx Context,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
metadata: &mut MetadataStorage,
info: &ConcreteStructBoxedDeconstructLibfunc,
) -> Result<()> {
let CoreTypeConcrete::Box(box_info) = registry.get_type(&info.param_signatures()[0].ty)? else {
native_panic!("Should receive a Box type as argument");
};
let struct_ty = registry.build_type(context, helper, metadata, &box_info.ty)?;
let box_ptr = entry.arg(0)?;
let fields = (0..info.members.len())
.map(|i| {
entry.gep(
context,
location,
box_ptr,
&[GepIndex::Const(0), GepIndex::Const(i as i32)],
struct_ty,
)
})
.collect::<std::result::Result<Vec<_>, _>>()?;
helper.br(entry, 0, &fields, location)
}
#[cfg(test)]
mod test {
use crate::{
jit_struct,
utils::testing::{get_compiled_program, run_program_assert_output},
Value,
};
#[test]
fn boxed_deconstruct_3_fields() {
let program =
get_compiled_program("test_data_artifacts/programs/libfuncs/struct_boxed_deconstruct");
run_program_assert_output(
&program,
"deconstruct_struct_3_fields",
&[],
jit_struct!(Value::Felt252(2.into()), Value::Uint8(2), Value::Uint128(2)),
);
}
#[test]
fn boxed_deconstruct_1_field() {
let program =
get_compiled_program("test_data_artifacts/programs/libfuncs/struct_boxed_deconstruct");
run_program_assert_output(
&program,
"deconstruct_struct_1_field",
&[],
jit_struct!(Value::Uint8(2)),
);
}
#[test]
fn boxed_deconstruct_empty_struct() {
let program =
get_compiled_program("test_data_artifacts/programs/libfuncs/struct_boxed_deconstruct");
run_program_assert_output(&program, "deconstruct_empty_struct", &[], jit_struct!());
}
#[test]
fn boxed_deconstruct_struct_snapshot() {
let program =
get_compiled_program("test_data_artifacts/programs/libfuncs/struct_boxed_deconstruct");
run_program_assert_output(
&program,
"deconstruct_struct_snapshot",
&[],
jit_struct!(Value::Felt252(2.into()), Value::Uint8(2), Value::Uint128(2)),
);
}
}