//===-------- Selected.cpp - Implementation of the Selected class  --------===//
//
//                              The VMKit project
//
// This file is distributed under the University of Illinois Open Source 
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "MutatorThread.h"
#include "MvmGC.h"

#include <sys/mman.h>
#include <set>

using namespace mvm;

uintptr_t Collector::TraceLocal = 0;

extern "C" void* JnJVM_org_mmtk_plan_marksweep_MSMutator_alloc__IIIII(uintptr_t Mutator, int32_t sz, int32_t align, int32_t offset, int32_t allocator, int32_t site) __attribute__((always_inline));
extern "C" int32_t JnJVM_org_mmtk_plan_MutatorContext_checkAllocator__III(uintptr_t Mutator, int32_t bytes, int32_t align, int32_t allocator) __attribute__((always_inline));
extern "C" void JnJVM_org_mmtk_plan_marksweep_MSMutator_postAlloc__Lorg_vmmagic_unboxed_ObjectReference_2Lorg_vmmagic_unboxed_ObjectReference_2II(uintptr_t Mutator, uintptr_t ref, uintptr_t typeref,
    int32_t bytes, int32_t allocator) __attribute__((always_inline));

extern "C" uint32_t MMTkMutatorSize;
extern "C" void JnJVM_org_j3_config_Selected_00024Mutator__0003Cinit_0003E__(uintptr_t);
extern "C" VirtualTable* org_j3_config_Selected_4Mutator_VT;
extern "C" void JnJVM_org_mmtk_plan_MutatorContext_initMutator__I(uintptr_t, int32_t);
extern "C" void JnJVM_org_mmtk_plan_MutatorContext_deinitMutator__(uintptr_t);
  
extern "C" void JnJVM_org_j3_config_Selected_00024Collector__0003Cinit_0003E__(uintptr_t);
extern "C" VirtualTable* org_j3_config_Selected_4Collector_VT;
extern "C" uint32_t MMTkCollectorSize;

extern "C" void JnJVM_org_mmtk_utility_heap_HeapGrowthManager_boot__Lorg_vmmagic_unboxed_Extent_2Lorg_vmmagic_unboxed_Extent_2(intptr_t, intptr_t);
extern "C" uintptr_t* org_j3_config_Selected_4Plan_static;
extern "C" void MMTkPlanBoot(uintptr_t);
extern "C" void MMTkPlanPostBoot(uintptr_t);
extern "C" void MMTkPlanFullBoot(uintptr_t);
  
extern "C" void Java_org_j3_mmtk_Collection_triggerCollection__I(uintptr_t, int32_t);
//===-------------------- TODO: make those virtual. -------------------===//
extern "C" void JnJVM_org_mmtk_plan_TraceLocal_reportDelayedRootEdge__Lorg_vmmagic_unboxed_Address_2(uintptr_t TraceLocal, void** slot);
extern "C" void JnJVM_org_mmtk_plan_TraceLocal_processEdge__Lorg_vmmagic_unboxed_ObjectReference_2Lorg_vmmagic_unboxed_Address_2(
    uintptr_t TraceLocal, void* source, void* slot);
extern "C" void JnJVM_org_mmtk_plan_TraceLocal_processRootEdge__Lorg_vmmagic_unboxed_Address_2Z(
    uintptr_t TraceLocal, void* slot, uint8_t untraced);

extern "C" gc* JnJVM_org_mmtk_plan_TraceLocal_retainForFinalize__Lorg_vmmagic_unboxed_ObjectReference_2(uintptr_t TraceLocal, void* obj);
extern "C" gc* JnJVM_org_mmtk_plan_TraceLocal_retainReferent__Lorg_vmmagic_unboxed_ObjectReference_2(uintptr_t TraceLocal, void* obj);
extern "C" gc* JnJVM_org_mmtk_plan_TraceLocal_getForwardedReference__Lorg_vmmagic_unboxed_ObjectReference_2(uintptr_t TraceLocal, void* obj);
extern "C" gc* JnJVM_org_mmtk_plan_TraceLocal_getForwardedReferent__Lorg_vmmagic_unboxed_ObjectReference_2(uintptr_t TraceLocal, void* obj);
extern "C" gc* JnJVM_org_mmtk_plan_TraceLocal_getForwardedFinalizable__Lorg_vmmagic_unboxed_ObjectReference_2(uintptr_t TraceLocal, void* obj);
  
extern "C" uint8_t JnJVM_org_mmtk_plan_marksweep_MSTraceLocal_isLive__Lorg_vmmagic_unboxed_ObjectReference_2(uintptr_t TraceLocal, void* obj);


extern "C" void* gcmalloc(uint32_t sz, void* _VT) {
  gc* res = 0;
  llvm_gcroot(res, 0);
  VirtualTable* VT = (VirtualTable*)_VT;
  sz = llvm::RoundUpToAlignment(sz, sizeof(void*));
  uintptr_t Mutator = mvm::MutatorThread::get()->MutatorContext;
  int allocator = JnJVM_org_mmtk_plan_MutatorContext_checkAllocator__III(Mutator, sz, 0, 0);
  res = (gc*)JnJVM_org_mmtk_plan_marksweep_MSMutator_alloc__IIIII(Mutator, sz, 0, 0, allocator, 0);
  res->setVirtualTable(VT);
  JnJVM_org_mmtk_plan_marksweep_MSMutator_postAlloc__Lorg_vmmagic_unboxed_ObjectReference_2Lorg_vmmagic_unboxed_ObjectReference_2II(Mutator, (uintptr_t)res, (uintptr_t)VT, sz, allocator);
  return res;
}

