/*
 *  Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "idef-parser.h"
#include "parser-helpers.h"
#include "idef-parser.tab.h"
#include "idef-parser.yy.h"

void yyerror(YYLTYPE *locp,
             yyscan_t scanner __attribute__((unused)),
             Context *c,
             const char *s)
{
    const char *code_ptr = c->input_buffer;

    fprintf(stderr, "WARNING (%s): '%s'\n", c->inst.name->str, s);

    fprintf(stderr, "Problematic range: ");
    for (int i = locp->first_column; i < locp->last_column; i++) {
        if (code_ptr[i] != '\n') {
            fprintf(stderr, "%c", code_ptr[i]);
        }
    }
    fprintf(stderr, "\n");

    for (unsigned i = 0;
         i < 80 &&
         code_ptr[locp->first_column - 10 + i] != '\0' &&
         code_ptr[locp->first_column - 10 + i] != '\n';
         i++) {
        fprintf(stderr, "%c", code_ptr[locp->first_column - 10 + i]);
    }
    fprintf(stderr, "\n");
    for (unsigned i = 0; i < 9; i++) {
        fprintf(stderr, " ");
    }
    fprintf(stderr, "^");
    for (int i = 0; i < (locp->last_column - locp->first_column) - 1; i++) {
        fprintf(stderr, "~");
    }
    fprintf(stderr, "\n");
    c->inst.error_count++;
}

bool is_direct_predicate(HexValue *value)
{
    return value->pred.id >= '0' && value->pred.id <= '3';
}

bool is_inside_ternary(Context *c)
{
    return c->ternary->len > 0;
}

/* Print functions */
void str_print(Context *c, YYLTYPE *locp, const char *string)
{
    (void) locp;
    EMIT(c, "%s", string);
}

void uint8_print(Context *c, YYLTYPE *locp, uint8_t *num)
{
    (void) locp;
    EMIT(c, "%u", *num);
}

void uint64_print(Context *c, YYLTYPE *locp, uint64_t *num)
{
    (void) locp;
    EMIT(c, "%" PRIu64, *num);
}

void int_print(Context *c, YYLTYPE *locp, int *num)
{
    (void) locp;
    EMIT(c, "%d", *num);
}

void uint_print(Context *c, YYLTYPE *locp, unsigned *num)
{
    (void) locp;
    EMIT(c, "%u", *num);
}

void tmp_print(Context *c, YYLTYPE *locp, HexTmp *tmp)
{
    (void) locp;
    EMIT(c, "tmp_%d", tmp->index);
}

void pred_print(Context *c, YYLTYPE *locp, HexPred *pred, bool is_dotnew)
{
    (void) locp;
    char suffix = is_dotnew ? 'N' : 'V';
    EMIT(c, "P%c%c", pred->id, suffix);
}

void reg_compose(Context *c, YYLTYPE *locp, HexReg *reg, char reg_id[5])
{
    memset(reg_id, 0, 5 * sizeof(char));
    switch (reg->type) {
    case GENERAL_PURPOSE:
        reg_id[0] = 'R';
        break;
    case CONTROL:
        reg_id[0] = 'C';
        break;
    case MODIFIER:
        reg_id[0] = 'M';
        break;
    case DOTNEW:
        reg_id[0] = 'N';
        reg_id[1] = reg->id;
        reg_id[2] = 'N';
        return;
    }
    switch (reg->bit_width) {
    case 32:
        reg_id[1] = reg->id;
        reg_id[2] = 'V';
        break;
    case 64:
        reg_id[1] = reg->id;
        reg_id[2] = reg->id;
        reg_id[3] = 'V';
        break;
    default:
        yyassert(c, locp, false, "Unhandled register bit width!\n");
    }
}

static void reg_arg_print(Context *c, YYLTYPE *locp, HexReg *reg)
{
    char reg_id[5];
    reg_compose(c, locp, reg, reg_id);
    EMIT(c, "%s", reg_id);
}

void reg_print(Context *c, YYLTYPE *locp, HexReg *reg)
{
    (void) locp;
    EMIT(c, "hex_gpr[%u]", reg->id);
}

void imm_print(Context *c, YYLTYPE *locp, HexValue *rvalue)
{
    HexImm *imm = &rvalue->imm;
    switch (imm->type) {
    case I:
        EMIT(c, "i");
        break;
    case VARIABLE:
        EMIT(c, "%ciV", imm->id);
        break;
    case VALUE:
        if (rvalue->bit_width == 32) {
            if (rvalue->signedness == UNSIGNED) {
                EMIT(c, "((uint32_t) 0x%" PRIx32 ")", (uint32_t) imm->value);
            }  else {
                EMIT(c, "((int32_t) 0x%" PRIx32 ")", (int32_t) imm->value);
            }
        } else if (rvalue->bit_width == 64) {
            if (rvalue->signedness == UNSIGNED) {
                EMIT(c, "((uint64_t) 0x%" PRIx64 "ULL)", (uint64_t) imm->value);
            } else {
                EMIT(c, "((int64_t) 0x%" PRIx64 "LL)", (int64_t) imm->value);
            }
        } else {
            g_assert_not_reached();
        }
        break;
    case QEMU_TMP:
        EMIT(c, "qemu_tmp_%" PRIu64, imm->index);
        break;
    case IMM_PC:
        EMIT(c, "ctx->base.pc_next");
        break;
    case IMM_CONSTEXT:
        EMIT(c, "insn->extension_valid");
        break;
    default:
        yyassert(c, locp, false, "Cannot print this expression!");
    }
}

void var_print(Context *c, YYLTYPE *locp, HexVar *var)
{
    (void) locp;
    EMIT(c, "%s", var->name->str);
}

void rvalue_print(Context *c, YYLTYPE *locp, void *pointer)
{
  HexValue *rvalue = (HexValue *) pointer;
  switch (rvalue->type) {
  case REGISTER:
      reg_print(c, locp, &rvalue->reg);
      break;
  case REGISTER_ARG:
      reg_arg_print(c, locp, &rvalue->reg);
      break;
  case TEMP:
      tmp_print(c, locp, &rvalue->tmp);
      break;
  case IMMEDIATE:
      imm_print(c, locp, rvalue);
      break;
  case VARID:
      var_print(c, locp, &rvalue->var);
      break;
  case PREDICATE:
      pred_print(c, locp, &rvalue->pred, rvalue->is_dotnew);
      break;
  default:
      yyassert(c, locp, false, "Cannot print this expression!");
  }
}

void out_assert(Context *c, YYLTYPE *locp,
                void *dummy __attribute__((unused)))
{
    yyassert(c, locp, false, "Unhandled print type!");
}

/* Copy output code buffer */
void commit(Context *c)
{
    /* Emit instruction pseudocode */
    EMIT_SIG(c, "\n" START_COMMENT " ");
    for (char *x = c->inst.code_begin; x < c->inst.code_end; x++) {
        EMIT_SIG(c, "%c", *x);
    }
    EMIT_SIG(c, " " END_COMMENT "\n");

    /* Commit instruction code to output file */
    fwrite(c->signature_str->str, sizeof(char), c->signature_str->len,
           c->output_file);
    fwrite(c->header_str->str, sizeof(char), c->header_str->len,
           c->output_file);
    fwrite(c->out_str->str, sizeof(char), c->out_str->len,
           c->output_file);

    fwrite(c->signature_str->str, sizeof(char), c->signature_str->len,
           c->defines_file);
    fprintf(c->defines_file, ";\n");
}

static void gen_c_int_type(Context *c, YYLTYPE *locp, unsigned bit_width,
                           HexSignedness signedness)
{
    const char *signstr = (signedness == UNSIGNED) ? "u" : "";
    OUT(c, locp, signstr, "int", &bit_width, "_t");
}

static HexValue gen_constant(Context *c,
                             YYLTYPE *locp,
                             const char *value,
                             unsigned bit_width,
                             HexSignedness signedness)
{
    HexValue rvalue;
    assert(bit_width == 32 || bit_width == 64);
    memset(&rvalue, 0, sizeof(HexValue));
    rvalue.type = TEMP;
    rvalue.bit_width = bit_width;
    rvalue.signedness = signedness;
    rvalue.is_dotnew = false;
    rvalue.tmp.index = c->inst.tmp_count;
    OUT(c, locp, "TCGv_i", &bit_width, " tmp_", &c->inst.tmp_count,
        " = tcg_constant_i", &bit_width, "(", value, ");\n");
    c->inst.tmp_count++;
    return rvalue;
}

/* Temporary values creation */
HexValue gen_tmp(Context *c,
                 YYLTYPE *locp,
                 unsigned bit_width,
                 HexSignedness signedness)
{
    HexValue rvalue;
    assert(bit_width == 32 || bit_width == 64);
    memset(&rvalue, 0, sizeof(HexValue));
    rvalue.type = TEMP;
    rvalue.bit_width = bit_width;
    rvalue.signedness = signedness;
    rvalue.is_dotnew = false;
    rvalue.tmp.index = c->inst.tmp_count;
    OUT(c, locp, "TCGv_i", &bit_width, " tmp_", &c->inst.tmp_count,
        " = tcg_temp_new_i", &bit_width, "();\n");
    c->inst.tmp_count++;
    return rvalue;
}

static HexValue gen_constant_from_imm(Context *c,
                                      YYLTYPE *locp,
                                      HexValue *value)
{
    HexValue rvalue;
    assert(value->type == IMMEDIATE);
    memset(&rvalue, 0, sizeof(HexValue));
    rvalue.type = TEMP;
    rvalue.bit_width = value->bit_width;
    rvalue.signedness = value->signedness;
    rvalue.is_dotnew = false;
    rvalue.tmp.index = c->inst.tmp_count;
    /*
     * Here we output the call to `tcg_constant_i<width>` in
     * order to create the temporary value. Note, that we
     * add a cast
     *
     *   `tcg_constant_i<width>`((int<width>_t) ...)`
     *
     * This cast is required to avoid implicit integer
     * conversion warnings since all immediates are
     * output as `((int64_t) 123ULL)`, even if the
     * integer is 32-bit.
     */
    OUT(c, locp, "TCGv_i", &rvalue.bit_width, " tmp_", &c->inst.tmp_count);
    OUT(c, locp, " = tcg_constant_i", &rvalue.bit_width,
        "((int", &rvalue.bit_width, "_t) (", value, "));\n");

    c->inst.tmp_count++;
    return rvalue;
}

