#ifndef LLVM_TOOLS_LLVM_BOLT_SYS_RISCV
#define LLVM_TOOLS_LLVM_BOLT_SYS_RISCV

// Save all registers while keeping 16B stack alignment
#define SAVE_ALL                                                               \
  "addi sp, sp, -16\n"                                                         \
  "sd x0, 0(sp)\n"                                                             \
  "sd x1, 8(sp)\n"                                                             \
  "addi sp, sp, -16\n"                                                         \
  "sd x2, 0(sp)\n"                                                             \
  "sd x3, 8(sp)\n"                                                             \
  "addi sp, sp, -16\n"                                                         \
  "sd x4, 0(sp)\n"                                                             \
  "sd x5, 8(sp)\n"                                                             \
  "addi sp, sp, -16\n"                                                         \
  "sd x6, 0(sp)\n"                                                             \
  "sd x7, 8(sp)\n"                                                             \
  "addi sp, sp, -16\n"                                                         \
  "sd x8, 0(sp)\n"                                                             \
  "sd x9, 8(sp)\n"                                                             \
  "addi sp, sp, -16\n"                                                         \
  "sd x10, 0(sp)\n"                                                            \
  "sd x11, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x12, 0(sp)\n"                                                            \
  "sd x13, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x14, 0(sp)\n"                                                            \
  "sd x15, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x16, 0(sp)\n"                                                            \
  "sd x17, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x18, 0(sp)\n"                                                            \
  "sd x19, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x20, 0(sp)\n"                                                            \
  "sd x21, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x22, 0(sp)\n"                                                            \
  "sd x23, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x24, 0(sp)\n"                                                            \
  "sd x25, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x26, 0(sp)\n"                                                            \
  "sd x27, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x28, 0(sp)\n"                                                            \
  "sd x29, 8(sp)\n"                                                            \
  "addi sp, sp, -16\n"                                                         \
  "sd x30, 0(sp)\n"                                                            \
  "sd x31, 8(sp)\n"
// Mirrors SAVE_ALL
#define RESTORE_ALL                                                            \
  "ld  x30, 0(sp)\n"                                                           \
  "ld  x31, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x28, 0(sp)\n"                                                           \
  "ld  x29, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x26, 0(sp)\n"                                                           \
  "ld  x27, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x24, 0(sp)\n"                                                           \
  "ld  x25, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x22, 0(sp)\n"                                                           \
  "ld  x23, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x20, 0(sp)\n"                                                           \
  "ld  x21, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x18, 0(sp)\n"                                                           \
  "ld  x19, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x16, 0(sp)\n"                                                           \
  "ld  x17, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x14, 0(sp)\n"                                                           \
  "ld  x15, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x12, 0(sp)\n"                                                           \
  "ld  x13, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x10, 0(sp)\n"                                                           \
  "ld  x11, 8(sp)\n"                                                           \
  "addi sp, sp,  16\n"                                                         \
  "ld  x8, 0(sp)\n"                                                            \
  "ld  x9, 8(sp)\n"                                                            \
  "addi sp, sp,  16\n"                                                         \
  "ld  x6, 0(sp)\n"                                                            \
  "ld  x7, 8(sp)\n"                                                            \
  "addi sp, sp,  16\n"                                                         \
  "ld  x4, 0(sp)\n"                                                            \
  "ld  x5, 8(sp)\n"                                                            \
  "addi sp, sp,  16\n"                                                         \
  "ld  x2, 0(sp)\n"                                                            \
  "ld  x3, 8(sp)\n"                                                            \
  "addi sp, sp,  16\n"                                                         \
  "ld  x0, 0(sp)\n"                                                            \
  "ld  x1, 8(sp)\n"                                                            \
  "addi sp, sp,  16\n"

