if (!user_set_comm_handle) { MPI_Comm_free(&MPI_COMM_HANDLE); }

#include <communicator_quda.h>

#define MPI_CHECK(mpi_call)                                                                                \
  do {                                                                                \
    int status = mpi_call;                                                                                \
    if (status != MPI_SUCCESS) {                                                                                \
      char err_string[128];                                                                                \
      int err_len;                                                                                \
      MPI_Error_string(status, err_string, &err_len);                                                                  \
      err_string[127] = '\0';                                                                                \
      errorQuda("(MPI) %s", err_string);                                                                               \
    }                                                                                \
  } while (0)

struct MsgHandle_s {
  /**
     The persistant MPI communicator handle that is created with
     MPI_Send_init / MPI_Recv_init.
   */
  MPI_Request request;

  /**
     To create a strided communicator, a MPI_Vector datatype has to be
     created.  This is where it is stored.
   */
  MPI_Datatype datatype;

  /**
     Whether a custom datatype has been created or not.  Used to
     determine whether we need to free the datatype or not.
   */
  bool custom;
};

Communicator::Communicator(int nDim, const int *commDims, QudaCommsMap rank_from_coords, void *map_data,
                           bool user_set_comm_handle_, void *user_comm)
{
  user_set_comm_handle = user_set_comm_handle_;

  int initialized;
  MPI_CHECK(MPI_Initialized(&initialized));

  if (!initialized) { assert(false); }

  if (user_set_comm_handle) {
    MPI_COMM_HANDLE = *((MPI_Comm *)user_comm);
  } else {
    MPI_Comm_dup(MPI_COMM_WORLD, &MPI_COMM_HANDLE);
  }

  comm_init(nDim, commDims, rank_from_coords, map_data);
  globalReduce.push(true);
}

Communicator::Communicator(Communicator &other, const int *comm_split) : globalReduce(other.globalReduce)
{
  user_set_comm_handle = false;

  constexpr int nDim = 4;

  quda::CommKey comm_dims_split;

  quda::CommKey comm_key_split;
  quda::CommKey comm_color_split;

  for (int d = 0; d < nDim; d++) {
    assert(other.comm_dim(d) % comm_split[d] == 0);
    comm_dims_split[d] = other.comm_dim(d) / comm_split[d];
    comm_key_split[d] = other.comm_coord(d) % comm_dims_split[d];
    comm_color_split[d] = other.comm_coord(d) / comm_dims_split[d];
  }

  int key = index(nDim, comm_dims_split.data(), comm_key_split.data());
  int color = index(nDim, comm_split, comm_color_split.data());

  MPI_CHECK(MPI_Comm_split(other.MPI_COMM_HANDLE, color, key, &MPI_COMM_HANDLE));
  int my_rank_;
  MPI_CHECK(MPI_Comm_rank(MPI_COMM_HANDLE, &my_rank_));

  QudaCommsMap func = lex_rank_from_coords_dim_t;
  comm_init(nDim, comm_dims_split.data(), func, comm_dims_split.data());
}

Communicator::~Communicator()
{
  comm_finalize();

}

void Communicator::comm_gather_hostname(char *hostname_recv_buf)
{
  // determine which GPU this rank will use
  char *hostname = comm_hostname();
  MPI_CHECK(MPI_Allgather(hostname, 128, MPI_CHAR, hostname_recv_buf, 128, MPI_CHAR, MPI_COMM_HANDLE));
}

void Communicator::comm_gather_gpuid(int *gpuid_recv_buf)
{
  int gpuid = comm_gpuid();
  MPI_CHECK(MPI_Allgather(&gpuid, 1, MPI_INT, gpuid_recv_buf, 1, MPI_INT, MPI_COMM_HANDLE));
}

void Communicator::comm_init(int ndim, const int *dims, QudaCommsMap rank_from_coords, void *map_data)
{
  int initialized;
  MPI_CHECK(MPI_Initialized(&initialized));

  if (!initialized) { errorQuda("MPI has not been initialized"); }

  MPI_CHECK(MPI_Comm_rank(MPI_COMM_HANDLE, &rank));
  MPI_CHECK(MPI_Comm_size(MPI_COMM_HANDLE, &size));

  int grid_size = 1;
  for (int i = 0; i < ndim; i++) { grid_size *= dims[i]; }
  if (grid_size != size) {
    errorQuda("Communication grid size declared via initCommsGridQuda() does not match"
              " total number of MPI ranks (%d != %d)",
              grid_size, size);
  }

  comm_init_common(ndim, dims, rank_from_coords, map_data);
}

