diff --git a/src/isa/exec.rs b/src/isa/exec.rs index 0795b90..42b6f56 100644 --- a/src/isa/exec.rs +++ b/src/isa/exec.rs @@ -99,8 +99,7 @@ pub trait InstructionSet: Bytecode + core::fmt::Display + core::fmt::Debug { fn dst_regs(&self) -> BTreeSet; /// Returns computational complexity of the instruction - #[inline] - fn complexity(&self) -> u64 { 1 } + fn complexity(&self) -> u64; /// Executes given instruction taking all registers as input and output. /// @@ -172,6 +171,26 @@ where } } + fn complexity(&self) -> u64 { + match self { + Instr::ControlFlow(instr) => instr.complexity(), + Instr::Put(instr) => instr.complexity(), + Instr::Move(instr) => instr.complexity(), + Instr::Cmp(instr) => instr.complexity(), + Instr::Arithmetic(instr) => instr.complexity(), + Instr::Bitwise(instr) => instr.complexity(), + Instr::Bytes(instr) => instr.complexity(), + Instr::Digest(instr) => instr.complexity(), + #[cfg(feature = "secp256k1")] + Instr::Secp256k1(instr) => instr.complexity(), + #[cfg(feature = "curve25519")] + Instr::Curve25519(instr) => instr.complexity(), + Instr::ExtensionCodes(instr) => instr.complexity(), + Instr::ReservedInstruction(instr) => instr.complexity(), + Instr::Nop => 1, + } + } + #[inline] fn exec(&self, regs: &mut CoreRegs, site: LibSite, ctx: &Self::Context<'_>) -> ExecStep { match self { @@ -412,6 +431,8 @@ impl InstructionSet for MoveOp { } } + fn complexity(&self) -> u64 { 1 } + fn exec(&self, regs: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { match self { MoveOp::MovA(reg, idx1, idx2) => { @@ -546,6 +567,8 @@ impl InstructionSet for CmpOp { } } + fn complexity(&self) -> u64 { 1 } + fn exec(&self, regs: &mut CoreRegs, _: LibSite, _: &()) -> ExecStep { match self { CmpOp::GtA(sign_flag, reg, idx1, idx2) => { @@ -887,6 +910,8 @@ impl InstructionSet for BitwiseOp { } } + fn complexity(&self) -> u64 { 1 } + fn exec(&self, regs: &mut CoreRegs, _site: LibSite, _: &()) -> ExecStep { fn shl(original: &[u8], shift: usize, n_bytes: usize) -> [u8; 1024] { let mut ret = [0u8; 1024]; @@ -1639,6 +1664,8 @@ impl InstructionSet for ReservedOp { fn dst_regs(&self) -> BTreeSet { bset![] } + fn complexity(&self) -> u64 { u64::MAX } + fn exec(&self, regs: &mut CoreRegs, site: LibSite, ctx: &()) -> ExecStep { ControlFlowOp::Fail.exec(regs, site, ctx) } diff --git a/src/isa/macros.rs b/src/isa/macros.rs index 05d4b0c..d157a73 100644 --- a/src/isa/macros.rs +++ b/src/isa/macros.rs @@ -27,8 +27,8 @@ /// /// ``` /// # use aluvm::aluasm; -/// # use aluvm::{Prog, Vm}; -/// # use aluvm::library::Lib; +/// # use aluvm::Vm; +/// # use aluvm::library::{Lib, LibSite}; /// # use aluvm::isa::Instr; /// /// let code = aluasm! { @@ -46,9 +46,8 @@ /// }; /// /// let lib = Lib::assemble(&code).unwrap(); -/// let program = Prog::::new(lib); /// let mut vm = Vm::::new(); -/// match vm.run(&program, &()) { +/// match vm.exec(LibSite::default(), |_| Some(&lib), &()) { /// true => println!("success"), /// false => println!("failure"), /// } diff --git a/src/lib.rs b/src/lib.rs index d469711..8d16cb5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,7 +156,6 @@ pub mod data; #[macro_use] pub mod isa; pub mod library; -mod program; pub mod reg; #[cfg(feature = "stl")] pub mod stl; @@ -167,7 +166,6 @@ pub use isa::Isa; pub use library::LibArmorError; #[doc(hidden)] pub use paste::paste; -pub use program::{Prog, ProgError, Program}; pub use vm::Vm; /// Struct types library name. diff --git a/src/program.rs b/src/program.rs deleted file mode 100644 index 742a867..0000000 --- a/src/program.rs +++ /dev/null @@ -1,177 +0,0 @@ -// Reference rust implementation of AluVM (arithmetic logic unit virtual machine). -// To find more on AluVM please check -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2021-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2021-2022 LNP/BP Standards Association. All rights reserved. -// Copyright (C) 2023-2024 UBIDECO Institute. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[cfg(all(feature = "alloc", not(feature = "std")))] -use alloc::borrow::ToOwned; -use alloc::collections::{btree_map, BTreeMap}; -#[cfg(all(feature = "alloc", not(feature = "std")))] -use alloc::string::String; -use core::marker::PhantomData; - -use crate::isa::InstructionSet; -use crate::library::constants::LIBS_MAX_TOTAL; -use crate::library::{IsaName, Lib, LibId, LibSite}; - -/// Trait for a concrete program implementation provided by a runtime environment. -pub trait Program { - /// Instruction set architecture used by the program. - type Isa: InstructionSet; - - /// Iterator type over libraries - type Iter<'a>: Iterator - where - Self: 'a; - - /// Returns number of libraries used by the program. - fn lib_count(&self) -> u16; - - /// Returns an iterator over libraries used by the program. - fn libs(&self) -> Self::Iter<'_>; - - /// Returns library corresponding to the provided [`LibId`], if the library is known to the - /// program. - fn lib(&self, id: LibId) -> Option<&Lib>; - - /// Main entry point into the program. - fn entrypoint(&self) -> LibSite; -} - -/// Errors returned by [`Prog::add_lib`] method -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[cfg_attr(feature = "std", derive(Error))] -#[display(doc_comments)] -pub enum ProgError { - /// ISA id {0} is not supported by the selected instruction set - IsaNotSupported(IsaName), - - /// Attempt to add library when maximum possible number of libraries is already present in - /// the VM - TooManyLibs, -} - -/// The most trivial form of a program which is just a collection of libraries with some entry -/// point. -/// -/// # Generics -/// -/// `RUNTIME_MAX_TOTAL_LIBS`: Maximum total number of libraries supported by a runtime, if it is -/// less than [`LIBS_MAX_TOTAL`]. If the value set is greater than [`LIBS_MAX_TOTAL`] the -/// value is ignored and [`LIBS_MAX_TOTAL`] constant is used instead. -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] -// #[cfg_attr(feature = "strict_encoding", derive(StrictEncode, StrictDecode))] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -// We need to hardcode generic as a literal, otherwise serde > 1.0.152 fails compilation -pub struct Prog -where - Isa: InstructionSet, -{ - /// Libraries known to the runtime, identified by their hashes. - libs: BTreeMap, - - /// Entrypoint for the main function. - entrypoint: LibSite, - - // #[cfg_attr(feature = "strict_encoding", strict_encoding(skip))] - #[cfg_attr(feature = "serde", serde(skip))] - phantom: PhantomData, -} - -impl Prog -where - Isa: InstructionSet, -{ - const RUNTIME_MAX_TOTAL_LIBS: u16 = RUNTIME_MAX_TOTAL_LIBS; - - fn empty_unchecked() -> Self { - Prog { libs: BTreeMap::new(), entrypoint: LibSite::with(0, zero!()), phantom: default!() } - } - - /// Constructs new virtual machine runtime using provided single library. Entry point is set - /// to zero offset by default. - pub fn new(lib: Lib) -> Self { - let mut runtime = Self::empty_unchecked(); - let id = lib.id(); - runtime.add_lib(lib).expect("adding single library to lib segment overflows"); - runtime.set_entrypoint(LibSite::with(0, id)); - runtime - } - - /// Constructs new virtual machine runtime from a set of libraries with a given entry point. - pub fn with( - libs: impl IntoIterator, - entrypoint: LibSite, - ) -> Result { - let mut runtime = Self::empty_unchecked(); - for lib in libs { - runtime.add_lib(lib)?; - } - runtime.set_entrypoint(entrypoint); - Ok(runtime) - } - - /// Adds Alu bytecode library to the virtual machine runtime. - /// - /// # Errors - /// - /// Checks requirement that the total number of libraries must not exceed [`LIBS_MAX_TOTAL`] - /// and `RUNTIME_MAX_TOTAL_LIBS` - or returns [`ProgError::TooManyLibs`] otherwise. - /// - /// Checks that the ISA used by the VM supports ISA extensions specified by the library and - /// returns [`ProgError::IsaNotSupported`] otherwise. - /// - /// # Returns - /// - /// `true` if the library was already known and `false` otherwise. - #[inline] - pub fn add_lib(&mut self, lib: Lib) -> Result { - if self.lib_count() >= LIBS_MAX_TOTAL.min(Self::RUNTIME_MAX_TOTAL_LIBS) { - return Err(ProgError::TooManyLibs); - } - for isa in lib.isae.iter() { - if !Isa::is_supported(isa) { - return Err(ProgError::IsaNotSupported(isa.to_owned())); - } - } - Ok(self.libs.insert(lib.id(), lib).is_none()) - } - - // TODO: Return error if the library is not known - /// Sets new entry point value (used when calling [`crate::Vm::run`]) - pub fn set_entrypoint(&mut self, entrypoint: LibSite) { self.entrypoint = entrypoint; } -} - -impl Program for Prog -where - Isa: InstructionSet, -{ - type Isa = Isa; - type Iter<'a> = btree_map::Values<'a, LibId, Lib> where Self: 'a; - - fn lib_count(&self) -> u16 { self.libs.len() as u16 } - - fn libs(&self) -> Self::Iter<'_> { self.libs.values() } - - fn lib(&self, id: LibId) -> Option<&Lib> { self.libs.get(&id) } - - fn entrypoint(&self) -> LibSite { self.entrypoint } -} diff --git a/src/vm.rs b/src/vm.rs index d299fe8..240e982 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -28,9 +28,8 @@ use alloc::boxed::Box; use core::marker::PhantomData; use crate::isa::{Instr, InstructionSet, ReservedOp}; -use crate::library::LibSite; +use crate::library::{Lib, LibId, LibSite}; use crate::reg::CoreRegs; -use crate::Program; /// Alu virtual machine providing single-core execution environment #[derive(Debug, Default)] @@ -57,24 +56,15 @@ where /// # Returns /// /// Value of the `st0` register at the end of the program execution. - pub fn run(&mut self, program: &impl Program, context: &Isa::Context<'_>) -> bool { - self.call(program, program.entrypoint(), context) - } - - /// Executes the program starting from the provided entry point. - /// - /// # Returns - /// - /// Value of the `st0` register at the end of the program execution. - pub fn call( + pub fn exec<'prog>( &mut self, - program: &impl Program, - method: LibSite, + entry_point: LibSite, + lib_resolver: impl Fn(LibId) -> Option<&'prog Lib>, context: &Isa::Context<'_>, ) -> bool { - let mut call = Some(method); + let mut call = Some(entry_point); while let Some(ref mut site) = call { - if let Some(lib) = program.lib(site.lib) { + if let Some(lib) = lib_resolver(site.lib) { call = lib.exec::(site.pos, &mut self.registers, context); } else if let Some(pos) = site.pos.checked_add(1) { site.pos = pos;