/* This file is a part of MIR project.
   Copyright (C) 2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/

/* We don't use TOC.  So r2 is not necessary for the generated code.  */
static void fancy_abort (int code) {
  if (!code) abort ();
}
#undef gen_assert
#define gen_assert(c) fancy_abort (c)

#include <limits.h>

#define HREG_EL(h) h##_HARD_REG
#define REP_SEP ,
enum {
  REP8 (HREG_EL, R0, R1, R2, R3, R4, R5, R6, R7),
  REP8 (HREG_EL, R8, R9, R10, R11, R12, R13, R14, R15),
  REP8 (HREG_EL, R16, R17, R18, R19, R20, R21, R22, R23),
  REP8 (HREG_EL, R24, R25, R26, R27, R28, R29, R30, R31),
  REP8 (HREG_EL, F0, F1, F2, F3, F4, F5, F6, F7),
  REP8 (HREG_EL, F8, F9, F10, F11, F12, F13, F14, F15),
  REP8 (HREG_EL, F16, F17, F18, F19, F20, F21, F22, F23),
  REP8 (HREG_EL, F24, F25, F26, F27, F28, F29, F30, F31),
  HREG_EL (LR),
};
#undef REP_SEP

static const MIR_reg_t MAX_HARD_REG = LR_HARD_REG;
static const MIR_reg_t SP_HARD_REG = R1_HARD_REG;
static const MIR_reg_t FP_HARD_REG = R31_HARD_REG;
static const MIR_reg_t LINK_HARD_REG = LR_HARD_REG;

static int target_locs_num (MIR_reg_t loc, MIR_type_t type) {
  return /*loc > MAX_HARD_REG && */ type == MIR_T_LD ? 2 : 1;
}

static inline MIR_reg_t target_nth_loc (MIR_reg_t loc, MIR_type_t type, int n) { return loc + n; }

/* Hard regs not used in machinized code, preferably call used ones. */
const MIR_reg_t TEMP_INT_HARD_REG1 = R11_HARD_REG, TEMP_INT_HARD_REG2 = R12_HARD_REG;
const MIR_reg_t TEMP_FLOAT_HARD_REG1 = F11_HARD_REG, TEMP_FLOAT_HARD_REG2 = F12_HARD_REG;
const MIR_reg_t TEMP_DOUBLE_HARD_REG1 = F11_HARD_REG, TEMP_DOUBLE_HARD_REG2 = F12_HARD_REG;
const MIR_reg_t TEMP_LDOUBLE_HARD_REG1 = F11_HARD_REG;  //???
const MIR_reg_t TEMP_LDOUBLE_HARD_REG2 = F12_HARD_REG;

static inline int target_hard_reg_type_ok_p (MIR_reg_t hard_reg, MIR_type_t type) {
  assert (hard_reg <= MAX_HARD_REG);
  if (type == MIR_T_LD) return FALSE;
  return MIR_fp_type_p (type) ? F0_HARD_REG <= hard_reg && hard_reg <= F31_HARD_REG
                              : hard_reg < F0_HARD_REG;
}

static inline int target_fixed_hard_reg_p (MIR_reg_t hard_reg) {
  assert (hard_reg <= MAX_HARD_REG);
  return (hard_reg == FP_HARD_REG || hard_reg == SP_HARD_REG
          || hard_reg == LR_HARD_REG
          /* don't bother to allocate R0 as it has special meaning for base reg and of addi: */
          || hard_reg == R0_HARD_REG || hard_reg == R2_HARD_REG || hard_reg == R13_HARD_REG
          || hard_reg == TEMP_INT_HARD_REG1 || hard_reg == TEMP_INT_HARD_REG2
          || hard_reg == TEMP_FLOAT_HARD_REG1 || hard_reg == TEMP_FLOAT_HARD_REG2
          || hard_reg == TEMP_DOUBLE_HARD_REG1 || hard_reg == TEMP_DOUBLE_HARD_REG2
          || hard_reg == TEMP_LDOUBLE_HARD_REG1 || hard_reg == TEMP_LDOUBLE_HARD_REG2);
}

static inline int target_call_used_hard_reg_p (MIR_reg_t hard_reg, MIR_type_t type) {
  assert (hard_reg <= MAX_HARD_REG);
  return ((R0_HARD_REG <= hard_reg && hard_reg <= R13_HARD_REG)
          || (F0_HARD_REG <= hard_reg && hard_reg <= F13_HARD_REG));
}

/* Stack layout (r1(sp) refers to the last reserved stack slot
   address) from higher address to lower address memory:

        +-> Back chain                                    BE                 LE
        |   Floating point register save area             optional        optional
        |   General register save area                    optional        optional
        |   VRSAVE save word (32-bits)                      0              NA
        |   Alignment padding (4 or 12 bytes)
        |   Vector register save area (quadword aligned)    we don't have it
        |   Local variable space                          optional        optional
        |   Parameter save area  (for callees)            (SP + 48)       (SP + 32) optional
        |   TOC save area                                 (SP + 40)       (SP + 24)
        |   link editor doubleword (we don't use it)      (SP + 32)          NA
        |   compiler doubleword    (we don't use it)      (SP + 24)          NA
        |   LR save area (used by callee)                 (SP + 16)       (SP + 16)
        |   CR save area                                  (SP + 8)        (SP + 8)
SP,R31->+-- Back chain                                    (SP + 0)        (SP + 0)
            Alloca area (after that new 48 or 32 bytes header should be created with new values)

Originaly SP(r1) and FP (r31) are the same but r1 can be changed by alloca */

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define PPC64_STACK_HEADER_SIZE 32
#define PPC64_TOC_OFFSET 24
#define PPC64_FUNC_DESC_LEN 0
#else
#define PPC64_STACK_HEADER_SIZE 48
#define PPC64_TOC_OFFSET 40
#define PPC64_FUNC_DESC_LEN 24
#endif

/* ppc64 has 3-ops insns */
static const MIR_insn_code_t target_io_dup_op_insn_codes[] = {MIR_INSN_BOUND};

static MIR_insn_code_t get_ext_code (MIR_type_t type) {
  switch (type) {
  case MIR_T_I8: return MIR_EXT8;
  case MIR_T_U8: return MIR_UEXT8;
  case MIR_T_I16: return MIR_EXT16;
  case MIR_T_U16: return MIR_UEXT16;
  case MIR_T_I32: return MIR_EXT32;
  case MIR_T_U32: return MIR_UEXT32;
  default: return MIR_INVALID_INSN;
  }
}

DEF_VARR (int);
DEF_VARR (uint8_t);
DEF_VARR (uint64_t);

struct insn_pattern_info {
  int start, num;
};

typedef struct insn_pattern_info insn_pattern_info_t;
DEF_VARR (insn_pattern_info_t);

struct label_ref {
  int abs_addr_p, short_addr_p;
  size_t label_val_disp;
  MIR_label_t label;
};

typedef struct label_ref label_ref_t;
DEF_VARR (label_ref_t);

DEF_VARR (MIR_code_reloc_t);

struct target_ctx {
  unsigned char alloca_p, block_arg_func_p, leaf_p, switch_p;
  size_t param_save_area_size;
  VARR (int) * pattern_indexes;
  VARR (insn_pattern_info_t) * insn_pattern_info;
  VARR (uint8_t) * result_code;
  VARR (label_ref_t) * label_refs;
  VARR (uint64_t) * abs_address_locs;
  VARR (MIR_code_reloc_t) * relocs;
};

#define alloca_p gen_ctx->target_ctx->alloca_p
#define block_arg_func_p gen_ctx->target_ctx->block_arg_func_p
#define leaf_p gen_ctx->target_ctx->leaf_p
#define switch_p gen_ctx->target_ctx->switch_p
#define param_save_area_size gen_ctx->target_ctx->param_save_area_size
#define pattern_indexes gen_ctx->target_ctx->pattern_indexes
#define insn_pattern_info gen_ctx->target_ctx->insn_pattern_info
#define result_code gen_ctx->target_ctx->result_code
#define label_refs gen_ctx->target_ctx->label_refs
#define abs_address_locs gen_ctx->target_ctx->abs_address_locs
#define relocs gen_ctx->target_ctx->relocs

static void gen_mov (gen_ctx_t gen_ctx, MIR_insn_t anchor, MIR_insn_code_t code, MIR_op_t dst_op,
                     MIR_op_t src_op) {
  gen_add_insn_before (gen_ctx, anchor, MIR_new_insn (gen_ctx->ctx, code, dst_op, src_op));
}

static void mir_blk_mov (uint64_t *to, uint64_t *from, uint64_t nwords) {
  for (; nwords > 0; nwords--) *to++ = *from++;
}

static const char *BLK_MOV = "mir.blk_mov";
static const char *BLK_MOV_P = "mir.blk_mov.p";

static void gen_blk_mov (gen_ctx_t gen_ctx, MIR_insn_t anchor, size_t to_disp,
                         MIR_reg_t to_base_hard_reg, size_t from_disp, MIR_reg_t from_base_reg,
                         size_t qwords, int save_regs) {
  MIR_context_t ctx = gen_ctx->ctx;
  MIR_func_t func = curr_func_item->u.func;
  MIR_item_t proto_item, func_import_item;
  MIR_insn_t new_insn;
  MIR_op_t ops[5], freg_op, treg_op, treg_op2, treg_op3;

  treg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
  if (qwords <= 16) {
    for (; qwords > 0; qwords--, to_disp += 8, from_disp += 8) {
      gen_mov (gen_ctx, anchor, MIR_MOV, treg_op,
               MIR_new_mem_op (ctx, MIR_T_I64, from_disp, from_base_reg, 0, 1));
      gen_mov (gen_ctx, anchor, MIR_MOV,
               _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, to_disp, to_base_hard_reg,
                                         MIR_NON_HARD_REG, 1),
               treg_op);
    }
    return;
  }
  treg_op2 = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
  treg_op3 = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
  /* Save arg regs: */
  if (save_regs > 0) gen_mov (gen_ctx, anchor, MIR_MOV, treg_op, _MIR_new_hard_reg_op (ctx, 3));
  if (save_regs > 1) gen_mov (gen_ctx, anchor, MIR_MOV, treg_op2, _MIR_new_hard_reg_op (ctx, 4));
  if (save_regs > 2) gen_mov (gen_ctx, anchor, MIR_MOV, treg_op3, _MIR_new_hard_reg_op (ctx, 5));
  /* call blk move: */
  proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, BLK_MOV_P, 0, NULL, 3, MIR_T_I64,
                                   "to", MIR_T_I64, "from", MIR_T_I64, "nwords");
  func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, BLK_MOV, mir_blk_mov);
  freg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
  new_insn = MIR_new_insn (ctx, MIR_MOV, freg_op, MIR_new_ref_op (ctx, func_import_item));
  gen_add_insn_before (gen_ctx, anchor, new_insn);
  gen_add_insn_before (gen_ctx, anchor,
                       MIR_new_insn (gen_ctx->ctx, MIR_ADD, _MIR_new_hard_reg_op (ctx, 3),
                                     _MIR_new_hard_reg_op (ctx, to_base_hard_reg),
                                     MIR_new_int_op (ctx, to_disp)));
  gen_add_insn_before (gen_ctx, anchor,
                       MIR_new_insn (gen_ctx->ctx, MIR_ADD, _MIR_new_hard_reg_op (ctx, 4),
                                     MIR_new_reg_op (ctx, from_base_reg),
                                     MIR_new_int_op (ctx, from_disp)));
  gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, 5), MIR_new_int_op (ctx, qwords));
  ops[0] = MIR_new_ref_op (ctx, proto_item);
  ops[1] = freg_op;
  ops[2] = _MIR_new_hard_reg_op (ctx, 3);
  ops[3] = _MIR_new_hard_reg_op (ctx, 4);
  ops[4] = _MIR_new_hard_reg_op (ctx, 5);
  new_insn = MIR_new_insn_arr (ctx, MIR_CALL, 5, ops);
  gen_add_insn_before (gen_ctx, anchor, new_insn);
  /* Restore arg regs: */
  if (save_regs > 0) gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, 3), treg_op);
  if (save_regs > 1) gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, 4), treg_op2);
  if (save_regs > 2) gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, 5), treg_op3);
}