HexValue gen_imm_value(Context *c __attribute__((unused)),
                       YYLTYPE *locp,
                       int value,
                       unsigned bit_width,
                       HexSignedness signedness)
{
    (void) locp;
    HexValue rvalue;
    assert(bit_width == 32 || bit_width == 64);
    memset(&rvalue, 0, sizeof(HexValue));
    rvalue.type = IMMEDIATE;
    rvalue.bit_width = bit_width;
    rvalue.signedness = signedness;
    rvalue.is_dotnew = false;
    rvalue.imm.type = VALUE;
    rvalue.imm.value = value;
    return rvalue;
}

HexValue gen_imm_qemu_tmp(Context *c, YYLTYPE *locp, unsigned bit_width,
                          HexSignedness signedness)
{
    (void) locp;
    HexValue rvalue;
    assert(bit_width == 32 || bit_width == 64);
    memset(&rvalue, 0, sizeof(HexValue));
    rvalue.type = IMMEDIATE;
    rvalue.is_dotnew = false;
    rvalue.bit_width = bit_width;
    rvalue.signedness = signedness;
    rvalue.imm.type = QEMU_TMP;
    rvalue.imm.index = c->inst.qemu_tmp_count++;
    return rvalue;
}

HexValue rvalue_materialize(Context *c, YYLTYPE *locp, HexValue *rvalue)
{
    if (rvalue->type == IMMEDIATE) {
        return gen_constant_from_imm(c, locp, rvalue);
    }
    return *rvalue;
}

HexValue gen_rvalue_extend(Context *c, YYLTYPE *locp, HexValue *rvalue)
{
    assert_signedness(c, locp, rvalue->signedness);
    if (rvalue->bit_width > 32) {
        return *rvalue;
    }

    if (rvalue->type == IMMEDIATE) {
        HexValue res = gen_imm_qemu_tmp(c, locp, 64, rvalue->signedness);
        gen_c_int_type(c, locp, 64, rvalue->signedness);
        OUT(c, locp, " ", &res, " = (");
        gen_c_int_type(c, locp, 64, rvalue->signedness);
        OUT(c, locp, ")", rvalue, ";\n");
        return res;
    } else {
        HexValue res = gen_tmp(c, locp, 64, rvalue->signedness);
        bool is_unsigned = (rvalue->signedness == UNSIGNED);
        const char *sign_suffix = is_unsigned ? "u" : "";
        OUT(c, locp, "tcg_gen_ext", sign_suffix,
            "_i32_i64(", &res, ", ", rvalue, ");\n");
        return res;
    }
}

HexValue gen_rvalue_truncate(Context *c, YYLTYPE *locp, HexValue *rvalue)
{
    if (rvalue->type == IMMEDIATE) {
        HexValue res = *rvalue;
        res.bit_width = 32;
        return res;
    } else {
        if (rvalue->bit_width == 64) {
            HexValue res = gen_tmp(c, locp, 32, rvalue->signedness);
            OUT(c, locp, "tcg_gen_trunc_i64_tl(", &res, ", ", rvalue, ");\n");
            return res;
        }
    }
    return *rvalue;
}

/*
 * Attempts to lookup the `Var` struct associated with the given `varid`.
 * The `dst` argument is populated with the found name, bit_width, and
 * signedness, given that `dst` is non-NULL. Returns true if the lookup
 * succeeded and false otherwise.
 */
static bool try_find_variable(Context *c, YYLTYPE *locp,
                              HexValue *dst,
                              HexValue *varid)
{
    yyassert(c, locp, varid, "varid to lookup is NULL");
    yyassert(c, locp, varid->type == VARID,
             "Can only lookup variables by varid");
    for (unsigned i = 0; i < c->inst.allocated->len; i++) {
        Var *curr = &g_array_index(c->inst.allocated, Var, i);
        if (g_string_equal(varid->var.name, curr->name)) {
            if (dst) {
                dst->var.name = curr->name;
                dst->bit_width = curr->bit_width;
                dst->signedness = curr->signedness;
            }
            return true;
        }
    }
    return false;
}

/* Calls `try_find_variable` and asserts succcess. */
static void find_variable(Context *c, YYLTYPE *locp,
                          HexValue *dst,
                          HexValue *varid)
{
    bool found = try_find_variable(c, locp, dst, varid);
    yyassert(c, locp, found, "Use of undeclared variable!\n");
}

/* Handle signedness, if both unsigned -> result is unsigned, else signed */
static inline HexSignedness bin_op_signedness(Context *c, YYLTYPE *locp,
                                              HexSignedness sign1,
                                              HexSignedness sign2)
{
    assert_signedness(c, locp, sign1);
    assert_signedness(c, locp, sign2);
    return (sign1 == UNSIGNED && sign2 == UNSIGNED) ? UNSIGNED : SIGNED;
}

void gen_varid_allocate(Context *c,
                        YYLTYPE *locp,
                        HexValue *varid,
                        unsigned bit_width,
                        HexSignedness signedness)
{
    const char *bit_suffix = (bit_width == 64) ? "i64" : "i32";
    bool found = try_find_variable(c, locp, NULL, varid);
    Var new_var;

    memset(&new_var, 0, sizeof(Var));

    yyassert(c, locp, !found, "Redeclaration of variables not allowed!");
    assert_signedness(c, locp, signedness);

    /* `varid` only carries name information */
    new_var.name = varid->var.name;
    new_var.bit_width = bit_width;
    new_var.signedness = signedness;

    EMIT_HEAD(c, "TCGv_%s %s", bit_suffix, varid->var.name->str);
    EMIT_HEAD(c, " = tcg_temp_new_%s();\n", bit_suffix);
    g_array_append_val(c->inst.allocated, new_var);
}

enum OpTypes {
    IMM_IMM = 0,
    IMM_REG = 1,
    REG_IMM = 2,
    REG_REG = 3,
};

HexValue gen_bin_cmp(Context *c,
                     YYLTYPE *locp,
                     TCGCond type,
                     HexValue *op1,
                     HexValue *op2)
{
    HexValue op1_m = *op1;
    HexValue op2_m = *op2;
    enum OpTypes op_types = (op1_m.type != IMMEDIATE) << 1
                            | (op2_m.type != IMMEDIATE);

    bool op_is64bit = op1_m.bit_width == 64 || op2_m.bit_width == 64;
    const char *bit_suffix = op_is64bit ? "i64" : "i32";
    unsigned bit_width = (op_is64bit) ? 64 : 32;
    HexValue res = gen_tmp(c, locp, bit_width, UNSIGNED);

    /* Extend to 64-bits, if required */
    if (op_is64bit) {
        op1_m = gen_rvalue_extend(c, locp, &op1_m);
        op2_m = gen_rvalue_extend(c, locp, &op2_m);
    }

    switch (op_types) {
    case IMM_IMM:
    case IMM_REG:
        yyassert(c, locp, false, "Binary comparisons between IMM op IMM and"
                                 "IMM op REG not handled!");
        break;
    case REG_IMM:
        OUT(c, locp, "tcg_gen_setcondi_", bit_suffix, "(");
        OUT(c, locp, cond_to_str(type), ", ", &res, ", ", &op1_m, ", ", &op2_m,
            ");\n");
        break;
    case REG_REG:
        OUT(c, locp, "tcg_gen_setcond_", bit_suffix, "(");
        OUT(c, locp, cond_to_str(type), ", ", &res, ", ", &op1_m, ", ", &op2_m,
            ");\n");
        break;
    default:
        fprintf(stderr, "Error in evalutating immediateness!");
        abort();
    }
    return res;
}

static void gen_simple_op(Context *c, YYLTYPE *locp, unsigned bit_width,
                          const char *bit_suffix, HexValue *res,
                          enum OpTypes op_types,
                          HexValue *op1,
                          HexValue *op2,
                          const char *imm_imm,
                          const char *imm_reg,
                          const char *reg_imm,
                          const char *reg_reg)
{
    switch (op_types) {
    case IMM_IMM: {
        HexSignedness signedness = bin_op_signedness(c, locp,
                                                     op1->signedness,
                                                     op2->signedness);
        gen_c_int_type(c, locp, bit_width, signedness);
        OUT(c, locp, " ", res,
            " = ", op1, imm_imm, op2, ";\n");
    } break;
    case IMM_REG:
        OUT(c, locp, imm_reg, bit_suffix,
            "(", res, ", ", op2, ", ", op1, ");\n");
        break;
    case REG_IMM:
        OUT(c, locp, reg_imm, bit_suffix,
            "(", res, ", ", op1, ", ", op2, ");\n");
        break;
    case REG_REG:
        OUT(c, locp, reg_reg, bit_suffix,
            "(", res, ", ", op1, ", ", op2, ");\n");
        break;
    }
}

static void gen_sub_op(Context *c, YYLTYPE *locp, unsigned bit_width,
                       const char *bit_suffix, HexValue *res,
                       enum OpTypes op_types, HexValue *op1,
                       HexValue *op2)
{
    switch (op_types) {
    case IMM_IMM: {
        HexSignedness signedness = bin_op_signedness(c, locp,
                                                     op1->signedness,
                                                     op2->signedness);
        gen_c_int_type(c, locp, bit_width, signedness);
        OUT(c, locp, " ", res,
            " = ", op1, " - ", op2, ";\n");
    } break;
    case IMM_REG: {
        OUT(c, locp, "tcg_gen_subfi_", bit_suffix,
            "(", res, ", ", op1, ", ", op2, ");\n");
    } break;
    case REG_IMM: {
        OUT(c, locp, "tcg_gen_subi_", bit_suffix,
            "(", res, ", ", op1, ", ", op2, ");\n");
    } break;
    case REG_REG: {
        OUT(c, locp, "tcg_gen_sub_", bit_suffix,
            "(", res, ", ", op1, ", ", op2, ");\n");
    } break;
    }
}

