cad13785e2
* Start on the C++ conversion for tagged ints
568 lines
13 KiB
C++
568 lines
13 KiB
C++
#include "pxtbase.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace pxt {
|
|
|
|
TValue incr(TValue e) {
|
|
if (isRefCounted(e)) {
|
|
getVTable((RefObject *)e);
|
|
#if MEMDBG_ENABLED
|
|
if (((RefObject *)e)->refcnt != 0xffff)
|
|
MEMDBG("INCR: %p refs=%d", e, ((RefObject *)e)->refcnt);
|
|
#endif
|
|
((RefObject *)e)->ref();
|
|
}
|
|
return e;
|
|
}
|
|
|
|
void decr(TValue e) {
|
|
if (isRefCounted(e)) {
|
|
#if MEMDBG_ENABLED
|
|
if (((RefObject *)e)->refcnt != 0xffff)
|
|
MEMDBG("DECR: %p refs=%d", e, ((RefObject *)e)->refcnt);
|
|
#endif
|
|
((RefObject *)e)->unref();
|
|
}
|
|
}
|
|
|
|
// TODO
|
|
Action mkAction(int reflen, int totallen, int startptr) {
|
|
check(0 <= reflen && reflen <= totallen, ERR_SIZE, 1);
|
|
check(reflen <= totallen && totallen <= 255, ERR_SIZE, 2);
|
|
check(bytecode[startptr] == 0xffff, ERR_INVALID_BINARY_HEADER, 3);
|
|
check(bytecode[startptr + 1] == PXT_REF_TAG_ACTION, ERR_INVALID_BINARY_HEADER, 4);
|
|
|
|
uintptr_t tmp = (uintptr_t)&bytecode[startptr];
|
|
|
|
if (totallen == 0) {
|
|
return (TValue)tmp; // no closure needed
|
|
}
|
|
|
|
void *ptr = ::operator new(sizeof(RefAction) + totallen * sizeof(unsigned));
|
|
RefAction *r = new (ptr) RefAction();
|
|
r->len = totallen;
|
|
r->reflen = reflen;
|
|
r->func = (ActionCB)((tmp + 4) | 1);
|
|
memset(r->fields, 0, r->len * sizeof(unsigned));
|
|
|
|
MEMDBG("mkAction: start=%p => %p", startptr, r);
|
|
|
|
return (Action)r;
|
|
}
|
|
|
|
// TODO
|
|
TValue runAction3(Action a, TValue arg0, TValue arg1, TValue arg2) {
|
|
auto aa = (RefAction *)a;
|
|
if (aa->vtable == PXT_REF_TAG_ACTION) {
|
|
check(aa->refcnt == 0xffff, ERR_INVALID_BINARY_HEADER, 4);
|
|
return ((ActionCB)(((uintptr_t)a + 4) | 1))(NULL, arg0, arg1, arg2);
|
|
} else {
|
|
check(aa->refcnt != 0xffff, ERR_INVALID_BINARY_HEADER, 4);
|
|
return aa->runCore(arg0, arg1, arg2);
|
|
}
|
|
}
|
|
|
|
TValue runAction2(Action a, TValue arg0, TValue arg1) {
|
|
return runAction3(a, arg0, arg1, 0);
|
|
}
|
|
|
|
TValue runAction1(Action a, TValue arg0) {
|
|
return runAction3(a, arg0, 0, 0);
|
|
}
|
|
|
|
TValue runAction0(Action a) {
|
|
return runAction3(a, 0, 0, 0);
|
|
}
|
|
|
|
RefRecord *mkClassInstance(int vtableOffset) {
|
|
VTable *vtable = (VTable *)&bytecode[vtableOffset];
|
|
|
|
intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3);
|
|
intcheck(vtable->methods[1] == &RefRecord_print, ERR_SIZE, 4);
|
|
|
|
void *ptr = ::operator new(vtable->numbytes);
|
|
RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable));
|
|
memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord));
|
|
MEMDBG("mkClass: vt=%p => %p", vtable, r);
|
|
return r;
|
|
}
|
|
|
|
TValue RefRecord::ld(int idx) {
|
|
// intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1);
|
|
return fields[idx];
|
|
}
|
|
|
|
TValue RefRecord::ldref(int idx) {
|
|
// DMESG("LD %p len=%d reflen=%d idx=%d", this, len, reflen, idx);
|
|
// intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2);
|
|
TValue tmp = fields[idx];
|
|
incr(tmp);
|
|
return tmp;
|
|
}
|
|
|
|
void RefRecord::st(int idx, TValue v) {
|
|
// intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3);
|
|
fields[idx] = v;
|
|
}
|
|
|
|
void RefRecord::stref(int idx, TValue v) {
|
|
// DMESG("ST %p len=%d reflen=%d idx=%d", this, len, reflen, idx);
|
|
// intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4);
|
|
decr(fields[idx]);
|
|
fields[idx] = v;
|
|
}
|
|
|
|
void RefObject::destroyVT() {
|
|
((RefObjectMethod)getVTable(this)->methods[0])(this);
|
|
::operator delete(this);
|
|
}
|
|
|
|
void RefObject::printVT() {
|
|
((RefObjectMethod)getVTable(this)->methods[1])(this);
|
|
}
|
|
|
|
void RefRecord_destroy(RefRecord *r) {
|
|
VTable *tbl = getVTable(r);
|
|
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(r->fields[i]);
|
|
r->fields[i] = 0;
|
|
}
|
|
}
|
|
|
|
void RefRecord_print(RefRecord *r) {
|
|
DMESG("RefRecord %p r=%d size=%d bytes", r, r->refcnt, getVTable(r)->numbytes);
|
|
}
|
|
|
|
TValue Segment::get(unsigned i) {
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("In Segment::get index:%d", i);
|
|
this->print();
|
|
#endif
|
|
|
|
if (i < length) {
|
|
return data[i];
|
|
}
|
|
return Segment::DefaultValue;
|
|
}
|
|
|
|
void Segment::setRef(unsigned i, TValue value) {
|
|
decr(get(i));
|
|
set(i, value);
|
|
}
|
|
|
|
void Segment::set(unsigned i, TValue value) {
|
|
if (i < size) {
|
|
data[i] = value;
|
|
} else if (i < Segment::MaxSize) {
|
|
growByMin(i + 1);
|
|
data[i] = value;
|
|
} else {
|
|
return;
|
|
}
|
|
if (length <= i) {
|
|
length = i + 1;
|
|
}
|
|
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("In Segment::set");
|
|
this->print();
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
ramint_t Segment::growthFactor(ramint_t size) {
|
|
if (size == 0) {
|
|
return 4;
|
|
}
|
|
if (size < 64) {
|
|
return size * 2; // Double
|
|
}
|
|
if (size < 512) {
|
|
return size * 5 / 3; // Grow by 1.66 rate
|
|
}
|
|
// Grow by constant rate
|
|
if ((unsigned)size + 256 < MaxSize)
|
|
return size + 256;
|
|
else
|
|
return MaxSize;
|
|
}
|
|
|
|
void Segment::growByMin(ramint_t minSize) {
|
|
growBy(max(minSize, growthFactor(size)));
|
|
}
|
|
|
|
void Segment::growBy(ramint_t newSize) {
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("growBy: %d", newSize);
|
|
this->print();
|
|
#endif
|
|
if (size < newSize) {
|
|
// this will throw if unable to allocate
|
|
TValue *tmp = (TValue *)(::operator new(newSize * sizeof(TValue)));
|
|
|
|
// Copy existing data
|
|
if (size) {
|
|
memcpy(tmp, data, size * sizeof(TValue));
|
|
}
|
|
// fill the rest with default value
|
|
memset(tmp + size, 0, (newSize - size) * sizeof(TValue));
|
|
|
|
// free older segment;
|
|
::operator delete(data);
|
|
|
|
data = tmp;
|
|
size = newSize;
|
|
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("growBy - after reallocation");
|
|
this->print();
|
|
#endif
|
|
}
|
|
// else { no shrinking yet; }
|
|
return;
|
|
}
|
|
|
|
void Segment::ensure(ramint_t newSize) {
|
|
if (newSize < size) {
|
|
return;
|
|
}
|
|
growByMin(newSize);
|
|
}
|
|
|
|
void Segment::setLength(unsigned newLength) {
|
|
if (newLength > size) {
|
|
ensure(length);
|
|
}
|
|
length = newLength;
|
|
return;
|
|
}
|
|
|
|
void Segment::push(TValue value) {
|
|
this->set(length, value);
|
|
}
|
|
|
|
TValue Segment::pop() {
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("In Segment::pop");
|
|
this->print();
|
|
#endif
|
|
|
|
if (length > 0) {
|
|
--length;
|
|
TValue value = data[length];
|
|
data[length] = Segment::DefaultValue;
|
|
return value;
|
|
}
|
|
return Segment::DefaultValue;
|
|
}
|
|
|
|
// this function removes an element at index i and shifts the rest of the elements to
|
|
// left to fill the gap
|
|
TValue Segment::remove(unsigned i) {
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("In Segment::remove index:%d", i);
|
|
this->print();
|
|
#endif
|
|
if (i < length) {
|
|
// value to return
|
|
TValue ret = data[i];
|
|
if (i + 1 < length) {
|
|
// Move the rest of the elements to fill in the gap.
|
|
memmove(data + i, data + i + 1, (length - i - 1) * sizeof(unsigned));
|
|
}
|
|
length--;
|
|
data[length] = Segment::DefaultValue;
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("After Segment::remove index:%d", i);
|
|
this->print();
|
|
#endif
|
|
return ret;
|
|
}
|
|
return Segment::DefaultValue;
|
|
}
|
|
|
|
// this function inserts element value at index i by shifting the rest of the elements right.
|
|
void Segment::insert(unsigned i, TValue value) {
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("In Segment::insert index:%d value:%d", i, value);
|
|
this->print();
|
|
#endif
|
|
|
|
if (i < length) {
|
|
ensure(length + 1);
|
|
|
|
// Move the rest of the elements to fill in the gap.
|
|
memmove(data + i + 1, data + i, (length - i) * sizeof(unsigned));
|
|
|
|
data[i] = value;
|
|
length++;
|
|
} else {
|
|
// This is insert beyond the length, just call set which will adjust the length
|
|
set(i, value);
|
|
}
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("After Segment::insert index:%d", i);
|
|
this->print();
|
|
#endif
|
|
}
|
|
|
|
void Segment::print() {
|
|
DMESG("Segment: %p, length: %d, size: %d", data, (unsigned)length, (unsigned)size);
|
|
for (unsigned i = 0; i < size; i++) {
|
|
DMESG("-> %d", (unsigned)(uintptr_t)data[i]);
|
|
}
|
|
}
|
|
|
|
bool Segment::isValidIndex(unsigned i) {
|
|
if (i > length) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Segment::destroy() {
|
|
#ifdef DEBUG_BUILD
|
|
DMESG("In Segment::destroy");
|
|
this->print();
|
|
#endif
|
|
length = size = 0;
|
|
::operator delete(data);
|
|
data = nullptr;
|
|
}
|
|
|
|
void RefCollection::push(TValue x) {
|
|
incr(x);
|
|
head.push(x);
|
|
}
|
|
|
|
TValue RefCollection::pop() {
|
|
TValue ret = head.pop();
|
|
incr(ret);
|
|
return ret;
|
|
}
|
|
|
|
TValue RefCollection::getAt(int i) {
|
|
TValue tmp = head.get(i);
|
|
incr(tmp);
|
|
return tmp;
|
|
}
|
|
|
|
TValue RefCollection::removeAt(int i) {
|
|
return head.remove(i);
|
|
}
|
|
|
|
void RefCollection::insertAt(int i, TValue value) {
|
|
head.insert(i, value);
|
|
incr(value);
|
|
}
|
|
|
|
void RefCollection::setAt(int i, TValue value) {
|
|
incr(value);
|
|
head.setRef(i, value);
|
|
}
|
|
|
|
int RefCollection::indexOf(TValue x, int start) {
|
|
#ifndef X86_64
|
|
unsigned i = start;
|
|
while (head.isValidIndex(i)) {
|
|
if (pxt::eq_bool(head.get(i), x)) {
|
|
return (int)i;
|
|
}
|
|
i++;
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
bool RefCollection::removeElement(TValue x) {
|
|
int idx = indexOf(x, 0);
|
|
if (idx >= 0) {
|
|
decr(removeAt(idx));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
namespace Coll0 {
|
|
PXT_VTABLE_BEGIN(RefCollection, 0, 0)
|
|
PXT_VTABLE_END
|
|
} // namespace Coll0
|
|
|
|
RefCollection::RefCollection() : RefObject(0) {
|
|
vtable = PXT_VTABLE_TO_INT(&Coll0::RefCollection_vtable);
|
|
}
|
|
|
|
void RefCollection::destroy(RefCollection *t) {
|
|
for (unsigned i = 0; i < t->head.getLength(); i++) {
|
|
decr(t->head.get(i));
|
|
}
|
|
t->head.destroy();
|
|
}
|
|
|
|
void RefCollection::print(RefCollection *t) {
|
|
DMESG("RefCollection %p r=%d size=%d", t, t->refcnt, t->head.getLength());
|
|
t->head.print();
|
|
}
|
|
|
|
PXT_VTABLE_CTOR(RefAction) {}
|
|
|
|
// fields[] contain captured locals
|
|
void RefAction::destroy(RefAction *t) {
|
|
for (int i = 0; i < t->reflen; ++i) {
|
|
decr(t->fields[i]);
|
|
t->fields[i] = 0;
|
|
}
|
|
}
|
|
|
|
void RefAction::print(RefAction *t) {
|
|
DMESG("RefAction %p r=%d pc=%X size=%d (%d refs)", t, t->refcnt,
|
|
(const uint8_t *)t->func - (const uint8_t *)bytecode, t->len, t->reflen);
|
|
}
|
|
|
|
void RefLocal::print(RefLocal *t) {
|
|
DMESG("RefLocal %p r=%d v=%d", t, t->refcnt, t->v);
|
|
}
|
|
|
|
void RefLocal::destroy(RefLocal *) {}
|
|
|
|
PXT_VTABLE_CTOR(RefLocal) {
|
|
v = 0;
|
|
}
|
|
|
|
PXT_VTABLE_CTOR(RefRefLocal) {
|
|
v = 0;
|
|
}
|
|
|
|
void RefRefLocal::print(RefRefLocal *t) {
|
|
DMESG("RefRefLocal %p r=%d v=%p", t, t->refcnt, (void *)t->v);
|
|
}
|
|
|
|
void RefRefLocal::destroy(RefRefLocal *t) {
|
|
decr(t->v);
|
|
}
|
|
|
|
PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker)
|
|
PXT_VTABLE_END
|
|
RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {}
|
|
|
|
void RefMap::destroy(RefMap *t) {
|
|
for (unsigned i = 0; i < t->values.getLength(); ++i) {
|
|
decr(t->values.get(i));
|
|
t->values.set(i, 0);
|
|
}
|
|
t->keys.destroy();
|
|
t->values.destroy();
|
|
}
|
|
|
|
int RefMap::findIdx(unsigned key) {
|
|
for (unsigned i = 0; i < keys.getLength(); ++i) {
|
|
if ((uintptr_t)keys.get(i) == key)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void RefMap::print(RefMap *t) {
|
|
DMESG("RefMap %p r=%d size=%d", t, t->refcnt, t->keys.getLength());
|
|
}
|
|
|
|
#ifdef PXT_MEMLEAK_DEBUG
|
|
std::set<TValue> allptrs;
|
|
void debugMemLeaks() {
|
|
DMESG("LIVE POINTERS:");
|
|
for (std::set<TValue>::iterator itr = allptrs.begin(); itr != allptrs.end(); itr++) {
|
|
anyPrint(*itr);
|
|
}
|
|
DMESG("LIVE POINTERS END.");
|
|
dumpDmesg();
|
|
}
|
|
#else
|
|
void debugMemLeaks() {}
|
|
#endif
|
|
|
|
void error(PXT_ERROR code, int subcode) {
|
|
DMESG("Error: %d [%d]", code, subcode);
|
|
target_panic(42);
|
|
}
|
|
|
|
uint16_t *bytecode;
|
|
TValue *globals;
|
|
|
|
unsigned *allocate(ramint_t sz) {
|
|
unsigned *arr = new unsigned[sz];
|
|
memset(arr, 0, sz * sizeof(unsigned));
|
|
return arr;
|
|
}
|
|
|
|
void checkStr(bool cond, const char *msg) {
|
|
if (!cond) {
|
|
while (true) {
|
|
// uBit.display.scroll(msg, 100);
|
|
// uBit.sleep(100);
|
|
}
|
|
}
|
|
}
|
|
|
|
int templateHash() {
|
|
return ((int *)bytecode)[4];
|
|
}
|
|
|
|
int programHash() {
|
|
return ((int *)bytecode)[6];
|
|
}
|
|
|
|
int getNumGlobals() {
|
|
return bytecode[16];
|
|
}
|
|
|
|
#ifndef X86_64
|
|
void exec_binary(unsigned *pc) {
|
|
// XXX re-enable once the calibration code is fixed and [editor/embedded.ts]
|
|
// properly prepends a call to [internal_main].
|
|
// ::touch_develop::internal_main();
|
|
|
|
// unique group for radio based on source hash
|
|
// ::touch_develop::micro_bit::radioDefaultGroup = programHash();
|
|
|
|
unsigned ver = *pc++;
|
|
checkStr(ver == 0x4209, ":( Bad runtime version");
|
|
|
|
bytecode = *((uint16_t **)pc++); // the actual bytecode is here
|
|
globals = (TValue *)allocate(getNumGlobals());
|
|
|
|
// can be any valid address, best in RAM for speed
|
|
globals[0] = (TValue)&globals;
|
|
|
|
// just compare the first word
|
|
// TODO
|
|
checkStr(((uint32_t *)bytecode)[0] == 0x923B8E70 && (unsigned)templateHash() == *pc,
|
|
":( Failed partial flash");
|
|
|
|
uintptr_t startptr = (uintptr_t)bytecode;
|
|
|
|
startptr += 48; // header
|
|
startptr |= 1; // Thumb state
|
|
|
|
initRuntime();
|
|
|
|
((unsigned (*)())startptr)();
|
|
|
|
#ifdef PXT_MEMLEAK_DEBUG
|
|
pxt::debugMemLeaks();
|
|
#endif
|
|
|
|
pxt::releaseFiber();
|
|
}
|
|
|
|
void start() {
|
|
exec_binary((unsigned *)functionsAndBytecode);
|
|
}
|
|
#endif
|
|
|
|
} // namespace pxt
|