static void machinize_call (gen_ctx_t gen_ctx, MIR_insn_t call_insn) {
  MIR_context_t ctx = gen_ctx->ctx;
  MIR_func_t func = curr_func_item->u.func;
  MIR_proto_t proto = call_insn->ops[0].u.ref->u.proto;
  int vararg_p = proto->vararg_p;
  size_t qwords, disp, nargs, nops = MIR_insn_nops (ctx, call_insn), start = proto->nres + 2;
  size_t mem_size = 0, n_iregs = 0, n_fregs = 0;
  MIR_type_t type, mem_type;
  MIR_op_mode_t mode;
  MIR_var_t *arg_vars = NULL;
  MIR_reg_t ret_reg;
  MIR_op_t arg_op, temp_op, arg_reg_op, ret_reg_op, mem_op;
  MIR_insn_code_t new_insn_code, ext_code;
  MIR_insn_t new_insn, ext_insn;

  if (call_insn->code == MIR_INLINE) call_insn->code = MIR_CALL;
  if (proto->args == NULL) {
    nargs = 0;
  } else {
    gen_assert (nops >= VARR_LENGTH (MIR_var_t, proto->args)
                && (vararg_p || nops - start == VARR_LENGTH (MIR_var_t, proto->args)));
    nargs = VARR_LENGTH (MIR_var_t, proto->args);
    arg_vars = VARR_ADDR (MIR_var_t, proto->args);
  }
  if (call_insn->ops[1].mode != MIR_OP_REG && call_insn->ops[1].mode != MIR_OP_HARD_REG) {
    // ??? to optimize (can be immediate operand for func call)
    temp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
    new_insn = MIR_new_insn (ctx, MIR_MOV, temp_op, call_insn->ops[1]);
    call_insn->ops[1] = temp_op;
    gen_add_insn_before (gen_ctx, call_insn, new_insn);
  }
  for (size_t i = start; i < nops; i++) {
    arg_op = call_insn->ops[i];
    gen_assert (arg_op.mode == MIR_OP_REG || arg_op.mode == MIR_OP_HARD_REG
                || (arg_op.mode == MIR_OP_MEM && MIR_all_blk_type_p (arg_op.u.mem.type)));
    if (i - start < nargs) {
      type = arg_vars[i - start].type;
    } else if (call_insn->ops[i].mode == MIR_OP_MEM) {
      type = arg_op.u.mem.type;
      gen_assert (MIR_all_blk_type_p (type));
    } else {
      mode = call_insn->ops[i].value_mode;  // ??? smaller ints
      gen_assert (mode == MIR_OP_INT || mode == MIR_OP_UINT || mode == MIR_OP_FLOAT
                  || mode == MIR_OP_DOUBLE || mode == MIR_OP_LDOUBLE);
      if (mode == MIR_OP_FLOAT)
        (*MIR_get_error_func (ctx)) (MIR_call_op_error,
                                     "passing float variadic arg (should be passed as double)");
      type = mode == MIR_OP_DOUBLE ? MIR_T_D : mode == MIR_OP_LDOUBLE ? MIR_T_LD : MIR_T_I64;
    }
    ext_insn = NULL;
    if ((ext_code = get_ext_code (type)) != MIR_INVALID_INSN) { /* extend arg if necessary */
      temp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
      ext_insn = MIR_new_insn (ctx, ext_code, temp_op, arg_op);
      call_insn->ops[i] = arg_op = temp_op;
    }
    mem_type = type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD ? type : MIR_T_I64;  // ???
    if ((type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD) && n_fregs < 13) {
      /* put arguments to argument hard regs */
      if (ext_insn != NULL) gen_add_insn_before (gen_ctx, call_insn, ext_insn);
      arg_reg_op = _MIR_new_hard_reg_op (ctx, F1_HARD_REG + n_fregs);
      gen_mov (gen_ctx, call_insn,
               type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : MIR_LDMOV,  // ???
               arg_reg_op, arg_op);
      call_insn->ops[i] = arg_reg_op;
      if (vararg_p) {                                             // ??? dead insns
        if (n_iregs >= 8 || (type == MIR_T_LD && n_iregs == 7)) { /* store in memory too */
          mem_op = _MIR_new_hard_reg_mem_op (ctx, mem_type, mem_size + PPC64_STACK_HEADER_SIZE,
                                             SP_HARD_REG, MIR_NON_HARD_REG, 1);
          gen_assert (n_fregs < 12);
          gen_mov (gen_ctx, call_insn, type == MIR_T_LD ? MIR_LDMOV : MIR_DMOV, mem_op, arg_reg_op);
        }
        if (n_iregs < 8) { /* load into gp reg too */
          mem_op = _MIR_new_hard_reg_mem_op (ctx, mem_type, -16, SP_HARD_REG, MIR_NON_HARD_REG, 1);
          gen_mov (gen_ctx, call_insn, type == MIR_T_LD ? MIR_LDMOV : MIR_DMOV, mem_op, arg_reg_op);
          mem_type = mem_type == MIR_T_F ? MIR_T_I32 : MIR_T_I64;  // ???
          mem_op = _MIR_new_hard_reg_mem_op (ctx, mem_type, -16, SP_HARD_REG, MIR_NON_HARD_REG, 1);
          arg_reg_op = _MIR_new_hard_reg_op (ctx, R3_HARD_REG + n_iregs);
          gen_mov (gen_ctx, call_insn, MIR_MOV, arg_reg_op, mem_op);
          call_insn->ops[i] = arg_reg_op; /* keep it alive */
          if (type == MIR_T_LD && n_iregs + 1 < 8) {
            mem_op = _MIR_new_hard_reg_mem_op (ctx, mem_type, -8, SP_HARD_REG, MIR_NON_HARD_REG, 1);
            gen_mov (gen_ctx, call_insn, MIR_MOV,
                     _MIR_new_hard_reg_op (ctx, R3_HARD_REG + n_iregs + 1), mem_op);
          }
        }
      }
      n_fregs += type == MIR_T_LD ? 2 : 1;
    } else if (MIR_blk_type_p (type)) {
      gen_assert (arg_op.mode == MIR_OP_MEM && arg_op.u.mem.disp >= 0 && arg_op.u.mem.index == 0);
      qwords = (arg_op.u.mem.disp + 7) / 8;
      for (disp = 0; qwords > 0 && n_iregs < 8; qwords--, n_iregs++, mem_size += 8, disp += 8) {
        arg_reg_op = _MIR_new_hard_reg_op (ctx, R3_HARD_REG + n_iregs);
        gen_mov (gen_ctx, call_insn, MIR_MOV, arg_reg_op,
                 MIR_new_mem_op (ctx, MIR_T_I64, disp, arg_op.u.mem.base, 0, 1));
        setup_call_hard_reg_args (gen_ctx, call_insn, R3_HARD_REG + n_iregs);
      }
      if (qwords > 0)
        gen_blk_mov (gen_ctx, call_insn, mem_size + PPC64_STACK_HEADER_SIZE, SP_HARD_REG, disp,
                     arg_op.u.mem.base, qwords, n_iregs);
      mem_size += qwords * 8;
      n_iregs += qwords;
      continue;
    } else if (type != MIR_T_F && type != MIR_T_D && type != MIR_T_LD && n_iregs < 8) {
      if (ext_insn != NULL) gen_add_insn_before (gen_ctx, call_insn, ext_insn);
      arg_reg_op = _MIR_new_hard_reg_op (ctx, R3_HARD_REG + n_iregs);
      if (type != MIR_T_RBLK) {
        gen_mov (gen_ctx, call_insn, MIR_MOV, arg_reg_op, arg_op);
      } else {
        assert (arg_op.mode == MIR_OP_MEM);
        gen_mov (gen_ctx, call_insn, MIR_MOV, arg_reg_op, MIR_new_reg_op (ctx, arg_op.u.mem.base));
        arg_reg_op = _MIR_new_hard_reg_mem_op (ctx, MIR_T_RBLK, arg_op.u.mem.disp,
                                               R3_HARD_REG + n_iregs, MIR_NON_HARD_REG, 1);
      }
      call_insn->ops[i] = arg_reg_op;
    } else { /* put arguments on the stack */
      if (ext_insn != NULL) gen_add_insn_before (gen_ctx, call_insn, ext_insn);
      new_insn_code
        = (type == MIR_T_F ? MIR_FMOV
                           : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV);
      mem_op = _MIR_new_hard_reg_mem_op (ctx, mem_type, mem_size + PPC64_STACK_HEADER_SIZE,
                                         SP_HARD_REG, MIR_NON_HARD_REG, 1);
      if (type != MIR_T_RBLK) {
        gen_mov (gen_ctx, call_insn, new_insn_code, mem_op, arg_op);
      } else {
        assert (arg_op.mode == MIR_OP_MEM);
        gen_mov (gen_ctx, call_insn, new_insn_code, mem_op,
                 MIR_new_reg_op (ctx, arg_op.u.mem.base));
      }
      call_insn->ops[i] = mem_op;
    }
    mem_size += type == MIR_T_LD ? 16 : 8;
    n_iregs += type == MIR_T_LD ? 2 : 1;
  }
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  if (vararg_p && mem_size < 64) mem_size = 64; /* to save all arg gprs  */
#else
  if (mem_size < 64) mem_size = 64; /* minimal param save area  */
#endif
  if (param_save_area_size < mem_size) param_save_area_size = mem_size;
  n_iregs = n_fregs = 0;
  for (size_t i = 0; i < proto->nres; i++) {
    ret_reg_op = call_insn->ops[i + 2];
    gen_assert (ret_reg_op.mode == MIR_OP_REG || ret_reg_op.mode == MIR_OP_HARD_REG);
    type = proto->res_types[i];
    if (((type == MIR_T_F || type == MIR_T_D) && n_fregs < 4)
        || (type == MIR_T_LD && n_fregs < 3)) {
      new_insn_code = type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : MIR_LDMOV;
      ret_reg = F1_HARD_REG + n_fregs++;
    } else if (n_iregs < 8) {
      new_insn_code = MIR_MOV;
      ret_reg = R3_HARD_REG + n_iregs++;
    } else {
      (*MIR_get_error_func (ctx)) (MIR_ret_error,
                                   "ppc64 can not handle this combination of return values");
    }
    new_insn = MIR_new_insn (ctx, new_insn_code, ret_reg_op, _MIR_new_hard_reg_op (ctx, ret_reg));
    MIR_insert_insn_after (ctx, curr_func_item, call_insn, new_insn);
    call_insn->ops[i + 2] = new_insn->ops[1];
    if ((ext_code = get_ext_code (type)) != MIR_INVALID_INSN) {
      MIR_insert_insn_after (ctx, curr_func_item, new_insn,
                             MIR_new_insn (ctx, ext_code, ret_reg_op, ret_reg_op));
      new_insn = DLIST_NEXT (MIR_insn_t, new_insn);
    }
    create_new_bb_insns (gen_ctx, call_insn, DLIST_NEXT (MIR_insn_t, new_insn), call_insn);
  }
}

static long double mir_i2ld (int64_t i) { return i; }
static const char *I2LD = "mir.i2ld";
static const char *I2LD_P = "mir.i2ld.p";

static long double mir_ui2ld (uint64_t i) { return i; }
static const char *UI2LD = "mir.ui2ld";
static const char *UI2LD_P = "mir.ui2ld.p";

static long double mir_f2ld (float f) { return f; }
static const char *F2LD = "mir.f2ld";
static const char *F2LD_P = "mir.f2ld.p";

static long double mir_d2ld (double d) { return d; }
static const char *D2LD = "mir.d2ld";
static const char *D2LD_P = "mir.d2ld.p";

static int64_t mir_ld2i (long double ld) { return ld; }
static const char *LD2I = "mir.ld2i";
static const char *LD2I_P = "mir.ld2i.p";

static float mir_ld2f (long double ld) { return ld; }
static const char *LD2F = "mir.ld2f";
static const char *LD2F_P = "mir.ld2f.p";

static double mir_ld2d (long double ld) { return ld; }
static const char *LD2D = "mir.ld2d";
static const char *LD2D_P = "mir.ld2d.p";

static long double mir_ldadd (long double d1, long double d2) { return d1 + d2; }
static const char *LDADD = "mir.ldadd";
static const char *LDADD_P = "mir.ldadd.p";

static long double mir_ldsub (long double d1, long double d2) { return d1 - d2; }
static const char *LDSUB = "mir.ldsub";
static const char *LDSUB_P = "mir.ldsub.p";

static long double mir_ldmul (long double d1, long double d2) { return d1 * d2; }
static const char *LDMUL = "mir.ldmul";
static const char *LDMUL_P = "mir.ldmul.p";

static long double mir_lddiv (long double d1, long double d2) { return d1 / d2; }
static const char *LDDIV = "mir.lddiv";
static const char *LDDIV_P = "mir.lddiv.p";

static long double mir_ldneg (long double d) { return -d; }
static const char *LDNEG = "mir.ldneg";
static const char *LDNEG_P = "mir.ldneg.p";

static const char *VA_ARG_P = "mir.va_arg.p";
static const char *VA_ARG = "mir.va_arg";
static const char *VA_BLOCK_ARG_P = "mir.va_block_arg.p";
static const char *VA_BLOCK_ARG = "mir.va_block_arg";

static int64_t mir_ldeq (long double d1, long double d2) { return d1 == d2; }
static const char *LDEQ = "mir.ldeq";
static const char *LDEQ_P = "mir.ldeq.p";

static int64_t mir_ldne (long double d1, long double d2) { return d1 != d2; }
static const char *LDNE = "mir.ldne";
static const char *LDNE_P = "mir.ldne.p";

static int64_t mir_ldlt (long double d1, long double d2) { return d1 < d2; }
static const char *LDLT = "mir.ldlt";
static const char *LDLT_P = "mir.ldlt.p";

static int64_t mir_ldge (long double d1, long double d2) { return d1 >= d2; }
static const char *LDGE = "mir.ldge";
static const char *LDGE_P = "mir.ldge.p";

static int64_t mir_ldgt (long double d1, long double d2) { return d1 > d2; }
static const char *LDGT = "mir.ldgt";
static const char *LDGT_P = "mir.ldgt.p";

static int64_t mir_ldle (long double d1, long double d2) { return d1 <= d2; }
static const char *LDLE = "mir.ldle";
static const char *LDLE_P = "mir.ldle.p";

static int get_builtin (gen_ctx_t gen_ctx, MIR_insn_code_t code, MIR_item_t *proto_item,
                        MIR_item_t *func_import_item) {
  MIR_context_t ctx = gen_ctx->ctx;
  MIR_type_t res_type;

  *func_import_item = *proto_item = NULL; /* to remove uninitialized warning */
  switch (code) {
  case MIR_I2LD:
    res_type = MIR_T_LD;
    *proto_item
      = _MIR_builtin_proto (ctx, curr_func_item->module, I2LD_P, 1, &res_type, 1, MIR_T_I64, "v");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, I2LD, mir_i2ld);
    return 1;
  case MIR_UI2LD:
    res_type = MIR_T_LD;
    *proto_item
      = _MIR_builtin_proto (ctx, curr_func_item->module, UI2LD_P, 1, &res_type, 1, MIR_T_I64, "v");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, UI2LD, mir_ui2ld);
    return 1;
  case MIR_F2LD:
    res_type = MIR_T_LD;
    *proto_item
      = _MIR_builtin_proto (ctx, curr_func_item->module, F2LD_P, 1, &res_type, 1, MIR_T_F, "v");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, F2LD, mir_f2ld);
    return 1;
  case MIR_D2LD:
    res_type = MIR_T_LD;
    *proto_item
      = _MIR_builtin_proto (ctx, curr_func_item->module, D2LD_P, 1, &res_type, 1, MIR_T_D, "v");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, D2LD, mir_d2ld);
    return 1;
  case MIR_LD2I:
    res_type = MIR_T_I64;
    *proto_item
      = _MIR_builtin_proto (ctx, curr_func_item->module, LD2I_P, 1, &res_type, 1, MIR_T_LD, "v");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LD2I, mir_ld2i);
    return 1;
  case MIR_LD2F:
    res_type = MIR_T_F;
    *proto_item
      = _MIR_builtin_proto (ctx, curr_func_item->module, LD2F_P, 1, &res_type, 1, MIR_T_LD, "v");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LD2F, mir_ld2f);
    return 1;
  case MIR_LD2D:
    res_type = MIR_T_D;
    *proto_item
      = _MIR_builtin_proto (ctx, curr_func_item->module, LD2D_P, 1, &res_type, 1, MIR_T_LD, "v");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LD2D, mir_ld2d);
    return 1;
  case MIR_LDADD:
    res_type = MIR_T_LD;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDADD_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDADD, mir_ldadd);
    return 2;
  case MIR_LDSUB:
    res_type = MIR_T_LD;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDSUB_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDSUB, mir_ldsub);
    return 2;
  case MIR_LDMUL:
    res_type = MIR_T_LD;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDMUL_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDMUL, mir_ldmul);
    return 2;
  case MIR_LDDIV:
    res_type = MIR_T_LD;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDDIV_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDDIV, mir_lddiv);
    return 2;
  case MIR_LDNEG:
    res_type = MIR_T_LD;
    *proto_item
      = _MIR_builtin_proto (ctx, curr_func_item->module, LDNEG_P, 1, &res_type, 1, MIR_T_LD, "d");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDNEG, mir_ldneg);
    return 1;
  case MIR_LDEQ:
    res_type = MIR_T_I64;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDEQ_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDEQ, mir_ldeq);
    return 2;
  case MIR_LDNE:
    res_type = MIR_T_I64;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDNE_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDNE, mir_ldne);
    return 2;
  case MIR_LDLT:
    res_type = MIR_T_I64;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDLT_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDLT, mir_ldlt);
    return 2;
  case MIR_LDGE:
    res_type = MIR_T_I64;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDGE_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDGE, mir_ldge);
    return 2;
  case MIR_LDGT:
    res_type = MIR_T_I64;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDGT_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDGT, mir_ldgt);
    return 2;
  case MIR_LDLE:
    res_type = MIR_T_I64;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDLE_P, 1, &res_type, 2,
                                      MIR_T_LD, "d1", MIR_T_LD, "d2");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDLE, mir_ldle);
    return 2;
  case MIR_VA_ARG:
    res_type = MIR_T_I64;
    *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, VA_ARG_P, 1, &res_type, 2,
                                      MIR_T_I64, "va", MIR_T_I64, "type");
    *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, VA_ARG, va_arg_builtin);
    return 2;
  case MIR_VA_BLOCK_ARG:
    *proto_item
      = _MIR_builtin_proto (ctx, curr_func_item->module, VA_BLOCK_ARG_P, 0, NULL, 4, MIR_T_I64,
                            "res", MIR_T_I64, "va", MIR_T_I64, "size", MIR_T_I64, "ncase");
    *func_import_item
      = _MIR_builtin_func (ctx, curr_func_item->module, VA_BLOCK_ARG, va_block_arg_builtin);
    return 4;
  default: return 0;
  }
}

static MIR_disp_t target_get_stack_slot_offset (gen_ctx_t gen_ctx, MIR_type_t type,
                                                MIR_reg_t slot) {
  /* slot is 0, 1, ... */
  return ((MIR_disp_t) slot * 8 + PPC64_STACK_HEADER_SIZE + param_save_area_size);
}

static void set_prev_sp_op (gen_ctx_t gen_ctx, MIR_insn_t anchor, MIR_op_t *prev_sp_op) {
  if (!block_arg_func_p) {
    /* don't use r11 as we can have spilled param<-mem in param set up which needs r11 as a temp */
    block_arg_func_p = TRUE;
    *prev_sp_op = _MIR_new_hard_reg_op (gen_ctx->ctx, R12_HARD_REG);
    gen_mov (gen_ctx, anchor, MIR_MOV, *prev_sp_op,
             _MIR_new_hard_reg_mem_op (gen_ctx->ctx, MIR_T_I64, 0, SP_HARD_REG, MIR_NON_HARD_REG,
                                       1));
  }
}