static void gen_asl_op(Context *c, YYLTYPE *locp, unsigned bit_width,
                       bool op_is64bit, const char *bit_suffix,
                       HexValue *res, enum OpTypes op_types,
                       HexValue *op1, HexValue *op2)
{
    HexValue op1_m = *op1;
    HexValue op2_m = *op2;
    switch (op_types) {
    case IMM_IMM: {
        HexSignedness signedness = bin_op_signedness(c, locp,
                                                     op1->signedness,
                                                     op2->signedness);
        gen_c_int_type(c, locp, bit_width, signedness);
        OUT(c, locp, " ", res,
            " = ", op1, " << ", op2, ";\n");
    } break;
    case REG_IMM: {
        OUT(c, locp, "if (", op2, " >= ", &bit_width, ") {\n");
        OUT(c, locp, "tcg_gen_movi_", bit_suffix, "(", res, ", 0);\n");
        OUT(c, locp, "} else {\n");
        OUT(c, locp, "tcg_gen_shli_", bit_suffix,
                "(", res, ", ", op1, ", ", op2, ");\n");
        OUT(c, locp, "}\n");
    } break;
    case IMM_REG:
        op1_m.bit_width = bit_width;
        op1_m = rvalue_materialize(c, locp, &op1_m);
        /* fallthrough */
    case REG_REG: {
        OUT(c, locp, "tcg_gen_shl_", bit_suffix,
            "(", res, ", ", &op1_m, ", ", op2, ");\n");
    } break;
    }
    if (op_types == IMM_REG || op_types == REG_REG) {
        /*
         * Handle left shift by 64/32 which hexagon-sim expects to clear out
         * register
         */
        HexValue zero = gen_constant(c, locp, "0", bit_width, UNSIGNED);
        HexValue edge = gen_imm_value(c, locp, bit_width, bit_width, UNSIGNED);
        edge = rvalue_materialize(c, locp, &edge);
        if (op_is64bit) {
            op2_m = gen_rvalue_extend(c, locp, &op2_m);
        }
        op1_m = rvalue_materialize(c, locp, &op1_m);
        op2_m = rvalue_materialize(c, locp, &op2_m);
        OUT(c, locp, "tcg_gen_movcond_i", &bit_width);
        OUT(c, locp, "(TCG_COND_GEU, ", res, ", ", &op2_m, ", ", &edge);
        OUT(c, locp, ", ", &zero, ", ", res, ");\n");
    }
}

static void gen_asr_op(Context *c, YYLTYPE *locp, unsigned bit_width,
                       bool op_is64bit, const char *bit_suffix,
                       HexValue *res, enum OpTypes op_types,
                       HexValue *op1, HexValue *op2)
{
    HexValue op1_m = *op1;
    HexValue op2_m = *op2;
    switch (op_types) {
    case IMM_IMM:
    case IMM_REG:
        yyassert(c, locp, false, "ASR between IMM op IMM, and IMM op REG"
                                 " not handled!");
        break;
    case REG_IMM: {
        HexSignedness signedness = bin_op_signedness(c, locp,
                                                     op1->signedness,
                                                     op2->signedness);
        OUT(c, locp, "{\n");
        gen_c_int_type(c, locp, bit_width, signedness);
        OUT(c, locp, " shift = ", op2, ";\n");
        OUT(c, locp, "if (", op2, " >= ", &bit_width, ") {\n");
        OUT(c, locp, "    shift = ", &bit_width, " - 1;\n");
        OUT(c, locp, "}\n");
        OUT(c, locp, "tcg_gen_sari_", bit_suffix,
            "(", res, ", ", op1, ", shift);\n}\n");
    } break;
    case REG_REG:
        OUT(c, locp, "tcg_gen_sar_", bit_suffix,
            "(", res, ", ", &op1_m, ", ", op2, ");\n");
        break;
    }
    if (op_types == REG_REG) {
        /* Handle right shift by values >= bit_width */
        const char *offset = op_is64bit ? "63" : "31";
        HexValue tmp = gen_tmp(c, locp, bit_width, SIGNED);
        HexValue zero = gen_constant(c, locp, "0", bit_width, SIGNED);
        HexValue edge = gen_imm_value(c, locp, bit_width, bit_width, UNSIGNED);

        edge = rvalue_materialize(c, locp, &edge);
        if (op_is64bit) {
            op2_m = gen_rvalue_extend(c, locp, &op2_m);
        }
        op1_m = rvalue_materialize(c, locp, &op1_m);
        op2_m = rvalue_materialize(c, locp, &op2_m);

        OUT(c, locp, "tcg_gen_extract_", bit_suffix, "(",
            &tmp, ", ", &op1_m, ", ", offset, ", 1);\n");
        OUT(c, locp, "tcg_gen_sub_", bit_suffix, "(",
            &tmp, ", ", &zero, ", ", &tmp, ");\n");
        OUT(c, locp, "tcg_gen_movcond_i", &bit_width);
        OUT(c, locp, "(TCG_COND_GEU, ", res, ", ", &op2_m, ", ", &edge);
        OUT(c, locp, ", ", &tmp, ", ", res, ");\n");
    }
}

static void gen_lsr_op(Context *c, YYLTYPE *locp, unsigned bit_width,
                       bool op_is64bit, const char *bit_suffix,
                       HexValue *res, enum OpTypes op_types,
                       HexValue *op1, HexValue *op2)
{
    HexValue op1_m = *op1;
    HexValue op2_m = *op2;
    switch (op_types) {
    case IMM_IMM:
    case IMM_REG:
        yyassert(c, locp, false, "LSR between IMM op IMM, and IMM op REG"
                                 " not handled!");
        break;
    case REG_IMM:
        OUT(c, locp, "if (", op2, " >= ", &bit_width, ") {\n");
        OUT(c, locp, "tcg_gen_movi_", bit_suffix, "(", res, ", 0);\n");
        OUT(c, locp, "} else {\n");
        OUT(c, locp, "tcg_gen_shri_", bit_suffix,
            "(", res, ", ", op1, ", ", op2, ");\n");
        OUT(c, locp, "}\n");
        break;
    case REG_REG:
        OUT(c, locp, "tcg_gen_shr_", bit_suffix,
            "(", res, ", ", &op1_m, ", ", op2, ");\n");
        break;
    }
    if (op_types == REG_REG) {
        /* Handle right shift by values >= bit_width */
        HexValue zero = gen_constant(c, locp, "0", bit_width, UNSIGNED);
        HexValue edge = gen_imm_value(c, locp, bit_width, bit_width, UNSIGNED);
        edge = rvalue_materialize(c, locp, &edge);
        if (op_is64bit) {
            op2_m = gen_rvalue_extend(c, locp, &op2_m);
        }
        op1_m = rvalue_materialize(c, locp, &op1_m);
        op2_m = rvalue_materialize(c, locp, &op2_m);
        OUT(c, locp, "tcg_gen_movcond_i", &bit_width);
        OUT(c, locp, "(TCG_COND_GEU, ", res, ", ", &op2_m, ", ", &edge);
        OUT(c, locp, ", ", &zero, ", ", res, ");\n");
    }
}

/*
 * Note: This implementation of logical `and` does not mirror that in C.
 * We do not short-circuit logical expressions!
 */
static void gen_andl_op(Context *c, YYLTYPE *locp, unsigned bit_width,
                        const char *bit_suffix, HexValue *res,
                        enum OpTypes op_types, HexValue *op1,
                        HexValue *op2)
{
    (void) bit_width;
    HexValue tmp1, tmp2;
    HexValue zero = gen_constant(c, locp, "0", 32, UNSIGNED);
    memset(&tmp1, 0, sizeof(HexValue));
    memset(&tmp2, 0, sizeof(HexValue));
    switch (op_types) {
    case IMM_IMM:
    case IMM_REG:
    case REG_IMM:
        yyassert(c, locp, false, "ANDL between IMM op IMM, IMM op REG, and"
                                 " REG op IMM, not handled!");
        break;
    case REG_REG:
        tmp1 = gen_bin_cmp(c, locp, TCG_COND_NE, op1, &zero);
        tmp2 = gen_bin_cmp(c, locp, TCG_COND_NE, op2, &zero);
        OUT(c, locp, "tcg_gen_and_", bit_suffix,
            "(", res, ", ", &tmp1, ", ", &tmp2, ");\n");
        break;
    }
}

static void gen_minmax_op(Context *c, YYLTYPE *locp, unsigned bit_width,
                          HexValue *res, enum OpTypes op_types,
                          HexValue *op1, HexValue *op2, bool minmax)
{
    const char *mm;
    HexValue op1_m = *op1;
    HexValue op2_m = *op2;
    bool is_unsigned;

    assert_signedness(c, locp, res->signedness);
    is_unsigned = res->signedness == UNSIGNED;

    if (minmax) {
        /* Max */
        mm = is_unsigned ? "tcg_gen_umax" : "tcg_gen_smax";
    } else {
        /* Min */
        mm = is_unsigned ? "tcg_gen_umin" : "tcg_gen_smin";
    }
    switch (op_types) {
    case IMM_IMM:
        yyassert(c, locp, false, "MINMAX between IMM op IMM, not handled!");
        break;
    case IMM_REG:
        op1_m.bit_width = bit_width;
        op1_m = rvalue_materialize(c, locp, &op1_m);
        OUT(c, locp, mm, "_i", &bit_width, "(");
        OUT(c, locp, res, ", ", &op1_m, ", ", op2, ");\n");
        break;
    case REG_IMM:
        op2_m.bit_width = bit_width;
        op2_m = rvalue_materialize(c, locp, &op2_m);
        /* Fallthrough */
    case REG_REG:
        OUT(c, locp, mm, "_i", &bit_width, "(");
        OUT(c, locp, res, ", ", op1, ", ", &op2_m, ");\n");
        break;
    }
}

