Browse Source

tag pointers

blueloveTH 3 years ago
parent
commit
44be9e8bde
5 changed files with 172 additions and 162 deletions
  1. 1 1
      src/ceval.h
  2. 40 26
      src/memory.h
  3. 31 64
      src/obj.h
  4. 23 25
      src/pocketpy.h
  5. 77 46
      src/vm.h

+ 1 - 1
src/ceval.h

@@ -69,7 +69,7 @@ PyVar VM::run_frame(Frame* frame){
             pkpy::Args items = frame->pop_n_reversed(byte.arg);
             bool done = false;
             for(int i=0; i<items.size(); i++){
-                if(!items[i]->is_type(tp_ref)) {
+                if(!is_type(items[i], tp_ref)) {
                     done = true;
                     for(int j=i; j<items.size(); j++) frame->try_deref(this, items[j]);
                     frame->push(PyTuple(std::move(items)));

+ 40 - 26
src/memory.h

@@ -20,38 +20,38 @@ namespace pkpy{
     class shared_ptr {
         int* counter;
 
-#define _t() ((T*)(counter + 1))
-#define _inc_counter() if(counter) ++(*counter)
-#define _dec_counter() if(counter && --(*counter) == 0){ SpAllocator<T>::dealloc(counter); }
+        inline T* _t() const {
+            if(is_tagged()) UNREACHABLE();
+            return (T*)(counter + 1);
+        }
+        inline void _inc_counter() const { if(counter) ++(*counter); }
+        inline void _dec_counter() const { if(counter && --(*counter) == 0){ SpAllocator<T>::dealloc(counter); } }
 
     public:
         shared_ptr() : counter(nullptr) {}
         shared_ptr(int* counter) : counter(counter) {}
         shared_ptr(const shared_ptr& other) : counter(other.counter) {
-            _inc_counter();
+            if(!is_tagged()) _inc_counter();
         }
         shared_ptr(shared_ptr&& other) noexcept : counter(other.counter) {
             other.counter = nullptr;
         }
-        ~shared_ptr() { _dec_counter(); }
-
-        bool operator==(const shared_ptr& other) const {
-            return counter == other.counter;
-        }
+        ~shared_ptr() { if(!is_tagged()) _dec_counter(); }
 
-        bool operator!=(const shared_ptr& other) const {
-            return counter != other.counter;
-        }
-
-        bool operator==(std::nullptr_t) const {
-            return counter == nullptr;
-        }
-
-        bool operator!=(std::nullptr_t) const {
-            return counter != nullptr;
-        }
+        bool operator==(const shared_ptr& other) const { return counter == other.counter; }
+        bool operator!=(const shared_ptr& other) const { return counter != other.counter; }
+        bool operator<(const shared_ptr& other) const { return counter < other.counter; }
+        bool operator>(const shared_ptr& other) const { return counter > other.counter; }
+        bool operator<=(const shared_ptr& other) const { return counter <= other.counter; }
+        bool operator>=(const shared_ptr& other) const { return counter >= other.counter; }
+        bool operator==(std::nullptr_t) const { return counter == nullptr; }
+        bool operator!=(std::nullptr_t) const { return counter != nullptr; }
 
         shared_ptr& operator=(const shared_ptr& other) {
+            if(is_tagged()) {
+                counter = other.counter;
+                return *this;
+            }
             _dec_counter();
             counter = other.counter;
             _inc_counter();
@@ -59,6 +59,11 @@ namespace pkpy{
         }
 
         shared_ptr& operator=(shared_ptr&& other) noexcept {
+            if(is_tagged()) {
+                counter = other.counter;
+                other.counter = nullptr;
+                return *this;
+            }
             _dec_counter();
             counter = other.counter;
             other.counter = nullptr;
@@ -68,17 +73,26 @@ namespace pkpy{
         T& operator*() const { return *_t(); }
         T* operator->() const { return _t(); }
         T* get() const { return _t(); }
-        int use_count() const { return counter ? *counter : 0; }
+
+        int use_count() const { 
+            if(is_tagged()) return 1;
+            return counter ? *counter : 0;
+        }
 
         void reset(){
-            _dec_counter();
+            if(!is_tagged()) _dec_counter();
             counter = nullptr;
         }
-    };
 
-#undef _t
-#undef _inc_counter
-#undef _dec_counter
+        template <typename __VAL>
+        inline __VAL cast() const { return reinterpret_cast<__VAL>(counter); }
+
+        inline bool is_tagged() const { return (cast<std::uintptr_t>() & 0b11) != 0b00; }
+        inline bool is_tag_00() const { return (cast<std::uintptr_t>() & 0b11) == 0b00; }
+        inline bool is_tag_01() const { return (cast<std::uintptr_t>() & 0b11) == 0b01; }
+        inline bool is_tag_10() const { return (cast<std::uintptr_t>() & 0b11) == 0b10; }
+        inline bool is_tag_11() const { return (cast<std::uintptr_t>() & 0b11) == 0b11; }
+    };
 
     template <typename T, typename U, typename... Args>
     shared_ptr<T> make_shared(Args&&... args) {

+ 31 - 64
src/obj.h

@@ -79,17 +79,13 @@ public:
 struct PyObject {
     Type type;
     pkpy::NameDict* _attr;
-    // void* _tid;
-    const int _size;
 
     inline bool is_attr_valid() const noexcept { return _attr != nullptr; }
     inline pkpy::NameDict& attr() noexcept { return *_attr; }
     inline PyVar& attr(const Str& name) noexcept { return (*_attr)[name]; }
-
-    inline bool is_type(Type type) const noexcept{ return this->type == type; }
     virtual void* value() = 0;
 
-    PyObject(Type type, const int size) : type(type), _size(size) {}
+    PyObject(Type type) : type(type) {}
     virtual ~PyObject() { delete _attr; }
 };
 
@@ -97,8 +93,8 @@ template <typename T>
 struct Py_ : PyObject {
     T _value;
 
-    Py_(Type type, const T& val): PyObject(type, sizeof(Py_<T>)), _value(val) { _init(); }
-    Py_(Type type, T&& val): PyObject(type, sizeof(Py_<T>)), _value(std::move(val)) { _init(); }
+    Py_(Type type, const T& val): PyObject(type), _value(val) { _init(); }
+    Py_(Type type, T&& val): PyObject(type), _value(std::move(val)) { _init(); }
 
     inline void _init() noexcept {
         if constexpr (std::is_same_v<T, Dummy> || std::is_same_v<T, Type>) {
@@ -113,66 +109,37 @@ struct Py_ : PyObject {
 #define OBJ_GET(T, obj) (((Py_<T>*)((obj).get()))->_value)
 #define OBJ_NAME(obj) OBJ_GET(Str, (obj)->attr(__name__))
 
-#define PY_CLASS(mod, name) \
-    inline static Type _type(VM* vm) { return OBJ_GET(Type, vm->_modules[#mod]->attr(#name)); } \
-    inline static const char* _mod() { return #mod; } \
-    inline static const char* _name() { return #name; }
-
+const int kTpIntIndex = 2;
+const int kTpFloatIndex = 3;
 
-namespace pkpy {
-    template<int N>
-    struct MemBlock {
-        std::vector<void*> a;
-        int block_size;
-
-        MemBlock(int block_size) : block_size(block_size) {
-            new_block();
-        }
+inline bool is_type(const PyVar& obj, Type type) noexcept {
+    switch(type.index){
+        case kTpIntIndex: return obj.is_tag_01();
+        case kTpFloatIndex: return obj.is_tag_10();
+        default: return obj->type == type;
+    }
+}
 
-        void new_block(){
-            int8_t* total = (int8_t*)malloc(N * block_size);
-            for(int i = 0; i < block_size; ++i){
-                a.push_back((void*)(total + i * N));
-            }
-        }
+inline bool is_int_or_float(const PyVar& obj) noexcept {
+    return obj.is_tag_01() || obj.is_tag_10();
+}
 
-        inline void* alloc(){
-            if(a.empty()) new_block();
-            void* p = a.back();
-            a.pop_back();
-            return p;
-        }
+inline bool is_int(const PyVar& obj) noexcept {
+    return obj.is_tag_01();
+}
 
-        inline void dealloc(void* p) noexcept{
-            a.push_back(p);
-        }
+inline bool is_float(const PyVar& obj) noexcept {
+    return obj.is_tag_10();
+}
 
-        ~MemBlock(){
-            free(a[0]);
-        }
-    };
-
-    constexpr int kMemObjSize = sizeof(int) + sizeof(Py_<i64>);
-    static THREAD_LOCAL MemBlock<kMemObjSize> _mem_pool(512);
-
-    template<>
-    struct SpAllocator<PyObject> {
-        template<typename U>
-        inline static int* alloc(){
-            if constexpr (sizeof(int) + sizeof(U) == kMemObjSize) {
-                return (int*)_mem_pool.alloc();
-            }
-            return (int*)malloc(sizeof(int) + sizeof(U));
-        }
+#define PY_CLASS(mod, name) \
+    inline static Type _type(VM* vm) { return OBJ_GET(Type, vm->_modules[#mod]->attr(#name)); } \
+    inline static const char* _mod() { return #mod; } \
+    inline static const char* _name() { return #name; }
 
-        inline static void dealloc(int* counter){
-            PyObject* obj = (PyObject*)(counter + 1);
-            obj->~PyObject();
-            if(obj->_size == kMemObjSize - sizeof(int)){
-                _mem_pool.dealloc(counter);
-            }else{
-                free(counter);
-            }
-        }
-    };
-}
+union __8B {
+    i64 _int;
+    f64 _float;
+    __8B(i64 val) : _int(val) {}
+    __8B(f64 val) : _float(val) {}
+};

+ 23 - 25
src/pocketpy.h

@@ -19,8 +19,8 @@ CodeObject_ VM::compile(Str source, Str filename, CompileMode mode) {
 }
 
 #define BIND_NUM_ARITH_OPT(name, op)                                                                    \
-    _vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, pkpy::Args& args){                 \
-        if(args[0]->is_type(vm->tp_int) && args[1]->is_type(vm->tp_int)){                             \
+    _vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, pkpy::Args& args){                         \
+        if(is_type(args[0], vm->tp_int) && is_type(args[1], vm->tp_int)){                               \
             return vm->PyInt(vm->PyInt_AS_C(args[0]) op vm->PyInt_AS_C(args[1]));                       \
         }else{                                                                                          \
             return vm->PyFloat(vm->num_to_float(args[0]) op vm->num_to_float(args[1]));                 \
@@ -28,11 +28,9 @@ CodeObject_ VM::compile(Str source, Str filename, CompileMode mode) {
     });
 
 #define BIND_NUM_LOGICAL_OPT(name, op, is_eq)                                                           \
-    _vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, pkpy::Args& args){                 \
-        bool _0 = args[0]->is_type(vm->tp_int) || args[0]->is_type(vm->tp_float);                     \
-        bool _1 = args[1]->is_type(vm->tp_int) || args[1]->is_type(vm->tp_float);                     \
-        if(!_0 || !_1){                                                                                 \
-            if constexpr(is_eq) return vm->PyBool(args[0].get() op args[1].get());                      \
+    _vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, pkpy::Args& args){                         \
+        if(!is_int_or_float(args[0]) || !is_int_or_float(args[1])){                                     \
+            if constexpr(is_eq) return vm->PyBool(args[0] op args[1]);                                  \
             vm->TypeError("unsupported operand type(s) for " #op );                                     \
         }                                                                                               \
         return vm->PyBool(vm->num_to_float(args[0]) op vm->num_to_float(args[1]));                      \
@@ -170,7 +168,7 @@ void init_builtins(VM* _vm) {
     });
 
     _vm->_bind_methods<1>({"int", "float"}, "__pow__", [](VM* vm, pkpy::Args& args) {
-        if(args[0]->is_type(vm->tp_int) && args[1]->is_type(vm->tp_int)){
+        if(is_int(args[0]) && is_int(args[1])){
             i64 lhs = vm->PyInt_AS_C(args[0]);
             i64 rhs = vm->PyInt_AS_C(args[1]);
             bool flag = false;
@@ -190,18 +188,18 @@ void init_builtins(VM* _vm) {
 
     /************ PyInt ************/
     _vm->bind_static_method<1>("int", "__new__", [](VM* vm, pkpy::Args& args) {
-        if (args[0]->is_type(vm->tp_int)) return args[0];
-        if (args[0]->is_type(vm->tp_float)) return vm->PyInt((i64)vm->PyFloat_AS_C(args[0]));
-        if (args[0]->is_type(vm->tp_bool)) return vm->PyInt(vm->PyBool_AS_C(args[0]) ? 1 : 0);
-        if (args[0]->is_type(vm->tp_str)) {
+        if (is_type(args[0], vm->tp_int)) return args[0];
+        if (is_type(args[0], vm->tp_float)) return vm->PyInt((i64)vm->PyFloat_AS_C(args[0]));
+        if (is_type(args[0], vm->tp_bool)) return vm->PyInt(vm->PyBool_AS_C(args[0]) ? 1 : 0);
+        if (is_type(args[0], vm->tp_str)) {
             const Str& s = vm->PyStr_AS_C(args[0]);
             try{
                 size_t parsed = 0;
                 i64 val = std::stoll(s, &parsed, 10);
-                if(parsed != s.size()) throw std::invalid_argument("");
+                if(parsed != s.size()) throw std::invalid_argument("<?>");
                 return vm->PyInt(val);
             }catch(std::invalid_argument&){
-                vm->ValueError("invalid literal for int(): '" + s + "'");
+                vm->ValueError("invalid literal for int(): " + s.escape(true));
             }
         }
         vm->TypeError("int() argument must be a int, float, bool or str");
@@ -236,10 +234,10 @@ void init_builtins(VM* _vm) {
 
     /************ PyFloat ************/
     _vm->bind_static_method<1>("float", "__new__", [](VM* vm, pkpy::Args& args) {
-        if (args[0]->is_type(vm->tp_int)) return vm->PyFloat((f64)vm->PyInt_AS_C(args[0]));
-        if (args[0]->is_type(vm->tp_float)) return args[0];
-        if (args[0]->is_type(vm->tp_bool)) return vm->PyFloat(vm->PyBool_AS_C(args[0]) ? 1.0 : 0.0);
-        if (args[0]->is_type(vm->tp_str)) {
+        if (is_type(args[0], vm->tp_int)) return vm->PyFloat((f64)vm->PyInt_AS_C(args[0]));
+        if (is_type(args[0], vm->tp_float)) return args[0];
+        if (is_type(args[0], vm->tp_bool)) return vm->PyFloat(vm->PyBool_AS_C(args[0]) ? 1.0 : 0.0);
+        if (is_type(args[0], vm->tp_str)) {
             const Str& s = vm->PyStr_AS_C(args[0]);
             if(s == "inf") return vm->PyFloat(INFINITY);
             if(s == "-inf") return vm->PyFloat(-INFINITY);
@@ -258,7 +256,7 @@ void init_builtins(VM* _vm) {
         f64 val = vm->PyFloat_AS_C(args[0]);
         if(std::isinf(val) || std::isnan(val)) return vm->PyStr(std::to_string(val));
         StrStream ss;
-        ss << std::setprecision(std::numeric_limits<f64>::max_digits10-1) << val;
+        ss << std::setprecision(std::numeric_limits<f64>::max_digits10-1-2) << val;
         std::string s = ss.str();
         if(std::all_of(s.begin()+1, s.end(), isdigit)) s += ".0";
         return vm->PyStr(s);
@@ -307,13 +305,13 @@ void init_builtins(VM* _vm) {
     });
 
     _vm->bind_method<1>("str", "__eq__", [](VM* vm, pkpy::Args& args) {
-        if(args[0]->is_type(vm->tp_str) && args[1]->is_type(vm->tp_str))
+        if(is_type(args[0], vm->tp_str) && is_type(args[1], vm->tp_str))
             return vm->PyBool(vm->PyStr_AS_C(args[0]) == vm->PyStr_AS_C(args[1]));
         return vm->PyBool(args[0] == args[1]);
     });
 
     _vm->bind_method<1>("str", "__ne__", [](VM* vm, pkpy::Args& args) {
-        if(args[0]->is_type(vm->tp_str) && args[1]->is_type(vm->tp_str))
+        if(is_type(args[0], vm->tp_str) && is_type(args[1], vm->tp_str))
             return vm->PyBool(vm->PyStr_AS_C(args[0]) != vm->PyStr_AS_C(args[1]));
         return vm->PyBool(args[0] != args[1]);
     });
@@ -321,7 +319,7 @@ void init_builtins(VM* _vm) {
     _vm->bind_method<1>("str", "__getitem__", [](VM* vm, pkpy::Args& args) {
         const Str& _self (vm->PyStr_AS_C(args[0]));
 
-        if(args[1]->is_type(vm->tp_slice)){
+        if(is_type(args[1], vm->tp_slice)){
             pkpy::Slice s = vm->PySlice_AS_C(args[1]);
             s.normalize(_self.u8_length());
             return vm->PyStr(_self.u8_substr(s.start, s.stop));
@@ -441,7 +439,7 @@ void init_builtins(VM* _vm) {
     _vm->bind_method<1>("list", "__getitem__", [](VM* vm, pkpy::Args& args) {
         const pkpy::List& self = vm->PyList_AS_C(args[0]);
 
-        if(args[1]->is_type(vm->tp_slice)){
+        if(is_type(args[1], vm->tp_slice)){
             pkpy::Slice s = vm->PySlice_AS_C(args[1]);
             s.normalize(self.size());
             pkpy::List new_list;
@@ -483,7 +481,7 @@ void init_builtins(VM* _vm) {
     _vm->bind_method<1>("tuple", "__getitem__", [](VM* vm, pkpy::Args& args) {
         const pkpy::Tuple& self = vm->PyTuple_AS_C(args[0]);
 
-        if(args[1]->is_type(vm->tp_slice)){
+        if(is_type(args[1], vm->tp_slice)){
             pkpy::Slice s = vm->PySlice_AS_C(args[1]);
             s.normalize(self.size());
             pkpy::List new_list;
@@ -592,7 +590,7 @@ void add_module_dis(VM* vm){
     PyVar mod = vm->new_module("dis");
     vm->bind_func<1>(mod, "dis", [](VM* vm, pkpy::Args& args) {
         PyVar f = args[0];
-        if(f->is_type(vm->tp_bound_method)) f = vm->PyBoundMethod_AS_C(args[0]).method;
+        if(is_type(f, vm->tp_bound_method)) f = vm->PyBoundMethod_AS_C(args[0]).method;
         CodeObject_ code = vm->PyFunction_AS_C(f).code;
         (*vm->_stdout) << vm->disassemble(code);
         return vm->None;

+ 77 - 46
src/vm.h

@@ -63,15 +63,15 @@ public:
     }
 
     PyVar asRepr(const PyVar& obj){
-        if(obj->is_type(tp_type)) return PyStr("<class '" + OBJ_GET(Str, obj->attr(__name__)) + "'>");
+        if(is_type(obj, tp_type)) return PyStr("<class '" + OBJ_GET(Str, obj->attr(__name__)) + "'>");
         return call(obj, __repr__);
     }
 
     const PyVar& asBool(const PyVar& obj){
-        if(obj->is_type(tp_bool)) return obj;
+        if(is_type(obj, tp_bool)) return obj;
         if(obj == None) return False;
-        if(obj->is_type(tp_int)) return PyBool(PyInt_AS_C(obj) != 0);
-        if(obj->is_type(tp_float)) return PyBool(PyFloat_AS_C(obj) != 0.0);
+        if(is_type(obj, tp_int)) return PyBool(PyInt_AS_C(obj) != 0);
+        if(is_type(obj, tp_float)) return PyBool(PyFloat_AS_C(obj) != 0.0);
         PyVarOrNull len_fn = getattr(obj, __len__, false);
         if(len_fn != nullptr){
             PyVar ret = call(len_fn);
@@ -81,7 +81,7 @@ public:
     }
 
     PyVar asIter(const PyVar& obj){
-        if(obj->is_type(tp_native_iterator)) return obj;
+        if(is_type(obj, tp_native_iterator)) return obj;
         PyVarOrNull iter_f = getattr(obj, __iter__, false);
         if(iter_f != nullptr) return call(iter_f);
         TypeError(OBJ_NAME(_t(obj)).escape(true) + " object is not iterable");
@@ -89,7 +89,7 @@ public:
     }
 
     PyVar asList(const PyVar& iterable){
-        if(iterable->is_type(tp_list)) return iterable;
+        if(is_type(iterable, tp_list)) return iterable;
         return call(_t(tp_list), pkpy::one_arg(iterable));
     }
 
@@ -125,7 +125,7 @@ public:
     }
 
     PyVar call(const PyVar& _callable, pkpy::Args args, const pkpy::Args& kwargs, bool opCall){
-        if(_callable->is_type(tp_type)){
+        if(is_type(_callable, tp_type)){
             PyVar* new_f = _callable->attr().try_get(__new__);
             PyVar obj;
             if(new_f != nullptr){
@@ -139,17 +139,17 @@ public:
         }
 
         const PyVar* callable = &_callable;
-        if((*callable)->is_type(tp_bound_method)){
+        if(is_type(*callable, tp_bound_method)){
             auto& bm = PyBoundMethod_AS_C((*callable));
             callable = &bm.method;      // get unbound method
             args.extend_self(bm.obj);
         }
         
-        if((*callable)->is_type(tp_native_function)){
+        if(is_type(*callable, tp_native_function)){
             const auto& f = OBJ_GET(pkpy::NativeFunc, *callable);
             if(kwargs.size() != 0) TypeError("native_function does not accept keyword arguments");
             return f(this, args);
-        } else if((*callable)->is_type(tp_function)){
+        } else if(is_type(*callable, tp_function)){
             const pkpy::Function& fn = PyFunction_AS_C(*callable);
             pkpy::shared_ptr<pkpy::NameDict> _locals = pkpy::make_shared<pkpy::NameDict>();
             pkpy::NameDict& locals = *_locals;
@@ -285,7 +285,7 @@ public:
     }
 
     PyVar new_type_object(PyVar mod, Str name, PyVar base){
-        if(!base->is_type(tp_type)) UNREACHABLE();
+        if(!is_type(base, tp_type)) UNREACHABLE();
         PyVar obj = pkpy::make_shared<PyObject, Py_<Type>>(tp_type, _all_types.size());
         setattr(obj, __base__, base);
         Str fullName = name;
@@ -306,12 +306,12 @@ public:
 
     template<typename T>
     inline PyVar new_object(const PyVar& type, const T& _value) {
-        if(!type->is_type(tp_type)) UNREACHABLE();
+        if(!is_type(type, tp_type)) UNREACHABLE();
         return pkpy::make_shared<PyObject, Py_<RAW(T)>>(OBJ_GET(Type, type), _value);
     }
     template<typename T>
     inline PyVar new_object(const PyVar& type, T&& _value) {
-        if(!type->is_type(tp_type)) UNREACHABLE();
+        if(!is_type(type, tp_type)) UNREACHABLE();
         return pkpy::make_shared<PyObject, Py_<RAW(T)>>(OBJ_GET(Type, type), std::move(_value));
     }
 
@@ -340,12 +340,12 @@ public:
         pkpy::NameDict::iterator it;
         PyObject* cls;
 
-        if(obj->is_type(tp_super)){
+        if(is_type(obj, tp_super)){
             const PyVar* root = &obj;
             int depth = 1;
             while(true){
                 root = &OBJ_GET(PyVar, *root);
-                if(!(*root)->is_type(tp_super)) break;
+                if(!is_type(*root, tp_super)) break;
                 depth++;
             }
             cls = _t(*root).get();
@@ -364,14 +364,13 @@ public:
         while(cls != None.get()) {
             it = cls->attr().find(name);
             if(it != cls->attr().end()){
-                PyVar valueFromCls = it->second;
-                if(valueFromCls->is_type(tp_function) || valueFromCls->is_type(tp_native_function)){
-                    return PyBoundMethod({obj, std::move(valueFromCls)});
+                if(is_type(it->second, tp_function) || is_type(it->second, tp_native_function)){
+                    return PyBoundMethod({obj, it->second});
                 }else{
-                    return valueFromCls;
+                    return it->second;
                 }
             }
-            cls = cls->attr()[__base__].get();
+            cls = cls->attr(__base__).get();
         }
         if(throw_err) AttributeError(obj, name);
         return nullptr;
@@ -379,10 +378,11 @@ public:
 
     template<typename T>
     inline void setattr(PyVar& obj, const Str& name, T&& value) {
+        if(obj.is_tagged()) TypeError("cannot set attribute");
         PyObject* p = obj.get();
-        while(p->is_type(tp_super)) p = static_cast<PyVar*>(p->value())->get();
+        while(p->type == tp_super) p = static_cast<PyVar*>(p->value())->get();
         if(!p->is_attr_valid()) TypeError("cannot set attribute");
-        p->attr()[name] = std::forward<T>(value);
+        p->attr(name) = std::forward<T>(value);
     }
 
     template<int ARGC>
@@ -422,9 +422,9 @@ public:
     }
 
     inline f64 num_to_float(const PyVar& obj){
-        if (obj->is_type(tp_int)){
+        if (is_int(obj)){
             return (f64)PyInt_AS_C(obj);
-        }else if(obj->is_type(tp_float)){
+        }else if(is_float(obj)){
             return PyFloat_AS_C(obj);
         }
         TypeError("expected 'int' or 'float', got " + OBJ_NAME(_t(obj)).escape(true));
@@ -432,12 +432,12 @@ public:
     }
 
     PyVar num_negated(const PyVar& obj){
-        if (obj->is_type(tp_int)){
+        if (is_int(obj)){
             return PyInt(-PyInt_AS_C(obj));
-        }else if(obj->is_type(tp_float)){
+        }else if(is_float(obj)){
             return PyFloat(-PyFloat_AS_C(obj));
         }
-        TypeError("unsupported operand type(s) for -");
+        TypeError("expected 'int' or 'float', got " + OBJ_NAME(_t(obj)).escape(true));
         return nullptr;
     }
 
@@ -510,7 +510,7 @@ public:
 
         for(int i=0; i<co->consts.size(); i++){
             PyVar obj = co->consts[i];
-            if(obj->is_type(tp_function)){
+            if(is_type(obj, tp_function)){
                 const auto& f = PyFunction_AS_C(obj);
                 ss << disassemble(f.code);
             }
@@ -533,8 +533,8 @@ public:
 
     inline const BaseRef* PyRef_AS_C(const PyVar& obj)
     {
-        if(!obj->is_type(tp_ref)) TypeError("expected an l-value");
-        return (const BaseRef*)(obj->value());
+        if(!is_type(obj, tp_ref)) TypeError("expected an l-value");
+        return static_cast<const BaseRef*>(obj->value());
     }
 
     inline const Str& PyStr_AS_C(const PyVar& obj) {
@@ -550,8 +550,35 @@ public:
         return new_object(tp_str, value);
     }
 
-    DEF_NATIVE(Int, i64, tp_int)
-    DEF_NATIVE(Float, f64, tp_float)
+    inline PyVar PyInt(i64 value) {
+        const i64 MIN_SAFE_INT = -((i64)1 << 62);
+        const i64 MAX_SAFE_INT = ((i64)1 << 62) - 1;
+        if(value < MIN_SAFE_INT || value > MAX_SAFE_INT){
+            _error("OverflowError", std::to_string(value) + " is out of range");
+        }
+        value = (value << 2) | 0b01;
+        return PyVar(reinterpret_cast<int*>(value));
+    }
+
+    inline i64 PyInt_AS_C(const PyVar& obj){
+        check_type(obj, tp_int);
+        i64 value = obj.cast<i64>();
+        return value >> 2;
+    }
+
+    inline PyVar PyFloat(f64 value) {
+        auto bits = __8B(value);
+        i64 _int = bits._int;
+        bits._int = (_int & 0b00) | 0b10;
+        return PyVar(reinterpret_cast<int*>(bits._int));
+    }
+
+    inline f64 PyFloat_AS_C(const PyVar& obj){
+        check_type(obj, tp_float);
+        i64 _int = obj.cast<i64>();
+        return __8B(_int)._float;
+    }
+
     DEF_NATIVE(List, pkpy::List, tp_list)
     DEF_NATIVE(Tuple, pkpy::Tuple, tp_tuple)
     DEF_NATIVE(Function, pkpy::Function, tp_function)
@@ -576,9 +603,11 @@ public:
         _types["object"] = _tp_object;
         _types["type"] = _tp_type;
 
-        tp_bool = _new_type_object("bool");
         tp_int = _new_type_object("int");
         tp_float = _new_type_object("float");
+        if(tp_int.index != kTpIntIndex || tp_float.index != kTpFloatIndex) UNREACHABLE();
+
+        tp_bool = _new_type_object("bool");
         tp_str = _new_type_object("str");
         tp_list = _new_type_object("list");
         tp_tuple = _new_type_object("tuple");
@@ -617,15 +646,9 @@ public:
     }
 
     i64 hash(const PyVar& obj){
-        if (obj->is_type(tp_int)) return PyInt_AS_C(obj);
-        if (obj->is_type(tp_bool)) return PyBool_AS_C(obj) ? 1 : 0;
-        if (obj->is_type(tp_float)){
-            f64 val = PyFloat_AS_C(obj);
-            return (i64)std::hash<f64>()(val);
-        }
-        if (obj->is_type(tp_str)) return PyStr_AS_C(obj).hash();
-        if (obj->is_type(tp_type)) return (i64)obj.get();
-        if (obj->is_type(tp_tuple)) {
+        if (is_type(obj, tp_str)) return PyStr_AS_C(obj).hash();
+        if (is_int(obj)) return PyInt_AS_C(obj);        
+        if (is_type(obj, tp_tuple)) {
             i64 x = 1000003;
             const pkpy::Tuple& items = PyTuple_AS_C(obj);
             for (int i=0; i<items.size(); i++) {
@@ -634,6 +657,12 @@ public:
             }
             return x;
         }
+        if (is_type(obj, tp_type)) return obj.cast<i64>();
+        if (is_type(obj, tp_bool)) return PyBool_AS_C(obj) ? 1 : 0;
+        if (is_float(obj)){
+            f64 val = PyFloat_AS_C(obj);
+            return (i64)std::hash<f64>()(val);
+        }
         TypeError("unhashable type: " +  OBJ_NAME(_t(obj)).escape(true));
         return 0;
     }
@@ -673,7 +702,7 @@ public:
     }
 
     inline void check_type(const PyVar& obj, Type type){
-        if(obj->is_type(type)) return;
+        if(is_type(obj, type)) return;
         TypeError("expected " + OBJ_NAME(_t(type)).escape(true) + ", but got " + OBJ_NAME(_t(obj)).escape(true));
     }
 
@@ -682,6 +711,8 @@ public:
     }
 
     inline PyVar& _t(const PyVar& obj){
+        if(is_int(obj)) return _t(tp_int);
+        if(is_float(obj)) return _t(tp_float);
         return _all_types[OBJ_GET(Type, _t(obj->type)).index];
     }
 
@@ -805,10 +836,10 @@ void TupleRef::set(VM* vm, Frame* frame, PyVar val) const{
     if(args.size() < objs.size()) vm->ValueError("not enough values to unpack");     \
     for (int i = 0; i < objs.size(); i++) vm->PyRef_AS_C(objs[i])->set(vm, frame, args[i]);
 
-    if(val->is_type(vm->tp_tuple)){
+    if(is_type(val, vm->tp_tuple)){
         const pkpy::Tuple& args = OBJ_GET(pkpy::Tuple, val);
         TUPLE_REF_SET()
-    }else if(val->is_type(vm->tp_list)){
+    }else if(is_type(val, vm->tp_list)){
         const pkpy::List& args = OBJ_GET(pkpy::List, val);
         TUPLE_REF_SET()
     }else{
@@ -823,7 +854,7 @@ void TupleRef::del(VM* vm, Frame* frame) const{
 
 /***** Frame's Impl *****/
 inline void Frame::try_deref(VM* vm, PyVar& v){
-    if(v->is_type(vm->tp_ref)) v = vm->PyRef_AS_C(v)->get(vm, this);
+    if(is_type(v, vm->tp_ref)) v = vm->PyRef_AS_C(v)->get(vm, this);
 }
 
 PyVar pkpy::NativeFunc::operator()(VM* vm, pkpy::Args& args) const{