//===-- examples/RepeatedMethodExchange/RepeatedMethodExchange.cpp - An example use of the JIT --===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This small program provides an example of how to quickly build a small
//  module with two functions and execute it with the JIT.
//
//===----------------------------------------------------------------------===//

#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Instructions.h"
#include "llvm/ExecutionEngine/Interpreter.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/IRBuilder.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"

#include <pthread.h>

using namespace llvm;

union {
	void *i8;
	volatile uint64_t *u64;
} counterAddr;

ExecutionEngine *EE;
Function *IncCounterFn;

void *concurrentTask(void *data) {
  int replaced = 0;
  clock_t lastClock = clock();
  uint64_t lastValue = *counterAddr.u64;
  uint64_t initialMegaIncPerSecond = 0;
  do {
    sleep(1);
    clock_t newClock = clock();
    uint64_t newValue = *counterAddr.u64;
    double megaIncPerSecond = 1. * (newValue - lastValue) / (newClock - lastClock) * CLOCKS_PER_SEC / 1e6;
    if (replaced == 0)
	    printf("After %5d replacements:%9.3f Mcalls/sec\n", replaced, megaIncPerSecond);
    else if (replaced < 10 || (replaced < 100 & replaced % 5 == 0) || (replaced % 20) == 0)
	    printf("After %5d replacements:%9.3f Mcalls/sec (%7.3f%% of initial)\n", replaced,
	    	megaIncPerSecond, 100. * megaIncPerSecond / initialMegaIncPerSecond);
    lastClock = newClock;
    lastValue = newValue;
    if (replaced == 0)
    	initialMegaIncPerSecond = megaIncPerSecond;

    //void *oldFnAddr = EE->getPointerToGlobalIfAvailable(IncCounterFn);
    EE->recompileAndRelinkFunction(IncCounterFn);
    //void *newFnAddr = EE->getPointerToGlobalIfAvailable(IncCounterFn);
    //printf("Recompiled incCounter function: %p -> %p\n", oldFnAddr, newFnAddr);
  } while (++replaced < 1000);
  return 0;
}

int main() {
  
  InitializeNativeTarget();

  LLVMContext Context;
  
  // Create some module to put our function into it.
  Module *M = new Module("test", Context);

  // Create a global variable of type i64. This value is incremeted on each
  // call to the test method.
  GlobalVariable *Counter = new GlobalVariable(*M, Type::getInt64Ty(Context),
    false, GlobalValue::ExternalLinkage, ConstantInt::get(Context, APInt(64, 0)), "counter");

  // Create the incCounter function. Fill it with code to increment the global counter.
  IncCounterFn = Function::Create(
    FunctionType::get(Type::getVoidTy(Context), ArrayRef<Type*>(), false),
    GlobalValue::ExternalLinkage, "incCounter", M);
  // Code:
  BasicBlock *BB = BasicBlock::Create(Context, "Entry", IncCounterFn);
  Value *oldVal = new LoadInst(Counter, "OldValue", true, BB);
  Value *newVal = BinaryOperator::Create(BinaryOperator::Add, oldVal,
    ConstantInt::get(Context, APInt(64, 1)), "NewValue", BB);
  new StoreInst(newVal, Counter, true, BB);
  ReturnInst::Create(Context, BB);


  // Create the main function. Fill it with code to repeatedly call the incCounter method.
  Function *MainFn = Function::Create(
    FunctionType::get(Type::getInt32Ty(Context), ArrayRef<Type*>(), false),
    GlobalValue::ExternalLinkage, "main", M);
  // Code:
  BasicBlock *Entry = BasicBlock::Create(Context, "Entry", MainFn);
  BasicBlock *LoopBlock = BasicBlock::Create(Context, "Loop", MainFn);
  CallInst::Create(IncCounterFn, "", LoopBlock);
  BranchInst::Create(LoopBlock, LoopBlock);
  BranchInst::Create(LoopBlock, Entry);

  //DebugFlag = true;
  //SetCurrentDebugType(""); //"x86-emitter");

  // Now we create the JIT.
  EE = EngineBuilder(M).create();
  //EE->SetKeepStubs(true);

  outs() << "We just constructed this LLVM module:\n\n" << *M;

  // Store addresses for the concurrent task:
  counterAddr.i8 = EE->getPointerToGlobal(Counter);

  // Start a thread to repeatedly recompile the incCounter function and read the counter value.
  pthread_t thread;
  int result = pthread_create( &thread, NULL, concurrentTask, 0 );
  if (result != 0) {
    outs() << "Could not create thread\n";
    return 1;
  }

  // Call the main function with no arguments:
  outs() << "\n\nStarting main method...\n";
  outs().flush();

  std::vector<GenericValue> noargs;
  GenericValue gv = EE->runFunction(MainFn, noargs);

  delete EE;
  llvm_shutdown();
  return 0;
}