/* Code generation functions */
HexValue gen_bin_op(Context *c,
                    YYLTYPE *locp,
                    OpType type,
                    HexValue *op1,
                    HexValue *op2)
{
    /* Replicate operands to avoid side effects */
    HexValue op1_m = *op1;
    HexValue op2_m = *op2;
    enum OpTypes op_types;
    bool op_is64bit;
    HexSignedness signedness;
    unsigned bit_width;
    const char *bit_suffix;
    HexValue res;

    memset(&res, 0, sizeof(HexValue));

    /*
     * If the operands are VARID's we need to look up the
     * type information.
     */
    if (op1_m.type == VARID) {
        find_variable(c, locp, &op1_m, &op1_m);
    }
    if (op2_m.type == VARID) {
        find_variable(c, locp, &op2_m, &op2_m);
    }

    op_types = (op1_m.type != IMMEDIATE) << 1
               | (op2_m.type != IMMEDIATE);
    op_is64bit = op1_m.bit_width == 64 || op2_m.bit_width == 64;
    /* Shift greater than 32 are 64 bits wide */

    if (type == ASL_OP && op2_m.type == IMMEDIATE &&
        op2_m.imm.type == VALUE && op2_m.imm.value >= 32) {
        op_is64bit = true;
    }

    bit_width = (op_is64bit) ? 64 : 32;
    bit_suffix = op_is64bit ? "i64" : "i32";

    /* Extend to 64-bits, if required */
    if (op_is64bit) {
        op1_m = gen_rvalue_extend(c, locp, &op1_m);
        op2_m = gen_rvalue_extend(c, locp, &op2_m);
    }

    signedness = bin_op_signedness(c, locp, op1_m.signedness, op2_m.signedness);
    if (op_types != IMM_IMM) {
        res = gen_tmp(c, locp, bit_width, signedness);
    } else {
        res = gen_imm_qemu_tmp(c, locp, bit_width, signedness);
    }

    switch (type) {
    case ADD_OP:
        gen_simple_op(c, locp, bit_width, bit_suffix, &res,
                      op_types, &op1_m, &op2_m,
                      " + ",
                      "tcg_gen_addi_",
                      "tcg_gen_addi_",
                      "tcg_gen_add_");
        break;
    case SUB_OP:
        gen_sub_op(c, locp, bit_width, bit_suffix, &res, op_types,
                   &op1_m, &op2_m);
        break;
    case MUL_OP:
        gen_simple_op(c, locp, bit_width, bit_suffix, &res,
                      op_types, &op1_m, &op2_m,
                      " * ",
                      "tcg_gen_muli_",
                      "tcg_gen_muli_",
                      "tcg_gen_mul_");
        break;
    case ASL_OP:
        gen_asl_op(c, locp, bit_width, op_is64bit, bit_suffix, &res, op_types,
                   &op1_m, &op2_m);
        break;
    case ASR_OP:
        gen_asr_op(c, locp, bit_width, op_is64bit, bit_suffix, &res, op_types,
                   &op1_m, &op2_m);
        break;
    case LSR_OP:
        gen_lsr_op(c, locp, bit_width, op_is64bit, bit_suffix, &res, op_types,
                   &op1_m, &op2_m);
        break;
    case ANDB_OP:
        gen_simple_op(c, locp, bit_width, bit_suffix, &res,
                      op_types, &op1_m, &op2_m,
                      " & ",
                      "tcg_gen_andi_",
                      "tcg_gen_andi_",
                      "tcg_gen_and_");
        break;
    case ORB_OP:
        gen_simple_op(c, locp, bit_width, bit_suffix, &res,
                      op_types, &op1_m, &op2_m,
                      " | ",
                      "tcg_gen_ori_",
                      "tcg_gen_ori_",
                      "tcg_gen_or_");
        break;
    case XORB_OP:
        gen_simple_op(c, locp, bit_width, bit_suffix, &res,
                      op_types, &op1_m, &op2_m,
                      " ^ ",
                      "tcg_gen_xori_",
                      "tcg_gen_xori_",
                      "tcg_gen_xor_");
        break;
    case ANDL_OP:
        gen_andl_op(c, locp, bit_width, bit_suffix, &res, op_types, &op1_m,
                    &op2_m);
        break;
    case MINI_OP:
        gen_minmax_op(c, locp, bit_width, &res, op_types, &op1_m, &op2_m,
                      false);
        break;
    case MAXI_OP:
        gen_minmax_op(c, locp, bit_width, &res, op_types, &op1_m, &op2_m, true);
        break;
    }
    return res;
}

HexValue gen_cast_op(Context *c,
                     YYLTYPE *locp,
                     HexValue *src,
                     unsigned target_width,
                     HexSignedness signedness)
{
    HexValue res;
    assert_signedness(c, locp, src->signedness);
    if (src->bit_width == target_width) {
        res = *src;
    } else if (src->bit_width < target_width) {
        res = gen_rvalue_extend(c, locp, src);
    } else {
        /* src->bit_width > target_width */
        res = gen_rvalue_truncate(c, locp, src);
    }
    res.signedness = signedness;
    return res;
}


/*
 * Implements an extension when the `src_width` is an immediate.
 * If the `value` to extend is also an immediate we use `extract/sextract`
 * from QEMU `bitops.h`. If `value` is a TCGv then we rely on
 * `tcg_gen_extract/tcg_gen_sextract`.
 */
static HexValue gen_extend_imm_width_op(Context *c,
                                        YYLTYPE *locp,
                                        HexValue *src_width,
                                        unsigned dst_width,
                                        HexValue *value,
                                        HexSignedness signedness)
{
    /*
     * If the source width is not an immediate value, we need to guard
     * our extend op with if statements to handle the case where
     * `src_width_m` is 0.
     */
    const char *sign_prefix;
    bool need_guarding;

    assert_signedness(c, locp, signedness);
    assert(dst_width == 64 || dst_width == 32);
    assert(src_width->type == IMMEDIATE);

    sign_prefix = (signedness == UNSIGNED) ? "" : "s";
    need_guarding = (src_width->imm.type != VALUE);

    if (src_width->imm.type == VALUE &&
        src_width->imm.value == 0) {
        /*
         * We can bail out early if the source width is known to be zero
         * at translation time.
         */
        return gen_imm_value(c, locp, 0, dst_width, signedness);
    }

    if (value->type == IMMEDIATE) {
        /*
         * If both the value and source width are immediates,
         * we can perform the extension at translation time
         * using QEMUs bitops.
         */
        HexValue res = gen_imm_qemu_tmp(c, locp, dst_width, signedness);
        gen_c_int_type(c, locp, dst_width, signedness);
        OUT(c, locp, " ", &res, " = 0;\n");
        if (need_guarding) {
            OUT(c, locp, "if (", src_width, " != 0) {\n");
        }
        OUT(c, locp, &res, " = ", sign_prefix, "extract", &dst_width);
        OUT(c, locp, "(", value, ", 0, ", src_width, ");\n");
        if (need_guarding) {
            OUT(c, locp, "}\n");
        }
        return res;
    } else {
        /*
         * If the source width is an immediate and the value to
         * extend is a TCGv, then use tcg_gen_extract/tcg_gen_sextract
         */
        HexValue res = gen_tmp(c, locp, dst_width, signedness);

        /*
         * If the width is an immediate value we know it is non-zero
         * at this point, otherwise we need an if-statement
         */
        if (need_guarding) {
            OUT(c, locp, "if (", src_width, " != 0) {\n");
        }
        OUT(c, locp, "tcg_gen_", sign_prefix, "extract_i", &dst_width);
        OUT(c, locp, "(", &res, ", ", value, ", 0, ", src_width,
            ");\n");
        if (need_guarding) {
            OUT(c, locp, "} else {\n");
            OUT(c, locp, "tcg_gen_movi_i", &dst_width, "(", &res,
                ", 0);\n");
            OUT(c, locp, "}\n");
        }
        return res;
    }
}

/*
 * Implements an extension when the `src_width` is given by
 * a TCGv. Here we need to reimplement the behaviour of
 * `tcg_gen_extract` and the like using shifts and masks.
 */
static HexValue gen_extend_tcg_width_op(Context *c,
                                        YYLTYPE *locp,
                                        HexValue *src_width,
                                        unsigned dst_width,
                                        HexValue *value,
                                        HexSignedness signedness)
{
    HexValue src_width_m = rvalue_materialize(c, locp, src_width);
    HexValue zero = gen_constant(c, locp, "0", dst_width, UNSIGNED);
    HexValue shift = gen_tmp(c, locp, dst_width, UNSIGNED);
    HexValue res;

    assert_signedness(c, locp, signedness);
    assert(dst_width == 64 || dst_width == 32);
    assert(src_width->type != IMMEDIATE);

    res = gen_tmp(c, locp, dst_width, signedness);

    OUT(c, locp, "tcg_gen_subfi_i", &dst_width);
    OUT(c, locp, "(", &shift, ", ", &dst_width, ", ", &src_width_m, ");\n");
    if (signedness == UNSIGNED) {
        HexValue mask = gen_constant(c, locp, "-1", dst_width, UNSIGNED);
        OUT(c, locp, "tcg_gen_shr_i", &dst_width, "(",
            &res, ", ", &mask, ", ", &shift, ");\n");
        OUT(c, locp, "tcg_gen_and_i", &dst_width, "(",
            &res, ", ", &res, ", ", value, ");\n");
    } else {
        OUT(c, locp, "tcg_gen_shl_i", &dst_width, "(",
            &res, ", ", value, ", ", &shift, ");\n");
        OUT(c, locp, "tcg_gen_sar_i", &dst_width, "(",
            &res, ", ", &res, ", ", &shift, ");\n");
    }
    OUT(c, locp, "tcg_gen_movcond_i", &dst_width, "(TCG_COND_EQ, ", &res,
        ", ");
    OUT(c, locp, &src_width_m, ", ", &zero, ", ", &zero, ", ", &res,
        ");\n");

    return res;
}

HexValue gen_extend_op(Context *c,
                       YYLTYPE *locp,
                       HexValue *src_width,
                       unsigned dst_width,
                       HexValue *value,
                       HexSignedness signedness)
{
    unsigned bit_width = (dst_width == 64) ? 64 : 32;
    HexValue value_m = *value;
    HexValue src_width_m = *src_width;

    assert_signedness(c, locp, signedness);
    yyassert(c, locp, value_m.bit_width <= bit_width &&
                      src_width_m.bit_width <= bit_width,
                      "Extending to a size smaller than the current size"
                      " makes no sense");

    if (value_m.bit_width < bit_width) {
        value_m = gen_rvalue_extend(c, locp, &value_m);
    }

    if (src_width_m.bit_width < bit_width) {
        src_width_m = gen_rvalue_extend(c, locp, &src_width_m);
    }

    if (src_width_m.type == IMMEDIATE) {
        return gen_extend_imm_width_op(c, locp, &src_width_m, bit_width,
                                       &value_m, signedness);
    } else {
        return gen_extend_tcg_width_op(c, locp, &src_width_m, bit_width,
                                       &value_m, signedness);
    }
}

