diff --git a/include/pocketpy/common.h b/include/pocketpy/common.h index 9567e753..38b6cd23 100644 --- a/include/pocketpy/common.h +++ b/include/pocketpy/common.h @@ -76,10 +76,6 @@ struct NumberTraits<4> { template static float_t stof(Args&&... args) { return std::stof(std::forward(args)...); } - - static constexpr int_t c0 = 0b00000000011111111111111111111100; - static constexpr int_t c1 = 0b11111111111111111111111111111100; - static constexpr int_t c2 = 0b00000000000000000000000000000011; }; template <> @@ -89,18 +85,59 @@ struct NumberTraits<8> { template static float_t stof(Args&&... args) { return std::stod(std::forward(args)...); } - - static constexpr int_t c0 = 0b0000000000001111111111111111111111111111111111111111111111111100; - static constexpr int_t c1 = 0b1111111111111111111111111111111111111111111111111111111111111100; - static constexpr int_t c2 = 0b0000000000000000000000000000000000000000000000000000000000000011; }; using Number = NumberTraits; -using i64 = int64_t; +using i64 = int64_t; // always 64-bit using f64 = Number::float_t; +template +union BitsCvtImpl; + +template<> +union BitsCvtImpl<4>{ + NumberTraits<4>::int_t _int; + NumberTraits<4>::float_t _float; + + struct{ + unsigned int sign: 1; + unsigned int exp: 8; + uint64_t mantissa: 23; + } _float_bits; + + static constexpr int C0 = 127; // 2^7 - 1 + static constexpr int C1 = -62; // 2 - 2^6 + static constexpr int C2 = 63; // 2^6 - 1 + + BitsCvtImpl(NumberTraits<4>::float_t val): _float(val) {} + BitsCvtImpl(NumberTraits<4>::int_t val): _int(val) {} +}; + +template<> +union BitsCvtImpl<8>{ + NumberTraits<8>::int_t _int; + NumberTraits<8>::float_t _float; + + struct{ + unsigned int sign: 1; + unsigned int exp: 11; + uint64_t mantissa: 52; + } _float_bits; + + static constexpr int C0 = 1023; // 2^10 - 1 + static constexpr int C1 = -510; // 2 - 2^9 + static constexpr int C2 = 511; // 2^9 - 1 + + BitsCvtImpl(NumberTraits<8>::float_t val): _float(val) {} + BitsCvtImpl(NumberTraits<8>::int_t val): _int(val) {} +}; + +using BitsCvt = BitsCvtImpl; + static_assert(sizeof(i64) == 8); -static_assert(sizeof(f64) == sizeof(void*)); +static_assert(sizeof(Number::float_t) == sizeof(void*)); +static_assert(sizeof(Number::int_t) == sizeof(void*)); +static_assert(sizeof(BitsCvt) == sizeof(void*)); static_assert(std::numeric_limits::is_iec559); struct Dummy { }; @@ -133,10 +170,33 @@ struct Type { struct PyObject; #define PK_BITS(p) (reinterpret_cast(p)) -// special singals, is_tagged() for them is true -inline PyObject* const PY_NULL = (PyObject*)0b000011; // tagged null -inline PyObject* const PY_OP_CALL = (PyObject*)0b100011; -inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011; +inline PyObject* tag_float(f64 val){ + BitsCvt decomposed(val); + unsigned int exp_7b = decomposed._float_bits.exp; + if(exp_7b - BitsCvt::C0 < BitsCvt::C1){ + exp_7b = 0; + decomposed._float_bits.mantissa = 0; + }else if(exp_7b - BitsCvt::C0 > BitsCvt::C2){ + exp_7b = BitsCvt::C0; + if(!std::isnan(val)) decomposed._float_bits.mantissa = 0; + } + decomposed._float_bits.exp = exp_7b - BitsCvt::C0 + BitsCvt::C2; + decomposed._int = (decomposed._int << 1) | 0b01; + return reinterpret_cast(decomposed._int); +} + +inline f64 untag_float(PyObject* val){ + BitsCvt decomposed(reinterpret_cast(val)); + decomposed._int >>= 1; + unsigned int exp_7b = decomposed._float_bits.exp; + if(exp_7b == 0) return 0.0f; + if(exp_7b == BitsCvt::C0){ + decomposed._float_bits.exp = -1; + return decomposed._float; + } + decomposed._float_bits.exp = exp_7b - BitsCvt::C2 + BitsCvt::C0; + return decomposed._float; +} // is_pod<> for c++17 and c++20 template diff --git a/include/pocketpy/obj.h b/include/pocketpy/obj.h index a4fdb50a..1ffaf9fb 100644 --- a/include/pocketpy/obj.h +++ b/include/pocketpy/obj.h @@ -123,20 +123,29 @@ struct PyObject{ } }; +struct PySignalObject: PyObject { + PySignalObject() : PyObject(0) { + gc.enabled = false; + } + void _obj_gc_mark() override {} +}; + +inline PyObject* const PY_NULL = new PySignalObject(); +inline PyObject* const PY_OP_CALL = new PySignalObject(); +inline PyObject* const PY_OP_YIELD = new PySignalObject(); + const int kTpIntIndex = 2; const int kTpFloatIndex = 3; inline bool is_tagged(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) != 0b00; } -inline bool is_small_int(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b01; } +inline bool is_small_int(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b10; } inline bool is_heap_int(PyObject* p) noexcept { return !is_tagged(p) && p->type.index == kTpIntIndex; } -inline bool is_float(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b10; } -inline bool is_special(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b11; } +inline bool is_float(PyObject* p) noexcept { return (PK_BITS(p) & 1) == 1; } // 01 or 11 inline bool is_int(PyObject* p) noexcept { return is_small_int(p) || is_heap_int(p); } inline bool is_type(PyObject* obj, Type type) { #if PK_DEBUG_EXTRA_CHECK if(obj == nullptr) throw std::runtime_error("is_type() called with nullptr"); - if(is_special(obj)) throw std::runtime_error("is_type() called with special object"); #endif switch(type.index){ case kTpIntIndex: return is_int(obj); @@ -148,7 +157,6 @@ inline bool is_type(PyObject* obj, Type type) { inline bool is_non_tagged_type(PyObject* obj, Type type) { #if PK_DEBUG_EXTRA_CHECK if(obj == nullptr) throw std::runtime_error("is_non_tagged_type() called with nullptr"); - if(is_special(obj)) throw std::runtime_error("is_non_tagged_type() called with special object"); #endif return !is_tagged(obj) && obj->type == type; } @@ -200,13 +208,6 @@ Str obj_type_name(VM* vm, Type type); #define OBJ_NAME(obj) PK_OBJ_GET(Str, vm->getattr(obj, __name__)) #endif -union BitsCvt { - Number::int_t _int; - f64 _float; - BitsCvt(Number::int_t val) : _int(val) {} - BitsCvt(f64 val) : _float(val) {} -}; - template struct is_py_class : std::false_type {}; template struct is_py_class> : std::true_type {}; diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 2df7425a..1d3ace54 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -505,7 +505,7 @@ PY_CAST_INT(unsigned long) PY_CAST_INT(unsigned long long) template<> inline float py_cast(VM* vm, PyObject* obj){ - if(is_float(obj)) return BitsCvt(PK_BITS(obj) & Number::c1)._float; + if(is_float(obj)) return untag_float(obj); i64 bits; if(try_cast_int(obj, &bits)) return (float)bits; vm->TypeError("expected 'int' or 'float', got " + OBJ_NAME(vm->_t(obj)).escape()); @@ -515,7 +515,7 @@ template<> inline float _py_cast(VM* vm, PyObject* obj){ return py_cast(vm, obj); } template<> inline double py_cast(VM* vm, PyObject* obj){ - if(is_float(obj)) return BitsCvt(PK_BITS(obj) & Number::c1)._float; + if(is_float(obj)) return untag_float(obj); i64 bits; if(try_cast_int(obj, &bits)) return (float)bits; vm->TypeError("expected 'int' or 'float', got " + OBJ_NAME(vm->_t(obj)).escape()); @@ -532,7 +532,7 @@ const i64 kMinSmallInt = -(1ll << 28); inline PyObject* py_var(VM* vm, T _val){ \ i64 val = static_cast(_val); \ if(val >= kMinSmallInt && val <= kMaxSmallInt){ \ - val = (val << 2) | 0b01; \ + val = (val << 2) | 0b10; \ return reinterpret_cast(val); \ }else{ \ return vm->heap.gcnew(vm->tp_int, val); \ @@ -554,16 +554,7 @@ PY_VAR_INT(unsigned long long) #define PY_VAR_FLOAT(T) \ inline PyObject* py_var(VM* vm, T _val){ \ PK_UNUSED(vm); \ - BitsCvt val(static_cast(_val)); \ - i64 bits = val._int & Number::c1; \ - i64 tail = val._int & Number::c2; \ - if(tail == 0b10){ \ - if(bits&0b100) bits += 0b100; \ - }else if(tail == 0b11){ \ - bits += 0b100; \ - } \ - bits |= 0b10; \ - return reinterpret_cast(bits); \ + return tag_float(static_cast(_val)); \ } PY_VAR_FLOAT(float)