mirror of
https://github.com/corda/corda.git
synced 2025-01-03 19:54:13 +00:00
improve efficiency of array bounds checks and generate a thunk table for native functions called indirectly
This commit is contained in:
parent
51256f3cc2
commit
65830a76c5
@ -159,11 +159,11 @@ class Assembler {
|
||||
virtual int argumentRegister(unsigned index) = 0;
|
||||
|
||||
virtual void plan(UnaryOperation op, unsigned size, uint8_t* typeMask,
|
||||
uint64_t* registerMask, uintptr_t* procedure) = 0;
|
||||
uint64_t* registerMask, bool* thunk) = 0;
|
||||
|
||||
virtual void plan(BinaryOperation op, unsigned size, uint8_t* aTypeMask,
|
||||
uint64_t* aRegisterMask, uint8_t* bTypeMask,
|
||||
uint64_t* bRegisterMask, uintptr_t* procedure) = 0;
|
||||
uint64_t* bRegisterMask, bool* thunk) = 0;
|
||||
|
||||
virtual void apply(Operation op) = 0;
|
||||
|
||||
|
703
src/compile.cpp
703
src/compile.cpp
File diff suppressed because it is too large
Load Diff
180
src/compiler.cpp
180
src/compiler.cpp
@ -184,11 +184,12 @@ class Value: public Compiler::Operand {
|
||||
|
||||
class Context {
|
||||
public:
|
||||
Context(System* system, Assembler* assembler, Zone* zone, void* indirection):
|
||||
Context(System* system, Assembler* assembler, Zone* zone,
|
||||
Compiler::Client* client):
|
||||
system(system),
|
||||
assembler(assembler),
|
||||
zone(zone),
|
||||
indirection(indirection),
|
||||
client(client),
|
||||
logicalIp(-1),
|
||||
state(new (zone->allocate(sizeof(State))) State(0, 0)),
|
||||
logicalCode(0),
|
||||
@ -220,7 +221,7 @@ class Context {
|
||||
System* system;
|
||||
Assembler* assembler;
|
||||
Zone* zone;
|
||||
void* indirection;
|
||||
Compiler::Client* client;
|
||||
int logicalIp;
|
||||
State* state;
|
||||
LogicalInstruction* logicalCode;
|
||||
@ -884,6 +885,13 @@ anyRegisterSite(Context* c)
|
||||
return virtualSite(c, 0, 1 << RegisterOperand, ~static_cast<uint64_t>(0));
|
||||
}
|
||||
|
||||
VirtualSite*
|
||||
registerOrConstantSite(Context* c)
|
||||
{
|
||||
return virtualSite(c, 0, (1 << RegisterOperand) | (1 << ConstantOperand),
|
||||
~static_cast<uint64_t>(0));
|
||||
}
|
||||
|
||||
Site*
|
||||
targetOrRegister(Context* c, Value* v)
|
||||
{
|
||||
@ -1366,14 +1374,8 @@ class CallEvent: public Event {
|
||||
s = s->next;
|
||||
}
|
||||
|
||||
if (flags & Compiler::Indirect) {
|
||||
int r = c->assembler->returnLow();
|
||||
addRead(c, address, BytesPerWord, fixedRegisterSite(c, r));
|
||||
mask &= ~(1 << r);
|
||||
} else {
|
||||
addRead(c, address, BytesPerWord, virtualSite
|
||||
(c, 0, ~0, (static_cast<uint64_t>(mask) << 32) | mask));
|
||||
}
|
||||
addRead(c, address, BytesPerWord, virtualSite
|
||||
(c, 0, ~0, (static_cast<uint64_t>(mask) << 32) | mask));
|
||||
|
||||
for (Stack* s = stack; s; s = s->next) {
|
||||
s->pushEvent->active = true;
|
||||
@ -1392,12 +1394,7 @@ class CallEvent: public Event {
|
||||
pushNow(c, stack);
|
||||
|
||||
UnaryOperation type = ((flags & Compiler::Aligned) ? AlignedCall : Call);
|
||||
if (flags & Compiler::Indirect) {
|
||||
apply(c, type, BytesPerWord,
|
||||
constantSite(c, reinterpret_cast<intptr_t>(c->indirection)));
|
||||
} else {
|
||||
apply(c, type, BytesPerWord, address->source);
|
||||
}
|
||||
apply(c, type, BytesPerWord, address->source);
|
||||
|
||||
if (traceHandler) {
|
||||
traceHandler->handleTrace
|
||||
@ -1575,14 +1572,14 @@ appendMove(Context* c, BinaryOperation type, unsigned size, Value* src,
|
||||
|
||||
VirtualSite* srcTarget = virtualSite(c, dst);
|
||||
VirtualSite* dstTarget = virtualSite(c);
|
||||
uintptr_t procedure;
|
||||
bool thunk;
|
||||
|
||||
c->assembler->plan(type, size,
|
||||
&(srcTarget->typeMask), &(srcTarget->registerMask),
|
||||
&(dstTarget->typeMask), &(dstTarget->registerMask),
|
||||
&procedure);
|
||||
&thunk);
|
||||
|
||||
assert(c, procedure == 0); // todo
|
||||
assert(c, not thunk); // todo
|
||||
|
||||
new (c->zone->allocate(sizeof(MoveEvent)))
|
||||
MoveEvent(c, type, size, src, dst, srcTarget, dstTarget);
|
||||
@ -1619,14 +1616,14 @@ appendCompare(Context* c, unsigned size, Value* first, Value* second)
|
||||
{
|
||||
VirtualSite* firstTarget = virtualSite(c);
|
||||
VirtualSite* secondTarget = virtualSite(c);
|
||||
uintptr_t procedure;
|
||||
bool thunk;
|
||||
|
||||
c->assembler->plan(Compare, size,
|
||||
&(firstTarget->typeMask), &(firstTarget->registerMask),
|
||||
&(secondTarget->typeMask), &(secondTarget->registerMask),
|
||||
&procedure);
|
||||
&thunk);
|
||||
|
||||
assert(c, procedure == 0); // todo
|
||||
assert(c, not thunk); // todo
|
||||
|
||||
if (DebugAppend) {
|
||||
fprintf(stderr, "appendCompare\n");
|
||||
@ -1723,14 +1720,14 @@ appendCombine(Context* c, BinaryOperation type, unsigned size, Value* first,
|
||||
{
|
||||
VirtualSite* firstTarget = virtualSite(c);
|
||||
VirtualSite* secondTarget = virtualSite(c, result);
|
||||
uintptr_t procedure;
|
||||
bool thunk;
|
||||
|
||||
c->assembler->plan(type, size,
|
||||
&(firstTarget->typeMask), &(firstTarget->registerMask),
|
||||
&(secondTarget->typeMask), &(secondTarget->registerMask),
|
||||
&procedure);
|
||||
&thunk);
|
||||
|
||||
if (procedure) {
|
||||
if (thunk) {
|
||||
secondTarget->value = 0;
|
||||
|
||||
Stack* oldStack = c->state->stack;
|
||||
@ -1741,8 +1738,8 @@ appendCombine(Context* c, BinaryOperation type, unsigned size, Value* first,
|
||||
Stack* argumentStack = c->state->stack;
|
||||
c->state->stack = oldStack;
|
||||
|
||||
appendCall(c, value(c, constantSite(c, procedure)), Compiler::Indirect,
|
||||
0, result, size, argumentStack, 2);
|
||||
appendCall(c, value(c, constantSite(c, c->client->getThunk(type, size))),
|
||||
0, 0, result, size, argumentStack, 2);
|
||||
} else {
|
||||
if (DebugAppend) {
|
||||
fprintf(stderr, "appendCombine\n");
|
||||
@ -1798,12 +1795,12 @@ appendTranslate(Context* c, UnaryOperation type, unsigned size, Value* value,
|
||||
}
|
||||
|
||||
VirtualSite* target = virtualSite(c, result);
|
||||
uintptr_t procedure;
|
||||
bool thunk;
|
||||
|
||||
c->assembler->plan
|
||||
(type, size, &(target->typeMask), &(target->registerMask), &procedure);
|
||||
(type, size, &(target->typeMask), &(target->registerMask), &thunk);
|
||||
|
||||
assert(c, procedure == 0); // todo
|
||||
assert(c, not thunk); // todo
|
||||
|
||||
target->typeMask &= ~(1 << MemoryOperand);
|
||||
|
||||
@ -2178,6 +2175,105 @@ appendLocal(Context* c, unsigned size, Local* local)
|
||||
new (c->zone->allocate(sizeof(LocalEvent))) LocalEvent(c, size, local);
|
||||
}
|
||||
|
||||
CodePromise*
|
||||
codePromise(Context* c, Event* e)
|
||||
{
|
||||
return e->promises = new (c->zone->allocate(sizeof(CodePromise)))
|
||||
CodePromise(c, e->promises);
|
||||
}
|
||||
|
||||
CodePromise*
|
||||
codePromise(Context* c, int offset)
|
||||
{
|
||||
return new (c->zone->allocate(sizeof(CodePromise))) CodePromise(c, offset);
|
||||
}
|
||||
|
||||
class BoundsCheckEvent: public Event {
|
||||
public:
|
||||
BoundsCheckEvent(Context* c, Value* object, unsigned lengthOffset,
|
||||
Value* index, intptr_t handler):
|
||||
Event(c), object(object), lengthOffset(lengthOffset), index(index),
|
||||
handler(handler)
|
||||
{
|
||||
addRead(c, object, BytesPerWord, anyRegisterSite(c));
|
||||
addRead(c, index, BytesPerWord, registerOrConstantSite(c));
|
||||
}
|
||||
|
||||
virtual void compile(Context* c) {
|
||||
if (DebugCompile) {
|
||||
fprintf(stderr, "BoundsCheckEvent.compile\n");
|
||||
}
|
||||
|
||||
Assembler* a = c->assembler;
|
||||
|
||||
ConstantSite* constant = 0;
|
||||
for (Site* s = index->sites; s; s = s->next) {
|
||||
if (s->type(c) == ConstantOperand) {
|
||||
constant = static_cast<ConstantSite*>(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CodePromise* nextPromise = codePromise(c, -1);
|
||||
CodePromise* outOfBoundsPromise;
|
||||
|
||||
if (constant) {
|
||||
expect(c, constant->value.value->value() >= 0);
|
||||
} else {
|
||||
outOfBoundsPromise = codePromise(c, -1);
|
||||
|
||||
apply(c, Compare, 4, constantSite(c, resolved(c, 0)), index->source);
|
||||
|
||||
Assembler::Constant outOfBoundsConstant(outOfBoundsPromise);
|
||||
a->apply
|
||||
(JumpIfLess, BytesPerWord, ConstantOperand, &outOfBoundsConstant);
|
||||
}
|
||||
|
||||
assert(c, object->source->type(c) == RegisterOperand);
|
||||
int base = static_cast<RegisterSite*>(object->source)->register_.low;
|
||||
|
||||
Site* length = memorySite(c, base, lengthOffset);
|
||||
length->acquire(c, 0, 0, 0);
|
||||
|
||||
apply(c, Compare, BytesPerWord, index->source, length);
|
||||
|
||||
length->release(c);
|
||||
|
||||
Assembler::Constant nextConstant(nextPromise);
|
||||
a->apply(JumpIfGreater, BytesPerWord, ConstantOperand, &nextConstant);
|
||||
|
||||
if (constant == 0) {
|
||||
outOfBoundsPromise->offset = a->length();
|
||||
}
|
||||
|
||||
ResolvedPromise handlerPromise(handler);
|
||||
Assembler::Constant handlerConstant(&handlerPromise);
|
||||
a->apply(Call, BytesPerWord, ConstantOperand, &handlerConstant);
|
||||
|
||||
nextPromise->offset = a->length();
|
||||
|
||||
nextRead(c, object);
|
||||
nextRead(c, index);
|
||||
}
|
||||
|
||||
Value* object;
|
||||
unsigned lengthOffset;
|
||||
Value* index;
|
||||
intptr_t handler;
|
||||
};
|
||||
|
||||
void
|
||||
appendBoundsCheck(Context* c, Value* object, unsigned lengthOffset,
|
||||
Value* index, intptr_t handler)
|
||||
{
|
||||
if (DebugAppend) {
|
||||
fprintf(stderr, "appendLocal\n");
|
||||
}
|
||||
|
||||
new (c->zone->allocate(sizeof(BoundsCheckEvent))) BoundsCheckEvent
|
||||
(c, object, lengthOffset, index, handler);
|
||||
}
|
||||
|
||||
Site*
|
||||
readSource(Context* c, Stack* stack, Read* r)
|
||||
{
|
||||
@ -2420,8 +2516,9 @@ class Client: public Assembler::Client {
|
||||
|
||||
class MyCompiler: public Compiler {
|
||||
public:
|
||||
MyCompiler(System* s, Assembler* assembler, Zone* zone, void* indirection):
|
||||
c(s, assembler, zone, indirection), client(&c)
|
||||
MyCompiler(System* s, Assembler* assembler, Zone* zone,
|
||||
Compiler::Client* compilerClient):
|
||||
c(s, assembler, zone, compilerClient), client(&c)
|
||||
{
|
||||
assembler->setClient(&client);
|
||||
}
|
||||
@ -2569,9 +2666,7 @@ class MyCompiler: public Compiler {
|
||||
}
|
||||
|
||||
Promise* machineIp() {
|
||||
Event* e = c.logicalCode[c.logicalIp].lastEvent;
|
||||
return e->promises = new (c.zone->allocate(sizeof(CodePromise)))
|
||||
CodePromise(&c, e->promises);
|
||||
return codePromise(&c, c.logicalCode[c.logicalIp].lastEvent);
|
||||
}
|
||||
|
||||
virtual void mark(Operand* label) {
|
||||
@ -2710,6 +2805,13 @@ class MyCompiler: public Compiler {
|
||||
return v;
|
||||
}
|
||||
|
||||
virtual void checkBounds(Operand* object, unsigned lengthOffset,
|
||||
Operand* index, intptr_t handler)
|
||||
{
|
||||
appendBoundsCheck(&c, static_cast<Value*>(object),
|
||||
lengthOffset, static_cast<Value*>(index), handler);
|
||||
}
|
||||
|
||||
virtual void store(unsigned size, Operand* src, Operand* dst) {
|
||||
appendMove(&c, Move, size, static_cast<Value*>(src),
|
||||
static_cast<Value*>(dst));
|
||||
@ -2876,7 +2978,7 @@ class MyCompiler: public Compiler {
|
||||
}
|
||||
|
||||
Context c;
|
||||
Client client;
|
||||
::Client client;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -2885,10 +2987,10 @@ namespace vm {
|
||||
|
||||
Compiler*
|
||||
makeCompiler(System* system, Assembler* assembler, Zone* zone,
|
||||
void* indirection)
|
||||
Compiler::Client* client)
|
||||
{
|
||||
return new (zone->allocate(sizeof(MyCompiler)))
|
||||
MyCompiler(system, assembler, zone, indirection);
|
||||
MyCompiler(system, assembler, zone, client);
|
||||
}
|
||||
|
||||
} // namespace vm
|
||||
|
@ -19,9 +19,16 @@ namespace vm {
|
||||
|
||||
class Compiler {
|
||||
public:
|
||||
class Client {
|
||||
public:
|
||||
virtual ~Client() { }
|
||||
|
||||
virtual intptr_t getThunk(UnaryOperation op, unsigned size) = 0;
|
||||
virtual intptr_t getThunk(BinaryOperation op, unsigned size) = 0;
|
||||
};
|
||||
|
||||
static const unsigned Aligned = 1 << 0;
|
||||
static const unsigned NoReturn = 1 << 1;
|
||||
static const unsigned Indirect = 1 << 2;
|
||||
|
||||
class Operand { };
|
||||
|
||||
@ -80,6 +87,9 @@ class Compiler {
|
||||
virtual void storeLocal(unsigned size, Operand* src, unsigned index) = 0;
|
||||
virtual Operand* loadLocal(unsigned size, unsigned index) = 0;
|
||||
|
||||
virtual void checkBounds(Operand* object, unsigned lengthOffset,
|
||||
Operand* index, intptr_t handler) = 0;
|
||||
|
||||
virtual void store(unsigned size, Operand* src, Operand* dst) = 0;
|
||||
virtual Operand* load(unsigned size, Operand* src) = 0;
|
||||
virtual Operand* loadz(unsigned size, Operand* src) = 0;
|
||||
@ -114,7 +124,7 @@ class Compiler {
|
||||
|
||||
Compiler*
|
||||
makeCompiler(System* system, Assembler* assembler, Zone* zone,
|
||||
void* indirection);
|
||||
Compiler::Client* client);
|
||||
|
||||
} // namespace vm
|
||||
|
||||
|
44
src/thunks.cpp
Normal file
44
src/thunks.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
THUNK(tryInitClass)
|
||||
THUNK(findInterfaceMethodFromInstance)
|
||||
THUNK(compareDoublesG)
|
||||
THUNK(compareDoublesL)
|
||||
THUNK(compareFloatsG)
|
||||
THUNK(compareFloatsL)
|
||||
THUNK(addDouble)
|
||||
THUNK(subtractDouble)
|
||||
THUNK(multiplyDouble)
|
||||
THUNK(divideDouble)
|
||||
THUNK(moduloDouble)
|
||||
THUNK(negateDouble)
|
||||
THUNK(doubleToFloat)
|
||||
THUNK(doubleToInt)
|
||||
THUNK(doubleToLong)
|
||||
THUNK(addFloat)
|
||||
THUNK(subtractFloat)
|
||||
THUNK(multiplyFloat)
|
||||
THUNK(divideFloat)
|
||||
THUNK(moduloFloat)
|
||||
THUNK(negateFloat)
|
||||
THUNK(floatToDouble)
|
||||
THUNK(floatToInt)
|
||||
THUNK(floatToLong)
|
||||
THUNK(intToDouble)
|
||||
THUNK(intToFloat)
|
||||
THUNK(longToDouble)
|
||||
THUNK(longToFloat)
|
||||
THUNK(divideLong)
|
||||
THUNK(moduloLong)
|
||||
THUNK(makeBlankObjectArray)
|
||||
THUNK(makeBlankArray)
|
||||
THUNK(lookUpAddress)
|
||||
THUNK(setMaybeNull)
|
||||
THUNK(acquireMonitorForObject)
|
||||
THUNK(releaseMonitorForObject)
|
||||
THUNK(makeMultidimensionalArray)
|
||||
THUNK(throw_)
|
||||
THUNK(checkCast)
|
||||
THUNK(instanceOf)
|
||||
THUNK(makeNewWeakReference)
|
||||
THUNK(makeNew)
|
||||
THUNK(set)
|
||||
THUNK(gcIfNecessary)
|
39
src/x86.cpp
39
src/x86.cpp
@ -35,18 +35,6 @@ enum {
|
||||
r15 = 15,
|
||||
};
|
||||
|
||||
int64_t FORCE_ALIGN
|
||||
divideLong(int64_t a, int64_t b)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
|
||||
int64_t FORCE_ALIGN
|
||||
moduloLong(int64_t a, int64_t b)
|
||||
{
|
||||
return a % b;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isInt8(intptr_t v)
|
||||
{
|
||||
@ -364,16 +352,23 @@ jumpR(Context* c, unsigned size UNUSED, Assembler::Register* a)
|
||||
{
|
||||
assert(c, size == BytesPerWord);
|
||||
|
||||
if (a->low & 8) rex(c, 0x40, a->low);
|
||||
c->code.append(0xff);
|
||||
c->code.append(0xe0 | a->low);
|
||||
c->code.append(0xe0 | (a->low & 7));
|
||||
}
|
||||
|
||||
void
|
||||
jumpC(Context* c, unsigned size UNUSED, Assembler::Constant* a)
|
||||
jumpC(Context* c, unsigned size, Assembler::Constant* a)
|
||||
{
|
||||
assert(c, size == BytesPerWord);
|
||||
|
||||
unconditional(c, 0xe9, a);
|
||||
if (BytesPerWord == 8) {
|
||||
Assembler::Register r(r10);
|
||||
moveCR(c, size, a, &r);
|
||||
jumpR(c, size, &r);
|
||||
} else {
|
||||
unconditional(c, 0xe9, a);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -660,7 +655,7 @@ moveCM(Context* c, unsigned size, Assembler::Constant* a,
|
||||
break;
|
||||
|
||||
case 4:
|
||||
encode(c, 0xc7, 0, b, true);
|
||||
encode(c, 0xc7, 0, b, false);
|
||||
c->code.append4(a->value->value());
|
||||
break;
|
||||
|
||||
@ -1940,7 +1935,7 @@ class MyAssembler: public Assembler {
|
||||
}
|
||||
|
||||
virtual void plan(UnaryOperation op, unsigned size, uint8_t* typeMask,
|
||||
uint64_t* registerMask, uintptr_t* procedure)
|
||||
uint64_t* registerMask, bool* thunk)
|
||||
{
|
||||
if (op == Negate and BytesPerWord == 4 and size == 8) {
|
||||
*typeMask = 1 << RegisterOperand;
|
||||
@ -1950,12 +1945,12 @@ class MyAssembler: public Assembler {
|
||||
*typeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
||||
*registerMask = ~static_cast<uint64_t>(0);
|
||||
}
|
||||
*procedure = 0;
|
||||
*thunk = false;
|
||||
}
|
||||
|
||||
virtual void plan(BinaryOperation op, unsigned size, uint8_t* aTypeMask,
|
||||
uint64_t* aRegisterMask, uint8_t* bTypeMask,
|
||||
uint64_t* bRegisterMask, uintptr_t* procedure)
|
||||
uint64_t* bRegisterMask, bool* thunk)
|
||||
{
|
||||
*aTypeMask = ~0;
|
||||
*aRegisterMask = ~static_cast<uint64_t>(0);
|
||||
@ -1963,7 +1958,7 @@ class MyAssembler: public Assembler {
|
||||
*bTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
||||
*bRegisterMask = ~static_cast<uint64_t>(0);
|
||||
|
||||
*procedure = 0;
|
||||
*thunk = false;
|
||||
|
||||
switch (op) {
|
||||
case Compare:
|
||||
@ -2004,7 +1999,7 @@ class MyAssembler: public Assembler {
|
||||
case Divide:
|
||||
if (BytesPerWord == 4 and size == 8) {
|
||||
*bTypeMask = ~0;
|
||||
*procedure = reinterpret_cast<uintptr_t>(divideLong);
|
||||
*thunk = true;
|
||||
} else {
|
||||
*aRegisterMask = ~((1 << rax) | (1 << rdx));
|
||||
*bRegisterMask = 1 << rax;
|
||||
@ -2014,7 +2009,7 @@ class MyAssembler: public Assembler {
|
||||
case Remainder:
|
||||
if (BytesPerWord == 4 and size == 8) {
|
||||
*bTypeMask = ~0;
|
||||
*procedure = reinterpret_cast<uintptr_t>(moduloLong);
|
||||
*thunk = true;
|
||||
} else {
|
||||
*aRegisterMask = ~((1 << rax) | (1 << rdx));
|
||||
*bRegisterMask = 1 << rax;
|
||||
|
Loading…
Reference in New Issue
Block a user