int Communicator::comm_rank(void) { return rank; }

size_t Communicator::comm_size(void) { return size; }

/**
 * Declare a message handle for sending `nbytes` to the `rank` with `tag`.
 */
MsgHandle *Communicator::comm_declare_send_rank(void *buffer, int rank, int tag, size_t nbytes)
{
  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Send_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for receiving `nbytes` from the `rank` with `tag`.
 */
MsgHandle *Communicator::comm_declare_recv_rank(void *buffer, int rank, int tag, size_t nbytes)
{
  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Recv_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for sending to a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_send_displaced(void *buffer, const int displacement[], size_t nbytes)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement + displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Send_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for receiving from a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_receive_displaced(void *buffer, const int displacement[], size_t nbytes)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement - displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Recv_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for sending to a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_strided_send_displaced(void *buffer, const int displacement[], size_t blksize,
                                                             int nblocks, size_t stride)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement + displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));

  // create a new strided MPI type
  MPI_CHECK(MPI_Type_vector(nblocks, blksize, stride, MPI_BYTE, &(mh->datatype)));
  MPI_CHECK(MPI_Type_commit(&(mh->datatype)));
  mh->custom = true;

  MPI_CHECK(MPI_Send_init(buffer, 1, mh->datatype, rank, tag, MPI_COMM_HANDLE, &(mh->request)));

  return mh;
}

/**
 * Declare a message handle for receiving from a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_strided_receive_displaced(void *buffer, const int displacement[], size_t blksize,
                                                                int nblocks, size_t stride)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement - displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));

  // create a new strided MPI type
  MPI_CHECK(MPI_Type_vector(nblocks, blksize, stride, MPI_BYTE, &(mh->datatype)));
  MPI_CHECK(MPI_Type_commit(&(mh->datatype)));
  mh->custom = true;

  MPI_CHECK(MPI_Recv_init(buffer, 1, mh->datatype, rank, tag, MPI_COMM_HANDLE, &(mh->request)));

  return mh;
}

void Communicator::comm_free(MsgHandle *&mh)
{
  MPI_CHECK(MPI_Request_free(&(mh->request)));
  if (mh->custom) MPI_CHECK(MPI_Type_free(&(mh->datatype)));
  host_free(mh);
  mh = nullptr;
}

void Communicator::comm_start(MsgHandle *mh) { MPI_CHECK(MPI_Start(&(mh->request))); }

void Communicator::comm_wait(MsgHandle *mh) { MPI_CHECK(MPI_Wait(&(mh->request), MPI_STATUS_IGNORE)); }

int Communicator::comm_query(MsgHandle *mh)
{
  int query;
  MPI_CHECK(MPI_Test(&(mh->request), &query, MPI_STATUS_IGNORE));

  return query;
}

void Communicator::comm_allreduce(double *data)
{
  if (!comm_deterministic_reduce()) {
    double recvbuf;
    MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_HANDLE));
    *data = recvbuf;
  } else {
    const size_t n = comm_size();
    double *recv_buf = (double *)safe_malloc(n * sizeof(double));
    MPI_CHECK(MPI_Allgather(data, 1, MPI_DOUBLE, recv_buf, 1, MPI_DOUBLE, MPI_COMM_HANDLE));
    *data = deterministic_reduce(recv_buf, n);
    host_free(recv_buf);
  }
}

void Communicator::comm_allreduce_max(double *data)
{
  double recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_HANDLE));
  *data = recvbuf;
}

void Communicator::comm_allreduce_min(double *data)
{
  double recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_HANDLE));
  *data = recvbuf;
}

void Communicator::comm_allreduce_array(double *data, size_t size)
{
  if (!comm_deterministic_reduce()) {
    double *recvbuf = new double[size];
    MPI_CHECK(MPI_Allreduce(data, recvbuf, size, MPI_DOUBLE, MPI_SUM, MPI_COMM_HANDLE));
    memcpy(data, recvbuf, size * sizeof(double));
    delete[] recvbuf;
  } else {
    size_t n = comm_size();
    double *recv_buf = new double[size * n];
    MPI_CHECK(MPI_Allgather(data, size, MPI_DOUBLE, recv_buf, size, MPI_DOUBLE, MPI_COMM_HANDLE));

    double *recv_trans = new double[size * n];
    for (size_t i = 0; i < n; i++) {
      for (size_t j = 0; j < size; j++) { recv_trans[j * n + i] = recv_buf[i * size + j]; }
    }

    for (size_t i = 0; i < size; i++) { data[i] = deterministic_reduce(recv_trans + i * n, n); }

    delete[] recv_buf;
    delete[] recv_trans;
  }
}

void Communicator::comm_allreduce_max_array(double *data, size_t size)
{
  double *recvbuf = new double[size];
  MPI_CHECK(MPI_Allreduce(data, recvbuf, size, MPI_DOUBLE, MPI_MAX, MPI_COMM_HANDLE));
  memcpy(data, recvbuf, size * sizeof(double));
  delete[] recvbuf;
}

void Communicator::comm_allreduce_min_array(double *data, size_t size)
{
  double *recvbuf = new double[size];
  MPI_CHECK(MPI_Allreduce(data, recvbuf, size, MPI_DOUBLE, MPI_MIN, MPI_COMM_HANDLE));
  memcpy(data, recvbuf, size * sizeof(double));
  delete[] recvbuf;
}

void Communicator::comm_allreduce_int(int *data)
{
  int recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_INT, MPI_SUM, MPI_COMM_HANDLE));
  *data = recvbuf;
}

void Communicator::comm_allreduce_xor(uint64_t *data)
{
  if (sizeof(uint64_t) != sizeof(unsigned long)) errorQuda("unsigned long is not 64-bit");
  uint64_t recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_UNSIGNED_LONG, MPI_BXOR, MPI_COMM_HANDLE));
  *data = recvbuf;
}

/**  broadcast from rank 0 */
void Communicator::comm_broadcast(void *data, size_t nbytes)
{
  MPI_CHECK(MPI_Bcast(data, (int)nbytes, MPI_BYTE, 0, MPI_COMM_HANDLE));
}

