delay resolving method call offsets until all methods have been compiled when creating a boot image

This commit is contained in:
Joel Dice 2008-11-27 13:59:40 -07:00
parent 035aa0ecd4
commit f698c24ea6
5 changed files with 176 additions and 122 deletions

@ -78,9 +78,14 @@ const int AnyRegister = -2;
class Promise {
public:
class Listener {
public:
virtual void* resolve(int64_t value) = 0;
};
virtual int64_t value() = 0;
virtual bool resolved() = 0;
virtual bool offer(void*) { return false; }
virtual Listener* listen(unsigned) { return 0; }
};
class ResolvedPromise: public Promise {
@ -98,9 +103,11 @@ class ResolvedPromise: public Promise {
int64_t value_;
};
class OfferPromise: public Promise {
class ListenPromise: public Promise {
public:
OfferPromise(System* s): s(s), offset(0) { }
ListenPromise(System* s, Allocator* allocator):
s(s), allocator(allocator), listener(0)
{ }
virtual int64_t value() {
abort(s);
@ -110,13 +117,13 @@ class OfferPromise: public Promise {
return false;
}
virtual bool offer(void* offset) {
this->offset = offset;
return true;
virtual Listener* listen(unsigned sizeInBytes) {
return listener = static_cast<Listener*>(allocator->allocate(sizeInBytes));
}
System* s;
void* offset;
Allocator* allocator;
Listener* listener;
};
class TraceHandler {

@ -43,10 +43,13 @@ makeCodeImage(Thread* t, BootImage* image, uint8_t* code, unsigned capacity)
unsigned size;
t->m->processor->compileThunks(t, image, code, &size, capacity);
object objectTable = makeHashMap(t, 0, 0);
PROTECT(t, objectTable);
Zone zone(t->m->system, t->m->heap, 64 * 1024);
object constants = 0;
PROTECT(t, constants);
object calls = 0;
PROTECT(t, calls);
for (Finder::Iterator it(t->m->finder); it.hasMore();) {
unsigned nameSize;
@ -61,15 +64,20 @@ makeCodeImage(Thread* t, BootImage* image, uint8_t* code, unsigned capacity)
object method = arrayBody(t, classMethodTable(t, c), i);
if (methodCode(t, method)) {
t->m->processor->compileMethod
(t, &zone, code, &size, capacity, objectTable, method);
(t, &zone, code, &size, capacity, &constants, &calls, method);
}
}
}
}
for (; calls; calls = tripleThird(t, calls)) {
static_cast<ListenPromise*>(pointerValue(t, tripleSecond(t, calls)))
->listener->resolve(methodCompiled(t, tripleFirst(t, calls)));
}
image->codeSize = size;
return objectTable;
return constants;
}
unsigned
@ -167,27 +175,21 @@ makeHeapImage(Thread* t, BootImage* image, uintptr_t* heap, uintptr_t* map,
}
void
updateCodeTable(Thread* t, object codeTable, uint8_t* code, uintptr_t* codeMap,
updateConstants(Thread* t, object constants, uint8_t* code, uintptr_t* codeMap,
HeapMap* heapTable)
{
intptr_t i = 0;
for (HashMapIterator it(t, codeTable); it.hasMore(); ++i) {
object mapEntry = it.next();
intptr_t target = heapTable->find(tripleFirst(t, mapEntry));
for (; constants; constants = tripleThird(t, constants)) {
intptr_t target = heapTable->find(tripleFirst(t, constants));
assert(t, target >= 0);
for (object fixup = tripleSecond(t, mapEntry);
fixup;
fixup = pairSecond(t, fixup))
{
OfferPromise* p = static_cast<OfferPromise*>
(pointerValue(t, pairFirst(t, fixup)));
assert(t, p->offset);
void* dst = static_cast<ListenPromise*>
(pointerValue(t, tripleSecond(t, constants)))->listener->resolve(target);
memcpy(p->offset, &target, BytesPerWord);
markBit(codeMap, reinterpret_cast<intptr_t>(p->offset)
- reinterpret_cast<intptr_t>(code));
}
assert(t, reinterpret_cast<intptr_t>(dst)
>= reinterpret_cast<intptr_t>(code));
markBit(codeMap, reinterpret_cast<intptr_t>(dst)
- reinterpret_cast<intptr_t>(code));
}
}
@ -208,7 +210,7 @@ writeBootImage(Thread* t, FILE* out)
(t->m->heap->allocate(codeMapSize(CodeCapacity)));
memset(codeMap, 0, codeMapSize(CodeCapacity));
object codeTable = makeCodeImage(t, &image, code, CodeCapacity);
object constants = makeCodeImage(t, &image, code, CodeCapacity);
const unsigned HeapCapacity = 32 * 1024 * 1024;
uintptr_t* heap = static_cast<uintptr_t*>
@ -220,7 +222,7 @@ writeBootImage(Thread* t, FILE* out)
HeapWalker* heapWalker = makeHeapImage
(t, &image, heap, heapMap, HeapCapacity);
updateCodeTable(t, codeTable, code, codeMap, heapWalker->map());
updateConstants(t, constants, code, codeMap, heapWalker->map());
heapWalker->dispose();

@ -131,7 +131,7 @@ compareIpToMethodBounds(Thread* t, intptr_t ip, object method)
if (ip < start) {
return -1;
} else if (ip < start + compiledSize(start))
} else if (ip < start + static_cast<intptr_t>(compiledSize(start)))
{
return 0;
} else {
@ -458,18 +458,20 @@ class BootContext {
MyProtector(Thread* t, BootContext* c): Protector(t), c(c) { }
virtual void visit(Heap::Visitor* v) {
v->visit(&(c->objectTable));
v->visit(&(c->constants));
v->visit(&(c->calls));
}
BootContext* c;
};
BootContext(Thread* t, object objectTable, Zone* zone):
protector(t, this), objectTable(objectTable), zone(zone)
BootContext(Thread* t, object constants, object calls, Zone* zone):
protector(t, this), constants(constants), calls(calls), zone(zone)
{ }
MyProtector protector;
object objectTable;
object constants;
object calls;
Zone* zone;
};
@ -638,24 +640,12 @@ class Frame {
if (context->bootContext) {
BootContext* bc = context->bootContext;
object node = hashMapFindNode
(t, bc->objectTable, o, objectHash, objectEqual);
PROTECT(t, node);
Promise* p = new (bc->zone->allocate(sizeof(OfferPromise)))
OfferPromise(t->m->system);
Promise* p = new (bc->zone->allocate(sizeof(ListenPromise)))
ListenPromise(t->m->system, bc->zone);
PROTECT(t, o);
object pointer = makePointer(t, p);
if (node) {
object fixup = makePair(t, pointer, tripleSecond(t, node));
vm::set(t, node, TripleSecond, fixup);
} else {
PROTECT(t, o);
object fixup = makePair(t, pointer, 0);
// todo: use a hash function that compares by value
hashMapInsert(t, bc->objectTable, o, fixup, objectHash);
}
bc->constants = makeTriple(t, o, pointer, bc->constants);
return c->promiseConstant(p);
} else {
@ -1745,26 +1735,37 @@ compileDirectInvoke(MyThread* t, Frame* frame, object target)
frame->trace(target, false),
rSize,
0);
} else if (methodCompiled(t, target) == defaultThunk(t)
or (frame->context->bootContext
and methodClass(t, target)
!= methodClass(t, frame->context->method)))
{
// todo: when creating a boot image, log intra-class calls for
// later fixup
result = c->call
(c->constant(defaultThunk(t)),
Compiler::Aligned,
frame->trace(target, false),
rSize,
0);
} else {
result = c->call
(c->constant(methodCompiled(t, target)),
0,
frame->trace(0, false),
rSize,
0);
BootContext* bc = frame->context->bootContext;
if (bc) {
Promise* p = new (bc->zone->allocate(sizeof(ListenPromise)))
ListenPromise(t->m->system, bc->zone);
PROTECT(t, target);
object pointer = makePointer(t, p);
bc->calls = makeTriple(t, target, pointer, bc->calls);
result = c->call
(c->promiseConstant(p),
0,
frame->trace(0, false),
rSize,
0);
} else if (methodCompiled(t, target) == defaultThunk(t)) {
result = c->call
(c->constant(defaultThunk(t)),
Compiler::Aligned,
frame->trace(target, false),
rSize,
0);
} else {
result = c->call
(c->constant(methodCompiled(t, target)),
0,
frame->trace(0, false),
rSize,
0);
}
}
}
@ -5053,15 +5054,17 @@ class MyProcessor: public Processor {
}
virtual void compileMethod(Thread* vmt, Zone* zone, uint8_t* code,
unsigned* offset, unsigned capacity, object table,
object method)
unsigned* offset, unsigned capacity,
object* constants, object* calls, object method)
{
MyThread* t = static_cast<MyThread*>(vmt);
FixedAllocator allocator(t, code + *offset, capacity);
BootContext bootContext(t, table, zone);
BootContext bootContext(t, *constants, *calls, zone);
compile(t, &allocator, &bootContext, method);
*constants = bootContext.constants;
*calls = bootContext.calls;
*offset += allocator.offset;
}
@ -5104,13 +5107,16 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p,
{
class ThunkContext {
public:
ThunkContext(MyThread* t): context(t), promise(t->m->system) { }
ThunkContext(MyThread* t, Zone* zone):
context(t), promise(t->m->system, zone)
{ }
Context context;
OfferPromise promise;
ListenPromise promise;
};
ThunkContext defaultContext(t);
Zone zone(t->m->system, t->m->heap, 1024);
ThunkContext defaultContext(t, &zone);
{ Assembler* a = defaultContext.context.assembler;
@ -5125,19 +5131,15 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p,
Assembler::Register result(a->returnLow());
a->apply(Jump, BytesPerWord, RegisterOperand, &result);
if (image) {
image->defaultThunk = static_cast<uint8_t*>
(defaultContext.promise.offset) - imageBase;
void* p = defaultContext.promise.listener->resolve
(reinterpret_cast<intptr_t>(voidPointer(compileMethod)));
memset(defaultContext.promise.offset, 0, BytesPerWord);
} else {
memcpy(defaultContext.promise.offset,
voidPointer(compileMethod),
BytesPerWord);
if (image) {
image->defaultThunk = static_cast<uint8_t*>(p) - imageBase;
}
}
ThunkContext nativeContext(t);
ThunkContext nativeContext(t, &zone);
{ Assembler* a = nativeContext.context.assembler;
@ -5150,19 +5152,15 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p,
a->apply(Return);
if (image) {
image->nativeThunk = static_cast<uint8_t*>
(nativeContext.promise.offset) - imageBase;
void* p = nativeContext.promise.listener->resolve
(reinterpret_cast<intptr_t>(voidPointer(invokeNative)));
memset(nativeContext.promise.offset, 0, BytesPerWord);
} else {
memcpy(nativeContext.promise.offset,
voidPointer(invokeNative),
BytesPerWord);
if (image) {
image->nativeThunk = static_cast<uint8_t*>(p) - imageBase;
}
}
ThunkContext aioobContext(t);
ThunkContext aioobContext(t, &zone);
{ Assembler* a = aioobContext.context.assembler;
@ -5172,19 +5170,15 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p,
Assembler::Constant proc(&(aioobContext.promise));
a->apply(LongCall, BytesPerWord, ConstantOperand, &proc);
if (image) {
image->aioobThunk = static_cast<uint8_t*>
(aioobContext.promise.offset) - imageBase;
void* p = aioobContext.promise.listener->resolve
(reinterpret_cast<intptr_t>(voidPointer(throwArrayIndexOutOfBounds)));
memset(aioobContext.promise.offset, 0, BytesPerWord);
} else {
memcpy(aioobContext.promise.offset,
voidPointer(throwArrayIndexOutOfBounds),
BytesPerWord);
if (image) {
image->aioobThunk = static_cast<uint8_t*>(p) - imageBase;
}
}
ThunkContext tableContext(t);
ThunkContext tableContext(t, &zone);
{ Assembler* a = tableContext.context.assembler;
@ -5225,12 +5219,11 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p,
#define THUNK(s) \
tableContext.context.assembler->writeTo(start); \
start += p->thunkSize; \
if (image) { \
image->s##Thunk = static_cast<uint8_t*> \
(tableContext.promise.offset) - imageBase; \
memset(tableContext.promise.offset, 0, BytesPerWord); \
} else { \
memcpy(tableContext.promise.offset, voidPointer(s), BytesPerWord); \
{ void* p = tableContext.promise.listener->resolve \
(reinterpret_cast<intptr_t>(voidPointer(s))); \
if (image) { \
image->s##Thunk = static_cast<uint8_t*>(p) - imageBase; \
} \
}
#include "thunks.cpp"

@ -121,7 +121,8 @@ class Processor {
virtual void
compileMethod(Thread* t, Zone* zone, uint8_t* code, unsigned* offset,
unsigned capacity, object table, object method) = 0;
unsigned capacity, object* constants, object* calls,
object method) = 0;
virtual void
visitRoots(BootImage* image, HeapWalker* w) = 0;

@ -135,6 +135,38 @@ class Task {
Task* next;
};
void
resolveOffset(System* s, uint8_t* instruction, unsigned instructionSize,
int64_t value)
{
intptr_t v = reinterpret_cast<uint8_t*>(value)
- instruction - instructionSize;
expect(s, isInt32(v));
int32_t v4 = v;
memcpy(instruction + instructionSize - 4, &v4, 4);
}
class OffsetListener: public Promise::Listener {
public:
OffsetListener(System* s, uint8_t* instruction,
unsigned instructionSize):
s(s),
instruction(instruction),
instructionSize(instructionSize)
{ }
virtual void* resolve(int64_t value) {
resolveOffset(s, instruction, instructionSize, value);
return 0;
}
System* s;
uint8_t* instruction;
unsigned instructionSize;
};
class OffsetTask: public Task {
public:
OffsetTask(Task* next, Promise* promise, unsigned instructionOffset,
@ -146,14 +178,14 @@ class OffsetTask: public Task {
{ }
virtual void run(Context* c) {
uint8_t* instruction = c->result + instructionOffset;
intptr_t v = reinterpret_cast<uint8_t*>(promise->value())
- instruction - instructionSize;
expect(c, isInt32(v));
int32_t v4 = v;
memcpy(instruction + instructionSize - 4, &v4, 4);
if (promise->resolved()) {
resolveOffset
(c->s, c->result + instructionOffset, instructionSize,
promise->value());
} else {
new (promise->listen(sizeof(OffsetListener)))
OffsetListener(c->s, c->result + instructionOffset, instructionSize);
}
}
Promise* promise;
@ -169,6 +201,25 @@ appendOffsetTask(Context* c, Promise* promise, int instructionOffset,
(c->tasks, promise, instructionOffset, instructionSize);
}
void
copyWord(void* dst, int64_t src)
{
intptr_t v = src;
memcpy(dst, &v, BytesPerWord);
}
class ImmediateListener: public Promise::Listener {
public:
ImmediateListener(void* dst): dst(dst) { }
virtual void* resolve(int64_t value) {
copyWord(dst, value);
return 0;
}
void* dst;
};
class ImmediateTask: public Task {
public:
ImmediateTask(Task* next, Promise* promise, unsigned offset):
@ -179,10 +230,10 @@ class ImmediateTask: public Task {
virtual void run(Context* c) {
if (promise->resolved()) {
intptr_t v = promise->value();
memcpy(c->result + offset, &v, BytesPerWord);
} else if (not promise->offer(c->result + offset)) {
abort(c);
copyWord(c->result + offset, promise->value());
} else {
new (promise->listen(sizeof(ImmediateListener)))
ImmediateListener(c->result + offset);
}
}