// Anonymous namespace covering everything but our library entry point
namespace {

// Get the difference between runtime addrress of .text section and
// static address in section header table. Can be extracted from arbitrary
// pc value recorded at runtime to get the corresponding static address, which
// in turn can be used to search for indirect call description. Needed because
// indirect call descriptions are read-only non-relocatable data.
uint64_t getTextBaseAddress() {
  uint64_t DynAddr;
  uint64_t StaticAddr;
  __asm__ volatile("lla %0, __hot_end\n\t"
                   "lui %1, %%hi(__hot_end)\n\t"
                   "addi %1, %1, %%lo(__hot_end)\n\t"
                   : "=r"(DynAddr), "=r"(StaticAddr));
  return DynAddr - StaticAddr;
}

uint64_t __read(uint64_t fd, const void *buf, uint64_t count) {
  uint64_t ret;
  register uint64_t a0 __asm__("a0") = fd;
  register const void *a1 __asm__("a1") = buf;
  register uint64_t a2 __asm__("a2") = count;
  register uint64_t a7 __asm__("a7") =
      63; // Assuming 63 is the syscall number for read
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret)
                       : "r"(a0), "r"(a1), "r"(a2), "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __write(uint64_t fd, const void *buf, uint64_t count) {
  uint64_t ret;
  register uint64_t a0 __asm__("a0") = fd;
  register const void *a1 __asm__("a1") = buf;
  register uint64_t a2 __asm__("a2") = count;
  register uint32_t a7 __asm__("a7") =
      64; // Assuming 64 is the syscall number for write
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret)
                       : "r"(a0), "r"(a1), "r"(a2), "r"(a7)
                       : "memory");
  return ret;
}

void *__mmap(uint64_t addr, uint64_t size, uint64_t prot, uint64_t flags,
             uint64_t fd, uint64_t offset) {
  void *ret;
  register uint64_t a0 __asm__("a0") = addr;
  register uint64_t a1 __asm__("a1") = size;
  register uint64_t a2 __asm__("a2") = prot;
  register uint64_t a3 __asm__("a3") = flags;
  register uint64_t a4 __asm__("a4") = fd;
  register uint64_t a5 __asm__("a5") = offset;
  register uint32_t a7 __asm__("a7") =
      222; // Assuming 222 is the syscall number for mmap
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret)
                       : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5),
                         "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __munmap(void *addr, uint64_t size) {
  uint64_t ret;
  register void *a0 __asm__("a0") = addr;
  register uint64_t a1 __asm__("a1") = size;
  register uint32_t a7 __asm__("a7") = 215;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __exit(uint64_t code) {
  uint64_t ret;
  register uint64_t a0 __asm__("a0") = code;
  register uint32_t a7 __asm__("a7") = 94;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0)
                       : "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __open(const char *pathname, uint64_t flags, uint64_t mode) {
  uint64_t ret;
  register int a0 __asm__("a0") =
      -100; // Assuming -100 is an invalid file descriptor
  register const char *a1 __asm__("a1") = pathname;
  register uint64_t a2 __asm__("a2") = flags;
  register uint64_t a3 __asm__("a3") = mode;
  register uint64_t a7 __asm__("a7") =
      56; // Assuming 56 is the syscall number for open
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret)
                       : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a7)
                       : "memory");
  return ret;
}

long __getdents64(unsigned int fd, dirent64 *dirp, size_t count) {
  long ret;
  register unsigned int a0 __asm__("a0") = fd;
  register dirent64 *a1 __asm__("a1") = dirp;
  register size_t a2 __asm__("a2") = count;
  register uint32_t a7 __asm__("a7") = 61;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a2), "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __readlink(const char *pathname, char *buf, size_t bufsize) {
  uint64_t ret;
  register int a0 __asm__("a0") = -100;
  register const char *a1 __asm__("a1") = pathname;
  register char *a2 __asm__("a2") = buf;
  register size_t a3 __asm__("a3") = bufsize;
  register uint32_t a7 __asm__("a7") = 78; // readlinkat
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a2), "r"(a3), "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __lseek(uint64_t fd, uint64_t pos, uint64_t whence) {
  uint64_t ret;
  register uint64_t a0 __asm__("a0") = fd;
  register uint64_t a1 __asm__("a1") = pos;
  register uint64_t a2 __asm__("a2") = whence;
  register uint32_t a7 __asm__("a7") = 62;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a2), "r"(a7)
                       : "memory");
  return ret;
}

int __ftruncate(uint64_t fd, uint64_t length) {
  int ret;
  register uint64_t a0 __asm__("a0") = fd;
  register uint64_t a1 __asm__("a1") = length;
  register uint32_t a7 __asm__("a7") = 46;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a7)
                       : "memory");
  return ret;
}

int __close(uint64_t fd) {
  int ret;
  register uint64_t a0 __asm__("a0") = fd;
  register uint32_t a7 __asm__("a7") =
      57; // Assuming 57 is the syscall number for close
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret)
                       : "r"(a0), "r"(a7)
                       : "memory");
  return ret;
}