void Communicator::comm_barrier(void) { MPI_CHECK(MPI_Barrier(MPI_COMM_HANDLE)); }

void Communicator::comm_abort_(int status) { MPI_Abort(MPI_COMM_WORLD, status); }

int Communicator::comm_rank_global()
{
  int rank;
  MPI_CHECK(MPI_Comm_rank(MPI_COMM_WORLD, &rank));
  return rank;
}


for (int i = 0; i < ndim; i++) { grid_size *= dims[i]; }

#include <communicator_quda.h>

#define MPI_CHECK(mpi_call)                                                                                \
  do {                                                                                \
    int status = mpi_call;                                                                                \
    if (status != MPI_SUCCESS) {                                                                                \
      char err_string[128];                                                                                \
      int err_len;                                                                                \
      MPI_Error_string(status, err_string, &err_len);                                                                  \
      err_string[127] = '\0';                                                                                \
      errorQuda("(MPI) %s", err_string);                                                                               \
    }                                                                                \
  } while (0)

struct MsgHandle_s {
  /**
     The persistant MPI communicator handle that is created with
     MPI_Send_init / MPI_Recv_init.
   */
  MPI_Request request;

  /**
     To create a strided communicator, a MPI_Vector datatype has to be
     created.  This is where it is stored.
   */
  MPI_Datatype datatype;

  /**
     Whether a custom datatype has been created or not.  Used to
     determine whether we need to free the datatype or not.
   */
  bool custom;
};

Communicator::Communicator(int nDim, const int *commDims, QudaCommsMap rank_from_coords, void *map_data,
                           bool user_set_comm_handle_, void *user_comm)
{
  user_set_comm_handle = user_set_comm_handle_;

  int initialized;
  MPI_CHECK(MPI_Initialized(&initialized));

  if (!initialized) { assert(false); }

  if (user_set_comm_handle) {
    MPI_COMM_HANDLE = *((MPI_Comm *)user_comm);
  } else {
    MPI_Comm_dup(MPI_COMM_WORLD, &MPI_COMM_HANDLE);
  }

  comm_init(nDim, commDims, rank_from_coords, map_data);
  globalReduce.push(true);
}

