From a53af18ee84a60db88c9534da40d40d429e25fec Mon Sep 17 00:00:00 2001 From: BLUELOVETH Date: Thu, 5 Jun 2025 22:26:28 +0800 Subject: [PATCH] Make `py_Name` opaque pointer (#370) * backup * backup * backup * backup * backup * backup * Update codeobject.c * backup * Update ceval.c * backup * backup * fix all * revert ci * fix all * fix all * fix pybind * add `PK_ENABLE_CUSTOM_SNAME` --- 3rd/libhv/include/libhv_bindings.hpp | 2 + CMakeLists.txt | 5 + CMakeOptions.txt | 1 + include/pocketpy/common/name.h | 15 ++ include/pocketpy/common/smallmap.h | 14 +- include/pocketpy/common/str.h | 1 + include/pocketpy/common/utils.h | 4 +- include/pocketpy/config.h | 10 ++ include/pocketpy/export.h | 6 + include/pocketpy/interpreter/frame.h | 2 - include/pocketpy/interpreter/name.h | 18 --- include/pocketpy/interpreter/typeinfo.h | 8 +- include/pocketpy/interpreter/vm.h | 5 +- include/pocketpy/objects/codeobject.h | 11 +- include/pocketpy/objects/namedict.h | 37 +++-- include/pocketpy/pocketpy.h | 77 ++++----- include/pocketpy/xmacros/opcodes.h | 20 ++- include/pocketpy/xmacros/smallmap.h | 2 +- include/pybind11/internal/class.h | 8 +- include/pybind11/internal/error.h | 32 ++-- src/common/name.c | 101 ++++++++++++ src/common/smallmap.c | 14 +- src/common/sstream.c | 2 +- src/common/str.c | 9 ++ src/compiler/compiler.c | 137 ++++++++-------- src/interpreter/ceval.c | 205 ++++++++++++++++-------- src/interpreter/frame.c | 12 +- src/interpreter/heap.c | 7 +- src/interpreter/name.c | 75 --------- src/interpreter/typeinfo.c | 25 +-- src/interpreter/vm.c | 47 +++--- src/modules/dis.c | 33 +++- src/modules/pickle.c | 30 ++-- src/modules/pkpy.c | 50 +++--- src/objects/codeobject.c | 33 ++-- src/objects/namedict.c | 134 +++++++++++++++- src/public/internal.c | 19 ++- src/public/modules.c | 15 +- src/public/py_exception.c | 7 + src/public/py_mappingproxy.c | 26 +-- src/public/py_ops.c | 10 +- src/public/py_str.c | 9 +- src/public/stack_ops.c | 30 +--- src/public/values.c | 12 +- 44 files changed, 809 insertions(+), 511 deletions(-) create mode 100644 include/pocketpy/common/name.h delete mode 100644 include/pocketpy/interpreter/name.h create mode 100644 src/common/name.c delete mode 100644 src/interpreter/name.c diff --git a/3rd/libhv/include/libhv_bindings.hpp b/3rd/libhv/include/libhv_bindings.hpp index 42a09e38..b9c459a5 100644 --- a/3rd/libhv/include/libhv_bindings.hpp +++ b/3rd/libhv/include/libhv_bindings.hpp @@ -4,6 +4,8 @@ #include "http/HttpMessage.h" #include "base/hplatform.h" +#include "pocketpy/common/name.h" + extern "C" void pk__add_module_libhv(); void libhv_HttpRequest_create(py_OutRef out, HttpRequestPtr ptr); diff --git a/CMakeLists.txt b/CMakeLists.txt index 9df3df41..00680035 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,11 @@ if(PK_ENABLE_WATCHDOG) add_definitions(-DPK_ENABLE_WATCHDOG=1) endif() +if(PK_ENABLE_CUSTOM_SNAME) + add_definitions(-DPK_ENABLE_CUSTOM_SNAME=1) +endif() + + if(PK_BUILD_MODULE_LZ4) add_subdirectory(3rd/lz4) add_definitions(-DPK_BUILD_MODULE_LZ4) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 1a22197b..10f4fa20 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -9,6 +9,7 @@ endif() option(PK_ENABLE_OS "" OFF) option(PK_ENABLE_DETERMINISM "" OFF) option(PK_ENABLE_WATCHDOG "" OFF) +option(PK_ENABLE_CUSTOM_SNAME "" OFF) # modules option(PK_BUILD_MODULE_LZ4 "" OFF) diff --git a/include/pocketpy/common/name.h b/include/pocketpy/common/name.h new file mode 100644 index 00000000..9f8803b6 --- /dev/null +++ b/include/pocketpy/common/name.h @@ -0,0 +1,15 @@ +#pragma once + +#include "pocketpy/pocketpy.h" + +void pk_names_initialize(); +void pk_names_finalize(); + +#define MAGIC_METHOD(x) extern py_Name x; +#include "pocketpy/xmacros/magics.h" +#undef MAGIC_METHOD + +py_Name py_namev(c11_sv name); +c11_sv py_name2sv(py_Name index); +py_Name py_name(const char* name); +const char* py_name2str(py_Name index); \ No newline at end of file diff --git a/include/pocketpy/common/smallmap.h b/include/pocketpy/common/smallmap.h index ad95857b..74556202 100644 --- a/include/pocketpy/common/smallmap.h +++ b/include/pocketpy/common/smallmap.h @@ -5,17 +5,23 @@ #include #define SMALLMAP_T__HEADER -#define K uint16_t +#define K py_Name #define V int -#define NAME c11_smallmap_n2i +#define NAME c11_smallmap_n2d #include "pocketpy/xmacros/smallmap.h" #undef SMALLMAP_T__HEADER +#define SMALLMAP_T__HEADER +#define K int +#define V int +#define NAME c11_smallmap_d2d +#include "pocketpy/xmacros/smallmap.h" +#undef SMALLMAP_T__HEADER #define SMALLMAP_T__HEADER #define K c11_sv -#define V uint16_t -#define NAME c11_smallmap_s2n +#define V int +#define NAME c11_smallmap_v2d #define less(a, b) (c11_sv__cmp((a), (b)) < 0) #define equal(a, b) (c11_sv__cmp((a), (b)) == 0) #include "pocketpy/xmacros/smallmap.h" diff --git a/include/pocketpy/common/str.h b/include/pocketpy/common/str.h index 92ec1071..83fcf20f 100644 --- a/include/pocketpy/common/str.h +++ b/include/pocketpy/common/str.h @@ -60,6 +60,7 @@ int c11_sv__index2(c11_sv self, c11_sv sub, int start); int c11_sv__count(c11_sv self, c11_sv sub); bool c11_sv__startswith(c11_sv self, c11_sv prefix); bool c11_sv__endswith(c11_sv self, c11_sv suffix); +uint64_t c11_sv__hash(c11_sv self); c11_string* c11_sv__replace(c11_sv self, char old, char new_); c11_string* c11_sv__replace2(c11_sv self, c11_sv old, c11_sv new_); diff --git a/include/pocketpy/common/utils.h b/include/pocketpy/common/utils.h index cd842498..5a219ac9 100644 --- a/include/pocketpy/common/utils.h +++ b/include/pocketpy/common/utils.h @@ -23,6 +23,9 @@ abort(); \ } while(0) +#define c11__rtassert(cond) \ + if(!(cond)) c11__abort("assertion failed: %s", #cond) + #define c11__min(a, b) ((a) < (b) ? (a) : (b)) #define c11__max(a, b) ((a) > (b) ? (a) : (b)) @@ -42,4 +45,3 @@ typedef struct RefCounted { PK_FREE(obj); \ } \ } while(0) - diff --git a/include/pocketpy/config.h b/include/pocketpy/config.h index 6f3a6bcd..91ad1d4e 100644 --- a/include/pocketpy/config.h +++ b/include/pocketpy/config.h @@ -20,6 +20,10 @@ #define PK_ENABLE_WATCHDOG 0 #endif +#ifndef PK_ENABLE_CUSTOM_SNAME // can be overridden by cmake +#define PK_ENABLE_CUSTOM_SNAME 0 +#endif + // GC min threshold #ifndef PK_GC_MIN_THRESHOLD // can be overridden by cmake #define PK_GC_MIN_THRESHOLD 32768 @@ -60,6 +64,12 @@ #define PK_M_DEG2RAD 0.017453292519943295 #define PK_M_RAD2DEG 57.29577951308232 +// Hash table load factor (smaller ones mean less collision but more memory) +// For class instance +#define PK_INST_ATTR_LOAD_FACTOR 0.67f +// For class itself +#define PK_TYPE_ATTR_LOAD_FACTOR 0.5f + #ifdef _WIN32 #define PK_PLATFORM_SEP '\\' #else diff --git a/include/pocketpy/export.h b/include/pocketpy/export.h index b7baee80..d115a8e0 100644 --- a/include/pocketpy/export.h +++ b/include/pocketpy/export.h @@ -55,3 +55,9 @@ #else #define PK_IS_DESKTOP_PLATFORM 0 #endif + +#if defined(__GNUC__) || defined(__clang__) + #define PK_DEPRECATED __attribute__((deprecated)) +#else + #define PK_DEPRECATED +#endif \ No newline at end of file diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 2475a477..8b0fcf22 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -1,9 +1,7 @@ #pragma once -#include "pocketpy/common/memorypool.h" #include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/namedict.h" -#include "pocketpy/objects/object.h" #include "pocketpy/pocketpy.h" void FastLocals__to_dict(py_TValue* locals, const CodeObject* co) PY_RETURN; diff --git a/include/pocketpy/interpreter/name.h b/include/pocketpy/interpreter/name.h deleted file mode 100644 index b1f08520..00000000 --- a/include/pocketpy/interpreter/name.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "pocketpy/objects/base.h" -#include "pocketpy/common/smallmap.h" - -typedef struct { - char* data; // null-terminated data - int size; // size of the data excluding the null-terminator - py_TValue obj; // cached `str` object (lazy initialized) -} RInternedEntry; - -typedef struct { - c11_smallmap_s2n interned; - c11_vector /* T=RInternedEntry */ r_interned; -} InternedNames; - -void InternedNames__ctor(InternedNames* self); -void InternedNames__dtor(InternedNames* self); diff --git a/include/pocketpy/interpreter/typeinfo.h b/include/pocketpy/interpreter/typeinfo.h index 2f7cc63e..e2901fa2 100644 --- a/include/pocketpy/interpreter/typeinfo.h +++ b/include/pocketpy/interpreter/typeinfo.h @@ -3,8 +3,6 @@ #include "pocketpy/pocketpy.h" #include "pocketpy/objects/object.h" -#define PK_MAGIC_SLOTS_COMMON_LENGTH (__missing__ - __xor__) -#define PK_MAGIC_SLOTS_UNCOMMON_LENGTH (__xor__ + 1) #define PK_MAX_CHUNK_LENGTH 256 typedef struct py_TypeInfo { @@ -25,8 +23,7 @@ typedef struct py_TypeInfo { void (*on_end_subclass)(struct py_TypeInfo*); // backdoor for enum module /* Magic Slots */ - py_TValue magic_0[PK_MAGIC_SLOTS_COMMON_LENGTH]; // common magic slots - py_TValue* magic_1; // uncommon magic slots + // (deleted) } py_TypeInfo; typedef struct TypeList { @@ -39,7 +36,4 @@ void TypeList__dtor(TypeList* self); py_TypeInfo* TypeList__get(TypeList* self, py_Type index); py_TypeInfo* TypeList__emplace(TypeList* self); void TypeList__apply(TypeList* self, void (*f)(py_TypeInfo*, void*), void* ctx); -py_TValue* TypeList__magic(py_TypeInfo* self, unsigned index); -py_TValue* TypeList__magic_readonly(py_TypeInfo* self, unsigned index); -#define TypeList__magic_common(ti, index) ((ti)->magic_0 + ((index)-PK_MAGIC_SLOTS_UNCOMMON_LENGTH)) diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 00381d7a..78065bed 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -1,12 +1,12 @@ #pragma once #include "pocketpy/common/memorypool.h" +#include "pocketpy/common/name.h" #include "pocketpy/objects/codeobject.h" #include "pocketpy/pocketpy.h" #include "pocketpy/interpreter/heap.h" #include "pocketpy/interpreter/frame.h" #include "pocketpy/interpreter/typeinfo.h" -#include "pocketpy/interpreter/name.h" #include "pocketpy/interpreter/line_profiler.h" #include @@ -49,6 +49,8 @@ typedef struct VM { py_TValue reg[8]; // users' registers void* ctx; // user-defined context + NameDict cached_names; + py_StackRef curr_class; py_StackRef curr_decl_based_function; TraceInfo trace_info; @@ -56,7 +58,6 @@ typedef struct VM { LineProfiler line_profiler; py_TValue vectorcall_buffer[PK_MAX_CO_VARNAMES]; - InternedNames names; FixedMemoryPool pool_frame; ManagedHeap heap; ValueStack stack; // put `stack` at the end for better cache locality diff --git a/include/pocketpy/objects/codeobject.h b/include/pocketpy/objects/codeobject.h index 47f8233b..607d1f0c 100644 --- a/include/pocketpy/objects/codeobject.h +++ b/include/pocketpy/objects/codeobject.h @@ -75,9 +75,11 @@ typedef struct CodeObject { c11_vector /*T=py_TValue*/ consts; // constants c11_vector /*T=py_Name*/ varnames; // local variables - int nlocals; // cached varnames.size() + c11_vector /*T=py_Name*/ names; + int nlocals; - c11_smallmap_n2i varnames_inv; + c11_smallmap_n2d varnames_inv; + c11_smallmap_n2d names_inv; c11_vector /*T=CodeBlock*/ blocks; c11_vector /*T=FuncDecl_*/ func_decls; @@ -89,11 +91,12 @@ typedef struct CodeObject { void CodeObject__ctor(CodeObject* self, SourceData_ src, c11_sv name); void CodeObject__dtor(CodeObject* self); int CodeObject__add_varname(CodeObject* self, py_Name name); +int CodeObject__add_name(CodeObject* self, py_Name name); void CodeObject__gc_mark(const CodeObject* self, c11_vector* p_stack); typedef struct FuncDeclKwArg { int index; // index in co->varnames - uint16_t key; // name of this argument + py_Name key; // name of this argument py_TValue value; // default value } FuncDeclKwArg; @@ -111,7 +114,7 @@ typedef struct FuncDecl { const char* docstring; // docstring of this function (weak ref) FuncType type; - c11_smallmap_n2i kw_to_index; + c11_smallmap_n2d kw_to_index; } FuncDecl; typedef FuncDecl* FuncDecl_; diff --git a/include/pocketpy/objects/namedict.h b/include/pocketpy/objects/namedict.h index 5a168f82..1ce2a735 100644 --- a/include/pocketpy/objects/namedict.h +++ b/include/pocketpy/objects/namedict.h @@ -1,16 +1,8 @@ #pragma once #include "pocketpy/common/vector.h" -#include "pocketpy/common/str.h" #include "pocketpy/objects/base.h" -#include - -#define SMALLMAP_T__HEADER -#define K uint16_t -#define V py_TValue -#define NAME NameDict -#include "pocketpy/xmacros/smallmap.h" -#undef SMALLMAP_T__HEADER +#include "pocketpy/pocketpy.h" /* A simple binary tree for storing modules. */ typedef struct ModuleDict { @@ -26,3 +18,30 @@ void ModuleDict__set(ModuleDict* self, const char* key, py_TValue val); py_TValue* ModuleDict__try_get(ModuleDict* self, const char* path); bool ModuleDict__contains(ModuleDict* self, const char* path); void ModuleDict__apply_mark(ModuleDict* self, c11_vector* p_stack); + +/////////////////// NameDict /////////////////// + +typedef struct NameDict_KV { + py_Name key; + py_TValue value; +} NameDict_KV; + +// https://github.com/pocketpy/pocketpy/blob/v1.x/include/pocketpy/namedict.h +typedef struct NameDict { + int length; + float load_factor; + int capacity; + int critical_size; + uintptr_t mask; + NameDict_KV* items; +} NameDict; + +NameDict* NameDict__new(float load_factor); +void NameDict__delete(NameDict* self); +void NameDict__ctor(NameDict* self, float load_factor); +void NameDict__dtor(NameDict* self); +py_TValue* NameDict__try_get(NameDict* self, py_Name key); +bool NameDict__contains(NameDict* self, py_Name key); +void NameDict__set(NameDict* self, py_Name key, py_TValue* value); +bool NameDict__del(NameDict* self, py_Name key); +void NameDict__clear(NameDict* self); \ No newline at end of file diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 547af499..93c68986 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -13,12 +13,12 @@ extern "C" { #endif /************* Public Types *************/ - +/// A helper struct for `py_Name`. +typedef struct py_OpaqueName py_OpaqueName; +/// A pointer that represents a python identifier. For fast name resolution. +typedef py_OpaqueName* py_Name; /// A opaque type that represents a python object. You cannot access its members directly. typedef struct py_TValue py_TValue; -/// An integer that represents a python identifier. This is to achieve string pooling and fast name -/// resolution. -typedef uint16_t py_Name; /// An integer that represents a python type. `0` is invalid. typedef int16_t py_Type; /// A 64-bit integer type. Corresponds to `int` in python. @@ -237,13 +237,13 @@ PK_API py_Name py_name(const char*); /// Convert a name to a null-terminated string. PK_API const char* py_name2str(py_Name); /// Convert a name to a python `str` object with cache. -PK_API py_GlobalRef py_name2ref(py_Name); +PK_API py_ItemRef py_name2ref(py_Name); /// Convert a `c11_sv` to a name. PK_API py_Name py_namev(c11_sv); /// Convert a name to a `c11_sv`. PK_API c11_sv py_name2sv(py_Name); -#define py_ismagicname(name) (name <= __missing__) +#define py_ismagicname(name) (true) /************* Meta Operations *************/ @@ -318,7 +318,7 @@ PK_API bool py_issubclass(py_Type derived, py_Type base); /// Get the magic method from the given type only. /// The returned reference is always valid. However, its value may be `nil`. -PK_API py_GlobalRef py_tpgetmagic(py_Type type, py_Name name); +PK_API PK_DEPRECATED py_GlobalRef py_tpgetmagic(py_Type type, py_Name name); /// Search the magic method from the given type to the base type. /// Return `NULL` if not found. PK_API py_GlobalRef py_tpfindmagic(py_Type, py_Name name); @@ -438,8 +438,8 @@ PK_API void py_bindfunc(py_Ref obj, const char* name, py_CFunction f); /// @param setter setter function. Use `NULL` if not needed. PK_API void py_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFunction setter); - -#define py_bindmagic(type, __magic__, f) py_newnativefunc(py_tpgetmagic((type), __magic__), (f)) +/// Bind a magic method to type. +PK_API void py_bindmagic(py_Type type, py_Name name, py_CFunction f); #define PY_CHECK_ARGC(n) \ if(argc != n) return TypeError("expected %d arguments, got %d", n, argc) @@ -469,21 +469,32 @@ PK_API bool py_delitem(py_Ref self, py_Ref key) PY_RAISE; /// The result will be set to `py_retval()`. /// The stack remains unchanged after the operation. PK_API bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) PY_RAISE PY_RETURN; - -#define py_binaryadd(lhs, rhs) py_binaryop(lhs, rhs, __add__, __radd__) -#define py_binarysub(lhs, rhs) py_binaryop(lhs, rhs, __sub__, __rsub__) -#define py_binarymul(lhs, rhs) py_binaryop(lhs, rhs, __mul__, __rmul__) -#define py_binarytruediv(lhs, rhs) py_binaryop(lhs, rhs, __truediv__, __rtruediv__) -#define py_binaryfloordiv(lhs, rhs) py_binaryop(lhs, rhs, __floordiv__, __rfloordiv__) -#define py_binarymod(lhs, rhs) py_binaryop(lhs, rhs, __mod__, __rmod__) -#define py_binarypow(lhs, rhs) py_binaryop(lhs, rhs, __pow__, __rpow__) - -#define py_binarylshift(lhs, rhs) py_binaryop(lhs, rhs, __lshift__, 0) -#define py_binaryrshift(lhs, rhs) py_binaryop(lhs, rhs, __rshift__, 0) -#define py_binaryand(lhs, rhs) py_binaryop(lhs, rhs, __and__, 0) -#define py_binaryor(lhs, rhs) py_binaryop(lhs, rhs, __or__, 0) -#define py_binaryxor(lhs, rhs) py_binaryop(lhs, rhs, __xor__, 0) -#define py_binarymatmul(lhs, rhs) py_binaryop(lhs, rhs, __matmul__, 0) +/// lhs + rhs +PK_API bool py_binaryadd(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs - rhs +PK_API bool py_binarysub(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs * rhs +PK_API bool py_binarymul(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs / rhs +PK_API bool py_binarytruediv(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs // rhs +PK_API bool py_binaryfloordiv(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs % rhs +PK_API bool py_binarymod(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs ** rhs +PK_API bool py_binarypow(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs << rhs +PK_API bool py_binarylshift(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs >> rhs +PK_API bool py_binaryrshift(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs & rhs +PK_API bool py_binaryand(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs | rhs +PK_API bool py_binaryor(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs ^ rhs +PK_API bool py_binaryxor(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs @ rhs +PK_API bool py_binarymatmul(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; /************* Stack Operations *************/ @@ -710,14 +721,6 @@ PK_API int py_replinput(char* buf, int max_size); /// %t: py_Type /// %n: py_Name -enum py_MagicName { - py_MagicName__NULL, // 0 is reserved - -#define MAGIC_METHOD(x) x, -#include "pocketpy/xmacros/magics.h" -#undef MAGIC_METHOD -}; - enum py_PredefinedType { tp_nil = 0, tp_object = 1, @@ -745,11 +748,11 @@ enum py_PredefinedType { tp_locals, tp_code, tp_dict, - tp_dict_iterator, // 1 slot - tp_property, // 2 slots (getter + setter) - tp_star_wrapper, // 1 slot + int level - tp_staticmethod, // 1 slot - tp_classmethod, // 1 slot + tp_dict_iterator, // 1 slot + tp_property, // 2 slots (getter + setter) + tp_star_wrapper, // 1 slot + int level + tp_staticmethod, // 1 slot + tp_classmethod, // 1 slot tp_NoneType, tp_NotImplementedType, tp_ellipsis, diff --git a/include/pocketpy/xmacros/opcodes.h b/include/pocketpy/xmacros/opcodes.h index d27a66d1..99f3e448 100644 --- a/include/pocketpy/xmacros/opcodes.h +++ b/include/pocketpy/xmacros/opcodes.h @@ -51,7 +51,25 @@ OPCODE(BUILD_SET) OPCODE(BUILD_SLICE) OPCODE(BUILD_STRING) /**************************/ -OPCODE(BINARY_OP) +OPCODE(BINARY_ADD) +OPCODE(BINARY_SUB) +OPCODE(BINARY_MUL) +OPCODE(BINARY_TRUEDIV) +OPCODE(BINARY_FLOORDIV) +OPCODE(BINARY_MOD) +OPCODE(BINARY_POW) +OPCODE(BINARY_LSHIFT) +OPCODE(BINARY_RSHIFT) +OPCODE(BINARY_AND) +OPCODE(BINARY_OR) +OPCODE(BINARY_XOR) +OPCODE(BINARY_MATMUL) +OPCODE(COMPARE_LT) +OPCODE(COMPARE_LE) +OPCODE(COMPARE_EQ) +OPCODE(COMPARE_NE) +OPCODE(COMPARE_GT) +OPCODE(COMPARE_GE) OPCODE(IS_OP) OPCODE(CONTAINS_OP) /**************************/ diff --git a/include/pocketpy/xmacros/smallmap.h b/include/pocketpy/xmacros/smallmap.h index 3c85bbb0..6a14580f 100644 --- a/include/pocketpy/xmacros/smallmap.h +++ b/include/pocketpy/xmacros/smallmap.h @@ -6,7 +6,7 @@ /* Input */ #define K int #define V float -#define NAME c11_smallmap_i2f +#define NAME c11_smallmap_d2f #endif /* Optional Input */ diff --git a/include/pybind11/internal/class.h b/include/pybind11/internal/class.h index 69060ec4..1a6e2166 100644 --- a/include/pybind11/internal/class.h +++ b/include/pybind11/internal/class.h @@ -30,8 +30,8 @@ public: auto& info = type_info::of(); info.name = name; - py_newfunction( - py_tpgetmagic(this->index(), __new__), + py_bind( + py_tpobject(this->index()), "__new__(type, *args, **kwargs)", [](int, py_Ref stack) { auto cls = py_offset(stack, 0); @@ -44,9 +44,7 @@ public: py_newobject(py_retval(), steal(cls).index(), slot, sizeof(instance)); new (data) instance{instance::Flag::Own, operator new (info->size), info}; return true; - }, - nullptr, - 0); + }); } /// bind constructor diff --git a/include/pybind11/internal/error.h b/include/pybind11/internal/error.h index 1db80454..3aa9ad66 100644 --- a/include/pybind11/internal/error.h +++ b/include/pybind11/internal/error.h @@ -101,25 +101,25 @@ inline object::operator bool () const { return raise_call(m_ptr); } return object::from_ret(); \ } -PKBIND_BINARY_OPERATOR(==, __eq__, __eq__) -PKBIND_BINARY_OPERATOR(!=, __ne__, __ne__) -PKBIND_BINARY_OPERATOR(<, __lt__, __gt__) -PKBIND_BINARY_OPERATOR(<=, __le__, __ge__) -PKBIND_BINARY_OPERATOR(>, __gt__, __lt__) -PKBIND_BINARY_OPERATOR(>=, __ge__, __le__) +PKBIND_BINARY_OPERATOR(==, py_name("__eq__"), py_name("__eq__")) +PKBIND_BINARY_OPERATOR(!=, py_name("__ne__"), py_name("__ne__")) +PKBIND_BINARY_OPERATOR(<, py_name("__lt__"), py_name("__gt__")) +PKBIND_BINARY_OPERATOR(<=, py_name("__le__"), py_name("__ge__")) +PKBIND_BINARY_OPERATOR(>, py_name("__gt__"), py_name("__lt__")) +PKBIND_BINARY_OPERATOR(>=, py_name("__ge__"), py_name("__le__")) -PKBIND_BINARY_OPERATOR(+, __add__, __radd__) -PKBIND_BINARY_OPERATOR(-, __sub__, __rsub__) -PKBIND_BINARY_OPERATOR(*, __mul__, __rmul__) -PKBIND_BINARY_OPERATOR(/, __truediv__, __rtruediv__) -PKBIND_BINARY_OPERATOR(%, __mod__, __rmod__) +PKBIND_BINARY_OPERATOR(+, py_name("__add__"), py_name("__radd__")) +PKBIND_BINARY_OPERATOR(-, py_name("__sub__"), py_name("__rsub__")) +PKBIND_BINARY_OPERATOR(*, py_name("__mul__"), py_name("__rmul__")) +PKBIND_BINARY_OPERATOR(/, py_name("__truediv__"), py_name("__rtruediv__")) +PKBIND_BINARY_OPERATOR(%, py_name("__mod__"), py_name("__rmod__")) // FIXME: support __rand__ ... -PKBIND_BINARY_OPERATOR(&, __and__, 0) -PKBIND_BINARY_OPERATOR(|, __or__, 0) -PKBIND_BINARY_OPERATOR(^, __xor__, 0) -PKBIND_BINARY_OPERATOR(<<, __lshift__, 0) -PKBIND_BINARY_OPERATOR(>>, __rshift__, 0) +PKBIND_BINARY_OPERATOR(&, py_name("__and__"), nullptr) +PKBIND_BINARY_OPERATOR(|, py_name("__or__"), nullptr) +PKBIND_BINARY_OPERATOR(^, py_name("__xor__"), nullptr) +PKBIND_BINARY_OPERATOR(<<, py_name("__lshift__"), nullptr) +PKBIND_BINARY_OPERATOR(>>, py_name("__rshift__"), nullptr) #undef PKBIND_BINARY_OPERATOR diff --git a/src/common/name.c b/src/common/name.c new file mode 100644 index 00000000..b50e1c29 --- /dev/null +++ b/src/common/name.c @@ -0,0 +1,101 @@ +#if PK_ENABLE_CUSTOM_SNAME == 0 + +#include "pocketpy/common/name.h" +#include "pocketpy/common/str.h" +#include "pocketpy/pocketpy.h" +#include + +typedef struct NameBucket NameBucket; + +typedef struct NameBucket { + NameBucket* next; + uint64_t hash; + int size; // size of the data excluding the null-terminator + char data[]; // null-terminated data +} NameBucket; + +static struct { + NameBucket* table[0x10000]; + atomic_flag lock; +} pk_string_table; + +#define MAGIC_METHOD(x) py_Name x; +#include "pocketpy/xmacros/magics.h" +#undef MAGIC_METHOD + +void pk_names_initialize() { +#define MAGIC_METHOD(x) x = py_name(#x); +#include "pocketpy/xmacros/magics.h" +#undef MAGIC_METHOD +} + +void pk_names_finalize() { + for(int i = 0; i < 0x10000; i++) { + NameBucket* p = pk_string_table.table[i]; + while(p) { + NameBucket* next = p->next; + PK_FREE(p); + p = next; + } + pk_string_table.table[i] = NULL; + } +} + +py_Name py_namev(c11_sv name) { + while(atomic_flag_test_and_set(&pk_string_table.lock)) { + // busy-wait until the lock is released + } + uint64_t hash = c11_sv__hash(name); + int index = hash & 0xFFFF; + NameBucket* p = pk_string_table.table[index]; + NameBucket* prev = NULL; + bool found = false; + while(p) { + c11_sv p_sv = {p->data, p->size}; + if(p->hash == hash && c11__sveq(p_sv, name)) { + found = true; + break; + } + prev = p; + p = p->next; + } + if(found) { + atomic_flag_clear(&pk_string_table.lock); + return (py_Name)p; + } + + // generate new index + NameBucket* bucket = PK_MALLOC(sizeof(NameBucket) + name.size + 1); + bucket->next = NULL; + bucket->hash = hash; + bucket->size = name.size; + memcpy(bucket->data, name.data, name.size); + bucket->data[name.size] = '\0'; + if(prev == NULL) { + pk_string_table.table[index] = bucket; + } else { + assert(prev->next == NULL); + prev->next = bucket; + } + atomic_flag_clear(&pk_string_table.lock); + return (py_Name)bucket; +} + +c11_sv py_name2sv(py_Name index) { + NameBucket* p = (NameBucket*)index; + return (c11_sv){p->data, p->size}; +} + +py_Name py_name(const char* name) { + c11_sv sv; + sv.data = name; + sv.size = strlen(name); + return py_namev(sv); +} + +const char* py_name2str(py_Name index) { + NameBucket* p = (NameBucket*)index; + return p->data; +} + +#endif \ No newline at end of file diff --git a/src/common/smallmap.c b/src/common/smallmap.c index 3a265158..228fd42f 100644 --- a/src/common/smallmap.c +++ b/src/common/smallmap.c @@ -1,17 +1,23 @@ #include "pocketpy/common/smallmap.h" #define SMALLMAP_T__SOURCE -#define K uint16_t +#define K py_Name #define V int -#define NAME c11_smallmap_n2i +#define NAME c11_smallmap_n2d #include "pocketpy/xmacros/smallmap.h" #undef SMALLMAP_T__SOURCE +#define SMALLMAP_T__SOURCE +#define K int +#define V int +#define NAME c11_smallmap_d2d +#include "pocketpy/xmacros/smallmap.h" +#undef SMALLMAP_T__SOURCE #define SMALLMAP_T__SOURCE #define K c11_sv -#define V uint16_t -#define NAME c11_smallmap_s2n +#define V int +#define NAME c11_smallmap_v2d #define less(a, b) (c11_sv__cmp((a), (b)) < 0) #define equal(a, b) (c11_sv__cmp((a), (b)) == 0) #include "pocketpy/xmacros/smallmap.h" diff --git a/src/common/sstream.c b/src/common/sstream.c index 87a0afc3..97c82f9b 100644 --- a/src/common/sstream.c +++ b/src/common/sstream.c @@ -224,7 +224,7 @@ void pk_vsprintf(c11_sbuf* ss, const char* fmt, va_list args) { break; } case 'n': { - py_Name n = va_arg(args, int); + py_Name n = va_arg(args, void*); c11_sbuf__write_cstr(ss, py_name2str(n)); break; } diff --git a/src/common/str.c b/src/common/str.c index 876c2c20..0982c455 100644 --- a/src/common/str.c +++ b/src/common/str.c @@ -181,6 +181,15 @@ bool c11_sv__endswith(c11_sv self, c11_sv suffix) { return memcmp(self.data + self.size - suffix.size, suffix.data, suffix.size) == 0; } +uint64_t c11_sv__hash(c11_sv self) { + uint64_t hash = 5381; + for(int i = 0; i < self.size; i++) { + // hash * 33 + c + hash = ((hash << 5) + hash) + (unsigned char)self.data[i]; + } + return hash; +} + c11_vector /* T=c11_sv */ c11_sv__split(c11_sv self, char sep) { c11_vector retval; c11_vector__ctor(&retval, sizeof(c11_sv)); diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 29c93e60..c557630b 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1,5 +1,6 @@ #include "pocketpy/compiler/compiler.h" #include "pocketpy/common/vector.h" +#include "pocketpy/common/name.h" #include "pocketpy/compiler/lexer.h" #include "pocketpy/objects/base.h" #include "pocketpy/objects/codeobject.h" @@ -61,9 +62,9 @@ typedef struct Ctx { int level; int curr_iblock; bool is_compiling_class; - c11_vector /*T=Expr* */ s_expr; - c11_smallmap_n2i global_names; - c11_smallmap_s2n co_consts_string_dedup_map; + c11_vector /*T=Expr_p*/ s_expr; + c11_smallmap_n2d global_names; + c11_smallmap_v2d co_consts_string_dedup_map; // this stores 0-based index instead of pointer } Ctx; typedef struct Expr Expr; @@ -75,11 +76,12 @@ static int Ctx__enter_block(Ctx* self, CodeBlockType type); static void Ctx__exit_block(Ctx* self); static int Ctx__emit_(Ctx* self, Opcode opcode, uint16_t arg, int line); static int Ctx__emit_virtual(Ctx* self, Opcode opcode, uint16_t arg, int line, bool virtual); -static void Ctx__revert_last_emit_(Ctx* self); +// static void Ctx__revert_last_emit_(Ctx* self); static int Ctx__emit_int(Ctx* self, int64_t value, int line); static void Ctx__patch_jump(Ctx* self, int index); static void Ctx__emit_jump(Ctx* self, int target, int line); static int Ctx__add_varname(Ctx* self, py_Name name); +static int Ctx__add_name(Ctx* self, py_Name name); static int Ctx__add_const(Ctx* self, py_Ref); static int Ctx__add_const_string(Ctx* self, c11_sv); static void Ctx__emit_store_name(Ctx* self, NameScope scope, py_Name name, int line); @@ -100,7 +102,7 @@ typedef struct NameExpr { void NameExpr__emit_(Expr* self_, Ctx* ctx) { NameExpr* self = (NameExpr*)self_; - int index = c11_smallmap_n2i__get(&ctx->co->varnames_inv, self->name, -1); + int index = c11_smallmap_n2d__get(&ctx->co->varnames_inv, self->name, -1); if(self->scope == NAME_LOCAL && index >= 0) { // we know this is a local variable Ctx__emit_(ctx, OP_LOAD_FAST, index, self->line); @@ -117,7 +119,7 @@ void NameExpr__emit_(Expr* self_, Ctx* ctx) { } } } - Ctx__emit_(ctx, op, self->name, self->line); + Ctx__emit_(ctx, op, Ctx__add_name(ctx, self->name), self->line); } } @@ -129,7 +131,7 @@ bool NameExpr__emit_del(Expr* self_, Ctx* ctx) { break; case NAME_GLOBAL: { Opcode op = ctx->co->src->is_dynamic ? OP_DELETE_NAME : OP_DELETE_GLOBAL; - Ctx__emit_(ctx, op, self->name, self->line); + Ctx__emit_(ctx, op, Ctx__add_name(ctx, self->name), self->line); break; } default: c11__unreachable(); @@ -140,7 +142,7 @@ bool NameExpr__emit_del(Expr* self_, Ctx* ctx) { bool NameExpr__emit_store(Expr* self_, Ctx* ctx) { NameExpr* self = (NameExpr*)self_; if(ctx->is_compiling_class) { - Ctx__emit_(ctx, OP_STORE_CLASS_ATTR, self->name, self->line); + Ctx__emit_(ctx, OP_STORE_CLASS_ATTR, Ctx__add_name(ctx, self->name), self->line); return true; } Ctx__emit_store_name(ctx, self->scope, self->name, self->line); @@ -710,19 +712,19 @@ static void BinaryExpr__dtor(Expr* self_) { vtdelete(self->rhs); } -static py_Name cmp_token2name(TokenIndex token) { +static Opcode cmp_token2op(TokenIndex token) { switch(token) { - case TK_LT: return __lt__; - case TK_LE: return __le__; - case TK_EQ: return __eq__; - case TK_NE: return __ne__; - case TK_GT: return __gt__; - case TK_GE: return __ge__; + case TK_LT: return OP_COMPARE_LT; + case TK_LE: return OP_COMPARE_LE; + case TK_EQ: return OP_COMPARE_EQ; + case TK_NE: return OP_COMPARE_NE; + case TK_GT: return OP_COMPARE_GT; + case TK_GE: return OP_COMPARE_GE; default: return 0; } } -#define is_compare_expr(e) ((e)->vt->is_binary && cmp_token2name(((BinaryExpr*)(e))->op)) +#define is_compare_expr(e) ((e)->vt->is_binary && cmp_token2op(((BinaryExpr*)(e))->op)) static void _emit_compare(BinaryExpr* self, Ctx* ctx, c11_vector* jmps) { if(is_compare_expr(self->lhs)) { @@ -733,7 +735,7 @@ static void _emit_compare(BinaryExpr* self, Ctx* ctx, c11_vector* jmps) { vtemit_(self->rhs, ctx); // [a, b] Ctx__emit_(ctx, OP_DUP_TOP, BC_NOARG, self->line); // [a, b, b] Ctx__emit_(ctx, OP_ROT_THREE, BC_NOARG, self->line); // [b, a, b] - Ctx__emit_(ctx, OP_BINARY_OP, cmp_token2name(self->op), self->line); + Ctx__emit_(ctx, cmp_token2op(self->op), BC_NOARG, self->line); // [b, RES] int index = Ctx__emit_(ctx, OP_SHORTCUT_IF_FALSE_OR_POP, BC_NOARG, self->line); c11_vector__push(int, jmps, index); @@ -743,7 +745,7 @@ static void BinaryExpr__emit_(Expr* self_, Ctx* ctx) { BinaryExpr* self = (BinaryExpr*)self_; c11_vector /*T=int*/ jmps; c11_vector__ctor(&jmps, sizeof(int)); - if(cmp_token2name(self->op) && is_compare_expr(self->lhs)) { + if(cmp_token2op(self->op) && is_compare_expr(self->lhs)) { // (a < b) < c BinaryExpr* e = (BinaryExpr*)self->lhs; _emit_compare(e, ctx, &jmps); @@ -759,24 +761,24 @@ static void BinaryExpr__emit_(Expr* self_, Ctx* ctx) { vtemit_(self->rhs, ctx); - Opcode opcode = OP_BINARY_OP; + Opcode opcode; uint16_t arg = BC_NOARG; switch(self->op) { - case TK_ADD: arg = __add__ | (__radd__ << 8); break; - case TK_SUB: arg = __sub__ | (__rsub__ << 8); break; - case TK_MUL: arg = __mul__ | (__rmul__ << 8); break; - case TK_DIV: arg = __truediv__ | (__rtruediv__ << 8); break; - case TK_FLOORDIV: arg = __floordiv__ | (__rfloordiv__ << 8); break; - case TK_MOD: arg = __mod__ | (__rmod__ << 8); break; - case TK_POW: arg = __pow__ | (__rpow__ << 8); break; + case TK_ADD: opcode = OP_BINARY_ADD; break; + case TK_SUB: opcode = OP_BINARY_SUB; break; + case TK_MUL: opcode = OP_BINARY_MUL; break; + case TK_DIV: opcode = OP_BINARY_TRUEDIV; break; + case TK_FLOORDIV: opcode = OP_BINARY_FLOORDIV; break; + case TK_MOD: opcode = OP_BINARY_MOD; break; + case TK_POW: opcode = OP_BINARY_POW; break; - case TK_LT: arg = __lt__ | (__gt__ << 8); break; - case TK_LE: arg = __le__ | (__ge__ << 8); break; - case TK_EQ: arg = __eq__ | (__eq__ << 8); break; - case TK_NE: arg = __ne__ | (__ne__ << 8); break; - case TK_GT: arg = __gt__ | (__lt__ << 8); break; - case TK_GE: arg = __ge__ | (__le__ << 8); break; + case TK_LT: opcode = OP_COMPARE_LT; break; + case TK_LE: opcode = OP_COMPARE_LE; break; + case TK_EQ: opcode = OP_COMPARE_EQ; break; + case TK_NE: opcode = OP_COMPARE_NE; break; + case TK_GT: opcode = OP_COMPARE_GT; break; + case TK_GE: opcode = OP_COMPARE_GE; break; case TK_IN: opcode = OP_CONTAINS_OP; @@ -795,13 +797,13 @@ static void BinaryExpr__emit_(Expr* self_, Ctx* ctx) { arg = 1; break; - case TK_LSHIFT: arg = __lshift__; break; - case TK_RSHIFT: arg = __rshift__; break; - case TK_AND: arg = __and__; break; - case TK_OR: arg = __or__; break; - case TK_XOR: arg = __xor__; break; - case TK_DECORATOR: arg = __matmul__; break; - default: assert(false); + case TK_LSHIFT: opcode = OP_BINARY_LSHIFT; break; + case TK_RSHIFT: opcode = OP_BINARY_RSHIFT; break; + case TK_AND: opcode = OP_BINARY_AND; break; + case TK_OR: opcode = OP_BINARY_OR; break; + case TK_XOR: opcode = OP_BINARY_XOR; break; + case TK_DECORATOR: opcode = OP_BINARY_MATMUL; break; + default: c11__unreachable(); } Ctx__emit_(ctx, opcode, arg, self->line); @@ -945,20 +947,20 @@ void AttribExpr__dtor(Expr* self_) { void AttribExpr__emit_(Expr* self_, Ctx* ctx) { AttribExpr* self = (AttribExpr*)self_; vtemit_(self->child, ctx); - Ctx__emit_(ctx, OP_LOAD_ATTR, self->name, self->line); + Ctx__emit_(ctx, OP_LOAD_ATTR, Ctx__add_name(ctx, self->name), self->line); } bool AttribExpr__emit_del(Expr* self_, Ctx* ctx) { AttribExpr* self = (AttribExpr*)self_; vtemit_(self->child, ctx); - Ctx__emit_(ctx, OP_DELETE_ATTR, self->name, self->line); + Ctx__emit_(ctx, OP_DELETE_ATTR, Ctx__add_name(ctx, self->name), self->line); return true; } bool AttribExpr__emit_store(Expr* self_, Ctx* ctx) { AttribExpr* self = (AttribExpr*)self_; vtemit_(self->child, ctx); - Ctx__emit_(ctx, OP_STORE_ATTR, self->name, self->line); + Ctx__emit_(ctx, OP_STORE_ATTR, Ctx__add_name(ctx, self->name), self->line); return true; } @@ -966,14 +968,14 @@ void AttribExpr__emit_inplace(Expr* self_, Ctx* ctx) { AttribExpr* self = (AttribExpr*)self_; vtemit_(self->child, ctx); Ctx__emit_(ctx, OP_DUP_TOP, BC_NOARG, self->line); - Ctx__emit_(ctx, OP_LOAD_ATTR, self->name, self->line); + Ctx__emit_(ctx, OP_LOAD_ATTR, Ctx__add_name(ctx, self->name), self->line); } bool AttribExpr__emit_istore(Expr* self_, Ctx* ctx) { // [a, val] -> [val, a] AttribExpr* self = (AttribExpr*)self_; Ctx__emit_(ctx, OP_ROT_TWO, BC_NOARG, self->line); - Ctx__emit_(ctx, OP_STORE_ATTR, self->name, self->line); + Ctx__emit_(ctx, OP_STORE_ATTR, Ctx__add_name(ctx, self->name), self->line); return true; } @@ -1031,7 +1033,7 @@ void CallExpr__emit_(Expr* self_, Ctx* ctx) { if(self->callable->vt->is_attrib) { AttribExpr* p = (AttribExpr*)self->callable; vtemit_(p->child, ctx); - Ctx__emit_(ctx, OP_LOAD_METHOD, p->name, p->line); + Ctx__emit_(ctx, OP_LOAD_METHOD, Ctx__add_name(ctx, p->name), p->line); } else { vtemit_(self->callable, ctx); Ctx__emit_(ctx, OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE); @@ -1046,7 +1048,7 @@ void CallExpr__emit_(Expr* self_, Ctx* ctx) { c11__foreach(Expr*, &self->args, e) { vtemit_(*e, ctx); } c11__foreach(CallExprKwArg, &self->kwargs, e) { - Ctx__emit_int(ctx, e->key, self->line); + Ctx__emit_int(ctx, (uintptr_t)e->key, self->line); vtemit_(e->val, ctx); } int KWARGC = self->kwargs.length; @@ -1074,8 +1076,8 @@ static void Ctx__ctor(Ctx* self, CodeObject* co, FuncDecl* func, int level) { self->curr_iblock = 0; self->is_compiling_class = false; c11_vector__ctor(&self->s_expr, sizeof(Expr*)); - c11_smallmap_n2i__ctor(&self->global_names); - c11_smallmap_s2n__ctor(&self->co_consts_string_dedup_map); + c11_smallmap_n2d__ctor(&self->global_names); + c11_smallmap_v2d__ctor(&self->co_consts_string_dedup_map); } static void Ctx__dtor(Ctx* self) { @@ -1084,13 +1086,13 @@ static void Ctx__dtor(Ctx* self) { vtdelete(c11__getitem(Expr*, &self->s_expr, i)); } c11_vector__dtor(&self->s_expr); - c11_smallmap_n2i__dtor(&self->global_names); + c11_smallmap_n2d__dtor(&self->global_names); // free the dedup map - c11__foreach(c11_smallmap_s2n_KV, &self->co_consts_string_dedup_map, p_kv) { + c11__foreach(c11_smallmap_v2d_KV, &self->co_consts_string_dedup_map, p_kv) { const char* p = p_kv->key.data; PK_FREE((void*)p); } - c11_smallmap_s2n__dtor(&self->co_consts_string_dedup_map); + c11_smallmap_v2d__dtor(&self->co_consts_string_dedup_map); } static int Ctx__prepare_loop_divert(Ctx* self, int line, bool is_break) { @@ -1166,10 +1168,10 @@ static int Ctx__emit_(Ctx* self, Opcode opcode, uint16_t arg, int line) { return Ctx__emit_virtual(self, opcode, arg, line, false); } -static void Ctx__revert_last_emit_(Ctx* self) { - c11_vector__pop(&self->co->codes); - c11_vector__pop(&self->co->codes_ex); -} +// static void Ctx__revert_last_emit_(Ctx* self) { +// c11_vector__pop(&self->co->codes); +// c11_vector__pop(&self->co->codes_ex); +// } static int Ctx__emit_int(Ctx* self, int64_t value, int line) { if(INT16_MIN <= value && value <= INT16_MAX) { @@ -1199,6 +1201,8 @@ static int Ctx__add_varname(Ctx* self, py_Name name) { return CodeObject__add_varname(self->co, name); } +static int Ctx__add_name(Ctx* self, py_Name name) { return CodeObject__add_name(self->co, name); } + static int Ctx__add_const_string(Ctx* self, c11_sv key) { if(key.size > 100) { py_Ref tmp = c11_vector__emplace(&self->co->consts); @@ -1206,7 +1210,7 @@ static int Ctx__add_const_string(Ctx* self, c11_sv key) { int index = self->co->consts.length - 1; return index; } - uint16_t* val = c11_smallmap_s2n__try_get(&self->co_consts_string_dedup_map, key); + int* val = c11_smallmap_v2d__try_get(&self->co_consts_string_dedup_map, key); if(val) { return *val; } else { @@ -1217,7 +1221,7 @@ static int Ctx__add_const_string(Ctx* self, c11_sv key) { char* new_buf = PK_MALLOC(key.size + 1); memcpy(new_buf, key.data, key.size); new_buf[key.size] = 0; - c11_smallmap_s2n__set(&self->co_consts_string_dedup_map, + c11_smallmap_v2d__set(&self->co_consts_string_dedup_map, (c11_sv){new_buf, key.size}, index); return index; @@ -1235,7 +1239,7 @@ static void Ctx__emit_store_name(Ctx* self, NameScope scope, py_Name name, int l case NAME_LOCAL: Ctx__emit_(self, OP_STORE_FAST, Ctx__add_varname(self, name), line); break; case NAME_GLOBAL: { Opcode op = self->co->src->is_dynamic ? OP_STORE_NAME : OP_STORE_GLOBAL; - Ctx__emit_(self, op, name, line); + Ctx__emit_(self, op, Ctx__add_name(self, name), line); } break; default: c11__unreachable(); } @@ -1742,7 +1746,7 @@ static Error* exprName(Compiler* self) { py_Name name = py_namev(Token__sv(prev())); NameScope scope = name_scope(self); // promote this name to global scope if needed - if(c11_smallmap_n2i__contains(&ctx()->global_names, name)) { + if(c11_smallmap_n2d__contains(&ctx()->global_names, name)) { if(self->src->is_dynamic) return SyntaxError(self, "cannot use global keyword here"); scope = NAME_GLOBAL; } @@ -2371,7 +2375,7 @@ static Error* compile_function(Compiler* self, int decorators) { } } - Ctx__emit_(ctx(), OP_STORE_CLASS_ATTR, decl_name, prev()->line); + Ctx__emit_(ctx(), OP_STORE_CLASS_ATTR, Ctx__add_name(ctx(), decl_name), prev()->line); } else { NameExpr* e = NameExpr__new(prev()->line, decl_name, name_scope(self)); vtemit_store((Expr*)e, ctx()); @@ -2399,7 +2403,7 @@ static Error* compile_class(Compiler* self, int decorators) { } else { Ctx__s_emit_top(ctx()); // [] } - Ctx__emit_(ctx(), OP_BEGIN_CLASS, name, BC_KEEPLINE); + Ctx__emit_(ctx(), OP_BEGIN_CLASS, Ctx__add_name(ctx(), name), BC_KEEPLINE); c11__foreach(Ctx, &self->contexts, it) { if(it->is_compiling_class) return SyntaxError(self, "nested class is not allowed"); @@ -2409,7 +2413,7 @@ static Error* compile_class(Compiler* self, int decorators) { ctx()->is_compiling_class = false; Ctx__s_emit_decorators(ctx(), decorators); - Ctx__emit_(ctx(), OP_END_CLASS, name, BC_KEEPLINE); + Ctx__emit_(ctx(), OP_END_CLASS, Ctx__add_name(ctx(), name), BC_KEEPLINE); return NULL; } @@ -2517,7 +2521,7 @@ __EAT_DOTS_END: Ctx__emit_(ctx(), OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); consume(TK_ID); c11_sv name = Token__sv(prev()); - Ctx__emit_(ctx(), OP_LOAD_ATTR, py_namev(name), prev()->line); + Ctx__emit_(ctx(), OP_LOAD_ATTR, Ctx__add_name(ctx(), py_namev(name)), prev()->line); if(match(TK_AS)) { consume(TK_ID); name = Token__sv(prev()); @@ -2722,7 +2726,7 @@ static Error* compile_stmt(Compiler* self) { do { consume(TK_ID); py_Name name = py_namev(Token__sv(prev())); - c11_smallmap_n2i__set(&ctx()->global_names, name, 0); + c11_smallmap_n2d__set(&ctx()->global_names, name, 0); } while(match(TK_COMMA)); consume_end_stmt(); break; @@ -2783,7 +2787,10 @@ static Error* compile_stmt(Compiler* self) { NameExpr* ne = (NameExpr*)Ctx__s_top(ctx()); int index = Ctx__add_const_string(ctx(), type_hint); Ctx__emit_(ctx(), OP_LOAD_CONST, index, BC_KEEPLINE); - Ctx__emit_(ctx(), OP_ADD_CLASS_ANNOTATION, ne->name, BC_KEEPLINE); + Ctx__emit_(ctx(), + OP_ADD_CLASS_ANNOTATION, + Ctx__add_name(ctx(), ne->name), + BC_KEEPLINE); } } } diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index dd4bc08f..0921392f 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -70,7 +70,7 @@ static bool unpack_dict_to_buffer(py_Ref key, py_Ref val, void* ctx) { py_TValue** p = ctx; if(py_isstr(key)) { py_Name name = py_namev(py_tosv(key)); - py_newint(*p, name); + py_newint(*p, (uintptr_t)name); py_assign(*p + 1, val); (*p) += 2; return true; @@ -80,7 +80,8 @@ static bool unpack_dict_to_buffer(py_Ref key, py_Ref val, void* ctx) { FrameResult VM__run_top_frame(VM* self) { py_Frame* frame = self->top_frame; - Bytecode* codes; + Bytecode* co_codes; + py_Name* co_names; const py_Frame* base_frame = frame; @@ -91,11 +92,13 @@ FrameResult VM__run_top_frame(VM* self) { py_exception(tp_RecursionError, "maximum recursion depth exceeded"); goto __ERROR; } - codes = frame->co->codes.data; + // NOTE: remember to change another occurrence after __ERROR_RE_RAISE: + co_codes = frame->co->codes.data; + co_names = frame->co->names.data; frame->ip++; __NEXT_STEP: - byte = codes[frame->ip]; + byte = co_codes[frame->ip]; if(self->trace_info.func) { SourceLocation loc = Frame__source_location(frame); @@ -195,7 +198,7 @@ FrameResult VM__run_top_frame(VM* self) { ud->closure = FastLocals__to_namedict(frame->locals, frame->co); py_Name name = py_name(decl->code.name->data); // capture itself to allow recursion - NameDict__set(ud->closure, name, *SP()); + NameDict__set(ud->closure, name, SP()); } SP()++; DISPATCH(); @@ -211,13 +214,13 @@ FrameResult VM__run_top_frame(VM* self) { PUSH(val); DISPATCH(); } - py_Name name = c11__getitem(uint16_t, &frame->co->varnames, byte.arg); + py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg); UnboundLocalError(name); goto __ERROR; } case OP_LOAD_NAME: { assert(frame->is_locals_special); - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; // locals switch(frame->locals->type) { case tp_locals: { @@ -261,7 +264,7 @@ FrameResult VM__run_top_frame(VM* self) { goto __ERROR; } case OP_LOAD_NONLOCAL: { - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; py_Ref tmp = Frame__getclosure(frame, name); if(tmp != NULL) { PUSH(tmp); @@ -283,7 +286,7 @@ FrameResult VM__run_top_frame(VM* self) { goto __ERROR; } case OP_LOAD_GLOBAL: { - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; int res = Frame__getglobal(frame, name); if(res == 1) { PUSH(&self->last_retval); @@ -299,7 +302,8 @@ FrameResult VM__run_top_frame(VM* self) { goto __ERROR; } case OP_LOAD_ATTR: { - if(py_getattr(TOP(), byte.arg)) { + py_Name name = co_names[byte.arg]; + if(py_getattr(TOP(), name)) { py_assign(TOP(), py_retval()); } else { goto __ERROR; @@ -308,7 +312,7 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_LOAD_CLASS_GLOBAL: { assert(self->curr_class); - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; py_Ref tmp = py_getdict(self->curr_class, name); if(tmp) { PUSH(tmp); @@ -331,10 +335,11 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_LOAD_METHOD: { // [self] -> [unbound, self] - bool ok = py_pushmethod(byte.arg); + py_Name name = co_names[byte.arg]; + bool ok = py_pushmethod(name); if(!ok) { // fallback to getattr - if(py_getattr(TOP(), byte.arg)) { + if(py_getattr(TOP(), name)) { py_assign(TOP(), py_retval()); py_newnil(SP()++); } else { @@ -368,7 +373,7 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_STORE_NAME: { assert(frame->is_locals_special); - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; switch(frame->locals->type) { case tp_locals: { py_Frame* noproxy = frame->locals->_ptr; @@ -395,13 +400,15 @@ FrameResult VM__run_top_frame(VM* self) { } } case OP_STORE_GLOBAL: { - if(!Frame__setglobal(frame, byte.arg, TOP())) goto __ERROR; + py_Name name = co_names[byte.arg]; + if(!Frame__setglobal(frame, name, TOP())) goto __ERROR; POP(); DISPATCH(); } case OP_STORE_ATTR: { // [val, a] -> a.b = val - if(!py_setattr(TOP(), byte.arg, SECOND())) goto __ERROR; + py_Name name = co_names[byte.arg]; + if(!py_setattr(TOP(), name, SECOND())) goto __ERROR; STACK_SHRINK(2); DISPATCH(); } @@ -435,7 +442,7 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_DELETE_NAME: { assert(frame->is_locals_special); - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; switch(frame->locals->type) { case tp_locals: { py_Frame* noproxy = frame->locals->_ptr; @@ -464,7 +471,7 @@ FrameResult VM__run_top_frame(VM* self) { } } case OP_DELETE_GLOBAL: { - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; int res = Frame__delglobal(frame, name); if(res == 1) DISPATCH(); if(res == -1) goto __ERROR; @@ -473,7 +480,8 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_DELETE_ATTR: { - if(!py_delattr(TOP(), byte.arg)) goto __ERROR; + py_Name name = co_names[byte.arg]; + if(!py_delattr(TOP(), name)) goto __ERROR; DISPATCH(); } @@ -594,14 +602,33 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } /*****************************/ - case OP_BINARY_OP: { - py_Name op = byte.arg & 0xFF; - py_Name rop = byte.arg >> 8; - if(!pk_stack_binaryop(self, op, rop)) goto __ERROR; - POP(); - *TOP() = self->last_retval; - DISPATCH(); +#define CASE_BINARY_OP(label, op, rop)\ + case label: {\ + if(!pk_stack_binaryop(self, op, rop)) goto __ERROR;\ + POP();\ + *TOP() = self->last_retval;\ + DISPATCH();\ } + CASE_BINARY_OP(OP_BINARY_ADD, __add__, __radd__) + CASE_BINARY_OP(OP_BINARY_SUB, __sub__, __rsub__) + CASE_BINARY_OP(OP_BINARY_MUL, __mul__, __rmul__) + CASE_BINARY_OP(OP_BINARY_TRUEDIV, __truediv__, __rtruediv__) + CASE_BINARY_OP(OP_BINARY_FLOORDIV, __floordiv__, __rfloordiv__) + CASE_BINARY_OP(OP_BINARY_MOD, __mod__, __rmod__) + CASE_BINARY_OP(OP_BINARY_POW, __pow__, __rpow__) + CASE_BINARY_OP(OP_BINARY_LSHIFT, __lshift__, 0) + CASE_BINARY_OP(OP_BINARY_RSHIFT, __rshift__, 0) + CASE_BINARY_OP(OP_BINARY_AND, __and__, 0) + CASE_BINARY_OP(OP_BINARY_OR, __or__, 0) + CASE_BINARY_OP(OP_BINARY_XOR, __xor__, 0) + CASE_BINARY_OP(OP_BINARY_MATMUL, __matmul__, 0) + CASE_BINARY_OP(OP_COMPARE_LT, __lt__, __gt__) + CASE_BINARY_OP(OP_COMPARE_LE, __le__, __ge__) + CASE_BINARY_OP(OP_COMPARE_EQ, __eq__, __eq__) + CASE_BINARY_OP(OP_COMPARE_NE, __ne__, __ne__) + CASE_BINARY_OP(OP_COMPARE_GT, __gt__, __lt__) + CASE_BINARY_OP(OP_COMPARE_GE, __ge__, __le__) +#undef CASE_BINARY_OP case OP_IS_OP: { bool res = py_isidentical(SECOND(), TOP()); POP(); @@ -875,7 +902,7 @@ FrameResult VM__run_top_frame(VM* self) { case OP_POP_IMPORT_STAR: { // [module] NameDict* dict = PyObject__dict(TOP()->_obj); - py_Ref all = NameDict__try_get(dict, __all__); + py_ItemRef all = NameDict__try_get(dict, __all__); if(all) { py_TValue* p; int length = pk_arrayview(all, &p); @@ -885,7 +912,7 @@ FrameResult VM__run_top_frame(VM* self) { } for(int i = 0; i < length; i++) { py_Name name = py_namev(py_tosv(p + i)); - py_Ref value = NameDict__try_get(dict, name); + py_ItemRef value = NameDict__try_get(dict, name); if(value == NULL) { ImportError("cannot import name '%n'", name); goto __ERROR; @@ -894,9 +921,9 @@ FrameResult VM__run_top_frame(VM* self) { } } } else { - for(int i = 0; i < dict->length; i++) { - NameDict_KV* kv = c11__at(NameDict_KV, dict, i); - if(!kv->key) continue; + for(int i = 0; i < dict->capacity; i++) { + NameDict_KV* kv = &dict->items[i]; + if(kv->key == NULL) continue; c11_sv name = py_name2sv(kv->key); if(name.size == 0 || name.data[0] == '_') continue; if(!Frame__setglobal(frame, kv->key, &kv->value)) goto __ERROR; @@ -999,7 +1026,7 @@ FrameResult VM__run_top_frame(VM* self) { /////////// case OP_BEGIN_CLASS: { // [base] - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; py_Type base; if(py_isnone(TOP())) { base = tp_object; @@ -1027,7 +1054,7 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_END_CLASS: { // [cls or decorated] - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; if(!Frame__setglobal(frame, name, TOP())) goto __ERROR; if(py_istype(TOP(), tp_type)) { @@ -1037,9 +1064,9 @@ FrameResult VM__run_top_frame(VM* self) { py_TypeInfo* base_ti = ti->base_ti; if(base_ti->on_end_subclass) base_ti->on_end_subclass(ti); } - py_TValue* slot_eq = TypeList__magic_common(ti, __eq__); - py_TValue* slot_ne = TypeList__magic_common(ti, __ne__); - if(!py_isnil(slot_eq) && py_isnil(slot_ne)) { + py_TValue* slot_eq = py_getdict(&ti->self, __eq__); + py_TValue* slot_ne = py_getdict(&ti->self, __ne__); + if(slot_eq && !slot_ne) { TypeError("'%n' implements '__eq__' but not '__ne__'", ti->name); goto __ERROR; } @@ -1052,7 +1079,7 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_STORE_CLASS_ATTR: { assert(self->curr_class); - py_Name name = byte.arg; + py_Name name = co_names[byte.arg]; // TOP() can be a function, classmethod or custom decorator py_Ref actual_func = TOP(); if(actual_func->type == tp_classmethod) { @@ -1072,7 +1099,8 @@ FrameResult VM__run_top_frame(VM* self) { py_Type type = py_totype(self->curr_class); py_TypeInfo* ti = TypeList__get(&self->types, type); if(py_isnil(&ti->annotations)) py_newdict(&ti->annotations); - bool ok = py_dict_setitem_by_str(&ti->annotations, py_name2str(byte.arg), TOP()); + py_Name name = co_names[byte.arg]; + bool ok = py_dict_setitem_by_str(&ti->annotations, py_name2str(name), TOP()); if(!ok) goto __ERROR; POP(); DISPATCH(); @@ -1211,7 +1239,8 @@ FrameResult VM__run_top_frame(VM* self) { return RES_ERROR; } frame = self->top_frame; - codes = frame->co->codes.data; + co_codes = frame->co->codes.data; + co_names = frame->co->names.data; goto __ERROR; } } @@ -1220,30 +1249,28 @@ FrameResult VM__run_top_frame(VM* self) { } const char* pk_op2str(py_Name op) { - switch(op) { - case __eq__: return "=="; - case __ne__: return "!="; - case __lt__: return "<"; - case __le__: return "<="; - case __gt__: return ">"; - case __ge__: return ">="; - case __add__: return "+"; - case __sub__: return "-"; - case __mul__: return "*"; - case __truediv__: return "/"; - case __floordiv__: return "//"; - case __mod__: return "%"; - case __pow__: return "**"; - case __lshift__: return "<<"; - case __rshift__: return ">>"; - case __and__: return "&"; - case __or__: return "|"; - case __xor__: return "^"; - case __neg__: return "-"; - case __invert__: return "~"; - case __matmul__: return "@"; - default: return py_name2str(op); - } + if(__eq__ == op) return "=="; + if(__ne__ == op) return "!="; + if(__lt__ == op) return "<"; + if(__le__ == op) return "<="; + if(__gt__ == op) return ">"; + if(__ge__ == op) return ">="; + if(__add__ == op) return "+"; + if(__sub__ == op) return "-"; + if(__mul__ == op) return "*"; + if(__truediv__ == op) return "/"; + if(__floordiv__ == op) return "//"; + if(__mod__ == op) return "%"; + if(__pow__ == op) return "**"; + if(__lshift__ == op) return "<<"; + if(__rshift__ == op) return ">>"; + if(__and__ == op) return "&"; + if(__or__ == op) return "|"; + if(__xor__ == op) return "^"; + if(__neg__ == op) return "-"; + if(__invert__ == op) return "~"; + if(__matmul__ == op) return "@"; + return py_name2str(op); } bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) { @@ -1289,6 +1316,58 @@ bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) { return ok; } +bool py_binaryadd(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __add__, __radd__); +} + +bool py_binarysub(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __sub__, __rsub__); +} + +bool py_binarymul(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __mul__, __rmul__); +} + +bool py_binarytruediv(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __truediv__, __rtruediv__); +} + +bool py_binaryfloordiv(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __floordiv__, __rfloordiv__); +} + +bool py_binarymod(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __mod__, __rmod__); +} + +bool py_binarypow(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __pow__, __rpow__); +} + +bool py_binarylshift(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __lshift__, 0); +} + +bool py_binaryrshift(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __rshift__, 0); +} + +bool py_binaryand(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __and__, 0); +} + +bool py_binaryor(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __or__, 0); +} + +bool py_binaryxor(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __xor__, 0); +} + +bool py_binarymatmul(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __matmul__, 0); +} + static bool stack_format_object(VM* self, c11_sv spec) { // format TOS via `spec` inplace // spec: '!r:.2f', '.2f' diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 40ecfb2a..47853917 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -16,7 +16,7 @@ void ValueStack__dtor(ValueStack* self) { self->sp = self->begin; } void FastLocals__to_dict(py_TValue* locals, const CodeObject* co) { py_StackRef dict = py_pushtmp(); py_newdict(dict); - c11__foreach(c11_smallmap_n2i_KV, &co->varnames_inv, entry) { + c11__foreach(c11_smallmap_n2d_KV, &co->varnames_inv, entry) { py_TValue* value = &locals[entry->value]; if(!py_isnil(value)) { bool ok = py_dict_setitem(dict, py_name2ref(entry->key), value); @@ -29,10 +29,10 @@ void FastLocals__to_dict(py_TValue* locals, const CodeObject* co) { } NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co) { - NameDict* dict = NameDict__new(); - c11__foreach(c11_smallmap_n2i_KV, &co->varnames_inv, entry) { - py_TValue value = locals[entry->value]; - if(!py_isnil(&value)) NameDict__set(dict, entry->key, value); + NameDict* dict = NameDict__new(PK_INST_ATTR_LOAD_FACTOR); + c11__foreach(c11_smallmap_n2d_KV, &co->varnames_inv, entry) { + py_Ref val = &locals[entry->value]; + if(!py_isnil(val)) NameDict__set(dict, entry->key, val); } return dict; } @@ -166,7 +166,7 @@ int Frame__delglobal(py_Frame* self, py_Name name) { py_StackRef Frame__getlocal_noproxy(py_Frame* self, py_Name name) { assert(!self->is_locals_special); - int index = c11_smallmap_n2i__get(&self->co->varnames_inv, name, -1); + int index = c11_smallmap_n2d__get(&self->co->varnames_inv, name, -1); if(index == -1) return NULL; return &self->locals[index]; } diff --git a/src/interpreter/heap.c b/src/interpreter/heap.c index 37b1d2ff..e50aaceb 100644 --- a/src/interpreter/heap.c +++ b/src/interpreter/heap.c @@ -44,7 +44,8 @@ void ManagedHeap__collect_if_needed(ManagedHeap* self) { const int lower = PK_GC_MIN_THRESHOLD / 2; float free_ratio = (float)avg_freed / self->gc_threshold; int new_threshold = self->gc_threshold * (1 / free_ratio); - // printf("gc_threshold=%d, avg_freed=%d, new_threshold=%d\n", self->gc_threshold, avg_freed, new_threshold); + // printf("gc_threshold=%d, avg_freed=%d, new_threshold=%d\n", self->gc_threshold, avg_freed, + // new_threshold); self->gc_threshold = c11__min(c11__max(new_threshold, lower), upper); } @@ -98,7 +99,9 @@ PyObject* ManagedHeap__gcnew(ManagedHeap* self, py_Type type, int slots, int uds if(slots >= 0) { memset(obj->flex, 0, slots * sizeof(py_TValue)); } else { - NameDict__ctor((void*)obj->flex); + float load_factor = (type == tp_type || type == tp_module) ? PK_TYPE_ATTR_LOAD_FACTOR + : PK_INST_ATTR_LOAD_FACTOR; + NameDict__ctor((void*)obj->flex, load_factor); } self->gc_counter++; diff --git a/src/interpreter/name.c b/src/interpreter/name.c deleted file mode 100644 index 71966ee1..00000000 --- a/src/interpreter/name.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "pocketpy/interpreter/name.h" -#include "pocketpy/interpreter/vm.h" - -void InternedNames__ctor(InternedNames* self) { - c11_smallmap_s2n__ctor(&self->interned); - c11_vector__ctor(&self->r_interned, sizeof(RInternedEntry)); - - // initialize all magic names -#define MAGIC_METHOD(x) \ - if(x != py_name(#x)) abort(); -#include "pocketpy/xmacros/magics.h" -#undef MAGIC_METHOD -} - -void InternedNames__dtor(InternedNames* self) { - for(int i = 0; i < self->r_interned.length; i++) { - PK_FREE(c11__getitem(RInternedEntry, &self->r_interned, i).data); - } - c11_smallmap_s2n__dtor(&self->interned); - c11_vector__dtor(&self->r_interned); -} - -py_Name py_name(const char* name) { - c11_sv sv; - sv.data = name; - sv.size = strlen(name); - return py_namev(sv); -} - -py_Name py_namev(c11_sv name) { - InternedNames* self = &pk_current_vm->names; - uint16_t index = c11_smallmap_s2n__get(&self->interned, name, 0); - if(index != 0) return index; - // generate new index - if(self->interned.length > 65530) c11__abort("py_Name index overflow"); - // NOTE: we must allocate the string in the heap so iterators are not invalidated - char* p = PK_MALLOC(name.size + 1); - memcpy(p, name.data, name.size); - p[name.size] = '\0'; - RInternedEntry* entry = c11_vector__emplace(&self->r_interned); - entry->data = p; - entry->size = name.size; - memset(&entry->obj, 0, sizeof(py_TValue)); - index = self->r_interned.length; // 1-based - // save to _interned - c11_smallmap_s2n__set(&self->interned, (c11_sv){p, name.size}, index); - assert(self->interned.length == self->r_interned.length); - return index; -} - -const char* py_name2str(py_Name index) { - InternedNames* self = &pk_current_vm->names; - assert(index > 0 && index <= self->interned.length); - return c11__getitem(RInternedEntry, &self->r_interned, index - 1).data; -} - -c11_sv py_name2sv(py_Name index) { - InternedNames* self = &pk_current_vm->names; - assert(index > 0 && index <= self->interned.length); - RInternedEntry entry = c11__getitem(RInternedEntry, &self->r_interned, index - 1); - return (c11_sv){entry.data, entry.size}; -} - -py_GlobalRef py_name2ref(py_Name index) { - InternedNames* self = &pk_current_vm->names; - assert(index > 0 && index <= self->interned.length); - RInternedEntry* entry = c11__at(RInternedEntry, &self->r_interned, index - 1); - if(entry->obj.type == tp_nil) { - c11_sv sv; - sv.data = entry->data; - sv.size = entry->size; - py_newstrv(&entry->obj, sv); - } - return &entry->obj; -} diff --git a/src/interpreter/typeinfo.c b/src/interpreter/typeinfo.c index 35178c6b..7d352ae6 100644 --- a/src/interpreter/typeinfo.c +++ b/src/interpreter/typeinfo.c @@ -12,7 +12,7 @@ void TypeList__ctor(TypeList* self) { void TypeList__dtor(TypeList* self) { for(py_Type t = 0; t < self->length; t++) { py_TypeInfo* info = TypeList__get(self, t); - if(info->magic_1) PK_FREE(info->magic_1); + (void)info; } for(int i = 0; i < PK_MAX_CHUNK_LENGTH; i++) { if(self->chunks[i]) PK_FREE(self->chunks[i]); @@ -47,28 +47,5 @@ void TypeList__apply(TypeList* self, void (*f)(py_TypeInfo*, void*), void* ctx) } } -py_TValue* TypeList__magic(py_TypeInfo* self, unsigned index) { - if(index > __xor__) { - // common magic slots - return TypeList__magic_common(self, index); - } - // uncommon magic slots - if(self->magic_1 == NULL) { - self->magic_1 = PK_MALLOC(sizeof(py_TValue) * PK_MAGIC_SLOTS_UNCOMMON_LENGTH); - memset(self->magic_1, 0, sizeof(py_TValue) * PK_MAGIC_SLOTS_UNCOMMON_LENGTH); - } - return self->magic_1 + index; -} - -py_TValue* TypeList__magic_readonly(py_TypeInfo* self, unsigned index) { - if(index > __xor__) { - // common magic slots - return TypeList__magic_common(self, index); - } - // uncommon magic slots - if(self->magic_1 == NULL) return py_NIL(); - return self->magic_1 + index; -} - #undef CHUNK_SIZE #undef LOG2_CHUNK_SIZE \ No newline at end of file diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index d9986d8a..bc6ba8ad 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -65,7 +65,6 @@ static void py_TypeInfo__ctor(py_TypeInfo* self, void VM__ctor(VM* self) { self->top_frame = NULL; - InternedNames__ctor(&self->names); ModuleDict__ctor(&self->modules, "", *py_NIL()); TypeList__ctor(&self->types); @@ -97,6 +96,7 @@ void VM__ctor(VM* self) { ManagedHeap__ctor(&self->heap); ValueStack__ctor(&self->stack); + NameDict__ctor(&self->cached_names, PK_INST_ATTR_LOAD_FACTOR); /* Init Builtin Types */ // 0: unused @@ -274,7 +274,7 @@ void VM__dtor(VM* self) { TypeList__dtor(&self->types); FixedMemoryPool__dtor(&self->pool_frame); ValueStack__dtor(&self->stack); - InternedNames__dtor(&self->names); + NameDict__dtor(&self->cached_names); } void VM__push_frame(VM* self, py_Frame* frame) { @@ -442,8 +442,8 @@ static bool if(decl->starred_kwarg != -1) py_newdict(&buffer[decl->starred_kwarg]); for(int j = 0; j < kwargc; j++) { - py_Name key = py_toint(&p1[2 * j]); - int index = c11_smallmap_n2i__get(&decl->kw_to_index, key, -1); + py_Name key = (py_Name)py_toint(&p1[2 * j]); + int index = c11_smallmap_n2d__get(&decl->kw_to_index, key, -1); // if key is an explicit key, set as local variable if(index >= 0) { buffer[index] = p1[2 * j + 1]; @@ -653,20 +653,6 @@ void ManagedHeap__mark(ManagedHeap* self) { py_TypeInfo* ti = TypeList__get(&vm->types, i); // mark type object pk__mark_value(&ti->self); - // mark common magic slots - for(int j = 0; j < PK_MAGIC_SLOTS_COMMON_LENGTH; j++) { - py_TValue* slot = ti->magic_0 + j; - if(py_isnil(slot)) continue; - pk__mark_value(slot); - } - // mark uncommon magic slots - if(ti->magic_1) { - for(int j = 0; j < PK_MAGIC_SLOTS_UNCOMMON_LENGTH; j++) { - py_TValue* slot = ti->magic_1 + j; - if(py_isnil(slot)) continue; - pk__mark_value(slot); - } - } // mark type annotations pk__mark_value(&ti->annotations); } @@ -680,12 +666,6 @@ void ManagedHeap__mark(ManagedHeap* self) { for(int i = 0; i < c11__count_array(vm->reg); i++) { pk__mark_value(&vm->reg[i]); } - // mark interned names - for(int i = 0; i < vm->names.r_interned.length; i++) { - RInternedEntry* entry = c11__at(RInternedEntry, &vm->names.r_interned, i); - pk__mark_value(&entry->obj); - } - /*****************************/ while(p_stack->length > 0) { PyObject* obj = c11_vector__back(PyObject*, p_stack); @@ -698,9 +678,10 @@ void ManagedHeap__mark(ManagedHeap* self) { for(int i = 0; i < obj->slots; i++) pk__mark_value(p + i); } else if(obj->slots == -1) { - NameDict* namedict = PyObject__dict(obj); - for(int i = 0; i < namedict->length; i++) { - NameDict_KV* kv = c11__at(NameDict_KV, namedict, i); + NameDict* dict = PyObject__dict(obj); + for(int i = 0; i < dict->capacity; i++) { + NameDict_KV* kv = &dict->items[i]; + if(kv->key == NULL) continue; pk__mark_value(&kv->value); } } @@ -854,4 +835,16 @@ int py_replinput(char* buf, int max_size) { buf[size] = '\0'; return size; +} + +py_Ref py_name2ref(py_Name name) { + assert(name != NULL); + NameDict* d = &pk_current_vm->cached_names; + py_Ref res = NameDict__try_get(d, name); + if(res != NULL) return res; + // not found, create a new one + py_TValue tmp; + py_newstrv(&tmp, py_name2sv(name)); + NameDict__set(d, name, &tmp); + return NameDict__try_get(d, name); } \ No newline at end of file diff --git a/src/modules/dis.c b/src/modules/dis.c index 85c7fc68..fbcffc92 100644 --- a/src/modules/dis.c +++ b/src/modules/dis.c @@ -84,7 +84,8 @@ static bool disassemble(CodeObject* co) { case OP_BEGIN_CLASS: case OP_DELETE_GLOBAL: case OP_STORE_CLASS_ATTR: { - pk_sprintf(&ss, " (%n)", byte.arg); + py_Name name = c11__getitem(py_Name, &co->names, byte.arg); + pk_sprintf(&ss, " (%n)", name); break; } case OP_LOAD_FAST: @@ -99,11 +100,31 @@ static bool disassemble(CodeObject* co) { pk_sprintf(&ss, " (%s)", decl->code.name->data); break; } - case OP_BINARY_OP: { - py_Name name = byte.arg & 0xFF; - pk_sprintf(&ss, " (%s)", pk_op2str(name)); - break; - } +#define CASE_BINARY_OP(label, op, rop) \ + case label: { \ + pk_sprintf(&ss, " (%s)", pk_op2str(op)); \ + break; \ + } + CASE_BINARY_OP(OP_BINARY_ADD, __add__, __radd__) + CASE_BINARY_OP(OP_BINARY_SUB, __sub__, __rsub__) + CASE_BINARY_OP(OP_BINARY_MUL, __mul__, __rmul__) + CASE_BINARY_OP(OP_BINARY_TRUEDIV, __truediv__, __rtruediv__) + CASE_BINARY_OP(OP_BINARY_FLOORDIV, __floordiv__, __rfloordiv__) + CASE_BINARY_OP(OP_BINARY_MOD, __mod__, __rmod__) + CASE_BINARY_OP(OP_BINARY_POW, __pow__, __rpow__) + CASE_BINARY_OP(OP_BINARY_LSHIFT, __lshift__, 0) + CASE_BINARY_OP(OP_BINARY_RSHIFT, __rshift__, 0) + CASE_BINARY_OP(OP_BINARY_AND, __and__, 0) + CASE_BINARY_OP(OP_BINARY_OR, __or__, 0) + CASE_BINARY_OP(OP_BINARY_XOR, __xor__, 0) + CASE_BINARY_OP(OP_BINARY_MATMUL, __matmul__, 0) + CASE_BINARY_OP(OP_COMPARE_LT, __lt__, __gt__) + CASE_BINARY_OP(OP_COMPARE_LE, __le__, __ge__) + CASE_BINARY_OP(OP_COMPARE_EQ, __eq__, __eq__) + CASE_BINARY_OP(OP_COMPARE_NE, __ne__, __ne__) + CASE_BINARY_OP(OP_COMPARE_GT, __gt__, __lt__) + CASE_BINARY_OP(OP_COMPARE_GE, __ge__, __le__) +#undef CASE_BINARY_OP } } while(0); diff --git a/src/modules/pickle.c b/src/modules/pickle.c index cc0112c9..b9e2e6e3 100644 --- a/src/modules/pickle.c +++ b/src/modules/pickle.c @@ -374,16 +374,18 @@ static bool pkl__write_object(PickleObject* buf, py_TValue* obj) { } if(ti->is_python) { NameDict* dict = PyObject__dict(obj->_obj); - for(int i = dict->length - 1; i >= 0; i--) { - NameDict_KV* kv = c11__at(NameDict_KV, dict, i); + for(int i = dict->capacity - 1; i >= 0; i--) { + NameDict_KV* kv = &dict->items[i]; + if(kv->key == NULL) continue; if(!pkl__write_object(buf, &kv->value)) return false; } pkl__emit_op(buf, PKL_OBJECT); pkl__emit_int(buf, obj->type); buf->used_types[obj->type] = true; pkl__emit_int(buf, dict->length); - for(int i = 0; i < dict->length; i++) { - NameDict_KV* kv = c11__at(NameDict_KV, dict, i); + for(int i = 0; i < dict->capacity; i++) { + NameDict_KV* kv = &dict->items[i]; + if(kv->key == NULL) continue; c11_sv field = py_name2sv(kv->key); // include '\0' PickleObject__write_bytes(buf, field.data, field.size + 1); @@ -440,7 +442,7 @@ static py_i64 pkl__header_read_int(const unsigned char** p, char sep) { return out; } -bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_n2i* type_mapping); +bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_d2d* type_mapping); bool py_pickle_loads(const unsigned char* data, int size) { const unsigned char* p = data; @@ -450,8 +452,8 @@ bool py_pickle_loads(const unsigned char* data, int size) { return ValueError("invalid pickle data"); p += 4; - c11_smallmap_n2i type_mapping; - c11_smallmap_n2i__ctor(&type_mapping); + c11_smallmap_d2d type_mapping; + c11_smallmap_d2d__ctor(&type_mapping); while(true) { if(*p == '\n') { @@ -462,25 +464,25 @@ bool py_pickle_loads(const unsigned char* data, int size) { c11_sv path = pkl__header_read_sv(&p, ')'); py_Type new_type = pkl__header_find_type(path); if(new_type == 0) { - c11_smallmap_n2i__dtor(&type_mapping); + c11_smallmap_d2d__dtor(&type_mapping); return ImportError("cannot find type '%v'", path); } - if(type != new_type) c11_smallmap_n2i__set(&type_mapping, type, new_type); + if(type != new_type) c11_smallmap_d2d__set(&type_mapping, type, new_type); } int memo_length = pkl__header_read_int(&p, '\n'); bool ok = py_pickle_loads_body(p, memo_length, &type_mapping); - c11_smallmap_n2i__dtor(&type_mapping); + c11_smallmap_d2d__dtor(&type_mapping); return ok; } -static py_Type pkl__fix_type(py_Type type, c11_smallmap_n2i* type_mapping) { - int new_type = c11_smallmap_n2i__get(type_mapping, type, -1); +static py_Type pkl__fix_type(py_Type type, c11_smallmap_d2d* type_mapping) { + int new_type = c11_smallmap_d2d__get(type_mapping, type, -1); if(new_type != -1) return (py_Type)new_type; return type; } -bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_n2i* type_mapping) { +bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_d2d* type_mapping) { py_StackRef p0 = py_peek(0); py_Ref p_memo = py_newtuple(py_pushtmp(), memo_length); while(true) { @@ -685,7 +687,7 @@ bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_ for(int i = 0; i < dict_length; i++) { py_StackRef value = py_peek(-1); c11_sv field = {(const char*)p, strlen((const char*)p)}; - NameDict__set(dict, py_namev(field), *value); + NameDict__set(dict, py_namev(field), value); py_pop(); p += field.size + 1; } diff --git a/src/modules/pkpy.c b/src/modules/pkpy.c index 45d75ff1..397f71d9 100644 --- a/src/modules/pkpy.c +++ b/src/modules/pkpy.c @@ -160,7 +160,7 @@ static void static bool _pk_compute_thread_flags[16]; static void c11_ComputeThread__dtor(c11_ComputeThread* self) { - if(!self->is_done) { + if(!atomic_load(&self->is_done)) { c11__abort("ComputeThread(%d) is not done yet!! But the object was deleted.", self->vm_index); } @@ -187,7 +187,7 @@ static bool ComputeThread__new__(int argc, py_Ref argv) { c11_ComputeThread* self = py_newobject(py_retval(), py_totype(argv), 0, sizeof(c11_ComputeThread)); self->vm_index = 0; - self->is_done = true; + atomic_store(&self->is_done, true); self->last_retval_data = NULL; self->last_retval_size = 0; self->last_error = NULL; @@ -217,15 +217,17 @@ static bool ComputeThread__init__(int argc, py_Ref argv) { static bool ComputeThread_is_done(int argc, py_Ref argv) { PY_CHECK_ARGC(1); c11_ComputeThread* self = py_touserdata(argv); - py_newbool(py_retval(), self->is_done); + bool value = atomic_load(&self->is_done); + py_newbool(py_retval(), value); return true; } static bool ComputeThread_wait_for_done(int argc, py_Ref argv) { PY_CHECK_ARGC(1); c11_ComputeThread* self = py_touserdata(argv); - while(!self->is_done) + while(!atomic_load(&self->is_done)) { c11_thrd_yield(); + } py_newnone(py_retval()); return true; } @@ -233,7 +235,7 @@ static bool ComputeThread_wait_for_done(int argc, py_Ref argv) { static bool ComputeThread_last_error(int argc, py_Ref argv) { PY_CHECK_ARGC(1); c11_ComputeThread* self = py_touserdata(argv); - if(!self->is_done) return OSError("thread is not done yet"); + if(!atomic_load(&self->is_done)) return OSError("thread is not done yet"); if(self->last_error) { py_newstr(py_retval(), self->last_error); } else { @@ -245,7 +247,7 @@ static bool ComputeThread_last_error(int argc, py_Ref argv) { static bool ComputeThread_last_retval(int argc, py_Ref argv) { PY_CHECK_ARGC(1); c11_ComputeThread* self = py_touserdata(argv); - if(!self->is_done) return OSError("thread is not done yet"); + if(!atomic_load(&self->is_done)) return OSError("thread is not done yet"); if(self->last_retval_data == NULL) return ValueError("no retval available"); return py_pickle_loads(self->last_retval_data, self->last_retval_size); } @@ -273,12 +275,12 @@ static c11_thrd_retval_t ComputeThreadJob_call(void* arg) { unsigned char* retval_data = py_tobytes(py_retval(), &retval_size); self->last_retval_data = c11_memdup(retval_data, retval_size); self->last_retval_size = retval_size; - self->is_done = true; + atomic_store(&self->is_done, true); return (c11_thrd_retval_t)0; __ERROR: self->last_error = py_formatexc(); - self->is_done = true; + atomic_store(&self->is_done, true); py_clearexc(p0); py_newnone(py_retval()); return (c11_thrd_retval_t)0; @@ -296,12 +298,12 @@ static c11_thrd_retval_t ComputeThreadJob_exec(void* arg) { unsigned char* retval_data = py_tobytes(py_retval(), &retval_size); self->last_retval_data = c11_memdup(retval_data, retval_size); self->last_retval_size = retval_size; - self->is_done = true; + atomic_store(&self->is_done, true); return (c11_thrd_retval_t)0; __ERROR: self->last_error = py_formatexc(); - self->is_done = true; + atomic_store(&self->is_done, true); py_clearexc(p0); return (c11_thrd_retval_t)0; } @@ -309,7 +311,7 @@ __ERROR: static bool ComputeThread_submit_exec(int argc, py_Ref argv) { PY_CHECK_ARGC(2); c11_ComputeThread* self = py_touserdata(py_arg(0)); - if(!self->is_done) return OSError("thread is not done yet"); + if(!atomic_load(&self->is_done)) return OSError("thread is not done yet"); PY_CHECK_ARG_TYPE(1, tp_str); const char* source = py_tostr(py_arg(1)); /**************************/ @@ -319,10 +321,10 @@ static bool ComputeThread_submit_exec(int argc, py_Ref argv) { job->mode = EXEC_MODE; c11_ComputeThread__reset_job(self, job, ComputeThreadJobExec__dtor); /**************************/ - self->is_done = false; + atomic_store(&self->is_done, false); bool ok = c11_thrd_create(&self->thread, ComputeThreadJob_exec, job); if(!ok) { - self->is_done = true; + atomic_store(&self->is_done, true); return OSError("thrd_create() failed"); } py_newnone(py_retval()); @@ -332,7 +334,7 @@ static bool ComputeThread_submit_exec(int argc, py_Ref argv) { static bool ComputeThread_submit_eval(int argc, py_Ref argv) { PY_CHECK_ARGC(2); c11_ComputeThread* self = py_touserdata(py_arg(0)); - if(!self->is_done) return OSError("thread is not done yet"); + if(!atomic_load(&self->is_done)) return OSError("thread is not done yet"); PY_CHECK_ARG_TYPE(1, tp_str); const char* source = py_tostr(py_arg(1)); /**************************/ @@ -342,10 +344,10 @@ static bool ComputeThread_submit_eval(int argc, py_Ref argv) { job->mode = EVAL_MODE; c11_ComputeThread__reset_job(self, job, ComputeThreadJobExec__dtor); /**************************/ - self->is_done = false; + atomic_store(&self->is_done, false); bool ok = c11_thrd_create(&self->thread, ComputeThreadJob_exec, job); if(!ok) { - self->is_done = true; + atomic_store(&self->is_done, true); return OSError("thrd_create() failed"); } py_newnone(py_retval()); @@ -355,7 +357,7 @@ static bool ComputeThread_submit_eval(int argc, py_Ref argv) { static bool ComputeThread_submit_call(int argc, py_Ref argv) { PY_CHECK_ARGC(4); c11_ComputeThread* self = py_touserdata(py_arg(0)); - if(!self->is_done) return OSError("thread is not done yet"); + if(!atomic_load(&self->is_done)) return OSError("thread is not done yet"); PY_CHECK_ARG_TYPE(1, tp_str); PY_CHECK_ARG_TYPE(2, tp_tuple); PY_CHECK_ARG_TYPE(3, tp_dict); @@ -379,10 +381,10 @@ static bool ComputeThread_submit_call(int argc, py_Ref argv) { job->kwargs_size = kwargs_size; c11_ComputeThread__reset_job(self, job, ComputeThreadJobCall__dtor); /**************************/ - self->is_done = false; + atomic_store(&self->is_done, false); bool ok = c11_thrd_create(&self->thread, ComputeThreadJob_call, job); if(!ok) { - self->is_done = true; + atomic_store(&self->is_done, true); return OSError("thrd_create() failed"); } py_newnone(py_retval()); @@ -392,8 +394,8 @@ static bool ComputeThread_submit_call(int argc, py_Ref argv) { static bool c11_ComputeThread__exec_blocked(c11_ComputeThread* self, const char* source, enum py_CompileMode mode) { - if(!self->is_done) return OSError("thread is not done yet"); - self->is_done = false; + if(!atomic_load(&self->is_done)) return OSError("thread is not done yet"); + atomic_store(&self->is_done, false); char* err = NULL; int old_vm_index = py_currentvm(); py_switchvm(self->vm_index); @@ -405,14 +407,14 @@ static bool c11_ComputeThread__exec_blocked(c11_ComputeThread* self, unsigned char* retval_data = py_tobytes(py_retval(), &retval_size); py_switchvm(old_vm_index); bool ok = py_pickle_loads(retval_data, retval_size); - self->is_done = true; + atomic_store(&self->is_done, true); return ok; __ERROR: err = py_formatexc(); py_clearexc(p0); py_switchvm(old_vm_index); - self->is_done = true; + atomic_store(&self->is_done, true); RuntimeError("c11_ComputeThread__exec_blocked() failed:\n%s", err); PK_FREE(err); return false; @@ -454,7 +456,7 @@ static void pk_ComputeThread__register(py_Ref mod) { py_bindmethod(type, "eval", ComputeThread_eval); } -static void pkpy_configmacros_add(py_Ref dict, const char* key, int val){ +static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) { assert(dict->type == tp_dict); py_TValue tmp; py_newint(&tmp, val); diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index 6483bfe5..857d0a69 100644 --- a/src/objects/codeobject.c +++ b/src/objects/codeobject.c @@ -20,7 +20,7 @@ static void FuncDecl__dtor(FuncDecl* self) { CodeObject__dtor(&self->code); c11_vector__dtor(&self->args); c11_vector__dtor(&self->kwargs); - c11_smallmap_n2i__dtor(&self->kw_to_index); + c11_smallmap_n2d__dtor(&self->kw_to_index); } FuncDecl_ FuncDecl__rcnew(SourceData_ src, c11_sv name) { @@ -39,14 +39,14 @@ FuncDecl_ FuncDecl__rcnew(SourceData_ src, c11_sv name) { self->docstring = NULL; self->type = FuncType_UNSET; - c11_smallmap_n2i__ctor(&self->kw_to_index); + c11_smallmap_n2d__ctor(&self->kw_to_index); return self; } bool FuncDecl__is_duplicated_arg(const FuncDecl* decl, py_Name name) { py_Name tmp_name; c11__foreach(int, &decl->args, j) { - tmp_name = c11__getitem(py_Name, &decl->args, *j); + tmp_name = c11__getitem(py_Name, &decl->code.varnames, *j); if(tmp_name == name) return true; } c11__foreach(FuncDeclKwArg, &decl->kwargs, kv) { @@ -71,7 +71,7 @@ void FuncDecl__add_arg(FuncDecl* self, py_Name name) { void FuncDecl__add_kwarg(FuncDecl* self, py_Name name, const py_TValue* value) { int index = CodeObject__add_varname(&self->code, name); - c11_smallmap_n2i__set(&self->kw_to_index, name, index); + c11_smallmap_n2d__set(&self->kw_to_index, name, index); FuncDeclKwArg* item = c11_vector__emplace(&self->kwargs); item->index = index; item->key = name; @@ -123,10 +123,12 @@ void CodeObject__ctor(CodeObject* self, SourceData_ src, c11_sv name) { c11_vector__ctor(&self->codes_ex, sizeof(BytecodeEx)); c11_vector__ctor(&self->consts, sizeof(py_TValue)); - c11_vector__ctor(&self->varnames, sizeof(uint16_t)); + c11_vector__ctor(&self->varnames, sizeof(py_Name)); + c11_vector__ctor(&self->names, sizeof(py_Name)); self->nlocals = 0; - c11_smallmap_n2i__ctor(&self->varnames_inv); + c11_smallmap_n2d__ctor(&self->varnames_inv); + c11_smallmap_n2d__ctor(&self->names_inv); c11_vector__ctor(&self->blocks, sizeof(CodeBlock)); c11_vector__ctor(&self->func_decls, sizeof(FuncDecl_)); @@ -147,8 +149,10 @@ void CodeObject__dtor(CodeObject* self) { c11_vector__dtor(&self->consts); c11_vector__dtor(&self->varnames); + c11_vector__dtor(&self->names); - c11_smallmap_n2i__dtor(&self->varnames_inv); + c11_smallmap_n2d__dtor(&self->varnames_inv); + c11_smallmap_n2d__dtor(&self->names_inv); c11_vector__dtor(&self->blocks); @@ -170,12 +174,21 @@ void Function__ctor(Function* self, FuncDecl_ decl, py_GlobalRef module, py_Ref } int CodeObject__add_varname(CodeObject* self, py_Name name) { - int index = c11_smallmap_n2i__get(&self->varnames_inv, name, -1); + int index = c11_smallmap_n2d__get(&self->varnames_inv, name, -1); if(index >= 0) return index; - c11_vector__push(uint16_t, &self->varnames, name); + c11_vector__push(py_Name, &self->varnames, name); self->nlocals++; index = self->varnames.length - 1; - c11_smallmap_n2i__set(&self->varnames_inv, name, index); + c11_smallmap_n2d__set(&self->varnames_inv, name, index); + return index; +} + +int CodeObject__add_name(CodeObject* self, py_Name name) { + int index = c11_smallmap_n2d__get(&self->names_inv, name, -1); + if(index >= 0) return index; + c11_vector__push(py_Name, &self->names, name); + index = self->names.length - 1; + c11_smallmap_n2d__set(&self->names_inv, name, index); return index; } diff --git a/src/objects/namedict.c b/src/objects/namedict.c index a7e6a4ae..d5f2cdbb 100644 --- a/src/objects/namedict.c +++ b/src/objects/namedict.c @@ -1,12 +1,8 @@ #include "pocketpy/objects/namedict.h" +#include "pocketpy/common/utils.h" #include "pocketpy/objects/object.h" - -#define SMALLMAP_T__SOURCE -#define K uint16_t -#define V py_TValue -#define NAME NameDict -#include "pocketpy/xmacros/smallmap.h" -#undef SMALLMAP_T__SOURCE +#include "pocketpy/pocketpy.h" +#include void ModuleDict__ctor(ModuleDict* self, const char* path, py_TValue module) { assert(path != NULL); @@ -90,3 +86,127 @@ void ModuleDict__apply_mark(ModuleDict* self, c11_vector* p_stack) { if(self->left) ModuleDict__apply_mark(self->left, p_stack); if(self->right) ModuleDict__apply_mark(self->right, p_stack); } + +/////////////////// NameDict /////////////////// + +#define HASH_PROBE_1(__k, ok, i) \ + ok = false; \ + i = (uintptr_t)(__k) & self->mask; \ + while(self->items[i].key != NULL) { \ + if(self->items[i].key == (__k)) { \ + ok = true; \ + break; \ + } \ + i = (i + 1) & self->mask; \ + } + +#define HASH_PROBE_0 HASH_PROBE_1 + +static void NameDict__set_capacity_and_alloc_items(NameDict* self, int val) { + self->capacity = val; + self->critical_size = val * self->load_factor; + self->mask = (uintptr_t)val - 1; + + self->items = PK_MALLOC(self->capacity * sizeof(NameDict_KV)); + memset(self->items, 0, self->capacity * sizeof(NameDict_KV)); +} + +static void NameDict__rehash_2x(NameDict* self) { + NameDict_KV* old_items = self->items; + int old_capacity = self->capacity; + NameDict__set_capacity_and_alloc_items(self, self->capacity * 2); + for(int i = 0; i < old_capacity; i++) { + if(old_items[i].key == NULL) continue; + bool ok; + uintptr_t j; + HASH_PROBE_1(old_items[i].key, ok, j); + c11__rtassert(!ok); + self->items[j] = old_items[i]; + } + PK_FREE(old_items); +} + +NameDict* NameDict__new(float load_factor) { + NameDict* p = PK_MALLOC(sizeof(NameDict)); + NameDict__ctor(p, load_factor); + return p; +} + +void NameDict__delete(NameDict* self) { + NameDict__dtor(self); + PK_FREE(self); +} + +void NameDict__ctor(NameDict* self, float load_factor) { + assert(load_factor > 0.0f && load_factor < 1.0f); + self->length = 0; + self->load_factor = load_factor; + NameDict__set_capacity_and_alloc_items(self, 4); +} + +void NameDict__dtor(NameDict* self) { PK_FREE(self->items); } + +py_TValue* NameDict__try_get(NameDict* self, py_Name key) { + bool ok; + uintptr_t i; + HASH_PROBE_0(key, ok, i); + if(!ok) return NULL; + return &self->items[i].value; +} + +bool NameDict__contains(NameDict* self, py_Name key) { + bool ok; + uintptr_t i; + HASH_PROBE_0(key, ok, i); + return ok; +} + +void NameDict__set(NameDict* self, py_Name key, py_TValue* val) { + bool ok; + uintptr_t i; + HASH_PROBE_1(key, ok, i); + if(!ok) { + self->length++; + if(self->length > self->critical_size) { + NameDict__rehash_2x(self); + HASH_PROBE_1(key, ok, i); + } + self->items[i].key = key; + } + self->items[i].value = *val; +} + +bool NameDict__del(NameDict* self, py_Name key) { + bool ok; + uintptr_t i; + HASH_PROBE_0(key, ok, i); + if(!ok) return false; + self->items[i].key = NULL; + self->items[i].value = *py_NIL(); + self->length--; + // tidy + uintptr_t pre_z = i; + uintptr_t z = (i + 1) & self->mask; + while(self->items[z].key != NULL) { + uintptr_t h = (uintptr_t)self->items[z].key & self->mask; + if(h != i) break; + // std::swap(_items[pre_z], _items[z]); + NameDict_KV tmp = self->items[pre_z]; + self->items[pre_z] = self->items[z]; + self->items[z] = tmp; + pre_z = z; + z = (z + 1) & self->mask; + } + return true; +} + +void NameDict__clear(NameDict* self) { + for(int i = 0; i < self->capacity; i++) { + self->items[i].key = NULL; + self->items[i].value = *py_NIL(); + } + self->length = 0; +} + +#undef HASH_PROBE_0 +#undef HASH_PROBE_1 \ No newline at end of file diff --git a/src/public/internal.c b/src/public/internal.c index 7bbe124b..5a2912fc 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -3,6 +3,7 @@ #include "pocketpy/pocketpy.h" #include "pocketpy/common/utils.h" +#include "pocketpy/common/name.h" #include "pocketpy/interpreter/vm.h" _Thread_local VM* pk_current_vm; @@ -18,6 +19,8 @@ void py_initialize() { return; } + pk_names_initialize(); + // check endianness int x = 1; bool is_little_endian = *(char*)&x == 1; @@ -62,6 +65,8 @@ void py_finalize() { pk_current_vm = &pk_default_vm; VM__dtor(&pk_default_vm); pk_current_vm = NULL; + + pk_names_finalize(); } void py_switchvm(int index) { @@ -229,14 +234,7 @@ bool pk_loadmethod(py_StackRef self, py_Name name) { py_Ref py_tpfindmagic(py_Type t, py_Name name) { assert(py_ismagicname(name)); - py_TypeInfo* ti = pk__type_info(t); - do { - py_Ref f = TypeList__magic_readonly(ti, name); - assert(f != NULL); - if(!py_isnil(f)) return f; - ti = ti->base_ti; - } while(ti); - return NULL; + return py_tpfindname(t, name); } py_Ref py_tpfindname(py_Type t, py_Name name) { @@ -249,10 +247,11 @@ py_Ref py_tpfindname(py_Type t, py_Name name) { return NULL; } -py_Ref py_tpgetmagic(py_Type type, py_Name name) { +PK_DEPRECATED py_Ref py_tpgetmagic(py_Type type, py_Name name) { assert(py_ismagicname(name)); py_TypeInfo* ti = pk__type_info(type); - return TypeList__magic(ti, name); + py_Ref retval = py_getdict(&ti->self, name); + return retval != NULL ? retval : py_NIL(); } py_Ref py_tpobject(py_Type type) { diff --git a/src/public/modules.c b/src/public/modules.c index bd47e3b3..14c1431d 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -617,7 +617,7 @@ static bool int max_index = -1; c11__foreach(Bytecode, &co->codes, bc) { if(bc->op == OP_LOAD_NAME) { - c11_sv name = py_name2sv(bc->arg); + c11_sv name = py_name2sv(c11__getitem(py_Name, &co->names, bc->arg)); if(name.data[0] != '_') continue; int index; if(name.size == 1) { @@ -752,11 +752,11 @@ py_TValue pk_builtins__register() { // some patches py_bindmagic(tp_NoneType, __repr__, NoneType__repr__); - *py_tpgetmagic(tp_NoneType, __hash__) = *py_None(); + py_setdict(py_tpobject(tp_NoneType), __hash__, py_None()); py_bindmagic(tp_ellipsis, __repr__, ellipsis__repr__); - *py_tpgetmagic(tp_ellipsis, __hash__) = *py_None(); + py_setdict(py_tpobject(tp_ellipsis), __hash__, py_None()); py_bindmagic(tp_NotImplementedType, __repr__, NotImplementedType__repr__); - *py_tpgetmagic(tp_NotImplementedType, __hash__) = *py_None(); + py_setdict(py_tpobject(tp_NotImplementedType), __hash__, py_None()); return *builtins; } @@ -764,9 +764,10 @@ void function__gc_mark(void* ud, c11_vector* p_stack) { Function* func = ud; if(func->globals) pk__mark_value(func->globals); if(func->closure) { - NameDict* namedict = func->closure; - for(int i = 0; i < namedict->length; i++) { - NameDict_KV* kv = c11__at(NameDict_KV, namedict, i); + NameDict* dict = func->closure; + for(int i = 0; i < dict->capacity; i++) { + NameDict_KV* kv = &dict->items[i]; + if(kv->key == NULL) continue; pk__mark_value(&kv->value); } } diff --git a/src/public/py_exception.c b/src/public/py_exception.c index 1b2e3aeb..4110818b 100644 --- a/src/public/py_exception.c +++ b/src/public/py_exception.c @@ -225,6 +225,13 @@ char* py_formatexc() { } bool py_exception(py_Type type, const char* fmt, ...) { +#ifndef NDEBUG + if(py_checkexc(true)) { + const char* name = py_tpname(pk_current_vm->curr_exception.type); + c11__abort("py_exception(): `%s` was already set!", name); + } +#endif + c11_sbuf buf; c11_sbuf__ctor(&buf); va_list args; diff --git a/src/public/py_mappingproxy.c b/src/public/py_mappingproxy.c index 805958d7..fd492469 100644 --- a/src/public/py_mappingproxy.c +++ b/src/public/py_mappingproxy.c @@ -52,29 +52,11 @@ static bool namedict_items(int argc, py_Ref argv) { py_Ref object = py_getslot(argv, 0); NameDict* dict = PyObject__dict(object->_obj); py_newlist(py_retval()); - if(object->type == tp_type) { - py_TypeInfo* ti = pk__type_info(py_totype(object)); - for(int j = 0; j < PK_MAGIC_SLOTS_COMMON_LENGTH; j++) { - if(py_isnil(ti->magic_0 + j)) continue; - py_Ref slot = py_list_emplace(py_retval()); - py_Ref p = py_newtuple(slot, 2); - p[0] = *py_name2ref(j + PK_MAGIC_SLOTS_UNCOMMON_LENGTH); - p[1] = ti->magic_0[j]; - } - if(ti->magic_1) { - for(int j = 0; j < PK_MAGIC_SLOTS_UNCOMMON_LENGTH; j++) { - if(py_isnil(ti->magic_1 + j)) continue; - py_Ref slot = py_list_emplace(py_retval()); - py_Ref p = py_newtuple(slot, 2); - p[0] = *py_name2ref(j); - p[1] = ti->magic_1[j]; - } - } - } - for(int i = 0; i < dict->length; i++) { + for(int i = 0; i < dict->capacity; i++) { + NameDict_KV* kv = &dict->items[i]; + if(kv->key == NULL) continue; py_Ref slot = py_list_emplace(py_retval()); py_Ref p = py_newtuple(slot, 2); - NameDict_KV* kv = c11__at(NameDict_KV, dict, i); p[0] = *py_name2ref(kv->key); p[1] = kv->value; } @@ -97,7 +79,7 @@ py_Type pk_namedict__register() { py_bindmagic(type, __setitem__, namedict__setitem__); py_bindmagic(type, __delitem__, namedict__delitem__); py_bindmagic(type, __contains__, namedict__contains__); - py_newnone(py_tpgetmagic(type, __hash__)); + py_setdict(py_tpobject(type), __hash__, py_None()); py_bindmethod(type, "items", namedict_items); py_bindmethod(type, "clear", namedict_clear); return type; diff --git a/src/public/py_ops.c b/src/public/py_ops.c index 71d13bb7..ed643c86 100644 --- a/src/public/py_ops.c +++ b/src/public/py_ops.c @@ -54,11 +54,11 @@ int py_bool(py_Ref val) { bool py_hash(py_Ref val, int64_t* out) { py_TypeInfo* ti = pk__type_info(val->type); do { - py_Ref slot_hash = TypeList__magic_common(ti, __hash__); - if(py_isnone(slot_hash)) break; - py_Ref slot_eq = TypeList__magic_common(ti, __eq__); - if(!py_isnil(slot_eq)) { - if(py_isnil(slot_hash)) break; + py_Ref slot_hash = py_getdict(&ti->self, __hash__); + if(slot_hash && py_isnone(slot_hash)) break; + py_Ref slot_eq = py_getdict(&ti->self, __eq__); + if(slot_eq) { + if(!slot_hash) break; if(!py_call(slot_hash, 1, val)) return false; if(!py_checkint(py_retval())) return false; *out = py_toint(py_retval()); diff --git a/src/public/py_str.c b/src/public/py_str.c index 06175ec2..e7145ca1 100644 --- a/src/public/py_str.c +++ b/src/public/py_str.c @@ -103,13 +103,8 @@ static bool str__new__(int argc, py_Ref argv) { static bool str__hash__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); - int size; - const char* data = py_tostrn(&argv[0], &size); - uint64_t res = 0; - for(int i = 0; i < size; i++) { - res = res * 31 + data[i]; - } - py_newint(py_retval(), res); + uint64_t res = c11_sv__hash(py_tosv(argv)); + py_newint(py_retval(), (py_i64)res); return true; } diff --git a/src/public/stack_ops.c b/src/public/stack_ops.c index 055cf46d..db34c0a6 100644 --- a/src/public/stack_ops.c +++ b/src/public/stack_ops.c @@ -10,23 +10,12 @@ void py_setreg(int i, py_Ref val) { pk_current_vm->reg[i] = *val; } py_Ref py_getdict(py_Ref self, py_Name name) { assert(self && self->is_ptr); - if(!py_ismagicname(name) || self->type != tp_type) { - return NameDict__try_get(PyObject__dict(self->_obj), name); - } else { - py_Type* ud = py_touserdata(self); - py_Ref slot = py_tpgetmagic(*ud, name); - return py_isnil(slot) ? NULL : slot; - } + return NameDict__try_get(PyObject__dict(self->_obj), name); } void py_setdict(py_Ref self, py_Name name, py_Ref val) { assert(self && self->is_ptr); - if(!py_ismagicname(name) || self->type != tp_type) { - NameDict__set(PyObject__dict(self->_obj), name, *val); - } else { - py_Type* ud = py_touserdata(self); - *py_tpgetmagic(*ud, name) = *val; - } + NameDict__set(PyObject__dict(self->_obj), name, val); } py_ItemRef py_emplacedict(py_Ref self, py_Name name) { @@ -37,8 +26,9 @@ py_ItemRef py_emplacedict(py_Ref self, py_Name name) { bool py_applydict(py_Ref self, bool (*f)(py_Name, py_Ref, void*), void* ctx) { assert(self && self->is_ptr); NameDict* dict = PyObject__dict(self->_obj); - for(int i = 0; i < dict->length; i++) { - NameDict_KV* kv = c11__at(NameDict_KV, dict, i); + for(int i = 0; i < dict->capacity; i++) { + NameDict_KV* kv = &dict->items[i]; + if(kv->key == NULL) continue; bool ok = f(kv->key, &kv->value, ctx); if(!ok) return false; } @@ -47,13 +37,7 @@ bool py_applydict(py_Ref self, bool (*f)(py_Name, py_Ref, void*), void* ctx) { bool py_deldict(py_Ref self, py_Name name) { assert(self && self->is_ptr); - if(!py_ismagicname(name) || self->type != tp_type) { - return NameDict__del(PyObject__dict(self->_obj), name); - } else { - py_Type* ud = py_touserdata(self); - py_newnil(py_tpgetmagic(*ud, name)); - return true; - } + return NameDict__del(PyObject__dict(self->_obj), name); } py_Ref py_getslot(py_Ref self, int i) { @@ -119,7 +103,7 @@ void py_pushnone() { void py_pushname(py_Name name) { VM* vm = pk_current_vm; - py_newint(vm->stack.sp++, name); + py_newint(vm->stack.sp++, (uintptr_t)name); } py_Ref py_pushtmp() { diff --git a/src/public/values.c b/src/public/values.c index edb86f4e..a05abde6 100644 --- a/src/public/values.c +++ b/src/public/values.c @@ -83,14 +83,22 @@ void py_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFu py_setdict(py_tpobject(type), py_name(name), &tmp); } +void py_bindmagic(py_Type type, py_Name name, py_CFunction f) { + py_Ref tmp = py_emplacedict(py_tpobject(type), name); + py_newnativefunc(tmp, f); +} + void py_bind(py_Ref obj, const char* sig, py_CFunction f) { py_TValue tmp; py_Name name = py_newfunction(&tmp, sig, f, NULL, 0); py_setdict(obj, name, &tmp); } -py_Name - py_newfunction(py_OutRef out, const char* sig, py_CFunction f, const char* docstring, int slots) { +py_Name py_newfunction(py_OutRef out, + const char* sig, + py_CFunction f, + const char* docstring, + int slots) { char buffer[256]; snprintf(buffer, sizeof(buffer), "def %s: pass", sig); // fn(a, b, *c, d=1) -> None