From 35dc45760c4691613d704f822adba3aab6727c9d Mon Sep 17 00:00:00 2001 From: martoon Date: Fri, 8 Dec 2023 04:33:02 +0400 Subject: [PATCH] MIR: Add `SIZE` instruction --- contrib/mir/src/ast.rs | 1 + contrib/mir/src/ast/overloads.rs | 9 ++++ contrib/mir/src/gas.rs | 5 ++ contrib/mir/src/interpreter.rs | 86 ++++++++++++++++++++++++++++++++ contrib/mir/src/typechecker.rs | 59 ++++++++++++++++++++++ 5 files changed, 160 insertions(+) diff --git a/contrib/mir/src/ast.rs b/contrib/mir/src/ast.rs index 2f871bd61be4..39d7b1e59414 100644 --- a/contrib/mir/src/ast.rs +++ b/contrib/mir/src/ast.rs @@ -401,6 +401,7 @@ pub enum Instruction<'a> { Get(overloads::Get), Update(overloads::Update), Concat(overloads::Concat), + Size(overloads::Size), Seq(Vec), Unpair, Cons, diff --git a/contrib/mir/src/ast/overloads.rs b/contrib/mir/src/ast/overloads.rs index 2725ad3e3678..274cd883a9d1 100644 --- a/contrib/mir/src/ast/overloads.rs +++ b/contrib/mir/src/ast/overloads.rs @@ -61,6 +61,15 @@ pub enum Update { Map, } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Size { + String, + Bytes, + List, + Set, + Map, +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Iter { List, diff --git a/contrib/mir/src/gas.rs b/contrib/mir/src/gas.rs index 7933947eab15..dbfe6c4b4cdd 100644 --- a/contrib/mir/src/gas.rs +++ b/contrib/mir/src/gas.rs @@ -234,6 +234,11 @@ pub mod interpret_cost { pub const NIL: u32 = 10; pub const CONS: u32 = 15; pub const EMPTY_SET: u32 = 300; + pub const SIZE_STRING: u32 = 15; + pub const SIZE_BYTES: u32 = 10; + pub const SIZE_LIST: u32 = 10; + pub const SIZE_SET: u32 = 10; + pub const SIZE_MAP: u32 = 10; pub const CHAIN_ID: u32 = 15; pub const PACK: u32 = 0; pub const SELF: u32 = 10; diff --git a/contrib/mir/src/interpreter.rs b/contrib/mir/src/interpreter.rs index 47e62acedf05..284f8b4feb23 100644 --- a/contrib/mir/src/interpreter.rs +++ b/contrib/mir/src/interpreter.rs @@ -625,6 +625,23 @@ fn interpret_one<'a>( }; } }, + I::Size(overload) => { + macro_rules! run_size { + ($ctor:tt, $gas:ident) => {{ + let e = pop!(V::$ctor); + ctx.gas.consume(interpret_cost::$gas)?; + let res = e.len(); + stack.push(V::Nat(res.into())); + }}; + } + match overload { + overloads::Size::String => run_size!(String, SIZE_STRING), + overloads::Size::Bytes => run_size!(Bytes, SIZE_BYTES), + overloads::Size::List => run_size!(List, SIZE_LIST), + overloads::Size::Set => run_size!(Set, SIZE_SET), + overloads::Size::Map => run_size!(Map, SIZE_MAP), + } + } I::ChainId => { ctx.gas.consume(interpret_cost::CHAIN_ID)?; stack.push(V::ChainId(ctx.chain_id.clone())); @@ -2217,6 +2234,75 @@ mod interpreter_tests { ); } + #[test] + fn size_string() { + let mut ctx = Ctx::default(); + let mut stack = stk![TypedValue::String("abc".into())]; + assert_eq!( + interpret(&vec![Size(overloads::Size::String)], &mut ctx, &mut stack), + Ok(()) + ); + assert_eq!(stack, stk![TypedValue::nat(3)]); + assert_eq!( + ctx.gas.milligas(), + Gas::default().milligas() - interpret_cost::SIZE_STRING - interpret_cost::INTERPRET_RET + ); + } + + #[test] + fn size_bytes() { + let mut ctx = Ctx::default(); + let mut stack = stk![TypedValue::Bytes(b"abc".to_vec())]; + assert_eq!( + interpret(&vec![Size(overloads::Size::Bytes)], &mut ctx, &mut stack), + Ok(()) + ); + assert_eq!(stack, stk![TypedValue::nat(3)]); + } + + #[test] + fn size_list() { + let mut ctx = Ctx::default(); + let list = (1..=3).map(TypedValue::nat).collect(); + let mut stack = stk![TypedValue::List(list)]; + assert_eq!( + interpret(&vec![Size(overloads::Size::List)], &mut ctx, &mut stack), + Ok(()) + ); + assert_eq!(stack, stk![TypedValue::nat(3)]); + assert_eq!( + ctx.gas.milligas(), + Gas::default().milligas() - interpret_cost::SIZE_LIST - interpret_cost::INTERPRET_RET + ); + } + + #[test] + fn size_set() { + let mut ctx = Ctx::default(); + let set = (1..=3).map(TypedValue::nat).collect(); + let mut stack = stk![TypedValue::Set(set)]; + assert_eq!( + interpret(&vec![Size(overloads::Size::Set)], &mut ctx, &mut stack), + Ok(()) + ); + assert_eq!(stack, stk![TypedValue::nat(3)]); + } + + #[test] + fn size_map() { + let mut ctx = Ctx::default(); + let map = BTreeMap::from([ + (TypedValue::nat(1), TypedValue::nat(1)), + (TypedValue::nat(2), TypedValue::nat(2)), + ]); + let mut stack = stk![TypedValue::Map(map)]; + assert_eq!( + interpret(&vec![Size(overloads::Size::Map)], &mut ctx, &mut stack), + Ok(()) + ); + assert_eq!(stack, stk![TypedValue::nat(2)]); + } + #[test] fn seq() { let mut stack = stk![V::int(1), V::nat(2)]; diff --git a/contrib/mir/src/typechecker.rs b/contrib/mir/src/typechecker.rs index 4398e5fb61a1..1f315ec7e6c7 100644 --- a/contrib/mir/src/typechecker.rs +++ b/contrib/mir/src/typechecker.rs @@ -1119,6 +1119,30 @@ pub(crate) fn typecheck_instruction<'a>( (App(UPDATE, [], _), [] | [_] | [_, _]) => no_overload!(UPDATE, len 3), (App(UPDATE, expect_args!(0), _), _) => unexpected_micheline!(), + (App(SIZE, [], _), [.., T::String]) => { + stack[0] = T::Nat; + I::Size(overloads::Size::String) + } + (App(SIZE, [], _), [.., T::Bytes]) => { + stack[0] = T::Nat; + I::Size(overloads::Size::Bytes) + } + (App(SIZE, [], _), [.., T::List(_)]) => { + stack[0] = T::Nat; + I::Size(overloads::Size::List) + } + (App(SIZE, [], _), [.., T::Set(_)]) => { + stack[0] = T::Nat; + I::Size(overloads::Size::Set) + } + (App(SIZE, [], _), [.., T::Map(..)]) => { + stack[0] = T::Nat; + I::Size(overloads::Size::Map) + } + (App(SIZE, [], _), [.., _]) => no_overload!(SIZE), + (App(SIZE, [], _), []) => no_overload!(SIZE, len 1), + (App(SIZE, expect_args!(0), _), _) => unexpected_micheline!(), + (App(CHAIN_ID, [], _), ..) => { stack.push(T::ChainId); I::ChainId @@ -3255,6 +3279,41 @@ mod typecheck_tests { ); } + #[test] + fn size() { + fn check(inp_ty: Type, expected_overload: overloads::Size) { + let mut stack = tc_stk![inp_ty]; + assert_eq!( + typecheck_instruction(&parse("SIZE").unwrap(), &mut Ctx::default(), &mut stack), + Ok(Size(expected_overload)) + ); + assert_eq!(stack, tc_stk![Type::Nat]); + } + check(Type::String, overloads::Size::String); + check(Type::Bytes, overloads::Size::Bytes); + check(Type::new_list(Type::Int), overloads::Size::List); + check(Type::new_set(Type::Int), overloads::Size::Set); + check(Type::new_map(Type::Int, Type::Nat), overloads::Size::Map); + } + + #[test] + fn size_wrong_ty() { + let mut stack = tc_stk![Type::Int]; + assert_eq!( + typecheck_instruction(&parse("SIZE").unwrap(), &mut Ctx::default(), &mut stack), + Err(TcError::NoMatchingOverload { + instr: Prim::SIZE, + stack: stk![Type::Int], + reason: None + }) + ); + } + + #[test] + fn test_size_short() { + too_short_test(&app!(SIZE), Prim::SIZE, 1); + } + #[test] fn seq() { let mut stack = tc_stk![Type::Int, Type::Nat]; -- GitLab