/*
 * Implements `rdeposit` for the special case where `width`
 * is of TCGv type. In this case we need to reimplement the behaviour
 * of `tcg_gen_deposit*` using binary operations and masks/shifts.
 *
 * Note: this is the only type of `rdeposit` that occurs, meaning the
 * `width` is _NEVER_ of IMMEDIATE type.
 */
void gen_rdeposit_op(Context *c,
                     YYLTYPE *locp,
                     HexValue *dst,
                     HexValue *value,
                     HexValue *begin,
                     HexValue *width)
{
    /*
     * Otherwise if the width is not known, we fallback on reimplementing
     * desposit in TCG.
     */
    HexValue begin_m = *begin;
    HexValue value_m = *value;
    HexValue width_m = *width;
    const char *mask_str = (dst->bit_width == 32)
        ? "0xffffffffUL"
        : "0xffffffffffffffffUL";
    HexValue mask = gen_constant(c, locp, mask_str, dst->bit_width,
                                 UNSIGNED);
    const char *dst_width_str = (dst->bit_width == 32) ? "32" : "64";
    HexValue k64 = gen_constant(c, locp, dst_width_str, dst->bit_width,
                                UNSIGNED);
    HexValue res;
    HexValue zero;

    assert(dst->bit_width >= value->bit_width);
    assert(begin->type == IMMEDIATE && begin->imm.type == VALUE);
    assert(dst->type == REGISTER_ARG);

    yyassert(c, locp, width->type != IMMEDIATE,
             "Immediate index to rdeposit not handled!");

    yyassert(c, locp, value_m.bit_width == dst->bit_width &&
                      begin_m.bit_width == dst->bit_width &&
                      width_m.bit_width == dst->bit_width,
                      "Extension/truncation should be taken care of"
                      " before rdeposit!");

    width_m = rvalue_materialize(c, locp, &width_m);

    /*
     * mask = 0xffffffffffffffff >> (64 - width)
     * mask = mask << begin
     * value = (value << begin) & mask
     * res = dst & ~mask
     * res = res | value
     * dst = (width != 0) ? res : dst
     */
    k64 = gen_bin_op(c, locp, SUB_OP, &k64, &width_m);
    mask = gen_bin_op(c, locp, LSR_OP, &mask, &k64);
    mask = gen_bin_op(c, locp, ASL_OP, &mask, &begin_m);
    value_m = gen_bin_op(c, locp, ASL_OP, &value_m, &begin_m);
    value_m = gen_bin_op(c, locp, ANDB_OP, &value_m, &mask);

    OUT(c, locp, "tcg_gen_not_i", &dst->bit_width, "(", &mask, ", ",
        &mask, ");\n");
    res = gen_bin_op(c, locp, ANDB_OP, dst, &mask);
    res = gen_bin_op(c, locp, ORB_OP, &res, &value_m);

    /*
     * We don't need to truncate `res` here, since all operations involved use
     * the same bit width.
     */

    /* If the width is zero, then return the identity dst = dst */
    zero = gen_constant(c, locp, "0", res.bit_width, UNSIGNED);
    OUT(c, locp, "tcg_gen_movcond_i", &res.bit_width, "(TCG_COND_NE, ",
        dst);
    OUT(c, locp, ", ", &width_m, ", ", &zero, ", ", &res, ", ", dst,
        ");\n");
}

void gen_deposit_op(Context *c,
                    YYLTYPE *locp,
                    HexValue *dst,
                    HexValue *value,
                    HexValue *index,
                    HexCast *cast)
{
    HexValue value_m = *value;
    unsigned bit_width = (dst->bit_width == 64) ? 64 : 32;
    unsigned width = cast->bit_width;

    yyassert(c, locp, index->type == IMMEDIATE,
             "Deposit index must be immediate!\n");

    /*
     * Using tcg_gen_deposit_i**(dst, dst, ...) requires dst to be
     * initialized.
     */
    gen_inst_init_args(c, locp);

    /* If the destination value is 32, truncate the value, otherwise extend */
    if (dst->bit_width != value->bit_width) {
        if (bit_width == 32) {
            value_m = gen_rvalue_truncate(c, locp, &value_m);
        } else {
            value_m = gen_rvalue_extend(c, locp, &value_m);
        }
    }
    value_m = rvalue_materialize(c, locp, &value_m);
    OUT(c, locp, "tcg_gen_deposit_i", &bit_width, "(", dst, ", ", dst, ", ");
    OUT(c, locp, &value_m, ", ", index, " * ", &width, ", ", &width, ");\n");
}

HexValue gen_rextract_op(Context *c,
                         YYLTYPE *locp,
                         HexValue *src,
                         unsigned begin,
                         unsigned width)
{
    unsigned bit_width = (src->bit_width == 64) ? 64 : 32;
    HexValue res = gen_tmp(c, locp, bit_width, UNSIGNED);
    OUT(c, locp, "tcg_gen_extract_i", &bit_width, "(", &res);
    OUT(c, locp, ", ", src, ", ", &begin, ", ", &width, ");\n");
    return res;
}

HexValue gen_extract_op(Context *c,
                        YYLTYPE *locp,
                        HexValue *src,
                        HexValue *index,
                        HexExtract *extract)
{
    unsigned bit_width = (src->bit_width == 64) ? 64 : 32;
    unsigned width = extract->bit_width;
    const char *sign_prefix;
    HexValue res;

    yyassert(c, locp, index->type == IMMEDIATE,
             "Extract index must be immediate!\n");
    assert_signedness(c, locp, extract->signedness);

    sign_prefix = (extract->signedness == UNSIGNED) ? "" : "s";
    res = gen_tmp(c, locp, bit_width, extract->signedness);

    OUT(c, locp, "tcg_gen_", sign_prefix, "extract_i", &bit_width,
        "(", &res, ", ", src);
    OUT(c, locp, ", ", index, " * ", &width, ", ", &width, ");\n");

    /* Some extract operations have bit_width != storage_bit_width */
    if (extract->storage_bit_width > bit_width) {
        HexValue tmp = gen_tmp(c, locp, extract->storage_bit_width,
                               extract->signedness);
        const char *sign_suffix = (extract->signedness == UNSIGNED) ? "u" : "";
        OUT(c, locp, "tcg_gen_ext", sign_suffix, "_i32_i64(",
            &tmp, ", ", &res, ");\n");
        res = tmp;
    }
    return res;
}

void gen_write_reg(Context *c, YYLTYPE *locp, HexValue *reg, HexValue *value)
{
    HexValue value_m = *value;
    yyassert(c, locp, reg->type == REGISTER, "reg must be a register!");
    value_m = gen_rvalue_truncate(c, locp, &value_m);
    value_m = rvalue_materialize(c, locp, &value_m);
    OUT(c,
        locp,
        "gen_log_reg_write(ctx, ", &reg->reg.id, ", ",
        &value_m, ");\n");
}

void gen_assign(Context *c,
                YYLTYPE *locp,
                HexValue *dst,
                HexValue *value)
{
    HexValue value_m = *value;
    unsigned bit_width;

    yyassert(c, locp, !is_inside_ternary(c),
             "Assign in ternary not allowed!");

    if (dst->type == REGISTER) {
        gen_write_reg(c, locp, dst, &value_m);
        return;
    }

    if (dst->type == VARID) {
        find_variable(c, locp, dst, dst);
    }
    bit_width = dst->bit_width == 64 ? 64 : 32;

    if (bit_width != value_m.bit_width) {
        if (bit_width == 64) {
            value_m = gen_rvalue_extend(c, locp, &value_m);
        } else {
            value_m = gen_rvalue_truncate(c, locp, &value_m);
        }
    }

    const char *imm_suffix = (value_m.type == IMMEDIATE) ? "i" : "";
    OUT(c, locp, "tcg_gen_mov", imm_suffix, "_i", &bit_width,
        "(", dst, ", ", &value_m, ");\n");
}

HexValue gen_convround(Context *c,
                       YYLTYPE *locp,
                       HexValue *src)
{
    HexValue src_m = *src;
    unsigned bit_width = src_m.bit_width;
    const char *size = (bit_width == 32) ? "32" : "64";
    HexValue res = gen_tmp(c, locp, bit_width, src->signedness);
    HexValue mask = gen_constant(c, locp, "0x3", bit_width, UNSIGNED);
    HexValue one = gen_constant(c, locp, "1", bit_width, UNSIGNED);
    HexValue and;
    HexValue src_p1;

    and = gen_bin_op(c, locp, ANDB_OP, &src_m, &mask);
    src_p1 = gen_bin_op(c, locp, ADD_OP, &src_m, &one);

    OUT(c, locp, "tcg_gen_movcond_i", size, "(TCG_COND_EQ, ", &res);
    OUT(c, locp, ", ", &and, ", ", &mask, ", ");
    OUT(c, locp, &src_p1, ", ", &src_m, ");\n");

    return res;
}

static HexValue gen_convround_n_b(Context *c,
                                  YYLTYPE *locp,
                                  HexValue *a,
                                  HexValue *n)
{
    HexValue one = gen_constant(c, locp, "1", 32, UNSIGNED);
    HexValue res = gen_tmp(c, locp, 64, UNSIGNED);
    HexValue tmp = gen_tmp(c, locp, 32, UNSIGNED);
    HexValue tmp_64 = gen_tmp(c, locp, 64, UNSIGNED);

    assert(n->type != IMMEDIATE);
    OUT(c, locp, "tcg_gen_ext_i32_i64(", &res, ", ", a, ");\n");
    OUT(c, locp, "tcg_gen_shl_i32(", &tmp);
    OUT(c, locp, ", ", &one, ", ", n, ");\n");
    OUT(c, locp, "tcg_gen_and_i32(", &tmp);
    OUT(c, locp, ", ", &tmp, ", ", a, ");\n");
    OUT(c, locp, "tcg_gen_shri_i32(", &tmp);
    OUT(c, locp, ", ", &tmp, ", 1);\n");
    OUT(c, locp, "tcg_gen_ext_i32_i64(", &tmp_64, ", ", &tmp, ");\n");
    OUT(c, locp, "tcg_gen_add_i64(", &res);
    OUT(c, locp, ", ", &res, ", ", &tmp_64, ");\n");

    return res;
}

