Switch to much tighter memory layout
This commit is contained in:
Родитель
d7819ff326
Коммит
53019799d1
|
@ -2,7 +2,6 @@
|
|||
build
|
||||
*.swp
|
||||
*.log
|
||||
microbit-touchdevelop/MicroBitTouchDevelop.h.gch
|
||||
test.cpp
|
||||
yotta_modules
|
||||
yotta_targets
|
||||
|
|
192
inc/pxt.h
192
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<RefObject*> 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 T>
|
||||
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<uint32_t> 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<MapEntry> 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<uint8_t> 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
|
||||
|
|
151
source/pxt.cpp
151
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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче