//===- ChildTarget.inc - Child process for external JIT execution for Unix -==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of the Unix-specific parts of the ChildTarget class
// which executes JITed code in a separate process from where it was built.
//
//===----------------------------------------------------------------------===//

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif

#ifdef __APPLE__
#include <mach/mach.h>
#endif

#if defined(__mips__)
#  if defined(__OpenBSD__)
#    include <mips64/sysarch.h>
#  else
#    include <sys/cachectl.h>
#  endif
#endif

#ifdef __APPLE__
extern "C" void sys_icache_invalidate(const void *Addr, size_t len);
#else
extern "C" void __clear_cache(void *, void*);
#endif

namespace {

struct ConnectionData_t {
  int InputPipe;
  int OutputPipe;

  ConnectionData_t(int in, int out) : InputPipe(in), OutputPipe(out) {}
};

} // namespace

LLIChildTarget::~LLIChildTarget() {
  delete static_cast<ConnectionData_t *>(ConnectionData);
}

// OS-specific methods
void LLIChildTarget::initializeConnection() {
  // Store the parent ends of the pipes
  ConnectionData = (void*)new ConnectionData_t(STDIN_FILENO, STDOUT_FILENO);
}

int LLIChildTarget::WriteBytes(const void *Data, size_t Size) {
  return write(((ConnectionData_t*)ConnectionData)->OutputPipe, Data, Size);
}

int LLIChildTarget::ReadBytes(void *Data, size_t Size) {
  return read(((ConnectionData_t*)ConnectionData)->InputPipe, Data, Size);
}

// The functions below duplicate functionality that is implemented in
// Support/Memory.cpp with the goal of avoiding a dependency on any
// llvm libraries.

uint64_t LLIChildTarget::allocate(uint32_t Alignment, uint32_t Size) {
  if (!Alignment)
    Alignment = 16;

  static const size_t PageSize = getpagesize();
  const size_t NumPages = (Size+PageSize-1)/PageSize;
  Size = NumPages*PageSize;

  int fd = -1;
#ifdef NEED_DEV_ZERO_FOR_MMAP
  static int zero_fd = open("/dev/zero", O_RDWR);
  if (zero_fd == -1)
    return 0;
  fd = zero_fd;
#endif

  int MMFlags = MAP_PRIVATE |
#ifdef HAVE_MMAP_ANONYMOUS
  MAP_ANONYMOUS
#else
  MAP_ANON
#endif
  ; // Ends statement above

  uint64_t Addr = (uint64_t)::mmap(0, Size, PROT_READ | PROT_WRITE, MMFlags, fd, 0);
  if (Addr == (uint64_t)MAP_FAILED)
    return 0;

  // Align the address.
  Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);

  m_AllocatedBufferMap[Addr] = Size;

  // Return aligned address
  return Addr;
}

void LLIChildTarget::makeSectionExecutable(uint64_t Addr, uint32_t Size) {
  // FIXME: We have to mark the memory as RWX because multiple code chunks may
  // be on the same page.  The RemoteTarget interface should be changed to
  // work around that.
  int Result = ::mprotect((void*)Addr, Size, PROT_READ | PROT_WRITE | PROT_EXEC);
  if (Result != 0)
    InvalidateInstructionCache((const void *)Addr, Size);
}

/// InvalidateInstructionCache - Before the JIT can run a block of code
/// that has been emitted it must invalidate the instruction cache on some
/// platforms.
void LLIChildTarget::InvalidateInstructionCache(const void *Addr,
                                        size_t Len) {

// icache invalidation for PPC and ARM.
#if defined(__APPLE__)

#  if (defined(__POWERPC__) || defined (__ppc__) || \
     defined(_POWER) || defined(_ARCH_PPC)) || defined(__arm__)
  sys_icache_invalidate(const_cast<void *>(Addr), Len);
#  endif

#else

#  if (defined(__POWERPC__) || defined (__ppc__) || \
       defined(_POWER) || defined(_ARCH_PPC)) && defined(__GNUC__)
  const size_t LineSize = 32;

  const intptr_t Mask = ~(LineSize - 1);
  const intptr_t StartLine = ((intptr_t) Addr) & Mask;
  const intptr_t EndLine = ((intptr_t) Addr + Len + LineSize - 1) & Mask;

  for (intptr_t Line = StartLine; Line < EndLine; Line += LineSize)
    asm volatile("dcbf 0, %0" : : "r"(Line));
  asm volatile("sync");

  for (intptr_t Line = StartLine; Line < EndLine; Line += LineSize)
    asm volatile("icbi 0, %0" : : "r"(Line));
  asm volatile("isync");
#  elif defined(__arm__) && defined(__GNUC__)
  // FIXME: Can we safely always call this for __GNUC__ everywhere?
  const char *Start = static_cast<const char *>(Addr);
  const char *End = Start + Len;
  __clear_cache(const_cast<char *>(Start), const_cast<char *>(End));
#  elif defined(__mips__)
  const char *Start = static_cast<const char *>(Addr);
  cacheflush(const_cast<char *>(Start), Len, BCACHE);
#  endif

#endif  // end apple
}

void LLIChildTarget::releaseMemory(uint64_t Addr, uint32_t Size) {
  ::munmap((void*)Addr, Size);
}