static void target_machinize (gen_ctx_t gen_ctx) {
  MIR_context_t ctx = gen_ctx->ctx;
  MIR_func_t func;
  MIR_type_t type, res_type;
  MIR_insn_code_t code, new_insn_code;
  MIR_insn_t insn, next_insn, new_insn, anchor;
  MIR_reg_t ret_reg;
  MIR_op_t ret_reg_op, arg_reg_op, prev_sp_op, temp_op, arg_var_op;
  size_t i, int_arg_num, fp_arg_num, disp, var_args_start, qwords, offset;

  assert (curr_func_item->item_type == MIR_func_item);
  func = curr_func_item->u.func;
  block_arg_func_p = FALSE;
  param_save_area_size = 0;
  anchor = DLIST_HEAD (MIR_insn_t, func->insns);
  if (func->vararg_p)
    set_prev_sp_op (gen_ctx, anchor, &prev_sp_op); /* arg can be taken from memory */
  disp = PPC64_STACK_HEADER_SIZE;                  /* param area start in the caller frame */
  for (i = int_arg_num = fp_arg_num = 0; i < func->nargs; i++) {
    /* Argument extensions is already done in simplify */
    /* Prologue: generate arg_var = hard_reg|stack mem ... */
    type = VARR_GET (MIR_var_t, func->vars, i).type;
    arg_var_op = MIR_new_reg_op (ctx, i + 1);
    if ((type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD) && fp_arg_num < 13) {
      if (type == MIR_T_LD && fp_arg_num == 12) { /* dmov f14,disp(r1) -> lfd f14,disp(r1): */
        set_prev_sp_op (gen_ctx, anchor, &prev_sp_op);
        arg_reg_op = _MIR_new_hard_reg_op (ctx, F14_HARD_REG);
        gen_mov (gen_ctx, anchor, MIR_DMOV, arg_reg_op,
                 _MIR_new_hard_reg_mem_op (ctx, MIR_T_D, disp + 8, R12_HARD_REG, MIR_NON_HARD_REG,
                                           1));
      }
      arg_reg_op = _MIR_new_hard_reg_op (ctx, F1_HARD_REG + fp_arg_num);
      gen_mov (gen_ctx, anchor, type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : MIR_LDMOV,
               arg_var_op, arg_reg_op); /* (f|d|ld|)mov arg, arg_hard_reg */
      fp_arg_num += type == MIR_T_LD ? 2 : 1;
    } else if (type == MIR_T_F || type == MIR_T_D
               || type == MIR_T_LD) { /* (f|d|ld|)mov arg, arg_memory */
      set_prev_sp_op (gen_ctx, anchor, &prev_sp_op);
      gen_mov (gen_ctx, anchor, type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : MIR_LDMOV,
               arg_var_op,
               _MIR_new_hard_reg_mem_op (ctx, type, disp, R12_HARD_REG, MIR_NON_HARD_REG, 1));
    } else if (MIR_blk_type_p (type)) {
      qwords = (VARR_GET (MIR_var_t, func->vars, i).size + 7) / 8;
      offset = int_arg_num < 8 ? PPC64_STACK_HEADER_SIZE + int_arg_num * 8 : disp;
      set_prev_sp_op (gen_ctx, anchor, &prev_sp_op);
      for (; qwords > 0 && int_arg_num < 8; qwords--, int_arg_num++, disp += 8) {
        if (!func->vararg_p)
          gen_mov (gen_ctx, anchor, MIR_MOV,
                   _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64,
                                             PPC64_STACK_HEADER_SIZE + int_arg_num * 8,
                                             R12_HARD_REG, MIR_NON_HARD_REG, 1),
                   _MIR_new_hard_reg_op (ctx, R3_HARD_REG + int_arg_num));
      }
      gen_add_insn_before (gen_ctx, anchor,
                           MIR_new_insn (ctx, MIR_ADD, arg_var_op,
                                         _MIR_new_hard_reg_op (ctx, R12_HARD_REG),
                                         MIR_new_int_op (ctx, offset)));
      disp += qwords * 8;
      int_arg_num += qwords;
      continue;
    } else if (int_arg_num < 8) { /* mov arg, arg_hard_reg */
      arg_reg_op = _MIR_new_hard_reg_op (ctx, R3_HARD_REG + int_arg_num);
      gen_mov (gen_ctx, anchor, MIR_MOV, arg_var_op, arg_reg_op);
    } else { /* mov arg, arg_memory */
      set_prev_sp_op (gen_ctx, anchor, &prev_sp_op);
      gen_mov (gen_ctx, anchor, MIR_MOV, arg_var_op,
               _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, disp, R12_HARD_REG, MIR_NON_HARD_REG, 1));
    }
    disp += type == MIR_T_LD ? 16 : 8;
    int_arg_num += type == MIR_T_LD ? 2 : 1;
  }
  var_args_start = disp;
  switch_p = alloca_p = FALSE;
  leaf_p = TRUE;
  for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; insn = next_insn) {
    MIR_item_t proto_item, func_import_item;
    int nargs;

    next_insn = DLIST_NEXT (MIR_insn_t, insn);
    code = insn->code;
    if (code == MIR_LDBEQ || code == MIR_LDBNE || code == MIR_LDBLT || code == MIR_LDBGE
        || code == MIR_LDBGT || code == MIR_LDBLE) { /* split to cmp and branch */
      temp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
      code = (code == MIR_LDBEQ
                ? MIR_LDEQ
                : code == MIR_LDBNE
                    ? MIR_LDNE
                    : code == MIR_LDBLT
                        ? MIR_LDLT
                        : code == MIR_LDBGE ? MIR_LDGE : code == MIR_LDBGT ? MIR_LDGT : MIR_LDLE);
      new_insn = MIR_new_insn (ctx, code, temp_op, insn->ops[1], insn->ops[2]);
      gen_add_insn_before (gen_ctx, insn, new_insn);
      next_insn = MIR_new_insn (ctx, MIR_BT, insn->ops[0], temp_op);
      gen_add_insn_after (gen_ctx, new_insn, next_insn);
      gen_delete_insn (gen_ctx, insn);
      insn = new_insn;
    }
    if ((nargs = get_builtin (gen_ctx, code, &proto_item, &func_import_item)) > 0) {
      if (code == MIR_VA_ARG || code == MIR_VA_BLOCK_ARG) {
	/* Use a builtin func call:
	   mov func_reg, func ref; [mov reg3, type;] call proto, func_reg, res_reg, va_reg,
	   reg3 */
        MIR_op_t ops[6], func_reg_op, reg_op3;
        MIR_op_t res_reg_op = insn->ops[0], va_reg_op = insn->ops[1], op3 = insn->ops[2];

        assert (res_reg_op.mode == MIR_OP_REG && va_reg_op.mode == MIR_OP_REG
                && op3.mode == (code == MIR_VA_ARG ? MIR_OP_MEM : MIR_OP_REG));
        func_reg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
        reg_op3 = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
        next_insn = new_insn
          = MIR_new_insn (ctx, MIR_MOV, func_reg_op, MIR_new_ref_op (ctx, func_import_item));
        gen_add_insn_before (gen_ctx, insn, new_insn);
	if (code == MIR_VA_ARG) {
	  new_insn = MIR_new_insn (ctx, MIR_MOV, reg_op3,
				   MIR_new_int_op (ctx, (int64_t) op3.u.mem.type));
	  op3 = reg_op3;
	  gen_add_insn_before (gen_ctx, insn, new_insn);
	}
        ops[0] = MIR_new_ref_op (ctx, proto_item);
        ops[1] = func_reg_op;
        ops[2] = res_reg_op;
        ops[3] = va_reg_op;
        ops[4] = op3;
	if (code == MIR_VA_BLOCK_ARG) ops[5] = insn->ops[3];
        new_insn = MIR_new_insn_arr (ctx, MIR_CALL, code == MIR_VA_ARG ? 5 : 6, ops);
        gen_add_insn_before (gen_ctx, insn, new_insn);
        gen_delete_insn (gen_ctx, insn);
      } else { /* Use builtin: mov freg, func ref; call proto, freg, res_reg, op_reg[, op_reg2] */
        MIR_op_t freg_op, res_reg_op = insn->ops[0], op_reg_op = insn->ops[1], ops[5];

        assert (res_reg_op.mode == MIR_OP_REG && op_reg_op.mode == MIR_OP_REG);
        freg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
        next_insn = new_insn
          = MIR_new_insn (ctx, MIR_MOV, freg_op, MIR_new_ref_op (ctx, func_import_item));
        gen_add_insn_before (gen_ctx, insn, new_insn);
        ops[0] = MIR_new_ref_op (ctx, proto_item);
        ops[1] = freg_op;
        ops[2] = res_reg_op;
        ops[3] = op_reg_op;
        if (nargs == 2) ops[4] = insn->ops[2];
        new_insn = MIR_new_insn_arr (ctx, MIR_CALL, nargs + 3, ops);
        gen_add_insn_before (gen_ctx, insn, new_insn);
        gen_delete_insn (gen_ctx, insn);
      }
    } else if (code == MIR_VA_START) {
      MIR_op_t treg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
      MIR_op_t treg_op2 = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func));
      MIR_op_t va_op = insn->ops[0];
      MIR_reg_t va_reg;

      assert (func->vararg_p && va_op.mode == MIR_OP_REG);
      va_reg = va_op.u.reg;
      /* Insns can be non-simplified as soon as they match a machine insn.  */
      /* treg = mem64[r1]; treg = treg + var_args_start; mem64[va_reg] = treg */
      gen_mov (gen_ctx, insn, MIR_MOV, treg_op,
               _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 0, R1_HARD_REG, MIR_NON_HARD_REG, 1));
      gen_mov (gen_ctx, insn, MIR_MOV, treg_op2, MIR_new_int_op (ctx, var_args_start));
      /* don't use immediate in ADD as treg_op can become r0: */
      new_insn = MIR_new_insn (ctx, MIR_ADD, treg_op, treg_op, treg_op2);
      gen_add_insn_before (gen_ctx, insn, new_insn);
      gen_mov (gen_ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_I64, 0, va_reg, 0, 1), treg_op);
      gen_delete_insn (gen_ctx, insn);
    } else if (code == MIR_VA_END) { /* do nothing */
      gen_delete_insn (gen_ctx, insn);
    } else if (MIR_call_code_p (code)) {
      machinize_call (gen_ctx, insn);
      leaf_p = FALSE;
    } else if (code == MIR_ALLOCA) {
      alloca_p = TRUE;
    } else if (code == MIR_SWITCH) {
      switch_p = TRUE;
    } else if (code == MIR_RET) {
      /* In simplify we already transformed code for one return insn
         and added extension insn (if any).  */
      uint32_t n_gpregs = 0, n_fregs = 0;

      assert (func->nres == MIR_insn_nops (ctx, insn));
      for (size_t i = 0; i < func->nres; i++) {
        assert (insn->ops[i].mode == MIR_OP_REG);
        res_type = func->res_types[i];
        if (((res_type == MIR_T_F || res_type == MIR_T_D) && n_fregs < 4)
            || (res_type == MIR_T_LD && n_fregs < 3)) {
          new_insn_code
            = res_type == MIR_T_F ? MIR_FMOV : res_type == MIR_T_D ? MIR_DMOV : MIR_LDMOV;
          ret_reg = F1_HARD_REG + n_fregs++;
        } else if (n_gpregs < 8) {
          new_insn_code = MIR_MOV;
          ret_reg = R3_HARD_REG + n_gpregs++;
        } else {
          (*MIR_get_error_func (ctx)) (MIR_ret_error,
                                       "ppc64 can not handle this combination of return values");
        }
        ret_reg_op = _MIR_new_hard_reg_op (ctx, ret_reg);
        gen_mov (gen_ctx, insn, new_insn_code, ret_reg_op, insn->ops[i]);
        insn->ops[i] = ret_reg_op;
      }
    }
  }
}

static void isave (gen_ctx_t gen_ctx, MIR_insn_t anchor, int disp, MIR_reg_t hard_reg) {
  gen_mov (gen_ctx, anchor, MIR_MOV,
           _MIR_new_hard_reg_mem_op (gen_ctx->ctx, MIR_T_I64, disp, R1_HARD_REG, MIR_NON_HARD_REG,
                                     1),
           _MIR_new_hard_reg_op (gen_ctx->ctx, hard_reg));
}

static void fsave (gen_ctx_t gen_ctx, MIR_insn_t anchor, int disp, MIR_reg_t hard_reg) {
  gen_mov (gen_ctx, anchor, MIR_DMOV,
           _MIR_new_hard_reg_mem_op (gen_ctx->ctx, MIR_T_D, disp, R1_HARD_REG, MIR_NON_HARD_REG, 1),
           _MIR_new_hard_reg_op (gen_ctx->ctx, hard_reg));
}

static void target_make_prolog_epilog (gen_ctx_t gen_ctx, bitmap_t used_hard_regs,
                                       size_t stack_slots_num) {
  MIR_context_t ctx = gen_ctx->ctx;
  MIR_func_t func;
  MIR_insn_t anchor, new_insn;
  MIR_op_t sp_reg_op, fp_reg_op, r0_reg_op, lr_reg_op;
  int64_t start_save_regs_offset;
  size_t i, n, frame_size, saved_iregs_num, saved_fregs_num;

  assert (curr_func_item->item_type == MIR_func_item);
  func = curr_func_item->u.func;
  anchor = DLIST_HEAD (MIR_insn_t, func->insns);
  if (func->vararg_p) {
    for (i = 0; i < 8; i++)
      isave (gen_ctx, anchor, PPC64_STACK_HEADER_SIZE + i * 8, i + R3_HARD_REG);
  }
  for (i = saved_iregs_num = saved_fregs_num = 0; i <= MAX_HARD_REG; i++)
    if (!target_call_used_hard_reg_p (i, MIR_T_UNDEF) && bitmap_bit_p (used_hard_regs, i)) {
      if (i < F0_HARD_REG)
        saved_iregs_num++;
      else
        saved_fregs_num++;
    }
  if (leaf_p && !alloca_p && !switch_p /* switch changes LR */
      && saved_iregs_num == 0 && saved_fregs_num == 0 && stack_slots_num == 0)
    return;
  saved_iregs_num++; /* for fp (R31) ??? only alloca_p */
  r0_reg_op = _MIR_new_hard_reg_op (ctx, R0_HARD_REG);
  lr_reg_op = _MIR_new_hard_reg_op (ctx, LR_HARD_REG);
  sp_reg_op = _MIR_new_hard_reg_op (ctx, R1_HARD_REG);
  fp_reg_op = _MIR_new_hard_reg_op (ctx, R31_HARD_REG);
  /* Prologue: */
  frame_size = param_save_area_size + PPC64_STACK_HEADER_SIZE + stack_slots_num * 8;
  start_save_regs_offset = frame_size;
  frame_size += (saved_iregs_num + saved_fregs_num) * 8;
  if (frame_size % 16 != 0) frame_size = (frame_size + 15) / 16 * 16;
  gen_mov (gen_ctx, anchor, MIR_MOV, r0_reg_op, lr_reg_op); /* r0 = lr */
  gen_mov (gen_ctx, anchor, MIR_MOV,
           _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 16, R1_HARD_REG, MIR_NON_HARD_REG, 1),
           r0_reg_op); /* mem[r1] = r0 */
  gen_mov (gen_ctx, anchor, MIR_MOV, r0_reg_op, sp_reg_op);
  new_insn = MIR_new_insn (ctx, MIR_ADD, sp_reg_op, sp_reg_op, MIR_new_int_op (ctx, -frame_size));
  gen_add_insn_before (gen_ctx, anchor, new_insn); /* r1 -= frame_size */
  gen_mov (gen_ctx, anchor, MIR_MOV,
           _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 0, R1_HARD_REG, MIR_NON_HARD_REG, 1),
           r0_reg_op); /* mem[r1] = r0 */
  gen_mov (gen_ctx, anchor, MIR_MOV,
           _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, PPC64_TOC_OFFSET, R1_HARD_REG,
                                     MIR_NON_HARD_REG, 1),
           _MIR_new_hard_reg_op (ctx, R2_HARD_REG)); /* mem[r1+toc_off] = r2 */
  for (n = i = 0; i <= MAX_HARD_REG; i++)
    if (!target_call_used_hard_reg_p (i, MIR_T_UNDEF) && bitmap_bit_p (used_hard_regs, i)) {
      if (i < F0_HARD_REG)
        isave (gen_ctx, anchor, start_save_regs_offset + (n++) * 8, i);
      else
        fsave (gen_ctx, anchor, start_save_regs_offset + (n++) * 8, i);
    }
  isave (gen_ctx, anchor, start_save_regs_offset + n * 8, R31_HARD_REG); /* save R31 */
  gen_mov (gen_ctx, anchor, MIR_MOV, fp_reg_op, sp_reg_op);              /* r31 = r1 */
  /* Epilogue: */
  anchor = DLIST_TAIL (MIR_insn_t, func->insns);
  assert (anchor->code == MIR_RET);
  /* Restoring hard registers: */
  for (i = n = 0; i <= MAX_HARD_REG; i++)
    if (!target_call_used_hard_reg_p (i, MIR_T_UNDEF) && bitmap_bit_p (used_hard_regs, i)) {
      if (i < F0_HARD_REG) {
        gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, i),
                 _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, start_save_regs_offset + (n++) * 8,
                                           FP_HARD_REG, MIR_NON_HARD_REG, 1));
      } else {
        gen_mov (gen_ctx, anchor, MIR_DMOV, _MIR_new_hard_reg_op (ctx, i),
                 _MIR_new_hard_reg_mem_op (ctx, MIR_T_D, start_save_regs_offset + (n++) * 8,
                                           FP_HARD_REG, MIR_NON_HARD_REG, 1));
      }
    }
  /* Restore sp, fp, lr */
  new_insn = MIR_new_insn (ctx, MIR_ADD, sp_reg_op, fp_reg_op, MIR_new_int_op (ctx, frame_size));
  gen_add_insn_before (gen_ctx, anchor, new_insn); /* sp = fp + frame_size */
  gen_mov (gen_ctx, anchor, MIR_MOV, fp_reg_op,
           _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, start_save_regs_offset + n * 8, FP_HARD_REG,
                                     MIR_NON_HARD_REG, 1)); /* restore fp */
  gen_mov (gen_ctx, anchor, MIR_MOV, r0_reg_op,
           _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 16, R1_HARD_REG, MIR_NON_HARD_REG,
                                     1));                   /* r0 = 16(sp) */
  gen_mov (gen_ctx, anchor, MIR_MOV, lr_reg_op, r0_reg_op); /* lr = r0 */
}