extern "C" void addFinalizationCandidate(void* obj) __attribute__((always_inline));

extern "C" void addFinalizationCandidate(void* obj) {
  llvm_gcroot(obj, 0);
  mvm::Thread::get()->MyVM->addFinalizationCandidate((gc*)obj);
}

extern "C" void* gcmallocUnresolved(uint32_t sz, VirtualTable* VT) {
  gc* res = 0;
  llvm_gcroot(res, 0);
  res = (gc*)gcmalloc(sz, VT);
  if (VT->destructor) addFinalizationCandidate(res);
  return res;
}

void MutatorThread::init(Thread* _th) {
  MutatorThread* th = (MutatorThread*)_th;
  th->MutatorContext =
    (uintptr_t)th->Allocator.Allocate(MMTkMutatorSize, "Mutator");
  ((VirtualTable**)th->MutatorContext)[0] = org_j3_config_Selected_4Mutator_VT;
  JnJVM_org_j3_config_Selected_00024Mutator__0003Cinit_0003E__(th->MutatorContext);
  JnJVM_org_mmtk_plan_MutatorContext_initMutator__I(th->MutatorContext, (int32_t)_th->getThreadID());
  th->realRoutine(_th);
  JnJVM_org_mmtk_plan_MutatorContext_deinitMutator__(th->MutatorContext);
}

bool Collector::isLive(gc* ptr) {
  return JnJVM_org_mmtk_plan_marksweep_MSTraceLocal_isLive__Lorg_vmmagic_unboxed_ObjectReference_2(TraceLocal, ptr);
}

void Collector::scanObject(void** ptr) {
  JnJVM_org_mmtk_plan_TraceLocal_reportDelayedRootEdge__Lorg_vmmagic_unboxed_Address_2(TraceLocal, ptr);
}
 
void Collector::markAndTrace(void* source, void* ptr) {
  assert(TraceLocal && "scanning without a trace local");
  JnJVM_org_mmtk_plan_TraceLocal_processEdge__Lorg_vmmagic_unboxed_ObjectReference_2Lorg_vmmagic_unboxed_Address_2(TraceLocal, source, ptr);
}
  
void Collector::markAndTraceRoot(void* ptr) {
  assert(TraceLocal && "scanning without a trace local");
  JnJVM_org_mmtk_plan_TraceLocal_processRootEdge__Lorg_vmmagic_unboxed_Address_2Z(TraceLocal, ptr, true);
}

gc* Collector::retainForFinalize(gc* val) {
  assert(TraceLocal && "scanning without a trace local");
  return JnJVM_org_mmtk_plan_TraceLocal_retainForFinalize__Lorg_vmmagic_unboxed_ObjectReference_2(TraceLocal, val);
}
  
gc* Collector::retainReferent(gc* val) {
  assert(TraceLocal && "scanning without a trace local");
  return JnJVM_org_mmtk_plan_TraceLocal_retainReferent__Lorg_vmmagic_unboxed_ObjectReference_2(TraceLocal, val);
}
  
gc* Collector::getForwardedFinalizable(gc* val) {
  assert(TraceLocal && "scanning without a trace local");
  return JnJVM_org_mmtk_plan_TraceLocal_getForwardedFinalizable__Lorg_vmmagic_unboxed_ObjectReference_2(TraceLocal, val);
}
  
gc* Collector::getForwardedReference(gc* val) {
  assert(TraceLocal && "scanning without a trace local");
  return JnJVM_org_mmtk_plan_TraceLocal_getForwardedReference__Lorg_vmmagic_unboxed_ObjectReference_2(TraceLocal, val);
}
  
gc* Collector::getForwardedReferent(gc* val) {
  assert(TraceLocal && "scanning without a trace local");
  return JnJVM_org_mmtk_plan_TraceLocal_getForwardedReferent__Lorg_vmmagic_unboxed_ObjectReference_2(TraceLocal, val);
}

void Collector::collect() {
  Java_org_j3_mmtk_Collection_triggerCollection__I(NULL, 2);
}

void Collector::initialise() {
  // Allocate the memory for MMTk right now, to avoid conflicts with
  // other allocators.
#if defined (__MACH__)
  uint32 flags = MAP_PRIVATE | MAP_ANON | MAP_FIXED;
#else
  uint32 flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
#endif
  void* baseAddr = mmap((void*)0x30000000, 0x40000000, PROT_READ | PROT_WRITE,
                        flags, -1, 0);
  if (baseAddr == MAP_FAILED) {
    perror("mmap");
    abort();
  }
  
  JnJVM_org_mmtk_utility_heap_HeapGrowthManager_boot__Lorg_vmmagic_unboxed_Extent_2Lorg_vmmagic_unboxed_Extent_2(128 * 1024 * 1024, 1024 * 1024 * 1024);
  
  uintptr_t Plan = *org_j3_config_Selected_4Plan_static;
  MMTkPlanBoot(Plan);
  MMTkPlanPostBoot(Plan);  
  MMTkPlanFullBoot(Plan);  
}

extern "C" void* MMTkMutatorAllocate(uint32_t size, VirtualTable* VT) {
  void* val = MutatorThread::get()->Allocator.Allocate(size, "MMTk");
  ((void**)val)[0] = VT;
  return val;
}

//TODO: Remove these.
std::set<gc*> __InternalSet__;
void* Collector::begOf(gc* obj) {
  abort();
}
