Skip to content

Commit

Permalink
Added basic support for signed ints. (#3450)
Browse files Browse the repository at this point in the history
  • Loading branch information
orizi authored Jun 22, 2023
1 parent 03aa221 commit d80d29b
Show file tree
Hide file tree
Showing 24 changed files with 1,562 additions and 35 deletions.
125 changes: 125 additions & 0 deletions corelib/src/integer.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,31 @@ impl U256TryIntoFelt252 of TryInto<u256, felt252> {
)
}
}
impl I8IntoFelt252 of Into<i8, felt252> {
fn into(self: i8) -> felt252 {
i8_to_felt252(self)
}
}
impl I16IntoFelt252 of Into<i16, felt252> {
fn into(self: i16) -> felt252 {
i16_to_felt252(self)
}
}
impl I32IntoFelt252 of Into<i32, felt252> {
fn into(self: i32) -> felt252 {
i32_to_felt252(self)
}
}
impl I64IntoFelt252 of Into<i64, felt252> {
fn into(self: i64) -> felt252 {
i64_to_felt252(self)
}
}
impl I128IntoFelt252 of Into<i128, felt252> {
fn into(self: i128) -> felt252 {
i128_to_felt252(self)
}
}

// TODO(lior): Restrict the function (using traits) in the high-level compiler so that wrong types
// will not lead to Sierra errors.
Expand Down Expand Up @@ -1893,3 +1918,103 @@ impl U256Zeroable of Zeroable<u256> {
self != U256Zeroable::zero()
}
}

#[derive(Copy, Drop)]
extern type i8;
impl NumericLiterali8 of NumericLiteral<i8>;
extern fn i8_const<value>() -> i8 nopanic;
extern fn i8_to_felt252(a: i8) -> felt252 nopanic;

extern fn i8_is_zero(a: i8) -> IsZeroResult<i8> implicits() nopanic;
extern fn i8_eq(lhs: i8, rhs: i8) -> bool implicits() nopanic;

impl I8PartialEq of PartialEq<i8> {
#[inline(always)]
fn eq(lhs: @i8, rhs: @i8) -> bool {
i8_eq(*lhs, *rhs)
}
#[inline(always)]
fn ne(lhs: @i8, rhs: @i8) -> bool {
!(*lhs == *rhs)
}
}

#[derive(Copy, Drop)]
extern type i16;
impl NumericLiterali16 of NumericLiteral<i16>;
extern fn i16_const<value>() -> i16 nopanic;
extern fn i16_to_felt252(a: i16) -> felt252 nopanic;

extern fn i16_is_zero(a: i16) -> IsZeroResult<i16> implicits() nopanic;
extern fn i16_eq(lhs: i16, rhs: i16) -> bool implicits() nopanic;

impl I16PartialEq of PartialEq<i16> {
#[inline(always)]
fn eq(lhs: @i16, rhs: @i16) -> bool {
i16_eq(*lhs, *rhs)
}
#[inline(always)]
fn ne(lhs: @i16, rhs: @i16) -> bool {
!(*lhs == *rhs)
}
}

#[derive(Copy, Drop)]
extern type i32;
impl NumericLiterali32 of NumericLiteral<i32>;
extern fn i32_const<value>() -> i32 nopanic;
extern fn i32_to_felt252(a: i32) -> felt252 nopanic;

extern fn i32_is_zero(a: i32) -> IsZeroResult<i32> implicits() nopanic;
extern fn i32_eq(lhs: i32, rhs: i32) -> bool implicits() nopanic;

impl I32PartialEq of PartialEq<i32> {
#[inline(always)]
fn eq(lhs: @i32, rhs: @i32) -> bool {
i32_eq(*lhs, *rhs)
}
#[inline(always)]
fn ne(lhs: @i32, rhs: @i32) -> bool {
!(*lhs == *rhs)
}
}

#[derive(Copy, Drop)]
extern type i64;
impl NumericLiterali64 of NumericLiteral<i64>;
extern fn i64_const<value>() -> i64 nopanic;
extern fn i64_to_felt252(a: i64) -> felt252 nopanic;

extern fn i64_is_zero(a: i64) -> IsZeroResult<i64> implicits() nopanic;
extern fn i64_eq(lhs: i64, rhs: i64) -> bool implicits() nopanic;

impl I64PartialEq of PartialEq<i64> {
#[inline(always)]
fn eq(lhs: @i64, rhs: @i64) -> bool {
i64_eq(*lhs, *rhs)
}
#[inline(always)]
fn ne(lhs: @i64, rhs: @i64) -> bool {
!(*lhs == *rhs)
}
}