Communicator::Communicator(Communicator &other, const int *comm_split) : globalReduce(other.globalReduce)
{
  user_set_comm_handle = false;

  constexpr int nDim = 4;

  quda::CommKey comm_dims_split;

  quda::CommKey comm_key_split;
  quda::CommKey comm_color_split;

  for (int d = 0; d < nDim; d++) {
    assert(other.comm_dim(d) % comm_split[d] == 0);
    comm_dims_split[d] = other.comm_dim(d) / comm_split[d];
    comm_key_split[d] = other.comm_coord(d) % comm_dims_split[d];
    comm_color_split[d] = other.comm_coord(d) / comm_dims_split[d];
  }

  int key = index(nDim, comm_dims_split.data(), comm_key_split.data());
  int color = index(nDim, comm_split, comm_color_split.data());

  MPI_CHECK(MPI_Comm_split(other.MPI_COMM_HANDLE, color, key, &MPI_COMM_HANDLE));
  int my_rank_;
  MPI_CHECK(MPI_Comm_rank(MPI_COMM_HANDLE, &my_rank_));

  QudaCommsMap func = lex_rank_from_coords_dim_t;
  comm_init(nDim, comm_dims_split.data(), func, comm_dims_split.data());
}

Communicator::~Communicator()
{
  comm_finalize();
  if (!user_set_comm_handle) { MPI_Comm_free(&MPI_COMM_HANDLE); }
}

void Communicator::comm_gather_hostname(char *hostname_recv_buf)
{
  // determine which GPU this rank will use
  char *hostname = comm_hostname();
  MPI_CHECK(MPI_Allgather(hostname, 128, MPI_CHAR, hostname_recv_buf, 128, MPI_CHAR, MPI_COMM_HANDLE));
}

void Communicator::comm_gather_gpuid(int *gpuid_recv_buf)
{
  int gpuid = comm_gpuid();
  MPI_CHECK(MPI_Allgather(&gpuid, 1, MPI_INT, gpuid_recv_buf, 1, MPI_INT, MPI_COMM_HANDLE));
}

void Communicator::comm_init(int ndim, const int *dims, QudaCommsMap rank_from_coords, void *map_data)
{
  int initialized;
  MPI_CHECK(MPI_Initialized(&initialized));

  if (!initialized) { errorQuda("MPI has not been initialized"); }

  MPI_CHECK(MPI_Comm_rank(MPI_COMM_HANDLE, &rank));
  MPI_CHECK(MPI_Comm_size(MPI_COMM_HANDLE, &size));

  int grid_size = 1;

  if (grid_size != size) {
    errorQuda("Communication grid size declared via initCommsGridQuda() does not match"
              " total number of MPI ranks (%d != %d)",
              grid_size, size);
  }

  comm_init_common(ndim, dims, rank_from_coords, map_data);
}

int Communicator::comm_rank(void) { return rank; }

size_t Communicator::comm_size(void) { return size; }

/**
 * Declare a message handle for sending `nbytes` to the `rank` with `tag`.
 */
MsgHandle *Communicator::comm_declare_send_rank(void *buffer, int rank, int tag, size_t nbytes)
{
  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Send_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for receiving `nbytes` from the `rank` with `tag`.
 */
MsgHandle *Communicator::comm_declare_recv_rank(void *buffer, int rank, int tag, size_t nbytes)
{
  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Recv_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for sending to a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_send_displaced(void *buffer, const int displacement[], size_t nbytes)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement + displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Send_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for receiving from a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_receive_displaced(void *buffer, const int displacement[], size_t nbytes)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement - displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Recv_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for sending to a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_strided_send_displaced(void *buffer, const int displacement[], size_t blksize,
                                                             int nblocks, size_t stride)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement + displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));

  // create a new strided MPI type
  MPI_CHECK(MPI_Type_vector(nblocks, blksize, stride, MPI_BYTE, &(mh->datatype)));
  MPI_CHECK(MPI_Type_commit(&(mh->datatype)));
  mh->custom = true;

  MPI_CHECK(MPI_Send_init(buffer, 1, mh->datatype, rank, tag, MPI_COMM_HANDLE, &(mh->request)));

  return mh;
}

/**
 * Declare a message handle for receiving from a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_strided_receive_displaced(void *buffer, const int displacement[], size_t blksize,
                                                                int nblocks, size_t stride)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement - displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));

  // create a new strided MPI type
  MPI_CHECK(MPI_Type_vector(nblocks, blksize, stride, MPI_BYTE, &(mh->datatype)));
  MPI_CHECK(MPI_Type_commit(&(mh->datatype)));
  mh->custom = true;

  MPI_CHECK(MPI_Recv_init(buffer, 1, mh->datatype, rank, tag, MPI_COMM_HANDLE, &(mh->request)));

  return mh;
}

void Communicator::comm_free(MsgHandle *&mh)
{
  MPI_CHECK(MPI_Request_free(&(mh->request)));
  if (mh->custom) MPI_CHECK(MPI_Type_free(&(mh->datatype)));
  host_free(mh);
  mh = nullptr;
}

void Communicator::comm_start(MsgHandle *mh) { MPI_CHECK(MPI_Start(&(mh->request))); }

void Communicator::comm_wait(MsgHandle *mh) { MPI_CHECK(MPI_Wait(&(mh->request), MPI_STATUS_IGNORE)); }

int Communicator::comm_query(MsgHandle *mh)
{
  int query;
  MPI_CHECK(MPI_Test(&(mh->request), &query, MPI_STATUS_IGNORE));

  return query;
}

void Communicator::comm_allreduce(double *data)
{
  if (!comm_deterministic_reduce()) {
    double recvbuf;
    MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_HANDLE));
    *data = recvbuf;
  } else {
    const size_t n = comm_size();
    double *recv_buf = (double *)safe_malloc(n * sizeof(double));
    MPI_CHECK(MPI_Allgather(data, 1, MPI_DOUBLE, recv_buf, 1, MPI_DOUBLE, MPI_COMM_HANDLE));
    *data = deterministic_reduce(recv_buf, n);
    host_free(recv_buf);
  }
}

void Communicator::comm_allreduce_max(double *data)
{
  double recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_HANDLE));
  *data = recvbuf;
}

void Communicator::comm_allreduce_min(double *data)
{
  double recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_HANDLE));
  *data = recvbuf;
}

void Communicator::comm_allreduce_array(double *data, size_t size)
{
  if (!comm_deterministic_reduce()) {
    double *recvbuf = new double[size];
    MPI_CHECK(MPI_Allreduce(data, recvbuf, size, MPI_DOUBLE, MPI_SUM, MPI_COMM_HANDLE));
    memcpy(data, recvbuf, size * sizeof(double));
    delete[] recvbuf;
  } else {
    size_t n = comm_size();
    double *recv_buf = new double[size * n];
    MPI_CHECK(MPI_Allgather(data, size, MPI_DOUBLE, recv_buf, size, MPI_DOUBLE, MPI_COMM_HANDLE));

    double *recv_trans = new double[size * n];
    for (size_t i = 0; i < n; i++) {
      for (size_t j = 0; j < size; j++) { recv_trans[j * n + i] = recv_buf[i * size + j]; }
    }

    for (size_t i = 0; i < size; i++) { data[i] = deterministic_reduce(recv_trans + i * n, n); }

    delete[] recv_buf;
    delete[] recv_trans;
  }
}

void Communicator::comm_allreduce_max_array(double *data, size_t size)
{
  double *recvbuf = new double[size];
  MPI_CHECK(MPI_Allreduce(data, recvbuf, size, MPI_DOUBLE, MPI_MAX, MPI_COMM_HANDLE));
  memcpy(data, recvbuf, size * sizeof(double));
  delete[] recvbuf;
}

void Communicator::comm_allreduce_min_array(double *data, size_t size)
{
  double *recvbuf = new double[size];
  MPI_CHECK(MPI_Allreduce(data, recvbuf, size, MPI_DOUBLE, MPI_MIN, MPI_COMM_HANDLE));
  memcpy(data, recvbuf, size * sizeof(double));
  delete[] recvbuf;
}

void Communicator::comm_allreduce_int(int *data)
{
  int recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_INT, MPI_SUM, MPI_COMM_HANDLE));
  *data = recvbuf;
}

void Communicator::comm_allreduce_xor(uint64_t *data)
{
  if (sizeof(uint64_t) != sizeof(unsigned long)) errorQuda("unsigned long is not 64-bit");
  uint64_t recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_UNSIGNED_LONG, MPI_BXOR, MPI_COMM_HANDLE));
  *data = recvbuf;
}

/**  broadcast from rank 0 */
void Communicator::comm_broadcast(void *data, size_t nbytes)
{
  MPI_CHECK(MPI_Bcast(data, (int)nbytes, MPI_BYTE, 0, MPI_COMM_HANDLE));
}