struct pattern {
  MIR_insn_code_t code;
  /* Pattern elements:
     blank - ignore
     X - match everything
     $ - finish successfully matching
     r - register but LR
     R - r but R0

     h<one or two decimal digits> - hard register with given number

        memory with signed 16-bit disp and optional non-R0 base:
     m[0-2] - int (signed or unsigned) memory of size 8,16,32,64-bits
     ms[0-2] - signed int type memory of size 8,16,32,64-bits
     mu[0-2] - unsigned int type memory of size 8,16,32,64-bits

       memory with non-R0 base and index:
     M[0-3] - int (signed or unsigned) type memory of size 8,16,32,64-bits
     Ms[0-2] - signed int type memory of size 8,16,32,64-bits
     Mu[0-2] - unsigned int type memory of size 8,16,32,64-bits

     mds - signed 32-bit memory with scaled by 4 signed 16-bit disp and optional non-R0 base:
     Mds - 64-bit memory with scaled by 4 signed 16-bit disp and optional non-R0 base:

     i - 16 bit signed immediate
     I - 16 bit signed immediate shift left by 16
     u - 16 bit unsigned immediate
     U - 16 bit unsigned immediate shift left by 16
     x - 64 bit unsigned immediate whose hight 32-bit part is described by pattern 0*1*
     z - 32-bit unsigned immediate
     zs - 32-bit unsigned immediate with zero 0-th bit
     Z - any integer immediate
     Zs - 48-bit unsigned immediate with zero 0-th bit
     Sh - 6-bit unsigned shift
     sh - 5-bit unsigned shift
     ia - roundup (i, 16) as 16 bit signed integer

       memory with signed 16-bit disp and optional non-R0 base:
     mf - memory of float
     md - memory of double
     mld - memory of long double where disp + 8 is also in 16-bit range and non-R0 base reg
     mld0 - as previous bit with R0 base reg
     mds - signed 32-bit memory with scaled by 4 signed 16-bit disp and option non-R0 base:

       memory with non-R0 base and index:
     Mf - memory of float
     Md - memory of double

     L - reference or label as the 1st or 2nd op which can be present by signed 24-bit pc word
     offset

     Remember we have no float or (long) double immediate at this stage.  They were removed during
     simplification.  */

  const char *pattern;
  /* Bit addressing: 0..31
     Replacement elements:
     blank - ignore
     ; - insn separation
     o<number> - opcode [0..5], <number> is decimal
     O<number> - opcode [21..30], <number> is decimal
     P<number> - opcode [26..30], <number> is decimal
     (r|n)t[0-2] - put n-th operand register into rd field [6..10]
     (r|n)s[0-2] - put n-th operand register into rs field [6..10] -- source
     (r|n)a[0-2] - put n-th operand register into rn field [11..15]
     (r|n)b[0-2] - put n-th operand register into rm field [16..20]
     rc[0-2] - put n-th operand register into rc field [21..25]
                   n above means operand reg+1
     Ra[0-2] - put n-th operand register (which is not R0) into rn field [11..15]
     h(t,s,a,b)<dec digits> - hardware register with given number in rt,ra,rb field
     sr<number> - special reg with given number [11..15]
     m = operand is (8-,16-,32-,64-bit) mem with base (0 reg means 0) and signed 16-bit disp
     M = operand is (8-,16-,32-,64-bit) mem with base (0 reg means 0) and index
     mds = 32-bit scaled mem with base (0 reg means 0) and signed 16-bit disp scaled by 4
     Mds = 64-bit scaled mem with base (0 reg means 0) and signed 16-bit disp scaled by 4
     d<number> = field [30..31]
     mt = 8-byte memory -16(r1)
     i - 16 bit signed immediate - field [16..31]
     I - 16 bit signed immediate shift left by 16 - field [16..31]
     u - 16 bit unsigned immediate - field [16..31]
     U - 16 bit unsigned immediate shift left by 16 - field [16..31]
     z[0-3] - n-th 16 bytes of 64-bit immediate
     x - mb for x immediate
     sh<number> - field [16..20]
     Sh<number> - field [16..20,30]
     Mb<number> - field [21..26], and zero bits [27..28]
     Me<number> - field [21..26], and one in bit [27..29]
     Sh - Sh value: field [16..20,30]
     sh - sh value: field [16..20]
     Shr - 64 - Sh value: field [16..20,30]
     shr - 32 - sh value: field [16..20]
     mbSh - mb with value 64-Sh: field [21..26] and zero in bits [27..29]
     mbsh - mb with value 32-sh: field [21..25]
     meSh - mb with value 63-Sh: field [21..26] and 1 in bits [27..29]
     mesh - mb with value 31-sh: field [26..30]
     mb<number> - number in filed [21..25]
     me<number> - number in filed [26..30]
     ia - roundup (i, 16)
     ih - PPC64_STACK_HEADER_SIZE + param_area_size

     bf<number> - field [6..8]
     L - operand-label as 24-bit word pc offset scaled by 4: field [6..29]
     l - operand-label as 14-bit word pc offset scaled by 4: field [16..29]
     l<number> - operand-label as 14-bit word pc offset scaled by 4: field [16..29]
     L<number> - long data bitfield [10..10]
     BO<number> - field [6..10] for bcond
     BI<number> - field [11..15] for bcond
     LK<number> - field [31..31]

     at - address disp PPC64_TOC_OFFSET
     T - switch table displacement
  */
  const char *replacement;
};