static HexValue gen_convround_n_c(Context *c,
                                  YYLTYPE *locp,
                                  HexValue *a,
                                  HexValue *n)
{
    HexValue res = gen_tmp(c, locp, 64, UNSIGNED);
    HexValue one = gen_constant(c, locp, "1", 32, UNSIGNED);
    HexValue tmp = gen_tmp(c, locp, 32, UNSIGNED);
    HexValue tmp_64 = gen_tmp(c, locp, 64, UNSIGNED);

    OUT(c, locp, "tcg_gen_ext_i32_i64(", &res, ", ", a, ");\n");
    OUT(c, locp, "tcg_gen_subi_i32(", &tmp);
    OUT(c, locp, ", ", n, ", 1);\n");
    OUT(c, locp, "tcg_gen_shl_i32(", &tmp);
    OUT(c, locp, ", ", &one, ", ", &tmp, ");\n");
    OUT(c, locp, "tcg_gen_ext_i32_i64(", &tmp_64, ", ", &tmp, ");\n");
    OUT(c, locp, "tcg_gen_add_i64(", &res);
    OUT(c, locp, ", ", &res, ", ", &tmp_64, ");\n");

    return res;
}

HexValue gen_convround_n(Context *c,
                         YYLTYPE *locp,
                         HexValue *src,
                         HexValue *pos)
{
    HexValue zero = gen_constant(c, locp, "0", 64, UNSIGNED);
    HexValue l_32 = gen_constant(c, locp, "1", 32, UNSIGNED);
    HexValue cond = gen_tmp(c, locp, 32, UNSIGNED);
    HexValue cond_64 = gen_tmp(c, locp, 64, UNSIGNED);
    HexValue mask = gen_tmp(c, locp, 32, UNSIGNED);
    HexValue n_64 = gen_tmp(c, locp, 64, UNSIGNED);
    HexValue res = gen_tmp(c, locp, 64, UNSIGNED);
    /* If input is 64 bit cast it to 32 */
    HexValue src_casted = gen_cast_op(c, locp, src, 32, src->signedness);
    HexValue pos_casted = gen_cast_op(c, locp, pos, 32, pos->signedness);
    HexValue r1;
    HexValue r2;
    HexValue r3;

    src_casted = rvalue_materialize(c, locp, &src_casted);
    pos_casted = rvalue_materialize(c, locp, &pos_casted);

    /*
     * r1, r2, and r3 represent the results of three different branches.
     *   - r1 picked if pos_casted == 0
     *   - r2 picked if (src_casted & ((1 << (pos_casted - 1)) - 1)) == 0),
     *     that is if bits 0, ..., pos_casted-1 are all 0.
     *   - r3 picked otherwise.
     */
    r1 = gen_rvalue_extend(c, locp, &src_casted);
    r2 = gen_convround_n_b(c, locp, &src_casted, &pos_casted);
    r3 = gen_convround_n_c(c, locp, &src_casted, &pos_casted);

    /*
     * Calculate the condition
     *   (src_casted & ((1 << (pos_casted - 1)) - 1)) == 0),
     * which checks if the bits 0,...,pos-1 are all 0.
     */
    OUT(c, locp, "tcg_gen_sub_i32(", &mask);
    OUT(c, locp, ", ", &pos_casted, ", ", &l_32, ");\n");
    OUT(c, locp, "tcg_gen_shl_i32(", &mask);
    OUT(c, locp, ", ", &l_32, ", ", &mask, ");\n");
    OUT(c, locp, "tcg_gen_sub_i32(", &mask);
    OUT(c, locp, ", ", &mask, ", ", &l_32, ");\n");
    OUT(c, locp, "tcg_gen_and_i32(", &cond);
    OUT(c, locp, ", ", &src_casted, ", ", &mask, ");\n");
    OUT(c, locp, "tcg_gen_extu_i32_i64(", &cond_64, ", ", &cond, ");\n");

    OUT(c, locp, "tcg_gen_ext_i32_i64(", &n_64, ", ", &pos_casted, ");\n");

    /*
     * if the bits 0, ..., pos_casted-1 are all 0, then pick r2 otherwise,
     * pick r3.
     */
    OUT(c, locp, "tcg_gen_movcond_i64");
    OUT(c, locp, "(TCG_COND_EQ, ", &res, ", ", &cond_64, ", ", &zero);
    OUT(c, locp, ", ", &r2, ", ", &r3, ");\n");

    /* Lastly, if the pos_casted == 0, then pick r1 */
    OUT(c, locp, "tcg_gen_movcond_i64");
    OUT(c, locp, "(TCG_COND_EQ, ", &res, ", ", &n_64, ", ", &zero);
    OUT(c, locp, ", ", &r1, ", ", &res, ");\n");

    /* Finally shift back val >>= n */
    OUT(c, locp, "tcg_gen_shr_i64(", &res);
    OUT(c, locp, ", ", &res, ", ", &n_64, ");\n");

    res = gen_rvalue_truncate(c, locp, &res);
    return res;
}

HexValue gen_round(Context *c,
                   YYLTYPE *locp,
                   HexValue *src,
                   HexValue *pos)
{
    HexValue zero = gen_constant(c, locp, "0", 64, UNSIGNED);
    HexValue one = gen_constant(c, locp, "1", 64, UNSIGNED);
    HexValue res;
    HexValue n_m1;
    HexValue shifted;
    HexValue sum;
    HexValue src_width;
    HexValue a;
    HexValue b;

    assert_signedness(c, locp, src->signedness);
    yyassert(c, locp, src->bit_width <= 32,
             "fRNDN not implemented for bit widths > 32!");

    res = gen_tmp(c, locp, 64, src->signedness);

    src_width = gen_imm_value(c, locp, src->bit_width, 32, UNSIGNED);
    a = gen_extend_op(c, locp, &src_width, 64, src, SIGNED);
    a = rvalue_materialize(c, locp, &a);

    src_width = gen_imm_value(c, locp, 5, 32, UNSIGNED);
    b = gen_extend_op(c, locp, &src_width, 64, pos, UNSIGNED);
    b = rvalue_materialize(c, locp, &b);

    n_m1 = gen_bin_op(c, locp, SUB_OP, &b, &one);
    shifted = gen_bin_op(c, locp, ASL_OP, &one, &n_m1);
    sum = gen_bin_op(c, locp, ADD_OP, &shifted, &a);

    OUT(c, locp, "tcg_gen_movcond_i64");
    OUT(c, locp, "(TCG_COND_EQ, ", &res, ", ", &b, ", ", &zero);
    OUT(c, locp, ", ", &a, ", ", &sum, ");\n");

    return res;
}

/* Circular addressing mode with auto-increment */
void gen_circ_op(Context *c,
                 YYLTYPE *locp,
                 HexValue *addr,
                 HexValue *increment,
                 HexValue *modifier)
{
    HexValue cs = gen_tmp(c, locp, 32, UNSIGNED);
    HexValue increment_m = *increment;
    increment_m = rvalue_materialize(c, locp, &increment_m);
    OUT(c, locp, "gen_read_reg(", &cs, ", HEX_REG_CS0 + MuN);\n");
    OUT(c,
        locp,
        "gen_helper_fcircadd(",
        addr,
        ", ",
        addr,
        ", ",
        &increment_m,
        ", ",
        modifier);
    OUT(c, locp, ", ", &cs, ");\n");
}

HexValue gen_locnt_op(Context *c, YYLTYPE *locp, HexValue *src)
{
    const char *bit_suffix = src->bit_width == 64 ? "64" : "32";
    HexValue src_m = *src;
    HexValue res;

    assert_signedness(c, locp, src->signedness);
    res = gen_tmp(c, locp, src->bit_width == 64 ? 64 : 32, src->signedness);
    src_m = rvalue_materialize(c, locp, &src_m);
    OUT(c, locp, "tcg_gen_not_i", bit_suffix, "(",
        &res, ", ", &src_m, ");\n");
    OUT(c, locp, "tcg_gen_clzi_i", bit_suffix, "(", &res, ", ", &res, ", ");
    OUT(c, locp, bit_suffix, ");\n");
    return res;
}

HexValue gen_ctpop_op(Context *c, YYLTYPE *locp, HexValue *src)
{
    const char *bit_suffix = src->bit_width == 64 ? "64" : "32";
    HexValue src_m = *src;
    HexValue res;
    assert_signedness(c, locp, src->signedness);
    res = gen_tmp(c, locp, src->bit_width == 64 ? 64 : 32, src->signedness);
    src_m = rvalue_materialize(c, locp, &src_m);
    OUT(c, locp, "tcg_gen_ctpop_i", bit_suffix,
        "(", &res, ", ", &src_m, ");\n");
    return res;
}

HexValue gen_rotl(Context *c, YYLTYPE *locp, HexValue *src, HexValue *width)
{
    const char *suffix = src->bit_width == 64 ? "i64" : "i32";
    HexValue amount = *width;
    HexValue res;
    assert_signedness(c, locp, src->signedness);
    res = gen_tmp(c, locp, src->bit_width, src->signedness);
    if (amount.bit_width < src->bit_width) {
        amount = gen_rvalue_extend(c, locp, &amount);
    } else {
        amount = gen_rvalue_truncate(c, locp, &amount);
    }
    amount = rvalue_materialize(c, locp, &amount);
    OUT(c, locp, "tcg_gen_rotl_", suffix, "(",
        &res, ", ", src, ", ", &amount, ");\n");

    return res;
}

HexValue gen_carry_from_add(Context *c,
                            YYLTYPE *locp,
                            HexValue *op1,
                            HexValue *op2,
                            HexValue *op3)
{
    HexValue zero = gen_constant(c, locp, "0", 64, UNSIGNED);
    HexValue res = gen_tmp(c, locp, 64, UNSIGNED);
    HexValue cf = gen_tmp(c, locp, 64, UNSIGNED);
    HexValue op1_m = rvalue_materialize(c, locp, op1);
    HexValue op2_m = rvalue_materialize(c, locp, op2);
    HexValue op3_m = rvalue_materialize(c, locp, op3);
    op3_m = gen_rvalue_extend(c, locp, &op3_m);

    OUT(c, locp, "tcg_gen_add2_i64(", &res, ", ", &cf, ", ", &op1_m, ", ",
        &zero);
    OUT(c, locp, ", ", &op3_m, ", ", &zero, ");\n");
    OUT(c, locp, "tcg_gen_add2_i64(", &res, ", ", &cf, ", ", &res, ", ", &cf);
    OUT(c, locp, ", ", &op2_m, ", ", &zero, ");\n");

    return cf;
}