void Communicator::comm_barrier(void) { MPI_CHECK(MPI_Barrier(MPI_COMM_HANDLE)); }

void Communicator::comm_abort_(int status) { MPI_Abort(MPI_COMM_WORLD, status); }

int Communicator::comm_rank_global()
{
  int rank;
  MPI_CHECK(MPI_Comm_rank(MPI_COMM_WORLD, &rank));
  return rank;
}


#include <communicator_quda.h>

#define MPI_CHECK(mpi_call)                                                                                \
  do {                                                                                \
    int status = mpi_call;                                                                                \
    if (status != MPI_SUCCESS) {                                                                                \
      char err_string[128];                                                                                \
      int err_len;                                                                                \
      MPI_Error_string(status, err_string, &err_len);                                                                  \
      err_string[127] = '\0';                                                                                \
      errorQuda("(MPI) %s", err_string);                                                                               \
    }                                                                                \
  } while (0)

struct MsgHandle_s {
  /**
     The persistant MPI communicator handle that is created with
     MPI_Send_init / MPI_Recv_init.
   */
  MPI_Request request;

  /**
     To create a strided communicator, a MPI_Vector datatype has to be
     created.  This is where it is stored.
   */
  MPI_Datatype datatype;

  /**
     Whether a custom datatype has been created or not.  Used to
     determine whether we need to free the datatype or not.
   */
  bool custom;
};

Communicator::Communicator(int nDim, const int *commDims, QudaCommsMap rank_from_coords, void *map_data,
                           bool user_set_comm_handle_, void *user_comm)
{
  user_set_comm_handle = user_set_comm_handle_;

  int initialized;
  MPI_CHECK(MPI_Initialized(&initialized));

  if (!initialized) { assert(false); }

  if (user_set_comm_handle) {
    MPI_COMM_HANDLE = *((MPI_Comm *)user_comm);
  } else {
    MPI_Comm_dup(MPI_COMM_WORLD, &MPI_COMM_HANDLE);
  }

  comm_init(nDim, commDims, rank_from_coords, map_data);
  globalReduce.push(true);
}

Communicator::Communicator(Communicator &other, const int *comm_split) : globalReduce(other.globalReduce)
{
  user_set_comm_handle = false;

  constexpr int nDim = 4;

  quda::CommKey comm_dims_split;

  quda::CommKey comm_key_split;
  quda::CommKey comm_color_split;

  for (int d = 0; d < nDim; d++) {
    assert(other.comm_dim(d) % comm_split[d] == 0);
    comm_dims_split[d] = other.comm_dim(d) / comm_split[d];
    comm_key_split[d] = other.comm_coord(d) % comm_dims_split[d];
    comm_color_split[d] = other.comm_coord(d) / comm_dims_split[d];
  }

  int key = index(nDim, comm_dims_split.data(), comm_key_split.data());
  int color = index(nDim, comm_split, comm_color_split.data());

  MPI_CHECK(MPI_Comm_split(other.MPI_COMM_HANDLE, color, key, &MPI_COMM_HANDLE));
  int my_rank_;
  MPI_CHECK(MPI_Comm_rank(MPI_COMM_HANDLE, &my_rank_));

  QudaCommsMap func = lex_rank_from_coords_dim_t;
  comm_init(nDim, comm_dims_split.data(), func, comm_dims_split.data());
}

Communicator::~Communicator()
{
  comm_finalize();
  if (!user_set_comm_handle) { MPI_Comm_free(&MPI_COMM_HANDLE); }
}

void Communicator::comm_gather_hostname(char *hostname_recv_buf)
{
  // determine which GPU this rank will use
  char *hostname = comm_hostname();
  MPI_CHECK(MPI_Allgather(hostname, 128, MPI_CHAR, hostname_recv_buf, 128, MPI_CHAR, MPI_COMM_HANDLE));
}

void Communicator::comm_gather_gpuid(int *gpuid_recv_buf)
{
  int gpuid = comm_gpuid();
  MPI_CHECK(MPI_Allgather(&gpuid, 1, MPI_INT, gpuid_recv_buf, 1, MPI_INT, MPI_COMM_HANDLE));
}

