Switch to much tighter memory layout

This commit is contained in:
Michal Moskal 2016-09-08 21:10:01 +01:00
Родитель d7819ff326
Коммит 53019799d1
3 изменённых файлов: 173 добавлений и 171 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -2,7 +2,6 @@
build build
*.swp *.swp
*.log *.log
microbit-touchdevelop/MicroBitTouchDevelop.h.gch
test.cpp test.cpp
yotta_modules yotta_modules
yotta_targets yotta_targets

192
inc/pxt.h
Просмотреть файл

@ -62,7 +62,6 @@ namespace pxt {
int templateHash(); int templateHash();
int programHash(); int programHash();
int getNumGlobals(); int getNumGlobals();
RefRecord* mkRecord(int reflen, int totallen);
RefRecord* mkClassInstance(int vtableOffset); RefRecord* mkClassInstance(int vtableOffset);
// The standard calling convention is: // The standard calling convention is:
@ -96,28 +95,52 @@ namespace pxt {
} }
#ifdef DEBUG_MEMLEAKS
class RefObject; class RefObject;
#ifdef DEBUG_MEMLEAKS
extern std::set<RefObject*> allptrs; extern std::set<RefObject*> allptrs;
#endif #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. // A base abstract class for ref-counted objects.
class RefObject class RefObject
{ {
public: public:
uint16_t refcnt; uint16_t refcnt;
uint16_t vtable;
RefObject() RefObject(uint16_t vt)
{ {
refcnt = 1; refcnt = 2;
vtable = vt;
#ifdef DEBUG_MEMLEAKS #ifdef DEBUG_MEMLEAKS
allptrs.insert(this); allptrs.insert(this);
#endif #endif
} }
// Call to disable pointer tracking on the current instance. inline VTable *getVTable() {
void canLeak() 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 #ifdef DEBUG_MEMLEAKS
allptrs.erase(this); allptrs.erase(this);
#endif #endif
@ -128,51 +151,17 @@ namespace pxt {
{ {
check(refcnt > 0, ERR_REF_DELETED); check(refcnt > 0, ERR_REF_DELETED);
//printf("INCR "); this->print(); //printf("INCR "); this->print();
refcnt++; refcnt += 2;
} }
inline void unref() inline void unref()
{ {
//printf("DECR "); this->print(); //printf("DECR "); this->print();
if (--refcnt == 0) { refcnt -= 2;
delete this; 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, // A ref-counted collection of either primitive or ref-counted objects (String, Image,
@ -183,13 +172,13 @@ namespace pxt {
public: public:
// 1 - collection of refs (need decr) // 1 - collection of refs (need decr)
// 2 - collection of strings (in fact we always have 3, never 2 alone) // 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; std::vector<uint32_t> data;
RefCollection(uint16_t f) : flags(f) {} RefCollection(uint16_t f);
virtual ~RefCollection();
virtual void print();
inline bool in_range(int x) { inline bool in_range(int x) {
return (0 <= x && x < (int)data.size()); return (0 <= x && x < (int)data.size());
@ -197,6 +186,9 @@ namespace pxt {
inline int length() { return data.size(); } inline int length() { return data.size(); }
void destroy();
void print();
void push(uint32_t x); void push(uint32_t x);
uint32_t getAt(int x); uint32_t getAt(int x);
void removeAt(int x); void removeAt(int x);
@ -214,49 +206,34 @@ namespace pxt {
: public RefObject : public RefObject
{ {
public: public:
uint16_t padding;
uint32_t vtable;
std::vector<MapEntry> data; std::vector<MapEntry> data;
RefMap() : padding(0), vtable(42) {} RefMap();
void destroy();
virtual ~RefMap(); void print();
virtual void print();
int findIdx(uint32_t key); int findIdx(uint32_t key);
}; };
struct VTable { // A ref-counted, user-defined JS object.
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.
class RefRecord class RefRecord
: public RefObject : public RefObject
{ {
public: 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. // The object is allocated, so that there is space at the end for the fields.
uint32_t fields[]; uint32_t fields[];
virtual ~RefRecord(); RefRecord(uint16_t v) : RefObject(v) {}
virtual void print();
uint32_t ld(int idx); uint32_t ld(int idx);
uint32_t ldref(int idx); uint32_t ldref(int idx);
void st(int idx, uint32_t v); void st(int idx, uint32_t v);
void stref(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; class RefAction;
typedef uint32_t (*ActionCB)(uint32_t *captured, uint32_t arg0, uint32_t arg1, uint32_t arg2); 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 // fields[] contain captured locals
uint32_t fields[]; uint32_t fields[];
virtual ~RefAction(); void destroy();
virtual void print(); void print();
RefAction();
inline void stCore(int idx, uint32_t v) inline void stCore(int idx, uint32_t v)
{ {
@ -298,9 +277,9 @@ namespace pxt {
{ {
public: public:
uint32_t v; uint32_t v;
void destroy();
virtual void print(); void print();
RefLocal() : v(0) {} RefLocal();
}; };
class RefRefLocal class RefRefLocal
@ -308,36 +287,10 @@ namespace pxt {
{ {
public: public:
uint32_t v; uint32_t v;
void destroy();
virtual void print(); void print();
virtual ~RefRefLocal(); RefRefLocal();
RefRefLocal() : v(0) {}
}; };
// 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 // The ARM Thumb generator in the JavaScript code is parsing
@ -352,6 +305,29 @@ namespace pxt { \
#define PXT_SHIMS_END }; } #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 #endif
// vim: ts=2 sw=2 expandtab // vim: ts=2 sw=2 expandtab

Просмотреть файл

@ -73,43 +73,29 @@ namespace pxt {
return runAction3(a, 0, 0, 0); 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) RefRecord* mkClassInstance(int vtableOffset)
{ {
VTable *vtable = (VTable*)&bytecode[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)); void *ptr = ::operator new(vtable->numbytes);
RefRecord *r = new (ptr) RefRecord(); RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable));
r->len = vtable->numfields; memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord));
r->reflen = 255;
memset(r->fields, 0, r->len * sizeof(uint32_t));
r->fields[0] = (uint32_t)vtable;
return r; return r;
} }
uint32_t RefRecord::ld(int idx) 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]; return fields[idx];
} }
uint32_t RefRecord::ldref(int idx) uint32_t RefRecord::ldref(int idx)
{ {
//printf("LD %p len=%d reflen=%d idx=%d\n", this, len, reflen, 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]; uint32_t tmp = fields[idx];
incr(tmp); incr(tmp);
return tmp; return tmp;
@ -117,52 +103,51 @@ namespace pxt {
void RefRecord::st(int idx, uint32_t v) 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; fields[idx] = v;
} }
void RefRecord::stref(int idx, uint32_t v) void RefRecord::stref(int idx, uint32_t v)
{ {
//printf("ST %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx); //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]); decr(fields[idx]);
fields[idx] = v; fields[idx] = v;
} }
RefRecord::~RefRecord() void RefObject::destroy() {
{ ((RefObjectMethod)getVTable()->methods[0])(this);
if (reflen == 255) { delete this;
// assuming vtable }
VTable *tbl = (VTable*)fields[0];
uint8_t *refmask = (uint8_t*)&tbl->methods[tbl->nummethods]; void RefObject::print() {
int len = tbl->numfields; ((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) { for (int i = 0; i < len; ++i) {
if (refmask[i]) decr(fields[i]); if (refmask[i]) decr(r->fields[i]);
fields[i] = 0; 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) { void RefCollection::push(uint32_t x) {
if (flags & 1) incr(x); if (isRef()) incr(x);
data.push_back(x); data.push_back(x);
} }
uint32_t RefCollection::getAt(int x) { uint32_t RefCollection::getAt(int x) {
if (in_range(x)) { if (in_range(x)) {
uint32_t tmp = data.at(x); uint32_t tmp = data.at(x);
if (flags & 1) incr(tmp); if (isRef()) incr(tmp);
return tmp; return tmp;
} }
else { else {
@ -175,7 +160,7 @@ namespace pxt {
if (!in_range(x)) if (!in_range(x))
return; return;
if (flags & 1) decr(data.at(x)); if (isRef()) decr(data.at(x));
data.erase(data.begin()+x); data.erase(data.begin()+x);
} }
@ -183,7 +168,7 @@ namespace pxt {
if (!in_range(x)) if (!in_range(x))
return; return;
if (flags & 1) { if (isRef()) {
decr(data.at(x)); decr(data.at(x));
incr(y); incr(y);
} }
@ -194,7 +179,7 @@ namespace pxt {
if (!in_range(start)) if (!in_range(start))
return -1; return -1;
if (flags & 2) { if (isString()) {
StringData *xx = (StringData*)x; StringData *xx = (StringData*)x;
for (uint32_t i = start; i < data.size(); ++i) { for (uint32_t i = start; i < data.size(); ++i) {
StringData *ee = (StringData*)data.at(i); StringData *ee = (StringData*)data.at(i);
@ -219,29 +204,55 @@ namespace pxt {
return 0; return 0;
} }
void RefObject::print() namespace Coll0 {
{ PXT_VTABLE_BEGIN(RefCollection, 0, 0)
printf("RefObject %p\n", this); 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 (this->isRef())
if (flags & 1) for (uint32_t i = 0; i < this->data.size(); ++i) {
for (uint32_t i = 0; i < data.size(); ++i) { decr(this->data[i]);
decr(data[i]); this->data[i] = 0;
data[i] = 0;
} }
data.resize(0); this->data.resize(0);
} }
void RefCollection::print() 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 // fields[] contain captured locals
RefAction::~RefAction() void RefAction::destroy()
{ {
for (int i = 0; i < this->reflen; ++i) { for (int i = 0; i < this->reflen; ++i) {
decr(fields[i]); decr(fields[i]);
@ -259,17 +270,33 @@ namespace pxt {
printf("RefLocal %p r=%d v=%d\n", this, refcnt, v); 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() void RefRefLocal::print()
{ {
printf("RefRefLocal %p r=%d v=%p\n", this, refcnt, (void*)v); printf("RefRefLocal %p r=%d v=%p\n", this, refcnt, (void*)v);
} }
RefRefLocal::~RefRefLocal() void RefRefLocal::destroy()
{ {
decr(v); 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) { for (unsigned i = 0; i < data.size(); ++i) {
if (data[i].key & 1) { if (data[i].key & 1) {
decr(data[i].val); decr(data[i].val);
@ -430,7 +457,7 @@ namespace pxt {
((uint32_t (*)())startptr)(); ((uint32_t (*)())startptr)();
#ifdef DEBUG_MEMLEAKS #ifdef DEBUG_MEMLEAKS
bitvm::debugMemLeaks(); pxt::debugMemLeaks();
#endif #endif
return; return;