blob: 8c20c7baf197e923570a958c9a178ab52b823fbf [file] [log] [blame]
#include <stdint.h>
#include <setjmp.h>
#include "llvm/IR/DataLayout.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "vmkit/allocator.h"
#include "vmkit/gc.h"
#include "j3/j3object.h"
#include "j3/j3method.h"
#include "j3/j3class.h"
#include "j3/j3classloader.h"
#include "j3/j3.h"
#include "j3/j3constants.h"
#include "j3/j3thread.h"
#include "j3/j3monitor.h"
#include "j3/j3field.h"
using namespace j3;
/*
* --- J3TypeChecker ---
*/
void J3TypeChecker::dump() {
fprintf(stderr, " offset: %u\n", offset);
for(uint32_t i=0; i<cacheOffset; i++) {
if(display[i])
fprintf(stderr, " display[%u]: %s\n", i, display[i]->type()->name()->cStr());
}
for(uint32_t i=0; i<nbSecondaryTypes; i++) {
fprintf(stderr, " secondary[%u]: %s\n", i, secondaryTypes[i]->type()->name()->cStr());
}
if(display[cacheOffset])
fprintf(stderr, " cache: %s\n", display[cacheOffset]->type()->name()->cStr());
}
/*
* --- J3VirtualTable ---
*/
J3VirtualTable* J3VirtualTable::create(J3Layout* layout) {
return new(layout->loader()->allocator(), 0) J3VirtualTable(layout, layout, 0, 0, 0);
}
J3VirtualTable* J3VirtualTable::create(J3Class* cl) {
J3Class* super = cl->super();
uint32_t base = cl == super ? 0 : super->vt()->nbVirtualMethods();
uint32_t n = base;
super->resolve();
J3Method* pm[cl->nbMethods()];
for(uint32_t i=0; i<cl->nbMethods(); i++) {
J3Method* meth = cl->methods()[i];
J3Method* parent = cl == super ? 0 : super->findMethod(0, meth->name(), meth->signature(), 0);
if(parent) {
pm[i] = parent;
meth->setIndex(parent->index());
} else {
pm[i] = meth;
meth->setIndex(n);
n++;
}
}
/* virtual table */
uint32_t isConcrete = !J3Cst::isInterface(cl->access());
n = isConcrete ? n : 0;
J3VirtualTable* res = new(cl->loader()->allocator(), n)
J3VirtualTable(cl, cl->super(), (J3Type**)cl->interfaces(), cl->nbInterfaces(), J3Cst::isInterface(cl->access()) ? 1 : 0);
res->_nbVirtualMethods = n;
if(isConcrete) {
if(super != cl) /* super->vt() is not yet allocated for Object */
memcpy(res->_virtualMethods, super->vt()->_virtualMethods, sizeof(void*)*super->vt()->nbVirtualMethods());
if(!J3Cst::isAbstract(cl->access())) {
void* interfaceTrampoline = J3Thread::get()->vm()->interfaceTrampoline;
for(uint32_t i=0; i<nbInterfaceMethodTable; i++)
res->_interfaceMethodTable[i] = interfaceTrampoline;
}
for(uint32_t i=0; i<cl->nbMethods(); i++) {
res->_virtualMethods[pm[i]->index()] = pm[i]->functionPointerOrVirtualTrampoline();
}
}
return res;
}
J3VirtualTable* J3VirtualTable::create(J3ArrayClass* cl) {
J3* vm = J3Thread::get()->vm();
J3Class* objClass = vm->objectClass;
J3Type* super = cl->component();
J3Type* base = super;
uint32_t dim = 1;
J3Type** secondaries = 0;
uint32_t nbSecondaries = 0;
bool isSecondary = 0;
// for objects for primitives
// Integer[][]
// Number[][] + ifces[][] int[][][]
// Object[][] Object[][]
// Object[] int[] Object[]
// Object + Serializable/Cloneable
while(base->isArrayClass()) {
base = base->asArrayClass()->component();
dim++;
}
if(base->isPrimitive()) {
super = objClass->getArray(dim-1);
nbSecondaries = vm->nbArrayInterfaces;
secondaries = (J3Type**)cl->loader()->allocator()->allocate(nbSecondaries*sizeof(J3Type*));
for(uint32_t i=0; i<nbSecondaries; i++) {
secondaries[i] = vm->arrayInterfaces[i];
if(dim > 1)
secondaries[i] = secondaries[i]->getArray(dim-1);
}
} else if(base == objClass) {
nbSecondaries = vm->nbArrayInterfaces;
secondaries = (J3Type**)alloca(nbSecondaries*sizeof(J3Type*));
for(uint32_t i=0; i<nbSecondaries; i++) {
secondaries[i] = vm->arrayInterfaces[i];
if(dim > 1)
secondaries[i] = secondaries[i]->getArray(dim - 1);
}
} else {
J3Class* baseClass = base->asClass();
baseClass->resolve();
if(J3Cst::isInterface(baseClass->access()))
isSecondary = 1;
super = baseClass->super()->getArray(dim);
super->resolve();
//printf("%s super is %ls (%d)\n", cl->name()->cStr(), super->name()->cStr(), isSecondary);
uint32_t n = baseClass->vt()->checker.nbSecondaryTypes;
secondaries = (J3Type**)alloca(n*sizeof(J3Type*));
for(uint32_t i=0; i<n; i++) {
secondaries[nbSecondaries] = baseClass->vt()->checker.secondaryTypes[i]->type();
if(secondaries[i] != baseClass) { /* don't add myself */
secondaries[nbSecondaries] = secondaries[nbSecondaries]->getArray(dim);
nbSecondaries++;
}
}
}
super->resolve();
J3VirtualTable* res = new(cl->loader()->allocator(), objClass->vt()->_nbVirtualMethods)
J3VirtualTable(cl, super, secondaries, nbSecondaries, isSecondary);
memcpy(res->_virtualMethods, objClass->vt()->_virtualMethods, sizeof(void*)*objClass->vt()->_nbVirtualMethods);
memcpy(res->_interfaceMethodTable, objClass->vt()->_interfaceMethodTable, sizeof(void*)*nbInterfaceMethodTable);
return res;
}
J3VirtualTable* J3VirtualTable::create(J3Primitive* prim) {
return new(prim->loader()->allocator(), 0) J3VirtualTable(prim, prim, 0, 0, 0);
}
void* J3VirtualTable::operator new(size_t unused, vmkit::BumpAllocator* allocator, size_t n) {
return allocator->allocate(sizeof(J3VirtualTable) + n*sizeof(void*) - sizeof(void*));
}
J3VirtualTable::J3VirtualTable(J3Type* type, J3Type* super, J3Type** interfaces, uint32_t nbInterfaces, bool isSecondary) {
_type = type;
// printf("*** Building the vt of %s based on %ls at %p\n", type->name()->cStr(), super->name()->cStr(), this);
if(super == type) {
checker.offset = 0;
checker.display[checker.offset] = this;
if(nbInterfaces)
J3::internalError("a root J3VirtualTable should not have interfaces");
} else {
uint32_t parentDisplayLength = super->vt()->checker.offset + 1;
//printf("%s (%p) secondary: %p %p (%d)\n", type->name()->cStr(), this, checker.secondaryTypes, checker.display[6], parentDisplayLength);
if(parentDisplayLength >= J3TypeChecker::cacheOffset)
isSecondary = 1;
memcpy(checker.display, super->vt()->checker.display, parentDisplayLength*sizeof(J3VirtualTable*));
checker.nbSecondaryTypes = super->vt()->checker.nbSecondaryTypes + nbInterfaces + isSecondary;
checker.secondaryTypes = (J3VirtualTable**)super->loader()->allocator()->allocate(checker.nbSecondaryTypes*sizeof(J3VirtualTable*));
//printf("%s: %d - %d %d\n", type->name()->cStr(), isSecondary, parentDisplayLength, J3TypeChecker::displayLength);
if(isSecondary) {
checker.offset = J3TypeChecker::cacheOffset;
checker.secondaryTypes[0] = this;
} else {
checker.offset = parentDisplayLength;
checker.display[checker.offset] = this;
}
memcpy(checker.secondaryTypes + isSecondary,
super->vt()->checker.secondaryTypes,
super->vt()->checker.nbSecondaryTypes*sizeof(J3VirtualTable*));
for(uint32_t i=0, n=isSecondary+super->vt()->checker.nbSecondaryTypes; i<nbInterfaces; i++) {
J3Type* sec = interfaces[i];
sec->resolve();
checker.secondaryTypes[n++] = sec->vt();
}
}
if(checker.nbSecondaryTypes) {
std::sort(checker.secondaryTypes, &checker.secondaryTypes[checker.nbSecondaryTypes]);
J3VirtualTable** it = std::unique(checker.secondaryTypes, &checker.secondaryTypes[checker.nbSecondaryTypes]);
checker.nbSecondaryTypes = std::distance(checker.secondaryTypes, it);
}
//dump();
}
bool J3VirtualTable::slowIsAssignableTo(J3VirtualTable* parent) {
for(uint32_t i=0; i<checker.nbSecondaryTypes; i++)
if(checker.secondaryTypes[i] == parent) {
checker.display[J3TypeChecker::cacheOffset] = parent;
return true;
}
return false;
}
bool J3VirtualTable::fastIsAssignableToPrimaryChecker(J3VirtualTable* parent, uint32_t parentOffset) {
return checker.display[parentOffset] == parent;
}
bool J3VirtualTable::fastIsAssignableToNonPrimaryChecker(J3VirtualTable* parent) {
if(checker.display[J3TypeChecker::cacheOffset] == parent)
return true;
else if(parent == this)
return true;
else
return slowIsAssignableTo(parent);
}
bool J3VirtualTable::isAssignableTo(J3VirtualTable* parent) {
uint32_t parentOffset = parent->checker.offset;
if(checker.display[parentOffset] == parent)
return true;
else if(parentOffset != J3TypeChecker::cacheOffset)
return false;
else if(parent == this)
return true;
else
return slowIsAssignableTo(parent);
}
void J3VirtualTable::dump() {
fprintf(stderr, "VirtualTable: %s%s (%p)\n",
type()->isLayout() && !type()->isClass() ? "static_" : "",
type()->name()->cStr(), this);
checker.dump();
}
/*
* --- J3Object ---
*/
J3VirtualTable* J3Object::vt() {
return _vt;
}
volatile uintptr_t* J3Object::header() {
return &_header;
}
J3Object* J3Object::allocate(J3VirtualTable* vt, uintptr_t n) {
J3Object* res = (J3Object*)vmkit::GC::allocate(n);
res->_vt = vt;
res->_header = 1;
return res;
}
J3Object* J3Object::doNew(J3Class* cl) {
cl->initialise();
return allocate(cl->vt(), cl->structSize());
}
void J3Object::monitorEnter(J3LockRecord* newRecord) {
uintptr_t header = _header;
if(isStackLocked(header)) {
J3LockRecord* record = asLockRecord(header);
if(vmkit::Thread::get(record) == vmkit::Thread::get()) {
record->lockCount++;
return;
}
} else if(isUnlocked(header)) {
newRecord->header = header;
newRecord->lockCount = 1;
if(__sync_val_compare_and_swap(&_header, header, (uintptr_t)newRecord) == header)
return;
}
inflate()->lock();
}
void J3Object::monitorExit() {
uintptr_t header = _header;
if(isStackLocked(header)) {
J3LockRecord* record = asLockRecord(header);
if(vmkit::Thread::get(record) == vmkit::Thread::get()) {
if(!--record->lockCount) {
if(__sync_val_compare_and_swap(&_header, header, record->header) == header)
return;
} else
return;
}
}
inflate()->unlock();
}
uint32_t J3Object::hashCode() {
static uint32_t curHashCode = 0;
while(1) {
uintptr_t header = _header;
if(isUnlocked(header)) { /* not locked, not inflated */
uint32_t res = header >> 8;
if(res)
return res;
do {
res = __sync_add_and_fetch(&curHashCode, 1) & 0xffffff;
} while(!res);
if(__sync_val_compare_and_swap(&_header, header, res<<8 | (header & 0xff)) == header)
return res;
} else {
/* if stack locked, force the inflation because I can not modify the stack of the owner */
J3Monitor* m = inflate();
header = m->header;
uint32_t res = header >> 8;
if(res)
return res;
do {
res = __sync_add_and_fetch(&curHashCode, 1) & 0xffffff;
} while(!res);
if(__sync_val_compare_and_swap(&m->header, header, res<<8 | (header & 0xff)) == header)
return res;
}
}
}
J3Monitor* J3Object::inflate() {
while(1) {
uintptr_t header = _header;
if(isInflated(header)) { /* already inflated */
J3Monitor* res = asMonitor(header);
if(res)
return res;
else
sched_yield(); /* another guy is trying to inflate this monitor */
} else if(__sync_val_compare_and_swap(&_header, header, 2) == header) {
/* ok, I'm the boss */
J3Monitor* monitor = J3Thread::get()->vm()->monitorManager.allocate();
if(isStackLocked(header)) { /* stack locked */
J3LockRecord* record = asLockRecord(header);
/* I can read record->header because, in the worst case, the owner is blocked in the sched_yield loop */
/* however, I can not read lockCount because the owner is maybe playing with this value */
monitor->prepare(this, record->header, record);
} else { /* not locked at all */
if(!isUnlocked(header))
J3::internalError("should not happen");
monitor->prepare(this, header, 0);
}
_header = (uintptr_t)monitor | 2;
return monitor;
}
}
}
bool J3Object::isLockOwner() {
vmkit::Thread* self = vmkit::Thread::get();
uintptr_t header = _header;
if(isInflated(header)) /* inflated */
return asMonitor(header)->isOwner(self);
else
return !isUnlocked(header) && vmkit::Thread::get((void*)header) == self;
}
/*
* --- J3ArrayObject ---
*/
J3Object* J3ArrayObject::doNew(J3ArrayClass* cl, uintptr_t length) {
cl->resolve();
J3ArrayObject* res = (J3ArrayObject*)allocate(cl->vt(), sizeof(J3ArrayObject) + (1 << cl->component()->logSize()) * length);
res->_length = length;
return res;
}
J3Object* J3ArrayObject::multianewArray(J3ArrayClass* cl, uint32_t dim, uint32_t* args) {
J3ObjectHandle* prev = J3Thread::get()->tell();
J3Object* res = cl->multianewArray(dim, args)->obj();
J3Thread::get()->restore(prev);
return res;
}
/*
* J3ObjectHandle
*/
void J3ObjectHandle::wait() {
obj()->inflate()->wait();
}
bool J3ObjectHandle::isLockOwner() {
return obj()->isLockOwner();
}
uint32_t J3ObjectHandle::hashCode() {
return obj()->hashCode();
}
J3ObjectHandle* J3ObjectHandle::allocate(J3VirtualTable* vt, size_t n) {
J3Object* res = J3Object::allocate(vt, n);
return J3Thread::get()->push(res);
}
J3ObjectHandle* J3ObjectHandle::doNewObject(J3Class* cl) {
J3Object* res = J3Object::doNew(cl);
return J3Thread::get()->push(res);
}
J3ObjectHandle* J3ObjectHandle::doNewArray(J3ArrayClass* cl, uint32_t length) {
J3Object* res = J3ArrayObject::doNew(cl, length);
return J3Thread::get()->push(res);
}
#define defAccessor(name, ctype, llvmtype, scale) \
ctype J3ObjectHandle::rawCAS##name(uintptr_t offset, ctype orig, ctype value) { \
if(scale == 2) { \
uint32_t io = *((uint32_t*)&orig); \
uint32_t iv = *((uint32_t*)&value); \
uint32_t ir = __sync_val_compare_and_swap((uint32_t*)((uintptr_t)obj() + offset), io, iv); \
return *((ctype*)&ir); \
} else { \
uint64_t io = *((uint32_t*)&orig); \
uint64_t iv = *((uint32_t*)&value); \
uint64_t ir = __sync_val_compare_and_swap((uint64_t*)((uintptr_t)obj() + offset), io, iv); \
return *((ctype*)&ir); \
} \
} \
\
void J3ObjectHandle::rawSet##name(uintptr_t offset, ctype value) { \
*((ctype*)((uintptr_t)obj() + offset)) = value; \
} \
\
ctype J3ObjectHandle::rawGet##name(uintptr_t offset) { \
return *((ctype*)((uintptr_t)obj() + offset)); \
} \
\
void J3ObjectHandle::set##name(J3Field* field, ctype value) { \
rawSet##name(field->offset(), value); \
} \
\
ctype J3ObjectHandle::get##name(J3Field* field) { \
return rawGet##name(field->offset()); \
} \
\
void J3ObjectHandle::set##name##At(uint32_t idx, ctype value) { \
rawSet##name(sizeof(J3ArrayObject) + idx*sizeof(ctype), value); \
} \
\
ctype J3ObjectHandle::get##name##At(uint32_t idx) { \
return rawGet##name(sizeof(J3ArrayObject) + idx*sizeof(ctype)); \
} \
\
void J3ObjectHandle::setRegion##name(uint32_t selfIdx, const ctype* buf, uint32_t bufIdx, uint32_t len) { \
if(selfIdx + len > arrayLength()) \
J3::arrayIndexOutOfBoundsException(); \
memcpy((uint8_t*)array() + sizeof(J3ArrayObject) + selfIdx*sizeof(ctype), \
(uint8_t*)buf + bufIdx*sizeof(ctype), \
len*sizeof(ctype)); \
} \
\
void J3ObjectHandle::getRegion##name(uint32_t selfIdx, const ctype* buf, uint32_t bufIdx, uint32_t len) { \
if(selfIdx + len > arrayLength()) \
J3::arrayIndexOutOfBoundsException(); \
memcpy((uint8_t*)buf + bufIdx*sizeof(ctype), \
(uint8_t*)array() + sizeof(J3ArrayObject) + selfIdx*sizeof(ctype), \
len*sizeof(ctype)); \
}
onJavaPrimitives(defAccessor)
#undef defAccessor
J3ObjectHandle* J3ObjectHandle::rawCASObject(uintptr_t offset, J3ObjectHandle* orig, J3ObjectHandle* value) {
J3Object* oo = orig ? orig->obj() : 0;
J3Object* ov = value ? value->obj() : 0;
J3Object* res = __sync_val_compare_and_swap((J3Object**)((uintptr_t)obj() + offset), oo, ov);
if(res == oo)
return orig;
else if(res == ov)
return value;
else
return J3Thread::get()->push(res);
}
void J3ObjectHandle::rawSetObject(uintptr_t offset, J3ObjectHandle* value) {
*((J3Object**)((uintptr_t)obj() + offset)) = value ? value->obj() : 0;
}
J3ObjectHandle* J3ObjectHandle::rawGetObject(uintptr_t offset) {
return J3Thread::get()->push(*((J3Object**)((uintptr_t)obj() + offset)));
}
void J3ObjectHandle::setObject(J3Field* field, J3ObjectHandle* value) {
rawSetObject(field->offset(), value);
}
J3ObjectHandle* J3ObjectHandle::getObject(J3Field* field) {
return rawGetObject(field->offset());
}
void J3ObjectHandle::setObjectAt(uint32_t idx, J3ObjectHandle* value) {
rawSetObject(sizeof(J3ArrayObject) + idx*sizeof(J3Object*), value);
}
J3ObjectHandle* J3ObjectHandle::getObjectAt(uint32_t idx) {
return rawGetObject(sizeof(J3ArrayObject) + idx*sizeof(J3Object*));
}
void J3ObjectHandle::rawObjectCopyTo(uint32_t fromOffset, J3ObjectHandle* to, uint32_t toOffset, uint32_t nbb) {
if(isSame(to))
memmove((uint8_t*)(to->obj()+1) + toOffset, (uint8_t*)(obj()+1) + fromOffset, nbb);
else
memcpy((uint8_t*)(to->obj()+1) + toOffset, (uint8_t*)(obj()+1) + fromOffset, nbb);
}
void J3ObjectHandle::rawArrayCopyTo(uint32_t fromOffset, J3ObjectHandle* to, uint32_t toOffset, uint32_t nbb) {
if(isSame(to))
memmove((uint8_t*)(to->array()+1) + toOffset, (uint8_t*)(array()+1) + fromOffset, nbb);
else
memcpy((uint8_t*)(to->array()+1) + toOffset, (uint8_t*)(array()+1) + fromOffset, nbb);
}
/*
* J3LocalReferences
*/
J3ObjectHandle* J3LocalReferences::push(J3Object* obj) {
if(obj) {
J3ObjectHandle* res = Stack<J3ObjectHandle>::push();
res->_obj = obj;
return res;
} else
return 0;
}
/*
* J3GlobalReferences
*/
J3GlobalReferences::J3GlobalReferences(vmkit::BumpAllocator* _allocator) :
references(_allocator),
emptySlots(_allocator) {
pthread_mutex_init(&mutex, 0);
}
J3ObjectHandle* J3GlobalReferences::add(J3ObjectHandle* handle) {
if(handle) {
pthread_mutex_lock(&mutex);
J3ObjectHandle* res = emptySlots.isEmpty() ? references.push() : *emptySlots.pop();
res->_obj = handle->_obj;
pthread_mutex_unlock(&mutex);
return res;
} else
return 0;
}
void J3GlobalReferences::del(J3ObjectHandle* handle) {
if(handle) {
handle->harakiri();
pthread_mutex_lock(&mutex);
*emptySlots.push() = handle;
pthread_mutex_unlock(&mutex);
}
}