From 0ebd4e0719796e9a8ea5a012d5e18cbfdaa6d876 Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Thu, 23 Jun 2022 21:55:42 +0200 Subject: [PATCH] refactor number methods as atomic function --- vm/src/builtins/float.rs | 62 +++++++--- vm/src/builtins/int.rs | 72 +++++++---- vm/src/protocol/number.rs | 243 +++++++++++++++++--------------------- 3 files changed, 200 insertions(+), 177 deletions(-) diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs index 9758f56dc..d006688dc 100644 --- a/vm/src/builtins/float.rs +++ b/vm/src/builtins/float.rs @@ -16,6 +16,7 @@ use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, TryFromObject, VirtualMachine, }; +use crossbeam_utils::atomic::AtomicCell; use num_bigint::{BigInt, ToBigInt}; use num_complex::Complex64; use num_rational::Ratio; @@ -552,34 +553,57 @@ impl Hashable for PyFloat { } } +macro_rules! atomic_func { + ($x:expr) => { + AtomicCell::new(Some($x)) + }; +} + impl AsNumber for PyFloat { const AS_NUMBER: PyNumberMethods = PyNumberMethods { - add: Some(|number, other, vm| Self::number_float_op(number, other, |a, b| a + b, vm)), - subtract: Some(|number, other, vm| Self::number_float_op(number, other, |a, b| a - b, vm)), - multiply: Some(|number, other, vm| Self::number_float_op(number, other, |a, b| a * b, vm)), - remainder: Some(|number, other, vm| Self::number_general_op(number, other, inner_mod, vm)), - divmod: Some(|number, other, vm| Self::number_general_op(number, other, inner_divmod, vm)), - power: Some(|number, other, vm| Self::number_general_op(number, other, float_pow, vm)), - negative: Some(|number, vm| { - let value = Self::number_downcast(number).value; + add: atomic_func!(|num, other, vm| Self::number_float_op(num, other, |a, b| a + b, vm)), + subtract: atomic_func!(|num, other, vm| Self::number_float_op( + num, + other, + |a, b| a - b, + vm + )), + multiply: atomic_func!(|num, other, vm| Self::number_float_op( + num, + other, + |a, b| a * b, + vm + )), + remainder: atomic_func!(|num, other, vm| Self::number_general_op( + num, other, inner_mod, vm + )), + divmod: atomic_func!(|num, other, vm| Self::number_general_op( + num, + other, + inner_divmod, + vm + )), + power: atomic_func!(|num, other, vm| Self::number_general_op(num, other, float_pow, vm)), + negative: atomic_func!(|num, vm| { + let value = Self::number_downcast(num).value; (-value).to_pyresult(vm) }), - positive: Some(|number, vm| Self::number_float(number, vm).to_pyresult(vm)), - absolute: Some(|number, vm| { - let value = Self::number_downcast(number).value; + positive: atomic_func!(|num, vm| Self::number_float(num, vm).to_pyresult(vm)), + absolute: atomic_func!(|num, vm| { + let value = Self::number_downcast(num).value; value.abs().to_pyresult(vm) }), - boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())), - int: Some(|number, vm| { - let value = Self::number_downcast(number).value; + boolean: atomic_func!(|num, _vm| Ok(Self::number_downcast(num).value.is_zero())), + int: atomic_func!(|num, vm| { + let value = Self::number_downcast(num).value; try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x)) }), - float: Some(|number, vm| Ok(Self::number_float(number, vm))), - floor_divide: Some(|number, other, vm| { - Self::number_general_op(number, other, inner_floordiv, vm) + float: atomic_func!(|num, vm| Ok(Self::number_float(num, vm))), + floor_divide: atomic_func!(|num, other, vm| { + Self::number_general_op(num, other, inner_floordiv, vm) }), - true_divide: Some(|number, other, vm| { - Self::number_general_op(number, other, inner_div, vm) + true_divide: atomic_func!(|num, other, vm| { + Self::number_general_op(num, other, inner_div, vm) }), ..PyNumberMethods::NOT_IMPLEMENTED }; diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs index 3d64af457..dc8695f3d 100644 --- a/vm/src/builtins/int.rs +++ b/vm/src/builtins/int.rs @@ -15,6 +15,7 @@ use crate::{ TryFromBorrowedObject, VirtualMachine, }; use bstr::ByteSlice; +use crossbeam_utils::atomic::AtomicCell; use num_bigint::{BigInt, BigUint, Sign}; use num_integer::Integer; use num_traits::{One, Pow, PrimInt, Signed, ToPrimitive, Zero}; @@ -755,36 +756,59 @@ impl Hashable for PyInt { } } +macro_rules! atomic_func { + ($x:expr) => { + AtomicCell::new(Some($x)) + }; +} + impl AsNumber for PyInt { const AS_NUMBER: PyNumberMethods = PyNumberMethods { - add: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a + b, vm)), - subtract: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a - b, vm)), - multiply: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a * b, vm)), - remainder: Some(|number, other, vm| Self::number_general_op(number, other, inner_mod, vm)), - divmod: Some(|number, other, vm| Self::number_general_op(number, other, inner_divmod, vm)), - power: Some(|number, other, vm| Self::number_general_op(number, other, inner_pow, vm)), - negative: Some(|number, vm| (&Self::number_downcast(number).value).neg().to_pyresult(vm)), - positive: Some(|number, vm| Ok(Self::number_int(number, vm).into())), - absolute: Some(|number, vm| Self::number_downcast(number).value.abs().to_pyresult(vm)), - boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())), - invert: Some(|number, vm| (&Self::number_downcast(number).value).not().to_pyresult(vm)), - lshift: Some(|number, other, vm| Self::number_general_op(number, other, inner_lshift, vm)), - rshift: Some(|number, other, vm| Self::number_general_op(number, other, inner_rshift, vm)), - and: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a & b, vm)), - xor: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a ^ b, vm)), - or: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a | b, vm)), - int: Some(|number, other| Ok(Self::number_int(number, other))), - float: Some(|number, vm| { - let zelf = Self::number_downcast(number); + add: atomic_func!(|num, other, vm| Self::number_int_op(num, other, |a, b| a + b, vm)), + subtract: atomic_func!(|num, other, vm| Self::number_int_op(num, other, |a, b| a - b, vm)), + multiply: atomic_func!(|num, other, vm| Self::number_int_op(num, other, |a, b| a * b, vm)), + remainder: atomic_func!(|num, other, vm| Self::number_general_op( + num, other, inner_mod, vm + )), + divmod: atomic_func!(|num, other, vm| Self::number_general_op( + num, + other, + inner_divmod, + vm + )), + power: atomic_func!(|num, other, vm| Self::number_general_op(num, other, inner_pow, vm)), + negative: atomic_func!(|num, vm| (&Self::number_downcast(num).value).neg().to_pyresult(vm)), + positive: atomic_func!(|num, vm| Ok(Self::number_int(num, vm).into())), + absolute: atomic_func!(|num, vm| Self::number_downcast(num).value.abs().to_pyresult(vm)), + boolean: atomic_func!(|num, _vm| Ok(Self::number_downcast(num).value.is_zero())), + invert: atomic_func!(|num, vm| (&Self::number_downcast(num).value).not().to_pyresult(vm)), + lshift: atomic_func!(|num, other, vm| Self::number_general_op( + num, + other, + inner_lshift, + vm + )), + rshift: atomic_func!(|num, other, vm| Self::number_general_op( + num, + other, + inner_rshift, + vm + )), + and: atomic_func!(|num, other, vm| Self::number_int_op(num, other, |a, b| a & b, vm)), + xor: atomic_func!(|num, other, vm| Self::number_int_op(num, other, |a, b| a ^ b, vm)), + or: atomic_func!(|num, other, vm| Self::number_int_op(num, other, |a, b| a | b, vm)), + int: atomic_func!(|num, other| Ok(Self::number_int(num, other))), + float: atomic_func!(|num, vm| { + let zelf = Self::number_downcast(num); try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x)) }), - floor_divide: Some(|number, other, vm| { - Self::number_general_op(number, other, inner_floordiv, vm) + floor_divide: atomic_func!(|num, other, vm| { + Self::number_general_op(num, other, inner_floordiv, vm) }), - true_divide: Some(|number, other, vm| { - Self::number_general_op(number, other, inner_truediv, vm) + true_divide: atomic_func!(|num, other, vm| { + Self::number_general_op(num, other, inner_truediv, vm) }), - index: Some(|number, vm| Ok(Self::number_int(number, vm))), + index: atomic_func!(|num, vm| Ok(Self::number_int(num, vm))), ..PyNumberMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index e8f1e9041..944126f54 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -1,148 +1,123 @@ +use crossbeam_utils::atomic::AtomicCell; + use crate::{ builtins::{int, PyByteArray, PyBytes, PyComplex, PyFloat, PyInt, PyIntRef, PyStr}, function::ArgBytesLike, stdlib::warnings, - AsObject, PyObject, PyPayload, PyRef, PyResult, TryFromBorrowedObject, VirtualMachine, + AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, + VirtualMachine, }; -#[allow(clippy::type_complexity)] -#[derive(Clone)] +type UnaryFunc = AtomicCell PyResult>>; +type BinaryFunc = + AtomicCell PyResult>>; + pub struct PyNumberMethods { /* Number implementations must check *both* arguments for proper type and implement the necessary conversions in the slot functions themselves. */ - pub add: Option PyResult>, - pub subtract: Option PyResult>, - pub multiply: Option PyResult>, - pub remainder: Option PyResult>, - pub divmod: Option PyResult>, - pub power: Option PyResult>, - pub negative: Option PyResult>, - pub positive: Option PyResult>, - pub absolute: Option PyResult>, - pub boolean: Option PyResult>, - pub invert: Option PyResult>, - pub lshift: Option PyResult>, - pub rshift: Option PyResult>, - pub and: Option PyResult>, - pub xor: Option PyResult>, - pub or: Option PyResult>, - pub int: Option PyResult>, - pub float: Option PyResult>>, + pub add: BinaryFunc, + pub subtract: BinaryFunc, + pub multiply: BinaryFunc, + pub remainder: BinaryFunc, + pub divmod: BinaryFunc, + pub power: BinaryFunc, + pub negative: UnaryFunc, + pub positive: UnaryFunc, + pub absolute: UnaryFunc, + pub boolean: UnaryFunc, + pub invert: UnaryFunc, + pub lshift: BinaryFunc, + pub rshift: BinaryFunc, + pub and: BinaryFunc, + pub xor: BinaryFunc, + pub or: BinaryFunc, + pub int: UnaryFunc>, + pub float: UnaryFunc>, - pub inplace_add: Option PyResult>, - pub inplace_subtract: Option PyResult>, - pub inplace_multiply: Option PyResult>, - pub inplace_remainder: Option PyResult>, - pub inplace_divmod: Option PyResult>, - pub inplace_power: Option PyResult>, - pub inplace_lshift: Option PyResult>, - pub inplace_rshift: Option PyResult>, - pub inplace_and: Option PyResult>, - pub inplace_xor: Option PyResult>, - pub inplace_or: Option PyResult>, + pub inplace_add: BinaryFunc, + pub inplace_subtract: BinaryFunc, + pub inplace_multiply: BinaryFunc, + pub inplace_remainder: BinaryFunc, + pub inplace_divmod: BinaryFunc, + pub inplace_power: BinaryFunc, + pub inplace_lshift: BinaryFunc, + pub inplace_rshift: BinaryFunc, + pub inplace_and: BinaryFunc, + pub inplace_xor: BinaryFunc, + pub inplace_or: BinaryFunc, - pub floor_divide: Option PyResult>, - pub true_divide: Option PyResult>, - pub inplace_floor_divide: Option PyResult>, - pub inplace_true_divide: Option PyResult>, + pub floor_divide: BinaryFunc, + pub true_divide: BinaryFunc, + pub inplace_floor_divide: BinaryFunc, + pub inplace_true_divide: BinaryFunc, - pub index: Option PyResult>, + pub index: UnaryFunc>, - pub matrix_multiply: Option PyResult>, - pub inplace_matrix_multiply: Option PyResult>, + pub matrix_multiply: BinaryFunc, + pub inplace_matrix_multiply: BinaryFunc, } impl PyNumberMethods { pub const NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods { - add: None, - subtract: None, - multiply: None, - remainder: None, - divmod: None, - power: None, - negative: None, - positive: None, - absolute: None, - boolean: None, - invert: None, - lshift: None, - rshift: None, - and: None, - xor: None, - or: None, - int: None, - float: None, - inplace_add: None, - inplace_subtract: None, - inplace_multiply: None, - inplace_remainder: None, - inplace_divmod: None, - inplace_power: None, - inplace_lshift: None, - inplace_rshift: None, - inplace_and: None, - inplace_xor: None, - inplace_or: None, - floor_divide: None, - true_divide: None, - inplace_floor_divide: None, - inplace_true_divide: None, - index: None, - matrix_multiply: None, - inplace_matrix_multiply: None, + add: AtomicCell::new(None), + subtract: AtomicCell::new(None), + multiply: AtomicCell::new(None), + remainder: AtomicCell::new(None), + divmod: AtomicCell::new(None), + power: AtomicCell::new(None), + negative: AtomicCell::new(None), + positive: AtomicCell::new(None), + absolute: AtomicCell::new(None), + boolean: AtomicCell::new(None), + invert: AtomicCell::new(None), + lshift: AtomicCell::new(None), + rshift: AtomicCell::new(None), + and: AtomicCell::new(None), + xor: AtomicCell::new(None), + or: AtomicCell::new(None), + int: AtomicCell::new(None), + float: AtomicCell::new(None), + inplace_add: AtomicCell::new(None), + inplace_subtract: AtomicCell::new(None), + inplace_multiply: AtomicCell::new(None), + inplace_remainder: AtomicCell::new(None), + inplace_divmod: AtomicCell::new(None), + inplace_power: AtomicCell::new(None), + inplace_lshift: AtomicCell::new(None), + inplace_rshift: AtomicCell::new(None), + inplace_and: AtomicCell::new(None), + inplace_xor: AtomicCell::new(None), + inplace_or: AtomicCell::new(None), + floor_divide: AtomicCell::new(None), + true_divide: AtomicCell::new(None), + inplace_floor_divide: AtomicCell::new(None), + inplace_true_divide: AtomicCell::new(None), + index: AtomicCell::new(None), + matrix_multiply: AtomicCell::new(None), + inplace_matrix_multiply: AtomicCell::new(None), }; - pub(crate) fn generic( - has_int: bool, - has_float: bool, - has_index: bool, - ) -> &'static PyNumberMethods { - static METHODS: &[PyNumberMethods] = &[ - new_generic(false, false, false), - new_generic(true, false, false), - new_generic(false, true, false), - new_generic(true, true, false), - new_generic(false, false, true), - new_generic(true, false, true), - new_generic(false, true, true), - new_generic(true, true, true), - ]; - - fn int(num: &PyNumber, vm: &VirtualMachine) -> PyResult> { - let ret = vm.call_special_method(num.obj.to_owned(), identifier!(vm, __int__), ())?; - ret.downcast::().map_err(|obj| { - vm.new_type_error(format!("__int__ returned non-int (type {})", obj.class())) - }) - } - fn float(num: &PyNumber, vm: &VirtualMachine) -> PyResult> { - let ret = vm.call_special_method(num.obj.to_owned(), identifier!(vm, __float__), ())?; - ret.downcast::().map_err(|obj| { - vm.new_type_error(format!( - "__float__ returned non-float (type {})", - obj.class() - )) - }) - } - fn index(num: &PyNumber, vm: &VirtualMachine) -> PyResult> { - let ret = vm.call_special_method(num.obj.to_owned(), identifier!(vm, __index__), ())?; - ret.downcast::().map_err(|obj| { - vm.new_type_error(format!("__index__ returned non-int (type {})", obj.class())) - }) - } - - const fn new_generic(has_int: bool, has_float: bool, has_index: bool) -> PyNumberMethods { - PyNumberMethods { - int: if has_int { Some(int) } else { None }, - float: if has_float { Some(float) } else { None }, - index: if has_index { Some(index) } else { None }, - ..PyNumberMethods::NOT_IMPLEMENTED - } - } - - let key = (has_int as usize) | ((has_float as usize) << 1) | ((has_index as usize) << 2); - - &METHODS[key] + fn int(num: &PyNumber, vm: &VirtualMachine) -> PyResult> { + let ret = vm.call_special_method(num.obj.to_owned(), identifier!(vm, __int__), ())?; + ret.downcast::().map_err(|obj| { + vm.new_type_error(format!("__int__ returned non-int (type {})", obj.class())) + }) + } + fn float(num: &PyNumber, vm: &VirtualMachine) -> PyResult> { + let ret = vm.call_special_method(num.obj.to_owned(), identifier!(vm, __float__), ())?; + ret.downcast::().map_err(|obj| { + vm.new_type_error(format!( + "__float__ returned non-float (type {})", + obj.class() + )) + }) + } + fn index(num: &PyNumber, vm: &VirtualMachine) -> PyResult> { + let ret = vm.call_special_method(num.obj.to_owned(), identifier!(vm, __index__), ())?; + ret.downcast::().map_err(|obj| { + vm.new_type_error(format!("__index__ returned non-int (type {})", obj.class())) + }) } } @@ -174,16 +149,16 @@ impl PyNumber<'_> { // PyNumber_Check pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool { Self::find_methods(obj, vm).map_or(false, |methods| { - methods.int.is_some() - || methods.index.is_some() - || methods.float.is_some() + methods.int.load().is_some() + || methods.index.load().is_some() + || methods.float.load().is_some() || obj.payload_is::() }) } // PyIndex_Check pub fn is_index(&self) -> bool { - self.methods().index.is_some() + self.methods().index.load().is_some() } pub fn int(&self, vm: &VirtualMachine) -> PyResult { @@ -201,7 +176,7 @@ impl PyNumber<'_> { if let Some(i) = self.obj.downcast_ref_if_exact::(vm) { Ok(i.to_owned()) - } else if let Some(f) = self.methods().int { + } else if let Some(f) = self.methods().int.load() { let ret = f(self, vm)?; if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -219,7 +194,7 @@ impl PyNumber<'_> { } else { Ok(ret) } - } else if self.methods().index.is_some() { + } else if self.methods().index.load().is_some() { self.index(vm) } else if let Ok(Ok(f)) = vm.get_special_method(self.obj.to_owned(), identifier!(vm, __trunc__)) @@ -260,7 +235,7 @@ impl PyNumber<'_> { Ok(Some(i.to_owned())) } else if let Some(i) = self.obj.payload::() { Ok(Some(vm.ctx.new_bigint(i.as_bigint()))) - } else if let Some(f) = self.methods().index { + } else if let Some(f) = self.methods().index.load() { let ret = f(self, vm)?; if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -295,7 +270,7 @@ impl PyNumber<'_> { pub fn float_opt(&self, vm: &VirtualMachine) -> PyResult>> { if let Some(float) = self.obj.downcast_ref_if_exact::(vm) { Ok(Some(float.to_owned())) - } else if let Some(f) = self.methods().float { + } else if let Some(f) = self.methods().float.load() { let ret = f(self, vm)?; if !ret.class().is(PyFloat::class(vm)) { warnings::warn( @@ -313,7 +288,7 @@ impl PyNumber<'_> { } else { Ok(Some(ret)) } - } else if self.methods().index.is_some() { + } else if self.methods().index.load().is_some() { let i = self.index(vm)?; let value = int::try_to_float(i.as_bigint(), vm)?; Ok(Some(vm.ctx.new_float(value)))