#[derive(Copy, Drop)]
extern type i128;
impl NumericLiterali128 of NumericLiteral<i128>;
extern fn i128_const<value>() -> i128 nopanic;
extern fn i128_to_felt252(a: i128) -> felt252 nopanic;

extern fn i128_is_zero(a: i128) -> IsZeroResult<i128> implicits() nopanic;
extern fn i128_eq(lhs: i128, rhs: i128) -> bool implicits() nopanic;

impl I128PartialEq of PartialEq<i128> {
#[inline(always)]
fn eq(lhs: @i128, rhs: @i128) -> bool {
i128_eq(*lhs, *rhs)
}
#[inline(always)]
fn ne(lhs: @i128, rhs: @i128) -> bool {
!(*lhs == *rhs)
}
}
12 changes: 7 additions & 5 deletions corelib/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,13 @@ mod ecdsa;
// Integer.
mod integer;
use integer::{
NumericLiteral, u128, u128_const, u128_sqrt, u128_is_zero, u8, u8_const, u16, u16_const, u32,
u32_const, u64, u64_const, u256, u256_sqrt, Felt252TryIntoU8, U8IntoFelt252, Felt252TryIntoU16,
U16IntoFelt252, Felt252TryIntoU32, U32IntoFelt252, Felt252TryIntoU64, U64IntoFelt252,
Felt252TryIntoU128, U128IntoFelt252, U16TryIntoU8, U32TryIntoU16, U64TryIntoU32, U128TryIntoU64,
Felt252IntoU256, Bitwise
i8, i8_const, I8IntoFelt252, i16, i16_const, I16IntoFelt252, i32, i32_const, I32IntoFelt252,
i64, i64_const, I64IntoFelt252, i128, i128_const, I128IntoFelt252, NumericLiteral, u128,
u128_const, u128_sqrt, u128_is_zero, u8, u8_const, u16, u16_const, u32, u32_const, u64,
u64_const, u256, u256_sqrt, Felt252TryIntoU8, U8IntoFelt252, Felt252TryIntoU16, U16IntoFelt252,
Felt252TryIntoU32, U32IntoFelt252, Felt252TryIntoU64, U64IntoFelt252, Felt252TryIntoU128,
U128IntoFelt252, U16TryIntoU8, U32TryIntoU16, U64TryIntoU32, U128TryIntoU64, Felt252IntoU256,
Bitwise
};

// Math.
Expand Down
34 changes: 34 additions & 0 deletions corelib/src/test/integer_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -1120,3 +1120,37 @@ fn test_u128_byte_reverse() {
'Wrong byte reverse'
);
}

#[test]
fn test_i8_operators() {
assert_eq(@1_i8, @1_i8, '1 == 1');
assert_ne(@1_i8, @2_i8, '1 != 2');
}


#[test]
fn test_i16_operators() {
assert_eq(@1_i16, @1_i16, '1 == 1');
assert_ne(@1_i16, @2_i16, '1 != 2');
}


#[test]
fn test_i32_operators() {
assert_eq(@1_i32, @1_i32, '1 == 1');
assert_ne(@1_i32, @2_i32, '1 != 2');
}


#[test]
fn test_i64_operators() {
assert_eq(@1_i64, @1_i64, '1 == 1');
assert_ne(@1_i64, @2_i64, '1 != 2');
}


#[test]
fn test_i128_operators() {
assert_eq(@1_i128, @1_i128, '1 == 1');
assert_ne(@1_i128, @2_i128, '1 != 2');
}
32 changes: 26 additions & 6 deletions crates/cairo-lang-semantic/src/corelib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
use cairo_lang_syntax::node::Terminal;
use cairo_lang_utils::{extract_matches, try_extract_matches, OptionFrom};
use num_bigint::BigInt;
use num_traits::{Num, Signed};
use num_traits::{Num, Signed, ToPrimitive};
use smol_str::SmolStr;

