From 53019799d1e13f6066181fb5a1519635bf9113e9 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Thu, 8 Sep 2016 21:10:01 +0100 Subject: [PATCH] Switch to much tighter memory layout --- .gitignore | 1 - inc/pxt.h | 192 ++++++++++++++++++++++--------------------------- source/pxt.cpp | 151 ++++++++++++++++++++++---------------- 3 files changed, 173 insertions(+), 171 deletions(-) diff --git a/.gitignore b/.gitignore index 402da36..359c623 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ build *.swp *.log -microbit-touchdevelop/MicroBitTouchDevelop.h.gch test.cpp yotta_modules yotta_targets diff --git a/inc/pxt.h b/inc/pxt.h index e1c0c60..418f708 100644 --- a/inc/pxt.h +++ b/inc/pxt.h @@ -62,7 +62,6 @@ namespace pxt { int templateHash(); int programHash(); int getNumGlobals(); - RefRecord* mkRecord(int reflen, int totallen); RefRecord* mkClassInstance(int vtableOffset); // The standard calling convention is: @@ -96,28 +95,52 @@ namespace pxt { } -#ifdef DEBUG_MEMLEAKS class RefObject; +#ifdef DEBUG_MEMLEAKS extern std::set allptrs; #endif + typedef void (*RefObjectMethod)(RefObject *self); + typedef void *PVoid; + typedef void **PPVoid; + + const PPVoid RefMapMarker = (PPVoid)(void*)43; + + struct VTable { + uint16_t numbytes; // in the entire object, including the vtable pointer + uint16_t userdata; + PVoid *ifaceTable; + PVoid methods[10]; + // refmask sits at &methods[nummethods] + }; + + const int vtableShift = 2; + // A base abstract class for ref-counted objects. class RefObject { public: uint16_t refcnt; + uint16_t vtable; - RefObject() + RefObject(uint16_t vt) { - refcnt = 1; + refcnt = 2; + vtable = vt; #ifdef DEBUG_MEMLEAKS allptrs.insert(this); #endif } - // Call to disable pointer tracking on the current instance. - void canLeak() - { + inline VTable *getVTable() { + return (VTable*)(vtable << vtableShift); + } + + void destroy(); + void print(); + + // Call to disable pointer tracking on the current instance (in destructor or some other hack) + inline void untrack() { #ifdef DEBUG_MEMLEAKS allptrs.erase(this); #endif @@ -128,51 +151,17 @@ namespace pxt { { check(refcnt > 0, ERR_REF_DELETED); //printf("INCR "); this->print(); - refcnt++; + refcnt += 2; } inline void unref() { //printf("DECR "); this->print(); - if (--refcnt == 0) { - delete this; + refcnt -= 2; + if (refcnt == 0) { + destroy(); } } - - virtual void print(); - - virtual ~RefObject() - { - // This is just a base class for ref-counted objects. - // There is nothing to free yet, but derived classes will have things to free. -#ifdef DEBUG_MEMLEAKS - allptrs.erase(this); -#endif - } - - // This is used by indexOf function, overridden in RefString - virtual bool equals(RefObject *other) - { - return this == other; - } - }; - - // Ref-counted wrapper around any C++ object. - template - class RefStruct - : public RefObject - { - public: - T v; - - virtual ~RefStruct() { } - - virtual void print() - { - printf("RefStruct %p r=%d\n", this, refcnt); - } - - RefStruct(const T& i) : v(i) {} }; // A ref-counted collection of either primitive or ref-counted objects (String, Image, @@ -183,13 +172,13 @@ namespace pxt { public: // 1 - collection of refs (need decr) // 2 - collection of strings (in fact we always have 3, never 2 alone) - uint16_t flags; + inline uint32_t getFlags() { return getVTable()->userdata; } + inline bool isRef() { return getFlags() & 1; } + inline bool isString() { return getFlags() & 2; } + std::vector data; - RefCollection(uint16_t f) : flags(f) {} - - virtual ~RefCollection(); - virtual void print(); + RefCollection(uint16_t f); inline bool in_range(int x) { return (0 <= x && x < (int)data.size()); @@ -197,6 +186,9 @@ namespace pxt { inline int length() { return data.size(); } + void destroy(); + void print(); + void push(uint32_t x); uint32_t getAt(int x); void removeAt(int x); @@ -214,49 +206,34 @@ namespace pxt { : public RefObject { public: - uint16_t padding; - uint32_t vtable; std::vector data; - RefMap() : padding(0), vtable(42) {} - - virtual ~RefMap(); - virtual void print(); - + RefMap(); + void destroy(); + void print(); int findIdx(uint32_t key); }; - struct VTable { - uint16_t refcount; // 0xffff - uint8_t numfields; - uint8_t nummethods; - uint32_t methods[]; - // refmask sits at &methods[nummethods] - }; - - // A ref-counted, user-defined Touch Develop object. + // A ref-counted, user-defined JS object. class RefRecord : public RefObject { public: - // Total number of fields. - uint8_t len; - // Number of fields which are ref-counted pointers; these always come first - // on the fields[] array. - uint8_t reflen; // The object is allocated, so that there is space at the end for the fields. uint32_t fields[]; - virtual ~RefRecord(); - virtual void print(); + RefRecord(uint16_t v) : RefObject(v) {} uint32_t ld(int idx); uint32_t ldref(int idx); void st(int idx, uint32_t v); void stref(int idx, uint32_t v); - }; + // these are needed when constructing vtables for user-defined classes + void RefRecord_destroy(RefRecord *r); + void RefRecord_print(RefRecord *r); + class RefAction; typedef uint32_t (*ActionCB)(uint32_t *captured, uint32_t arg0, uint32_t arg1, uint32_t arg2); @@ -272,8 +249,10 @@ namespace pxt { // fields[] contain captured locals uint32_t fields[]; - virtual ~RefAction(); - virtual void print(); + void destroy(); + void print(); + + RefAction(); inline void stCore(int idx, uint32_t v) { @@ -298,9 +277,9 @@ namespace pxt { { public: uint32_t v; - - virtual void print(); - RefLocal() : v(0) {} + void destroy(); + void print(); + RefLocal(); }; class RefRefLocal @@ -308,36 +287,10 @@ namespace pxt { { public: uint32_t v; - - virtual void print(); - virtual ~RefRefLocal(); - - RefRefLocal() : v(0) {} + void destroy(); + void print(); + RefRefLocal(); }; - - - // A ref-counted byte buffer - class RefBuffer - : public RefObject - { - public: - std::vector data; - - virtual ~RefBuffer() - { - data.resize(0); - } - - virtual void print() - { - printf("RefBuffer %p r=%d size=%d [%p, ...]\n", this, refcnt, data.size(), data.size() > 0 ? data[0] : 0); - } - - char *cptr() { return (char*)&data[0]; } - int size() { return data.size(); } - - }; - } // The ARM Thumb generator in the JavaScript code is parsing @@ -352,6 +305,29 @@ namespace pxt { \ #define PXT_SHIMS_END }; } +#pragma GCC diagnostic ignored "-Wpmf-conversions" + +#define PXT_VTABLE_TO_INT(vt) ((uint32_t)(vt) >> vtableShift) +#define PXT_VTABLE_BEGIN(classname, flags, iface) \ +const VTable classname ## _vtable \ + __attribute__((aligned(1 << vtableShift))) \ + = { \ + sizeof(classname), \ + flags, \ + iface, \ + { \ + (void*)&classname::destroy, \ + (void*)&classname::print, + +#define PXT_VTABLE_END } }; + +#define PXT_VTABLE_INIT(classname) \ + RefObject(PXT_VTABLE_TO_INT(&classname ## _vtable)) + +#define PXT_VTABLE_CTOR(classname) \ + PXT_VTABLE_BEGIN(classname, 0, 0) PXT_VTABLE_END \ + classname::classname() : PXT_VTABLE_INIT(classname) + #endif // vim: ts=2 sw=2 expandtab diff --git a/source/pxt.cpp b/source/pxt.cpp index 8a9c1e6..4f6188d 100644 --- a/source/pxt.cpp +++ b/source/pxt.cpp @@ -73,43 +73,29 @@ namespace pxt { return runAction3(a, 0, 0, 0); } - RefRecord* mkRecord(int reflen, int totallen) - { - intcheck(0 <= reflen && reflen <= totallen, ERR_SIZE, 1); - intcheck(reflen <= totallen && totallen < 255, ERR_SIZE, 2); - - void *ptr = ::operator new(sizeof(RefRecord) + totallen * sizeof(uint32_t)); - RefRecord *r = new (ptr) RefRecord(); - r->len = totallen; - r->reflen = reflen; - memset(r->fields, 0, r->len * sizeof(uint32_t)); - return r; - } - RefRecord* mkClassInstance(int vtableOffset) { VTable *vtable = (VTable*)&bytecode[vtableOffset]; - intcheck(vtable->refcount == 0xffff, ERR_SIZE, 3); + + intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3); + intcheck(vtable->methods[1] == &RefRecord_print, ERR_SIZE, 4); - void *ptr = ::operator new(sizeof(RefRecord) + vtable->numfields * sizeof(uint32_t)); - RefRecord *r = new (ptr) RefRecord(); - r->len = vtable->numfields; - r->reflen = 255; - memset(r->fields, 0, r->len * sizeof(uint32_t)); - r->fields[0] = (uint32_t)vtable; + void *ptr = ::operator new(vtable->numbytes); + RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable)); + memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord)); return r; } uint32_t RefRecord::ld(int idx) { - intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1); + //intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1); return fields[idx]; } uint32_t RefRecord::ldref(int idx) { //printf("LD %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx); - intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2); + //intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2); uint32_t tmp = fields[idx]; incr(tmp); return tmp; @@ -117,52 +103,51 @@ namespace pxt { void RefRecord::st(int idx, uint32_t v) { - intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3); + //intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3); fields[idx] = v; } void RefRecord::stref(int idx, uint32_t v) { //printf("ST %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx); - intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4); + //intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4); decr(fields[idx]); fields[idx] = v; } - RefRecord::~RefRecord() - { - if (reflen == 255) { - // assuming vtable - VTable *tbl = (VTable*)fields[0]; - uint8_t *refmask = (uint8_t*)&tbl->methods[tbl->nummethods]; - int len = tbl->numfields; + void RefObject::destroy() { + ((RefObjectMethod)getVTable()->methods[0])(this); + delete this; + } + + void RefObject::print() { + ((RefObjectMethod)getVTable()->methods[1])(this); + } + + void RefRecord_destroy(RefRecord *r) { + auto tbl = r->getVTable(); + uint8_t *refmask = (uint8_t*)&tbl->methods[tbl->userdata & 0xff]; + int len = (tbl->numbytes >> 2) - 1; for (int i = 0; i < len; ++i) { - if (refmask[i]) decr(fields[i]); - fields[i] = 0; + if (refmask[i]) decr(r->fields[i]); + r->fields[i] = 0; } - } else { - for (int i = 0; i < this->reflen; ++i) { - decr(fields[i]); - fields[i] = 0; - } - } } - void RefRecord::print() + void RefRecord_print(RefRecord *r) { - printf("RefRecord %p r=%d size=%d (%d refs)\n", this, refcnt, len, reflen); + printf("RefRecord %p r=%d size=%d bytes\n", r, r->refcnt, r->getVTable()->numbytes); } - void RefCollection::push(uint32_t x) { - if (flags & 1) incr(x); + if (isRef()) incr(x); data.push_back(x); } uint32_t RefCollection::getAt(int x) { if (in_range(x)) { uint32_t tmp = data.at(x); - if (flags & 1) incr(tmp); + if (isRef()) incr(tmp); return tmp; } else { @@ -175,7 +160,7 @@ namespace pxt { if (!in_range(x)) return; - if (flags & 1) decr(data.at(x)); + if (isRef()) decr(data.at(x)); data.erase(data.begin()+x); } @@ -183,7 +168,7 @@ namespace pxt { if (!in_range(x)) return; - if (flags & 1) { + if (isRef()) { decr(data.at(x)); incr(y); } @@ -194,7 +179,7 @@ namespace pxt { if (!in_range(start)) return -1; - if (flags & 2) { + if (isString()) { StringData *xx = (StringData*)x; for (uint32_t i = start; i < data.size(); ++i) { StringData *ee = (StringData*)data.at(i); @@ -219,29 +204,55 @@ namespace pxt { return 0; } - void RefObject::print() - { - printf("RefObject %p\n", this); + namespace Coll0 { + PXT_VTABLE_BEGIN(RefCollection, 0, 0) + PXT_VTABLE_END + } + namespace Coll1 { + PXT_VTABLE_BEGIN(RefCollection, 1, 0) + PXT_VTABLE_END + } + namespace Coll3 { + PXT_VTABLE_BEGIN(RefCollection, 3, 0) + PXT_VTABLE_END } - RefCollection::~RefCollection() + RefCollection::RefCollection(uint16_t flags) : RefObject(0) { + switch (flags) { + case 0: + vtable = PXT_VTABLE_TO_INT(&Coll0::RefCollection_vtable); + break; + case 1: + vtable = PXT_VTABLE_TO_INT(&Coll1::RefCollection_vtable); + break; + case 3: + vtable = PXT_VTABLE_TO_INT(&Coll3::RefCollection_vtable); + break; + default: + error(ERR_SIZE); + break; + } + } + + void RefCollection::destroy() { - // printf("KILL "); this->print(); - if (flags & 1) - for (uint32_t i = 0; i < data.size(); ++i) { - decr(data[i]); - data[i] = 0; + if (this->isRef()) + for (uint32_t i = 0; i < this->data.size(); ++i) { + decr(this->data[i]); + this->data[i] = 0; } - data.resize(0); + this->data.resize(0); } void RefCollection::print() { - printf("RefCollection %p r=%d flags=%d size=%d [%p, ...]\n", this, refcnt, flags, data.size(), data.size() > 0 ? data[0] : 0); + printf("RefCollection %p r=%d flags=%d size=%d [%p, ...]\n", this, refcnt, getFlags(), data.size(), data.size() > 0 ? data[0] : 0); } + PXT_VTABLE_CTOR(RefAction) {} + // fields[] contain captured locals - RefAction::~RefAction() + void RefAction::destroy() { for (int i = 0; i < this->reflen; ++i) { decr(fields[i]); @@ -259,17 +270,33 @@ namespace pxt { printf("RefLocal %p r=%d v=%d\n", this, refcnt, v); } + void RefLocal::destroy() + { + } + + PXT_VTABLE_CTOR(RefLocal) { + v = 0; + } + + PXT_VTABLE_CTOR(RefRefLocal) { + v = 0; + } + void RefRefLocal::print() { printf("RefRefLocal %p r=%d v=%p\n", this, refcnt, (void*)v); } - RefRefLocal::~RefRefLocal() + void RefRefLocal::destroy() { decr(v); } - RefMap::~RefMap() { + PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker) + PXT_VTABLE_END + RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {} + + void RefMap::destroy() { for (unsigned i = 0; i < data.size(); ++i) { if (data[i].key & 1) { decr(data[i].val); @@ -430,7 +457,7 @@ namespace pxt { ((uint32_t (*)())startptr)(); #ifdef DEBUG_MEMLEAKS - bitvm::debugMemLeaks(); + pxt::debugMemLeaks(); #endif return;