int __madvise(void *addr, size_t length, int advice) {
  int ret;
  register void *a0 __asm__("a0") = addr;
  register size_t a1 __asm__("a1") = length;
  register int a2 __asm__("a2") = advice;
  register uint32_t a7 __asm__("a7") = 233;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a2), "r"(a7)
                       : "memory");
  return ret;
}

int __uname(struct UtsNameTy *buf) {
  int ret;
  register UtsNameTy *a0 __asm__("a0") = buf;
  register uint32_t a7 __asm__("a7") = 160;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0)
                       : "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __nanosleep(const timespec *req, timespec *rem) {
  uint64_t ret;
  register const timespec *a0 __asm__("a0") = req;
  register timespec *a1 __asm__("a1") = rem;
  register uint32_t a7 __asm__("a7") = 101;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a7)
                       : "memory");
  return ret;
}

int64_t __fork() {
  uint64_t ret;
  // clone instead of fork with flags
  // "CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD"
  register uint64_t a0 __asm__("a0") = 0x1200011;
  register uint64_t a1 __asm__("a1") = 0;
  register uint64_t a2 __asm__("a2") = 0;
  register uint64_t a3 __asm__("a3") = 0;
  register uint64_t a4 __asm__("a4") = 0;
  register uint32_t a7 __asm__("a7") = 220;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a2), "r"(a3), "r"(a4), "r"(a7)
                       : "memory");
  return ret;
}

int __mprotect(void *addr, size_t len, int prot) {
  int ret;
  register void *a0 __asm__("a0") = addr;
  register size_t a1 __asm__("a1") = len;
  register int a2 __asm__("a2") = prot;
  register uint32_t a7 __asm__("a7") = 226;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a2), "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __getpid() {
  uint64_t ret;
  register uint32_t a7 __asm__("a7") = 172;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret)
                       : "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __getppid() {
  uint64_t ret;
  register uint32_t a7 __asm__("a7") = 173;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret)
                       : "r"(a7)
                       : "memory");
  return ret;
}

int __setpgid(uint64_t pid, uint64_t pgid) {
  int ret;
  register uint64_t a0 __asm__("a0") = pid;
  register uint64_t a1 __asm__("a1") = pgid;
  register uint32_t a7 __asm__("a7") = 154;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __getpgid(uint64_t pid) {
  uint64_t ret;
  register uint64_t a0 __asm__("a0") = pid;
  register uint32_t a7 __asm__("a7") = 155;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0)
                       : "r"(a7)
                       : "memory");
  return ret;
}

int __kill(uint64_t pid, int sig) {
  int ret;
  register uint64_t a0 __asm__("a0") = pid;
  register int a1 __asm__("a1") = sig;
  register uint32_t a7 __asm__("a7") = 129;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a7)
                       : "memory");
  return ret;
}

int __fsync(int fd) {
  int ret;
  register int a0 __asm__("a0") = fd;
  register uint32_t a7 __asm__("a7") = 82;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0)
                       : "r"(a7)
                       : "memory");
  return ret;
}

uint64_t __sigprocmask(int how, const void *set, void *oldset) {
  uint64_t ret;
  register int a0 __asm__("a0") = how;
  register const void *a1 __asm__("a1") = set;
  register void *a2 __asm__("a2") = oldset;
  register long a3 asm("a3") = 8;
  register uint32_t a7 __asm__("a7") = 135;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a2), "r"(a3), "r"(a7)
                       : "memory");
  return ret;
}

int __prctl(int option, unsigned long arg2, unsigned long arg3,
            unsigned long arg4, unsigned long arg5) {
  int ret;
  register int a0 __asm__("a0") = option;
  register unsigned long a1 __asm__("a1") = arg2;
  register unsigned long a2 __asm__("a2") = arg3;
  register unsigned long a3 __asm__("a3") = arg4;
  register unsigned long a4 __asm__("a4") = arg5;
  register uint32_t a7 __asm__("a7") = 167;
  __asm__ __volatile__("ecall\n\t"
                       "mv %0, a0"
                       : "=r"(ret), "+r"(a0), "+r"(a1)
                       : "r"(a2), "r"(a3), "r"(a4), "r"(a7)
                       : "cc", "memory");
  return ret;
}
} // anonymous namespace

#endif