diff --git a/book/src/field_functions.md b/book/src/field_functions.md index f58390650..301f34b68 100644 --- a/book/src/field_functions.md +++ b/book/src/field_functions.md @@ -67,7 +67,7 @@ impl External { } let mut module = Module::new(); -module.field_function(Protocol::GET, "field", External::field_get)?; +module.field_function(&Protocol::GET, "field", External::field_get)?; ``` Would allow for this in Rune: diff --git a/crates/rune-macros/src/context.rs b/crates/rune-macros/src/context.rs index 35522843f..973b1aa3f 100644 --- a/crates/rune-macros/src/context.rs +++ b/crates/rune-macros/src/context.rs @@ -272,7 +272,7 @@ impl Context { .. } = g; - let protocol_field = g.tokens.protocol(Protocol::$proto); + let protocol_field = g.tokens.protocol(&Protocol::$proto); match target { GenerateTarget::Named { field_ident, field_name } => { @@ -391,7 +391,7 @@ impl Context { quote!(#vm_try!(#try_clone::try_clone(&s.#field_ident))) }; - let protocol = g.tokens.protocol(Protocol::GET); + let protocol = g.tokens.protocol(&Protocol::GET); quote_spanned! { g.field.span() => module.field_function(&#protocol, #field_name, |s: &Self| #vm_result::Ok(#access))?; @@ -404,7 +404,7 @@ impl Context { quote!(#vm_try!(#try_clone::try_clone(&s.#field_index))) }; - let protocol = g.tokens.protocol(Protocol::GET); + let protocol = g.tokens.protocol(&Protocol::GET); quote_spanned! { g.field.span() => module.index_function(&#protocol, #field_index, |s: &Self| #vm_result::Ok(#access))?; @@ -427,7 +427,7 @@ impl Context { .. } = g; - let protocol = g.tokens.protocol(Protocol::SET); + let protocol = g.tokens.protocol(&Protocol::SET); match target { GenerateTarget::Named { field_ident, field_name } => { @@ -939,7 +939,7 @@ pub(crate) struct Tokens { impl Tokens { /// Define a tokenstream for the specified protocol - pub(crate) fn protocol(&self, sym: Protocol) -> TokenStream { + pub(crate) fn protocol(&self, sym: &Protocol) -> TokenStream { let mut stream = TokenStream::default(); self.protocol.to_tokens(&mut stream); ::default().to_tokens(&mut stream); diff --git a/crates/rune/src/compile/v1/assemble.rs b/crates/rune/src/compile/v1/assemble.rs index 8fedbeb56..6fcfc9897 100644 --- a/crates/rune/src/compile/v1/assemble.rs +++ b/crates/rune/src/compile/v1/assemble.rs @@ -1480,52 +1480,153 @@ fn expr_binary<'a, 'hir>( return compile_conditional_binop(cx, &hir.lhs, &hir.rhs, &hir.op, span, needs); } - let op = match hir.op { - ast::BinOp::Eq(..) => InstOp::Eq, - ast::BinOp::Neq(..) => InstOp::Neq, - ast::BinOp::Lt(..) => InstOp::Lt, - ast::BinOp::Gt(..) => InstOp::Gt, - ast::BinOp::Lte(..) => InstOp::Le, - ast::BinOp::Gte(..) => InstOp::Ge, - ast::BinOp::As(..) => InstOp::As, - ast::BinOp::Is(..) => InstOp::Is, - ast::BinOp::IsNot(..) => InstOp::IsNot, - ast::BinOp::And(..) => InstOp::And, - ast::BinOp::Or(..) => InstOp::Or, - ast::BinOp::Add(..) => InstOp::Add, - ast::BinOp::Sub(..) => InstOp::Sub, - ast::BinOp::Div(..) => InstOp::Div, - ast::BinOp::Mul(..) => InstOp::Mul, - ast::BinOp::Rem(..) => InstOp::Rem, - ast::BinOp::BitAnd(..) => InstOp::BitAnd, - ast::BinOp::BitXor(..) => InstOp::BitXor, - ast::BinOp::BitOr(..) => InstOp::BitOr, - ast::BinOp::Shl(..) => InstOp::Shl, - ast::BinOp::Shr(..) => InstOp::Shr, - - op => { - return Err(compile::Error::new( - span, - ErrorKind::UnsupportedBinaryOp { op }, - )); - } - }; - let mut a = cx.scopes.defer(span); let mut b = cx.scopes.defer(span); let asm = expr_array(cx, span, [(&hir.lhs, &mut a), (&hir.rhs, &mut b)])?; if let Some([a, b]) = asm.into_converging() { - cx.asm.push( - Inst::Op { - op, - a: a.addr(), - b: b.addr(), - out: needs.alloc_output()?, + let a = a.addr(); + let b = b.addr(); + let out = needs.alloc_output()?; + + let inst = match hir.op { + ast::BinOp::Eq(..) => Inst::Op { + op: InstOp::Eq, + a, + b, + out, }, - span, - )?; + ast::BinOp::Neq(..) => Inst::Op { + op: InstOp::Neq, + a, + b, + out, + }, + ast::BinOp::Lt(..) => Inst::Op { + op: InstOp::Lt, + a, + b, + out, + }, + ast::BinOp::Gt(..) => Inst::Op { + op: InstOp::Gt, + a, + b, + out, + }, + ast::BinOp::Lte(..) => Inst::Op { + op: InstOp::Le, + a, + b, + out, + }, + ast::BinOp::Gte(..) => Inst::Op { + op: InstOp::Ge, + a, + b, + out, + }, + ast::BinOp::As(..) => Inst::Op { + op: InstOp::As, + a, + b, + out, + }, + ast::BinOp::Is(..) => Inst::Op { + op: InstOp::Is, + a, + b, + out, + }, + ast::BinOp::IsNot(..) => Inst::Op { + op: InstOp::IsNot, + a, + b, + out, + }, + ast::BinOp::And(..) => Inst::Op { + op: InstOp::And, + a, + b, + out, + }, + ast::BinOp::Or(..) => Inst::Op { + op: InstOp::Or, + a, + b, + out, + }, + ast::BinOp::Add(..) => Inst::Arithmetic { + op: InstArithmeticOp::Add, + a, + b, + out, + }, + ast::BinOp::Sub(..) => Inst::Arithmetic { + op: InstArithmeticOp::Sub, + a, + b, + out, + }, + ast::BinOp::Div(..) => Inst::Arithmetic { + op: InstArithmeticOp::Div, + a, + b, + out, + }, + ast::BinOp::Mul(..) => Inst::Arithmetic { + op: InstArithmeticOp::Mul, + a, + b, + out, + }, + ast::BinOp::Rem(..) => Inst::Arithmetic { + op: InstArithmeticOp::Rem, + a, + b, + out, + }, + ast::BinOp::BitAnd(..) => Inst::Bitwise { + op: InstBitwiseOp::BitAnd, + a, + b, + out, + }, + ast::BinOp::BitXor(..) => Inst::Bitwise { + op: InstBitwiseOp::BitXor, + a, + b, + out, + }, + ast::BinOp::BitOr(..) => Inst::Bitwise { + op: InstBitwiseOp::BitOr, + a, + b, + out, + }, + ast::BinOp::Shl(..) => Inst::Shift { + op: InstShiftOp::Shl, + a, + b, + out, + }, + ast::BinOp::Shr(..) => Inst::Shift { + op: InstShiftOp::Shr, + a, + b, + out, + }, + + op => { + return Err(compile::Error::new( + span, + ErrorKind::UnsupportedBinaryOp { op }, + )); + } + }; + + cx.asm.push(inst, span)?; } a.free()?; @@ -1623,52 +1724,52 @@ fn compile_assign_binop<'a, 'hir>( ast::BinOp::AddAssign(..) => Inst::AssignArithmetic { op: InstArithmeticOp::Add, target, - value: value.addr(), + rhs: value.addr(), }, ast::BinOp::SubAssign(..) => Inst::AssignArithmetic { op: InstArithmeticOp::Sub, target, - value: value.addr(), + rhs: value.addr(), }, ast::BinOp::MulAssign(..) => Inst::AssignArithmetic { op: InstArithmeticOp::Mul, target, - value: value.addr(), + rhs: value.addr(), }, ast::BinOp::DivAssign(..) => Inst::AssignArithmetic { op: InstArithmeticOp::Div, target, - value: value.addr(), + rhs: value.addr(), }, ast::BinOp::RemAssign(..) => Inst::AssignArithmetic { op: InstArithmeticOp::Rem, target, - value: value.addr(), + rhs: value.addr(), }, ast::BinOp::BitAndAssign(..) => Inst::AssignBitwise { op: InstBitwiseOp::BitAnd, target, - value: value.addr(), + rhs: value.addr(), }, ast::BinOp::BitXorAssign(..) => Inst::AssignBitwise { op: InstBitwiseOp::BitXor, target, - value: value.addr(), + rhs: value.addr(), }, ast::BinOp::BitOrAssign(..) => Inst::AssignBitwise { op: InstBitwiseOp::BitOr, target, - value: value.addr(), + rhs: value.addr(), }, ast::BinOp::ShlAssign(..) => Inst::AssignShift { op: InstShiftOp::Shl, target, - value: value.addr(), + rhs: value.addr(), }, ast::BinOp::ShrAssign(..) => Inst::AssignShift { op: InstShiftOp::Shr, target, - value: value.addr(), + rhs: value.addr(), }, _ => { return Err(compile::Error::new(span, ErrorKind::UnsupportedBinaryExpr)); diff --git a/crates/rune/src/runtime/inst.rs b/crates/rune/src/runtime/inst.rs index c25faf89c..46b017a33 100644 --- a/crates/rune/src/runtime/inst.rs +++ b/crates/rune/src/runtime/inst.rs @@ -989,14 +989,7 @@ pub enum Inst { /// Where to store the variant. out: Output, }, - /// A built-in operation like `a + b` that takes its operands and pushes its - /// result to and from the stack. - /// - /// # Operation - /// - /// ```text - /// => - /// ``` + /// An operation. #[musli(packed)] Op { /// The kind of operation. @@ -1008,6 +1001,42 @@ pub enum Inst { /// Whether the produced value from the operation should be kept or not. out: Output, }, + /// An arithmetic operation. + #[musli(packed)] + Arithmetic { + /// The kind of operation. + op: InstArithmeticOp, + /// The address of the first argument. + a: InstAddress, + /// The address of the second argument. + b: InstAddress, + /// Whether the produced value from the operation should be kept or not. + out: Output, + }, + /// A bitwise operation. + #[musli(packed)] + Bitwise { + /// The kind of operation. + op: InstBitwiseOp, + /// The address of the first argument. + a: InstAddress, + /// The address of the second argument. + b: InstAddress, + /// Whether the produced value from the operation should be kept or not. + out: Output, + }, + /// A shift operation. + #[musli(packed)] + Shift { + /// The kind of operation. + op: InstShiftOp, + /// The address of the first argument. + a: InstAddress, + /// The address of the second argument. + b: InstAddress, + /// Whether the produced value from the operation should be kept or not. + out: Output, + }, /// Instruction for assigned arithmetic operations. #[musli(packed)] AssignArithmetic { @@ -1016,7 +1045,7 @@ pub enum Inst { /// The target of the operation. target: InstTarget, /// The value being assigned. - value: InstAddress, + rhs: InstAddress, }, /// Instruction for assigned bitwise operations. #[musli(packed)] @@ -1026,7 +1055,7 @@ pub enum Inst { /// The target of the operation. target: InstTarget, /// The value being assigned. - value: InstAddress, + rhs: InstAddress, }, /// Instruction for assigned shift operations. #[musli(packed)] @@ -1036,7 +1065,7 @@ pub enum Inst { /// The target of the operation. target: InstTarget, /// The value being assigned. - value: InstAddress, + rhs: InstAddress, }, /// Advance an iterator at the given position. #[musli(packed)] @@ -1494,26 +1523,6 @@ impl fmt::Display for InstShiftOp { #[derive(Debug, TryClone, Clone, Copy, Serialize, Deserialize, Decode, Encode)] #[try_clone(copy)] pub enum InstOp { - /// The add operation. `a + b`. - Add, - /// The sub operation. `a - b`. - Sub, - /// The multiply operation. `a * b`. - Mul, - /// The division operation. `a / b`. - Div, - /// The remainder operation. `a % b`. - Rem, - /// The bitwise and operation. `a & b`. - BitAnd, - /// The bitwise xor operation. `a ^ b`. - BitXor, - /// The bitwise or operation. `a | b`. - BitOr, - /// The shift left operation. `a << b`. - Shl, - /// The shift right operation. `a << b`. - Shr, /// Compare two values on the stack for lt and push the result as a /// boolean on the stack. Lt, @@ -1606,36 +1615,6 @@ pub enum InstOp { impl fmt::Display for InstOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Add => { - write!(f, "+")?; - } - Self::Sub => { - write!(f, "-")?; - } - Self::Mul => { - write!(f, "*")?; - } - Self::Div => { - write!(f, "/")?; - } - Self::Rem => { - write!(f, "%")?; - } - Self::BitAnd => { - write!(f, "&")?; - } - Self::BitXor => { - write!(f, "^")?; - } - Self::BitOr => { - write!(f, "|")?; - } - Self::Shl => { - write!(f, "<<")?; - } - Self::Shr => { - write!(f, ">>")?; - } Self::Lt => { write!(f, "<")?; } diff --git a/crates/rune/src/runtime/value/inline.rs b/crates/rune/src/runtime/value/inline.rs index 1bd3b7038..ca6a667ea 100644 --- a/crates/rune/src/runtime/value/inline.rs +++ b/crates/rune/src/runtime/value/inline.rs @@ -47,7 +47,6 @@ pub enum Inline { } impl Inline { - #[inline] pub(crate) fn as_integer(self) -> Result where T: TryFrom + TryFrom, diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index b041a2f14..a6e6c4f64 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -1,7 +1,6 @@ use core::cmp::Ordering; use core::fmt; use core::mem::replace; -use core::ops; use core::ptr::NonNull; use ::rust_alloc::sync::Arc; @@ -14,6 +13,9 @@ use crate::hash::{Hash, IntoHash, ToTypeHash}; use crate::modules::{option, result}; use crate::runtime; +mod ops; +use self::ops::*; + use super::{ budget, Args, Awaited, BorrowMut, Bytes, Call, ControlFlow, DynArgs, DynGuardedArgs, Dynamic, Format, FormatSpec, Formatter, FromValue, Function, Future, Generator, GeneratorState, @@ -1066,7 +1068,8 @@ impl Vm { let inline = match (lhs.as_ref(), rhs.as_ref()) { (Repr::Inline(Inline::Bool(lhs)), Repr::Inline(Inline::Bool(rhs))) => { - Inline::Bool(bool_op(*lhs, *rhs)) + let value = bool_op(*lhs, *rhs); + Inline::Bool(value) } (lhs, rhs) => { return err(VmErrorKind::UnsupportedBinaryOperation { @@ -1182,89 +1185,6 @@ impl Vm { Ok(moved) } - #[cfg_attr(feature = "bench", inline(never))] - fn internal_num_assign( - &mut self, - target: InstTarget, - ops: &'static ArithmeticOps, - rhs: InstAddress, - ) -> VmResult<()> { - let fallback = match vm_try!(target_value(&mut self.stack, &self.unit, target, rhs)) { - TargetValue::Same(value) => { - match value.as_mut() { - Repr::Inline(Inline::Signed(value)) => { - let out = vm_try!((ops.signed_op)(*value, *value).ok_or_else(ops.error)); - *value = out; - return VmResult::Ok(()); - } - Repr::Inline(Inline::Unsigned(value)) => { - let out = vm_try!((ops.unsigned_op)(*value, *value).ok_or_else(ops.error)); - *value = out; - return VmResult::Ok(()); - } - Repr::Inline(Inline::Float(value)) => { - let out = (ops.float_op)(*value, *value); - *value = out; - return VmResult::Ok(()); - } - Repr::Inline(value) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: ops.protocol.name, - lhs: value.type_info(), - rhs: value.type_info(), - }); - } - _ => {} - } - - TargetFallback::Value(value.clone(), value.clone()) - } - TargetValue::Pair(mut lhs, rhs) => { - match (lhs.as_mut(), rhs.as_ref()) { - (Repr::Inline(lhs), Repr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Signed(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - let out = vm_try!((ops.signed_op)(*lhs, rhs).ok_or_else(ops.error)); - *lhs = out; - return VmResult::Ok(()); - } - (Inline::Unsigned(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - let out = vm_try!((ops.unsigned_op)(*lhs, rhs).ok_or_else(ops.error)); - *lhs = out; - return VmResult::Ok(()); - } - (Inline::Float(lhs), Inline::Float(rhs)) => { - let out = (ops.float_op)(*lhs, *rhs); - *lhs = out; - return VmResult::Ok(()); - } - (lhs, rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: ops.protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - }, - (Repr::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: ops.protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - _ => {} - } - - TargetFallback::Value(lhs.clone(), rhs.clone()) - } - TargetValue::Fallback(fallback) => fallback, - }; - - self.target_fallback_assign(fallback, ops.protocol) - } - /// Execute a fallback operation. #[cfg_attr(feature = "bench", inline(never))] fn target_fallback_assign( @@ -1330,394 +1250,6 @@ impl Vm { } } - /// Internal impl of a numeric operation. - #[cfg_attr(feature = "bench", inline(never))] - fn internal_num( - &mut self, - protocol: &Protocol, - error: fn() -> VmErrorKind, - signed_op: fn(i64, i64) -> Option, - unsigned_op: fn(u64, u64) -> Option, - float_op: fn(f64, f64) -> f64, - lhs: InstAddress, - rhs: InstAddress, - out: Output, - ) -> VmResult<()> { - let rhs = self.stack.at(rhs); - let lhs = self.stack.at(lhs); - - 'fallback: { - let inline = match (lhs.as_ref(), rhs.as_ref()) { - (Repr::Inline(lhs), Repr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Unsigned(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - Inline::Unsigned(vm_try!(unsigned_op(*lhs, rhs).ok_or_else(error))) - } - (Inline::Signed(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - Inline::Signed(vm_try!(signed_op(*lhs, rhs).ok_or_else(error))) - } - (Inline::Float(lhs), Inline::Float(rhs)) => Inline::Float(float_op(*lhs, *rhs)), - (lhs, rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - }, - (Repr::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - _ => { - break 'fallback; - } - }; - - vm_try!(out.store(&mut self.stack, inline)); - return VmResult::Ok(()); - } - - let lhs = lhs.clone(); - let rhs = rhs.clone(); - - let mut args = DynGuardedArgs::new((&rhs,)); - - if let CallResult::Unsupported(lhs) = - vm_try!(self.call_instance_fn(Isolated::None, lhs, protocol, &mut args, out)) - { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - - VmResult::Ok(()) - } - - /// Internal impl of a numeric operation. - #[cfg_attr(feature = "bench", inline(never))] - fn internal_bit( - &mut self, - protocol: &Protocol, - signed_op: fn(i64, i64) -> i64, - unsigned_op: fn(u64, u64) -> u64, - bool_op: fn(bool, bool) -> bool, - lhs: InstAddress, - rhs: InstAddress, - out: Output, - ) -> VmResult<()> { - let lhs = self.stack.at(lhs); - let rhs = self.stack.at(rhs); - - 'fallback: { - let inline = match (lhs.as_ref(), rhs.as_ref()) { - (Repr::Inline(lhs), Repr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Unsigned(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - Inline::Unsigned(unsigned_op(*lhs, rhs)) - } - (Inline::Signed(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - Inline::Signed(signed_op(*lhs, rhs)) - } - (Inline::Bool(lhs), Inline::Bool(rhs)) => Inline::Bool(bool_op(*lhs, *rhs)), - (lhs, rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - }, - (Repr::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - _ => { - break 'fallback; - } - }; - - vm_try!(out.store(&mut self.stack, inline)); - return VmResult::Ok(()); - }; - - let lhs = lhs.clone(); - let rhs = rhs.clone(); - - let mut args = DynGuardedArgs::new((&rhs,)); - - if let CallResult::Unsupported(lhs) = - vm_try!(self.call_instance_fn(Isolated::None, lhs, protocol, &mut args, out)) - { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - - VmResult::Ok(()) - } - - fn internal_infallible_bitwise_assign( - &mut self, - target: InstTarget, - ops: &'static BitwiseOps, - rhs: InstAddress, - ) -> VmResult<()> { - let fallback = match vm_try!(target_value(&mut self.stack, &self.unit, target, rhs)) { - TargetValue::Same(value) => { - match value.as_mut() { - Repr::Inline(Inline::Unsigned(value)) => { - let rhs = *value; - (ops.unsigned_op)(value, rhs); - return VmResult::Ok(()); - } - Repr::Inline(Inline::Signed(value)) => { - let rhs = *value; - (ops.signed_op)(value, rhs); - return VmResult::Ok(()); - } - Repr::Inline(Inline::Bool(value)) => { - let rhs = *value; - (ops.bool_op)(value, rhs); - return VmResult::Ok(()); - } - Repr::Inline(value) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: ops.protocol.name, - lhs: value.type_info(), - rhs: value.type_info(), - }); - } - _ => {} - } - - TargetFallback::Value(value.clone(), value.clone()) - } - TargetValue::Pair(mut lhs, rhs) => { - match (lhs.as_mut(), rhs.as_ref()) { - (Repr::Inline(lhs), Repr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Unsigned(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - (ops.unsigned_op)(lhs, rhs); - return VmResult::Ok(()); - } - (Inline::Signed(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - (ops.signed_op)(lhs, rhs); - return VmResult::Ok(()); - } - (Inline::Bool(lhs), Inline::Bool(rhs)) => { - (ops.bool_op)(lhs, *rhs); - return VmResult::Ok(()); - } - (lhs, rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: ops.protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - }, - (Repr::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: ops.protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - _ => {} - } - - TargetFallback::Value(lhs.clone(), rhs.clone()) - } - TargetValue::Fallback(fallback) => fallback, - }; - - self.target_fallback_assign(fallback, ops.protocol) - } - - fn internal_shift( - &mut self, - protocol: &Protocol, - error: fn() -> VmErrorKind, - signed_op: fn(i64, u32) -> Option, - unsigned_op: fn(u64, u32) -> Option, - lhs: InstAddress, - rhs: InstAddress, - out: Output, - ) -> VmResult<()> { - let (lhs, rhs) = 'fallback: { - let inline = 'inline: { - match vm_try!(self.stack.pair(lhs, rhs)) { - Pair::Same(value) => { - if let Repr::Inline(lhs) = value.as_mut() { - match lhs { - Inline::Unsigned(value) => { - let shift = - vm_try!(u32::try_from(*value).ok().ok_or_else(error)); - let value = - vm_try!(unsigned_op(*value, shift).ok_or_else(error)); - break 'inline Inline::Unsigned(value); - } - Inline::Signed(value) => { - let shift = - vm_try!(u32::try_from(*value).ok().ok_or_else(error)); - let value = vm_try!(signed_op(*value, shift).ok_or_else(error)); - break 'inline Inline::Signed(value); - } - value => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: value.type_info(), - rhs: value.type_info(), - }); - } - } - }; - - break 'fallback (value.clone(), value.clone()); - } - Pair::Pair(lhs, rhs) => { - match (lhs.as_mut(), rhs.as_ref()) { - (Repr::Inline(lhs), Repr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Unsigned(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - let value = vm_try!(unsigned_op(*lhs, rhs).ok_or_else(error)); - break 'inline Inline::Unsigned(value); - } - (Inline::Signed(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - let value = vm_try!(signed_op(*lhs, rhs).ok_or_else(error)); - break 'inline Inline::Signed(value); - } - (lhs, rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - }, - (Repr::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - _ => {} - } - - break 'fallback (lhs.clone(), rhs.clone()); - } - } - }; - - vm_try!(out.store(&mut self.stack, inline)); - return VmResult::Ok(()); - }; - - let mut args = DynGuardedArgs::new((&rhs,)); - - if let CallResult::Unsupported(lhs) = - vm_try!(self.call_instance_fn(Isolated::None, lhs, protocol, &mut args, out)) - { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - - VmResult::Ok(()) - } - - fn internal_bitwise_assign( - &mut self, - target: InstTarget, - ops: &'static ShiftOps, - rhs: InstAddress, - ) -> VmResult<()> { - let fallback = match vm_try!(target_value(&mut self.stack, &self.unit, target, rhs)) { - TargetValue::Same(value) => { - match value.as_mut() { - Repr::Inline(Inline::Unsigned(value)) => { - let shift = vm_try!(u32::try_from(*value).ok().ok_or_else(ops.error)); - let out = vm_try!((ops.unsigned_op)(*value, shift).ok_or_else(ops.error)); - *value = out; - return VmResult::Ok(()); - } - Repr::Inline(Inline::Signed(value)) => { - let shift = vm_try!(u32::try_from(*value).ok().ok_or_else(ops.error)); - let out = vm_try!((ops.signed_op)(*value, shift).ok_or_else(ops.error)); - *value = out; - return VmResult::Ok(()); - } - Repr::Inline(value) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: ops.protocol.name, - lhs: value.type_info(), - rhs: value.type_info(), - }); - } - _ => {} - } - - TargetFallback::Value(value.clone(), value.clone()) - } - TargetValue::Pair(mut lhs, rhs) => { - match (lhs.as_mut(), rhs.as_ref()) { - (Repr::Inline(lhs), Repr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Unsigned(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - let out = vm_try!((ops.unsigned_op)(*lhs, rhs).ok_or_else(ops.error)); - *lhs = out; - return VmResult::Ok(()); - } - (Inline::Signed(lhs), rhs) => { - let rhs = vm_try!(rhs.as_integer()); - let out = vm_try!((ops.signed_op)(*lhs, rhs).ok_or_else(ops.error)); - *lhs = out; - return VmResult::Ok(()); - } - (lhs, rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: ops.protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - }, - (Repr::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: ops.protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); - } - _ => {} - } - - TargetFallback::Value(lhs.clone(), rhs.clone()) - } - TargetValue::Fallback(fallback) => fallback, - }; - - self.target_fallback_assign(fallback, ops.protocol) - } - #[cfg_attr(feature = "bench", inline(never))] fn op_await(&mut self, addr: InstAddress) -> VmResult { VmResult::Ok(vm_try!(self.stack.at(addr).clone().into_future())) @@ -1890,184 +1422,54 @@ impl Vm { #[cfg_attr(feature = "bench", inline(never))] fn op_allocate(&mut self, size: usize) -> VmResult<()> { - vm_try!(self.stack.resize(size)); - VmResult::Ok(()) - } - - #[cfg_attr(feature = "bench", inline(never))] - fn op_not(&mut self, operand: InstAddress, out: Output) -> VmResult<()> { - let value = self.stack.at(operand); - - let value = match value.as_ref() { - Repr::Inline(value) => match value { - Inline::Bool(value) => Value::from(!value), - Inline::Unsigned(value) => Value::from(!value), - Inline::Signed(value) => Value::from(!value), - actual => { - let operand = actual.type_info(); - return err(VmErrorKind::UnsupportedUnaryOperation { op: "!", operand }); - } - }, - actual => { - let operand = actual.type_info(); - return err(VmErrorKind::UnsupportedUnaryOperation { op: "!", operand }); - } - }; - - vm_try!(out.store(&mut self.stack, value)); - VmResult::Ok(()) - } - - #[cfg_attr(feature = "bench", inline(never))] - fn op_neg(&mut self, addr: InstAddress, out: Output) -> VmResult<()> { - let value = self.stack.at(addr); - - let value = match value.as_ref() { - Repr::Inline(value) => match value { - Inline::Float(value) => Value::from(-value), - Inline::Signed(value) => Value::from(-value), - actual => { - let operand = actual.type_info(); - return err(VmErrorKind::UnsupportedUnaryOperation { op: "-", operand }); - } - }, - actual => { - let operand = actual.type_info(); - return err(VmErrorKind::UnsupportedUnaryOperation { op: "-", operand }); - } - }; - - vm_try!(out.store(&mut self.stack, value)); - VmResult::Ok(()) - } - - #[cfg_attr(feature = "bench", inline(never))] - fn op_op( - &mut self, - op: InstOp, - lhs: InstAddress, - rhs: InstAddress, - out: Output, - ) -> VmResult<()> { - match op { - InstOp::Add => { - vm_try!(self.internal_num( - &Protocol::ADD, - || VmErrorKind::Overflow, - i64::checked_add, - u64::checked_add, - ops::Add::add, - lhs, - rhs, - out, - )); - } - InstOp::Sub => { - vm_try!(self.internal_num( - &Protocol::SUB, - || VmErrorKind::Underflow, - i64::checked_sub, - u64::checked_sub, - ops::Sub::sub, - lhs, - rhs, - out, - )); - } - InstOp::Mul => { - vm_try!(self.internal_num( - &Protocol::MUL, - || VmErrorKind::Overflow, - i64::checked_mul, - u64::checked_mul, - ops::Mul::mul, - lhs, - rhs, - out, - )); - } - InstOp::Div => { - vm_try!(self.internal_num( - &Protocol::DIV, - || VmErrorKind::DivideByZero, - i64::checked_div, - u64::checked_div, - ops::Div::div, - lhs, - rhs, - out, - )); - } - InstOp::Rem => { - vm_try!(self.internal_num( - &Protocol::REM, - || VmErrorKind::DivideByZero, - i64::checked_rem, - u64::checked_rem, - ops::Rem::rem, - lhs, - rhs, - out, - )); - } - InstOp::BitAnd => { - use ops::BitAnd as _; - vm_try!(self.internal_bit( - &Protocol::BIT_AND, - i64::bitand, - u64::bitand, - bool::bitand, - lhs, - rhs, - out, - )); - } - InstOp::BitXor => { - use ops::BitXor as _; - vm_try!(self.internal_bit( - &Protocol::BIT_XOR, - i64::bitxor, - u64::bitxor, - bool::bitxor, - lhs, - rhs, - out, - )); - } - InstOp::BitOr => { - use ops::BitOr as _; - vm_try!(self.internal_bit( - &Protocol::BIT_OR, - i64::bitor, - u64::bitor, - bool::bitor, - lhs, - rhs, - out, - )); - } - InstOp::Shl => { - vm_try!(self.internal_shift( - &Protocol::SHL, - || VmErrorKind::Overflow, - i64::checked_shl, - u64::checked_shl, - lhs, - rhs, - out, - )); + vm_try!(self.stack.resize(size)); + VmResult::Ok(()) + } + + #[cfg_attr(feature = "bench", inline(never))] + fn op_not(&mut self, operand: InstAddress, out: Output) -> VmResult<()> { + let value = self.stack.at(operand); + + let value = match value.as_ref() { + Repr::Inline(Inline::Bool(value)) => Value::from(!value), + Repr::Inline(Inline::Unsigned(value)) => Value::from(!value), + Repr::Inline(Inline::Signed(value)) => Value::from(!value), + value => { + let operand = value.type_info(); + return err(VmErrorKind::UnsupportedUnaryOperation { op: "!", operand }); } - InstOp::Shr => { - vm_try!(self.internal_shift( - &Protocol::SHR, - || VmErrorKind::Underflow, - i64::checked_shr, - u64::checked_shr, - lhs, - rhs, - out - )); + }; + + vm_try!(out.store(&mut self.stack, value)); + VmResult::Ok(()) + } + + #[cfg_attr(feature = "bench", inline(never))] + fn op_neg(&mut self, addr: InstAddress, out: Output) -> VmResult<()> { + let value = self.stack.at(addr); + + let value = match value.as_ref() { + Repr::Inline(Inline::Float(value)) => Value::from(-value), + Repr::Inline(Inline::Signed(value)) => Value::from(-value), + actual => { + let operand = actual.type_info(); + return err(VmErrorKind::UnsupportedUnaryOperation { op: "-", operand }); } + }; + + vm_try!(out.store(&mut self.stack, value)); + VmResult::Ok(()) + } + + #[cfg_attr(feature = "bench", inline(never))] + fn op_op( + &mut self, + op: InstOp, + lhs: InstAddress, + rhs: InstAddress, + out: Output, + ) -> VmResult<()> { + match op { InstOp::Lt => { vm_try!(self.internal_cmp(|o| matches!(o, Ordering::Less), lhs, rhs, out)); } @@ -2142,130 +1544,408 @@ impl Vm { } #[cfg_attr(feature = "bench", inline(never))] - fn op_assign_arithmetic( + fn op_arithmetic( &mut self, op: InstArithmeticOp, - target: InstTarget, - value: InstAddress, + lhs: InstAddress, + rhs: InstAddress, + out: Output, ) -> VmResult<()> { - static ADD: ArithmeticOps = ArithmeticOps { - protocol: &Protocol::ADD_ASSIGN, - error: || VmErrorKind::Overflow, - signed_op: i64::checked_add, - unsigned_op: u64::checked_add, - float_op: ops::Add::add, - }; + let ops = ArithmeticOps::from_op(op); - static SUB: ArithmeticOps = ArithmeticOps { - protocol: &Protocol::SUB_ASSIGN, - error: || VmErrorKind::Underflow, - signed_op: i64::checked_sub, - unsigned_op: u64::checked_sub, - float_op: ops::Add::add, - }; + let lhs = self.stack.at(lhs); + let rhs = self.stack.at(rhs); - static MUL: ArithmeticOps = ArithmeticOps { - protocol: &Protocol::MUL_ASSIGN, - error: || VmErrorKind::Overflow, - signed_op: i64::checked_mul, - unsigned_op: u64::checked_mul, - float_op: ops::Add::add, - }; + 'fallback: { + let inline = match (lhs.as_ref(), rhs.as_ref()) { + (Repr::Inline(lhs), Repr::Inline(rhs)) => match (lhs, rhs) { + (Inline::Unsigned(lhs), rhs) => { + let rhs = vm_try!(rhs.as_integer()); + let value = vm_try!((ops.u64)(*lhs, rhs).ok_or_else(ops.error)); + Inline::Unsigned(value) + } + (Inline::Signed(lhs), rhs) => { + let rhs = vm_try!(rhs.as_integer()); + let value = vm_try!((ops.i64)(*lhs, rhs).ok_or_else(ops.error)); + Inline::Signed(value) + } + (Inline::Float(lhs), Inline::Float(rhs)) => { + let value = (ops.f64)(*lhs, *rhs); + Inline::Float(value) + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + (Repr::Any(..), ..) => { + break 'fallback; + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }; - static DIV: ArithmeticOps = ArithmeticOps { - protocol: &Protocol::DIV_ASSIGN, - error: || VmErrorKind::DivideByZero, - signed_op: i64::checked_div, - unsigned_op: u64::checked_div, - float_op: ops::Add::add, - }; + vm_try!(out.store(&mut self.stack, inline)); + return VmResult::Ok(()); + } - static REM: ArithmeticOps = ArithmeticOps { - protocol: &Protocol::REM_ASSIGN, - error: || VmErrorKind::DivideByZero, - signed_op: i64::checked_rem, - unsigned_op: u64::checked_rem, - float_op: ops::Rem::rem, - }; + let lhs = lhs.clone(); + let rhs = rhs.clone(); - let ops = match op { - InstArithmeticOp::Add => &ADD, - InstArithmeticOp::Sub => &SUB, - InstArithmeticOp::Mul => &MUL, - InstArithmeticOp::Div => &DIV, - InstArithmeticOp::Rem => &REM, - }; + let mut args = DynGuardedArgs::new((rhs.clone(),)); + + if let CallResult::Unsupported(lhs) = + vm_try!(self.call_instance_fn(Isolated::None, lhs, &ops.protocol, &mut args, out)) + { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } - vm_try!(self.internal_num_assign(target, ops, value)); VmResult::Ok(()) } #[cfg_attr(feature = "bench", inline(never))] - fn op_assign_bitwise( + fn op_bitwise( &mut self, op: InstBitwiseOp, - target: InstTarget, - value: InstAddress, + lhs: InstAddress, + rhs: InstAddress, + out: Output, ) -> VmResult<()> { - static BIT_AND: BitwiseOps = BitwiseOps { - protocol: &Protocol::BIT_AND_ASSIGN, - signed_op: ops::BitAndAssign::bitand_assign, - unsigned_op: ops::BitAndAssign::bitand_assign, - bool_op: ops::BitAndAssign::bitand_assign, - }; + let ops = BitwiseOps::from_op(op); - static BIT_XOR: BitwiseOps = BitwiseOps { - protocol: &Protocol::BIT_XOR_ASSIGN, - signed_op: ops::BitXorAssign::bitxor_assign, - unsigned_op: ops::BitXorAssign::bitxor_assign, - bool_op: ops::BitXorAssign::bitxor_assign, - }; + let lhs = self.stack.at(lhs); + let rhs = self.stack.at(rhs); - static BIT_OR: BitwiseOps = BitwiseOps { - protocol: &Protocol::BIT_OR_ASSIGN, - signed_op: ops::BitOrAssign::bitor_assign, - unsigned_op: ops::BitOrAssign::bitor_assign, - bool_op: ops::BitOrAssign::bitor_assign, - }; + 'fallback: { + let inline = match (lhs.as_ref(), rhs.as_ref()) { + (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + let value = (ops.u64)(*lhs, rhs); + Inline::Unsigned(value) + } + (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + let value = (ops.i64)(*lhs, rhs); + Inline::Signed(value) + } + (Repr::Inline(Inline::Bool(lhs)), Repr::Inline(Inline::Bool(rhs))) => { + let value = (ops.bool)(*lhs, *rhs); + Inline::Bool(value) + } + (Repr::Any(_), _) => { + break 'fallback; + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }; - let ops = match op { - InstBitwiseOp::BitAnd => &BIT_AND, - InstBitwiseOp::BitXor => &BIT_XOR, - InstBitwiseOp::BitOr => &BIT_OR, + vm_try!(out.store(&mut self.stack, inline)); + return VmResult::Ok(()); }; - vm_try!(self.internal_infallible_bitwise_assign(target, ops, value,)); + let lhs = lhs.clone(); + let rhs = rhs.clone(); + + let mut args = DynGuardedArgs::new((&rhs,)); + + if let CallResult::Unsupported(lhs) = + vm_try!(self.call_instance_fn(Isolated::None, lhs, &ops.protocol, &mut args, out)) + { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + VmResult::Ok(()) } #[cfg_attr(feature = "bench", inline(never))] - fn op_assign_shift( + fn op_shift( &mut self, op: InstShiftOp, + lhs: InstAddress, + rhs: InstAddress, + out: Output, + ) -> VmResult<()> { + let ops = ShiftOps::from_op(op); + + let (lhs, rhs) = 'fallback: { + let inline = { + match vm_try!(self.stack.pair(lhs, rhs)) { + Pair::Same(value) => match value.as_mut() { + Repr::Inline(Inline::Unsigned(value)) => { + let shift = vm_try!(u32::try_from(*value).ok().ok_or_else(ops.error)); + let value = vm_try!((ops.u64)(*value, shift).ok_or_else(ops.error)); + Inline::Unsigned(value) + } + Repr::Inline(Inline::Signed(value)) => { + let shift = vm_try!(u32::try_from(*value).ok().ok_or_else(ops.error)); + let value = vm_try!((ops.i64)(*value, shift).ok_or_else(ops.error)); + Inline::Signed(value) + } + Repr::Any(..) => break 'fallback (value.clone(), value.clone()), + value => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: value.type_info(), + rhs: value.type_info(), + }); + } + }, + Pair::Pair(lhs, rhs) => match (lhs.as_mut(), rhs.as_ref()) { + (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + let value = vm_try!((ops.u64)(*lhs, rhs).ok_or_else(ops.error)); + Inline::Unsigned(value) + } + (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + let value = vm_try!((ops.i64)(*lhs, rhs).ok_or_else(ops.error)); + Inline::Signed(value) + } + (Repr::Any(..), _) => { + break 'fallback (lhs.clone(), rhs.clone()); + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + } + }; + + vm_try!(out.store(&mut self.stack, inline)); + return VmResult::Ok(()); + }; + + let mut args = DynGuardedArgs::new((rhs.clone(),)); + + if let CallResult::Unsupported(lhs) = + vm_try!(self.call_instance_fn(Isolated::None, lhs, &ops.protocol, &mut args, out)) + { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + + VmResult::Ok(()) + } + + #[cfg_attr(feature = "bench", inline(never))] + fn op_assign_arithmetic( + &mut self, + op: InstArithmeticOp, target: InstTarget, - value: InstAddress, + rhs: InstAddress, ) -> VmResult<()> { - static SHL: ShiftOps = ShiftOps { - protocol: &Protocol::SHL_ASSIGN, - error: || VmErrorKind::Overflow, - signed_op: i64::checked_shl, - unsigned_op: u64::checked_shl, + let ops = AssignArithmeticOps::from_op(op); + + let fallback = match vm_try!(target_value(&mut self.stack, &self.unit, target, rhs)) { + TargetValue::Same(value) => match value.as_mut() { + Repr::Inline(Inline::Signed(value)) => { + let out = vm_try!((ops.i64)(*value, *value).ok_or_else(ops.error)); + *value = out; + return VmResult::Ok(()); + } + Repr::Inline(Inline::Unsigned(value)) => { + let out = vm_try!((ops.u64)(*value, *value).ok_or_else(ops.error)); + *value = out; + return VmResult::Ok(()); + } + Repr::Inline(Inline::Float(value)) => { + let out = (ops.f64)(*value, *value); + *value = out; + return VmResult::Ok(()); + } + Repr::Any(..) => TargetFallback::Value(value.clone(), value.clone()), + value => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: value.type_info(), + rhs: value.type_info(), + }); + } + }, + TargetValue::Pair(mut lhs, rhs) => match (lhs.as_mut(), rhs.as_ref()) { + (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + let out = vm_try!((ops.i64)(*lhs, rhs).ok_or_else(ops.error)); + *lhs = out; + return VmResult::Ok(()); + } + (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + let out = vm_try!((ops.u64)(*lhs, rhs).ok_or_else(ops.error)); + *lhs = out; + return VmResult::Ok(()); + } + (Repr::Inline(Inline::Float(lhs)), Repr::Inline(Inline::Float(rhs))) => { + let out = (ops.f64)(*lhs, *rhs); + *lhs = out; + return VmResult::Ok(()); + } + (Repr::Any(..), _) => TargetFallback::Value(lhs.clone(), rhs.clone()), + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + TargetValue::Fallback(fallback) => fallback, }; - static SHR: ShiftOps = ShiftOps { - protocol: &Protocol::SHR_ASSIGN, - error: || VmErrorKind::Underflow, - signed_op: i64::checked_shr, - unsigned_op: u64::checked_shr, + self.target_fallback_assign(fallback, &ops.protocol) + } + + #[cfg_attr(feature = "bench", inline(never))] + fn op_assign_bitwise( + &mut self, + op: InstBitwiseOp, + target: InstTarget, + rhs: InstAddress, + ) -> VmResult<()> { + let ops = AssignBitwiseOps::from_ops(op); + + let fallback = match vm_try!(target_value(&mut self.stack, &self.unit, target, rhs)) { + TargetValue::Same(value) => match value.as_mut() { + Repr::Inline(Inline::Unsigned(value)) => { + let rhs = *value; + (ops.u64)(value, rhs); + return VmResult::Ok(()); + } + Repr::Inline(Inline::Signed(value)) => { + let rhs = *value; + (ops.i64)(value, rhs); + return VmResult::Ok(()); + } + Repr::Inline(Inline::Bool(value)) => { + let rhs = *value; + (ops.bool)(value, rhs); + return VmResult::Ok(()); + } + Repr::Any(..) => TargetFallback::Value(value.clone(), value.clone()), + value => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: value.type_info(), + rhs: value.type_info(), + }); + } + }, + TargetValue::Pair(mut lhs, rhs) => match (lhs.as_mut(), rhs.as_ref()) { + (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + (ops.u64)(lhs, rhs); + return VmResult::Ok(()); + } + (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + (ops.i64)(lhs, rhs); + return VmResult::Ok(()); + } + (Repr::Inline(Inline::Bool(lhs)), Repr::Inline(Inline::Bool(rhs))) => { + (ops.bool)(lhs, *rhs); + return VmResult::Ok(()); + } + (Repr::Any(..), ..) => TargetFallback::Value(lhs.clone(), rhs.clone()), + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + TargetValue::Fallback(fallback) => fallback, }; - let ops = match op { - InstShiftOp::Shl => &SHL, - InstShiftOp::Shr => &SHR, + self.target_fallback_assign(fallback, &ops.protocol) + } + + #[cfg_attr(feature = "bench", inline(never))] + fn op_assign_shift( + &mut self, + op: InstShiftOp, + target: InstTarget, + rhs: InstAddress, + ) -> VmResult<()> { + let ops = AssignShiftOps::from_op(op); + + let fallback = match vm_try!(target_value(&mut self.stack, &self.unit, target, rhs)) { + TargetValue::Same(value) => match value.as_mut() { + Repr::Inline(Inline::Unsigned(value)) => { + let shift = vm_try!(u32::try_from(*value).ok().ok_or_else(ops.error)); + let out = vm_try!((ops.u64)(*value, shift).ok_or_else(ops.error)); + *value = out; + return VmResult::Ok(()); + } + Repr::Inline(Inline::Signed(value)) => { + let shift = vm_try!(u32::try_from(*value).ok().ok_or_else(ops.error)); + let out = vm_try!((ops.i64)(*value, shift).ok_or_else(ops.error)); + *value = out; + return VmResult::Ok(()); + } + Repr::Any(..) => TargetFallback::Value(value.clone(), value.clone()), + value => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: value.type_info(), + rhs: value.type_info(), + }); + } + }, + TargetValue::Pair(mut lhs, rhs) => match (lhs.as_mut(), rhs.as_ref()) { + (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + let out = vm_try!((ops.u64)(*lhs, rhs).ok_or_else(ops.error)); + *lhs = out; + return VmResult::Ok(()); + } + (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => { + let rhs = vm_try!(rhs.as_integer()); + let out = vm_try!((ops.i64)(*lhs, rhs).ok_or_else(ops.error)); + *lhs = out; + return VmResult::Ok(()); + } + (Repr::Any(..), _) => TargetFallback::Value(lhs.clone(), rhs.clone()), + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: ops.protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + TargetValue::Fallback(fallback) => fallback, }; - vm_try!(self.internal_bitwise_assign(target, ops, value)); - VmResult::Ok(()) + self.target_fallback_assign(fallback, &ops.protocol) } /// Perform an index set operation. @@ -3624,14 +3304,23 @@ impl Vm { Inst::Op { op, a, b, out } => { vm_try!(self.op_op(op, a, b, out)); } - Inst::AssignArithmetic { op, target, value } => { - vm_try!(self.op_assign_arithmetic(op, target, value)); + Inst::Arithmetic { op, a, b, out } => { + vm_try!(self.op_arithmetic(op, a, b, out)); + } + Inst::Bitwise { op, a, b, out } => { + vm_try!(self.op_bitwise(op, a, b, out)); } - Inst::AssignBitwise { op, target, value } => { - vm_try!(self.op_assign_bitwise(op, target, value)); + Inst::Shift { op, a, b, out } => { + vm_try!(self.op_shift(op, a, b, out)); } - Inst::AssignShift { op, target, value } => { - vm_try!(self.op_assign_shift(op, target, value)); + Inst::AssignArithmetic { op, target, rhs } => { + vm_try!(self.op_assign_arithmetic(op, target, rhs)); + } + Inst::AssignBitwise { op, target, rhs } => { + vm_try!(self.op_assign_bitwise(op, target, rhs)); + } + Inst::AssignShift { op, target, rhs } => { + vm_try!(self.op_assign_shift(op, target, rhs)); } Inst::IterNext { addr, jump, out } => { vm_try!(self.op_iter_next(addr, jump, out)); @@ -3723,28 +3412,6 @@ fn check_args(args: usize, expected: usize) -> Result<(), VmErrorKind> { Ok(()) } -struct ArithmeticOps { - protocol: &'static Protocol, - error: fn() -> VmErrorKind, - signed_op: fn(i64, i64) -> Option, - unsigned_op: fn(u64, u64) -> Option, - float_op: fn(f64, f64) -> f64, -} - -struct BitwiseOps { - protocol: &'static Protocol, - signed_op: fn(&mut i64, i64), - unsigned_op: fn(&mut u64, u64), - bool_op: fn(&mut bool, bool), -} - -struct ShiftOps { - protocol: &'static Protocol, - error: fn() -> VmErrorKind, - signed_op: fn(i64, u32) -> Option, - unsigned_op: fn(u64, u32) -> Option, -} - enum TargetFallback { Value(Value, Value), Field(Value, Hash, Value), @@ -3760,6 +3427,7 @@ enum TargetValue<'a> { Fallback(TargetFallback), } +#[inline] fn target_value<'a>( stack: &'a mut Stack, unit: &Unit, diff --git a/crates/rune/src/runtime/vm/ops.rs b/crates/rune/src/runtime/vm/ops.rs new file mode 100644 index 000000000..b6c2bcc4d --- /dev/null +++ b/crates/rune/src/runtime/vm/ops.rs @@ -0,0 +1,221 @@ +use core::ops::{ + Add, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, Mul, Rem, Sub, +}; + +use crate::runtime::{InstArithmeticOp, InstBitwiseOp, InstShiftOp, Protocol, VmErrorKind}; + +pub(super) struct ArithmeticOps { + pub(super) protocol: Protocol, + pub(super) error: fn() -> VmErrorKind, + pub(super) i64: fn(i64, i64) -> Option, + pub(super) u64: fn(u64, u64) -> Option, + pub(super) f64: fn(f64, f64) -> f64, +} + +impl ArithmeticOps { + pub(super) fn from_op(op: InstArithmeticOp) -> &'static Self { + match op { + InstArithmeticOp::Add => &Self { + protocol: Protocol::ADD, + error: || VmErrorKind::Overflow, + i64: i64::checked_add, + u64: u64::checked_add, + f64: f64::add, + }, + InstArithmeticOp::Sub => &Self { + protocol: Protocol::SUB, + error: || VmErrorKind::Underflow, + i64: i64::checked_sub, + u64: u64::checked_sub, + f64: f64::sub, + }, + InstArithmeticOp::Mul => &Self { + protocol: Protocol::MUL, + error: || VmErrorKind::Overflow, + i64: i64::checked_mul, + u64: u64::checked_mul, + f64: f64::mul, + }, + InstArithmeticOp::Div => &Self { + protocol: Protocol::DIV, + error: || VmErrorKind::DivideByZero, + i64: i64::checked_div, + u64: u64::checked_div, + f64: f64::div, + }, + InstArithmeticOp::Rem => &Self { + protocol: Protocol::REM, + error: || VmErrorKind::DivideByZero, + i64: i64::checked_rem, + u64: u64::checked_rem, + f64: f64::rem, + }, + } + } +} + +pub(super) struct AssignArithmeticOps { + pub(super) protocol: Protocol, + pub(super) error: fn() -> VmErrorKind, + pub(super) i64: fn(i64, i64) -> Option, + pub(super) u64: fn(u64, u64) -> Option, + pub(super) f64: fn(f64, f64) -> f64, +} + +impl AssignArithmeticOps { + pub(super) fn from_op(op: InstArithmeticOp) -> &'static AssignArithmeticOps { + match op { + InstArithmeticOp::Add => &Self { + protocol: Protocol::ADD_ASSIGN, + error: || VmErrorKind::Overflow, + i64: i64::checked_add, + u64: u64::checked_add, + f64: f64::add, + }, + InstArithmeticOp::Sub => &Self { + protocol: Protocol::SUB_ASSIGN, + error: || VmErrorKind::Underflow, + i64: i64::checked_sub, + u64: u64::checked_sub, + f64: f64::sub, + }, + InstArithmeticOp::Mul => &Self { + protocol: Protocol::MUL_ASSIGN, + error: || VmErrorKind::Overflow, + i64: i64::checked_mul, + u64: u64::checked_mul, + f64: f64::mul, + }, + InstArithmeticOp::Div => &Self { + protocol: Protocol::DIV_ASSIGN, + error: || VmErrorKind::DivideByZero, + i64: i64::checked_div, + u64: u64::checked_div, + f64: f64::div, + }, + InstArithmeticOp::Rem => &Self { + protocol: Protocol::REM_ASSIGN, + error: || VmErrorKind::DivideByZero, + i64: i64::checked_rem, + u64: u64::checked_rem, + f64: f64::rem, + }, + } + } +} + +pub(super) struct AssignBitwiseOps { + pub(super) protocol: Protocol, + pub(super) i64: fn(&mut i64, i64), + pub(super) u64: fn(&mut u64, u64), + pub(super) bool: fn(&mut bool, bool), +} + +impl AssignBitwiseOps { + pub(super) fn from_ops(op: InstBitwiseOp) -> &'static Self { + match op { + InstBitwiseOp::BitAnd => &Self { + protocol: Protocol::BIT_AND_ASSIGN, + i64: i64::bitand_assign, + u64: u64::bitand_assign, + bool: bool::bitand_assign, + }, + InstBitwiseOp::BitXor => &Self { + protocol: Protocol::BIT_XOR_ASSIGN, + i64: i64::bitxor_assign, + u64: u64::bitxor_assign, + bool: bool::bitxor_assign, + }, + InstBitwiseOp::BitOr => &Self { + protocol: Protocol::BIT_OR_ASSIGN, + i64: i64::bitor_assign, + u64: u64::bitor_assign, + bool: bool::bitor_assign, + }, + } + } +} + +pub(super) struct BitwiseOps { + pub(super) protocol: Protocol, + pub(super) i64: fn(i64, i64) -> i64, + pub(super) u64: fn(u64, u64) -> u64, + pub(super) bool: fn(bool, bool) -> bool, +} + +impl BitwiseOps { + pub(super) fn from_op(op: InstBitwiseOp) -> &'static BitwiseOps { + match op { + InstBitwiseOp::BitAnd => &BitwiseOps { + protocol: Protocol::BIT_AND, + i64: i64::bitand, + u64: u64::bitand, + bool: bool::bitand, + }, + InstBitwiseOp::BitXor => &BitwiseOps { + protocol: Protocol::BIT_XOR, + i64: i64::bitxor, + u64: u64::bitxor, + bool: bool::bitxor, + }, + InstBitwiseOp::BitOr => &BitwiseOps { + protocol: Protocol::BIT_OR, + i64: i64::bitor, + u64: u64::bitor, + bool: bool::bitor, + }, + } + } +} + +pub(super) struct AssignShiftOps { + pub(super) protocol: Protocol, + pub(super) error: fn() -> VmErrorKind, + pub(super) i64: fn(i64, u32) -> Option, + pub(super) u64: fn(u64, u32) -> Option, +} + +impl AssignShiftOps { + pub(super) fn from_op(op: InstShiftOp) -> &'static AssignShiftOps { + match op { + InstShiftOp::Shl => &Self { + protocol: Protocol::SHL_ASSIGN, + error: || VmErrorKind::Overflow, + i64: i64::checked_shl, + u64: u64::checked_shl, + }, + InstShiftOp::Shr => &Self { + protocol: Protocol::SHR_ASSIGN, + error: || VmErrorKind::Underflow, + i64: i64::checked_shr, + u64: u64::checked_shr, + }, + } + } +} + +pub(super) struct ShiftOps { + pub(super) protocol: Protocol, + pub(super) error: fn() -> VmErrorKind, + pub(super) i64: fn(i64, u32) -> Option, + pub(super) u64: fn(u64, u32) -> Option, +} + +impl ShiftOps { + pub(super) fn from_op(op: InstShiftOp) -> &'static Self { + match op { + InstShiftOp::Shl => &Self { + protocol: Protocol::SHL, + error: || VmErrorKind::Overflow, + i64: i64::checked_shl, + u64: u64::checked_shl, + }, + InstShiftOp::Shr => &Self { + protocol: Protocol::SHR, + error: || VmErrorKind::Underflow, + i64: i64::checked_shr, + u64: u64::checked_shr, + }, + } + } +} diff --git a/crates/rune/src/tests/deprecation.rs b/crates/rune/src/tests/deprecation.rs index 30a9b07e9..235a84146 100644 --- a/crates/rune/src/tests/deprecation.rs +++ b/crates/rune/src/tests/deprecation.rs @@ -26,7 +26,7 @@ fn create_context() -> Result { module.function_meta(new)?; module - .field_function(Protocol::GET, "hello", |_this: &TestStruct| 1)? + .field_function(&Protocol::GET, "hello", |_this: &TestStruct| 1)? .deprecated("Deprecated get field fn")?; module diff --git a/crates/rune/src/tests/external_ops.rs b/crates/rune/src/tests/external_ops.rs index 607f32a9b..a008ea3ab 100644 --- a/crates/rune/src/tests/external_ops.rs +++ b/crates/rune/src/tests/external_ops.rs @@ -34,8 +34,8 @@ fn assign_ops_struct() -> Result<()> { let mut module = Module::new(); module.ty::()?; - module.associated_function(Protocol::$protocol, External::value)?; - module.field_function(Protocol::$protocol, "field", External::field)?; + module.associated_function(&Protocol::$protocol, External::value)?; + module.field_function(&Protocol::$protocol, "field", External::field)?; let mut context = Context::with_default_modules()?; context.install(module)?; @@ -116,8 +116,8 @@ fn assign_ops_tuple() -> Result<()> { let mut module = Module::new(); module.ty::()?; - module.associated_function(Protocol::$protocol, External::value)?; - module.index_function(Protocol::$protocol, 1, External::field)?; + module.associated_function(&Protocol::$protocol, External::value)?; + module.index_function(&Protocol::$protocol, 1, External::field)?; let mut context = Context::with_default_modules()?; context.install(module)?; @@ -192,7 +192,7 @@ fn ordering_struct() -> Result<()> { let mut module = Module::new(); module.ty::()?; - module.associated_function(Protocol::$protocol, External::value)?; + module.associated_function(&Protocol::$protocol, External::value)?; let mut context = Context::with_default_modules()?; context.install(module)?; @@ -261,7 +261,7 @@ fn eq_struct() -> Result<()> { let mut module = Module::new(); module.ty::()?; - module.associated_function(Protocol::$protocol, External::value)?; + module.associated_function(&Protocol::$protocol, External::value)?; let mut context = Context::with_default_modules()?; context.install(module)?; diff --git a/crates/rune/src/tests/type_name_native.rs b/crates/rune/src/tests/type_name_native.rs index 66a55d160..2e69c99ce 100644 --- a/crates/rune/src/tests/type_name_native.rs +++ b/crates/rune/src/tests/type_name_native.rs @@ -20,7 +20,7 @@ fn make_native_module() -> Result { module.ty::()?; module.function("native_fn", native_fn).build()?; module.associated_function("instance_fn", NativeStruct::instance_fn)?; - module.field_function(Protocol::GET, "x", NativeStruct::get_x)?; + module.field_function(&Protocol::GET, "x", NativeStruct::get_x)?; Ok(module) } diff --git a/crates/rune/src/tests/vm_try.rs b/crates/rune/src/tests/vm_try.rs index a5608de3c..477176b0c 100644 --- a/crates/rune/src/tests/vm_try.rs +++ b/crates/rune/src/tests/vm_try.rs @@ -11,7 +11,7 @@ fn custom_try() -> Result<()> { module.ty::()?; - module.associated_function(Protocol::TRY, |r: CustomResult| { + module.associated_function(&Protocol::TRY, |r: CustomResult| { if r.0 { ControlFlow::Continue(42i64) } else { diff --git a/examples/examples/custom_mul.rs b/examples/examples/custom_mul.rs index 5bfa125cf..3b28c477c 100644 --- a/examples/examples/custom_mul.rs +++ b/examples/examples/custom_mul.rs @@ -53,6 +53,6 @@ fn main() -> rune::support::Result<()> { fn module() -> Result { let mut m = Module::with_item(["module"])?; m.ty::()?; - m.associated_function(Protocol::MUL, Foo::mul)?; + m.associated_function(&Protocol::MUL, Foo::mul)?; Ok(m) } diff --git a/examples/examples/references.rs b/examples/examples/references.rs index 0f2223592..7e1f01f1a 100644 --- a/examples/examples/references.rs +++ b/examples/examples/references.rs @@ -19,7 +19,7 @@ impl Foo { fn main() -> rune::support::Result<()> { let mut module = Module::new(); module.ty::()?; - module.associated_function(Protocol::ADD_ASSIGN, Foo::add_assign)?; + module.associated_function(&Protocol::ADD_ASSIGN, Foo::add_assign)?; let mut context = rune_modules::default_context()?; context.install(module)?;