static const struct pattern patterns[] = {
  {MIR_MOV, "r r", "o31 O444 ra0 rs1 rb1"}, /* or ra,rs,rs */
  {MIR_MOV, "r h64", "o31 O339 rt0 sr8"},   /* mflr rt */
  {MIR_MOV, "h64 r", "o31 O467 rs1 sr8"},   /* mtlr rs */

  // ??? loads/stores with update
  {MIR_MOV, "r Mds", "o58 rt0 Mds"},   /* ld rt,ds-mem */
  {MIR_MOV, "Mds r", "o62 rs1 Mds"},   /* std rt,ds-mem */
  {MIR_MOV, "r M3", "o31 O21 rt0 M"},  /* ldx rt,index-mem */
  {MIR_MOV, "M3 r", "o31 O149 rs1 M"}, /* stdx rs,index-mem */

  {MIR_MOV, "r mu2", "o32 rt0 m"},     /* lwz rt,disp-mem */
  {MIR_MOV, "m2 r", "o36 rs1 m"},      /* stw rs,disp-mem */
  {MIR_MOV, "r Mu2", "o31 O23 rt0 M"}, /* lwzx rt,index-mem */
  {MIR_MOV, "M2 r", "o31 O151 rs1 M"}, /* stwx rs,index-mem */

  {MIR_MOV, "r mds", "o58 rt0 mds d2"}, /* lwa rt,ds-mem */
  {MIR_MOV, "r Ms2", "o31 O341 rt0 M"}, /* lwax rt,index-mem */

  {MIR_MOV, "r mu1", "o40 rt0 m"},      /* lhz rt,disp-mem */
  {MIR_MOV, "m1 r", "o44 rs1 m"},       /* sth rs,disp-mem */
  {MIR_MOV, "r Mu1", "o31 O279 rt0 M"}, /* lhzx rt,index-mem */
  {MIR_MOV, "M1 r", "o31 O407 rs1 M"},  /* sthx rs,index-mem */

  {MIR_MOV, "r ms1", "o42 rt0 m"},      /* lha rt,disp-mem */
  {MIR_MOV, "r Ms1", "o31 O343 rt0 M"}, /* lhax rt,index-mem */

  {MIR_MOV, "r mu0", "o34 rt0 m"},     /* lbz rt,disp-mem */
  {MIR_MOV, "m0 r", "o38 rs1 m"},      /* stb rs,disp-mem */
  {MIR_MOV, "r Mu0", "o31 O87 rt0 M"}, /* lbzx rt,index-mem */
  {MIR_MOV, "M0 r", "o31 O215 rs1 M"}, /* stbx rs,index-mem */

  {MIR_MOV, "r ms0", "o34 rt0 m; o31 O954 rs0 ra0"},     /* lbz rt,disp-mem; extsb rt,rt */
  {MIR_MOV, "r Ms0", "o31 O87 rt0 M; o31 O954 rs0 ra0"}, /* lbzx rt,index-mem; extsb rt,rt */

  {MIR_MOV, "r i", "o14 rt0 ha0 i"},                   /* li rt,i == addi rt,0,i */
  {MIR_MOV, "r I", "o15 rt0 ha0 I"},                   /* lis rt,i == addis rt,0,i */
  {MIR_MOV, "r zs", "o15 rt0 ha0 z2; o24 rt0 ra0 z3"}, /* lis rt,z2; ori rt,rt,z3 */
  /* lis rt,rt,z2; ori rt,rt,z3; clrdi rt,rt,X */
  {MIR_MOV, "r x", "o15 rt0 ha0 z2; o24 ra0 rs0 z3; o30 ra0 rs0 Sh0 x"},
  /* xor rt,rt,rt; oris rt,rt,z2; ori rt,rt,z3: */
  {MIR_MOV, "r z", "o31 O316 rs0 ra0 rb0; o25 ra0 rs0 z2; o24 ra0 rs0 z3"},
  /* li rt,r0,z1; rldicr rt,rt,32,31; oris rt,rt,z2; ori rt,rt,z3 */
  {MIR_MOV, "r Zs", "o14 rt0 ha0 z1; o30 rt0 ra0 Sh32 Me31; o25 ra0 rs0 z2; o24 ra0 rs0 z3"},
  /* lis rt,r0,z0; ori rt,rt,z1; rldicr rt,rt,32,31; oris rt,rt,z2; ori rt,rt,z3: */
  {MIR_MOV, "r Z",
   "o15 rt0 ha0 z0; o24 ra0 rs0 z1; o30 rt0 ra0 Sh32 Me31; o25 ra0 rs0 z2; o24 ra0 rs0 z3"},

  {MIR_FMOV, "r r", "o63 O72 rt0 rb1"}, /*  fmr rt,rb */
  {MIR_FMOV, "r mf", "o48 rt0 m"},      /* lfs rt, disp-mem */
  {MIR_FMOV, "r Mf", "o31 O535 rt0 M"}, /* lfsx rt, index-mem */
  {MIR_FMOV, "mf r", "o52 rt1 m"},      /* stfs rt, disp-mem */
  {MIR_FMOV, "Mf r", "o31 O663 rt1 M"}, /* stfsx rt, index-mem */

  {MIR_DMOV, "r r", "o63 O72 rt0 rb1"}, /*  fmr rt,rb */
  {MIR_DMOV, "r md", "o50 rt0 m"},      /* lds rt, disp-mem */
  {MIR_DMOV, "r Md", "o31 O599 rt0 M"}, /* lfdx rt, index-mem */
  {MIR_DMOV, "md r", "o54 rt1 m"},      /* stfd rt, disp-mem */
  {MIR_DMOV, "Md r", "o31 O727 rt1 M"}, /* stfdx rt, index-mem */

  {MIR_LDMOV, "r r", "o63 O72 rt0 rb1;o63 O72 nt0 nb1"}, /* fmr rt,rb; fmr rt+1,rb+1 */
  {MIR_LDMOV, "r mld", "o50 rt0 m; o50 nt0 mn"},         /* lfd rt,disp-mem; lfd rt+1,disp+8-mem */
  {MIR_LDMOV, "mld r", "o54 rt1 m; o54 nt1 mn"}, /* stfd rt,disp-mem; stfdx rt+1,disp+8-mem */
  {MIR_LDMOV, "r mld0",
   "o31 O444 ha11 hs0 hb0; o50 rt0 ha11; o50 nt0 ha11 i8"}, /* mr r11,r0; lfd rt,(r11); lfd
                                                               rt+1,8(r11) */
  {MIR_LDMOV, "mld0 r",
   "o31 O444 ha11 hs0 hb0; o54 rt1 ha11; o54 nt1 ha11 i8"}, /* mr r11,r0; stfd rt,(r11); stfdx
                                                               rt+1,8(r11) */

  {MIR_EXT8, "r r", "o31 O954 ra0 rs1"},  /* extsb ra,rs */
  {MIR_EXT16, "r r", "o31 O922 ra0 rs1"}, /* extsh ra,rs */
  {MIR_EXT32, "r r", "o31 O986 ra0 rs1"}, /* extsw ra,rs */

  {MIR_UEXT8, "r r", "o30 ra0 rs1 Sh0 Mb56"},  /* rldicl ra,rs,0,56 */
  {MIR_UEXT16, "r r", "o30 ra0 rs1 Sh0 Mb48"}, /* rldicl ra,rs,0,48 */
  {MIR_UEXT32, "r r", "o30 ra0 rs1 Sh0 Mb32"}, /* rldicl ra,rs,0,32 */

  {MIR_ADD, "r r r", "o31 O266 rt0 ra1 rb2"},  /* add rt,ra,rb */
  {MIR_ADD, "r R i", "o14 rt0 ra1 i"},         /* addi rt,ra,i */
  {MIR_ADD, "r R I", "o15 rt0 ra1 I"},         /* addis rt,ra,I */
  {MIR_ADDS, "r r r", "o31 O266 rt0 ra1 rb2"}, /* add rt,ra,rb */
  {MIR_ADDS, "r R i", "o14 rt0 ra1 i"},        /* addi rt,ra,i */
  {MIR_ADDS, "r R I", "o15 rt0 ra1 I"},        /* addis rt,ra,I */
  {MIR_FADD, "r r r", "o59 O21 rt0 ra1 rb2"},  /* fadds rt,ra,rb*/
  {MIR_DADD, "r r r", "o63 O21 rt0 ra1 rb2"},  /* fadd rt,ra,rb*/
  // ldadd is implemented through builtin

  // ??? transform sub immediate to add immediate
  {MIR_SUB, "r r r", "o31 O40 rt0 rb1 ra2"},  /* subf rt,ra,rb */
  {MIR_SUBS, "r r r", "o31 O40 rt0 rb1 ra2"}, /* subf rt,ra,rb */
  {MIR_FSUB, "r r r", "o59 O20 rt0 ra1 rb2"}, /* fsubs rt,ra,rb*/
  {MIR_DSUB, "r r r", "o63 O20 rt0 ra1 rb2"}, /* fsub rt,ra,rb*/
  // ldsub is implemented through builtin

  {MIR_MUL, "r r r", "o31 O233 rt0 ra1 rb2"},  /* mulld rt,ra,rb*/
  {MIR_MUL, "r r i", "o7 rt0 ra1 i"},          /* mulli rt,ra,i*/
  {MIR_MULS, "r r r", "o31 O235 rt0 ra1 rb2"}, /* mullw rt,ra,rb*/
  {MIR_FMUL, "r r r", "o59 P25 rt0 ra1 rc2"},  /* fmuls rt,ra,rc*/
  {MIR_DMUL, "r r r", "o63 P25 rt0 ra1 rc2"},  /* fmul rt,ra,rc*/
  // ldmul is implemented through builtin

  {MIR_DIV, "r r r", "o31 O489 rt0 ra1 rb2"},   /* divd rt,ra,rb*/
  {MIR_DIVS, "r r r", "o31 O491 rt0 ra1 rb2"},  /* divw rt,ra,rb*/
  {MIR_UDIV, "r r r", "o31 O457 rt0 ra1 rb2"},  /* divdu rt,ra,rb*/
  {MIR_UDIVS, "r r r", "o31 O459 rt0 ra1 rb2"}, /* divwu rt,ra,rb*/
  {MIR_FDIV, "r r r", "o59 O18 rt0 ra1 rb2"},   /* fdivs rt,ra,rb*/
  {MIR_DDIV, "r r r", "o63 O18 rt0 ra1 rb2"},   /* fdiv rt,ra,rb*/
  // lddiv is implemented through builtin

  /* divd r10,ra,rb;muld r10,r10,rb;subf r,r10,ra: */
  {MIR_MOD, "r r r", "o31 O489 ht10 ra1 rb2; o31 O233 ht10 ha10 rb2; o31 O40 rt0 ha10 rb1"},
  /* divw r10,ra,rb;mulw r10,r10,rb;subf r,r10,ra: */
  {MIR_MODS, "r r r", "o31 O491 ht10 ra1 rb2; o31 O235 ht10 ha10 rb2; o31 O40 rt0 ha10 rb1"},
  /* divdu r10,ra,rb;muld r10,r10,rb;subf r,r10,ra: */
  {MIR_UMOD, "r r r", "o31 O457 ht10 ra1 rb2; o31 O233 ht10 ha10 rb2; o31 O40 rt0 ha10 rb1"},
  /* divwu r10,ra,rb;mulw r10,r10,rb;subf r,r10,ra: */
  {MIR_UMODS, "r r r", "o31 O459 ht10 ra1 rb2; o31 O235 ht10 ha10 rb2; o31 O40 rt0 ha10 rb1"},

#define MFCR "o31 O19 rt0"
#define EQEND MFCR "; o21 rs0 ra0 sh31 mb31 me31"
#define CMPD "o31 O0 bf7 L1 ra1 rb2"
#define CMPDI(i) "o11 bf7 L1 ra1 " #i
#define CMPW "o31 O0 bf7 L0 ra1 rb2"
#define CMPWI(i) "o11 bf7 L0 ra1 " #i
#define FCMPU "o63 O0 bf7 ra1 rb2"
  // all ld insn are changed to builtins
  /* cmpd 7,ra,rb; mfcr rt; rlwinm rt,rt,31,31,31;*/
  {MIR_EQ, "r r r", CMPD "; " EQEND},
  /* cmpdi 7,ra,i; mfcr rt; rlwinm rt,rt,31,31,31*/
  {MIR_EQ, "r r i", CMPDI (i) "; " EQEND},
  /* cmpw 7,ra,rb; mfcr rt; rlwinm rt,rt,31,31,31;*/
  {MIR_EQS, "r r r", CMPW "; " EQEND},
  /* cmpwi 7,ra,i; mfcr rt; rlwinm rt,rt,31,31,31*/
  {MIR_EQS, "r r i", CMPWI (i) "; " EQEND},
  /* fcmpu 7,ra,rb; mfcr rt; rlwinm rt,rt,31,31,31;*/
  {MIR_FEQ, "r r r", FCMPU "; " EQEND},
  /* fcmpu 7,ra,rb; mfcr rt; rlwinm rt,rt,31,31,31;*/
  {MIR_DEQ, "r r r", FCMPU "; " EQEND},

#define NEEND EQEND "; o26 rs0 ra0 i1"
#define CRNOT(s, f) "o19 O33 ht" #s " ha" #f " hb" #f
  /* cmpd 7,ra,rb; mfcr rt; rlwinm rt,rt,31,31,31; xori rt,rt,1*/
  {MIR_NE, "r r r", CMPD "; " NEEND},
  /* cmpdi 7,ra,i; mfcr rt; rlwinm rt,rt,31,31,31; xori rt,rt,1*/
  {MIR_NE, "r r i", CMPDI (i) "; " NEEND},
  /* cmpw 7,ra,rb; mfcr rt; rlwinm rt,rt,31,31,31; xori rt,rt,1*/
  {MIR_NES, "r r r", CMPW "; " NEEND},
  /* cmpwi 7,ra,i; mfcr rt; rlwinm rt,rt,31,31,31; xori rt,rt,1*/
  {MIR_NES, "r r i", CMPWI (i) "; " NEEND},
  /* fcmpu 7,ra,rb; crnot 30,30; mfcr rt; rlwinm rt,rt,31,31,31;*/
  {MIR_FNE, "r r r", FCMPU "; " CRNOT (30, 30) "; " NEEND},
  /* fcmpu 7,ra,rb; crnot 30,30; mfcr rt; rlwinm rt,rt,31,31,31;*/
  {MIR_DNE, "r r r", FCMPU "; " CRNOT (30, 30) "; " NEEND},

#define RLWINM(n) "o21 rs0 ra0 sh" #n " mb31 me31"
  /* cmpd 7,ra,rb; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_LT, "r r r", CMPD "; " MFCR ";  " RLWINM (29)},
  /* cmpdi 7,ra,i; mfcr rt; rlwinm rt,rt,29,31,31*/
  {MIR_LT, "r r i", CMPDI (i) "; " MFCR ";  " RLWINM (29)},
  /* cmpw 7,ra,rb; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_LTS, "r r r", CMPW "; " MFCR ";  " RLWINM (29)},
  /* cmpwi 7,ra,i; mfcr rt; rlwinm rt,rt,29,31,31*/
  {MIR_LTS, "r r i", CMPWI (i) "; " MFCR ";  " RLWINM (29)},
  /* fcmpu 7,ra,rb; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_FLT, "r r r", FCMPU "; " MFCR ";  " RLWINM (29)},
  /* fcmpu 7,ra,rb; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_DLT, "r r r", FCMPU "; " MFCR ";  " RLWINM (29)},

#define CMPLD "o31 O32 bf7 L1 ra1 rb2"
#define CMPLDI "o10 bf7 L1 ra1 u"
#define CMPLW "o31 O32 bf7 L0 ra1 rb2"
#define CMPLWI "o10 bf7 L0 ra1 u"
  /* cmpld 7,ra,rb; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_ULT, "r r r", CMPLD "; " MFCR ";  " RLWINM (29)},
  /* cmpldi 7,ra,u; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_ULT, "r r u", CMPLDI "; " MFCR ";  " RLWINM (29)},
  /* cmplw 7,ra,rb; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_ULTS, "r r r", CMPLW "; " MFCR ";  " RLWINM (29)},
  /* cmplwi 7,ra,u; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_ULTS, "r r u", CMPLWI "; " MFCR ";  " RLWINM (29)},

  /* cmpd 7,ra,rb; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_GE, "r r r", CMPD "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},
  /* cmpdi 7,ra,i; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31*/
  {MIR_GE, "r r i", CMPDI (i) "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},
  /* cmpw 7,ra,rb; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_GES, "r r r", CMPW "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},
  /* cmpwi 7,ra,i; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31*/
  {MIR_GES, "r r i", CMPWI (i) "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},
  /* fcmpu 7,ra,rb; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_FGE, "r r r", FCMPU "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},
  /* fcmpu 7,ra,rb; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_DGE, "r r r", FCMPU "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},
  /* cmpld 7,ra,rb; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_UGE, "r r r", CMPLD "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},
  /* cmpldi 7,ra,u; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_UGE, "r r u", CMPLDI "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},
  /* cmplw 7,ra,rb; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_UGES, "r r r", CMPLW "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},
  /* cmplwi 7,ra,u; crnot 28,28; mfcr rt; rlwinm rt,rt,29,31,31;*/
  {MIR_UGES, "r r u", CMPLWI "; " CRNOT (28, 28) "; " MFCR ";  " RLWINM (29)},

  /* cmpd 7,ra,rb; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_GT, "r r r", CMPD "; " MFCR ";  " RLWINM (30)},
  /* cmpdi 7,ra,i; mfcr rt; rlwinm rt,rt,30,31,31*/
  {MIR_GT, "r r i", CMPDI (i) "; " MFCR ";  " RLWINM (30)},
  /* cmpw 7,ra,rb; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_GTS, "r r r", CMPW "; " MFCR ";  " RLWINM (30)},
  /* cmpwi 7,ra,i; mfcr rt; rlwinm rt,rt,30,31,31*/
  {MIR_GTS, "r r i", CMPWI (i) "; " MFCR ";  " RLWINM (30)},
  /* fcmpu 7,ra,rb; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_FGT, "r r r", FCMPU "; " MFCR ";  " RLWINM (30)},
  /* fcmpu 7,ra,rb; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_DGT, "r r r", FCMPU "; " MFCR ";  " RLWINM (30)},
  /* cmpld 7,ra,rb; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_UGT, "r r r", CMPLD "; " MFCR ";  " RLWINM (30)},
  /* cmpldi 7,ra,u; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_UGT, "r r u", CMPLDI "; " MFCR ";  " RLWINM (30)},
  /* cmplw 7,ra,rb; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_UGTS, "r r r", CMPLW "; " MFCR ";  " RLWINM (30)},
  /* cmplwi 7,ra,u; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_UGTS, "r r u", CMPLWI "; " MFCR ";  " RLWINM (30)},

  /* cmpd 7,ra,rb; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_LE, "r r r", CMPD "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},
  /* cmpdi 7,ra,i; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31*/
  {MIR_LE, "r r i", CMPDI (i) "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},
  /* cmpw 7,ra,rb; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_LES, "r r r", CMPW "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},
  /* cmpwi 7,ra,i; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31*/
  {MIR_LES, "r r i", CMPWI (i) "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},
  /* fcmpu 7,ra,rb; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_FLE, "r r r", FCMPU "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},
  /* fcmpu 7,ra,rb; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_DLE, "r r r", FCMPU "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},
  /* cmpld 7,ra,rb; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_ULE, "r r r", CMPLD "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},
  /* cmpldi 7,ra,u; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_ULE, "r r u", CMPLDI "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},
  /* cmplw 7,ra,rb; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_ULES, "r r r", CMPLW "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},
  /* cmplwi 7,ra,u; crnot 29,29; mfcr rt; rlwinm rt,rt,30,31,31;*/
  {MIR_ULES, "r r u", CMPLWI "; " CRNOT (29, 29) "; " MFCR ";  " RLWINM (30)},

  {MIR_JMP, "L", "o18 L"}, /* 24-bit offset word jmp */

#define BRC(o, i) "o16 BO" #o " BI" #i " l"
#define BRCL(o, i) "o16 BO" #o " BI" #i " l8; o18 L"
#define BRLOG(CODE, CMP, BI, COND, NEG_COND)                                       \
  {CODE, "l r", CMP (i0) "; " BRC (COND, BI)},        /* cmp 7,ra1,0;bcond 7,l; */ \
    {CODE, "L r", CMP (i0) "; " BRCL (NEG_COND, BI)}, /* cmp 7,ra1,0;bneg_cond 7,o8;b L;l8:*/

  BRLOG (MIR_BT, CMPDI, 30, 4, 12) BRLOG (MIR_BTS, CMPWI, 30, 4, 12)
    BRLOG (MIR_BF, CMPDI, 30, 12, 4) BRLOG (MIR_BFS, CMPWI, 30, 12, 4)

#define BRCMP(CODE, CMP, CMPI, BI, COND, NEG_COND)                                            \
  {CODE, "l r r", CMP "; " BRC (COND, BI)},         /* cmp 7,ra1,rb2;bcond 7,l; */            \
    {CODE, "l r i", CMPI "; " BRC (COND, BI)},      /* cmpi 7,ra1,i;bcond 7,l;:*/             \
    {CODE, "L r r", CMP "; " BRCL (NEG_COND, BI)},  /* cmp 7,ra1,rb2;bneg_cond 7,o8;b L;l8:*/ \
    {CODE, "L r i", CMPI "; " BRCL (NEG_COND, BI)}, /* cmp 7,ra1,i;bneg_cond 7,o8;b L;l8:*/

#define BRFCMP(CODE, BI, COND, NEG_COND)                                            \
  {CODE, "l r r", FCMPU "; " BRC (COND, BI)},        /* cmp 7,ra1,rb2;bcond 7,l; */ \
    {CODE, "L r r", FCMPU "; " BRCL (NEG_COND, BI)}, /* cmp 7,ra1,rb2;bneg_cond 7,o8;b L;l8:*/

  // all ld insn are changed to builtins and bt/bts
  BRCMP (MIR_BEQ, CMPD, CMPDI (i), 30, 12, 4) BRCMP (MIR_BEQS, CMPW, CMPWI (i), 30, 12, 4)
    BRFCMP (MIR_FBEQ, 30, 12, 4) BRFCMP (MIR_DBEQ, 30, 12, 4)

      BRCMP (MIR_BNE, CMPD, CMPDI (i), 30, 4, 12) BRCMP (MIR_BNES, CMPW, CMPWI (i), 30, 4, 12)
        BRFCMP (MIR_FBNE, 30, 4, 12) BRFCMP (MIR_DBNE, 30, 4, 12)

#define BRUCMP(CODE, CMP, CMPI, BI, COND, NEG_COND)                                           \
  {CODE, "l r r", CMP "; " BRC (COND, BI)},         /* cmp 7,ra1,rb2;bcond 7,l; */            \
    {CODE, "l r u", CMPI "; " BRC (COND, BI)},      /* cmpi 7,ra1,u;bcond 7,l;:*/             \
    {CODE, "L r r", CMP "; " BRCL (NEG_COND, BI)},  /* cmp 7,ra1,rb2;bneg_cond 7,o8;b L;l8:*/ \
    {CODE, "L r u", CMPI "; " BRCL (NEG_COND, BI)}, /* cmp 7,ra1,u;bneg_cond 7,o8;b L;l8:*/

          BRCMP (MIR_BLT, CMPD, CMPDI (i), 28, 12, 4) BRCMP (MIR_BLTS, CMPW, CMPWI (i), 28, 12, 4)
            BRFCMP (MIR_FBLT, 28, 12, 4) BRFCMP (MIR_DBLT, 28, 12, 4)
              BRUCMP (MIR_UBLT, CMPLD, CMPLDI, 28, 12, 4) BRUCMP (MIR_UBLTS, CMPLW, CMPLWI, 28, 12,
                                                                  4)

                BRCMP (MIR_BGE, CMPD, CMPDI (i), 28, 4, 12)
                  BRCMP (MIR_BGES, CMPW, CMPWI (i), 28, 4, 12) BRFCMP (MIR_FBGE, 28, 4, 12)
                    BRFCMP (MIR_DBGE, 28, 4, 12) BRUCMP (MIR_UBGE, CMPLD, CMPLDI, 28, 4, 12)
                      BRUCMP (MIR_UBGES, CMPLW, CMPLWI, 28, 4, 12)

                        BRCMP (MIR_BGT, CMPD, CMPDI (i), 29, 12, 4)
                          BRCMP (MIR_BGTS, CMPW, CMPWI (i), 29, 12, 4) BRFCMP (MIR_FBGT, 29, 12, 4)
                            BRFCMP (MIR_DBGT, 29, 12, 4) BRUCMP (MIR_UBGT, CMPLD, CMPLDI, 29, 12, 4)
                              BRUCMP (MIR_UBGTS, CMPLW, CMPLWI, 29, 12, 4)

                                BRCMP (MIR_BLE, CMPD, CMPDI (i), 29, 4, 12)
                                  BRCMP (MIR_BLES, CMPW, CMPWI (i), 29, 4, 12)
                                    BRFCMP (MIR_FBLE, 29, 4, 12) BRFCMP (MIR_DBLE, 29, 4, 12)
                                      BRUCMP (MIR_UBLE, CMPLD, CMPLDI, 29, 4, 12)
                                        BRUCMP (MIR_UBLES, CMPLW, CMPLWI, 29, 4, 12)

#define NEG "o31 O104 rt0 ra1"
#define FNEG "o63 O40 rt0 rb1"
                                          {MIR_NEG, "r r", NEG}, /* neg Rt,Ra */
  {MIR_NEGS, "r r", NEG},                                        /* neg Rt,Ra */
  {MIR_FNEG, "r r", FNEG},                                       /* fneg rt,rb */
  {MIR_DNEG, "r r", FNEG},                                       /* fneg rt,rb */
// ldneg is a builtin

#define SHR(s) "o31 O" #s " ra0 rs1 rb2"
  {MIR_LSH, "r r r", SHR (27)},                    /* sld ra,rs,rb */
  {MIR_LSHS, "r r r", SHR (24)},                   /* slw ra,rs,rb */
  {MIR_LSH, "r r Sh", "o30 ra0 rs1 Sh meSh"},      /* rldicr ra,rs,sh,63-sh */
  {MIR_LSHS, "r r sh", "o21 ra0 rs1 sh mb0 mesh"}, /* rlwinm ra,rs,sh,0,31-sh */

  {MIR_RSH, "r r r", SHR (794)},               /* srad ra,rs,rb */
  {MIR_RSHS, "r r r", SHR (792)},              /* sraw ra,rs,rb */
  {MIR_RSH, "r r Sh", "o31 p413 rs1 ra0 Sh"},  /* sradi ra,rs */
  {MIR_RSHS, "r r sh", "o31 O824 rs1 ra0 sh"}, /* srawi ra,rs */

  {MIR_URSH, "r r r", SHR (539)},                     /* srd ra,rs,rb */
  {MIR_URSHS, "r r r", SHR (536)},                    /* srw ra,rs,rb */
  {MIR_URSH, "r r Sh", "o30 ra0 rs1 Shr mbSh"},       /* rldicl ra,rs,64-sh,sh */
  {MIR_URSHS, "r r sh", "o21 ra0 rs1 shr mbsh me31"}, /* rlwinm ra,rs,32-sh,sh,31 */

// ??? nand nor
#define LOGR(s) "o31 O" #s "  ra0 rs1 rb2"
#define LOGU(s) "o" #s "  ra0 rs1 u"
#define LOGUS(s) "o" #s "  ra0 rs1 U"
  {MIR_AND, "r r r", LOGR (28)},   /* and ra,rs,rb */
  {MIR_AND, "r r u", LOGU (28)},   /* andi. ra,rs,u */
  {MIR_AND, "r r U", LOGUS (29)},  /* andis. ra,rs,U */
  {MIR_ANDS, "r r r", LOGR (28)},  /* and ra,rs,rb */
  {MIR_ANDS, "r r u", LOGU (28)},  /* andi. ra,rs,u */
  {MIR_ANDS, "r r U", LOGUS (29)}, /* andis. ra,rs,U */

  {MIR_OR, "r r r", LOGR (444)},  /* or ra,rs,rb */
  {MIR_OR, "r r u", LOGU (24)},   /* ori ra,rs,u */
  {MIR_OR, "r r U", LOGUS (25)},  /* oris ra,rs,U */
  {MIR_ORS, "r r r", LOGR (444)}, /* or ra,rs,rb */
  {MIR_ORS, "r r u", LOGU (24)},  /* ori ra,rs,u */
  {MIR_ORS, "r r U", LOGUS (25)}, /* oris ra,rs,U */

  {MIR_XOR, "r r r", LOGR (316)},  /* xor ra,rs,rb */
  {MIR_XOR, "r r u", LOGU (26)},   /* xori ra,rs,u */
  {MIR_XOR, "r r U", LOGUS (27)},  /* xoris ra,rs,U */
  {MIR_XORS, "r r r", LOGR (316)}, /* xor ra,rs,rb */
  {MIR_XORS, "r r u", LOGU (26)},  /* xori ra,rs,u */
  {MIR_XORS, "r r U", LOGUS (27)}, /* xoris ra,rs,U */

  /* std rt1,-16(r1); lfd f0,-16(r1); fcfids rt0,f0: */
  {MIR_I2F, "r r", "o62 rs1 mt; o50 ht32 mt; o59 O846 rt0 hb32"},
  /* std rt1,-16(r1); lfd f0,-16(r1); fcfid rt0,f0: */
  {MIR_I2D, "r r", "o62 rs1 mt; o50 ht32 mt; o63 O846 rt0 hb32"},
  /* std rt1,-16(r1); lfd f0,-16(r1); fcfidus rt0,f0: */
  {MIR_UI2F, "r r", "o62 rs1 mt; o50 ht32 mt; o59 O974 rt0 hb32"},
  /* std rt1,-16(r1); lfd f0,-16(r1); fcfid rt0,f0: */
  {MIR_UI2D, "r r", "o62 rs1 mt; o50 ht32 mt; o63 O974 rt0 hb32"},
  /* fctidz f0,rb; stfd f0,-16(r1);ld rt,-16(r1): */
  {MIR_F2I, "r r", "o63 O815 ht32 rb1; o54 hs32 mt; o58 rt0 mt"},
  /* fctidz f0,rb; stfd f0,-16(r1);ld rt,-16(r1): */
  {MIR_D2I, "r r", "o63 O815 ht32 rb1; o54 hs32 mt; o58 rt0 mt"},
  {MIR_F2D, "r r", "o63 O72 rt0 rb1"}, /* fmr rt,rb */
  {MIR_D2F, "r r", "o63 O12 rt0 rb1"}, /* frsp rt,rb */
// i2ld, ui2ld, ld2i, f2ld, d2ld, ld2f, ld2d are builtins

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  {MIR_CALL, "X h12 $", "o31 O467 rs1 sr9; o19 O528 BO20 BI0 LK1"}, /* mtctr r12; bcctrl */
  {MIR_CALL, "X r $",
   "o31 O444 ha12 rs1 rb1; o31 O467 rs1 sr9; o19 O528 BO20 BI0 LK1"}, /* mr r12,r; mtctr r; bcctrl
                                                                       */
#else
  /* mr r2,r0; ld r0,0(r2); ld r2,8(r2); mtctr r0; bcctrl */
  {MIR_CALL, "X h0 $",
   "o31 O444 ha2 hs0 hb0; o58 ht0 ha2; o58 ht2 ha2 i8; o31 O467 hs0 sr9; o19 O528 BO20 BI0 LK1; "
   "o31 O444 ha0 hs0 hb0"},
  /* ld r0,0(r); ld r2,8(r); mtctr r0; bcctrl */
  {MIR_CALL, "X r $",
   "o58 ht0 ra1; o58 ht2 ra1 i8; o31 O467 hs0 sr9; o19 O528 BO20 BI0 LK1; o31 O444 ha0 hs0 hb0"},
#endif

  {MIR_RET, "$", "o19 O16 BO20 BI0"}, /* bclr */

#if 0
  /* ld r10,16(r1); subf r1,rt,r1; ldx r0,(r1,rt); std r10,16(r1); std r0,0(r1);
     std r2,PPC64_TOC_OFFSET(r1); add rt,r1,PPC64_STACK_HEADER_SIZE+PARAM_AREA_SIZE: */
#define ALLOCA_END                                               \
  "o58 ht10 ha1 i16; o31 O40 ht1 ra0 hb1; o31 O21 ht0 ha1 rb0; " \
  "o62 hs10 ha1 i16; o62 hs0 ha1;o62 hs2 ha1 at; o14 rt0 ha1 ih"
#else
/* subf r1,rt,r1; ldx r0,(r1,rt); std r0,0(r1);
   add rt,r1,PPC64_STACK_HEADER_SIZE+PARAM_AREA_SIZE: */
#define ALLOCA_END                             \
  "o31 O40 ht1 ra0 hb1; o31 O21 ht0 ha1 rb0; " \
  "o62 hs0 ha1; o14 rt0 ha1 ih"
#endif
  /* addi rt,ra,15;rldicr rt,rt,0,59; ... : */
  {MIR_ALLOCA, "r r", "o14 rt0 ra1 i15; o30 ra0 rs0 Sh0 Me59; " ALLOCA_END},
  /* mov rt,ia; ...: */
  {MIR_ALLOCA, "r ia", "o14 rt0 ha0 ia; " ALLOCA_END},

  {MIR_BSTART, "r", "o31 O444 ra0 hs1 hb1"}, /* or ra,r1,r1 */
#if 0
  /* ld r0,0(r1);ld r2,PPC64_TOC_OFFSET(r1);ld r10,16(sp);or r1,rs,rs
     std r0,0(r1);std r2,PPC64_TOC_OFFSET(r1);std r10,16(sp): */
  {MIR_BEND, "r", "o58 hs0 ha1;o58 hs2 ha1 at;o58 hs10 ha1 i16;o31 O444 ha1 rs0 rb0;"
   "o62 hs0 ha1;o62 hs2 ha1 at;o62 hs10 ha1 i16"},
#else
  /* ld r0,0(r1);or r1,rs,rs; std r0,0(r1): */
  {MIR_BEND, "r", "o58 hs0 ha1;o31 O444 ha1 rs0 rb0; o62 hs0 ha1"},
#endif

  /* bl l4; mflr r0; rldicr r10,rt,3,60; add r10,r0,r10; ld r0,table-disp(r10); mtctr r0; bcctr;
     TableContent: */
  {MIR_SWITCH, "r $",
   "o18 l4 LK1; o31 O339 ht0 sr8; o30 ha10 rs0 Sh3 Me60; o31 O266 ht10 ha0 hb10; o58 ht0 ha10 T; "
   "o31 O467 hs0 sr9; o19 O528 BO20 BI0"},
};

static void target_get_early_clobbered_hard_regs (MIR_insn_t insn, MIR_reg_t *hr1, MIR_reg_t *hr2) {
  MIR_insn_code_t code = insn->code;

  *hr1 = *hr2 = MIR_NON_HARD_REG;
  if (code == MIR_MOD || code == MIR_MODS || code == MIR_UMOD || code == MIR_UMODS) {
    *hr1 = R10_HARD_REG;
  } else if (code == MIR_I2F || code == MIR_I2D || code == MIR_UI2F || code == MIR_UI2D
             || code == MIR_F2I || code == MIR_D2I) {
    *hr1 = F0_HARD_REG;
  } else if (code == MIR_LDMOV) { /* if mem base reg is R0 */
    *hr1 = R11_HARD_REG; /* don't use arg regs as ldmov can be used in param passing part */
  } else if (code == MIR_CALL || code == MIR_INLINE) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    *hr1 = R12_HARD_REG;
#else
    *hr1 = R2_HARD_REG;
#endif
#if 0
  } else if (code == MIR_ALLOCA || code == MIR_BEND) {
    *hr1 = R10_HARD_REG;
#endif
  } else if (code == MIR_SWITCH) {
    *hr1 = R10_HARD_REG;
  }
}

static int pattern_index_cmp (const void *a1, const void *a2) {
  int i1 = *(const int *) a1, i2 = *(const int *) a2;
  int c1 = (int) patterns[i1].code, c2 = (int) patterns[i2].code;

  return c1 != c2 ? c1 - c2 : (long) i1 - (long) i2;
}

static void patterns_init (gen_ctx_t gen_ctx) {
  int i, ind, n = sizeof (patterns) / sizeof (struct pattern);
  MIR_insn_code_t prev_code, code;
  insn_pattern_info_t *info_addr;
  insn_pattern_info_t pinfo = {0, 0};

  VARR_CREATE (int, pattern_indexes, 0);
  for (i = 0; i < n; i++) VARR_PUSH (int, pattern_indexes, i);
  qsort (VARR_ADDR (int, pattern_indexes), n, sizeof (int), pattern_index_cmp);
  VARR_CREATE (insn_pattern_info_t, insn_pattern_info, 0);
  for (i = 0; i < MIR_INSN_BOUND; i++) VARR_PUSH (insn_pattern_info_t, insn_pattern_info, pinfo);
  info_addr = VARR_ADDR (insn_pattern_info_t, insn_pattern_info);
  for (prev_code = MIR_INSN_BOUND, i = 0; i < n; i++) {
    ind = VARR_GET (int, pattern_indexes, i);
    if ((code = patterns[ind].code) != prev_code) {
      if (i != 0) info_addr[prev_code].num = i - info_addr[prev_code].start;
      info_addr[code].start = i;
      prev_code = code;
    }
  }
  assert (prev_code != MIR_INSN_BOUND);
  info_addr[prev_code].num = n - info_addr[prev_code].start;
}

static int int16_p (int64_t i) { return -(1 << 15) <= i && i < (1 << 15); }
static int uint16_p (uint64_t u) { return !(u >> 16); }
static int int16_shifted_p (int64_t i) { return (i & 0xffff) == 0 && int16_p (i >> 16); }
static int uint16_shifted_p (uint64_t u) { return (u & 0xffff) == 0 && uint16_p (u >> 16); }
static int uint31_p (uint64_t u) { return !(u >> 31); }
static int uint47_p (uint64_t u) { return !(u >> 47); }
static int uint32_p (uint64_t u) { return !(u >> 32); }
static int uint6_p (uint64_t u) { return !(u >> 6); }
static int uint5_p (uint64_t u) { return !(u >> 5); }
static int negative32_p (uint64_t u, uint64_t *n) {
  if (((u >> 31) & 1) == 0) return FALSE;
  /* high 32-bit part pattern: 0*1*, n contains number of ones. */
  for (u >>= 32, *n = 0; u & 1; u >>= 1, (*n)++)
    ;
  return u == 0;
}

static int pattern_match_p (gen_ctx_t gen_ctx, const struct pattern *pat, MIR_insn_t insn,
                            int use_short_label_p) {
  MIR_context_t ctx = gen_ctx->ctx;
  int nop;
  size_t nops = MIR_insn_nops (ctx, insn);
  const char *p;
  char ch, start_ch;
  MIR_op_t op;
  MIR_reg_t hr;

  for (nop = 0, p = pat->pattern; *p != 0; p++, nop++) {
    while (*p == ' ' || *p == '\t') p++;
    if (*p == '$') return TRUE;
    if (MIR_call_code_p (insn->code) && nop >= nops) return FALSE;
    gen_assert (nop < nops);
    op = insn->ops[nop];
    switch (start_ch = *p) {
    case 'X': break;
    case 'r':
      if (op.mode != MIR_OP_HARD_REG || op.u.hard_reg == LR_HARD_REG) return FALSE;
      break;
    case 'R':
      if (op.mode != MIR_OP_HARD_REG || op.u.hard_reg == R0_HARD_REG
          || op.u.hard_reg == LR_HARD_REG)
        return FALSE;
      break;
    case 'h':
      if (op.mode != MIR_OP_HARD_REG) return FALSE;
      ch = *++p;
      gen_assert ('0' <= ch && ch <= '9');
      hr = ch - '0';
      ch = *++p;
      if ('0' <= ch && ch <= '9')
        hr = hr * 10 + ch - '0';
      else
        --p;
      gen_assert (hr <= MAX_HARD_REG);
      if (op.u.hard_reg != hr) return FALSE;
      break;
    case 'm':
    case 'M': {
      MIR_type_t type, type2, type3 = MIR_T_BOUND;
      int ds_p = FALSE, l_p = FALSE, br0_p = FALSE, u_p = TRUE, s_p = TRUE;

      if (op.mode != MIR_OP_HARD_REG_MEM) return FALSE;
      ch = *++p;
      switch (ch) {
      case 'f':
        type = MIR_T_F;
        type2 = MIR_T_BOUND;
        break;
      case 'd':
        ch = *++p;
        if (ch == 's') {
          ds_p = s_p = TRUE;
          type = start_ch == 'M' ? MIR_T_I64 : MIR_T_I32;
          type2 = start_ch == 'M' ? MIR_T_U64 : MIR_T_BOUND;
#if MIR_PTR32
          if (start_ch == 'm') type3 = MIR_T_P;
#else
          if (start_ch == 'M') type3 = MIR_T_P;
#endif
        } else {
          p--;
          type = MIR_T_D;
          type2 = MIR_T_BOUND;
        }
        break;
      case 'l':
        ch = *++p;
        gen_assert (ch == 'd' && start_ch != 'M');
        ch = *++p;
        if (ch != '0')
          p--;
        else
          br0_p = TRUE;
        l_p = TRUE;
        type = MIR_T_LD;
        type2 = MIR_T_BOUND;
        break;
      case 'u':
      case 's':
        u_p = ch == 'u';
        s_p = ch == 's';
        ch = *++p;
        /* Fall through: */
      default:
        gen_assert ('0' <= ch && ch <= '3');
        if (ch == '0') {
          type = u_p ? MIR_T_U8 : MIR_T_I8;
          type2 = u_p && s_p ? MIR_T_I8 : MIR_T_BOUND;
        } else if (ch == '1') {
          type = u_p ? MIR_T_U16 : MIR_T_I16;
          type2 = u_p && s_p ? MIR_T_I16 : MIR_T_BOUND;
        } else if (ch == '2') {
          type = u_p ? MIR_T_U32 : MIR_T_I32;
          type2 = u_p && s_p ? MIR_T_I32 : MIR_T_BOUND;
#if MIR_PTR32
          if (u_p) type3 = MIR_T_P;
#endif
        } else {
          type = u_p ? MIR_T_U64 : MIR_T_I64;
          type2 = u_p && s_p ? MIR_T_I64 : MIR_T_BOUND;
#if MIR_PTR64
          type3 = MIR_T_P;
#endif
        }
      }
      if (op.u.hard_reg_mem.type != type && op.u.hard_reg_mem.type != type2
          && op.u.hard_reg_mem.type != type3)
        return FALSE;
      if (ds_p
          && (op.u.hard_reg_mem.index != MIR_NON_HARD_REG || op.u.hard_reg_mem.base == R0_HARD_REG
              || op.u.hard_reg_mem.disp % 4 != 0 || !int16_p (op.u.hard_reg_mem.disp)))
        return FALSE;
      if (!ds_p && start_ch == 'm'
          && (op.u.hard_reg_mem.index != MIR_NON_HARD_REG
              || (!br0_p && op.u.hard_reg_mem.base == R0_HARD_REG)
              || (br0_p && op.u.hard_reg_mem.base != R0_HARD_REG)
              || !int16_p (op.u.hard_reg_mem.disp)
              || (l_p && !int16_p (op.u.hard_reg_mem.disp + 8))))
        return FALSE;
      if (!ds_p && start_ch == 'M'
          && (op.u.hard_reg_mem.disp != 0
              || (op.u.hard_reg_mem.index != MIR_NON_HARD_REG && op.u.hard_reg_mem.scale != 1)
              || (op.u.hard_reg_mem.base == R0_HARD_REG
                  && op.u.hard_reg_mem.index != MIR_NON_HARD_REG)))
        return FALSE;
      break;
    }
    case 'i':
      if (op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) return FALSE;
      ch = *++p;
      if (ch == 'a') {
        if (!int16_p ((op.u.i + 15) / 16 * 16)) return FALSE;
      } else {
        p--;
        if (!int16_p (op.u.i)) return FALSE;
      }
      break;
    case 'u':
      if ((op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) || !uint16_p (op.u.u)) return FALSE;
      break;
    case 'I':
      if ((op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) || !int16_shifted_p (op.u.i))
        return FALSE;
      break;
    case 'U':
      if ((op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) || !uint16_shifted_p (op.u.u))
        return FALSE;
      break;
    case 'x':
    case 'z':
    case 'Z': {
      uint64_t v, n;

      if (op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT && op.mode != MIR_OP_REF) return FALSE;
      if (op.mode != MIR_OP_REF) {
        v = op.u.u;
      } else if (op.u.ref->item_type == MIR_data_item && op.u.ref->u.data->name != NULL
                 && _MIR_reserved_ref_name_p (ctx, op.u.ref->u.data->name)) {
        v = (uint64_t) op.u.ref->u.data->u.els;
      } else {
        v = (uint64_t) op.u.ref->addr;
      }
      if (start_ch == 'x') {
        if (!negative32_p (v, &n)) return FALSE;
      } else {
        ch = *++p;
        if (ch == 's') {
          if (start_ch == 'z' ? !uint31_p (v) : !uint47_p (op.u.u)) return FALSE;
        } else {
          p--;
          if (start_ch == 'z' && !uint32_p (v)) return FALSE;
        }
      }
      break;
    }
    case 's':
    case 'S':
      ch = *++p;
      gen_assert (ch == 'h');
      if (op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) return FALSE;
      if ((start_ch == 's' && !uint5_p (op.u.u)) || (start_ch == 'S' && !uint6_p (op.u.u)))
        return FALSE;
      break;
    case 'l':
      if (op.mode != MIR_OP_LABEL || !use_short_label_p) return FALSE;
      break;
    case 'L':
      if (op.mode != MIR_OP_LABEL && op.mode != MIR_OP_REF) return FALSE;
      break;
    default: gen_assert (FALSE);
    }
  }
  gen_assert (nop == nops);
  return TRUE;
}

static const char *find_insn_pattern_replacement (gen_ctx_t gen_ctx, MIR_insn_t insn,
                                                  int use_short_label_p) {
  int i;
  const struct pattern *pat;
  insn_pattern_info_t info = VARR_GET (insn_pattern_info_t, insn_pattern_info, insn->code);

  for (i = 0; i < info.num; i++) {
    pat = &patterns[VARR_GET (int, pattern_indexes, info.start + i)];
    if (pattern_match_p (gen_ctx, pat, insn, use_short_label_p)) return pat->replacement;
  }
  return NULL;
}

static void patterns_finish (gen_ctx_t gen_ctx) {
  VARR_DESTROY (int, pattern_indexes);
  VARR_DESTROY (insn_pattern_info_t, insn_pattern_info);
}

static int dec_value (int ch) { return '0' <= ch && ch <= '9' ? ch - '0' : -1; }

static uint64_t read_dec (const char **ptr) {
  int v;
  const char *p;
  uint64_t res = 0;

  for (p = *ptr; (v = dec_value (*p)) >= 0; p++) {
    gen_assert ((res >> 60) == 0);
    res = res * 10 + v;
  }
  gen_assert (p != *ptr);
  *ptr = p - 1;
  return res;
}

static uint32_t check_and_set_mask (uint32_t result_mask, uint32_t mask) {
  gen_assert ((result_mask & mask) == 0);
  return result_mask | mask;
}

static void put_uint32 (struct gen_ctx *gen_ctx, uint32_t v) {
  VARR_PUSH_ARR (uint8_t, result_code, (uint8_t *) &v, sizeof (v)); /* reserve */
  /* write with the right endianess: */
  ((uint32_t *) (VARR_ADDR (uint8_t, result_code) + VARR_LENGTH (uint8_t, result_code)))[-1] = v;
}

static void put_uint64 (struct gen_ctx *gen_ctx, uint64_t v) {
  VARR_PUSH_ARR (uint8_t, result_code, (uint8_t *) &v, sizeof (v)); /* reserve */
  ((uint64_t *) (VARR_ADDR (uint8_t, result_code) + VARR_LENGTH (uint8_t, result_code)))[-1] = v;
}

static void set_int64 (uint8_t *addr, int64_t v) { *(int64_t *) addr = v; }

static int64_t get_int64 (uint8_t *addr) { return *(int64_t *) addr; }

static void out_insn (gen_ctx_t gen_ctx, MIR_insn_t insn, const char *replacement) {
  MIR_context_t ctx = gen_ctx->ctx;
  size_t nops = MIR_insn_nops (ctx, insn);
  size_t offset;
  const char *p, *insn_str;
  label_ref_t lr;
  int switch_table_addr_insn_start = -1;
  uint32_t nop_binsn = 24 << (32 - 6); /* ori 0,0,0 */

  if (insn->code == MIR_ALLOCA
      && (insn->ops[1].mode == MIR_OP_INT || insn->ops[1].mode == MIR_OP_UINT))
    insn->ops[1].u.u = (insn->ops[1].u.u + 15) & -16;
  for (insn_str = replacement;; insn_str = p + 1) {
    MIR_op_t op;
    char ch, ch2, start_ch;
    uint32_t binsn = 0;
    int opcode = -1, opcode2 = -1, opcode3 = -1, opcode4 = -1, rt = -1, rs = -1, ra = -1, rb = -1,
        rc = -1, spreg = -1, sh = -1, Sh = -1;
    int disp = -1, disp4 = -1, mb = -1, me = -1, Mb = -1, Me = -1, bf = -1, BO = -1, BI = -1,
        imm = -1, LK = -1;
    int d = -1, lab_off = -1, lb = -1, label_ref_num = -1, n;
    uint32_t binsn_mask = 0;
    int switch_table_addr_p = FALSE;

    for (p = insn_str; (ch = *p) != '\0' && ch != ';'; p++) {
      if ((ch = *p) == 0 || ch == ';') break;
      switch ((start_ch = ch = *p)) {
      case ' ':
      case '\t': break;
      case 'o':
        ch2 = *++p;
        gen_assert (dec_value (ch2) >= 0 && opcode < 0);
        opcode = read_dec (&p);
        break;
      case 'O':
        ch2 = *++p;
        gen_assert (dec_value (ch2) >= 0 && opcode2 < 0);
        opcode2 = read_dec (&p);
        break;
      case 'p':
        ch2 = *++p;
        gen_assert (dec_value (ch2) >= 0 && opcode3 < 0);
        opcode3 = read_dec (&p);
        break;
      case 'P':
        ch2 = *++p;
        gen_assert (dec_value (ch2) >= 0 && opcode4 < 0);
        opcode4 = read_dec (&p);
        break;
      case 'r':
      case 'n':
      case 'R':
      case 'h': {
        int reg;

        ch2 = *++p;
        gen_assert (ch2 == 't' || ch2 == 's' || ch2 == 'a' || ch2 == 'b' || ch2 == 'c');
        gen_assert (start_ch != 'R' || ch2 == 'a');
        ch = *++p;
        if (start_ch == 'h') {
          reg = read_dec (&p);
        } else {
          gen_assert ('0' <= ch && ch <= '2' && ch - '0' < insn->nops);
          op = insn->ops[ch - '0'];
          gen_assert (op.mode == MIR_OP_HARD_REG);
          reg = op.u.hard_reg + (start_ch == 'n' ? 1 : 0);
        }
        if (reg > R31_HARD_REG) reg -= F0_HARD_REG;
        gen_assert (reg <= 31);
        if (ch2 == 't') {
          gen_assert (rt < 0);
          rt = reg;
        } else if (ch2 == 's') {
          gen_assert (rs < 0);
          rs = reg;
        } else if (ch2 == 'a') {
          gen_assert (ra < 0);
          ra = reg;
        } else if (ch2 == 'b') {
          gen_assert (rb < 0);
          rb = reg;
        } else {
          gen_assert (rc < 0);
          rc = reg;
        }
        break;
      }
      case 's':
        ch2 = *++p;
        if (ch2 == 'r') {
          ch2 = *++p;
          gen_assert (dec_value (ch2) >= 0 && spreg < 0);
          spreg = read_dec (&p);
        } else if (ch2 == 'h') {
          op = insn->ops[2];
          ch2 = *++p;
          gen_assert (sh < 0);
          if (dec_value (ch2) >= 0) {
            sh = read_dec (&p);
          } else if (ch2 == 'r') {
            gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT);
            sh = 32 - op.u.u;
          } else {
            --p;
            gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT);
            sh = op.u.u;
          }
        }
        break;
      case 'S':
        ch2 = *++p;
        gen_assert (ch2 == 'h' && Sh < 0);
        ch2 = *++p;
        if (dec_value (ch2) >= 0) {
          Sh = read_dec (&p);
        } else if (ch2 == 'r') {
          op = insn->ops[2];
          gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT);
          Sh = 64 - op.u.u;
        } else {
          --p;
          op = insn->ops[2];
          gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT);
          Sh = op.u.u;
        }
        break;
      case 'M': {
        int b_p;

        ch2 = *++p;
        if (ch2 == '9') {
          ch2 = *++p;
          gen_assert (ch2 == '1');
          ch2 = *++p;
          gen_assert (ch2 == '0' && ra < 0 && rb < 0);
          ra = R9_HARD_REG;
          rb = R10_HARD_REG;
        } else if ((b_p = ch2 == 'b') || ch2 == 'e') {
          ch2 = *++p;
          gen_assert (dec_value (ch2) >= 0 && Mb < 0 && Me < 0);
          if (b_p) {
            Mb = read_dec (&p);
            Mb = ((Mb & 0x1f) << 1) | ((Mb >> 5) & 1);
          } else {
            Me = read_dec (&p);
            Me = ((Me & 0x1f) << 1) | ((Me >> 5) & 1);
          }
        } else {
          op = insn->ops[0].mode == MIR_OP_HARD_REG_MEM ? insn->ops[0] : insn->ops[1];
          gen_assert (op.mode == MIR_OP_HARD_REG_MEM);
          if (ch2 == 'd') {
            ch2 = *++p;
            gen_assert (ch2 == 's' && ra < 0 && disp4 < 0);
            ra = op.u.hard_reg_mem.base;
            if (ra == MIR_NON_HARD_REG) ra = R0_HARD_REG;
            disp4 = op.u.hard_reg_mem.disp & 0xffff;
            gen_assert ((disp4 & 0x3) == 0);
          } else {
            --p;
            gen_assert (ra < 0 && rb < 0);
            ra = op.u.hard_reg_mem.base;
            rb = op.u.hard_reg_mem.index;
            if (rb == MIR_NON_HARD_REG) {
              rb = ra;
              ra = R0_HARD_REG;
            } else if (ra == MIR_NON_HARD_REG) {
              ra = R0_HARD_REG;
            } else if (ra == R0_HARD_REG) {
              ra = rb;
              ra = R0_HARD_REG;
            }
          }
        }
        break;
      }
      case 'm': {
        int b_p, single_p;

        ch2 = *++p;
        if (ch2 == 't') {
          gen_assert (ra < 0 && disp < 0);
          disp = (-16) & 0xffff;
          ra = R1_HARD_REG;
        } else if ((b_p = ch2 == 'b') || ch2 == 'e') {
          ch2 = *++p;
          if (dec_value (ch2) >= 0) {
            if (b_p) {
              gen_assert (mb < 0);
              mb = read_dec (&p);
            } else {
              gen_assert (me < 0);
              me = read_dec (&p);
            }
          } else {
            single_p = ch2 == 'S';
            gen_assert (ch2 == 's' || ch2 == 'S');
            ch2 = *++p;
            gen_assert (ch2 == 'h');
            op = insn->ops[2];
            gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT);
            if (single_p) {
              gen_assert (Mb < 0 && Me < 0);
              if (b_p) {
                Mb = op.u.i;
                Mb = ((Mb & 0x1f) << 1) | ((Mb >> 5) & 1);
              } else {
                Me = 63 - op.u.i;
                Me = ((Me & 0x1f) << 1) | ((Me >> 5) & 1);
              }
            } else if (b_p) {
              gen_assert (mb < 0);
              mb = op.u.i;
            } else {
              gen_assert (me < 0);
              me = 31 - op.u.i;
            }
          }
        } else {
          op = insn->ops[0].mode == MIR_OP_HARD_REG_MEM ? insn->ops[0] : insn->ops[1];
          gen_assert (op.mode == MIR_OP_HARD_REG_MEM);
          if (ch2 == 'd') {
            ch2 = *++p;
            gen_assert (ch2 == 's' && ra < 0 && disp4 < 0);
            ra = op.u.hard_reg_mem.base;
            if (ra == MIR_NON_HARD_REG) ra = R0_HARD_REG;
            disp4 = op.u.hard_reg_mem.disp & 0xffff;
            gen_assert ((disp4 & 0x3) == 0);
          } else {
            if (ch2 != 'n') --p;
            gen_assert (ra < 0 && disp < 0);
            ra = op.u.hard_reg_mem.base;
            if (ra == MIR_NON_HARD_REG) ra = R0_HARD_REG;
            disp = (op.u.hard_reg_mem.disp + (ch2 != 'n' ? 0 : 8)) & 0xffff;
          }
        }
        break;
      }
      case 'd':
        ch2 = *++p;
        gen_assert (d < 0 && dec_value (ch2) >= 0);
        d = read_dec (&p);
        break;
      case 'i':
        ch2 = *++p;
        if (ch2 == 'a') {
          op = insn->ops[nops - 1];
          gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT);
          gen_assert (imm < 0);
          imm = (op.u.i + 15) / 16 * 16;
          break;
        } else if (ch2 == 'h') {
          gen_assert (imm < 0);
          imm = PPC64_STACK_HEADER_SIZE + param_save_area_size;
          break;
        } else if (dec_value (ch2) >= 0) {
          gen_assert (imm < 0);
          imm = read_dec (&p);
          break;
        }
        p--;
        /* fall through: */
      case 'u':
      case 'I':
      case 'U':
        op = insn->ops[nops - 1];
        gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT);
        gen_assert (imm < 0);
        imm = (start_ch == 'i' || start_ch == 'u' ? op.u.u : op.u.u >> 16) & 0xffff;
        break;
      case 'x':
      case 'z': {
        int ok_p;
        uint64_t v, n;

        op = insn->ops[nops - 1];
        gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT || op.mode == MIR_OP_REF);
        if (op.mode != MIR_OP_REF) {
          v = op.u.u;
        } else if (op.u.ref->item_type == MIR_data_item && op.u.ref->u.data->name != NULL
                   && _MIR_reserved_ref_name_p (ctx, op.u.ref->u.data->name)) {
          v = (uint64_t) op.u.ref->u.data->u.els;
        } else {
          v = (uint64_t) op.u.ref->addr;
        }
        if (start_ch == 'x') {
          ok_p = negative32_p (v, &n);
          n = 32 - n;
          gen_assert (Mb < 0 && ok_p);
          Mb = ((n & 0x1f) << 1) | ((n >> 5) & 1);
        } else {
          gen_assert (imm < 0);
          n = dec_value (*++p);
          gen_assert (n >= 0 && n <= 3);
          imm = (v >> (3 - n) * 16) & 0xffff;
        }
        break;
      }
      case 'b':
        ch2 = *++p;
        gen_assert (ch2 == 'f');
        ch2 = *++p;
        gen_assert (dec_value (ch2) >= 0);
        gen_assert (bf < 0);
        bf = read_dec (&p);
        break;
      case 'B': {
        int o_p;

        ch2 = *++p;
        gen_assert (ch2 == 'O' || ch2 == 'I');
        o_p = ch2 == 'O';
        ch2 = *++p;
        gen_assert (dec_value (ch2) >= 0);
        if (o_p) {
          gen_assert (BO < 0);
          BO = read_dec (&p);
        } else {
          gen_assert (BI < 0);
          BI = read_dec (&p);
        }
        break;
      }
      case 'l': {
        ch2 = *++p;
        if ((n = dec_value (ch2)) >= 0) {
          gen_assert (lab_off < 0 && (n & 0x3) == 0);
          lab_off = n;
        } else {
          --p;
          gen_assert (insn->code != MIR_CALL);
          op = insn->ops[0];
          gen_assert (op.mode == MIR_OP_LABEL);
          lr.abs_addr_p = FALSE;
          lr.short_addr_p = TRUE;
          lr.label_val_disp = 0;
          lr.label = op.u.label;
          label_ref_num = VARR_LENGTH (label_ref_t, label_refs);
          VARR_PUSH (label_ref_t, label_refs, lr);
        }
        break;
      }
      case 'L': {
        ch2 = *++p;
        if (ch2 == 'K') {
          ch2 = *++p;
          gen_assert (LK < 0 && dec_value (ch2) >= 0);
          LK = read_dec (&p);
          gen_assert (LK <= 1);
        } else if ((n = dec_value (ch2)) >= 0) {
          gen_assert (lb < 0);
          lb = n;
        } else {
          --p;
          op = insn->ops[insn->code != MIR_CALL ? 0 : 1];
          gen_assert (op.mode == MIR_OP_LABEL);
          lr.abs_addr_p = lr.short_addr_p = FALSE;
          lr.label_val_disp = 0;
          lr.label = op.u.label;
          label_ref_num = VARR_LENGTH (label_ref_t, label_refs);
          VARR_PUSH (label_ref_t, label_refs, lr);
        }
        break;
      }
      case 'a':
        gen_assert (imm < 0);
        ch2 = *++p;
        gen_assert (ch2 == 't' || ch2 == 'a');
        imm = ch2 == 't' ? PPC64_TOC_OFFSET : 15 + PPC64_STACK_HEADER_SIZE + param_save_area_size;
        break;
      case 'T':
        gen_assert (!switch_table_addr_p && switch_table_addr_insn_start < 0);
        switch_table_addr_p = TRUE;
        break;
      default: gen_assert (FALSE);
      }
    }

    if (opcode >= 0) {
      gen_assert (opcode < 64);
      binsn |= opcode << (32 - 6);
      binsn_mask = check_and_set_mask (binsn_mask, 0x3f << (32 - 6));
    }
    if (opcode2 >= 0) {
      gen_assert (opcode2 < (1 << 10));
      binsn |= opcode2 << 1;
      binsn_mask = check_and_set_mask (binsn_mask, 0x3ff << 1);
    }
    if (opcode3 >= 0) {
      gen_assert (opcode3 < (1 << 9));
      binsn |= opcode3 << 2;
      binsn_mask = check_and_set_mask (binsn_mask, 0x1ff << 2);
    }
    if (opcode4 >= 0) {
      gen_assert (opcode4 < (1 << 5));
      binsn |= opcode4 << 1;
      binsn_mask = check_and_set_mask (binsn_mask, 0x1f << 1);
    }
    if (rt >= 0) {
      gen_assert (rt < 32);
      binsn |= rt << (32 - 11);
      binsn_mask = check_and_set_mask (binsn_mask, 0x1f << (32 - 11));
    }
    if (rs >= 0) {
      gen_assert (rs < 32);
      binsn |= rs << (32 - 11);
      binsn_mask = check_and_set_mask (binsn_mask, 0x1f << (32 - 11));
    }
    if (ra >= 0) {
      gen_assert (ra < 32);
      binsn |= ra << (32 - 16);
      binsn_mask = check_and_set_mask (binsn_mask, 0x1f << (32 - 16));
    }
    if (rb >= 0) {
      gen_assert (rb < 32);
      binsn |= rb << (32 - 21);
      binsn_mask = check_and_set_mask (binsn_mask, 0x1f << (32 - 21));
    }
    if (rc >= 0) {
      gen_assert (rc < 32);
      binsn |= rc << (32 - 26);
      binsn_mask = check_and_set_mask (binsn_mask, 0x1f << (32 - 26));
    }
    if (spreg >= 0) {
      gen_assert (spreg < (1 << 5));
      binsn |= spreg << 16;
      binsn_mask = check_and_set_mask (binsn_mask, 0x3ff << (32 - 21));
    }
    if (disp >= 0) {
      gen_assert (disp < (1 << 16));
      binsn |= disp;
      binsn_mask = check_and_set_mask (binsn_mask, 0xffff);
    }
    if (disp4 >= 0) {
      gen_assert (disp4 < (1 << 16) && (disp4 & 0x3) == 0);
      binsn |= disp4;
      binsn_mask = check_and_set_mask (binsn_mask, 0xfffc);
    }
    if (d >= 0) {
      gen_assert (d < (1 << 2));
      binsn |= d;
      binsn_mask = check_and_set_mask (binsn_mask, 0x3);
    }
    if (Sh >= 0) {
      gen_assert (Sh < (1 << 6));
      binsn |= ((Sh & 0x1f) << (32 - 21));
      binsn |= (Sh >> 4) & 0x2;
      binsn_mask = check_and_set_mask (binsn_mask, (0x1f << (32 - 21)) | 0x2);
    }
    if (sh >= 0) {
      gen_assert (sh < (1 << 5));
      binsn |= sh << (32 - 21);
      binsn_mask = check_and_set_mask (binsn_mask, 0x1f << (32 - 21));
    }
    if (mb >= 0) {
      gen_assert (mb < (1 << 5));
      binsn |= (mb & 0x1f) << 6;
      binsn_mask = check_and_set_mask (binsn_mask, (0x1f << 6));
    }
    if (me >= 0) {
      gen_assert (me < (1 << 5));
      binsn |= (me & 0x1f) << 1;
      binsn_mask = check_and_set_mask (binsn_mask, (0x1f << 1));
    }
    if (Mb >= 0) {
      gen_assert (Mb < (1 << 6));
      binsn |= (Mb & 0x3f) << (32 - 27);
      binsn_mask = check_and_set_mask (binsn_mask, (0x3f << (32 - 27)));
    }
    if (Me >= 0) {
      gen_assert (Me < (1 << 6));
      binsn |= (Me & 0x3f) << (32 - 27);
      binsn |= 1 << 2;
      binsn_mask = check_and_set_mask (binsn_mask, (0x3f << (32 - 27)) | (1 << 2));
    }
    if (imm >= 0) {
      gen_assert (imm < (1 << 16));
      binsn |= imm;
      binsn_mask = check_and_set_mask (binsn_mask, 0xffff);
    }
    if (lab_off >= 0) {
      gen_assert (lab_off < (1 << 16) && (lab_off & 0x3) == 0);
      binsn |= lab_off;
      binsn_mask = check_and_set_mask (binsn_mask, 0xfffc);
    }
    if (bf >= 0) {
      gen_assert (bf < 8);
      binsn |= bf << (32 - 9);
      binsn_mask = check_and_set_mask (binsn_mask, 0x7 << (32 - 9));
    }
    if (BO >= 0) {
      gen_assert (BO < 32);
      binsn |= BO << 21;
      binsn_mask = check_and_set_mask (binsn_mask, 0x1f << 21);
    }
    if (BI >= 0) {
      gen_assert (BI < 32);
      binsn |= BI << 16;
      binsn_mask = check_and_set_mask (binsn_mask, 0x1f << 16);
    }
    if (LK >= 0) {
      gen_assert (LK < 2);
      binsn |= LK;
      binsn_mask = check_and_set_mask (binsn_mask, 0x1);
    }
    if (lb >= 0) {
      gen_assert (lb < 2);
      binsn |= lb << (32 - 11);
      binsn_mask = check_and_set_mask (binsn_mask, 1 << (32 - 11));
    }
    if (label_ref_num >= 0)
      VARR_ADDR (label_ref_t, label_refs)
      [label_ref_num].label_val_disp
        = VARR_LENGTH (uint8_t, result_code);

    if (switch_table_addr_p) switch_table_addr_insn_start = VARR_LENGTH (uint8_t, result_code);
    put_uint32 (gen_ctx, binsn); /* output the machine insn */

    if (*p == 0) break;
  }

  if (switch_table_addr_insn_start < 0) return;
  if (VARR_LENGTH (uint8_t, result_code) % 8 == 4) put_uint32 (gen_ctx, nop_binsn);
  /* pc offset of T plus 3 insns after T: see switch */
  offset = (VARR_LENGTH (uint8_t, result_code) - switch_table_addr_insn_start) + 12;
  gen_assert ((offset & 0x3) == 0);
  *(uint32_t *) (VARR_ADDR (uint8_t, result_code) + switch_table_addr_insn_start) |= offset;
  gen_assert (insn->code == MIR_SWITCH);
  for (size_t i = 1; i < insn->nops; i++) {
    gen_assert (insn->ops[i].mode == MIR_OP_LABEL);
    lr.abs_addr_p = TRUE;
    lr.short_addr_p = FALSE;
    lr.label_val_disp = VARR_LENGTH (uint8_t, result_code);
    lr.label = insn->ops[i].u.label;
    VARR_PUSH (label_ref_t, label_refs, lr);
    put_uint64 (gen_ctx, 0); /* reserve mem for label address */
  }
}

