mirror of
https://github.com/corda/corda.git
synced 2025-01-06 05:04:20 +00:00
handle subroutines properly when generating frame maps (initial sketch)
This commit is contained in:
parent
f239424930
commit
b308354a3a
683
src/compile.cpp
683
src/compile.cpp
@ -493,6 +493,70 @@ class PoolElement: public Promise {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class Context;
|
class Context;
|
||||||
|
class SubroutineCall;
|
||||||
|
|
||||||
|
class Subroutine {
|
||||||
|
public:
|
||||||
|
Subroutine(unsigned ip, unsigned logIndex, Subroutine* next):
|
||||||
|
next(next),
|
||||||
|
calls(0),
|
||||||
|
handle(0),
|
||||||
|
ip(ip),
|
||||||
|
logIndex(logIndex),
|
||||||
|
stackIndex(0),
|
||||||
|
callCount(0),
|
||||||
|
tableIndex(0),
|
||||||
|
visited(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Subroutine* next;
|
||||||
|
SubroutineCall* calls;
|
||||||
|
Compiler::Subroutine* handle;
|
||||||
|
unsigned ip;
|
||||||
|
unsigned logIndex;
|
||||||
|
unsigned stackIndex;
|
||||||
|
unsigned callCount;
|
||||||
|
unsigned tableIndex;
|
||||||
|
bool visited;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubroutineCall {
|
||||||
|
public:
|
||||||
|
SubroutineCall(Subroutine* subroutine, Promise* returnAddress):
|
||||||
|
subroutine(subroutine),
|
||||||
|
returnAddress(returnAddress),
|
||||||
|
next(subroutine->calls)
|
||||||
|
{
|
||||||
|
subroutine->calls = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Subroutine* subroutine;
|
||||||
|
Promise* returnAddress;
|
||||||
|
SubroutineCall* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubroutinePath {
|
||||||
|
public:
|
||||||
|
SubroutinePath(SubroutineCall* call, SubroutinePath* next):
|
||||||
|
call(call),
|
||||||
|
next(next)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
SubroutineCall* call;
|
||||||
|
SubroutinePath* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubroutineTrace {
|
||||||
|
public:
|
||||||
|
SubroutineTrace(SubroutinePath* path, SubroutineTrace* next):
|
||||||
|
path(path),
|
||||||
|
next(next)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
SubroutinePath* path;
|
||||||
|
SubroutineTrace* next;
|
||||||
|
uintptr_t map[0];
|
||||||
|
};
|
||||||
|
|
||||||
class TraceElement: public TraceHandler {
|
class TraceElement: public TraceHandler {
|
||||||
public:
|
public:
|
||||||
@ -504,6 +568,7 @@ class TraceElement: public TraceHandler {
|
|||||||
context(context),
|
context(context),
|
||||||
address(0),
|
address(0),
|
||||||
next(next),
|
next(next),
|
||||||
|
subroutineTrace(0),
|
||||||
target(target),
|
target(target),
|
||||||
argumentIndex(0),
|
argumentIndex(0),
|
||||||
flags(flags)
|
flags(flags)
|
||||||
@ -519,6 +584,7 @@ class TraceElement: public TraceHandler {
|
|||||||
Context* context;
|
Context* context;
|
||||||
Promise* address;
|
Promise* address;
|
||||||
TraceElement* next;
|
TraceElement* next;
|
||||||
|
SubroutineTrace* subroutineTrace;
|
||||||
object target;
|
object target;
|
||||||
unsigned argumentIndex;
|
unsigned argumentIndex;
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
@ -548,7 +614,9 @@ enum Event {
|
|||||||
IpEvent,
|
IpEvent,
|
||||||
MarkEvent,
|
MarkEvent,
|
||||||
ClearEvent,
|
ClearEvent,
|
||||||
TraceEvent
|
TraceEvent,
|
||||||
|
PushSubroutineEvent,
|
||||||
|
PopSubroutineEvent
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
@ -685,11 +753,13 @@ class Context {
|
|||||||
method(method),
|
method(method),
|
||||||
bootContext(bootContext),
|
bootContext(bootContext),
|
||||||
objectPool(0),
|
objectPool(0),
|
||||||
objectPoolCount(0),
|
subroutines(0),
|
||||||
traceLog(0),
|
traceLog(0),
|
||||||
traceLogCount(0),
|
|
||||||
visitTable(makeVisitTable(t, &zone, method)),
|
visitTable(makeVisitTable(t, &zone, method)),
|
||||||
rootTable(makeRootTable(t, &zone, method)),
|
rootTable(makeRootTable(t, &zone, method)),
|
||||||
|
objectPoolCount(0),
|
||||||
|
traceLogCount(0),
|
||||||
|
dirtyRoots(false),
|
||||||
eventLog(t->m->system, t->m->heap, 1024),
|
eventLog(t->m->system, t->m->heap, 1024),
|
||||||
protector(this)
|
protector(this)
|
||||||
{ }
|
{ }
|
||||||
@ -703,11 +773,13 @@ class Context {
|
|||||||
method(0),
|
method(0),
|
||||||
bootContext(0),
|
bootContext(0),
|
||||||
objectPool(0),
|
objectPool(0),
|
||||||
objectPoolCount(0),
|
subroutines(0),
|
||||||
traceLog(0),
|
traceLog(0),
|
||||||
traceLogCount(0),
|
|
||||||
visitTable(0),
|
visitTable(0),
|
||||||
rootTable(0),
|
rootTable(0),
|
||||||
|
objectPoolCount(0),
|
||||||
|
traceLogCount(0),
|
||||||
|
dirtyRoots(false),
|
||||||
eventLog(t->m->system, t->m->heap, 0),
|
eventLog(t->m->system, t->m->heap, 0),
|
||||||
protector(this)
|
protector(this)
|
||||||
{ }
|
{ }
|
||||||
@ -725,11 +797,12 @@ class Context {
|
|||||||
object method;
|
object method;
|
||||||
BootContext* bootContext;
|
BootContext* bootContext;
|
||||||
PoolElement* objectPool;
|
PoolElement* objectPool;
|
||||||
unsigned objectPoolCount;
|
Subroutine* subroutines;
|
||||||
TraceElement* traceLog;
|
TraceElement* traceLog;
|
||||||
unsigned traceLogCount;
|
|
||||||
uint16_t* visitTable;
|
uint16_t* visitTable;
|
||||||
uintptr_t* rootTable;
|
uintptr_t* rootTable;
|
||||||
|
unsigned objectPoolCount;
|
||||||
|
unsigned traceLogCount;
|
||||||
bool dirtyRoots;
|
bool dirtyRoots;
|
||||||
Vector eventLog;
|
Vector eventLog;
|
||||||
MyProtector protector;
|
MyProtector protector;
|
||||||
@ -1308,11 +1381,63 @@ class Frame {
|
|||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned startSubroutine(unsigned ip, Promise* returnAddress) {
|
||||||
|
pushAddress(addressOperand(returnAddress));
|
||||||
|
|
||||||
|
Subroutine* subroutine = 0;
|
||||||
|
for (Subroutine* s = context->subroutines; s; s = s->next) {
|
||||||
|
if (s->ip == ip) {
|
||||||
|
subroutine = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subroutine == 0) {
|
||||||
|
context->subroutines = subroutine = new
|
||||||
|
(context->zone.allocate(sizeof(Subroutine)))
|
||||||
|
Subroutine(ip, context->eventLog.length(), context->subroutines);
|
||||||
|
}
|
||||||
|
|
||||||
|
subroutine->handle = c->startSubroutine();
|
||||||
|
this->subroutine = subroutine;
|
||||||
|
|
||||||
|
SubroutineCall* call = new
|
||||||
|
(context->zone.allocate(sizeof(SubroutineCall)))
|
||||||
|
SubroutineCall(subroutine, returnAddress);
|
||||||
|
|
||||||
|
context->eventLog.append(PushSubroutineEvent);
|
||||||
|
context->eventLog.appendAddress(call);
|
||||||
|
|
||||||
|
unsigned nextIndexIndex = context->eventLog.length();
|
||||||
|
context->eventLog.append2(0);
|
||||||
|
|
||||||
|
c->saveLocals();
|
||||||
|
|
||||||
|
return nextIndexIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void returnFromSubroutine(unsigned returnAddressLocal) {
|
||||||
|
c->endSubroutine(subroutine->handle);
|
||||||
|
subroutine->stackIndex = localOffsetFromStack
|
||||||
|
(t, translateLocalIndex(context, 1, returnAddressLocal),
|
||||||
|
context->method);
|
||||||
|
}
|
||||||
|
|
||||||
|
void endSubroutine(unsigned nextIndexIndex) {
|
||||||
|
context->eventLog.set2(nextIndexIndex, context->eventLog.length());
|
||||||
|
|
||||||
|
c->cleanLocals();
|
||||||
|
|
||||||
|
poppedInt();
|
||||||
|
|
||||||
|
context->eventLog.append(PopSubroutineEvent);
|
||||||
|
}
|
||||||
|
|
||||||
Context* context;
|
Context* context;
|
||||||
MyThread* t;
|
MyThread* t;
|
||||||
Compiler* c;
|
Compiler* c;
|
||||||
Compiler::Subroutine* subroutine;
|
Subroutine* subroutine;
|
||||||
uint8_t* stackMap;
|
uint8_t* stackMap;
|
||||||
unsigned ip;
|
unsigned ip;
|
||||||
unsigned sp;
|
unsigned sp;
|
||||||
@ -2296,67 +2421,6 @@ handleExit(MyThread* t, Frame* frame)
|
|||||||
(t, frame, getThunk(t, releaseMonitorForObjectThunk));
|
(t, frame, getThunk(t, releaseMonitorForObjectThunk));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
exceptionIndex(MyThread* t, object code, unsigned jsrIp, unsigned dstIp)
|
|
||||||
{
|
|
||||||
object table = codeExceptionHandlerTable(t, code);
|
|
||||||
unsigned length = exceptionHandlerTableLength(t, table);
|
|
||||||
for (unsigned i = 0; i < length; ++i) {
|
|
||||||
ExceptionHandler* eh = exceptionHandlerTableBody(t, table, i);
|
|
||||||
if (exceptionHandlerCatchType(eh) == 0) {
|
|
||||||
unsigned ip = exceptionHandlerIp(eh);
|
|
||||||
unsigned index;
|
|
||||||
switch (codeBody(t, code, ip++)) {
|
|
||||||
case astore:
|
|
||||||
index = codeBody(t, code, ip++);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case astore_0:
|
|
||||||
index = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case astore_1:
|
|
||||||
index = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case astore_2:
|
|
||||||
index = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case astore_3:
|
|
||||||
index = 3;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: abort(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ip == jsrIp) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (codeBody(t, code, ip++)) {
|
|
||||||
case jsr: {
|
|
||||||
uint32_t offset = codeReadInt16(t, code, ip);
|
|
||||||
if ((ip - 3) + offset == dstIp) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case jsr_w: {
|
|
||||||
uint32_t offset = codeReadInt32(t, code, ip);
|
|
||||||
if ((ip - 5) + offset == dstIp) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abort(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
inTryBlock(MyThread* t, object code, unsigned ip)
|
inTryBlock(MyThread* t, object code, unsigned ip)
|
||||||
{
|
{
|
||||||
@ -3540,30 +3604,14 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
|||||||
|
|
||||||
assert(t, newIp < codeLength(t, code));
|
assert(t, newIp < codeLength(t, code));
|
||||||
|
|
||||||
int index = exceptionIndex(t, code, thisIp, newIp);
|
unsigned start = frame->startSubroutine(newIp, c->machineIp(ip));
|
||||||
if (index >= 0) {
|
|
||||||
// store a null pointer at the same index the exception would
|
|
||||||
// be stored in the finally block so we can safely treat that
|
|
||||||
// location as a GC root. Of course, this assumes there
|
|
||||||
// wasn't already a live value there, which is something we
|
|
||||||
// should verify once we have complete data flow information
|
|
||||||
// (todo).
|
|
||||||
storeLocal(context, 1, c->constant(0), index);
|
|
||||||
frame->storedObject(translateLocalIndex(context, 1, index));
|
|
||||||
}
|
|
||||||
|
|
||||||
frame->pushAddress(frame->addressOperand(c->machineIp(ip)));
|
|
||||||
|
|
||||||
c->jmp(frame->machineIp(newIp));
|
c->jmp(frame->machineIp(newIp));
|
||||||
|
|
||||||
frame->subroutine = c->startSubroutine();
|
|
||||||
|
|
||||||
compile(t, frame, newIp);
|
compile(t, frame, newIp);
|
||||||
if (UNLIKELY(t->exception)) return;
|
if (UNLIKELY(t->exception)) return;
|
||||||
|
|
||||||
frame->poppedInt();
|
frame->endSubroutine(start);
|
||||||
|
|
||||||
c->restoreFromSubroutine(frame->subroutine);
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case l2d: {
|
case l2d: {
|
||||||
@ -4047,10 +4095,11 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ret:
|
case ret: {
|
||||||
c->jmp(loadLocal(context, 1, codeBody(t, code, ip)));
|
unsigned index = codeBody(t, code, ip);
|
||||||
c->endSubroutine(frame->subroutine);
|
c->jmp(loadLocal(context, 1, index));
|
||||||
return;
|
frame->returnFromSubroutine(index);
|
||||||
|
} return;
|
||||||
|
|
||||||
case return_:
|
case return_:
|
||||||
if (needsReturnBarrier(t, context->method)) {
|
if (needsReturnBarrier(t, context->method)) {
|
||||||
@ -4169,10 +4218,11 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
|||||||
frame->storeLong(codeReadInt16(t, code, ip));
|
frame->storeLong(codeReadInt16(t, code, ip));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ret:
|
case ret: {
|
||||||
c->jmp(loadLocal(context, 1, codeReadInt16(t, code, ip)));
|
unsigned index = codeReadInt16(t, code, ip);
|
||||||
c->endSubroutine(frame->subroutine);
|
c->jmp(loadLocal(context, 1, index));
|
||||||
return;
|
frame->returnFromSubroutine(index);
|
||||||
|
} return;
|
||||||
|
|
||||||
default: abort(t);
|
default: abort(t);
|
||||||
}
|
}
|
||||||
@ -4294,7 +4344,7 @@ printSet(uintptr_t m, unsigned limit)
|
|||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots,
|
calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots,
|
||||||
unsigned eventIndex)
|
unsigned eventIndex, SubroutinePath* subroutinePath = 0)
|
||||||
{
|
{
|
||||||
// for each instruction with more than one predecessor, and for each
|
// for each instruction with more than one predecessor, and for each
|
||||||
// stack position, determine if there exists a path to that
|
// stack position, determine if there exists a path to that
|
||||||
@ -4395,11 +4445,36 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots,
|
|||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(te->map, roots, mapSize * BytesPerWord);
|
if (subroutinePath == 0) {
|
||||||
|
memcpy(te->map, roots, mapSize * BytesPerWord);
|
||||||
|
} else {
|
||||||
|
te->subroutineTrace = new
|
||||||
|
(context->zone.allocate
|
||||||
|
(sizeof(SubroutineTrace) + (mapSize * BytesPerWord)))
|
||||||
|
SubroutineTrace(subroutinePath, te->subroutineTrace);
|
||||||
|
|
||||||
|
memcpy(te->subroutineTrace->map, roots, mapSize * BytesPerWord);
|
||||||
|
}
|
||||||
|
|
||||||
eventIndex += BytesPerWord;
|
eventIndex += BytesPerWord;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case PushSubroutineEvent: {
|
||||||
|
SubroutineCall* call;
|
||||||
|
context->eventLog.get(eventIndex, &call, BytesPerWord);
|
||||||
|
eventIndex += BytesPerWord;
|
||||||
|
unsigned nextIndex = context->eventLog.get2(eventIndex);
|
||||||
|
eventIndex = nextIndex;
|
||||||
|
|
||||||
|
calculateFrameMaps
|
||||||
|
(t, context, roots, call->subroutine->logIndex, new
|
||||||
|
(context->zone.allocate(sizeof(SubroutinePath)))
|
||||||
|
SubroutinePath(call, subroutinePath));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PopSubroutineEvent:
|
||||||
|
return static_cast<unsigned>(-1);
|
||||||
|
|
||||||
default: abort(t);
|
default: abort(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4422,7 +4497,7 @@ compareTraceElementPointers(const void* va, const void* vb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
frameObjectMapSize(MyThread* t, object method, object map)
|
simpleFrameMapTableSize(MyThread* t, object method, object map)
|
||||||
{
|
{
|
||||||
int size = frameMapSizeInBits(t, method);
|
int size = frameMapSizeInBits(t, method);
|
||||||
return ceiling(intArrayLength(t, map) * size, 32 + size);
|
return ceiling(intArrayLength(t, map) * size, 32 + size);
|
||||||
@ -4450,21 +4525,230 @@ finish(MyThread* t, Allocator* allocator, Assembler* a, const char* name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
setBit(MyThread* t, object map, unsigned count, unsigned size, unsigned i,
|
setBit(int32_t* dst, unsigned index)
|
||||||
unsigned j)
|
|
||||||
{
|
{
|
||||||
unsigned index = ((i * size) + j);
|
dst[index / 32] |= static_cast<int32_t>(1) << (index % 32);
|
||||||
intArrayBody(t, map, count + (index / 32))
|
|
||||||
|= static_cast<int32_t>(1) << (index % 32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
clearBit(MyThread* t, object map, unsigned count, unsigned size, unsigned i,
|
clearBit(int32_t* dst, unsigned index)
|
||||||
unsigned j)
|
|
||||||
{
|
{
|
||||||
unsigned index = ((i * size) + j);
|
dst[index / 32] &= ~(static_cast<int32_t>(1) << (index % 32));
|
||||||
intArrayBody(t, map, count + (index / 32))
|
}
|
||||||
&= ~(static_cast<int32_t>(1) << (index % 32));
|
|
||||||
|
void
|
||||||
|
print(SubroutinePath* path)
|
||||||
|
{
|
||||||
|
if (path) {
|
||||||
|
fprintf(stderr, " (");
|
||||||
|
while (true) {
|
||||||
|
fprintf(stderr, "%"LLD"", path->call->returnAddress->value());
|
||||||
|
path = path->next;
|
||||||
|
if (path) {
|
||||||
|
fprintf(stderr, ", ");
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
copyFrameMap(int32_t* dst, uintptr_t* src, unsigned mapSizeInBits,
|
||||||
|
unsigned offset, TraceElement* p,
|
||||||
|
SubroutinePath* subroutinePath)
|
||||||
|
{
|
||||||
|
if (DebugFrameMaps) {
|
||||||
|
fprintf(stderr, " orig roots at ip %p: ", reinterpret_cast<void*>
|
||||||
|
(p->address->value()));
|
||||||
|
printSet(src[0], ceiling(mapSizeInBits, BitsPerWord));
|
||||||
|
print(subroutinePath);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
fprintf(stderr, "final roots at ip %p: ", reinterpret_cast<void*>
|
||||||
|
(p->address->value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned j = 0; j < p->argumentIndex; ++j) {
|
||||||
|
if (getBit(src, j)) {
|
||||||
|
if (DebugFrameMaps) {
|
||||||
|
fprintf(stderr, "1");
|
||||||
|
}
|
||||||
|
setBit(dst, offset + j);
|
||||||
|
} else {
|
||||||
|
if (DebugFrameMaps) {
|
||||||
|
fprintf(stderr, "_");
|
||||||
|
}
|
||||||
|
clearBit(dst, offset + j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DebugFrameMaps) {
|
||||||
|
print(subroutinePath);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FrameMapTableHeader {
|
||||||
|
public:
|
||||||
|
FrameMapTableHeader(unsigned indexCount):
|
||||||
|
indexCount(indexCount)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
unsigned indexCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrameMapTableIndexElement {
|
||||||
|
public:
|
||||||
|
FrameMapTableIndexElement(int offset, unsigned base, unsigned path):
|
||||||
|
offset(offset),
|
||||||
|
base(base),
|
||||||
|
path(path)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
int offset;
|
||||||
|
unsigned base;
|
||||||
|
unsigned path;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrameMapTablePath {
|
||||||
|
public:
|
||||||
|
FrameMapTablePath(unsigned stackIndex, unsigned elementCount, unsigned next):
|
||||||
|
stackIndex(stackIndex),
|
||||||
|
elementCount(elementCount),
|
||||||
|
next(next)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
unsigned stackIndex;
|
||||||
|
unsigned elementCount;
|
||||||
|
unsigned next;
|
||||||
|
int32_t elements[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
object
|
||||||
|
makeGeneralFrameMapTable(MyThread* t, Context* context, uint8_t* start,
|
||||||
|
TraceElement** elements, unsigned pathFootprint,
|
||||||
|
unsigned mapCount)
|
||||||
|
{
|
||||||
|
unsigned mapSize = frameMapSizeInBits(t, context->method);
|
||||||
|
unsigned indexOffset = sizeof(FrameMapTableHeader);
|
||||||
|
unsigned mapsOffset = indexOffset
|
||||||
|
+ (context->traceLogCount * sizeof(FrameMapTableIndexElement));
|
||||||
|
unsigned pathsOffset = mapsOffset + (ceiling(mapCount * mapSize, 32) * 4);
|
||||||
|
|
||||||
|
object table = makeByteArray(t, pathsOffset + pathFootprint);
|
||||||
|
|
||||||
|
int8_t* body = &byteArrayBody(t, table, 0);
|
||||||
|
new (body) FrameMapTableHeader(context->traceLogCount);
|
||||||
|
|
||||||
|
unsigned nextTableIndex = pathsOffset;
|
||||||
|
unsigned nextMapIndex = 0;
|
||||||
|
for (unsigned i = 0; i < context->traceLogCount; ++i) {
|
||||||
|
TraceElement* p = elements[i];
|
||||||
|
unsigned mapBase = nextMapIndex;
|
||||||
|
|
||||||
|
unsigned pathIndex;
|
||||||
|
if (p->subroutineTrace) {
|
||||||
|
FrameMapTablePath* previous = 0;
|
||||||
|
Subroutine* subroutine = p->subroutineTrace->path->call->subroutine;
|
||||||
|
for (Subroutine* s = subroutine; s; s = s->next) {
|
||||||
|
if (s->tableIndex != 0) {
|
||||||
|
unsigned pathObjectSize = sizeof(FrameMapTablePath)
|
||||||
|
+ (sizeof(int32_t) * s->callCount);
|
||||||
|
|
||||||
|
assert(t, nextTableIndex + pathObjectSize
|
||||||
|
<= byteArrayLength(t, table));
|
||||||
|
|
||||||
|
s->tableIndex = nextTableIndex;
|
||||||
|
|
||||||
|
nextTableIndex += pathObjectSize;
|
||||||
|
|
||||||
|
FrameMapTablePath* current = new (body + s->tableIndex)
|
||||||
|
FrameMapTablePath
|
||||||
|
(s->stackIndex, s->callCount, s->next ? s->next->tableIndex : 0);
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
for (SubroutineCall* c = subroutine->calls; c; c = c->next) {
|
||||||
|
assert(t, i < s->callCount);
|
||||||
|
|
||||||
|
current->elements[i]
|
||||||
|
= static_cast<intptr_t>(c->returnAddress->value())
|
||||||
|
- reinterpret_cast<intptr_t>(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previous) {
|
||||||
|
previous->next = s->tableIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = current;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pathIndex = subroutine->tableIndex;
|
||||||
|
|
||||||
|
for (SubroutineTrace* trace = p->subroutineTrace;
|
||||||
|
trace; trace = trace->next)
|
||||||
|
{
|
||||||
|
assert(t, ceiling(nextMapIndex + mapSize, 32) * 4 <= pathsOffset);
|
||||||
|
|
||||||
|
copyFrameMap(reinterpret_cast<int32_t*>(body + mapsOffset),
|
||||||
|
trace->map, mapSize, nextMapIndex, p, trace->path);
|
||||||
|
|
||||||
|
nextMapIndex += mapSize;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pathIndex = 0;
|
||||||
|
|
||||||
|
assert(t, ceiling(nextMapIndex + mapSize, 32) * 4 <= pathsOffset);
|
||||||
|
|
||||||
|
copyFrameMap(reinterpret_cast<int32_t*>(body + mapsOffset), p->map,
|
||||||
|
mapSize, nextMapIndex, p, 0);
|
||||||
|
|
||||||
|
nextMapIndex += mapSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned elementIndex = indexOffset
|
||||||
|
+ (i * sizeof(FrameMapTableIndexElement));
|
||||||
|
|
||||||
|
assert(t, elementIndex + sizeof(FrameMapTableIndexElement) <= mapsOffset);
|
||||||
|
|
||||||
|
new (body + elementIndex) FrameMapTableIndexElement
|
||||||
|
(static_cast<intptr_t>(p->address->value())
|
||||||
|
- reinterpret_cast<intptr_t>(start), mapBase, pathIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
object
|
||||||
|
makeSimpleFrameMapTable(MyThread* t, Context* context, uint8_t* start,
|
||||||
|
TraceElement** elements)
|
||||||
|
{
|
||||||
|
unsigned mapSize = frameMapSizeInBits(t, context->method);
|
||||||
|
object table = makeIntArray
|
||||||
|
(t, context->traceLogCount
|
||||||
|
+ ceiling(context->traceLogCount * mapSize, 32));
|
||||||
|
|
||||||
|
assert(t, intArrayLength(t, table) == context->traceLogCount
|
||||||
|
+ simpleFrameMapTableSize(t, context->method, table));
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < context->traceLogCount; ++i) {
|
||||||
|
TraceElement* p = elements[i];
|
||||||
|
|
||||||
|
intArrayBody(t, table, i) = static_cast<intptr_t>(p->address->value())
|
||||||
|
- reinterpret_cast<intptr_t>(start);
|
||||||
|
|
||||||
|
assert(t, context->traceLogCount + ceiling((i + 1) * mapSize, 32)
|
||||||
|
<= intArrayLength(t, table));
|
||||||
|
|
||||||
|
copyFrameMap(&intArrayBody(t, table, context->traceLogCount), p->map,
|
||||||
|
mapSize, i * mapSize, p, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t*
|
uint8_t*
|
||||||
@ -4535,9 +4819,27 @@ finish(MyThread* t, Allocator* allocator, Context* context)
|
|||||||
if (context->traceLogCount) {
|
if (context->traceLogCount) {
|
||||||
TraceElement* elements[context->traceLogCount];
|
TraceElement* elements[context->traceLogCount];
|
||||||
unsigned index = 0;
|
unsigned index = 0;
|
||||||
|
unsigned pathFootprint = 0;
|
||||||
|
unsigned mapCount = 0;
|
||||||
for (TraceElement* p = context->traceLog; p; p = p->next) {
|
for (TraceElement* p = context->traceLog; p; p = p->next) {
|
||||||
assert(t, index < context->traceLogCount);
|
assert(t, index < context->traceLogCount);
|
||||||
|
|
||||||
|
SubroutineTrace* trace = p->subroutineTrace;
|
||||||
|
unsigned myMapCount = 1;
|
||||||
|
if (trace) {
|
||||||
|
for (SubroutinePath* sp = trace->path; sp; sp = sp->next) {
|
||||||
|
Subroutine* subroutine = sp->call->subroutine;
|
||||||
|
unsigned callCount = subroutine->callCount;
|
||||||
|
myMapCount *= callCount;
|
||||||
|
if (not subroutine->visited) {
|
||||||
|
subroutine->visited = true;
|
||||||
|
pathFootprint += sizeof(FrameMapTablePath)
|
||||||
|
+ (sizeof(int32_t) * callCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapCount += myMapCount;
|
||||||
|
|
||||||
elements[index++] = p;
|
elements[index++] = p;
|
||||||
|
|
||||||
if (p->target) {
|
if (p->target) {
|
||||||
@ -4550,47 +4852,12 @@ finish(MyThread* t, Allocator* allocator, Context* context)
|
|||||||
qsort(elements, context->traceLogCount, sizeof(TraceElement*),
|
qsort(elements, context->traceLogCount, sizeof(TraceElement*),
|
||||||
compareTraceElementPointers);
|
compareTraceElementPointers);
|
||||||
|
|
||||||
unsigned size = frameMapSizeInBits(t, context->method);
|
object map;
|
||||||
object map = makeIntArray
|
if (pathFootprint) {
|
||||||
(t, context->traceLogCount
|
map = makeGeneralFrameMapTable
|
||||||
+ ceiling(context->traceLogCount * size, 32));
|
(t, context, start, elements, pathFootprint, mapCount);
|
||||||
|
} else {
|
||||||
assert(t, intArrayLength(t, map) == context->traceLogCount
|
map = makeSimpleFrameMapTable(t, context, start, elements);
|
||||||
+ frameObjectMapSize(t, context->method, map));
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < context->traceLogCount; ++i) {
|
|
||||||
TraceElement* p = elements[i];
|
|
||||||
|
|
||||||
intArrayBody(t, map, i) = static_cast<intptr_t>(p->address->value())
|
|
||||||
- reinterpret_cast<intptr_t>(start);
|
|
||||||
|
|
||||||
if (DebugFrameMaps) {
|
|
||||||
fprintf(stderr, " orig roots at ip %p: ", reinterpret_cast<void*>
|
|
||||||
(p->address->value()));
|
|
||||||
printSet(p->map[0], frameMapSizeInWords(t, context->method));
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
|
|
||||||
fprintf(stderr, "final roots at ip %p: ", reinterpret_cast<void*>
|
|
||||||
(p->address->value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned j = 0; j < p->argumentIndex; ++j) {
|
|
||||||
if (getBit(p->map, j)) {
|
|
||||||
if (DebugFrameMaps) {
|
|
||||||
fprintf(stderr, "1");
|
|
||||||
}
|
|
||||||
setBit(t, map, context->traceLogCount, size, i, j);
|
|
||||||
} else {
|
|
||||||
if (DebugFrameMaps) {
|
|
||||||
fprintf(stderr, "_");
|
|
||||||
}
|
|
||||||
clearBit(t, map, context->traceLogCount, size, i, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DebugFrameMaps) {
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set(t, methodCode(t, context->method), CodePool, map);
|
set(t, methodCode(t, context->method), CodePool, map);
|
||||||
@ -5134,21 +5401,24 @@ invokeNative(MyThread* t)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned
|
void
|
||||||
frameMapIndex(MyThread* t, object method, int32_t offset)
|
findFrameMapInSimpleTable(MyThread* t, object method, object table,
|
||||||
|
int32_t offset, int32_t** map, unsigned* start)
|
||||||
{
|
{
|
||||||
object map = codePool(t, methodCode(t, method));
|
unsigned tableSize = simpleFrameMapTableSize(t, method, table);
|
||||||
unsigned mapSize = frameObjectMapSize(t, method, map);
|
unsigned indexSize = intArrayLength(t, table) - tableSize;
|
||||||
unsigned indexSize = intArrayLength(t, map) - mapSize;
|
|
||||||
|
*map = &intArrayBody(t, table, indexSize);
|
||||||
|
|
||||||
unsigned bottom = 0;
|
unsigned bottom = 0;
|
||||||
unsigned top = indexSize;
|
unsigned top = indexSize;
|
||||||
for (unsigned span = top - bottom; span; span = top - bottom) {
|
for (unsigned span = top - bottom; span; span = top - bottom) {
|
||||||
unsigned middle = bottom + (span / 2);
|
unsigned middle = bottom + (span / 2);
|
||||||
int32_t v = intArrayBody(t, map, middle);
|
int32_t v = intArrayBody(t, table, middle);
|
||||||
|
|
||||||
if (offset == v) {
|
if (offset == v) {
|
||||||
return (indexSize * 32) + (frameMapSizeInBits(t, method) * middle);
|
*start = frameMapSizeInBits(t, method) * middle;
|
||||||
|
return;
|
||||||
} else if (offset < v) {
|
} else if (offset < v) {
|
||||||
top = middle;
|
top = middle;
|
||||||
} else {
|
} else {
|
||||||
@ -5159,6 +5429,75 @@ frameMapIndex(MyThread* t, object method, int32_t offset)
|
|||||||
abort(t);
|
abort(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
findFrameMap(MyThread* t, void* stack, object method, object table,
|
||||||
|
unsigned pathIndex)
|
||||||
|
{
|
||||||
|
if (pathIndex) {
|
||||||
|
FrameMapTablePath* path = reinterpret_cast<FrameMapTablePath*>
|
||||||
|
(&byteArrayBody(t, table, pathIndex));
|
||||||
|
|
||||||
|
void* address = static_cast<void**>(stack)[path->stackIndex];
|
||||||
|
uint8_t* base = reinterpret_cast<uint8_t*>(methodAddress(t, method));
|
||||||
|
for (unsigned i = 0; i < path->elementCount; ++i) {
|
||||||
|
if (address == base + path->elements[i]) {
|
||||||
|
return i + (path->elementCount * findFrameMap
|
||||||
|
(t, stack, method, table, path->next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abort(t);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
findFrameMapInGeneralTable(MyThread* t, void* stack, object method,
|
||||||
|
object table, int32_t offset, int32_t** map,
|
||||||
|
unsigned* start)
|
||||||
|
{
|
||||||
|
FrameMapTableHeader* header = reinterpret_cast<FrameMapTableHeader*>
|
||||||
|
(&byteArrayBody(t, table, 0));
|
||||||
|
|
||||||
|
FrameMapTableIndexElement* index
|
||||||
|
= reinterpret_cast<FrameMapTableIndexElement*>
|
||||||
|
(&byteArrayBody(t, table, sizeof(FrameMapTableHeader)));
|
||||||
|
|
||||||
|
*map = reinterpret_cast<int32_t*>(index + header->indexCount);
|
||||||
|
|
||||||
|
unsigned bottom = 0;
|
||||||
|
unsigned top = header->indexCount;
|
||||||
|
for (unsigned span = top - bottom; span; span = top - bottom) {
|
||||||
|
unsigned middle = bottom + (span / 2);
|
||||||
|
FrameMapTableIndexElement* v = index + middle;
|
||||||
|
|
||||||
|
if (offset == v->offset) {
|
||||||
|
*start = v->base + findFrameMap(t, stack, method, table, v->path);
|
||||||
|
} else if (offset < v->offset) {
|
||||||
|
top = middle;
|
||||||
|
} else {
|
||||||
|
bottom = middle + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abort(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
findFrameMap(MyThread* t, void* stack, object method, int32_t offset,
|
||||||
|
int32_t** map, unsigned* start)
|
||||||
|
{
|
||||||
|
object table = codePool(t, methodCode(t, method));
|
||||||
|
if (objectClass(t, table)
|
||||||
|
== arrayBody(t, t->m->types, Machine::IntArrayType))
|
||||||
|
{
|
||||||
|
findFrameMapInSimpleTable(t, method, table, offset, map, start);
|
||||||
|
} else {
|
||||||
|
findFrameMapInGeneralTable(t, stack, method, table, offset, map, start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visitStackAndLocals(MyThread* t, Heap::Visitor* v, void* frame, object method,
|
visitStackAndLocals(MyThread* t, Heap::Visitor* v, void* frame, object method,
|
||||||
void* ip)
|
void* ip)
|
||||||
@ -5166,18 +5505,17 @@ visitStackAndLocals(MyThread* t, Heap::Visitor* v, void* frame, object method,
|
|||||||
unsigned count = frameMapSizeInBits(t, method);
|
unsigned count = frameMapSizeInBits(t, method);
|
||||||
|
|
||||||
if (count) {
|
if (count) {
|
||||||
object map = codePool(t, methodCode(t, method));
|
|
||||||
int index = frameMapIndex
|
|
||||||
(t, method, difference
|
|
||||||
(ip, reinterpret_cast<void*>(methodAddress(t, method))));
|
|
||||||
|
|
||||||
void* stack = stackForFrame(t, frame, method);
|
void* stack = stackForFrame(t, frame, method);
|
||||||
|
|
||||||
|
int32_t* map;
|
||||||
|
unsigned offset;
|
||||||
|
findFrameMap
|
||||||
|
(t, stack, method, difference
|
||||||
|
(ip, reinterpret_cast<void*>(methodAddress(t, method))), &map, &offset);
|
||||||
|
|
||||||
for (unsigned i = 0; i < count; ++i) {
|
for (unsigned i = 0; i < count; ++i) {
|
||||||
int j = index + i;
|
int j = offset + i;
|
||||||
if ((intArrayBody(t, map, j / 32)
|
if (map[j / 32] & (static_cast<int32_t>(1) << (j % 32))) {
|
||||||
& (static_cast<int32_t>(1) << (j % 32))))
|
|
||||||
{
|
|
||||||
v->visit(localObject(t, stack, method, i));
|
v->visit(localObject(t, stack, method, i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5288,17 +5626,16 @@ walkContinuationBody(MyThread* t, Heap::Walker* w, object c, int start)
|
|||||||
count -= start - first;
|
count -= start - first;
|
||||||
}
|
}
|
||||||
|
|
||||||
object map = codePool(t, methodCode(t, method));
|
int32_t* map;
|
||||||
int index = frameMapIndex
|
unsigned offset;
|
||||||
(t, method, difference
|
findFrameMap
|
||||||
|
(t, reinterpret_cast<uintptr_t*>(c) + stack, method, difference
|
||||||
(continuationAddress(t, c),
|
(continuationAddress(t, c),
|
||||||
reinterpret_cast<void*>(methodAddress(t, method))));
|
reinterpret_cast<void*>(methodAddress(t, method))), &map, &offset);
|
||||||
|
|
||||||
for (int i = count - 1; i >= 0; --i) {
|
for (int i = count - 1; i >= 0; --i) {
|
||||||
int j = index + i;
|
int j = offset + i;
|
||||||
if ((intArrayBody(t, map, j / 32)
|
if (map[j / 32] & (static_cast<int32_t>(1) << (j % 32))) {
|
||||||
& (static_cast<int32_t>(1) << (j % 32))))
|
|
||||||
{
|
|
||||||
if (not w->visit(stack + localOffsetFromStack(t, i, method))) {
|
if (not w->visit(stack + localOffsetFromStack(t, i, method))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -4016,6 +4016,31 @@ appendSaveLocals(Context* c)
|
|||||||
SaveLocalsEvent(c));
|
SaveLocalsEvent(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CleanLocalsEvent: public Event {
|
||||||
|
public:
|
||||||
|
CleanLocalsEvent(Context* c):
|
||||||
|
Event(c)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual const char* name() {
|
||||||
|
return "CleanLocalsEvent";
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void compile(Context* c) {
|
||||||
|
for (FrameIterator it(c, 0, c->locals); it.hasMore();) {
|
||||||
|
FrameIterator::Element e = it.next(c);
|
||||||
|
clean(c, e.value, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
appendCleanLocals(Context* c)
|
||||||
|
{
|
||||||
|
append(c, new (c->zone->allocate(sizeof(CleanLocalsEvent)))
|
||||||
|
CleanLocalsEvent(c));
|
||||||
|
}
|
||||||
|
|
||||||
class DummyEvent: public Event {
|
class DummyEvent: public Event {
|
||||||
public:
|
public:
|
||||||
DummyEvent(Context* c):
|
DummyEvent(Context* c):
|
||||||
@ -4927,6 +4952,8 @@ class MyCompiler: public Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void endSubroutine(Subroutine* subroutine) {
|
virtual void endSubroutine(Subroutine* subroutine) {
|
||||||
|
saveLocals();
|
||||||
|
|
||||||
static_cast<MySubroutine*>(subroutine)->forkState = ::saveState(&c);
|
static_cast<MySubroutine*>(subroutine)->forkState = ::saveState(&c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5038,6 +5065,13 @@ class MyCompiler: public Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.logicalIp = logicalIp;
|
c.logicalIp = logicalIp;
|
||||||
|
|
||||||
|
for (unsigned li = 0; li < c.localFootprint; ++li) {
|
||||||
|
Local* local = c.locals + li;
|
||||||
|
if (local->value == 0) {
|
||||||
|
initLocal(1, li);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Promise* machineIp(unsigned logicalIp) {
|
virtual Promise* machineIp(unsigned logicalIp) {
|
||||||
@ -5333,6 +5367,10 @@ class MyCompiler: public Compiler {
|
|||||||
appendSaveLocals(&c);
|
appendSaveLocals(&c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void cleanLocals() {
|
||||||
|
appendCleanLocals(&c);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void checkBounds(Operand* object, unsigned lengthOffset,
|
virtual void checkBounds(Operand* object, unsigned lengthOffset,
|
||||||
Operand* index, intptr_t handler)
|
Operand* index, intptr_t handler)
|
||||||
{
|
{
|
||||||
|
@ -43,7 +43,6 @@ class Compiler {
|
|||||||
|
|
||||||
virtual Subroutine* startSubroutine() = 0;
|
virtual Subroutine* startSubroutine() = 0;
|
||||||
virtual void endSubroutine(Subroutine* subroutine) = 0;
|
virtual void endSubroutine(Subroutine* subroutine) = 0;
|
||||||
virtual void restoreFromSubroutine(Subroutine* subroutine) = 0;
|
|
||||||
|
|
||||||
virtual void init(unsigned logicalCodeSize, unsigned parameterFootprint,
|
virtual void init(unsigned logicalCodeSize, unsigned parameterFootprint,
|
||||||
unsigned localFootprint, unsigned alignedFrameSize) = 0;
|
unsigned localFootprint, unsigned alignedFrameSize) = 0;
|
||||||
@ -96,6 +95,7 @@ class Compiler {
|
|||||||
unsigned index) = 0;
|
unsigned index) = 0;
|
||||||
virtual Operand* loadLocal(unsigned footprint, unsigned index) = 0;
|
virtual Operand* loadLocal(unsigned footprint, unsigned index) = 0;
|
||||||
virtual void saveLocals() = 0;
|
virtual void saveLocals() = 0;
|
||||||
|
virtual void cleanLocals() = 0;
|
||||||
|
|
||||||
virtual void checkBounds(Operand* object, unsigned lengthOffset,
|
virtual void checkBounds(Operand* object, unsigned lengthOffset,
|
||||||
Operand* index, intptr_t handler) = 0;
|
Operand* index, intptr_t handler) = 0;
|
||||||
|
@ -110,6 +110,11 @@ class Vector {
|
|||||||
append(&v, BytesPerWord);
|
append(&v, BytesPerWord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set2(unsigned offset, uint16_t v) {
|
||||||
|
assert(s, offset <= position - 2);
|
||||||
|
memcpy(data + offset, &v, 2);
|
||||||
|
}
|
||||||
|
|
||||||
unsigned get(unsigned offset) {
|
unsigned get(unsigned offset) {
|
||||||
uint8_t v; get(offset, &v, 1);
|
uint8_t v; get(offset, &v, 1);
|
||||||
return v;
|
return v;
|
||||||
|
@ -63,9 +63,9 @@ public class Subroutine {
|
|||||||
test(false, true);
|
test(false, true);
|
||||||
test(true, false);
|
test(true, false);
|
||||||
|
|
||||||
test2(1);
|
String.valueOf(test2(1));
|
||||||
test2(2);
|
String.valueOf(test2(2));
|
||||||
test2(3);
|
String.valueOf(test2(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DummyException extends RuntimeException { }
|
private static class DummyException extends RuntimeException { }
|
||||||
|
Loading…
Reference in New Issue
Block a user