From 01fca91b7137a5411c948b15900d41b3f91b9eb3 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Mon, 29 Jul 2024 10:50:44 +0200 Subject: [PATCH] vm: Optimize value cloning --- .github/workflows/ci.yml | 2 +- crates/rune/src/runtime/inst.rs | 11 +- crates/rune/src/runtime/object.rs | 2 +- crates/rune/src/runtime/shared.rs | 31 ++- crates/rune/src/runtime/stack.rs | 31 ++- crates/rune/src/runtime/value.rs | 52 ++++- crates/rune/src/runtime/vm.rs | 362 ++++++++++++++++++------------ 7 files changed, 330 insertions(+), 161 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04b0d4eb2..092660908 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - run: cargo run --bin rune -- fmt --recursive --verbose --check + # - run: cargo run --bin rune -- fmt --verbose --check clippy: runs-on: ubuntu-latest diff --git a/crates/rune/src/runtime/inst.rs b/crates/rune/src/runtime/inst.rs index f23f593f3..b1eeae61f 100644 --- a/crates/rune/src/runtime/inst.rs +++ b/crates/rune/src/runtime/inst.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use crate as rune; use crate::alloc::prelude::*; -use crate::runtime::{Call, FormatSpec, Memory, Mutable, Type, Value, VmError, VmResult}; +use crate::runtime::{Call, FormatSpec, Memory, Type, Value, VmError, VmResult}; use crate::Hash; /// Pre-canned panic reasons. @@ -1307,15 +1307,6 @@ impl IntoOutput for Value { } } -impl IntoOutput for Mutable { - type Output = Mutable; - - #[inline] - fn into_output(self) -> VmResult { - VmResult::Ok(self) - } -} - /// How an instruction addresses a value. #[derive( Default, diff --git a/crates/rune/src/runtime/object.rs b/crates/rune/src/runtime/object.rs index b4421b7ee..e3fc47ff7 100644 --- a/crates/rune/src/runtime/object.rs +++ b/crates/rune/src/runtime/object.rs @@ -375,7 +375,7 @@ impl Object { return VmResult::Ok(false); }; - if !vm_try!(Value::partial_eq_with(v1, &v2, caller)) { + if !vm_try!(Value::partial_eq_with(v1, v2, caller)) { return VmResult::Ok(false); } } diff --git a/crates/rune/src/runtime/shared.rs b/crates/rune/src/runtime/shared.rs index 8353022ea..9c6ba6622 100644 --- a/crates/rune/src/runtime/shared.rs +++ b/crates/rune/src/runtime/shared.rs @@ -220,13 +220,30 @@ impl TryClone for Shared { } impl Clone for Shared { + #[inline] fn clone(&self) -> Self { + // SAFETY: We know that the inner value is live in this instance. unsafe { SharedBox::inc(self.inner); } Self { inner: self.inner } } + + #[inline] + fn clone_from(&mut self, source: &Self) { + if ptr::eq(self.inner.as_ptr(), source.inner.as_ptr()) { + return; + } + + // SAFETY: We know that the inner value is live in both instances. + unsafe { + SharedBox::dec(self.inner); + SharedBox::inc(source.inner); + } + + self.inner = source.inner; + } } impl Drop for Shared { @@ -274,7 +291,12 @@ impl SharedBox { let count_ref = &*ptr::addr_of!((*this.as_ptr()).count); let count = count_ref.get(); - if count == 0 || count == usize::MAX { + debug_assert_ne!( + count, 0, + "Reference count of zero should only happen if Shared is incorrectly implemented" + ); + + if count == usize::MAX { crate::alloc::abort(); } @@ -291,9 +313,10 @@ impl SharedBox { let count_ref = &*ptr::addr_of!((*this.as_ptr()).count); let count = count_ref.get(); - if count == 0 { - crate::alloc::abort(); - } + debug_assert_ne!( + count, 0, + "Reference count of zero should only happen if Shared is incorrectly implemented" + ); let count = count - 1; count_ref.set(count); diff --git a/crates/rune/src/runtime/stack.rs b/crates/rune/src/runtime/stack.rs index 4bb0203b8..89c5b1c26 100644 --- a/crates/rune/src/runtime/stack.rs +++ b/crates/rune/src/runtime/stack.rs @@ -6,7 +6,7 @@ use core::slice; use crate::alloc::alloc::Global; use crate::alloc::prelude::*; use crate::alloc::{self, Vec}; -use crate::runtime::{InstAddress, Value, VmErrorKind}; +use crate::runtime::{InstAddress, Output, Value, VmErrorKind}; /// An error raised when accessing an address on the stack. #[derive(Debug, PartialEq)] @@ -487,6 +487,35 @@ impl Stack { Ok(()) } + /// Copy the value at the given address to the output. + pub(crate) fn copy(&mut self, from: InstAddress, out: Output) -> Result<(), StackError> { + let Some(to) = out.as_addr() else { + return Ok(()); + }; + + if from == to { + return Ok(()); + } + + let from = self.top.wrapping_add(from.offset()); + let to = self.top.wrapping_add(to.offset()); + + if from.max(to) >= self.stack.len() { + return Err(StackError { + addr: InstAddress::new(from.max(to).wrapping_sub(self.top)), + }); + } + + // SAFETY: We've checked that both addresses are in-bound and different + // just above. + unsafe { + let ptr = self.stack.as_mut_ptr(); + (*ptr.add(to)).clone_from(&*ptr.add(from).cast_const()); + } + + Ok(()) + } + /// Get a pair of addresses. pub(crate) fn pair(&mut self, a: InstAddress, b: InstAddress) -> Result, StackError> { if a == b { diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index 41e199f39..ffac23e4e 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -110,7 +110,6 @@ pub(crate) enum ValueShared { } /// An entry on the stack. -#[derive(Clone)] pub struct Value { repr: Repr, } @@ -2001,6 +2000,15 @@ impl From for Value { } } +impl IntoOutput for Inline { + type Output = Inline; + + #[inline] + fn into_output(self) -> VmResult { + VmResult::Ok(self) + } +} + impl TryFrom for Value { type Error = alloc::Error; @@ -2012,6 +2020,15 @@ impl TryFrom for Value { } } +impl IntoOutput for Mutable { + type Output = Mutable; + + #[inline] + fn into_output(self) -> VmResult { + VmResult::Ok(self) + } +} + impl ToValue for Value { #[inline] fn to_value(self) -> VmResult { @@ -2067,6 +2084,35 @@ impl MaybeTypeOf for Value { } } +impl Clone for Value { + #[inline] + fn clone(&self) -> Self { + let repr = match &self.repr { + Repr::Empty => Repr::Empty, + Repr::Inline(inline) => Repr::Inline(*inline), + Repr::Mutable(mutable) => Repr::Mutable(mutable.clone()), + }; + + Self { repr } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + match (&mut self.repr, &source.repr) { + (Repr::Empty, Repr::Empty) => {} + (Repr::Inline(lhs), Repr::Inline(rhs)) => { + *lhs = *rhs; + } + (Repr::Mutable(lhs), Repr::Mutable(rhs)) => { + lhs.clone_from(rhs); + } + (lhs, rhs) => { + *lhs = rhs.clone(); + } + } + } +} + impl TryClone for Value { fn try_clone(&self) -> alloc::Result { // NB: value cloning is a shallow clone of the underlying data. @@ -2402,10 +2448,14 @@ impl Mutable { } } +/// Ensures that `Value` and `Repr` is niche-filled when used in common +/// combinations. #[test] fn size_of_value() { use core::mem::size_of; + assert_eq!(size_of::(), size_of::()); assert_eq!(size_of::(), size_of::()); assert_eq!(size_of::>(), size_of::()); + assert_eq!(size_of::>(), size_of::()); } diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index 0f583ca8d..9251d99a7 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -145,6 +145,7 @@ macro_rules! target_value { let rhs = vm_try!($vm.stack.at($rhs)); let field = vm_try!($vm.unit.lookup_string(field)); + $lhs = vm_try!($vm.stack.at(lhs)).clone(); if let Some(value) = vm_try!(Vm::try_object_like_index_get_mut(&$lhs, field)) { @@ -1020,7 +1021,7 @@ impl Vm { } /// Implementation of getting a string index on an object-like type. - fn try_tuple_like_index_set(target: &mut Value, index: usize, value: Value) -> VmResult { + fn try_tuple_like_index_set(target: &Value, index: usize, value: &Value) -> VmResult { match vm_try!(target.value_ref()) { ValueRef::Inline(target) => match target { Inline::Unit => VmResult::Ok(false), @@ -1029,7 +1030,7 @@ impl Vm { ValueRef::Mutable(target) => match &mut *vm_try!(target.borrow_mut()) { Mutable::Tuple(tuple) => { if let Some(target) = tuple.get_mut(index) { - *target = value; + target.clone_from(value); return VmResult::Ok(true); } @@ -1037,7 +1038,7 @@ impl Vm { } Mutable::Vec(vec) => { if let Some(target) = vec.get_mut(index) { - *target = value; + target.clone_from(value); return VmResult::Ok(true); } @@ -1050,7 +1051,7 @@ impl Vm { _ => return VmResult::Ok(false), }; - *target = value; + target.clone_from(value); VmResult::Ok(true) } Mutable::Option(option) => { @@ -1059,12 +1060,12 @@ impl Vm { _ => return VmResult::Ok(false), }; - *target = value; + target.clone_from(value); VmResult::Ok(true) } Mutable::TupleStruct(tuple_struct) => { if let Some(target) = tuple_struct.get_mut(index) { - *target = value; + target.clone_from(value); return VmResult::Ok(true); } @@ -1073,7 +1074,7 @@ impl Vm { Mutable::Variant(variant) => { if let VariantData::Tuple(data) = variant.data_mut() { if let Some(target) = data.get_mut(index) { - *target = value; + target.clone_from(value); return VmResult::Ok(true); } } @@ -1130,53 +1131,47 @@ impl Vm { }; let hash = index.hash(); - let result = vm_try!(self.call_field_fn(Protocol::GET, target, hash, &mut (), out)); - VmResult::Ok(result) } - fn try_object_slot_index_set( - &mut self, - target: Value, - string_slot: usize, - value: Value, - ) -> VmResult> { - let field = vm_try!(self.unit.lookup_string(string_slot)); - + fn try_object_slot_index_set(target: &Value, field: &str, value: &Value) -> VmResult { 'fallback: { let ValueRef::Mutable(target) = vm_try!(target.value_ref()) else { - return VmResult::Ok(CallResult::Unsupported(target.clone())); + return err(VmErrorKind::MissingField { + target: vm_try!(target.type_info()), + field: vm_try!(field.try_to_owned()), + }); }; match &mut *vm_try!(target.borrow_mut()) { Mutable::Object(object) => { - let key = vm_try!(field.as_str().try_to_owned()); + let key = vm_try!(field.try_to_owned()); vm_try!(object.insert(key, value.clone())); - return VmResult::Ok(CallResult::Ok(())); + return VmResult::Ok(true); } Mutable::Struct(object) => { - if let Some(v) = object.get_mut(field.as_str()) { - *v = value.clone(); - return VmResult::Ok(CallResult::Ok(())); + if let Some(v) = object.get_mut(field) { + v.clone_from(value); + return VmResult::Ok(true); } return err(VmErrorKind::MissingField { target: object.type_info(), - field: vm_try!(field.as_str().try_to_owned()), + field: vm_try!(field.try_to_owned()), }); } Mutable::Variant(variant) => { if let VariantData::Struct(data) = variant.data_mut() { - if let Some(v) = data.get_mut(field.as_str()) { - *v = value.clone(); - return VmResult::Ok(CallResult::Ok(())); + if let Some(v) = data.get_mut(field) { + v.clone_from(value); + return VmResult::Ok(true); } } return err(VmErrorKind::MissingField { target: variant.type_info(), - field: vm_try!(field.as_str().try_to_owned()), + field: vm_try!(field.try_to_owned()), }); } _ => { @@ -1185,16 +1180,7 @@ impl Vm { } } - let target = target.clone(); - let value = value.clone(); - - let hash = field.hash(); - let mut args = DynGuardedArgs::new((value,)); - - let result = - vm_try!(self.call_field_fn(Protocol::SET, target, hash, &mut args, Output::discard())); - - VmResult::Ok(result) + VmResult::Ok(false) } fn on_tuple(&self, ty: TypeCheck, value: &Value, f: F) -> VmResult> @@ -1299,14 +1285,12 @@ impl Vm { rhs: InstAddress, out: Output, ) -> VmResult<()> { - let rhs = vm_try!(self.stack.at(rhs)).clone(); - let lhs = vm_try!(self.stack.at(lhs)).clone(); + let rhs = vm_try!(self.stack.at(rhs)); + let lhs = vm_try!(self.stack.at(lhs)); - match (vm_try!(lhs.value_ref()), vm_try!(rhs.value_ref())) { + let inline = match (vm_try!(lhs.value_ref()), vm_try!(rhs.value_ref())) { (ValueRef::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { - (Inline::Bool(lhs), Inline::Bool(rhs)) => { - vm_try!(out.store(&mut self.stack, || bool_op(*lhs, *rhs))); - } + (Inline::Bool(lhs), Inline::Bool(rhs)) => Inline::Bool(bool_op(*lhs, *rhs)), (lhs, rhs) => { return err(VmErrorKind::UnsupportedBinaryOperation { op, @@ -1324,6 +1308,7 @@ impl Vm { } }; + vm_try!(out.store(&mut self.stack, inline)); VmResult::Ok(()) } @@ -1582,23 +1567,39 @@ impl Vm { rhs: InstAddress, out: Output, ) -> VmResult<()> { - let rhs = vm_try!(self.stack.at(rhs)).clone(); - let lhs = vm_try!(self.stack.at(lhs)).clone(); + let rhs = vm_try!(self.stack.at(rhs)); + let lhs = vm_try!(self.stack.at(lhs)); - if let (Some(lhs), Some(rhs)) = (vm_try!(lhs.as_inline()), vm_try!(rhs.as_inline())) { - match (lhs, rhs) { - (Inline::Integer(lhs), Inline::Integer(rhs)) => { - let value = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); - vm_try!(out.store(&mut self.stack, value)); - return VmResult::Ok(()); + 'fallback: { + let inline = match (vm_try!(lhs.value_ref()), vm_try!(rhs.value_ref())) { + (ValueRef::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { + (Inline::Integer(lhs), Inline::Integer(rhs)) => { + Inline::Integer(vm_try!(integer_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(), + }); + } + }, + (ValueRef::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: vm_try!(rhs.type_info()), + }); } - (Inline::Float(lhs), Inline::Float(rhs)) => { - vm_try!(out.store(&mut self.stack, float_op(*lhs, *rhs))); - return VmResult::Ok(()); + _ => { + break 'fallback; } - _ => {} - } - }; + }; + + vm_try!(out.store(&mut self.stack, inline)); + return VmResult::Ok(()); + } let lhs = lhs.clone(); let rhs = rhs.clone(); @@ -1630,41 +1631,44 @@ impl Vm { rhs: InstAddress, out: Output, ) -> VmResult<()> { - let lhs = vm_try!(self.stack.at(lhs)).clone(); - let rhs = vm_try!(self.stack.at(rhs)).clone(); + let lhs = vm_try!(self.stack.at(lhs)); + let rhs = vm_try!(self.stack.at(rhs)); - match (vm_try!(lhs.value_ref()), vm_try!(rhs.value_ref())) { - (ValueRef::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { - (Inline::Integer(lhs), Inline::Integer(rhs)) => { - vm_try!(out.store(&mut self.stack, integer_op(*lhs, *rhs))); - return VmResult::Ok(()); - } - (Inline::Byte(lhs), Inline::Byte(rhs)) => { - vm_try!(out.store(&mut self.stack, byte_op(*lhs, *rhs))); - return VmResult::Ok(()); - } - (Inline::Bool(lhs), Inline::Bool(rhs)) => { - vm_try!(out.store(&mut self.stack, bool_op(*lhs, *rhs))); - return VmResult::Ok(()); - } - (lhs, rhs) => { + 'fallback: { + let inline = match (vm_try!(lhs.value_ref()), vm_try!(rhs.value_ref())) { + (ValueRef::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { + (Inline::Integer(lhs), Inline::Integer(rhs)) => { + Inline::Integer(integer_op(*lhs, *rhs)) + } + (Inline::Byte(lhs), Inline::Byte(rhs)) => Inline::Byte(byte_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(), + }); + } + }, + (ValueRef::Inline(lhs), rhs) => { return err(VmErrorKind::UnsupportedBinaryOperation { op: protocol.name, lhs: lhs.type_info(), - rhs: rhs.type_info(), + rhs: vm_try!(rhs.type_info()), }); } - }, - (ValueRef::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: vm_try!(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) = @@ -1773,40 +1777,69 @@ impl Vm { rhs: InstAddress, out: Output, ) -> VmResult<()> { - let rhs = vm_try!(self.stack.at(rhs)).clone(); - let lhs = vm_try!(self.stack.at_mut(lhs)); + let (lhs, rhs) = 'fallback: { + let inline = 'inline: { + match vm_try!(self.stack.pair(lhs, rhs)) { + Pair::Same(value) => { + if let ValueMut::Inline(lhs) = vm_try!(value.value_mut()) { + match lhs { + Inline::Integer(value) => { + break 'inline Inline::Integer(vm_try!(integer_op( + *value, *value + ) + .ok_or_else(error))); + } + value => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: value.type_info(), + rhs: value.type_info(), + }); + } + } + }; - match (vm_try!(lhs.value_mut()), vm_try!(rhs.value_ref())) { - (ValueMut::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { - (Inline::Integer(lhs), Inline::Integer(rhs)) => { - let integer = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); - vm_try!(out.store(&mut self.stack, integer)); - return VmResult::Ok(()); - } - (Inline::Byte(lhs), Inline::Integer(rhs)) => { - let byte = vm_try!(byte_op(*lhs, *rhs).ok_or_else(error)); - vm_try!(out.store(&mut self.stack, byte)); - return VmResult::Ok(()); - } - (lhs, rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }); + break 'fallback (value.clone(), value.clone()); + } + Pair::Pair(lhs, rhs) => { + match (vm_try!(lhs.value_mut()), vm_try!(rhs.value_ref())) { + (ValueMut::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { + (Inline::Integer(lhs), Inline::Integer(rhs)) => { + break 'inline Inline::Integer(vm_try!( + integer_op(*lhs, *rhs).ok_or_else(error) + )); + } + (Inline::Byte(lhs), Inline::Integer(rhs)) => { + break 'inline Inline::Byte(vm_try!( + byte_op(*lhs, *rhs).ok_or_else(error) + )); + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + (ValueMut::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: vm_try!(rhs.type_info()), + }); + } + _ => {} + } + + break 'fallback (lhs.clone(), rhs.clone()); + } } - }, - (ValueMut::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: protocol.name, - lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), - }); - } - _ => {} - } + }; - let lhs = lhs.clone(); + vm_try!(out.store(&mut self.stack, inline)); + return VmResult::Ok(()); + }; let mut args = DynGuardedArgs::new((&rhs,)); @@ -1935,8 +1968,7 @@ impl Vm { /// top of the stack. #[cfg_attr(feature = "bench", inline(never))] fn op_copy(&mut self, addr: InstAddress, out: Output) -> VmResult<()> { - let value = vm_try!(self.stack.at(addr)).clone(); - vm_try!(out.store(&mut self.stack, value)); + vm_try!(self.stack.copy(addr, out)); VmResult::Ok(()) } @@ -2054,7 +2086,7 @@ impl Vm { let out = vm_try!(self.stack.slice_at_mut(addr, count)); for (value, out) in tuple.iter().zip(out.iter_mut()) { - *out = value.clone(); + out.clone_from(value); } } @@ -2264,15 +2296,35 @@ impl Vm { )); } InstOp::Eq => { - let rhs = vm_try!(self.stack.at(rhs)).clone(); - let lhs = vm_try!(self.stack.at(lhs)).clone(); - let test = vm_try!(Value::partial_eq_with(&lhs, &rhs, self)); + let rhs = vm_try!(self.stack.at(rhs)); + let lhs = vm_try!(self.stack.at(lhs)); + + let test = if let (Some(lhs), Some(rhs)) = + (vm_try!(lhs.as_inline()), vm_try!(rhs.as_inline())) + { + vm_try!(lhs.partial_eq(rhs)) + } else { + let lhs = lhs.clone(); + let rhs = rhs.clone(); + vm_try!(Value::partial_eq_with(&lhs, &rhs, self)) + }; + vm_try!(out.store(&mut self.stack, test)); } InstOp::Neq => { - let rhs = vm_try!(self.stack.at(rhs)).clone(); - let lhs = vm_try!(self.stack.at(lhs)).clone(); - let test = vm_try!(Value::partial_eq_with(&lhs, &rhs, self)); + let rhs = vm_try!(self.stack.at(rhs)); + let lhs = vm_try!(self.stack.at(lhs)); + + let test = if let (Some(lhs), Some(rhs)) = + (vm_try!(lhs.as_inline()), vm_try!(rhs.as_inline())) + { + vm_try!(lhs.partial_eq(rhs)) + } else { + let lhs = lhs.clone(); + let rhs = rhs.clone(); + vm_try!(Value::partial_eq_with(&lhs, &rhs, self)) + }; + vm_try!(out.store(&mut self.stack, !test)); } InstOp::And => { @@ -2455,7 +2507,7 @@ impl Vm { }); }; - *v = value.clone(); + v.clone_from(value); } Mutable::Variant(variant) => { let VariantData::Struct(st) = variant.data_mut() else { @@ -2472,7 +2524,7 @@ impl Vm { }); }; - *v = value.clone(); + v.clone_from(value); } _ => { break 'fallback; @@ -2683,8 +2735,8 @@ impl Vm { index: usize, value: InstAddress, ) -> VmResult<()> { - let value = vm_try!(self.stack.at(value)).clone(); - let target = vm_try!(self.stack.at_mut(target)); + let value = vm_try!(self.stack.at(value)); + let target = vm_try!(self.stack.at(target)); if vm_try!(Self::try_tuple_like_index_set(target, index, value)) { return VmResult::Ok(()); @@ -2732,16 +2784,29 @@ impl Vm { slot: usize, value: InstAddress, ) -> VmResult<()> { - let target = vm_try!(self.stack.at(target)).clone(); - let value = vm_try!(self.stack.at(value)).clone(); + let target = vm_try!(self.stack.at(target)); + let value = vm_try!(self.stack.at(value)); + let field = vm_try!(self.unit.lookup_string(slot)); - if let CallResult::Unsupported(target) = - vm_try!(self.try_object_slot_index_set(target, slot, value)) - { + if vm_try!(Self::try_object_slot_index_set(target, field, value)) { + return VmResult::Ok(()); + } + + let target = target.clone(); + let value = value.clone(); + + let hash = field.hash(); + + let mut args = DynGuardedArgs::new((value,)); + + let result = + vm_try!(self.call_field_fn(Protocol::SET, target, hash, &mut args, Output::discard())); + + if let CallResult::Unsupported(target) = result { return err(VmErrorKind::UnsupportedObjectSlotIndexSet { target: vm_try!(target.type_info()), }); - } + }; VmResult::Ok(()) } @@ -3495,17 +3560,26 @@ impl Vm { args: usize, out: Output, ) -> VmResult> { - let function = vm_try!(self.stack.at(function)).clone(); + let function = vm_try!(self.stack.at(function)); - let ty = match vm_try!(function.borrow_ref()) { - ValueBorrowRef::Inline(value) => match value { + if let Some(value) = vm_try!(function.as_inline()) { + let ty = match value { Inline::Type(ty) => *ty, actual => { return err(VmErrorKind::UnsupportedCallFn { actual: actual.type_info(), }); } - }, + }; + + vm_try!(self.op_call(ty.into_hash(), addr, args, out)); + return VmResult::Ok(None); + } + + let function = function.clone(); + let function = vm_try!(function.borrow_ref()); + + match function { ValueBorrowRef::Mutable(value) => match &*value { Mutable::Function(function) => { return function.call_with_vm(self, addr, args, out); @@ -3516,10 +3590,12 @@ impl Vm { }); } }, - }; - - vm_try!(self.op_call(ty.into_hash(), addr, args, out)); - VmResult::Ok(None) + actual => { + return err(VmErrorKind::UnsupportedCallFn { + actual: actual.type_info(), + }); + } + } } #[cfg_attr(feature = "bench", inline(never))]