diff --git a/src/jit_runner.rs b/src/jit_runner.rs index 5158d2c95..b49621d14 100644 --- a/src/jit_runner.rs +++ b/src/jit_runner.rs @@ -215,7 +215,17 @@ pub fn execute( params_ptrs.push(next.to_jit(&arena, registry, param_type_id)?); } - CoreTypeConcrete::Sint16(_) => todo!(), + CoreTypeConcrete::Sint16(_) => { + let next = params_it + .next() + .ok_or_else(|| make_missing_parameter(param_type_id))?; + + if !matches!(next, JITValue::Sint16(_)) { + Err(make_unexpected_value_error("JITValue::Sint16".to_string()))?; + } + + params_ptrs.push(next.to_jit(&arena, registry, param_type_id)?); + } CoreTypeConcrete::Sint32(_) => todo!(), CoreTypeConcrete::Sint64(_) => todo!(), CoreTypeConcrete::Sint128(_) => todo!(), diff --git a/src/libfuncs.rs b/src/libfuncs.rs index 2e261a3d4..afc8879d4 100644 --- a/src/libfuncs.rs +++ b/src/libfuncs.rs @@ -41,6 +41,7 @@ pub mod mem; pub mod nullable; pub mod pedersen; pub mod poseidon; +pub mod sint16; pub mod sint8; pub mod snapshot_take; pub mod stark_net; @@ -203,7 +204,9 @@ where Self::Sint8(info) => { self::sint8::build(context, registry, entry, location, helper, metadata, info) } - CoreConcreteLibfunc::Sint16(_) => todo!(), + CoreConcreteLibfunc::Sint16(info) => { + self::sint16::build(context, registry, entry, location, helper, metadata, info) + } CoreConcreteLibfunc::Sint32(_) => todo!(), CoreConcreteLibfunc::Sint64(_) => todo!(), CoreConcreteLibfunc::Sint128(_) => todo!(), diff --git a/src/libfuncs/sint16.rs b/src/libfuncs/sint16.rs new file mode 100644 index 000000000..84961b974 --- /dev/null +++ b/src/libfuncs/sint16.rs @@ -0,0 +1,851 @@ +//! # `i16`-related libfuncs +use super::{LibfuncBuilder, LibfuncHelper}; + +use crate::{ + error::{ + libfuncs::{Error, Result}, + CoreTypeBuilderError, + }, + metadata::MetadataStorage, + types::TypeBuilder, + utils::ProgramRegistryExt, +}; +use cairo_lang_sierra::{ + extensions::{ + int::{ + signed::{Sint16Concrete, Sint16Traits, SintConcrete}, + IntConstConcreteLibfunc, IntOperationConcreteLibfunc, IntOperator, + }, + lib_func::SignatureOnlyConcreteLibfunc, + ConcreteLibfunc, GenericLibfunc, GenericType, + }, + program_registry::ProgramRegistry, +}; +use melior::{ + dialect::{ + arith::{self, CmpiPredicate}, + cf, llvm, + }, + ir::{ + attribute::{DenseI64ArrayAttribute, IntegerAttribute}, + operation::OperationBuilder, + r#type::IntegerType, + Attribute, Block, Location, Value, ValueLike, + }, + Context, +}; + +/// Select and call the correct libfunc builder function from the selector. +pub fn build<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + selector: &Sint16Concrete, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + match selector { + SintConcrete::Const(info) => { + build_const(context, registry, entry, location, helper, metadata, info) + } + SintConcrete::Operation(info) => { + build_operation(context, registry, entry, location, helper, info) + } + SintConcrete::Equal(info) => build_equal(context, registry, entry, location, helper, info), + SintConcrete::ToFelt252(info) => { + build_to_felt252(context, registry, entry, location, helper, metadata, info) + } + SintConcrete::FromFelt252(info) => { + build_from_felt252(context, registry, entry, location, helper, metadata, info) + } + SintConcrete::IsZero(info) => { + build_is_zero(context, registry, entry, location, helper, info) + } + SintConcrete::WideMul(info) => { + build_widemul(context, registry, entry, location, helper, metadata, info) + } + SintConcrete::Diff(info) => build_diff(context, registry, entry, location, helper, info), + } +} + +/// Generate MLIR operations for the `i16_const` libfunc. +pub fn build_const<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + info: &IntConstConcreteLibfunc, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + let value = info.c; + let value_ty = registry.build_type( + context, + helper, + registry, + metadata, + &info.signature.branch_signatures[0].vars[0].ty, + )?; + + let op0 = entry.append_operation(arith::constant( + context, + Attribute::parse(context, &format!("{value} : {value_ty}")).unwrap(), + location, + )); + entry.append_operation(helper.br(0, &[op0.result(0)?.into()], location)); + + Ok(()) +} + +/// Generate MLIR operations for the i16 operation libfunc. +pub fn build_operation<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + _registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + info: &IntOperationConcreteLibfunc, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + let range_check: Value = entry.argument(0)?.into(); + let lhs: Value = entry.argument(1)?.into(); + let rhs: Value = entry.argument(2)?.into(); + + let op_name = match info.operator { + IntOperator::OverflowingAdd => "llvm.intr.sadd.with.overflow", + IntOperator::OverflowingSub => "llvm.intr.ssub.with.overflow", + }; + + let values_type = lhs.r#type(); + + let result_type = llvm::r#type::r#struct( + context, + &[values_type, IntegerType::new(context, 1).into()], + false, + ); + + let result = entry + .append_operation( + OperationBuilder::new(op_name, location) + .add_operands(&[lhs, rhs]) + .add_results(&[result_type]) + .build()?, + ) + .result(0)? + .into(); + + let op_result = entry + .append_operation(llvm::extract_value( + context, + result, + DenseI64ArrayAttribute::new(context, &[0]), + values_type, + location, + )) + .result(0)? + .into(); + + // Create a const operation to get the 0 value to compare against + let zero_const = entry + .append_operation(arith::constant( + context, + IntegerAttribute::new(0.into(), values_type).into(), + location, + )) + .result(0)? + .into(); + // Check if the result is positive + let is_positive = entry + .append_operation(arith::cmpi( + context, + CmpiPredicate::Sge, + op_result, + zero_const, + location, + )) + .result(0)? + .into(); + + // Check overflow flag + let op_overflow = entry + .append_operation(llvm::extract_value( + context, + result, + DenseI64ArrayAttribute::new(context, &[1]), + IntegerType::new(context, 1).into(), + location, + )) + .result(0)? + .into(); + + let block_not_overflow = helper.append_block(Block::new(&[])); + let block_overflow = helper.append_block(Block::new(&[])); + + // The libfunc has three possible outputs: In Range, Overflow & Underflow + entry.append_operation(cf::cond_br( + context, + op_overflow, + block_overflow, + block_not_overflow, + &[], + &[], + location, + )); + // Check wether the result is positive to distinguish between undeflowing & overflowing results + block_overflow.append_operation(helper.cond_br( + context, + is_positive, + [1, 2], + [&[range_check, op_result], &[range_check, op_result]], + location, + )); + // No Oveflow/Underflow -> In range result + block_not_overflow.append_operation(helper.br(0, &[range_check, op_result], location)); + Ok(()) +} + +/// Generate MLIR operations for the `i16_eq` libfunc. +pub fn build_equal<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + _registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + _info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + let arg0: Value = entry.argument(0)?.into(); + let arg1: Value = entry.argument(1)?.into(); + + let op0 = entry.append_operation(arith::cmpi( + context, + CmpiPredicate::Eq, + arg0, + arg1, + location, + )); + + entry.append_operation(helper.cond_br( + context, + op0.result(0)?.into(), + [1, 0], + [&[]; 2], + location, + )); + + Ok(()) +} + +/// Generate MLIR operations for the `i16_is_zero` libfunc. +pub fn build_is_zero<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + _registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + _info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + let arg0: Value = entry.argument(0)?.into(); + + let op = entry.append_operation(arith::constant( + context, + IntegerAttribute::new(0, arg0.r#type()).into(), + location, + )); + let const_0 = op.result(0)?.into(); + + let condition = entry + .append_operation(arith::cmpi( + context, + CmpiPredicate::Eq, + arg0, + const_0, + location, + )) + .result(0)? + .into(); + + entry.append_operation(helper.cond_br(context, condition, [0, 1], [&[], &[arg0]], location)); + + Ok(()) +} + +/// Generate MLIR operations for the `i16_widemul` libfunc. +pub fn build_widemul<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + let target_type = registry.build_type( + context, + helper, + registry, + metadata, + &info.output_types()[0][0], + )?; + let lhs: Value = entry.argument(0)?.into(); + let rhs: Value = entry.argument(1)?.into(); + + let lhs = entry + .append_operation(arith::extsi(lhs, target_type, location)) + .result(0)? + .into(); + let rhs = entry + .append_operation(arith::extsi(rhs, target_type, location)) + .result(0)? + .into(); + + let result = entry + .append_operation(arith::muli(lhs, rhs, location)) + .result(0)? + .into(); + + entry.append_operation(helper.br(0, &[result], location)); + Ok(()) +} + +/// Generate MLIR operations for the `i16_to_felt252` libfunc. +pub fn build_to_felt252<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + let felt252_ty = registry.build_type( + context, + helper, + registry, + metadata, + &info.branch_signatures()[0].vars[0].ty, + )?; + let value: Value = entry.argument(0)?.into(); + + let result = entry + .append_operation(arith::extui(value, felt252_ty, location)) + .result(0)? + .into(); + + entry.append_operation(helper.br(0, &[result], location)); + + Ok(()) +} + +/// Generate MLIR operations for the `i16_from_felt252` libfunc. +pub fn build_from_felt252<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + let range_check: Value = entry.argument(0)?.into(); + let value: Value = entry.argument(1)?.into(); + + let felt252_ty = registry.build_type( + context, + helper, + registry, + metadata, + &info.param_signatures()[1].ty, + )?; + let result_ty = registry.build_type( + context, + helper, + registry, + metadata, + &info.branch_signatures()[0].vars[1].ty, + )?; + + let const_max = entry + .append_operation(arith::constant( + context, + Attribute::parse(context, &format!("{} : {}", i16::MAX, felt252_ty)).unwrap(), + location, + )) + .result(0)? + .into(); + + let is_ule = entry + .append_operation(arith::cmpi( + context, + CmpiPredicate::Ule, + value, + const_max, + location, + )) + .result(0)? + .into(); + + let block_success = helper.append_block(Block::new(&[])); + let block_failure = helper.append_block(Block::new(&[])); + + entry.append_operation(cf::cond_br( + context, + is_ule, + block_success, + block_failure, + &[], + &[], + location, + )); + + let value = block_success + .append_operation(arith::trunci(value, result_ty, location)) + .result(0)? + .into(); + block_success.append_operation(helper.br(0, &[range_check, value], location)); + + block_failure.append_operation(helper.br(1, &[range_check], location)); + + Ok(()) +} + +/// Generate MLIR operations for the `i16_diff` libfunc. +pub fn build_diff<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + _registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + _info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + let range_check: Value = entry.argument(0)?.into(); + let lhs: Value = entry.argument(1)?.into(); + let rhs: Value = entry.argument(2)?.into(); + + // Check if lhs >= rhs + let is_ge = entry + .append_operation(arith::cmpi(context, CmpiPredicate::Sge, lhs, rhs, location)) + .result(0)? + .into(); + + let result = entry + .append_operation(arith::subi(lhs, rhs, location)) + .result(0)? + .into(); + + entry.append_operation(helper.cond_br( + context, + is_ge, + [0, 1], + [&[range_check, result], &[range_check, result]], + location, + )); + + Ok(()) +} + +#[cfg(test)] +mod test { + use crate::{ + utils::test::{jit_enum, jit_panic, jit_struct, load_cairo, run_program_assert_output}, + values::JITValue, + }; + use cairo_felt::Felt252; + use cairo_lang_sierra::program::Program; + use lazy_static::lazy_static; + + lazy_static! { + static ref I16_OVERFLOWING_ADD: (String, Program) = load_cairo! { + fn run_test(lhs: i16, rhs: i16) -> i16 { + lhs + rhs + } + }; + static ref I16_OVERFLOWING_SUB: (String, Program) = load_cairo! { + fn run_test(lhs: i16, rhs: i16) -> i16 { + lhs - rhs + } + }; + static ref I16_EQUAL: (String, Program) = load_cairo! { + fn run_test(lhs: i16, rhs: i16) -> bool { + lhs == rhs + } + }; + static ref I16_IS_ZERO: (String, Program) = load_cairo! { + use zeroable::IsZeroResult; + + extern fn i16_is_zero(a: i16) -> IsZeroResult implicits() nopanic; + + fn run_test(value: i16) -> bool { + match i16_is_zero(value) { + IsZeroResult::Zero(_) => true, + IsZeroResult::NonZero(_) => false, + } + } + }; + static ref I16_WIDEMUL: (String, Program) = load_cairo! { + use integer::i16_wide_mul; + fn run_test(lhs: i16, rhs: i16) -> i32 { + i16_wide_mul(lhs, rhs) + } + }; + } + + #[test] + fn i16_const_min() { + let program = load_cairo!( + fn run_test() -> i16 { + 0_i16 + } + ); + + run_program_assert_output(&program, "run_test", &[], &[0i16.into()]); + } + + #[test] + fn i16_const_max() { + let program = load_cairo!( + fn run_test() -> i16 { + 32767_i16 + } + ); + + run_program_assert_output(&program, "run_test", &[], &[(i16::MAX).into()]); + } + + #[test] + fn i16_to_felt252() { + let program = load_cairo!( + use traits::Into; + + fn run_test() -> felt252 { + 2_i16.into() + } + ); + + run_program_assert_output(&program, "run_test", &[], &[Felt252::new(2).into()]); + } + + #[test] + fn i16_from_felt252() { + let program = load_cairo!( + use traits::TryInto; + + fn run_test() -> (Option, Option) { + (32767.try_into(), 32768.try_into()) + } + ); + + run_program_assert_output( + &program, + "run_test", + &[], + &[jit_struct!( + jit_enum!(0, 32767i16.into()), + jit_enum!(1, jit_struct!()), + )], + ); + } + + #[test] + fn i16_overflowing_add() { + #[track_caller] + fn run(lhs: i16, rhs: i16) { + let program = &I16_OVERFLOWING_ADD; + let error = Felt252::from_bytes_be(b"i16_add Overflow"); + + let add = lhs.checked_add(rhs); + + match add { + Some(result) => { + run_program_assert_output( + program, + "run_test", + &[lhs.into(), rhs.into()], + &[jit_enum!(0, jit_struct!(result.into()))], + ); + } + None => { + run_program_assert_output( + program, + "run_test", + &[lhs.into(), rhs.into()], + &[jit_panic!(JITValue::Felt252(error))], + ); + } + } + } + + const MAX: i16 = i16::MAX; + + run(0, 0); + run(0, 1); + run(0, MAX - 1); + run(0, MAX); + + run(1, 0); + run(1, 1); + run(1, MAX - 1); + run(1, MAX); + + run(MAX - 1, 0); + run(MAX - 1, 1); + run(MAX - 1, MAX - 1); + run(MAX - 1, MAX); + + run(MAX, 0); + run(MAX, 1); + run(MAX, MAX - 1); + run(MAX, MAX); + } + + #[test] + fn i16_overflowing_sub() { + #[track_caller] + fn run(lhs: i16, rhs: i16) { + let program = &I16_OVERFLOWING_SUB; + let error = Felt252::from_bytes_be(b"i16_sub Overflow"); + + let add = lhs.checked_sub(rhs); + + match add { + Some(result) => { + run_program_assert_output( + program, + "run_test", + &[lhs.into(), rhs.into()], + &[jit_enum!(0, jit_struct!(result.into()))], + ); + } + None => { + run_program_assert_output( + program, + "run_test", + &[lhs.into(), rhs.into()], + &[jit_panic!(JITValue::Felt252(error))], + ); + } + } + } + + const MAX: i16 = i16::MAX; + + run(0, 0); + run(0, 1); + run(0, MAX - 1); + run(0, MAX); + + run(1, 0); + run(1, 1); + run(1, MAX - 1); + run(1, MAX); + + run(MAX - 1, 0); + run(MAX - 1, 1); + run(MAX - 1, MAX - 1); + run(MAX - 1, MAX); + + run(MAX, 0); + run(MAX, 1); + run(MAX, MAX - 1); + run(MAX, MAX); + } + + #[test] + fn i16_equal() { + let program = &I16_EQUAL; + + run_program_assert_output( + program, + "run_test", + &[0i16.into(), 0i16.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i16.into(), 0i16.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[0i16.into(), 1i16.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i16.into(), 1i16.into()], + &[jit_enum!(1, jit_struct!())], + ); + } + + #[test] + fn i16_is_zero() { + let program = &I16_IS_ZERO; + + run_program_assert_output( + program, + "run_test", + &[0i16.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i16.into()], + &[jit_enum!(0, jit_struct!())], + ); + } + + #[test] + fn i16_safe_divmod() { + let program = &I16_IS_ZERO; + + run_program_assert_output( + program, + "run_test", + &[0i16.into(), 0i16.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[0i16.into(), 1i16.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[0i16.into(), i16::MAX.into()], + &[jit_enum!(1, jit_struct!())], + ); + + run_program_assert_output( + program, + "run_test", + &[1i16.into(), 0i16.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i16.into(), 1i16.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i16.into(), i16::MAX.into()], + &[jit_enum!(0, jit_struct!())], + ); + + run_program_assert_output( + program, + "run_test", + &[i16::MAX.into(), 0i16.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[i16::MAX.into(), 1i16.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[i16::MAX.into(), i16::MAX.into()], + &[jit_enum!(0, jit_struct!())], + ); + } + + #[test] + fn i16_widemul() { + let program = &I16_WIDEMUL; + + run_program_assert_output( + program, + "run_test", + &[0i16.into(), 0i16.into()], + &[0i32.into()], + ); + run_program_assert_output( + program, + "run_test", + &[0i16.into(), 1i16.into()], + &[0i32.into()], + ); + run_program_assert_output( + program, + "run_test", + &[1i16.into(), 0i16.into()], + &[0i32.into()], + ); + run_program_assert_output( + program, + "run_test", + &[1i16.into(), 1i16.into()], + &[1i32.into()], + ); + run_program_assert_output( + program, + "run_test", + &[i16::MAX.into(), i16::MAX.into()], + &[(i16::MAX as i32 * i16::MAX as i32).into()], + ); + } +} diff --git a/src/types.rs b/src/types.rs index 6ea22c828..35b16433b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -348,7 +348,13 @@ where metadata, WithSelf::new(self_ty, info), ), - CoreTypeConcrete::Sint32(_) => todo!(), + CoreTypeConcrete::Sint32(info) => self::uint32::build( + context, + module, + registry, + metadata, + WithSelf::new(self_ty, info), + ), CoreTypeConcrete::Sint64(_) => todo!(), CoreTypeConcrete::Sint128(_) => todo!(), CoreTypeConcrete::Bytes31(_) => todo!(), diff --git a/src/values.rs b/src/values.rs index 9b7c8d350..8f704d2a4 100644 --- a/src/values.rs +++ b/src/values.rs @@ -58,6 +58,7 @@ pub enum JITValue { Uint128(u128), Sint8(i8), Sint16(i16), + Sint32(i32), EcPoint(Felt252, Felt252), EcState(Felt252, Felt252, Felt252, Felt252), } @@ -112,6 +113,12 @@ impl From for JITValue { } } +impl From for JITValue { + fn from(value: i32) -> Self { + JITValue::Sint32(value) + } +} + impl + Clone> From<&[T]> for JITValue { fn from(value: &[T]) -> Self { Self::Array(value.iter().map(|x| x.clone().into()).collect()) @@ -407,6 +414,12 @@ impl JITValue { ptr } + JITValue::Sint32(value) => { + let ptr = arena.alloc_layout(Layout::new::()).cast(); + *ptr.cast::().as_mut() = *value; + + ptr + } JITValue::EcPoint(a, b) => { let ptr = arena .alloc_layout(layout_repeat(&get_integer_layout(252), 2).unwrap().0) @@ -511,7 +524,7 @@ impl JITValue { CoreTypeConcrete::Uint128MulGuarantee(_) => todo!(), CoreTypeConcrete::Sint8(_) => JITValue::Sint8(*ptr.cast::().as_ref()), CoreTypeConcrete::Sint16(_) => JITValue::Sint16(*ptr.cast::().as_ref()), - CoreTypeConcrete::Sint32(_) => todo!(), + CoreTypeConcrete::Sint32(_) => JITValue::Sint32(*ptr.cast::().as_ref()), CoreTypeConcrete::Sint64(_) => todo!(), CoreTypeConcrete::Sint128(_) => todo!(), CoreTypeConcrete::NonZero(info) => JITValue::from_jit(ptr, &info.ty, registry), @@ -706,7 +719,7 @@ impl ValueBuilder for CoreTypeConcrete { CoreTypeConcrete::Snapshot(_) => false, CoreTypeConcrete::Sint8(_) => false, CoreTypeConcrete::Sint16(_) => false, - CoreTypeConcrete::Sint32(_) => todo!(), + CoreTypeConcrete::Sint32(_) => false, CoreTypeConcrete::Sint64(_) => todo!(), CoreTypeConcrete::Sint128(_) => todo!(), CoreTypeConcrete::Bytes31(_) => todo!(), diff --git a/tests/cases.rs b/tests/cases.rs index 8477712fc..45e603f49 100644 --- a/tests/cases.rs +++ b/tests/cases.rs @@ -50,6 +50,14 @@ mod common; #[test_case("tests/cases/sint/i8_sub.cairo")] #[test_case("tests/cases/sint/i8_wide_mul.cairo")] #[test_case("tests/cases/sint/i8_to_from_felt252.cairo")] +// sint16 +#[test_case("tests/cases/sint/i16_diff.cairo")] +#[test_case("tests/cases/sint/i16_eq.cairo")] +#[test_case("tests/cases/sint/i16_is_zero.cairo")] +#[test_case("tests/cases/sint/i16_add.cairo")] +#[test_case("tests/cases/sint/i16_sub.cairo")] +#[test_case("tests/cases/sint/i16_wide_mul.cairo")] +#[test_case("tests/cases/sint/i16_to_from_felt252.cairo")] // structs #[test_case("tests/cases/structs/basic.cairo")] #[test_case("tests/cases/structs/bigger.cairo")] diff --git a/tests/cases/sint/i16_add.cairo b/tests/cases/sint/i16_add.cairo new file mode 100644 index 000000000..ce751d090 --- /dev/null +++ b/tests/cases/sint/i16_add.cairo @@ -0,0 +1,21 @@ +use integer::{i16_overflowing_add_impl, SignedIntegerResult}; + +fn overflowing_add(lhs: i16, rhs: i16) -> (i16, i16) { + match i16_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(res) => (res, 0), + SignedIntegerResult::Underflow(res) => (res, 1), + SignedIntegerResult::Overflow(res) =>(res, 2), + } +} + +fn main() -> (i16, i16, i16, i16, i16, i16, i16, i16) { + // In range additions + let (res_a, flag_a) = overflowing_add(16, 1); + let (res_b, flag_b) = overflowing_add(-1, -16); + // Underflow + let (res_c, flag_c) = overflowing_add(-3000, -1000); + // Overflow + let (res_d, flag_d) = overflowing_add(1000, 3000); + + ( res_a, flag_a, res_b, flag_b, res_c, flag_c, res_d, flag_d ) +} diff --git a/tests/cases/sint/i16_diff.cairo b/tests/cases/sint/i16_diff.cairo new file mode 100644 index 000000000..6f78d056c --- /dev/null +++ b/tests/cases/sint/i16_diff.cairo @@ -0,0 +1,24 @@ +fn diff(a: i16, b: i16) -> (u16, u16) { + match integer::i16_diff(a, b) { + Result::Ok(r) => (r, 0), + Result::Err(r) => (r, 1), + } +} + +fn main() -> ( + (u16, u16), (u16, u16), + (u16, u16), (u16, u16), + (u16, u16), (u16, u16), + (u16, u16), (u16, u16), +) { + ( + diff(18, 1), + diff(1, 18), + diff(0, 32767), + diff(32767, 0), + diff(-18, 1), + diff(1, -18), + diff(0, -32767), + diff(-32767, 0), + ) +} diff --git a/tests/cases/sint/i16_eq.cairo b/tests/cases/sint/i16_eq.cairo new file mode 100644 index 000000000..9aede7191 --- /dev/null +++ b/tests/cases/sint/i16_eq.cairo @@ -0,0 +1,7 @@ +fn main() -> (bool, bool, bool) { + ( + integer::i16_eq(17, 71), + integer::i16_eq(17, 17), + integer::i16_eq(2, -2) + ) +} diff --git a/tests/cases/sint/i16_is_zero.cairo b/tests/cases/sint/i16_is_zero.cairo new file mode 100644 index 000000000..6a6514142 --- /dev/null +++ b/tests/cases/sint/i16_is_zero.cairo @@ -0,0 +1,15 @@ +use zeroable::IsZeroResult; + +fn is_zero(val: i16) -> bool { + match integer::i16_is_zero(val) { + IsZeroResult::Zero => true, + IsZeroResult::NonZero(_) => false, + } +} +fn main() -> (bool, bool, bool) { + ( + is_zero(17), + is_zero(-17), + is_zero(0), + ) +} diff --git a/tests/cases/sint/i16_sub.cairo b/tests/cases/sint/i16_sub.cairo new file mode 100644 index 000000000..7ae44c2d7 --- /dev/null +++ b/tests/cases/sint/i16_sub.cairo @@ -0,0 +1,21 @@ +use integer::{i16_overflowing_sub_impl, SignedIntegerResult}; + +fn overflowing_sub(lhs: i16, rhs: i16) -> (i16, i16) { + match i16_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(res) => (res, 0), + SignedIntegerResult::Underflow(res) => (res, 1), + SignedIntegerResult::Overflow(res) =>(res, 2), + } +} + +fn main() -> (i16, i16, i16, i16, i16, i16, i16, i16) { + // In range subtractions + let (res_a, flag_a) = overflowing_sub(16, 1); + let (res_b, flag_b) = overflowing_sub(1, 16); + // Underflow + let (res_c, flag_c) = overflowing_sub(-3000, 1000); + // Overflow + let (res_d, flag_d) = overflowing_sub(3000, -1000); + + ( res_a, flag_a, res_b, flag_b, res_c, flag_c, res_d, flag_d ) +} diff --git a/tests/cases/sint/i16_to_from_felt252.cairo b/tests/cases/sint/i16_to_from_felt252.cairo new file mode 100644 index 000000000..9c1d17151 --- /dev/null +++ b/tests/cases/sint/i16_to_from_felt252.cairo @@ -0,0 +1,17 @@ +use traits::TryInto; + +fn main() -> ( + felt252, + felt252, + Option, + Option, + Option, +) { + ( + 17_i16.into(), + -17_i16.into(), + 17.try_into(), + 32769.try_into(), + 24857893469346.try_into(), + ) +} diff --git a/tests/cases/sint/i16_wide_mul.cairo b/tests/cases/sint/i16_wide_mul.cairo new file mode 100644 index 000000000..998d41f85 --- /dev/null +++ b/tests/cases/sint/i16_wide_mul.cairo @@ -0,0 +1,24 @@ +use integer::i16_wide_mul; + +fn main() -> ( + i32, i32, i32, + i32, i32, i32, + i32, i32, i32, + i32, i32, i32, +) { + ( + i16_wide_mul(0_i16, 0_i16), + i16_wide_mul(0_i16, 10_i16), + i16_wide_mul(0_i16, 255_i16), + i16_wide_mul(10_i16, 0_i16), + i16_wide_mul(10_i16, 10_i16), + i16_wide_mul(10_i16, 255_i16), + i16_wide_mul(255_i16, 0_i16), + i16_wide_mul(255_i16, 10_i16), + i16_wide_mul(255_i16, 255_i16), + i16_wide_mul(10_i16, -10_i16), + i16_wide_mul(10_i16, -255_i16), + i16_wide_mul(-255_i16, -255_i16), + + ) +} diff --git a/tests/common.rs b/tests/common.rs index 891e97705..ebcd245c6 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -666,7 +666,24 @@ pub fn compare_outputs( }; prop_assert_eq!(vm_value, native_value.into()) } - CoreTypeConcrete::Sint32(_) => todo!(), + CoreTypeConcrete::Sint32(_) => { + prop_assert!(vm_rets.peek().is_some(), "cairo-vm missing next value"); + prop_assert!( + native_rets.peek().is_some(), + "cairo-native missing next value" + ); + let mut vm_value: BigInt = BigInt::from_str(vm_rets.next().unwrap()).unwrap(); + // If the i16 value is negative we will get PRIME - val from the vm + if vm_value > *HALF_PRIME { + vm_value -= BigInt::from_biguint(Sign::Plus, PRIME.clone()); + } + let native_value: i32 = if let JITValue::Sint32(v) = native_rets.next().unwrap() { + *v + } else { + panic!("invalid type") + }; + prop_assert_eq!(vm_value, native_value.into()) + } CoreTypeConcrete::Sint64(_) => todo!(), CoreTypeConcrete::Sint128(_) => todo!(), CoreTypeConcrete::Bytes31(_) => todo!(),