void gen_addsat64(Context *c,
                  YYLTYPE *locp,
                  HexValue *dst,
                  HexValue *op1,
                  HexValue *op2)
{
    HexValue op1_m = rvalue_materialize(c, locp, op1);
    HexValue op2_m = rvalue_materialize(c, locp, op2);
    OUT(c, locp, "gen_add_sat_i64(ctx, ", dst, ", ", &op1_m, ", ",
                                  &op2_m, ");\n");
}

void gen_inst(Context *c, GString *iname)
{
    c->total_insn++;
    c->inst.name = iname;
    c->inst.allocated = g_array_new(FALSE, FALSE, sizeof(Var));
    c->inst.init_list = g_array_new(FALSE, FALSE, sizeof(HexValue));
    c->inst.strings = g_array_new(FALSE, FALSE, sizeof(GString *));
    EMIT_SIG(c, "void emit_%s(DisasContext *ctx, Insn *insn, Packet *pkt",
             c->inst.name->str);
}


/*
 * Initialize declared but uninitialized registers, but only for
 * non-conditional instructions
 */
void gen_inst_init_args(Context *c, YYLTYPE *locp)
{
    if (!c->inst.init_list) {
        return;
    }

    for (unsigned i = 0; i < c->inst.init_list->len; i++) {
        HexValue *val = &g_array_index(c->inst.init_list, HexValue, i);
        if (val->type == REGISTER_ARG) {
            /* Nothing to do here */
        } else if (val->type == PREDICATE) {
            char suffix = val->is_dotnew ? 'N' : 'V';
            EMIT_HEAD(c, "tcg_gen_movi_i%u(P%c%c, 0);\n", val->bit_width,
                      val->pred.id, suffix);
        } else {
            yyassert(c, locp, false, "Invalid arg type!");
        }
    }

    /* Free argument init list once we have initialized everything */
    g_array_free(c->inst.init_list, TRUE);
    c->inst.init_list = NULL;
}

void gen_inst_code(Context *c, YYLTYPE *locp)
{
    if (c->inst.error_count != 0) {
        fprintf(stderr,
                "Parsing of instruction %s generated %d errors!\n",
                c->inst.name->str,
                c->inst.error_count);
    } else {
        c->implemented_insn++;
        fprintf(c->enabled_file, "%s\n", c->inst.name->str);
        emit_footer(c);
        commit(c);
    }
    free_instruction(c);
}

void gen_pred_assign(Context *c, YYLTYPE *locp, HexValue *left_pred,
                     HexValue *right_pred)
{
    char pred_id[2] = {left_pred->pred.id, 0};
    bool is_direct = is_direct_predicate(left_pred);
    HexValue r = rvalue_materialize(c, locp, right_pred);
    r = gen_rvalue_truncate(c, locp, &r);
    yyassert(c, locp, !is_inside_ternary(c),
             "Predicate assign not allowed in ternary!");
    /* Extract predicate TCGv */
    if (is_direct) {
        *left_pred = gen_tmp(c, locp, 32, UNSIGNED);
    }
    /* Extract first 8 bits, and store new predicate value */
    OUT(c, locp, "tcg_gen_andi_i32(", left_pred, ", ", &r, ", 0xff);\n");
    if (is_direct) {
        OUT(c, locp, "gen_log_pred_write(ctx, ", pred_id, ", ", left_pred,
            ");\n");
    }
}

void gen_cancel(Context *c, YYLTYPE *locp)
{
    OUT(c, locp, "gen_cancel(insn->slot);\n");
}

void gen_load_cancel(Context *c, YYLTYPE *locp)
{
    OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n");
    OUT(c, locp, "ctx->s1_store_processed = false;\n");
    OUT(c, locp, "process_store(ctx, 1);\n");
    OUT(c, locp, "}\n");
}

void gen_load(Context *c, YYLTYPE *locp, HexValue *width,
              HexSignedness signedness, HexValue *ea, HexValue *dst)
{
    unsigned dst_bit_width;
    unsigned src_bit_width;

    /* Memop width is specified in the load macro */
    assert_signedness(c, locp, signedness);

    /* If dst is a variable, assert that is declared and load the type info */
    if (dst->type == VARID) {
        find_variable(c, locp, dst, dst);
    }

    src_bit_width = width->imm.value * 8;
    dst_bit_width = MAX(dst->bit_width, 32);

    /* Lookup the effective address EA */
    find_variable(c, locp, ea, ea);
    OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n");
    OUT(c, locp, "probe_noshuf_load(", ea, ", ", width, ", ctx->mem_idx);\n");
    OUT(c, locp, "process_store(ctx, 1);\n");
    OUT(c, locp, "}\n");

    OUT(c, locp, "tcg_gen_qemu_ld_i", &dst_bit_width);
    OUT(c, locp, "(");
    OUT(c, locp, dst, ", ", ea, ", ctx->mem_idx, MO_", &src_bit_width);
    if (signedness == SIGNED) {
        OUT(c, locp, " | MO_SIGN");
    }
    OUT(c, locp, " | MO_TE);\n");
}

void gen_store(Context *c, YYLTYPE *locp, HexValue *width, HexValue *ea,
               HexValue *src)
{
    HexValue src_m = *src;
    /* Memop width is specified in the store macro */
    unsigned mem_width = width->imm.value;
    /* Lookup the effective address EA */
    find_variable(c, locp, ea, ea);
    src_m = rvalue_materialize(c, locp, &src_m);
    OUT(c, locp, "gen_store", &mem_width, "(cpu_env, ", ea, ", ", &src_m);
    OUT(c, locp, ", insn->slot);\n");
}

void gen_sethalf(Context *c, YYLTYPE *locp, HexCast *sh, HexValue *n,
                 HexValue *dst, HexValue *value)
{
    yyassert(c, locp, n->type == IMMEDIATE,
             "Deposit index must be immediate!\n");
    if (dst->type == VARID) {
        find_variable(c, locp, dst, dst);
    }

    gen_deposit_op(c, locp, dst, value, n, sh);
}

void gen_setbits(Context *c, YYLTYPE *locp, HexValue *hi, HexValue *lo,
                 HexValue *dst, HexValue *value)
{
    unsigned len;
    HexValue tmp;

    yyassert(c, locp, hi->type == IMMEDIATE &&
             hi->imm.type == VALUE &&
             lo->type == IMMEDIATE &&
             lo->imm.type == VALUE,
             "Range deposit needs immediate values!\n");

    *value = gen_rvalue_truncate(c, locp, value);
    len = hi->imm.value + 1 - lo->imm.value;
    tmp = gen_tmp(c, locp, 32, value->signedness);
    /* Emit an `and` to ensure `value` is either 0 or 1. */
    OUT(c, locp, "tcg_gen_andi_i32(", &tmp, ", ", value, ", 1);\n");
    /* Use `neg` to map 0 -> 0 and 1 -> 0xffff... */
    OUT(c, locp, "tcg_gen_neg_i32(", &tmp, ", ", &tmp, ");\n");
    OUT(c, locp, "tcg_gen_deposit_i32(", dst, ", ", dst,
        ", ", &tmp, ", ");
    OUT(c, locp, lo, ", ", &len, ");\n");
}

unsigned gen_if_cond(Context *c, YYLTYPE *locp, HexValue *cond)
{
    const char *bit_suffix;
    /* Generate an end label, if false branch to that label */
    OUT(c, locp, "TCGLabel *if_label_", &c->inst.if_count,
        " = gen_new_label();\n");
    *cond = rvalue_materialize(c, locp, cond);
    bit_suffix = (cond->bit_width == 64) ? "i64" : "i32";
    OUT(c, locp, "tcg_gen_brcondi_", bit_suffix, "(TCG_COND_EQ, ", cond,
        ", 0, if_label_", &c->inst.if_count, ");\n");
    return c->inst.if_count++;
}

unsigned gen_if_else(Context *c, YYLTYPE *locp, unsigned index)
{
    unsigned if_index = c->inst.if_count++;
    /* Generate label to jump if else is not verified */
    OUT(c, locp, "TCGLabel *if_label_", &if_index,
        " = gen_new_label();\n");
    /* Jump out of the else statement */
    OUT(c, locp, "tcg_gen_br(if_label_", &if_index, ");\n");
    /* Fix the else label */
    OUT(c, locp, "gen_set_label(if_label_", &index, ");\n");
    return if_index;
}

HexValue gen_rvalue_pred(Context *c, YYLTYPE *locp, HexValue *pred)
{
    /* Predicted instructions need to zero out result args */
    gen_inst_init_args(c, locp);

    if (is_direct_predicate(pred)) {
        bool is_dotnew = pred->is_dotnew;
        char predicate_id[2] = { pred->pred.id, '\0' };
        char *pred_str = (char *) &predicate_id;
        *pred = gen_tmp(c, locp, 32, UNSIGNED);
        if (is_dotnew) {
            OUT(c, locp, "tcg_gen_mov_i32(", pred,
                ", ctx->new_pred_value[");
            OUT(c, locp, pred_str, "]);\n");
        } else {
            OUT(c, locp, "gen_read_preg(", pred, ", ", pred_str, ");\n");
        }
    }

    return *pred;
}

HexValue gen_rvalue_var(Context *c, YYLTYPE *locp, HexValue *var)
{
    find_variable(c, locp, var, var);
    return *var;
}

HexValue gen_rvalue_mpy(Context *c, YYLTYPE *locp, HexMpy *mpy,
                        HexValue *op1, HexValue *op2)
{
    HexValue res;
    memset(&res, 0, sizeof(HexValue));

    assert_signedness(c, locp, mpy->first_signedness);
    assert_signedness(c, locp, mpy->second_signedness);

    *op1 = gen_cast_op(c, locp, op1, mpy->first_bit_width * 2,
                     mpy->first_signedness);
    /* Handle fMPTY3216.. */
    if (mpy->first_bit_width == 32) {
        *op2 = gen_cast_op(c, locp, op2, 64, mpy->second_signedness);
    } else {
        *op2 = gen_cast_op(c, locp, op2, mpy->second_bit_width * 2,
                         mpy->second_signedness);
    }
    res = gen_bin_op(c, locp, MUL_OP, op1, op2);
    /* Handle special cases required by the language */
    if (mpy->first_bit_width == 16 && mpy->second_bit_width == 16) {
        HexValue src_width = gen_imm_value(c, locp, 32, 32, UNSIGNED);
        HexSignedness signedness = bin_op_signedness(c, locp,
                                                     mpy->first_signedness,
                                                     mpy->second_signedness);
        res = gen_extend_op(c, locp, &src_width, 64, &res,
                            signedness);
    }
    return res;
}