use crate::db::SemanticGroup;
Expand Down Expand Up @@ -639,6 +639,16 @@ pub fn get_const_libfunc_name_by_type(db: &dyn SemanticGroup, ty: TypeId) -> Str
"u64_const".into()
} else if ty == get_core_ty_by_name(db, "u128".into(), vec![]) {
"u128_const".into()
} else if ty == get_core_ty_by_name(db, "i8".into(), vec![]) {
"i8_const".into()
} else if ty == get_core_ty_by_name(db, "i16".into(), vec![]) {
"i16_const".into()
} else if ty == get_core_ty_by_name(db, "i32".into(), vec![]) {
"i32_const".into()
} else if ty == get_core_ty_by_name(db, "i64".into(), vec![]) {
"i64_const".into()
} else if ty == get_core_ty_by_name(db, "i128".into(), vec![]) {
"i128_const".into()
} else {
panic!("No const libfunc for type {}.", ty.format(db))
}
Expand All @@ -660,17 +670,27 @@ pub fn validate_literal(
)
.unwrap()
} else if ty == get_core_ty_by_name(db, "u8".into(), vec![]) {
value.is_negative() || value.bits() > 8
value.to_u8().is_none()
} else if ty == get_core_ty_by_name(db, "u16".into(), vec![]) {
value.is_negative() || value.bits() > 16
value.to_u16().is_none()
} else if ty == get_core_ty_by_name(db, "u32".into(), vec![]) {
value.is_negative() || value.bits() > 32
value.to_u32().is_none()
} else if ty == get_core_ty_by_name(db, "u64".into(), vec![]) {
value.is_negative() || value.bits() > 64
value.to_u64().is_none()
} else if ty == get_core_ty_by_name(db, "u128".into(), vec![]) {
value.is_negative() || value.bits() > 128
value.to_u128().is_none()
} else if ty == get_core_ty_by_name(db, "u256".into(), vec![]) {
value.is_negative() || value.bits() > 256
} else if ty == get_core_ty_by_name(db, "i8".into(), vec![]) {
value.to_i8().is_none()
} else if ty == get_core_ty_by_name(db, "i16".into(), vec![]) {
value.to_i16().is_none()
} else if ty == get_core_ty_by_name(db, "i32".into(), vec![]) {
value.to_i32().is_none()
} else if ty == get_core_ty_by_name(db, "i64".into(), vec![]) {
value.to_i64().is_none()
} else if ty == get_core_ty_by_name(db, "i128".into(), vec![]) {
value.to_i128().is_none()
} else {
return Err(SemanticDiagnosticKind::NoLiteralFunctionFound);
};
Expand Down
27 changes: 27 additions & 0 deletions crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use cairo_lang_sierra::extensions::felt252_dict::{
use cairo_lang_sierra::extensions::gas::{
BuiltinCostWithdrawGasLibfunc, CostTokenType, GasConcreteLibfunc,
};
use cairo_lang_sierra::extensions::int::signed::{SintConcrete, SintTraits};
use cairo_lang_sierra::extensions::int::signed128::Sint128Concrete;
use cairo_lang_sierra::extensions::int::unsigned::{UintConcrete, UintTraits};
use cairo_lang_sierra::extensions::int::unsigned128::Uint128Concrete;
use cairo_lang_sierra::extensions::int::unsigned256::Uint256Concrete;
Expand Down Expand Up @@ -172,6 +174,18 @@ pub fn core_libfunc_ap_change<InfoProvider: InvocationApChangeInfoProvider>(
CoreConcreteLibfunc::Uint512(libfunc) => match libfunc {
Uint512Concrete::DivModU256(_) => vec![ApChange::Known(47)],
},
CoreConcreteLibfunc::Sint8(libfunc) => sint_ap_change(libfunc),
CoreConcreteLibfunc::Sint16(libfunc) => sint_ap_change(libfunc),
CoreConcreteLibfunc::Sint32(libfunc) => sint_ap_change(libfunc),
CoreConcreteLibfunc::Sint64(libfunc) => sint_ap_change(libfunc),
CoreConcreteLibfunc::Sint128(libfunc) => match libfunc {
Sint128Concrete::Equal(_) => vec![ApChange::Known(1), ApChange::Known(1)],
Sint128Concrete::FromFelt252(_) => vec![ApChange::Known(1), ApChange::Known(6)],
Sint128Concrete::Const(_) | Sint128Concrete::ToFelt252(_) => {
vec![ApChange::Known(0)]
}
Sint128Concrete::IsZero(_) => vec![ApChange::Known(0), ApChange::Known(0)],
},
CoreConcreteLibfunc::Mem(libfunc) => match libfunc {
MemConcreteLibfunc::StoreTemp(libfunc) => {
vec![ApChange::Known(info_provider.type_size(&libfunc.ty))]
Expand Down Expand Up @@ -286,3 +300,16 @@ fn uint_ap_change<TUintTraits: UintTraits + IntMulTraits + IsZeroTraits>(
UintConcrete::Bitwise(_) => vec![ApChange::Known(0)],
}
}

/// Returns the ap changes for s8/s16/s32/s64 libfuncs.
fn sint_ap_change<TSintTraits: SintTraits + IntMulTraits + IsZeroTraits>(
libfunc: &SintConcrete<TSintTraits>,
) -> Vec<ApChange> {
match libfunc {
SintConcrete::Const(_) | SintConcrete::ToFelt252(_) => vec![ApChange::Known(0)],
SintConcrete::Equal(_) => vec![ApChange::Known(1), ApChange::Known(1)],
SintConcrete::FromFelt252(_) => vec![ApChange::Known(2), ApChange::Known(7)],
SintConcrete::IsZero(_) => vec![ApChange::Known(0), ApChange::Known(0)],
SintConcrete::WideMul(_) => vec![ApChange::Known(0)],
}
}
50 changes: 50 additions & 0 deletions crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use cairo_lang_sierra::extensions::gas::GasConcreteLibfunc::{
BuiltinWithdrawGas, GetAvailableGas, GetBuiltinCosts, RedepositGas, WithdrawGas,
};
use cairo_lang_sierra::extensions::gas::{BuiltinCostWithdrawGasLibfunc, CostTokenType};
use cairo_lang_sierra::extensions::int::signed::{SintConcrete, SintTraits};
use cairo_lang_sierra::extensions::int::signed128::Sint128Concrete;
use cairo_lang_sierra::extensions::int::unsigned::{UintConcrete, UintTraits};
use cairo_lang_sierra::extensions::int::unsigned128::Uint128Concrete;
use cairo_lang_sierra::extensions::int::unsigned256::Uint256Concrete;
Expand Down Expand Up @@ -251,6 +253,11 @@ pub fn core_libfunc_cost(
Uint128(libfunc) => u128_libfunc_cost(libfunc),
Uint256(libfunc) => u256_libfunc_cost(libfunc).into_iter().map(BranchCost::from).collect(),
Uint512(libfunc) => u512_libfunc_cost(libfunc).into_iter().map(BranchCost::from).collect(),
Sint8(libfunc) => sint_libfunc_cost(libfunc).into_iter().map(BranchCost::from).collect(),
Sint16(libfunc) => sint_libfunc_cost(libfunc).into_iter().map(BranchCost::from).collect(),
Sint32(libfunc) => sint_libfunc_cost(libfunc).into_iter().map(BranchCost::from).collect(),
Sint64(libfunc) => sint_libfunc_cost(libfunc).into_iter().map(BranchCost::from).collect(),
Sint128(libfunc) => s128_libfunc_cost(libfunc).into_iter().map(BranchCost::from).collect(),
Felt252(libfunc) => {
felt252_libfunc_cost(libfunc).into_iter().map(BranchCost::from).collect()
}
Expand Down Expand Up @@ -589,6 +596,49 @@ fn u512_libfunc_cost(libfunc: &Uint512Concrete) -> Vec<ConstCost> {
}
}

/// Returns costs for i64/i32/i16/i8 libfuncs.
fn sint_libfunc_cost<TSintTraits: SintTraits + IsZeroTraits + IntMulTraits>(
libfunc: &SintConcrete<TSintTraits>,
) -> Vec<BranchCost> {
match libfunc {
SintConcrete::Const(_) | SintConcrete::ToFelt252(_) | SintConcrete::WideMul(_) => {
vec![ConstCost::steps(0).into()]
}
SintConcrete::Equal(_) => {
vec![ConstCost::steps(2).into(), ConstCost::steps(3).into()]
}
SintConcrete::FromFelt252(_) => {
vec![
ConstCost { steps: 4, holes: 0, range_checks: 2 }.into(),
ConstCost { steps: 10, holes: 0, range_checks: 3 }.into(),
]
}
SintConcrete::IsZero(_) => vec![ConstCost::steps(1).into(), ConstCost::steps(1).into()],
}
}

/// Returns costs for i128 libfuncs.
fn s128_libfunc_cost(libfunc: &Sint128Concrete) -> Vec<BranchCost> {
let steps = |value| ConstCost { steps: value, ..Default::default() };
match libfunc {
Sint128Concrete::Const(_) | Sint128Concrete::ToFelt252(_) => {
vec![ConstCost::default().into()]
}
Sint128Concrete::FromFelt252(_) => {
vec![
ConstCost { steps: 2, holes: 0, range_checks: 1 }.into(),
ConstCost { steps: 11, holes: 0, range_checks: 3 }.into(),
]
}
Sint128Concrete::IsZero(_) => {
vec![steps(1).into(), steps(1).into()]
}
Sint128Concrete::Equal(_) => {
vec![steps(2).into(), steps(3).into()]
}
}
}

/// Returns costs for felt252 libfuncs.
fn felt252_libfunc_cost(libfunc: &Felt252Concrete) -> Vec<ConstCost> {
match libfunc {
Expand Down
Loading

0 comments on commit d80d29b

Please sign in to comment.