void Communicator::comm_init(int ndim, const int *dims, QudaCommsMap rank_from_coords, void *map_data)
{
  int initialized;
  MPI_CHECK(MPI_Initialized(&initialized));

  if (!initialized) { errorQuda("MPI has not been initialized"); }

  MPI_CHECK(MPI_Comm_rank(MPI_COMM_HANDLE, &rank));
  MPI_CHECK(MPI_Comm_size(MPI_COMM_HANDLE, &size));

  int grid_size = 1;
  for (int i = 0; i < ndim; i++) { grid_size *= dims[i]; }
  if (grid_size != size) {
    errorQuda("Communication grid size declared via initCommsGridQuda() does not match"
              " total number of MPI ranks (%d != %d)",
              grid_size, size);
  }

  comm_init_common(ndim, dims, rank_from_coords, map_data);
}

int Communicator::comm_rank(void) { return rank; }

size_t Communicator::comm_size(void) { return size; }

/**
 * Declare a message handle for sending `nbytes` to the `rank` with `tag`.
 */
MsgHandle *Communicator::comm_declare_send_rank(void *buffer, int rank, int tag, size_t nbytes)
{
  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Send_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for receiving `nbytes` from the `rank` with `tag`.
 */
MsgHandle *Communicator::comm_declare_recv_rank(void *buffer, int rank, int tag, size_t nbytes)
{
  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Recv_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for sending to a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_send_displaced(void *buffer, const int displacement[], size_t nbytes)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement + displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Send_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for receiving from a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_receive_displaced(void *buffer, const int displacement[], size_t nbytes)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement - displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));
  MPI_CHECK(MPI_Recv_init(buffer, nbytes, MPI_BYTE, rank, tag, MPI_COMM_HANDLE, &(mh->request)));
  mh->custom = false;

  return mh;
}

/**
 * Declare a message handle for sending to a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_strided_send_displaced(void *buffer, const int displacement[], size_t blksize,
                                                             int nblocks, size_t stride)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement + displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));

  // create a new strided MPI type
  MPI_CHECK(MPI_Type_vector(nblocks, blksize, stride, MPI_BYTE, &(mh->datatype)));
  MPI_CHECK(MPI_Type_commit(&(mh->datatype)));
  mh->custom = true;

  MPI_CHECK(MPI_Send_init(buffer, 1, mh->datatype, rank, tag, MPI_COMM_HANDLE, &(mh->request)));

  return mh;
}

/**
 * Declare a message handle for receiving from a node displaced in (x,y,z,t) according to "displacement"
 */
MsgHandle *Communicator::comm_declare_strided_receive_displaced(void *buffer, const int displacement[], size_t blksize,
                                                                int nblocks, size_t stride)
{
  Topology *topo = comm_default_topology();
  int ndim = comm_ndim(topo);
  check_displacement(displacement, ndim);

  int rank = comm_rank_displaced(topo, displacement);

  int tag = 0;
  for (int i = ndim - 1; i >= 0; i--) tag = tag * 4 * max_displacement - displacement[i] + max_displacement;
  tag = tag >= 0 ? tag : 2 * pow(4 * max_displacement, ndim) + tag;

  MsgHandle *mh = (MsgHandle *)safe_malloc(sizeof(MsgHandle));

  // create a new strided MPI type
  MPI_CHECK(MPI_Type_vector(nblocks, blksize, stride, MPI_BYTE, &(mh->datatype)));
  MPI_CHECK(MPI_Type_commit(&(mh->datatype)));
  mh->custom = true;

  MPI_CHECK(MPI_Recv_init(buffer, 1, mh->datatype, rank, tag, MPI_COMM_HANDLE, &(mh->request)));

  return mh;
}

void Communicator::comm_free(MsgHandle *&mh)
{
  MPI_CHECK(MPI_Request_free(&(mh->request)));
  if (mh->custom) MPI_CHECK(MPI_Type_free(&(mh->datatype)));
  host_free(mh);
  mh = nullptr;
}

void Communicator::comm_start(MsgHandle *mh) { MPI_CHECK(MPI_Start(&(mh->request))); }

void Communicator::comm_wait(MsgHandle *mh) { MPI_CHECK(MPI_Wait(&(mh->request), MPI_STATUS_IGNORE)); }

