Skip to content

Commit

Permalink
Add upcast<FromType, ToType> libfunc. (#2118)
Browse files Browse the repository at this point in the history
  • Loading branch information
liorgold2 committed Feb 16, 2023
1 parent 75960ce commit f030973
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 3 deletions.
4 changes: 4 additions & 0 deletions corelib/integer.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -971,3 +971,7 @@ impl U128TryIntoU64 of TryInto::<u128, u64> {
as_felt.try_into()
}
}

// TODO(lior): Restrict the function (using traits) in the high-level compiler so that wrong types
// will not lead to Sierra errors.
extern fn upcast<FromType, ToType>(x: FromType) -> ToType nopanic;
1 change: 1 addition & 0 deletions corelib/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ mod integer;
use integer::u128;
use integer::u128_const;
use integer::u128_sqrt;
use integer::upcast;
use integer::U128Add;
use integer::U128Sub;
use integer::U128Mul;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use cairo_lang_sierra::extensions::boxing::BoxConcreteLibfunc;
use cairo_lang_sierra::extensions::builtin_cost::{
BuiltinCostConcreteLibfunc, BuiltinCostGetGasLibfunc, CostTokenType,
};
use cairo_lang_sierra::extensions::casts::CastConcreteLibfunc;
use cairo_lang_sierra::extensions::core::CoreConcreteLibfunc;
use cairo_lang_sierra::extensions::dict_felt_to::DictFeltToConcreteLibfunc;
use cairo_lang_sierra::extensions::ec::EcConcreteLibfunc;
Expand Down Expand Up @@ -78,6 +79,9 @@ pub fn core_libfunc_ap_change<InfoProvider: InvocationApChangeInfoProvider>(
}
BuiltinCostConcreteLibfunc::GetBuiltinCosts(_) => vec![ApChange::Known(3)],
},
CoreConcreteLibfunc::Cast(libfunc) => match libfunc {
CastConcreteLibfunc::Upcast(_) => vec![ApChange::Known(0)],
},
CoreConcreteLibfunc::Ec(libfunc) => match libfunc {
EcConcreteLibfunc::IsZero(_) => vec![ApChange::Known(0), ApChange::Known(0)],
EcConcreteLibfunc::Neg(_) => vec![ApChange::Known(0)],
Expand Down
8 changes: 6 additions & 2 deletions crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use cairo_lang_sierra::extensions::boxing::BoxConcreteLibfunc;
use cairo_lang_sierra::extensions::builtin_cost::{
BuiltinCostConcreteLibfunc, BuiltinCostGetGasLibfunc, CostTokenType,
};
use cairo_lang_sierra::extensions::casts::CastConcreteLibfunc;
use cairo_lang_sierra::extensions::core::CoreConcreteLibfunc::{
self, ApTracking, Array, Bitwise, Bool, Box, BranchAlign, BuiltinCost, DictFeltTo, Drop, Dup,
Ec, Enum, Felt, FunctionCall, Gas, Mem, Pedersen, Struct, Uint128, Uint16, Uint32, Uint64,
self, ApTracking, Array, Bitwise, Bool, Box, BranchAlign, BuiltinCost, Cast, DictFeltTo, Drop,
Dup, Ec, Enum, Felt, FunctionCall, Gas, Mem, Pedersen, Struct, Uint128, Uint16, Uint32, Uint64,
Uint8, UnconditionalJump, UnwrapNonZero,
};
use cairo_lang_sierra::extensions::dict_felt_to::DictFeltToConcreteLibfunc;
Expand Down Expand Up @@ -170,6 +171,9 @@ pub fn core_libfunc_postcost<Ops: CostOperations, InfoProvider: InvocationCostIn
Bool(BoolConcreteLibfunc::Xor(_)) => vec![ops.steps(1)],
Bool(BoolConcreteLibfunc::Or(_)) => vec![ops.steps(2)],
Bool(BoolConcreteLibfunc::Equal(_)) => vec![ops.steps(2), ops.steps(3)],
Cast(libfunc) => match libfunc {
CastConcreteLibfunc::Upcast(_) => vec![ops.steps(0)],
},
Ec(libfunc) => match libfunc {
EcConcreteLibfunc::IsZero(_) => vec![ops.steps(1), ops.steps(1)],
EcConcreteLibfunc::Neg(_) => vec![ops.steps(0)],
Expand Down
14 changes: 14 additions & 0 deletions crates/cairo-lang-sierra-to-casm/src/invocations/casts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use cairo_lang_sierra::extensions::casts::CastConcreteLibfunc;

use super::misc::build_identity;
use super::{CompiledInvocation, CompiledInvocationBuilder, InvocationError};

/// Builds instructions for Sierra cast operations.
pub fn build(
libfunc: &CastConcreteLibfunc,
builder: CompiledInvocationBuilder<'_>,
) -> Result<CompiledInvocation, InvocationError> {
match libfunc {
CastConcreteLibfunc::Upcast(_) => build_identity(builder),
}
}
2 changes: 2 additions & 0 deletions crates/cairo-lang-sierra-to-casm/src/invocations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mod bitwise;
mod boolean;
mod boxing;
mod builtin_cost;
mod casts;
mod debug;
mod dict_felt_to;
mod ec;
Expand Down Expand Up @@ -453,6 +454,7 @@ pub fn compile_invocation(
CoreConcreteLibfunc::Felt(libfunc) => felt::build(libfunc, builder),
CoreConcreteLibfunc::Bitwise(_) => bitwise::build(builder),
CoreConcreteLibfunc::Bool(libfunc) => boolean::build(libfunc, builder),
CoreConcreteLibfunc::Cast(libfunc) => casts::build(libfunc, builder),
CoreConcreteLibfunc::Ec(libfunc) => ec::build(libfunc, builder),
CoreConcreteLibfunc::Uint8(libfunc) => uint::build_u8(libfunc, builder),
CoreConcreteLibfunc::Uint16(libfunc) => uint::build_u16(libfunc, builder),
Expand Down
2 changes: 2 additions & 0 deletions crates/cairo-lang-sierra/src/extensions/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::bitwise::{BitwiseLibfunc, BitwiseType};
use super::boolean::BoolLibfunc;
use super::branch_align::BranchAlignLibfunc;
use super::builtin_cost::{BuiltinCostLibfunc, BuiltinCostsType};
use super::casts::CastLibfunc;
use super::debug::DebugLibfunc;
use super::dict_felt_to::{DictFeltToLibfunc, DictFeltToType};
use super::dict_manager::DictManagerType;
Expand Down Expand Up @@ -73,6 +74,7 @@ define_libfunc_hierarchy! {
Bool(BoolLibfunc),
Box(BoxLibfunc),
BuiltinCost(BuiltinCostLibfunc),
Cast(CastLibfunc),
Drop(DropLibfunc),
Dup(DupLibfunc),
Ec(EcLibfunc),
Expand Down
2 changes: 1 addition & 1 deletion crates/cairo-lang-sierra/src/extensions/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub enum SpecializationError {
UnsupportedId,
#[error("Expected a different number of generic arguments")]
WrongNumberOfGenericArgs,
#[error("Provided generic arg is unsupported")]
#[error("Provided generic argument is unsupported")]
UnsupportedGenericArg,
#[error("index is out of a relevant range")]
IndexOutOfRange {
Expand Down
11 changes: 11 additions & 0 deletions crates/cairo-lang-sierra/src/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,16 @@ fn args_as_single_type(args: &[GenericArg]) -> Result<ConcreteTypeId, Specializa
}
}

/// Helper for extracting two types from the template arguments.
fn args_as_two_types(
args: &[GenericArg],
) -> Result<(ConcreteTypeId, ConcreteTypeId), SpecializationError> {
match args {
[GenericArg::Type(ty0), GenericArg::Type(ty1)] => Ok((ty0.clone(), ty1.clone())),
[_, _] => Err(SpecializationError::UnsupportedGenericArg),
_ => Err(SpecializationError::WrongNumberOfGenericArgs),
}
}

#[cfg(test)]
mod test;
69 changes: 69 additions & 0 deletions crates/cairo-lang-sierra/src/extensions/modules/casts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;

use super::uint::Uint16Type;
use crate::define_libfunc_hierarchy;
use crate::extensions::lib_func::{
LibfuncSignature, OutputVarInfo, SierraApChange, SignatureOnlyGenericLibfunc,
SignatureSpecializationContext,
};
use crate::extensions::uint::{Uint32Type, Uint64Type, Uint8Type};
use crate::extensions::uint128::Uint128Type;
use crate::extensions::{
args_as_two_types, NamedType, OutputVarReferenceInfo, SpecializationError,
};
use crate::ids::ConcreteTypeId;
use crate::program::GenericArg;

define_libfunc_hierarchy! {
pub enum CastLibfunc {
Upcast(UpcastLibfunc),
}, CastConcreteLibfunc
}

/// Libfunc for casting from one type to another where any input value can fit into the destination
/// type. For example, from u8 to u64.
#[derive(Default)]
pub struct UpcastLibfunc {}
impl SignatureOnlyGenericLibfunc for UpcastLibfunc {
const STR_ID: &'static str = "upcast";

fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
args: &[GenericArg],
) -> Result<LibfuncSignature, SpecializationError> {
let (from_ty, to_ty) = args_as_two_types(args)?;

let type_to_n_bits: UnorderedHashMap<ConcreteTypeId, usize> = vec![
(Uint8Type::ID, 8),
(Uint16Type::ID, 16),
(Uint32Type::ID, 32),
(Uint64Type::ID, 64),
(Uint128Type::ID, 128),
]
.into_iter()
.filter_map(|(generic_type, n_bits)| {
Some((context.get_concrete_type(generic_type, &[]).ok()?, n_bits))
})
.collect();

let from_nbits =
type_to_n_bits.get(&from_ty).ok_or(SpecializationError::UnsupportedGenericArg)?;
let to_nbits =
type_to_n_bits.get(&to_ty).ok_or(SpecializationError::UnsupportedGenericArg)?;

let is_valid = from_nbits <= to_nbits;
if !is_valid {
return Err(SpecializationError::UnsupportedGenericArg);
}

Ok(LibfuncSignature::new_non_branch(
vec![from_ty.clone()],
vec![OutputVarInfo {
ty: to_ty,
ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 },
}],
SierraApChange::Known { new_vars_only: true },
))
}
}
1 change: 1 addition & 0 deletions crates/cairo-lang-sierra/src/extensions/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod boolean;
pub mod boxing;
pub mod branch_align;
pub mod builtin_cost;
pub mod casts;
pub mod consts;
pub mod debug;
pub mod dict_felt_to;
Expand Down
1 change: 1 addition & 0 deletions crates/cairo-lang-sierra/src/simulation/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ pub fn simulate<
[value] => Ok((vec![value.clone(), value.clone()], 0)),
_ => Err(LibfuncSimulationError::WrongNumberOfArgs),
},
CoreConcreteLibfunc::Cast(_) => unimplemented!(),
}
}

Expand Down
1 change: 1 addition & 0 deletions tests/e2e_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ cairo_lang_test_utils::test_file_test!(
bitwise: "bitwise",
box_: "box",
builtin_costs: "builtin_costs",
casts: "casts",
dict_felt_to: "dict_felt_to",
ec: "ec",
get_gas_all: "get_gas_all",
Expand Down
29 changes: 29 additions & 0 deletions tests/e2e_test_data/libfuncs/casts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! > u16 to u64 upcast

//! > test_function_name
run_small_e2e_test

//! > cairo
fn foo(a: u16) -> u64 {
upcast(a)
}

//! > casm
[ap + 0] = [fp + -3], ap++;
ret;

//! > function_costs
test::foo: OrderedHashMap({Const: 100})

//! > sierra_code
type u16 = u16;
type u64 = u64;

libfunc upcast<u16, u64> = upcast<u16, u64>;
libfunc store_temp<u64> = store_temp<u64>;

upcast<u16, u64>([0]) -> ([1]);
store_temp<u64>([1]) -> ([2]);
return([2]);

test::foo@0([0]: u16) -> (u64);

0 comments on commit f030973

Please sign in to comment.