mirror of
https://github.com/corda/corda.git
synced 2025-04-18 16:18:12 +00:00
delay resolving method call offsets until all methods have been compiled when creating a boot image
This commit is contained in:
parent
035aa0ecd4
commit
f698c24ea6
@ -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();
|
||||
|
||||
|
151
src/compile.cpp
151
src/compile.cpp
@ -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;
|
||||
|
75
src/x86.cpp
75
src/x86.cpp
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user