static inline HexValue gen_rvalue_simple_unary(Context *c, YYLTYPE *locp,
                                               HexValue *value,
                                               const char *c_code,
                                               const char *tcg_code)
{
    unsigned bit_width = (value->bit_width == 64) ? 64 : 32;
    HexValue res;
    if (value->type == IMMEDIATE) {
        res = gen_imm_qemu_tmp(c, locp, bit_width, value->signedness);
        gen_c_int_type(c, locp, value->bit_width, value->signedness);
        OUT(c, locp, " ", &res, " = ", c_code, "(", value, ");\n");
    } else {
        res = gen_tmp(c, locp, bit_width, value->signedness);
        OUT(c, locp, tcg_code, "_i", &bit_width, "(", &res, ", ", value,
            ");\n");
    }
    return res;
}


HexValue gen_rvalue_not(Context *c, YYLTYPE *locp, HexValue *value)
{
    return gen_rvalue_simple_unary(c, locp, value, "~", "tcg_gen_not");
}

HexValue gen_rvalue_notl(Context *c, YYLTYPE *locp, HexValue *value)
{
    unsigned bit_width = (value->bit_width == 64) ? 64 : 32;
    HexValue res;
    if (value->type == IMMEDIATE) {
        res = gen_imm_qemu_tmp(c, locp, bit_width, value->signedness);
        gen_c_int_type(c, locp, value->bit_width, value->signedness);
        OUT(c, locp, " ", &res, " = !(", value, ");\n");
    } else {
        HexValue zero = gen_constant(c, locp, "0", bit_width, UNSIGNED);
        HexValue one = gen_constant(c, locp, "0xff", bit_width, UNSIGNED);
        res = gen_tmp(c, locp, bit_width, value->signedness);
        OUT(c, locp, "tcg_gen_movcond_i", &bit_width);
        OUT(c, locp, "(TCG_COND_EQ, ", &res, ", ", value, ", ", &zero);
        OUT(c, locp, ", ", &one, ", ", &zero, ");\n");
    }
    return res;
}

HexValue gen_rvalue_sat(Context *c, YYLTYPE *locp, HexSat *sat,
                        HexValue *width, HexValue *value)
{
    const char *unsigned_str;
    const char *bit_suffix = (value->bit_width == 64) ? "i64" : "i32";
    HexValue res;
    HexValue ovfl;
    /*
     * Note: all saturates are assumed to implicitly set overflow.
     * This assumption holds for the instructions currently parsed
     * by idef-parser.
     */
    yyassert(c, locp, width->imm.value < value->bit_width,
             "To compute overflow, source width must be greater than"
             " saturation width!");
    yyassert(c, locp, !is_inside_ternary(c),
             "Saturating from within a ternary is not allowed!");
    assert_signedness(c, locp, sat->signedness);

    unsigned_str = (sat->signedness == UNSIGNED) ? "u" : "";
    res = gen_tmp(c, locp, value->bit_width, sat->signedness);
    ovfl = gen_tmp(c, locp, 32, sat->signedness);
    OUT(c, locp, "gen_sat", unsigned_str, "_", bit_suffix, "_ovfl(");
    OUT(c, locp, &ovfl, ", ", &res, ", ", value, ", ", &width->imm.value,
        ");\n");
    OUT(c, locp, "gen_set_usr_field_if(ctx, USR_OVF,", &ovfl, ");\n");

    return res;
}

HexValue gen_rvalue_fscr(Context *c, YYLTYPE *locp, HexValue *value)
{
    HexValue key = gen_tmp(c, locp, 64, UNSIGNED);
    HexValue res = gen_tmp(c, locp, 64, UNSIGNED);
    HexValue frame_key = gen_tmp(c, locp, 32, UNSIGNED);
    *value = gen_rvalue_extend(c, locp, value);
    OUT(c, locp, "gen_read_reg(", &frame_key, ", HEX_REG_FRAMEKEY);\n");
    OUT(c, locp, "tcg_gen_concat_i32_i64(",
        &key, ", ", &frame_key, ", ", &frame_key, ");\n");
    OUT(c, locp, "tcg_gen_xor_i64(", &res, ", ", value, ", ", &key, ");\n");
    return res;
}

HexValue gen_rvalue_abs(Context *c, YYLTYPE *locp, HexValue *value)
{
    return gen_rvalue_simple_unary(c, locp, value, "abs", "tcg_gen_abs");
}

HexValue gen_rvalue_neg(Context *c, YYLTYPE *locp, HexValue *value)
{
    return gen_rvalue_simple_unary(c, locp, value, "-", "tcg_gen_neg");
}

HexValue gen_rvalue_brev(Context *c, YYLTYPE *locp, HexValue *value)
{
    HexValue res;
    yyassert(c, locp, value->bit_width <= 32,
             "fbrev not implemented for 64-bit integers!");
    res = gen_tmp(c, locp, value->bit_width, value->signedness);
    *value = rvalue_materialize(c, locp, value);
    OUT(c, locp, "gen_helper_fbrev(", &res, ", ", value, ");\n");
    return res;
}

HexValue gen_rvalue_ternary(Context *c, YYLTYPE *locp, HexValue *cond,
                            HexValue *true_branch, HexValue *false_branch)
{
    bool is_64bit = (true_branch->bit_width == 64) ||
                    (false_branch->bit_width == 64);
    unsigned bit_width = (is_64bit) ? 64 : 32;
    HexValue zero = gen_constant(c, locp, "0", bit_width, UNSIGNED);
    HexValue res = gen_tmp(c, locp, bit_width, UNSIGNED);

    if (is_64bit) {
        *cond = gen_rvalue_extend(c, locp, cond);
        *true_branch = gen_rvalue_extend(c, locp, true_branch);
        *false_branch = gen_rvalue_extend(c, locp, false_branch);
    } else {
        *cond = gen_rvalue_truncate(c, locp, cond);
    }
    *cond = rvalue_materialize(c, locp, cond);
    *true_branch = rvalue_materialize(c, locp, true_branch);
    *false_branch = rvalue_materialize(c, locp, false_branch);

    OUT(c, locp, "tcg_gen_movcond_i", &bit_width);
    OUT(c, locp, "(TCG_COND_NE, ", &res, ", ", cond, ", ", &zero);
    OUT(c, locp, ", ", true_branch, ", ", false_branch, ");\n");

    assert(c->ternary->len > 0);
    g_array_remove_index(c->ternary, c->ternary->len - 1);

    return res;
}

const char *cond_to_str(TCGCond cond)
{
    switch (cond) {
    case TCG_COND_NEVER:
        return "TCG_COND_NEVER";
    case TCG_COND_ALWAYS:
        return "TCG_COND_ALWAYS";
    case TCG_COND_EQ:
        return "TCG_COND_EQ";
    case TCG_COND_NE:
        return "TCG_COND_NE";
    case TCG_COND_LT:
        return "TCG_COND_LT";
    case TCG_COND_GE:
        return "TCG_COND_GE";
    case TCG_COND_LE:
        return "TCG_COND_LE";
    case TCG_COND_GT:
        return "TCG_COND_GT";
    case TCG_COND_LTU:
        return "TCG_COND_LTU";
    case TCG_COND_GEU:
        return "TCG_COND_GEU";
    case TCG_COND_LEU:
        return "TCG_COND_LEU";
    case TCG_COND_GTU:
        return "TCG_COND_GTU";
    default:
        abort();
    }
}

void emit_arg(Context *c, YYLTYPE *locp, HexValue *arg)
{
    switch (arg->type) {
    case REGISTER_ARG:
        if (arg->reg.type == DOTNEW) {
            EMIT_SIG(c, ", TCGv N%cN", arg->reg.id);
        } else {
            bool is64 = (arg->bit_width == 64);
            const char *type = is64 ? "TCGv_i64" : "TCGv_i32";
            char reg_id[5];
            reg_compose(c, locp, &(arg->reg), reg_id);
            EMIT_SIG(c, ", %s %s", type, reg_id);
            /* MuV register requires also MuN to provide its index */
            if (arg->reg.type == MODIFIER) {
                EMIT_SIG(c, ", int MuN");
            }
        }
        break;
    case PREDICATE:
        {
            char suffix = arg->is_dotnew ? 'N' : 'V';
            EMIT_SIG(c, ", TCGv P%c%c", arg->pred.id, suffix);
        }
        break;
    default:
        {
            fprintf(stderr, "emit_arg got unsupported argument!");
            abort();
        }
    }
}

void emit_footer(Context *c)
{
    EMIT(c, "}\n");
    EMIT(c, "\n");
}

void track_string(Context *c, GString *s)
{
    g_array_append_val(c->inst.strings, s);
}

void free_instruction(Context *c)
{
    assert(!is_inside_ternary(c));
    /* Free the strings */
    g_string_truncate(c->signature_str, 0);
    g_string_truncate(c->out_str, 0);
    g_string_truncate(c->header_str, 0);
    /* Free strings allocated by the instruction */
    for (unsigned i = 0; i < c->inst.strings->len; i++) {
        g_string_free(g_array_index(c->inst.strings, GString*, i), TRUE);
    }
    g_array_free(c->inst.strings, TRUE);
    /* Free INAME token value */
    g_string_free(c->inst.name, TRUE);
    /* Free variables and registers */
    g_array_free(c->inst.allocated, TRUE);
    /* Initialize instruction-specific portion of the context */
    memset(&(c->inst), 0, sizeof(Inst));
}

void assert_signedness(Context *c,
                       YYLTYPE *locp,
                       HexSignedness signedness)
{
    yyassert(c, locp,
             signedness != UNKNOWN_SIGNEDNESS,
             "Unspecified signedness");
}
