From 3c6d762f0b62b247e5b57eec53d093d90ba88290 Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:42:23 +0200 Subject: [PATCH] Add `Sint32` & `Sint64` types & libfuncs (#383) * Push progress * Parse Sint8 result value * Improve sub test + fix how signed values are compared against vm values * Clippy * Improve testing * Fix * Try stuff * Fix typos * Fix test values * Add test & fix wide_mul * First draft of diff libfunc * Improve test * Remove old code * Add test * Add Sint16 type + libfuncs * fmt * Push progress * Remove uneeded intermidiate variables * Update code * Update code * Add tests for i64 libfuncs * Adjust test values * Fix cairo programs * fmt * Remove old file * Fix comment * Fix comments * Update code --- src/jit_runner.rs | 36 +- src/libfuncs.rs | 10 +- src/libfuncs/sint16.rs | 4 +- src/libfuncs/sint32.rs | 851 ++++++++++++++++++++ src/libfuncs/sint64.rs | 854 +++++++++++++++++++++ src/libfuncs/sint8.rs | 4 +- src/types.rs | 16 +- src/values.rs | 34 +- tests/cases.rs | 24 +- tests/cases/sint/eq.cairo | 24 + tests/cases/sint/i16_add.cairo | 21 - tests/cases/sint/i16_add_sub.cairo | 43 ++ tests/cases/sint/i16_eq.cairo | 7 - tests/cases/sint/i16_is_zero.cairo | 15 - tests/cases/sint/i16_sub.cairo | 21 - tests/cases/sint/i16_to_from_felt252.cairo | 17 - tests/cases/sint/i32_add_sub.cairo | 43 ++ tests/cases/sint/i32_diff.cairo | 24 + tests/cases/sint/i32_wide_mul.cairo | 24 + tests/cases/sint/i64_add_sub.cairo | 43 ++ tests/cases/sint/i64_diff.cairo | 24 + tests/cases/sint/i64_wide_mul.cairo | 24 + tests/cases/sint/i8_add.cairo | 21 - tests/cases/sint/i8_add_sub.cairo | 43 ++ tests/cases/sint/i8_eq.cairo | 7 - tests/cases/sint/i8_is_zero.cairo | 15 - tests/cases/sint/i8_sub.cairo | 21 - tests/cases/sint/i8_to_from_felt252.cairo | 17 - tests/cases/sint/is_zero.cairo | 54 ++ tests/cases/sint/to_from_felt252.cairo | 34 + tests/common.rs | 40 +- 31 files changed, 2225 insertions(+), 190 deletions(-) create mode 100644 src/libfuncs/sint32.rs create mode 100644 src/libfuncs/sint64.rs create mode 100644 tests/cases/sint/eq.cairo delete mode 100644 tests/cases/sint/i16_add.cairo create mode 100644 tests/cases/sint/i16_add_sub.cairo delete mode 100644 tests/cases/sint/i16_eq.cairo delete mode 100644 tests/cases/sint/i16_is_zero.cairo delete mode 100644 tests/cases/sint/i16_sub.cairo delete mode 100644 tests/cases/sint/i16_to_from_felt252.cairo create mode 100644 tests/cases/sint/i32_add_sub.cairo create mode 100644 tests/cases/sint/i32_diff.cairo create mode 100644 tests/cases/sint/i32_wide_mul.cairo create mode 100644 tests/cases/sint/i64_add_sub.cairo create mode 100644 tests/cases/sint/i64_diff.cairo create mode 100644 tests/cases/sint/i64_wide_mul.cairo delete mode 100644 tests/cases/sint/i8_add.cairo create mode 100644 tests/cases/sint/i8_add_sub.cairo delete mode 100644 tests/cases/sint/i8_eq.cairo delete mode 100644 tests/cases/sint/i8_is_zero.cairo delete mode 100644 tests/cases/sint/i8_sub.cairo delete mode 100644 tests/cases/sint/i8_to_from_felt252.cairo create mode 100644 tests/cases/sint/is_zero.cairo create mode 100644 tests/cases/sint/to_from_felt252.cairo diff --git a/src/jit_runner.rs b/src/jit_runner.rs index b49621d14..99af926d8 100644 --- a/src/jit_runner.rs +++ b/src/jit_runner.rs @@ -226,9 +226,39 @@ pub fn execute( params_ptrs.push(next.to_jit(&arena, registry, param_type_id)?); } - CoreTypeConcrete::Sint32(_) => todo!(), - CoreTypeConcrete::Sint64(_) => todo!(), - CoreTypeConcrete::Sint128(_) => todo!(), + CoreTypeConcrete::Sint32(_) => { + let next = params_it + .next() + .ok_or_else(|| make_missing_parameter(param_type_id))?; + + if !matches!(next, JITValue::Sint32(_)) { + Err(make_unexpected_value_error("JITValue::Sint32".to_string()))?; + } + + params_ptrs.push(next.to_jit(&arena, registry, param_type_id)?); + } + CoreTypeConcrete::Sint64(_) => { + let next = params_it + .next() + .ok_or_else(|| make_missing_parameter(param_type_id))?; + + if !matches!(next, JITValue::Sint64(_)) { + Err(make_unexpected_value_error("JITValue::Sint64".to_string()))?; + } + + params_ptrs.push(next.to_jit(&arena, registry, param_type_id)?); + } + CoreTypeConcrete::Sint128(_) => { + let next = params_it + .next() + .ok_or_else(|| make_missing_parameter(param_type_id))?; + + if !matches!(next, JITValue::Sint128(_)) { + Err(make_unexpected_value_error("JITValue::Sint128".to_string()))?; + } + + params_ptrs.push(next.to_jit(&arena, registry, param_type_id)?); + } CoreTypeConcrete::NonZero(info) => { let next = params_it .next() diff --git a/src/libfuncs.rs b/src/libfuncs.rs index afc8879d4..83df646b4 100644 --- a/src/libfuncs.rs +++ b/src/libfuncs.rs @@ -42,6 +42,8 @@ pub mod nullable; pub mod pedersen; pub mod poseidon; pub mod sint16; +pub mod sint32; +pub mod sint64; pub mod sint8; pub mod snapshot_take; pub mod stark_net; @@ -207,8 +209,12 @@ where CoreConcreteLibfunc::Sint16(info) => { self::sint16::build(context, registry, entry, location, helper, metadata, info) } - CoreConcreteLibfunc::Sint32(_) => todo!(), - CoreConcreteLibfunc::Sint64(_) => todo!(), + CoreConcreteLibfunc::Sint32(info) => { + self::sint32::build(context, registry, entry, location, helper, metadata, info) + } + CoreConcreteLibfunc::Sint64(info) => { + self::sint64::build(context, registry, entry, location, helper, metadata, info) + } CoreConcreteLibfunc::Sint128(_) => todo!(), CoreConcreteLibfunc::Bytes31(_) => todo!(), } diff --git a/src/libfuncs/sint16.rs b/src/libfuncs/sint16.rs index 5e9444a6e..4ed90a1c5 100644 --- a/src/libfuncs/sint16.rs +++ b/src/libfuncs/sint16.rs @@ -547,11 +547,11 @@ mod test { fn i16_const_min() { let program = load_cairo!( fn run_test() -> i16 { - 0_i16 + -32768_i16 } ); - run_program_assert_output(&program, "run_test", &[], &[0i16.into()]); + run_program_assert_output(&program, "run_test", &[], &[i16::MIN.into()]); } #[test] diff --git a/src/libfuncs/sint32.rs b/src/libfuncs/sint32.rs new file mode 100644 index 000000000..480922fec --- /dev/null +++ b/src/libfuncs/sint32.rs @@ -0,0 +1,851 @@ +//! # `i32`-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::{Sint32Concrete, Sint32Traits, 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: &Sint32Concrete, +) -> 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 `i32_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 i32 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 `i32_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 `i32_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 `i32_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 `i32_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 `i32_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!("{} : {}", i32::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 `i32_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_lang_sierra::program::Program; + use lazy_static::lazy_static; + use starknet_types_core::felt::Felt; + + lazy_static! { + static ref I32_OVERFLOWING_ADD: (String, Program) = load_cairo! { + fn run_test(lhs: i32, rhs: i32) -> i32 { + lhs + rhs + } + }; + static ref I32_OVERFLOWING_SUB: (String, Program) = load_cairo! { + fn run_test(lhs: i32, rhs: i32) -> i32 { + lhs - rhs + } + }; + static ref I32_EQUAL: (String, Program) = load_cairo! { + fn run_test(lhs: i32, rhs: i32) -> bool { + lhs == rhs + } + }; + static ref I32_IS_ZERO: (String, Program) = load_cairo! { + use zeroable::IsZeroResult; + + extern fn i32_is_zero(a: i32) -> IsZeroResult implicits() nopanic; + + fn run_test(value: i32) -> bool { + match i32_is_zero(value) { + IsZeroResult::Zero(_) => true, + IsZeroResult::NonZero(_) => false, + } + } + }; + static ref I32_WIDEMUL: (String, Program) = load_cairo! { + use integer::i32_wide_mul; + fn run_test(lhs: i32, rhs: i32) -> i64 { + i32_wide_mul(lhs, rhs) + } + }; + } + + #[test] + fn i32_const_min() { + let program = load_cairo!( + fn run_test() -> i32 { + -2147483648_i32 + } + ); + + run_program_assert_output(&program, "run_test", &[], &[i32::MIN.into()]); + } + + #[test] + fn i32_const_max() { + let program = load_cairo!( + fn run_test() -> i32 { + 2147483647_i32 + } + ); + + run_program_assert_output(&program, "run_test", &[], &[(i32::MAX).into()]); + } + + #[test] + fn i32_to_felt252() { + let program = load_cairo!( + use traits::Into; + + fn run_test() -> felt252 { + 2_i32.into() + } + ); + + run_program_assert_output(&program, "run_test", &[], &[Felt::from(2).into()]); + } + + #[test] + fn i32_from_felt252() { + let program = load_cairo!( + use traits::TryInto; + + fn run_test() -> (Option, Option) { + (2147483647.try_into(), 2147483648.try_into()) + } + ); + + run_program_assert_output( + &program, + "run_test", + &[], + &[jit_struct!( + jit_enum!(0, 2147483647i32.into()), + jit_enum!(1, jit_struct!()), + )], + ); + } + + #[test] + fn i32_overflowing_add() { + #[track_caller] + fn run(lhs: i32, rhs: i32) { + let program = &I32_OVERFLOWING_ADD; + let error = Felt::from_bytes_be_slice(b"i32_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: i32 = i32::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 i32_overflowing_sub() { + #[track_caller] + fn run(lhs: i32, rhs: i32) { + let program = &I32_OVERFLOWING_SUB; + let error = Felt::from_bytes_be_slice(b"i32_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: i32 = i32::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 i32_equal() { + let program = &I32_EQUAL; + + run_program_assert_output( + program, + "run_test", + &[0i32.into(), 0i32.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i32.into(), 0i32.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[0i32.into(), 1i32.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i32.into(), 1i32.into()], + &[jit_enum!(1, jit_struct!())], + ); + } + + #[test] + fn i32_is_zero() { + let program = &I32_IS_ZERO; + + run_program_assert_output( + program, + "run_test", + &[0i32.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i32.into()], + &[jit_enum!(0, jit_struct!())], + ); + } + + #[test] + fn i32_safe_divmod() { + let program = &I32_IS_ZERO; + + run_program_assert_output( + program, + "run_test", + &[0i32.into(), 0i32.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[0i32.into(), 1i32.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[0i32.into(), i32::MAX.into()], + &[jit_enum!(1, jit_struct!())], + ); + + run_program_assert_output( + program, + "run_test", + &[1i32.into(), 0i32.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i32.into(), 1i32.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i32.into(), i32::MAX.into()], + &[jit_enum!(0, jit_struct!())], + ); + + run_program_assert_output( + program, + "run_test", + &[i32::MAX.into(), 0i32.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[i32::MAX.into(), 1i32.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[i32::MAX.into(), i32::MAX.into()], + &[jit_enum!(0, jit_struct!())], + ); + } + + #[test] + fn i32_widemul() { + let program = &I32_WIDEMUL; + + run_program_assert_output( + program, + "run_test", + &[0i32.into(), 0i32.into()], + &[0i64.into()], + ); + run_program_assert_output( + program, + "run_test", + &[0i32.into(), 1i32.into()], + &[0i64.into()], + ); + run_program_assert_output( + program, + "run_test", + &[1i32.into(), 0i32.into()], + &[0i64.into()], + ); + run_program_assert_output( + program, + "run_test", + &[1i32.into(), 1i32.into()], + &[1i64.into()], + ); + run_program_assert_output( + program, + "run_test", + &[i32::MAX.into(), i32::MAX.into()], + &[(i32::MAX as i64 * i32::MAX as i64).into()], + ); + } +} diff --git a/src/libfuncs/sint64.rs b/src/libfuncs/sint64.rs new file mode 100644 index 000000000..4f9a978b1 --- /dev/null +++ b/src/libfuncs/sint64.rs @@ -0,0 +1,854 @@ +//! # `i64`-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::{Sint64Concrete, Sint64Traits, 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: &Sint64Concrete, +) -> 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 `i64_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 i64 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 `i64_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 `i64_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 `i64_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 `i64_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 `i64_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!("{} : {}", i64::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 `i64_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_lang_sierra::program::Program; + use lazy_static::lazy_static; + use starknet_types_core::felt::Felt; + + lazy_static! { + static ref I64_OVERFLOWING_ADD: (String, Program) = load_cairo! { + fn run_test(lhs: i64, rhs: i64) -> i64 { + lhs + rhs + } + }; + static ref I64_OVERFLOWING_SUB: (String, Program) = load_cairo! { + fn run_test(lhs: i64, rhs: i64) -> i64 { + lhs - rhs + } + }; + static ref I64_EQUAL: (String, Program) = load_cairo! { + fn run_test(lhs: i64, rhs: i64) -> bool { + lhs == rhs + } + }; + static ref I64_IS_ZERO: (String, Program) = load_cairo! { + use zeroable::IsZeroResult; + + extern fn i64_is_zero(a: i64) -> IsZeroResult implicits() nopanic; + + fn run_test(value: i64) -> bool { + match i64_is_zero(value) { + IsZeroResult::Zero(_) => true, + IsZeroResult::NonZero(_) => false, + } + } + }; + static ref I64_WIDEMUL: (String, Program) = load_cairo! { + use integer::i64_wide_mul; + fn run_test(lhs: i64, rhs: i64) -> i128 { + i64_wide_mul(lhs, rhs) + } + }; + } + + #[test] + fn i64_const_min() { + let program = load_cairo!( + fn run_test() -> i64 { + -9223372036854775808_i64 + } + ); + + run_program_assert_output(&program, "run_test", &[], &[i64::MIN.into()]); + } + + #[test] + fn i64_const_max() { + let program = load_cairo!( + fn run_test() -> i64 { + 9223372036854775807_i64 + } + ); + + run_program_assert_output(&program, "run_test", &[], &[(i64::MAX).into()]); + } + + #[test] + fn i64_to_felt252() { + let program = load_cairo!( + use traits::Into; + + fn run_test() -> felt252 { + 2_i64.into() + } + ); + + run_program_assert_output(&program, "run_test", &[], &[Felt::from(2).into()]); + } + + #[test] + fn i64_from_felt252() { + let program = load_cairo!( + use traits::TryInto; + + fn run_test() -> (Option, Option) { + ( + 9223372036854775807.try_into(), + 9223372036854775808.try_into(), + ) + } + ); + + run_program_assert_output( + &program, + "run_test", + &[], + &[jit_struct!( + jit_enum!(0, 9223372036854775807i64.into()), + jit_enum!(1, jit_struct!()), + )], + ); + } + + #[test] + fn i64_overflowing_add() { + #[track_caller] + fn run(lhs: i64, rhs: i64) { + let program = &I64_OVERFLOWING_ADD; + let error = Felt::from_bytes_be_slice(b"i64_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: i64 = i64::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 i64_overflowing_sub() { + #[track_caller] + fn run(lhs: i64, rhs: i64) { + let program = &I64_OVERFLOWING_SUB; + let error = Felt::from_bytes_be_slice(b"i64_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: i64 = i64::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 i64_equal() { + let program = &I64_EQUAL; + + run_program_assert_output( + program, + "run_test", + &[0i64.into(), 0i64.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i64.into(), 0i64.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[0i64.into(), 1i64.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i64.into(), 1i64.into()], + &[jit_enum!(1, jit_struct!())], + ); + } + + #[test] + fn i64_is_zero() { + let program = &I64_IS_ZERO; + + run_program_assert_output( + program, + "run_test", + &[0i64.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i64.into()], + &[jit_enum!(0, jit_struct!())], + ); + } + + #[test] + fn i64_safe_divmod() { + let program = &I64_IS_ZERO; + + run_program_assert_output( + program, + "run_test", + &[0i64.into(), 0i64.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[0i64.into(), 1i64.into()], + &[jit_enum!(1, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[0i64.into(), i64::MAX.into()], + &[jit_enum!(1, jit_struct!())], + ); + + run_program_assert_output( + program, + "run_test", + &[1i64.into(), 0i64.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i64.into(), 1i64.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[1i64.into(), i64::MAX.into()], + &[jit_enum!(0, jit_struct!())], + ); + + run_program_assert_output( + program, + "run_test", + &[i64::MAX.into(), 0i64.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[i64::MAX.into(), 1i64.into()], + &[jit_enum!(0, jit_struct!())], + ); + run_program_assert_output( + program, + "run_test", + &[i64::MAX.into(), i64::MAX.into()], + &[jit_enum!(0, jit_struct!())], + ); + } + + #[test] + fn i64_widemul() { + let program = &I64_WIDEMUL; + + run_program_assert_output( + program, + "run_test", + &[0i64.into(), 0i64.into()], + &[0i128.into()], + ); + run_program_assert_output( + program, + "run_test", + &[0i64.into(), 1i64.into()], + &[0i128.into()], + ); + run_program_assert_output( + program, + "run_test", + &[1i64.into(), 0i64.into()], + &[0i128.into()], + ); + run_program_assert_output( + program, + "run_test", + &[1i64.into(), 1i64.into()], + &[1i128.into()], + ); + run_program_assert_output( + program, + "run_test", + &[i64::MAX.into(), i64::MAX.into()], + &[(i64::MAX as i128 * i64::MAX as i128).into()], + ); + } +} diff --git a/src/libfuncs/sint8.rs b/src/libfuncs/sint8.rs index a98f2fff9..ce2023dfa 100644 --- a/src/libfuncs/sint8.rs +++ b/src/libfuncs/sint8.rs @@ -547,11 +547,11 @@ mod test { fn i8_const_min() { let program = load_cairo!( fn run_test() -> i8 { - 0_i8 + -128_i8 } ); - run_program_assert_output(&program, "run_test", &[], &[0i8.into()]); + run_program_assert_output(&program, "run_test", &[], &[i8::MIN.into()]); } #[test] diff --git a/src/types.rs b/src/types.rs index 35b16433b..4ae7bcf06 100644 --- a/src/types.rs +++ b/src/types.rs @@ -355,8 +355,20 @@ where metadata, WithSelf::new(self_ty, info), ), - CoreTypeConcrete::Sint64(_) => todo!(), - CoreTypeConcrete::Sint128(_) => todo!(), + CoreTypeConcrete::Sint64(info) => self::uint64::build( + context, + module, + registry, + metadata, + WithSelf::new(self_ty, info), + ), + CoreTypeConcrete::Sint128(info) => self::uint128::build( + context, + module, + registry, + metadata, + WithSelf::new(self_ty, info), + ), CoreTypeConcrete::Bytes31(_) => todo!(), } } diff --git a/src/values.rs b/src/values.rs index a40ef401b..c8d34699b 100644 --- a/src/values.rs +++ b/src/values.rs @@ -59,6 +59,8 @@ pub enum JITValue { Sint8(i8), Sint16(i16), Sint32(i32), + Sint64(i64), + Sint128(i128), EcPoint(Felt, Felt), EcState(Felt, Felt, Felt, Felt), } @@ -119,6 +121,18 @@ impl From for JITValue { } } +impl From for JITValue { + fn from(value: i64) -> Self { + JITValue::Sint64(value) + } +} + +impl From for JITValue { + fn from(value: i128) -> Self { + JITValue::Sint128(value) + } +} + impl + Clone> From<&[T]> for JITValue { fn from(value: &[T]) -> Self { Self::Array(value.iter().map(|x| x.clone().into()).collect()) @@ -420,6 +434,18 @@ impl JITValue { ptr } + JITValue::Sint64(value) => { + let ptr = arena.alloc_layout(Layout::new::()).cast(); + *ptr.cast::().as_mut() = *value; + + ptr + } + JITValue::Sint128(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) @@ -525,8 +551,8 @@ impl JITValue { CoreTypeConcrete::Sint8(_) => JITValue::Sint8(*ptr.cast::().as_ref()), CoreTypeConcrete::Sint16(_) => JITValue::Sint16(*ptr.cast::().as_ref()), CoreTypeConcrete::Sint32(_) => JITValue::Sint32(*ptr.cast::().as_ref()), - CoreTypeConcrete::Sint64(_) => todo!(), - CoreTypeConcrete::Sint128(_) => todo!(), + CoreTypeConcrete::Sint64(_) => JITValue::Sint64(*ptr.cast::().as_ref()), + CoreTypeConcrete::Sint128(_) => JITValue::Sint128(*ptr.cast::().as_ref()), CoreTypeConcrete::NonZero(info) => JITValue::from_jit(ptr, &info.ty, registry), CoreTypeConcrete::Nullable(_) => todo!(), CoreTypeConcrete::Uninitialized(_) => todo!(), @@ -720,8 +746,8 @@ impl ValueBuilder for CoreTypeConcrete { CoreTypeConcrete::Sint8(_) => false, CoreTypeConcrete::Sint16(_) => false, CoreTypeConcrete::Sint32(_) => false, - CoreTypeConcrete::Sint64(_) => todo!(), - CoreTypeConcrete::Sint128(_) => todo!(), + CoreTypeConcrete::Sint64(_) => false, + CoreTypeConcrete::Sint128(_) => false, CoreTypeConcrete::Bytes31(_) => todo!(), } } diff --git a/tests/cases.rs b/tests/cases.rs index 105dad339..adb8e470a 100644 --- a/tests/cases.rs +++ b/tests/cases.rs @@ -42,22 +42,26 @@ mod common; #[test_case("tests/cases/uint/upcasts.cairo")] #[test_case("tests/cases/uint/wide_mul.cairo")] #[test_case("tests/cases/uint/u512_safe_divmod_by_u256.cairo")] +// sint +#[test_case("tests/cases/sint/eq.cairo")] +#[test_case("tests/cases/sint/is_zero.cairo")] +#[test_case("tests/cases/sint/to_from_felt252.cairo")] // sint8 #[test_case("tests/cases/sint/i8_diff.cairo")] -#[test_case("tests/cases/sint/i8_eq.cairo")] -#[test_case("tests/cases/sint/i8_is_zero.cairo")] -#[test_case("tests/cases/sint/i8_add.cairo")] -#[test_case("tests/cases/sint/i8_sub.cairo")] +#[test_case("tests/cases/sint/i8_add_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_add_sub.cairo")] #[test_case("tests/cases/sint/i16_wide_mul.cairo")] -#[test_case("tests/cases/sint/i16_to_from_felt252.cairo")] +// sint32 +#[test_case("tests/cases/sint/i32_diff.cairo")] +#[test_case("tests/cases/sint/i32_add_sub.cairo")] +#[test_case("tests/cases/sint/i32_wide_mul.cairo")] +// sint64 +#[test_case("tests/cases/sint/i64_diff.cairo")] +#[test_case("tests/cases/sint/i64_add_sub.cairo")] +#[test_case("tests/cases/sint/i64_wide_mul.cairo")] // structs #[test_case("tests/cases/structs/basic.cairo")] #[test_case("tests/cases/structs/bigger.cairo")] diff --git a/tests/cases/sint/eq.cairo b/tests/cases/sint/eq.cairo new file mode 100644 index 000000000..67e76469e --- /dev/null +++ b/tests/cases/sint/eq.cairo @@ -0,0 +1,24 @@ +fn main() -> ( + bool, bool, bool, + bool, bool, bool, + bool, bool, bool, + bool, bool, bool, +) { + ( + integer::i8_eq(17, 71), + integer::i8_eq(17, 17), + integer::i8_eq(2, -2), + + integer::i16_eq(17, 71), + integer::i16_eq(17, 17), + integer::i16_eq(2, -2), + + integer::i32_eq(17, 71), + integer::i32_eq(17, 17), + integer::i32_eq(2, -2), + + integer::i64_eq(17, 71), + integer::i64_eq(17, 17), + integer::i64_eq(2, -2), + ) +} diff --git a/tests/cases/sint/i16_add.cairo b/tests/cases/sint/i16_add.cairo deleted file mode 100644 index ce751d090..000000000 --- a/tests/cases/sint/i16_add.cairo +++ /dev/null @@ -1,21 +0,0 @@ -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_add_sub.cairo b/tests/cases/sint/i16_add_sub.cairo new file mode 100644 index 000000000..67a410831 --- /dev/null +++ b/tests/cases/sint/i16_add_sub.cairo @@ -0,0 +1,43 @@ +use integer::{i16_overflowing_add_impl, i16_overflowing_sub_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 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), + (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); + + // In range subtractions + let (res_e, flag_e) = overflowing_sub(16, 1); + let (res_f, flag_f) = overflowing_sub(1, 16); + // Underflow + let (res_g, flag_g) = overflowing_sub(-3000, 1000); + // Overflow + let (res_h, flag_h) = overflowing_sub(1000, -3000); + + ( + ( res_a, flag_a, res_b, flag_b, res_c, flag_c, res_d, flag_d ), + ( res_e, flag_e, res_f, flag_f, res_g, flag_g, res_h, flag_h ), + ) +} diff --git a/tests/cases/sint/i16_eq.cairo b/tests/cases/sint/i16_eq.cairo deleted file mode 100644 index 9aede7191..000000000 --- a/tests/cases/sint/i16_eq.cairo +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 6a6514142..000000000 --- a/tests/cases/sint/i16_is_zero.cairo +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index 7ae44c2d7..000000000 --- a/tests/cases/sint/i16_sub.cairo +++ /dev/null @@ -1,21 +0,0 @@ -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 deleted file mode 100644 index 9c1d17151..000000000 --- a/tests/cases/sint/i16_to_from_felt252.cairo +++ /dev/null @@ -1,17 +0,0 @@ -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/i32_add_sub.cairo b/tests/cases/sint/i32_add_sub.cairo new file mode 100644 index 000000000..8eeb23299 --- /dev/null +++ b/tests/cases/sint/i32_add_sub.cairo @@ -0,0 +1,43 @@ +use integer::{i32_overflowing_add_impl, i32_overflowing_sub_impl, SignedIntegerResult}; + +fn overflowing_add(lhs: i32, rhs: i32) -> (i32, i32) { + match i32_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(res) => (res, 0), + SignedIntegerResult::Underflow(res) => (res, 1), + SignedIntegerResult::Overflow(res) =>(res, 2), + } +} + +fn overflowing_sub(lhs: i32, rhs: i32) -> (i32, i32) { + match i32_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(res) => (res, 0), + SignedIntegerResult::Underflow(res) => (res, 1), + SignedIntegerResult::Overflow(res) =>(res, 2), + } +} + +fn main() -> ( + (i32, i32, i32, i32, i32, i32, i32, i32), + (i32, i32, i32, i32, i32, i32, i32, i32), +) { + // 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(-2000000000, -2000000000); + // Overflow + let (res_d, flag_d) = overflowing_add(2000000000, 2000000000); + + // In range subtractions + let (res_e, flag_e) = overflowing_sub(16, 1); + let (res_f, flag_f) = overflowing_sub(1, 16); + // Underflow + let (res_g, flag_g) = overflowing_sub(-2000000000, 2000000000); + // Overflow + let (res_h, flag_h) = overflowing_sub(2000000000, -2000000000); + + ( + ( res_a, flag_a, res_b, flag_b, res_c, flag_c, res_d, flag_d ), + ( res_e, flag_e, res_f, flag_f, res_g, flag_g, res_h, flag_h ), + ) +} diff --git a/tests/cases/sint/i32_diff.cairo b/tests/cases/sint/i32_diff.cairo new file mode 100644 index 000000000..0a0f7fa1e --- /dev/null +++ b/tests/cases/sint/i32_diff.cairo @@ -0,0 +1,24 @@ +fn diff(a: i32, b: i32) -> (u32, u32) { + match integer::i32_diff(a, b) { + Result::Ok(r) => (r, 0), + Result::Err(r) => (r, 1), + } +} + +fn main() -> ( + (u32, u32), (u32, u32), + (u32, u32), (u32, u32), + (u32, u32), (u32, u32), + (u32, u32), (u32, u32), +) { + ( + diff(18, 1), + diff(1, 18), + diff(0, 2147483647), + diff(2147483647, 0), + diff(-18, 1), + diff(1, -18), + diff(0, -2147483647), + diff(-2147483647, 0), + ) +} diff --git a/tests/cases/sint/i32_wide_mul.cairo b/tests/cases/sint/i32_wide_mul.cairo new file mode 100644 index 000000000..716715977 --- /dev/null +++ b/tests/cases/sint/i32_wide_mul.cairo @@ -0,0 +1,24 @@ +use integer::i32_wide_mul; + +fn main() -> ( + i64, i64, i64, + i64, i64, i64, + i64, i64, i64, + i64, i64, i64, +) { + ( + i32_wide_mul(0_i32, 0_i32), + i32_wide_mul(0_i32, 10_i32), + i32_wide_mul(0_i32, 2147483647_i32), + i32_wide_mul(10_i32, 0_i32), + i32_wide_mul(10_i32, 10_i32), + i32_wide_mul(10_i32, 2147483647_i32), + i32_wide_mul(2147483647_i32, 0_i32), + i32_wide_mul(2147483647_i32, 10_i32), + i32_wide_mul(2147483647_i32, 2147483647_i32), + i32_wide_mul(10_i32, -10_i32), + i32_wide_mul(10_i32, -2147483647_i32), + i32_wide_mul(-2147483647_i32, -2147483647_i32), + + ) +} diff --git a/tests/cases/sint/i64_add_sub.cairo b/tests/cases/sint/i64_add_sub.cairo new file mode 100644 index 000000000..ef66e98e4 --- /dev/null +++ b/tests/cases/sint/i64_add_sub.cairo @@ -0,0 +1,43 @@ +use integer::{i64_overflowing_add_impl, i64_overflowing_sub_impl, SignedIntegerResult}; + +fn overflowing_add(lhs: i64, rhs: i64) -> (i64, i64) { + match i64_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(res) => (res, 0), + SignedIntegerResult::Underflow(res) => (res, 1), + SignedIntegerResult::Overflow(res) =>(res, 2), + } +} + +fn overflowing_sub(lhs: i64, rhs: i64) -> (i64, i64) { + match i64_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(res) => (res, 0), + SignedIntegerResult::Underflow(res) => (res, 1), + SignedIntegerResult::Overflow(res) =>(res, 2), + } +} + +fn main() -> ( + (i64, i64, i64, i64, i64, i64, i64, i64), + (i64, i64, i64, i64, i64, i64, i64, i64), +) { + // 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(-9000000000000000000, -2000000000000000000); + // Overflow + let (res_d, flag_d) = overflowing_add(9000000000000000000, 2000000000000000000); + + // In range subtractions + let (res_e, flag_e) = overflowing_sub(16, 1); + let (res_f, flag_f) = overflowing_sub(1, 16); + // Underflow + let (res_g, flag_g) = overflowing_sub(-9000000000000000000, 2000000000000000000); + // Overflow + let (res_h, flag_h) = overflowing_sub(9000000000000000000, -2000000000000000000); + + ( + ( res_a, flag_a, res_b, flag_b, res_c, flag_c, res_d, flag_d ), + ( res_e, flag_e, res_f, flag_f, res_g, flag_g, res_h, flag_h ), + ) +} diff --git a/tests/cases/sint/i64_diff.cairo b/tests/cases/sint/i64_diff.cairo new file mode 100644 index 000000000..cfc74de1a --- /dev/null +++ b/tests/cases/sint/i64_diff.cairo @@ -0,0 +1,24 @@ +fn diff(a: i64, b: i64) -> (u64, u64) { + match integer::i64_diff(a, b) { + Result::Ok(r) => (r, 0), + Result::Err(r) => (r, 1), + } +} + +fn main() -> ( + (u64, u64), (u64, u64), + (u64, u64), (u64, u64), + (u64, u64), (u64, u64), + (u64, u64), (u64, u64), +) { + ( + diff(18, 1), + diff(1, 18), + diff(0, 9223372036854775807), + diff(9223372036854775807, 0), + diff(-18, 1), + diff(1, -18), + diff(0, -9223372036854775807), + diff(-9223372036854775807, 0), + ) +} diff --git a/tests/cases/sint/i64_wide_mul.cairo b/tests/cases/sint/i64_wide_mul.cairo new file mode 100644 index 000000000..0da94b309 --- /dev/null +++ b/tests/cases/sint/i64_wide_mul.cairo @@ -0,0 +1,24 @@ +use integer::i64_wide_mul; + +fn main() -> ( + i128, i128, i128, + i128, i128, i128, + i128, i128, i128, + i128, i128, i128, +) { + ( + i64_wide_mul(0_i64, 0_i64), + i64_wide_mul(0_i64, 10_i64), + i64_wide_mul(0_i64, 9223372036854775807_i64), + i64_wide_mul(10_i64, 0_i64), + i64_wide_mul(10_i64, 10_i64), + i64_wide_mul(10_i64, 9223372036854775807_i64), + i64_wide_mul(9223372036854775807_i64, 0_i64), + i64_wide_mul(9223372036854775807_i64, 10_i64), + i64_wide_mul(9223372036854775807_i64, 9223372036854775807_i64), + i64_wide_mul(10_i64, -10_i64), + i64_wide_mul(10_i64, -9223372036854775807_i64), + i64_wide_mul(-9223372036854775807_i64, -9223372036854775807_i64), + + ) +} diff --git a/tests/cases/sint/i8_add.cairo b/tests/cases/sint/i8_add.cairo deleted file mode 100644 index d644f54d2..000000000 --- a/tests/cases/sint/i8_add.cairo +++ /dev/null @@ -1,21 +0,0 @@ -use integer::{i8_overflowing_add_impl, SignedIntegerResult}; - -fn overflowing_add(lhs: i8, rhs: i8) -> (i8, i8) { - match i8_overflowing_add_impl(lhs, rhs) { - SignedIntegerResult::InRange(res) => (res, 0), - SignedIntegerResult::Underflow(res) => (res, 1), - SignedIntegerResult::Overflow(res) =>(res, 2), - } -} - -fn main() -> (i8, i8, i8, i8, i8, i8, i8, i8) { - // 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(-100, -100); - // Overflow - let (res_d, flag_d) = overflowing_add(100, 100); - - ( res_a, flag_a, res_b, flag_b, res_c, flag_c, res_d, flag_d ) -} diff --git a/tests/cases/sint/i8_add_sub.cairo b/tests/cases/sint/i8_add_sub.cairo new file mode 100644 index 000000000..59bd1e929 --- /dev/null +++ b/tests/cases/sint/i8_add_sub.cairo @@ -0,0 +1,43 @@ +use integer::{i8_overflowing_add_impl, i8_overflowing_sub_impl, SignedIntegerResult}; + +fn overflowing_add(lhs: i8, rhs: i8) -> (i8, i8) { + match i8_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(res) => (res, 0), + SignedIntegerResult::Underflow(res) => (res, 1), + SignedIntegerResult::Overflow(res) =>(res, 2), + } +} + +fn overflowing_sub(lhs: i8, rhs: i8) -> (i8, i8) { + match i8_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(res) => (res, 0), + SignedIntegerResult::Underflow(res) => (res, 1), + SignedIntegerResult::Overflow(res) =>(res, 2), + } +} + +fn main() -> ( + (i8, i8, i8, i8, i8, i8, i8, i8), + (i8, i8, i8, i8, i8, i8, i8, i8), +) { + // 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(-100, -100); + // Overflow + let (res_d, flag_d) = overflowing_add(100, 100); + + // In range subtractions + let (res_e, flag_e) = overflowing_sub(16, 1); + let (res_f, flag_f) = overflowing_sub(1, 16); + // Underflow + let (res_g, flag_g) = overflowing_sub(-100, 100); + // Overflow + let (res_h, flag_h) = overflowing_sub(100, -100); + + ( + ( res_a, flag_a, res_b, flag_b, res_c, flag_c, res_d, flag_d ), + ( res_e, flag_e, res_f, flag_f, res_g, flag_g, res_h, flag_h ), + ) +} diff --git a/tests/cases/sint/i8_eq.cairo b/tests/cases/sint/i8_eq.cairo deleted file mode 100644 index a7bf2d4bb..000000000 --- a/tests/cases/sint/i8_eq.cairo +++ /dev/null @@ -1,7 +0,0 @@ -fn main() -> (bool, bool, bool) { - ( - integer::i8_eq(17, 71), - integer::i8_eq(17, 17), - integer::i8_eq(2, -2) - ) -} diff --git a/tests/cases/sint/i8_is_zero.cairo b/tests/cases/sint/i8_is_zero.cairo deleted file mode 100644 index 4cf39e576..000000000 --- a/tests/cases/sint/i8_is_zero.cairo +++ /dev/null @@ -1,15 +0,0 @@ -use zeroable::IsZeroResult; - -fn is_zero(val: i8) -> bool { - match integer::i8_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/i8_sub.cairo b/tests/cases/sint/i8_sub.cairo deleted file mode 100644 index 8872e897f..000000000 --- a/tests/cases/sint/i8_sub.cairo +++ /dev/null @@ -1,21 +0,0 @@ -use integer::{i8_overflowing_sub_impl, SignedIntegerResult}; - -fn overflowing_sub(lhs: i8, rhs: i8) -> (i8, i8) { - match i8_overflowing_sub_impl(lhs, rhs) { - SignedIntegerResult::InRange(res) => (res, 0), - SignedIntegerResult::Underflow(res) => (res, 1), - SignedIntegerResult::Overflow(res) =>(res, 2), - } -} - -fn main() -> (i8, i8, i8, i8, i8, i8, i8, i8) { - // 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(-100, 100); - // Overflow - let (res_d, flag_d) = overflowing_sub(100, -100); - - ( res_a, flag_a, res_b, flag_b, res_c, flag_c, res_d, flag_d ) -} diff --git a/tests/cases/sint/i8_to_from_felt252.cairo b/tests/cases/sint/i8_to_from_felt252.cairo deleted file mode 100644 index 68d99f031..000000000 --- a/tests/cases/sint/i8_to_from_felt252.cairo +++ /dev/null @@ -1,17 +0,0 @@ -use traits::TryInto; - -fn main() -> ( - felt252, - felt252, - Option, - Option, - Option, -) { - ( - 17_i8.into(), - -17_i8.into(), - 17.try_into(), - 150.try_into(), - 24857893469346.try_into(), - ) -} diff --git a/tests/cases/sint/is_zero.cairo b/tests/cases/sint/is_zero.cairo new file mode 100644 index 000000000..13b9b986f --- /dev/null +++ b/tests/cases/sint/is_zero.cairo @@ -0,0 +1,54 @@ +use zeroable::IsZeroResult; + +fn is_zero_i8(val: i8) -> bool { + match integer::i8_is_zero(val) { + IsZeroResult::Zero => true, + IsZeroResult::NonZero(_) => false, + } +} + +fn is_zero_i16(val: i16) -> bool { + match integer::i16_is_zero(val) { + IsZeroResult::Zero => true, + IsZeroResult::NonZero(_) => false, + } +} + +fn is_zero_i32(val: i32) -> bool { + match integer::i32_is_zero(val) { + IsZeroResult::Zero => true, + IsZeroResult::NonZero(_) => false, + } +} + +fn is_zero_i64(val: i64) -> bool { + match integer::i64_is_zero(val) { + IsZeroResult::Zero => true, + IsZeroResult::NonZero(_) => false, + } +} + +fn main() -> ( + bool, bool, bool, + bool, bool, bool, + bool, bool, bool, + bool, bool, bool, +) { + ( + is_zero_i8(17), + is_zero_i8(-17), + is_zero_i8(0), + + is_zero_i16(17), + is_zero_i16(-17), + is_zero_i16(0), + + is_zero_i32(17), + is_zero_i32(-17), + is_zero_i32(0), + + is_zero_i64(17), + is_zero_i64(-17), + is_zero_i64(0), + ) +} diff --git a/tests/cases/sint/to_from_felt252.cairo b/tests/cases/sint/to_from_felt252.cairo new file mode 100644 index 000000000..ad6af0a45 --- /dev/null +++ b/tests/cases/sint/to_from_felt252.cairo @@ -0,0 +1,34 @@ +use traits::TryInto; + +fn main() -> ( + felt252, felt252, Option, Option, Option, + felt252, felt252, Option, Option, Option, + felt252, felt252, Option, Option, Option, + felt252, felt252, Option, Option, Option, +) { + ( + 17_i8.into(), + -17_i8.into(), + 17.try_into(), + 150.try_into(), + 24857893469346.try_into(), + + 17_i16.into(), + -17_i16.into(), + 17.try_into(), + 270.try_into(), + 24857893469346.try_into(), + + 17_i16.into(), + -17_i16.into(), + 17.try_into(), + 2147483649.try_into(), + 24857893469346.try_into(), + + 17_i32.into(), + -17_i32.into(), + 17.try_into(), + 9223372036854775809.try_into(), + 24857893469346978675645.try_into(), + ) +} diff --git a/tests/common.rs b/tests/common.rs index aad8ea8cf..5f16bf472 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -681,7 +681,7 @@ pub fn compare_outputs( "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 the i32 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()); } @@ -692,8 +692,42 @@ pub fn compare_outputs( }; prop_assert_eq!(vm_value, native_value.into()) } - CoreTypeConcrete::Sint64(_) => todo!(), - CoreTypeConcrete::Sint128(_) => todo!(), + CoreTypeConcrete::Sint64(_) => { + 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 i64 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: i64 = if let JITValue::Sint64(v) = native_rets.next().unwrap() { + *v + } else { + panic!("invalid type") + }; + prop_assert_eq!(vm_value, native_value.into()) + } + CoreTypeConcrete::Sint128(_) => { + 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 i128 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: i128 = if let JITValue::Sint128(v) = native_rets.next().unwrap() { + *v + } else { + panic!("invalid type") + }; + prop_assert_eq!(vm_value, native_value.into()) + } CoreTypeConcrete::Bytes31(_) => todo!(), }