static int target_insn_ok_p (gen_ctx_t gen_ctx, MIR_insn_t insn) {
  return find_insn_pattern_replacement (gen_ctx, insn, TRUE) != NULL;
}

static uint8_t *target_translate (gen_ctx_t gen_ctx, size_t *len) {
  MIR_context_t ctx = gen_ctx->ctx;
  size_t i;
  int short_label_disp_fail_p, n_iter = 0;
  MIR_insn_t insn, old_insn;
  const char *replacement;

  gen_assert (curr_func_item->item_type == MIR_func_item);
  do {
    VARR_TRUNC (uint8_t, result_code, 0);
    VARR_TRUNC (label_ref_t, label_refs, 0);
    VARR_TRUNC (uint64_t, abs_address_locs, 0);
    short_label_disp_fail_p = FALSE;
    for (insn = DLIST_HEAD (MIR_insn_t, curr_func_item->u.func->insns); insn != NULL;
         insn = DLIST_NEXT (MIR_insn_t, insn)) {
      MIR_insn_code_t code = insn->code;

      if ((code == MIR_RSH || code == MIR_LSH || code == MIR_URSH || code == MIR_RSHS
           || code == MIR_LSHS || code == MIR_URSHS)
          && (insn->ops[2].mode == MIR_OP_INT || insn->ops[2].mode == MIR_OP_UINT)) {
        if (insn->ops[2].u.i == 0) {
          gen_mov (gen_ctx, insn, MIR_MOV, insn->ops[0], insn->ops[1]);
          old_insn = insn;
          insn = DLIST_PREV (MIR_insn_t, insn);
          gen_delete_insn (gen_ctx, old_insn);
        } else {
          if (insn->ops[2].mode == MIR_OP_INT && insn->ops[2].u.i < 0) {
            switch (code) {
            case MIR_RSH: insn->code = MIR_LSH; break;
            case MIR_URSH: insn->code = MIR_LSH; break;
            case MIR_LSH: insn->code = MIR_RSH; break;
            case MIR_RSHS: insn->code = MIR_LSHS; break;
            case MIR_URSHS: insn->code = MIR_LSHS; break;
            case MIR_LSHS: insn->code = MIR_RSHS; break;
            default: gen_assert (FALSE); break;
            }
            insn->ops[2].u.i = -insn->ops[2].u.i;
          }
          if (code == MIR_RSH || code == MIR_LSH || code == MIR_URSH) {
            if (insn->ops[2].u.i > 64) insn->ops[2].u.i = 64;
          } else if (insn->ops[2].u.i > 32) {
            insn->ops[2].u.i = 32;
          }
        }
      }
      if (insn->code == MIR_LABEL) {
        set_label_disp (gen_ctx, insn, VARR_LENGTH (uint8_t, result_code));
      } else {
        int use_short_label_p = TRUE;

        if (n_iter > 0 && MIR_branch_code_p (code)) {
          MIR_label_t label = insn->ops[0].u.label;
          int64_t offset = (int64_t) get_label_disp (gen_ctx, label)
                           - (int64_t) VARR_LENGTH (uint8_t, result_code);

          use_short_label_p = ((offset < 0 ? -offset : offset) & ~(int64_t) 0x7fff) == 0;
        }
        replacement = find_insn_pattern_replacement (gen_ctx, insn, use_short_label_p);
        if (replacement == NULL) {
          fprintf (stderr, "fatal failure in matching insn:");
          MIR_output_insn (ctx, stderr, insn, curr_func_item->u.func, TRUE);
          exit (1);
        } else {
          gen_assert (replacement != NULL);
          out_insn (gen_ctx, insn, replacement);
        }
      }
    }
    /* Setting up labels */
    for (i = 0; i < VARR_LENGTH (label_ref_t, label_refs); i++) {
      label_ref_t lr = VARR_GET (label_ref_t, label_refs, i);

      if (lr.abs_addr_p) {
        set_int64 (&VARR_ADDR (uint8_t, result_code)[lr.label_val_disp],
                   (int64_t) get_label_disp (gen_ctx, lr.label));
        VARR_PUSH (uint64_t, abs_address_locs, lr.label_val_disp);
      } else if (lr.short_addr_p) { /* 14-bit relative addressing */
        int64_t offset = (int64_t) get_label_disp (gen_ctx, lr.label) - (int64_t) lr.label_val_disp;

        gen_assert ((offset & 0x3) == 0);
        if (((offset < 0 ? -offset : offset) & ~(int64_t) 0x7fff) != 0) {
          short_label_disp_fail_p = TRUE;
        } else {
          *(uint32_t *) (VARR_ADDR (uint8_t, result_code) + lr.label_val_disp)
            |= ((offset / 4) & 0x3fff) << 2;
        }
      } else { /* 24-bit relative address */
        int64_t offset = (int64_t) get_label_disp (gen_ctx, lr.label) - (int64_t) lr.label_val_disp;
        gen_assert ((offset & 0x3) == 0
                    && ((offset < 0 ? -offset : offset) & ~(int64_t) 0x1ffffff) == 0);
        *(uint32_t *) (VARR_ADDR (uint8_t, result_code) + lr.label_val_disp)
          |= ((offset / 4) & 0xffffff) << 2;
      }
    }
    n_iter++;
  } while (short_label_disp_fail_p);
  while (VARR_LENGTH (uint8_t, result_code) % 16 != 0) /* Align the pool */
    VARR_PUSH (uint8_t, result_code, 0);
  *len = VARR_LENGTH (uint8_t, result_code);
  return VARR_ADDR (uint8_t, result_code);
}

