From 93ca8d88f312f0aebece4bd0126e1741db84c7f9 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Tue, 31 Dec 2024 13:34:37 +0800 Subject: [PATCH] add `PK_LOW_MEMORY_MODE` --- include/pocketpy/config.h | 19 ++++++++-- include/pocketpy/interpreter/frame.h | 5 +-- src/common/sstream.c | 1 + src/interpreter/ceval.c | 55 +++++++++++++++++++++++----- src/interpreter/heap.c | 3 +- 5 files changed, 67 insertions(+), 16 deletions(-) diff --git a/include/pocketpy/config.h b/include/pocketpy/config.h index 36607502..1c9f5151 100644 --- a/include/pocketpy/config.h +++ b/include/pocketpy/config.h @@ -8,6 +8,11 @@ /*************** feature settings ***************/ +// Reduce the startup memory usage for embedded systems +#ifndef PK_LOW_MEMORY_MODE // can be overridden by cmake +#define PK_LOW_MEMORY_MODE 0 +#endif + // Whether to compile os-related modules or not #ifndef PK_ENABLE_OS // can be overridden by cmake #define PK_ENABLE_OS 1 @@ -15,7 +20,11 @@ // GC min threshold #ifndef PK_GC_MIN_THRESHOLD // can be overridden by cmake -#define PK_GC_MIN_THRESHOLD 16384 + #if PK_LOW_MEMORY_MODE + #define PK_GC_MIN_THRESHOLD 2048 + #else + #define PK_GC_MIN_THRESHOLD 16384 + #endif #endif // Memory allocation functions @@ -28,7 +37,11 @@ // This is the maximum size of the value stack in py_TValue units // The actual size in bytes equals `sizeof(py_TValue) * PK_VM_STACK_SIZE` #ifndef PK_VM_STACK_SIZE // can be overridden by cmake -#define PK_VM_STACK_SIZE 16384 + #if PK_LOW_MEMORY_MODE + #define PK_VM_STACK_SIZE 2048 + #else + #define PK_VM_STACK_SIZE 16384 + #endif #endif // This is the maximum number of local variables in a function @@ -51,4 +64,4 @@ #define PK_PLATFORM_SEP '\\' #else #define PK_PLATFORM_SEP '/' -#endif \ No newline at end of file +#endif diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index e3f0ce8e..5a8b1709 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -11,11 +11,10 @@ py_TValue* FastLocals__try_get_by_name(py_TValue* locals, const CodeObject* co, NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co); typedef struct ValueStack { - // We allocate extra PK_VM_STACK_SIZE/128 places to keep `_sp` valid when `is_overflow() == - // true`. py_TValue* sp; py_TValue* end; - py_TValue begin[PK_VM_STACK_SIZE + PK_VM_STACK_SIZE / 128]; + // We allocate extra places to keep `_sp` valid to detect stack overflow + py_TValue begin[PK_VM_STACK_SIZE + PK_MAX_CO_VARNAMES * 2]; } ValueStack; void ValueStack__ctor(ValueStack* self); diff --git a/src/common/sstream.c b/src/common/sstream.c index 70d77ddb..c34937c2 100644 --- a/src/common/sstream.c +++ b/src/common/sstream.c @@ -100,6 +100,7 @@ void c11_sbuf__write_quoted(c11_sbuf* self, c11_sv sv, char quote) { case '\b': c11_sbuf__write_cstrn(self, "\\b", 2); break; default: { int u8bytes = c11__u8_header(c, true); + if(i + u8bytes > sv.size) u8bytes = 0; // invalid utf8 if(u8bytes <= 1) { // not a valid utf8 char, or ascii if(!isprint(c)) { diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index a610fa32..671bbb5d 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -13,6 +13,14 @@ static bool stack_format_object(VM* self, c11_sv spec); #define CHECK_RETURN_FROM_EXCEPT_OR_FINALLY() \ if(self->is_curr_exc_handled) py_clearexc(NULL) +#define CHECK_STACK_OVERFLOW() \ + do { \ + if(self->stack.sp > self->stack.end) { \ + py_exception(tp_StackOverflowError, ""); \ + goto __ERROR; \ + } \ + } while(0) + #define DISPATCH() \ do { \ frame->ip++; \ @@ -92,7 +100,7 @@ FrameResult VM__run_top_frame(VM* self) { pk_print_stack(self, frame, byte); #endif - if(self->is_signal_interrupted){ + if(self->is_signal_interrupted) { self->is_signal_interrupted = false; py_exception(tp_KeyboardInterrupt, ""); goto __ERROR; @@ -132,15 +140,40 @@ FrameResult VM__run_top_frame(VM* self) { POP(); DISPATCH(); /*****************************************/ - case OP_LOAD_CONST: PUSH(c11__at(py_TValue, &frame->co->consts, byte.arg)); DISPATCH(); - case OP_LOAD_NONE: py_newnone(SP()++); DISPATCH(); - case OP_LOAD_TRUE: py_newbool(SP()++, true); DISPATCH(); - case OP_LOAD_FALSE: py_newbool(SP()++, false); DISPATCH(); + case OP_LOAD_CONST: { + CHECK_STACK_OVERFLOW(); + PUSH(c11__at(py_TValue, &frame->co->consts, byte.arg)); + DISPATCH(); + } + case OP_LOAD_NONE: { + CHECK_STACK_OVERFLOW(); + py_newnone(SP()++); + DISPATCH(); + } + case OP_LOAD_TRUE: { + CHECK_STACK_OVERFLOW(); + py_newbool(SP()++, true); + DISPATCH(); + } + case OP_LOAD_FALSE: { + CHECK_STACK_OVERFLOW(); + py_newbool(SP()++, false); + DISPATCH(); + } /*****************************************/ - case OP_LOAD_SMALL_INT: py_newint(SP()++, (int16_t)byte.arg); DISPATCH(); + case OP_LOAD_SMALL_INT: { + CHECK_STACK_OVERFLOW(); + py_newint(SP()++, (int16_t)byte.arg); + DISPATCH(); + } /*****************************************/ - case OP_LOAD_ELLIPSIS: py_newellipsis(SP()++); DISPATCH(); + case OP_LOAD_ELLIPSIS: { + CHECK_STACK_OVERFLOW(); + py_newellipsis(SP()++); + DISPATCH(); + } case OP_LOAD_FUNCTION: { + CHECK_STACK_OVERFLOW(); FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg); Function* ud = py_newobject(SP(), tp_function, 0, sizeof(Function)); Function__ctor(ud, decl, frame->module); @@ -952,8 +985,12 @@ FrameResult VM__run_top_frame(VM* self) { goto __ERROR; } - py_Type type = - pk_newtype(py_name2str(name), base, frame->module, NULL, base_ti->is_python, false); + py_Type type = pk_newtype(py_name2str(name), + base, + frame->module, + NULL, + base_ti->is_python, + false); PUSH(py_tpobject(type)); self->__curr_class = TOP(); DISPATCH(); diff --git a/src/interpreter/heap.c b/src/interpreter/heap.c index 7b044b68..34679923 100644 --- a/src/interpreter/heap.c +++ b/src/interpreter/heap.c @@ -1,5 +1,6 @@ #include "pocketpy/interpreter/heap.h" #include "pocketpy/common/memorypool.h" +#include "pocketpy/config.h" #include "pocketpy/objects/base.h" void ManagedHeap__ctor(ManagedHeap* self, VM* vm) { @@ -94,7 +95,7 @@ PyObject* PyObject__new(py_Type type, int slots, int size) { PyObject* self; // header + slots + udsize size = sizeof(PyObject) + PK_OBJ_SLOTS_SIZE(slots) + size; - if(size <= kPoolObjectBlockSize) { + if(!PK_LOW_MEMORY_MODE && size <= kPoolObjectBlockSize) { self = PoolObject_alloc(); self->gc_is_large = false; } else {