/*
 * Copyright (c) 2018 Google, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string.h>
#include <stdlib.h>

typedef enum {
  BCC_ARCH_PPC,
  BCC_ARCH_PPC_LE,
  BCC_ARCH_S390X,
  BCC_ARCH_ARM64,
  BCC_ARCH_MIPS,
  BCC_ARCH_RISCV64,
  BCC_ARCH_LOONGARCH,
  BCC_ARCH_X86
} bcc_arch_t;

typedef void *(*arch_callback_t)(bcc_arch_t arch, bool for_syscall);

static void *run_arch_callback(arch_callback_t fn, bool for_syscall = false)
{
  const char *archenv = getenv("ARCH");

  /* If ARCH is not set, detect from local arch clang is running on */
  if (!archenv) {
#if defined(__powerpc64__)
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    return fn(BCC_ARCH_PPC_LE, for_syscall);
#else
    return fn(BCC_ARCH_PPC, for_syscall);
#endif
#elif defined(__s390x__)
    return fn(BCC_ARCH_S390X, for_syscall);
#elif defined(__aarch64__)
    return fn(BCC_ARCH_ARM64, for_syscall);
#elif defined(__mips__)
    return fn(BCC_ARCH_MIPS, for_syscall);
#elif defined(__riscv) && (__riscv_xlen == 64)
    return fn(BCC_ARCH_RISCV64, for_syscall);
#elif defined(__loongarch__)
    return fn(BCC_ARCH_LOONGARCH, for_syscall);
#else
    return fn(BCC_ARCH_X86, for_syscall);
#endif
  }

  /* Otherwise read it from ARCH */
  if (!strcmp(archenv, "powerpc")) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    return fn(BCC_ARCH_PPC_LE, for_syscall);
#else
    return fn(BCC_ARCH_PPC, for_syscall);
#endif
  } else if (!strcmp(archenv, "s390x")) {
    return fn(BCC_ARCH_S390X, for_syscall);
  } else if (!strcmp(archenv, "arm64")) {
    return fn(BCC_ARCH_ARM64, for_syscall);
  } else if (!strcmp(archenv, "mips")) {
    return fn(BCC_ARCH_MIPS, for_syscall);
  } else if (!strcmp(archenv, "riscv64")) {
    return fn(BCC_ARCH_RISCV64, for_syscall);
  } else if (!strcmp(archenv, "loongarch")) {
    return fn(BCC_ARCH_LOONGARCH, for_syscall);
  } else {
    return fn(BCC_ARCH_X86, for_syscall);
  }
}