static void target_rebase (gen_ctx_t gen_ctx, uint8_t *base) {
  MIR_code_reloc_t reloc;

  VARR_TRUNC (MIR_code_reloc_t, relocs, 0);
  for (size_t i = 0; i < VARR_LENGTH (uint64_t, abs_address_locs); i++) {
    reloc.offset = VARR_GET (uint64_t, abs_address_locs, i);
    reloc.value = base + get_int64 (base + reloc.offset);
    VARR_PUSH (MIR_code_reloc_t, relocs, reloc);
  }
  _MIR_update_code_arr (gen_ctx->ctx, base, VARR_LENGTH (MIR_code_reloc_t, relocs),
                        VARR_ADDR (MIR_code_reloc_t, relocs));
}

static void target_init (gen_ctx_t gen_ctx) {
  gen_ctx->target_ctx = gen_malloc (gen_ctx, sizeof (struct target_ctx));
  VARR_CREATE (uint8_t, result_code, 0);
  VARR_CREATE (label_ref_t, label_refs, 0);
  VARR_CREATE (uint64_t, abs_address_locs, 0);
  VARR_CREATE (MIR_code_reloc_t, relocs, 0);
  patterns_init (gen_ctx);
}

static void target_finish (gen_ctx_t gen_ctx) {
  patterns_finish (gen_ctx);
  VARR_DESTROY (uint8_t, result_code);
  VARR_DESTROY (label_ref_t, label_refs);
  VARR_DESTROY (uint64_t, abs_address_locs);
  VARR_DESTROY (MIR_code_reloc_t, relocs);
  free (gen_ctx->target_ctx);
  gen_ctx->target_ctx = NULL;
}