int Communicator::comm_query(MsgHandle *mh)
{
  int query;
  MPI_CHECK(MPI_Test(&(mh->request), &query, MPI_STATUS_IGNORE));

  return query;
}

void Communicator::comm_allreduce(double *data)

{
  if (!comm_deterministic_reduce()) {
    double recvbuf;
    MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_HANDLE));
    *data = recvbuf;
  } else {
    const size_t n = comm_size();
    double *recv_buf = (double *)safe_malloc(n * sizeof(double));
    MPI_CHECK(MPI_Allgather(data, 1, MPI_DOUBLE, recv_buf, 1, MPI_DOUBLE, MPI_COMM_HANDLE));
    *data = deterministic_reduce(recv_buf, n);
    host_free(recv_buf);
  }
}

void Communicator::comm_allreduce_max(double *data)
{
  double recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_HANDLE));
  *data = recvbuf;
}

void Communicator::comm_allreduce_min(double *data)
{
  double recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_HANDLE));
  *data = recvbuf;
}

void Communicator::comm_allreduce_array(double *data, size_t size)
{
  if (!comm_deterministic_reduce()) {
    double *recvbuf = new double[size];
    MPI_CHECK(MPI_Allreduce(data, recvbuf, size, MPI_DOUBLE, MPI_SUM, MPI_COMM_HANDLE));
    memcpy(data, recvbuf, size * sizeof(double));
    delete[] recvbuf;
  } else {
    size_t n = comm_size();
    double *recv_buf = new double[size * n];
    MPI_CHECK(MPI_Allgather(data, size, MPI_DOUBLE, recv_buf, size, MPI_DOUBLE, MPI_COMM_HANDLE));

    double *recv_trans = new double[size * n];
    for (size_t i = 0; i < n; i++) {
      for (size_t j = 0; j < size; j++) { recv_trans[j * n + i] = recv_buf[i * size + j]; }
    }

    for (size_t i = 0; i < size; i++) { data[i] = deterministic_reduce(recv_trans + i * n, n); }

    delete[] recv_buf;
    delete[] recv_trans;
  }
}

void Communicator::comm_allreduce_max_array(double *data, size_t size)
{
  double *recvbuf = new double[size];
  MPI_CHECK(MPI_Allreduce(data, recvbuf, size, MPI_DOUBLE, MPI_MAX, MPI_COMM_HANDLE));
  memcpy(data, recvbuf, size * sizeof(double));
  delete[] recvbuf;
}

void Communicator::comm_allreduce_min_array(double *data, size_t size)
{
  double *recvbuf = new double[size];
  MPI_CHECK(MPI_Allreduce(data, recvbuf, size, MPI_DOUBLE, MPI_MIN, MPI_COMM_HANDLE));
  memcpy(data, recvbuf, size * sizeof(double));
  delete[] recvbuf;
}

void Communicator::comm_allreduce_int(int *data)
{
  int recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_INT, MPI_SUM, MPI_COMM_HANDLE));
  *data = recvbuf;
}

void Communicator::comm_allreduce_xor(uint64_t *data)
{
  if (sizeof(uint64_t) != sizeof(unsigned long)) errorQuda("unsigned long is not 64-bit");
  uint64_t recvbuf;
  MPI_CHECK(MPI_Allreduce(data, &recvbuf, 1, MPI_UNSIGNED_LONG, MPI_BXOR, MPI_COMM_HANDLE));
  *data = recvbuf;
}

/**  broadcast from rank 0 */
void Communicator::comm_broadcast(void *data, size_t nbytes)
{
  MPI_CHECK(MPI_Bcast(data, (int)nbytes, MPI_BYTE, 0, MPI_COMM_HANDLE));
}

void Communicator::comm_barrier(void) { MPI_CHECK(MPI_Barrier(MPI_COMM_HANDLE)); }

void Communicator::comm_abort_(int status) { MPI_Abort(MPI_COMM_WORLD, status); }

int Communicator::comm_rank_global()
{
  int rank;
  MPI_CHECK(MPI_Comm_rank(MPI_COMM_WORLD, &rank));
  return rank;
}


bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override

//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;

{
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }
}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


{
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }
}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }
}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }

//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { ret

 };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }
}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }
}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }
}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }

//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

static unsigned getFixupKindNumBytes(unsigned Kind) {

}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }
}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }

//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {

}

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }
}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}