1967a1ea98
Only predicate instruction arguments need to be initialized by idef-parser. This commit removes registers from the init_list and simplifies gen_inst_init_args() slightly. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com> Reviewed-by: Brian Cain <bcain@quicinc.com> Message-Id: <20240523125901.27797-5-anjo@rev.ng> Signed-off-by: Brian Cain <bcain@quicinc.com>
2149 lines
70 KiB
C
2149 lines
70 KiB
C
/*
|
|
* 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 success. */
|
|
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 evaluating 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
|
|
* deposit 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.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 increment_m = *increment;
|
|
increment_m = rvalue_materialize(c, locp, &increment_m);
|
|
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 instruction arguments. Only needed for
|
|
* predicate arguments, initialization of registers is handled by the Hexagon
|
|
* frontend.
|
|
*/
|
|
void gen_inst_init_args(Context *c, YYLTYPE *locp)
|
|
{
|
|
HexValue *val = NULL;
|
|
char suffix;
|
|
|
|
/* If init_list is NULL arguments have already been initialized */
|
|
if (!c->inst.init_list) {
|
|
return;
|
|
}
|
|
|
|
for (unsigned i = 0; i < c->inst.init_list->len; i++) {
|
|
val = &g_array_index(c->inst.init_list, HexValue, i);
|
|
suffix = val->is_dotnew ? 'N' : 'V';
|
|
yyassert(c, locp, val->type == PREDICATE,
|
|
"Only predicates need to be initialized!");
|
|
yyassert(c, locp, val->bit_width == 32,
|
|
"Predicates should always be 32 bits");
|
|
EMIT_HEAD(c, "tcg_gen_movi_i32(P%c%c, 0);\n", val->pred.id, suffix);
|
|
}
|
|
|
|
/* 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, "(tcg_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 CS for circular addressing*/
|
|
if (arg->reg.type == MODIFIER) {
|
|
EMIT_SIG(c, ", TCGv CS");
|
|
}
|
|
}
|
|
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 list of arguments that might need initialization, if they haven't
|
|
* already been freed.
|
|
*/
|
|
if (c->inst.init_list) {
|
|
g_array_free(c->inst.init_list, TRUE);
|
|
}
|
|
/* Free INAME token value */
|
|
g_string_free(c->inst.name, TRUE);
|
|
/* Free declared TCGv variables */
|
|
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");
|
|
}
|