mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-21 01:12:32 +08:00
fd67aa1129
Adds two new external authors to etc/update-copyright.py to cover bfd/ax_tls.m4, and adds gprofng to dirs handled automatically, then updates copyright messages as follows: 1) Update cgen/utils.scm emitted copyrights. 2) Run "etc/update-copyright.py --this-year" with an extra external author I haven't committed, 'Kalray SA.', to cover gas testsuite files (which should have their copyright message removed). 3) Build with --enable-maintainer-mode --enable-cgen-maint=yes. 4) Check out */po/*.pot which we don't update frequently.
1588 lines
46 KiB
C
1588 lines
46 KiB
C
/* kvx-dis.c -- Kalray MPPA generic disassembler.
|
|
Copyright (C) 2009-2024 Free Software Foundation, Inc.
|
|
Contributed by Kalray SA.
|
|
|
|
This file is part of the GNU opcodes library.
|
|
|
|
This library 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 3, or (at your option)
|
|
any later version.
|
|
|
|
It 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; see the file COPYING3. If not,
|
|
see <http://www.gnu.org/licenses/>. */
|
|
|
|
#define STATIC_TABLE
|
|
#define DEFINE_TABLE
|
|
|
|
#include "sysdep.h"
|
|
#include "disassemble.h"
|
|
#include "libiberty.h"
|
|
#include "opintl.h"
|
|
#include <assert.h>
|
|
#include "elf-bfd.h"
|
|
#include "kvx-dis.h"
|
|
|
|
#include "elf/kvx.h"
|
|
#include "opcode/kvx.h"
|
|
|
|
/* Steering values for the kvx VLIW architecture. */
|
|
|
|
typedef enum
|
|
{
|
|
Steering_BCU,
|
|
Steering_LSU,
|
|
Steering_MAU,
|
|
Steering_ALU,
|
|
Steering__
|
|
} enum_Steering;
|
|
typedef uint8_t Steering;
|
|
|
|
/* BundleIssue enumeration. */
|
|
|
|
typedef enum
|
|
{
|
|
BundleIssue_BCU,
|
|
BundleIssue_TCA,
|
|
BundleIssue_ALU0,
|
|
BundleIssue_ALU1,
|
|
BundleIssue_MAU,
|
|
BundleIssue_LSU,
|
|
BundleIssue__,
|
|
} enum_BundleIssue;
|
|
typedef uint8_t BundleIssue;
|
|
|
|
/* An IMMX syllable is associated with the BundleIssue Extension_BundleIssue[extension]. */
|
|
static const BundleIssue Extension_BundleIssue[] = {
|
|
BundleIssue_ALU0,
|
|
BundleIssue_ALU1,
|
|
BundleIssue_MAU,
|
|
BundleIssue_LSU
|
|
};
|
|
|
|
static inline int
|
|
kvx_steering (uint32_t x)
|
|
{
|
|
return (((x) & 0x60000000) >> 29);
|
|
}
|
|
|
|
static inline int
|
|
kvx_extension (uint32_t x)
|
|
{
|
|
return (((x) & 0x18000000) >> 27);
|
|
}
|
|
|
|
static inline int
|
|
kvx_has_parallel_bit (uint32_t x)
|
|
{
|
|
return (((x) & 0x80000000) == 0x80000000);
|
|
}
|
|
|
|
static inline int
|
|
kvx_is_tca_opcode (uint32_t x)
|
|
{
|
|
unsigned major = ((x) >> 24) & 0x1F;
|
|
return (major > 1) && (major < 8);
|
|
}
|
|
|
|
static inline int
|
|
kvx_is_nop_opcode (uint32_t x)
|
|
{
|
|
return ((x) << 1) == 0xFFFFFFFE;
|
|
}
|
|
|
|
/* A raw instruction. */
|
|
|
|
struct insn_s
|
|
{
|
|
uint32_t syllables[KVXMAXSYLLABLES];
|
|
int len;
|
|
};
|
|
typedef struct insn_s insn_t;
|
|
|
|
|
|
static uint32_t bundle_words[KVXMAXBUNDLEWORDS];
|
|
|
|
static insn_t bundle_insn[KVXMAXBUNDLEISSUE];
|
|
|
|
/* A re-interpreted instruction. */
|
|
|
|
struct instr_s
|
|
{
|
|
int valid;
|
|
int opcode;
|
|
int immx[2];
|
|
int immx_valid[2];
|
|
int immx_count;
|
|
int nb_syllables;
|
|
};
|
|
|
|
/* Option for "pretty printing", ie, not the usual little endian objdump output. */
|
|
static int opt_pretty = 0;
|
|
/* Option for not emiting a new line between all bundles. */
|
|
static int opt_compact_assembly = 0;
|
|
|
|
void
|
|
parse_kvx_dis_option (const char *option)
|
|
{
|
|
/* Try to match options that are simple flags. */
|
|
if (startswith (option, "pretty"))
|
|
{
|
|
opt_pretty = 1;
|
|
return;
|
|
}
|
|
|
|
if (startswith (option, "compact-assembly"))
|
|
{
|
|
opt_compact_assembly = 1;
|
|
return;
|
|
}
|
|
|
|
if (startswith (option, "no-compact-assembly"))
|
|
{
|
|
opt_compact_assembly = 0;
|
|
return;
|
|
}
|
|
|
|
/* Invalid option. */
|
|
opcodes_error_handler (_("unrecognised disassembler option: %s"), option);
|
|
}
|
|
|
|
static void
|
|
parse_kvx_dis_options (const char *options)
|
|
{
|
|
const char *option_end;
|
|
|
|
if (options == NULL)
|
|
return;
|
|
|
|
while (*options != '\0')
|
|
{
|
|
/* Skip empty options. */
|
|
if (*options == ',')
|
|
{
|
|
options++;
|
|
continue;
|
|
}
|
|
|
|
/* We know that *options is neither NUL or a comma. */
|
|
option_end = options + 1;
|
|
while (*option_end != ',' && *option_end != '\0')
|
|
option_end++;
|
|
|
|
parse_kvx_dis_option (options);
|
|
|
|
/* Go on to the next one. If option_end points to a comma, it
|
|
will be skipped above. */
|
|
options = option_end;
|
|
}
|
|
}
|
|
|
|
struct kvx_dis_env
|
|
{
|
|
int kvx_arch_size;
|
|
struct kvxopc *opc_table;
|
|
struct kvx_Register *kvx_registers;
|
|
const char ***kvx_modifiers;
|
|
int *kvx_dec_registers;
|
|
int *kvx_regfiles;
|
|
unsigned int kvx_max_dec_registers;
|
|
int initialized_p;
|
|
};
|
|
|
|
static struct kvx_dis_env env = {
|
|
.kvx_arch_size = 0,
|
|
.opc_table = NULL,
|
|
.kvx_registers = NULL,
|
|
.kvx_modifiers = NULL,
|
|
.kvx_dec_registers = NULL,
|
|
.kvx_regfiles = NULL,
|
|
.initialized_p = 0,
|
|
.kvx_max_dec_registers = 0
|
|
};
|
|
|
|
static void
|
|
kvx_dis_init (struct disassemble_info *info)
|
|
{
|
|
env.kvx_arch_size = 32;
|
|
switch (info->mach)
|
|
{
|
|
case bfd_mach_kv3_1_64:
|
|
env.kvx_arch_size = 64;
|
|
/* fallthrough */
|
|
case bfd_mach_kv3_1_usr:
|
|
case bfd_mach_kv3_1:
|
|
default:
|
|
env.opc_table = kvx_kv3_v1_optab;
|
|
env.kvx_regfiles = kvx_kv3_v1_regfiles;
|
|
env.kvx_registers = kvx_kv3_v1_registers;
|
|
env.kvx_modifiers = kvx_kv3_v1_modifiers;
|
|
env.kvx_dec_registers = kvx_kv3_v1_dec_registers;
|
|
break;
|
|
case bfd_mach_kv3_2_64:
|
|
env.kvx_arch_size = 64;
|
|
/* fallthrough */
|
|
case bfd_mach_kv3_2_usr:
|
|
case bfd_mach_kv3_2:
|
|
env.opc_table = kvx_kv3_v2_optab;
|
|
env.kvx_regfiles = kvx_kv3_v2_regfiles;
|
|
env.kvx_registers = kvx_kv3_v2_registers;
|
|
env.kvx_modifiers = kvx_kv3_v2_modifiers;
|
|
env.kvx_dec_registers = kvx_kv3_v2_dec_registers;
|
|
break;
|
|
case bfd_mach_kv4_1_64:
|
|
env.kvx_arch_size = 64;
|
|
/* fallthrough */
|
|
case bfd_mach_kv4_1_usr:
|
|
case bfd_mach_kv4_1:
|
|
env.opc_table = kvx_kv4_v1_optab;
|
|
env.kvx_regfiles = kvx_kv4_v1_regfiles;
|
|
env.kvx_registers = kvx_kv4_v1_registers;
|
|
env.kvx_modifiers = kvx_kv4_v1_modifiers;
|
|
env.kvx_dec_registers = kvx_kv4_v1_dec_registers;
|
|
break;
|
|
}
|
|
|
|
env.kvx_max_dec_registers = env.kvx_regfiles[KVX_REGFILE_DEC_REGISTERS];
|
|
|
|
if (info->disassembler_options)
|
|
parse_kvx_dis_options (info->disassembler_options);
|
|
|
|
env.initialized_p = 1;
|
|
}
|
|
|
|
static bool
|
|
kvx_reassemble_bundle (int wordcount, int *_insncount)
|
|
{
|
|
|
|
/* Debugging flag. */
|
|
int debug = 0;
|
|
|
|
/* Available resources. */
|
|
int bcu_taken = 0;
|
|
int tca_taken = 0;
|
|
int alu0_taken = 0;
|
|
int alu1_taken = 0;
|
|
int mau_taken = 0;
|
|
int lsu_taken = 0;
|
|
|
|
if (debug)
|
|
fprintf (stderr, "kvx_reassemble_bundle: wordcount = %d\n", wordcount);
|
|
|
|
if (wordcount > KVXMAXBUNDLEWORDS)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "bundle exceeds maximum size\n");
|
|
return false;
|
|
}
|
|
|
|
struct instr_s instr[KVXMAXBUNDLEISSUE];
|
|
memset (instr, 0, sizeof (instr));
|
|
assert (KVXMAXBUNDLEISSUE >= BundleIssue__);
|
|
|
|
int i;
|
|
unsigned int j;
|
|
|
|
for (i = 0; i < wordcount; i++)
|
|
{
|
|
uint32_t syllable = bundle_words[i];
|
|
switch (kvx_steering (syllable))
|
|
{
|
|
case Steering_BCU:
|
|
/* BCU or TCA instruction. */
|
|
if (i == 0)
|
|
{
|
|
if (kvx_is_tca_opcode (syllable))
|
|
{
|
|
if (tca_taken)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Too many TCA instructions");
|
|
return false;
|
|
}
|
|
if (debug)
|
|
fprintf (stderr,
|
|
"Syllable 0: Set valid on TCA for instr %d with 0x%x\n",
|
|
BundleIssue_TCA, syllable);
|
|
instr[BundleIssue_TCA].valid = 1;
|
|
instr[BundleIssue_TCA].opcode = syllable;
|
|
instr[BundleIssue_TCA].nb_syllables = 1;
|
|
tca_taken = 1;
|
|
}
|
|
else
|
|
{
|
|
if (debug)
|
|
fprintf (stderr,
|
|
"Syllable 0: Set valid on BCU for instr %d with 0x%x\n",
|
|
BundleIssue_BCU, syllable);
|
|
|
|
instr[BundleIssue_BCU].valid = 1;
|
|
instr[BundleIssue_BCU].opcode = syllable;
|
|
instr[BundleIssue_BCU].nb_syllables = 1;
|
|
bcu_taken = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (i == 1 && bcu_taken && kvx_is_tca_opcode (syllable))
|
|
{
|
|
if (tca_taken)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Too many TCA instructions");
|
|
return false;
|
|
}
|
|
if (debug)
|
|
fprintf (stderr,
|
|
"Syllable 0: Set valid on TCA for instr %d with 0x%x\n",
|
|
BundleIssue_TCA, syllable);
|
|
instr[BundleIssue_TCA].valid = 1;
|
|
instr[BundleIssue_TCA].opcode = syllable;
|
|
instr[BundleIssue_TCA].nb_syllables = 1;
|
|
tca_taken = 1;
|
|
}
|
|
else
|
|
{
|
|
/* Not first syllable in bundle, IMMX. */
|
|
struct instr_s *instr_p =
|
|
&(instr[Extension_BundleIssue[kvx_extension (syllable)]]);
|
|
int immx_count = instr_p->immx_count;
|
|
if (immx_count > 1)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Too many IMMX syllables");
|
|
return false;
|
|
}
|
|
instr_p->immx[immx_count] = syllable;
|
|
instr_p->immx_valid[immx_count] = 1;
|
|
instr_p->nb_syllables++;
|
|
if (debug)
|
|
fprintf (stderr,
|
|
"Set IMMX[%d] on instr %d for extension %d @ %d\n",
|
|
immx_count,
|
|
Extension_BundleIssue[kvx_extension (syllable)],
|
|
kvx_extension (syllable), i);
|
|
instr_p->immx_count = immx_count + 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Steering_ALU:
|
|
if (alu0_taken == 0)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Set valid on ALU0 for instr %d with 0x%x\n",
|
|
BundleIssue_ALU0, syllable);
|
|
instr[BundleIssue_ALU0].valid = 1;
|
|
instr[BundleIssue_ALU0].opcode = syllable;
|
|
instr[BundleIssue_ALU0].nb_syllables = 1;
|
|
alu0_taken = 1;
|
|
}
|
|
else if (alu1_taken == 0)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Set valid on ALU1 for instr %d with 0x%x\n",
|
|
BundleIssue_ALU1, syllable);
|
|
instr[BundleIssue_ALU1].valid = 1;
|
|
instr[BundleIssue_ALU1].opcode = syllable;
|
|
instr[BundleIssue_ALU1].nb_syllables = 1;
|
|
alu1_taken = 1;
|
|
}
|
|
else if (mau_taken == 0)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr,
|
|
"Set valid on MAU (ALU) for instr %d with 0x%x\n",
|
|
BundleIssue_MAU, syllable);
|
|
instr[BundleIssue_MAU].valid = 1;
|
|
instr[BundleIssue_MAU].opcode = syllable;
|
|
instr[BundleIssue_MAU].nb_syllables = 1;
|
|
mau_taken = 1;
|
|
}
|
|
else if (lsu_taken == 0)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr,
|
|
"Set valid on LSU (ALU) for instr %d with 0x%x\n",
|
|
BundleIssue_LSU, syllable);
|
|
instr[BundleIssue_LSU].valid = 1;
|
|
instr[BundleIssue_LSU].opcode = syllable;
|
|
instr[BundleIssue_LSU].nb_syllables = 1;
|
|
lsu_taken = 1;
|
|
}
|
|
else if (kvx_is_nop_opcode (syllable))
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Ignoring NOP (ALU) syllable\n");
|
|
}
|
|
else
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Too many ALU instructions");
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case Steering_MAU:
|
|
if (mau_taken == 1)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Too many MAU instructions");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Set valid on MAU for instr %d with 0x%x\n",
|
|
BundleIssue_MAU, syllable);
|
|
instr[BundleIssue_MAU].valid = 1;
|
|
instr[BundleIssue_MAU].opcode = syllable;
|
|
instr[BundleIssue_MAU].nb_syllables = 1;
|
|
mau_taken = 1;
|
|
}
|
|
break;
|
|
|
|
case Steering_LSU:
|
|
if (lsu_taken == 1)
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Too many LSU instructions");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Set valid on LSU for instr %d with 0x%x\n",
|
|
BundleIssue_LSU, syllable);
|
|
instr[BundleIssue_LSU].valid = 1;
|
|
instr[BundleIssue_LSU].opcode = syllable;
|
|
instr[BundleIssue_LSU].nb_syllables = 1;
|
|
lsu_taken = 1;
|
|
}
|
|
}
|
|
if (debug)
|
|
fprintf (stderr, "Continue %d < %d?\n", i, wordcount);
|
|
}
|
|
|
|
/* Fill bundle_insn and count read syllables. */
|
|
int instr_idx = 0;
|
|
for (i = 0; i < KVXMAXBUNDLEISSUE; i++)
|
|
{
|
|
if (instr[i].valid == 1)
|
|
{
|
|
int syllable_idx = 0;
|
|
|
|
/* First copy opcode. */
|
|
bundle_insn[instr_idx].syllables[syllable_idx++] = instr[i].opcode;
|
|
bundle_insn[instr_idx].len = 1;
|
|
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
if (instr[i].immx_valid[j])
|
|
{
|
|
if (debug)
|
|
fprintf (stderr, "Instr %d valid immx[%d] is valid\n", i,
|
|
j);
|
|
bundle_insn[instr_idx].syllables[syllable_idx++] =
|
|
instr[i].immx[j];
|
|
bundle_insn[instr_idx].len++;
|
|
}
|
|
}
|
|
|
|
if (debug)
|
|
fprintf (stderr,
|
|
"Instr %d valid, copying in bundle_insn (%d syllables <-> %d)\n",
|
|
i, bundle_insn[instr_idx].len, instr[i].nb_syllables);
|
|
instr_idx++;
|
|
}
|
|
}
|
|
|
|
if (debug)
|
|
fprintf (stderr, "End => %d instructions\n", instr_idx);
|
|
|
|
*_insncount = instr_idx;
|
|
return true;
|
|
}
|
|
|
|
struct decoded_insn
|
|
{
|
|
/* The entry in the opc_table. */
|
|
struct kvxopc *opc;
|
|
/* The number of operands. */
|
|
int nb_ops;
|
|
/* The content of an operands. */
|
|
struct
|
|
{
|
|
enum
|
|
{
|
|
CAT_REGISTER,
|
|
CAT_MODIFIER,
|
|
CAT_IMMEDIATE,
|
|
} type;
|
|
/* The value of the operands. */
|
|
uint64_t val;
|
|
/* If it is an immediate, its sign. */
|
|
int sign;
|
|
/* If it is an immediate, is it pc relative. */
|
|
int pcrel;
|
|
/* The width of the operand. */
|
|
int width;
|
|
/* If it is a modifier, the modifier category.
|
|
An index in the modifier table. */
|
|
int mod_idx;
|
|
} operands[KVXMAXOPERANDS];
|
|
};
|
|
|
|
static int
|
|
decode_insn (bfd_vma memaddr, insn_t * insn, struct decoded_insn *res)
|
|
{
|
|
|
|
int found = 0;
|
|
int idx = 0;
|
|
for (struct kvxopc * op = env.opc_table;
|
|
op->as_op && (((char) op->as_op[0]) != 0); op++)
|
|
{
|
|
/* Find the format of this insn. */
|
|
int opcode_match = 1;
|
|
|
|
if (op->wordcount != insn->len)
|
|
continue;
|
|
|
|
for (int i = 0; i < op->wordcount; i++)
|
|
if ((op->codewords[i].mask & insn->syllables[i]) !=
|
|
op->codewords[i].opcode)
|
|
opcode_match = 0;
|
|
|
|
int encoding_space_flags = env.kvx_arch_size == 32
|
|
? kvxOPCODE_FLAG_MODE32 : kvxOPCODE_FLAG_MODE64;
|
|
|
|
for (int i = 0; i < op->wordcount; i++)
|
|
if (!(op->codewords[i].flags & encoding_space_flags))
|
|
opcode_match = 0;
|
|
|
|
if (opcode_match)
|
|
{
|
|
res->opc = op;
|
|
|
|
for (int i = 0; op->format[i]; i++)
|
|
{
|
|
struct kvx_bitfield *bf = op->format[i]->bfield;
|
|
int bf_nb = op->format[i]->bitfields;
|
|
int width = op->format[i]->width;
|
|
int type = op->format[i]->type;
|
|
const char *type_name = op->format[i]->tname;
|
|
int flags = op->format[i]->flags;
|
|
int shift = op->format[i]->shift;
|
|
int bias = op->format[i]->bias;
|
|
uint64_t value = 0;
|
|
|
|
for (int bf_idx = 0; bf_idx < bf_nb; bf_idx++)
|
|
{
|
|
int insn_idx = (int) bf[bf_idx].to_offset / 32;
|
|
int to_offset = bf[bf_idx].to_offset % 32;
|
|
uint64_t encoded_value =
|
|
insn->syllables[insn_idx] >> to_offset;
|
|
encoded_value &= (1LL << bf[bf_idx].size) - 1;
|
|
value |= encoded_value << bf[bf_idx].from_offset;
|
|
}
|
|
if (flags & kvxSIGNED)
|
|
{
|
|
uint64_t signbit = 1LL << (width - 1);
|
|
value = (value ^ signbit) - signbit;
|
|
}
|
|
value = (value << shift) + bias;
|
|
|
|
#define KVX_PRINT_REG(regfile,value) \
|
|
if(env.kvx_regfiles[regfile]+value < env.kvx_max_dec_registers) { \
|
|
res->operands[idx].val = env.kvx_dec_registers[env.kvx_regfiles[regfile]+value]; \
|
|
res->operands[idx].type = CAT_REGISTER; \
|
|
idx++; \
|
|
} else { \
|
|
res->operands[idx].val = ~0; \
|
|
res->operands[idx].type = CAT_REGISTER; \
|
|
idx++; \
|
|
}
|
|
|
|
if (env.opc_table == kvx_kv3_v1_optab)
|
|
{
|
|
switch (type)
|
|
{
|
|
case RegClass_kv3_v1_singleReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_GPR, value)
|
|
break;
|
|
case RegClass_kv3_v1_pairedReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_PGR, value)
|
|
break;
|
|
case RegClass_kv3_v1_quadReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_QGR, value)
|
|
break;
|
|
case RegClass_kv3_v1_systemReg:
|
|
case RegClass_kv3_v1_aloneReg:
|
|
case RegClass_kv3_v1_onlyraReg:
|
|
case RegClass_kv3_v1_onlygetReg:
|
|
case RegClass_kv3_v1_onlysetReg:
|
|
case RegClass_kv3_v1_onlyfxReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_SFR, value)
|
|
break;
|
|
case RegClass_kv3_v1_coproReg0M4:
|
|
case RegClass_kv3_v1_coproReg1M4:
|
|
case RegClass_kv3_v1_coproReg2M4:
|
|
case RegClass_kv3_v1_coproReg3M4:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XCR, value)
|
|
break;
|
|
case RegClass_kv3_v1_blockRegE:
|
|
case RegClass_kv3_v1_blockRegO:
|
|
case RegClass_kv3_v1_blockReg0M4:
|
|
case RegClass_kv3_v1_blockReg1M4:
|
|
case RegClass_kv3_v1_blockReg2M4:
|
|
case RegClass_kv3_v1_blockReg3M4:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XBR, value)
|
|
break;
|
|
case RegClass_kv3_v1_vectorReg:
|
|
case RegClass_kv3_v1_vectorRegE:
|
|
case RegClass_kv3_v1_vectorRegO:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XVR, value)
|
|
break;
|
|
case RegClass_kv3_v1_tileReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XTR, value)
|
|
break;
|
|
case RegClass_kv3_v1_matrixReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XMR, value)
|
|
break;
|
|
case Immediate_kv3_v1_sysnumber:
|
|
case Immediate_kv3_v1_signed10:
|
|
case Immediate_kv3_v1_signed16:
|
|
case Immediate_kv3_v1_signed27:
|
|
case Immediate_kv3_v1_wrapped32:
|
|
case Immediate_kv3_v1_signed37:
|
|
case Immediate_kv3_v1_signed43:
|
|
case Immediate_kv3_v1_signed54:
|
|
case Immediate_kv3_v1_wrapped64:
|
|
case Immediate_kv3_v1_unsigned6:
|
|
res->operands[idx].val = value;
|
|
res->operands[idx].sign = flags & kvxSIGNED;
|
|
res->operands[idx].width = width;
|
|
res->operands[idx].type = CAT_IMMEDIATE;
|
|
res->operands[idx].pcrel = 0;
|
|
idx++;
|
|
break;
|
|
case Immediate_kv3_v1_pcrel17:
|
|
case Immediate_kv3_v1_pcrel27:
|
|
res->operands[idx].val = value + memaddr;
|
|
res->operands[idx].sign = flags & kvxSIGNED;
|
|
res->operands[idx].width = width;
|
|
res->operands[idx].type = CAT_IMMEDIATE;
|
|
res->operands[idx].pcrel = 1;
|
|
idx++;
|
|
break;
|
|
case Modifier_kv3_v1_column:
|
|
case Modifier_kv3_v1_comparison:
|
|
case Modifier_kv3_v1_doscale:
|
|
case Modifier_kv3_v1_exunum:
|
|
case Modifier_kv3_v1_floatcomp:
|
|
case Modifier_kv3_v1_qindex:
|
|
case Modifier_kv3_v1_rectify:
|
|
case Modifier_kv3_v1_rounding:
|
|
case Modifier_kv3_v1_roundint:
|
|
case Modifier_kv3_v1_saturate:
|
|
case Modifier_kv3_v1_scalarcond:
|
|
case Modifier_kv3_v1_silent:
|
|
case Modifier_kv3_v1_simplecond:
|
|
case Modifier_kv3_v1_speculate:
|
|
case Modifier_kv3_v1_splat32:
|
|
case Modifier_kv3_v1_variant:
|
|
{
|
|
int sz = 0;
|
|
int mod_idx = type - Modifier_kv3_v1_column;
|
|
for (sz = 0; env.kvx_modifiers[mod_idx][sz]; ++sz);
|
|
const char *mod = value < (unsigned) sz
|
|
? env.kvx_modifiers[mod_idx][value] : NULL;
|
|
if (!mod) goto retry;
|
|
res->operands[idx].val = value;
|
|
res->operands[idx].type = CAT_MODIFIER;
|
|
res->operands[idx].mod_idx = mod_idx;
|
|
idx++;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf (stderr, "error: unexpected operand type (%s)\n",
|
|
type_name);
|
|
exit (-1);
|
|
};
|
|
}
|
|
else if (env.opc_table == kvx_kv3_v2_optab)
|
|
{
|
|
switch (type)
|
|
{
|
|
case RegClass_kv3_v2_singleReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_GPR, value)
|
|
break;
|
|
case RegClass_kv3_v2_pairedReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_PGR, value)
|
|
break;
|
|
case RegClass_kv3_v2_quadReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_QGR, value)
|
|
break;
|
|
case RegClass_kv3_v2_systemReg:
|
|
case RegClass_kv3_v2_aloneReg:
|
|
case RegClass_kv3_v2_onlyraReg:
|
|
case RegClass_kv3_v2_onlygetReg:
|
|
case RegClass_kv3_v2_onlysetReg:
|
|
case RegClass_kv3_v2_onlyfxReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_SFR, value)
|
|
break;
|
|
case RegClass_kv3_v2_coproReg:
|
|
case RegClass_kv3_v2_coproReg0M4:
|
|
case RegClass_kv3_v2_coproReg1M4:
|
|
case RegClass_kv3_v2_coproReg2M4:
|
|
case RegClass_kv3_v2_coproReg3M4:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XCR, value)
|
|
break;
|
|
case RegClass_kv3_v2_blockReg:
|
|
case RegClass_kv3_v2_blockRegE:
|
|
case RegClass_kv3_v2_blockRegO:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XBR, value)
|
|
break;
|
|
case RegClass_kv3_v2_vectorReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XVR, value)
|
|
break;
|
|
case RegClass_kv3_v2_tileReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XTR, value)
|
|
break;
|
|
case RegClass_kv3_v2_matrixReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XMR, value)
|
|
break;
|
|
case RegClass_kv3_v2_buffer2Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X2R, value)
|
|
break;
|
|
case RegClass_kv3_v2_buffer4Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X4R, value)
|
|
break;
|
|
case RegClass_kv3_v2_buffer8Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X8R, value)
|
|
break;
|
|
case RegClass_kv3_v2_buffer16Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X16R, value)
|
|
break;
|
|
case RegClass_kv3_v2_buffer32Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X32R, value)
|
|
break;
|
|
case RegClass_kv3_v2_buffer64Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X64R, value)
|
|
break;
|
|
case Immediate_kv3_v2_brknumber:
|
|
case Immediate_kv3_v2_sysnumber:
|
|
case Immediate_kv3_v2_signed10:
|
|
case Immediate_kv3_v2_signed16:
|
|
case Immediate_kv3_v2_signed27:
|
|
case Immediate_kv3_v2_wrapped32:
|
|
case Immediate_kv3_v2_signed37:
|
|
case Immediate_kv3_v2_signed43:
|
|
case Immediate_kv3_v2_signed54:
|
|
case Immediate_kv3_v2_wrapped64:
|
|
case Immediate_kv3_v2_unsigned6:
|
|
res->operands[idx].val = value;
|
|
res->operands[idx].sign = flags & kvxSIGNED;
|
|
res->operands[idx].width = width;
|
|
res->operands[idx].type = CAT_IMMEDIATE;
|
|
res->operands[idx].pcrel = 0;
|
|
idx++;
|
|
break;
|
|
case Immediate_kv3_v2_pcrel27:
|
|
case Immediate_kv3_v2_pcrel17:
|
|
res->operands[idx].val = value + memaddr;
|
|
res->operands[idx].sign = flags & kvxSIGNED;
|
|
res->operands[idx].width = width;
|
|
res->operands[idx].type = CAT_IMMEDIATE;
|
|
res->operands[idx].pcrel = 1;
|
|
idx++;
|
|
break;
|
|
case Modifier_kv3_v2_accesses:
|
|
case Modifier_kv3_v2_boolcas:
|
|
case Modifier_kv3_v2_cachelev:
|
|
case Modifier_kv3_v2_channel:
|
|
case Modifier_kv3_v2_coherency:
|
|
case Modifier_kv3_v2_comparison:
|
|
case Modifier_kv3_v2_conjugate:
|
|
case Modifier_kv3_v2_doscale:
|
|
case Modifier_kv3_v2_exunum:
|
|
case Modifier_kv3_v2_floatcomp:
|
|
case Modifier_kv3_v2_hindex:
|
|
case Modifier_kv3_v2_lsomask:
|
|
case Modifier_kv3_v2_lsumask:
|
|
case Modifier_kv3_v2_lsupack:
|
|
case Modifier_kv3_v2_qindex:
|
|
case Modifier_kv3_v2_rounding:
|
|
case Modifier_kv3_v2_scalarcond:
|
|
case Modifier_kv3_v2_shuffleV:
|
|
case Modifier_kv3_v2_shuffleX:
|
|
case Modifier_kv3_v2_silent:
|
|
case Modifier_kv3_v2_simplecond:
|
|
case Modifier_kv3_v2_speculate:
|
|
case Modifier_kv3_v2_splat32:
|
|
case Modifier_kv3_v2_transpose:
|
|
case Modifier_kv3_v2_variant:
|
|
{
|
|
int sz = 0;
|
|
int mod_idx = type - Modifier_kv3_v2_accesses;
|
|
for (sz = 0; env.kvx_modifiers[mod_idx][sz];
|
|
++sz);
|
|
const char *mod = value < (unsigned) sz
|
|
? env.kvx_modifiers[mod_idx][value] : NULL;
|
|
if (!mod) goto retry;
|
|
res->operands[idx].val = value;
|
|
res->operands[idx].type = CAT_MODIFIER;
|
|
res->operands[idx].mod_idx = mod_idx;
|
|
idx++;
|
|
};
|
|
break;
|
|
default:
|
|
fprintf (stderr,
|
|
"error: unexpected operand type (%s)\n",
|
|
type_name);
|
|
exit (-1);
|
|
};
|
|
}
|
|
else if (env.opc_table == kvx_kv4_v1_optab)
|
|
{
|
|
switch (type)
|
|
{
|
|
|
|
case RegClass_kv4_v1_singleReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_GPR, value)
|
|
break;
|
|
case RegClass_kv4_v1_pairedReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_PGR, value)
|
|
break;
|
|
case RegClass_kv4_v1_quadReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_QGR, value)
|
|
break;
|
|
case RegClass_kv4_v1_systemReg:
|
|
case RegClass_kv4_v1_aloneReg:
|
|
case RegClass_kv4_v1_onlyraReg:
|
|
case RegClass_kv4_v1_onlygetReg:
|
|
case RegClass_kv4_v1_onlysetReg:
|
|
case RegClass_kv4_v1_onlyfxReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_SFR, value)
|
|
break;
|
|
case RegClass_kv4_v1_coproReg:
|
|
case RegClass_kv4_v1_coproReg0M4:
|
|
case RegClass_kv4_v1_coproReg1M4:
|
|
case RegClass_kv4_v1_coproReg2M4:
|
|
case RegClass_kv4_v1_coproReg3M4:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XCR, value)
|
|
break;
|
|
case RegClass_kv4_v1_blockReg:
|
|
case RegClass_kv4_v1_blockRegE:
|
|
case RegClass_kv4_v1_blockRegO:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XBR, value)
|
|
break;
|
|
case RegClass_kv4_v1_vectorReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XVR, value)
|
|
break;
|
|
case RegClass_kv4_v1_tileReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XTR, value)
|
|
break;
|
|
case RegClass_kv4_v1_matrixReg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_XMR, value)
|
|
break;
|
|
case RegClass_kv4_v1_buffer2Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X2R, value)
|
|
break;
|
|
case RegClass_kv4_v1_buffer4Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X4R, value)
|
|
break;
|
|
case RegClass_kv4_v1_buffer8Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X8R, value)
|
|
break;
|
|
case RegClass_kv4_v1_buffer16Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X16R, value)
|
|
break;
|
|
case RegClass_kv4_v1_buffer32Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X32R, value)
|
|
break;
|
|
case RegClass_kv4_v1_buffer64Reg:
|
|
KVX_PRINT_REG (KVX_REGFILE_DEC_X64R, value)
|
|
break;
|
|
case Immediate_kv4_v1_brknumber:
|
|
case Immediate_kv4_v1_sysnumber:
|
|
case Immediate_kv4_v1_signed10:
|
|
case Immediate_kv4_v1_signed16:
|
|
case Immediate_kv4_v1_signed27:
|
|
case Immediate_kv4_v1_wrapped32:
|
|
case Immediate_kv4_v1_signed37:
|
|
case Immediate_kv4_v1_signed43:
|
|
case Immediate_kv4_v1_signed54:
|
|
case Immediate_kv4_v1_wrapped64:
|
|
case Immediate_kv4_v1_unsigned6:
|
|
res->operands[idx].val = value;
|
|
res->operands[idx].sign = flags & kvxSIGNED;
|
|
res->operands[idx].width = width;
|
|
res->operands[idx].type = CAT_IMMEDIATE;
|
|
res->operands[idx].pcrel = 0;
|
|
idx++;
|
|
break;
|
|
case Immediate_kv4_v1_pcrel27:
|
|
case Immediate_kv4_v1_pcrel17:
|
|
res->operands[idx].val = value + memaddr;
|
|
res->operands[idx].sign = flags & kvxSIGNED;
|
|
res->operands[idx].width = width;
|
|
res->operands[idx].type = CAT_IMMEDIATE;
|
|
res->operands[idx].pcrel = 1;
|
|
idx++;
|
|
break;
|
|
case Modifier_kv4_v1_accesses:
|
|
case Modifier_kv4_v1_boolcas:
|
|
case Modifier_kv4_v1_cachelev:
|
|
case Modifier_kv4_v1_channel:
|
|
case Modifier_kv4_v1_coherency:
|
|
case Modifier_kv4_v1_comparison:
|
|
case Modifier_kv4_v1_conjugate:
|
|
case Modifier_kv4_v1_doscale:
|
|
case Modifier_kv4_v1_exunum:
|
|
case Modifier_kv4_v1_floatcomp:
|
|
case Modifier_kv4_v1_hindex:
|
|
case Modifier_kv4_v1_lsomask:
|
|
case Modifier_kv4_v1_lsumask:
|
|
case Modifier_kv4_v1_lsupack:
|
|
case Modifier_kv4_v1_qindex:
|
|
case Modifier_kv4_v1_rounding:
|
|
case Modifier_kv4_v1_scalarcond:
|
|
case Modifier_kv4_v1_shuffleV:
|
|
case Modifier_kv4_v1_shuffleX:
|
|
case Modifier_kv4_v1_silent:
|
|
case Modifier_kv4_v1_simplecond:
|
|
case Modifier_kv4_v1_speculate:
|
|
case Modifier_kv4_v1_splat32:
|
|
case Modifier_kv4_v1_transpose:
|
|
case Modifier_kv4_v1_variant:
|
|
{
|
|
int sz = 0;
|
|
int mod_idx = type - Modifier_kv4_v1_accesses;
|
|
for (sz = 0; env.kvx_modifiers[mod_idx][sz]; ++sz);
|
|
const char *mod = value < (unsigned) sz
|
|
? env.kvx_modifiers[mod_idx][value] : NULL;
|
|
if (!mod) goto retry;
|
|
res->operands[idx].val = value;
|
|
res->operands[idx].type = CAT_MODIFIER;
|
|
res->operands[idx].mod_idx = mod_idx;
|
|
idx++;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf (stderr, "error: unexpected operand type (%s)\n",
|
|
type_name);
|
|
exit (-1);
|
|
};
|
|
}
|
|
|
|
#undef KVX_PRINT_REG
|
|
}
|
|
|
|
found = 1;
|
|
break;
|
|
retry:;
|
|
idx = 0;
|
|
continue;
|
|
}
|
|
}
|
|
res->nb_ops = idx;
|
|
return found;
|
|
}
|
|
|
|
int
|
|
print_insn_kvx (bfd_vma memaddr, struct disassemble_info *info)
|
|
{
|
|
static int insnindex = 0;
|
|
static int insncount = 0;
|
|
insn_t *insn;
|
|
int readsofar = 0;
|
|
int found = 0;
|
|
int invalid_bundle = 0;
|
|
|
|
if (!env.initialized_p)
|
|
kvx_dis_init (info);
|
|
|
|
/* Clear instruction information field. */
|
|
info->insn_info_valid = 0;
|
|
info->branch_delay_insns = 0;
|
|
info->data_size = 0;
|
|
info->insn_type = dis_noninsn;
|
|
info->target = 0;
|
|
info->target2 = 0;
|
|
|
|
/* Set line length. */
|
|
info->bytes_per_line = 16;
|
|
|
|
|
|
/* If this is the beginning of the bundle, read BUNDLESIZE words and apply
|
|
decentrifugate function. */
|
|
if (insnindex == 0)
|
|
{
|
|
int wordcount;
|
|
for (wordcount = 0; wordcount < KVXMAXBUNDLEWORDS; wordcount++)
|
|
{
|
|
int status;
|
|
status =
|
|
(*info->read_memory_func) (memaddr + 4 * wordcount,
|
|
(bfd_byte *) (bundle_words +
|
|
wordcount), 4, info);
|
|
if (status != 0)
|
|
{
|
|
(*info->memory_error_func) (status, memaddr + 4 * wordcount,
|
|
info);
|
|
return -1;
|
|
}
|
|
if (!kvx_has_parallel_bit (bundle_words[wordcount]))
|
|
break;
|
|
}
|
|
wordcount++;
|
|
invalid_bundle = !kvx_reassemble_bundle (wordcount, &insncount);
|
|
}
|
|
|
|
assert (insnindex < KVXMAXBUNDLEISSUE);
|
|
insn = &(bundle_insn[insnindex]);
|
|
readsofar = insn->len * 4;
|
|
insnindex++;
|
|
|
|
if (opt_pretty)
|
|
{
|
|
(*info->fprintf_func) (info->stream, "[ ");
|
|
for (int i = 0; i < insn->len; i++)
|
|
(*info->fprintf_func) (info->stream, "%08x ", insn->syllables[i]);
|
|
(*info->fprintf_func) (info->stream, "] ");
|
|
}
|
|
|
|
/* Check for extension to right iff this is not the end of bundle. */
|
|
|
|
struct decoded_insn dec;
|
|
memset (&dec, 0, sizeof dec);
|
|
if (!invalid_bundle && (found = decode_insn (memaddr, insn, &dec)))
|
|
{
|
|
int ch;
|
|
(*info->fprintf_func) (info->stream, "%s", dec.opc->as_op);
|
|
const char *fmtp = dec.opc->fmtstring;
|
|
for (int i = 0; i < dec.nb_ops; ++i)
|
|
{
|
|
/* Print characters in the format string up to the following % or nul. */
|
|
while ((ch = *fmtp) && ch != '%')
|
|
{
|
|
(*info->fprintf_func) (info->stream, "%c", ch);
|
|
fmtp++;
|
|
}
|
|
|
|
/* Skip past %s. */
|
|
if (ch == '%')
|
|
{
|
|
ch = *fmtp++;
|
|
fmtp++;
|
|
}
|
|
|
|
switch (dec.operands[i].type)
|
|
{
|
|
case CAT_REGISTER:
|
|
(*info->fprintf_func) (info->stream, "%s",
|
|
env.kvx_registers[dec.operands[i].val].name);
|
|
break;
|
|
case CAT_MODIFIER:
|
|
{
|
|
const char *mod = env.kvx_modifiers[dec.operands[i].mod_idx][dec.operands[i].val];
|
|
(*info->fprintf_func) (info->stream, "%s", !mod || !strcmp (mod, ".") ? "" : mod);
|
|
}
|
|
break;
|
|
case CAT_IMMEDIATE:
|
|
{
|
|
if (dec.operands[i].pcrel)
|
|
{
|
|
/* Fill in instruction information. */
|
|
info->insn_info_valid = 1;
|
|
info->insn_type =
|
|
dec.operands[i].width ==
|
|
17 ? dis_condbranch : dis_branch;
|
|
info->target = dec.operands[i].val;
|
|
|
|
info->print_address_func (dec.operands[i].val, info);
|
|
}
|
|
else if (dec.operands[i].sign)
|
|
{
|
|
if (dec.operands[i].width <= 32)
|
|
{
|
|
(*info->fprintf_func) (info->stream, "%" PRId32 " (0x%" PRIx32 ")",
|
|
(int32_t) dec.operands[i].val,
|
|
(int32_t) dec.operands[i].val);
|
|
}
|
|
else
|
|
{
|
|
(*info->fprintf_func) (info->stream, "%" PRId64 " (0x%" PRIx64 ")",
|
|
dec.operands[i].val,
|
|
dec.operands[i].val);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dec.operands[i].width <= 32)
|
|
{
|
|
(*info->fprintf_func) (info->stream, "%" PRIu32 " (0x%" PRIx32 ")",
|
|
(uint32_t) dec.operands[i].
|
|
val,
|
|
(uint32_t) dec.operands[i].
|
|
val);
|
|
}
|
|
else
|
|
{
|
|
(*info->fprintf_func) (info->stream, "%" PRIu64 " (0x%" PRIx64 ")",
|
|
(uint64_t) dec.
|
|
operands[i].val,
|
|
(uint64_t) dec.
|
|
operands[i].val);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
while ((ch = *fmtp))
|
|
{
|
|
(*info->fprintf_styled_func) (info->stream, dis_style_text, "%c",
|
|
ch);
|
|
fmtp++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
(*info->fprintf_func) (info->stream, "*** invalid opcode ***\n");
|
|
insnindex = 0;
|
|
readsofar = 4;
|
|
}
|
|
|
|
if (found && (insnindex == insncount))
|
|
{
|
|
(*info->fprintf_func) (info->stream, ";;");
|
|
if (!opt_compact_assembly)
|
|
(*info->fprintf_func) (info->stream, "\n");
|
|
insnindex = 0;
|
|
}
|
|
|
|
return readsofar;
|
|
}
|
|
|
|
/* This function searches in the current bundle for the instructions required
|
|
by unwinding. For prologue:
|
|
(1) addd $r12 = $r12, <res_stack>
|
|
(2) get <gpr_ra_reg> = $ra
|
|
(3) sd <ofs>[$r12] = <gpr_ra_reg> or sq/so containing <gpr_ra_reg>
|
|
(4) sd <ofs>[$r12] = $r14 or sq/so containing r14
|
|
(5) addd $r14 = $r12, <fp_ofs> or copyd $r14 = $r12
|
|
The only difference seen between the code generated by gcc and clang
|
|
is the setting/resetting r14. gcc could also generate copyd $r14=$r12
|
|
instead of add addd $r14 = $r12, <ofs> when <ofs> is 0.
|
|
Vice-versa, <ofs> is not guaranteed to be 0 for clang, so, clang
|
|
could also generate addd instead of copyd
|
|
(6) call, icall, goto, igoto, cb., ret
|
|
For epilogue:
|
|
(1) addd $r12 = $r12, <res_stack>
|
|
(2) addd $r12 = $r14, <offset> or copyd $r12 = $r14
|
|
Same comment as prologue (5).
|
|
(3) ret, goto
|
|
(4) call, icall, igoto, cb. */
|
|
|
|
int
|
|
decode_prologue_epilogue_bundle (bfd_vma memaddr,
|
|
struct disassemble_info *info,
|
|
struct kvx_prologue_epilogue_bundle *peb)
|
|
{
|
|
int i, nb_insn, nb_syl;
|
|
|
|
peb->nb_insn = 0;
|
|
|
|
if (info->arch != bfd_arch_kvx)
|
|
return -1;
|
|
|
|
if (!env.initialized_p)
|
|
kvx_dis_init (info);
|
|
|
|
/* Read the bundle. */
|
|
for (nb_syl = 0; nb_syl < KVXMAXBUNDLEWORDS; nb_syl++)
|
|
{
|
|
if ((*info->read_memory_func) (memaddr + 4 * nb_syl,
|
|
(bfd_byte *) &bundle_words[nb_syl], 4,
|
|
info))
|
|
return -1;
|
|
if (!kvx_has_parallel_bit (bundle_words[nb_syl]))
|
|
break;
|
|
}
|
|
nb_syl++;
|
|
if (!kvx_reassemble_bundle (nb_syl, &nb_insn))
|
|
return -1;
|
|
|
|
/* Check for extension to right if this is not the end of bundle
|
|
find the format of this insn. */
|
|
for (int idx_insn = 0; idx_insn < nb_insn; idx_insn++)
|
|
{
|
|
insn_t *insn = &bundle_insn[idx_insn];
|
|
int is_add = 0, is_get = 0, is_a_peb_insn = 0, is_copyd = 0;
|
|
|
|
struct decoded_insn dec;
|
|
memset (&dec, 0, sizeof dec);
|
|
if (!decode_insn (memaddr, insn, &dec))
|
|
continue;
|
|
|
|
const char *op_name = dec.opc->as_op;
|
|
struct kvx_prologue_epilogue_insn *crt_peb_insn;
|
|
|
|
crt_peb_insn = &peb->insn[peb->nb_insn];
|
|
crt_peb_insn->nb_gprs = 0;
|
|
|
|
if (!strcmp (op_name, "addd"))
|
|
is_add = 1;
|
|
else if (!strcmp (op_name, "copyd"))
|
|
is_copyd = 1;
|
|
else if (!strcmp (op_name, "get"))
|
|
is_get = 1;
|
|
else if (!strcmp (op_name, "sd"))
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_SD;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
else if (!strcmp (op_name, "sq"))
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_SQ;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
else if (!strcmp (op_name, "so"))
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_SO;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
else if (!strcmp (op_name, "ret"))
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_RET;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
else if (!strcmp (op_name, "goto"))
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_GOTO;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
else if (!strcmp (op_name, "igoto"))
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_IGOTO;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
else if (!strcmp (op_name, "call") || !strcmp (op_name, "icall"))
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_CALL;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
else if (!strncmp (op_name, "cb", 2))
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_CB;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
for (i = 0; dec.opc->format[i]; i++)
|
|
{
|
|
struct kvx_operand *fmt = dec.opc->format[i];
|
|
struct kvx_bitfield *bf = fmt->bfield;
|
|
int bf_nb = fmt->bitfields;
|
|
int width = fmt->width;
|
|
int type = fmt->type;
|
|
int flags = fmt->flags;
|
|
int shift = fmt->shift;
|
|
int bias = fmt->bias;
|
|
uint64_t encoded_value, value = 0;
|
|
|
|
for (int bf_idx = 0; bf_idx < bf_nb; bf_idx++)
|
|
{
|
|
int insn_idx = (int) bf[bf_idx].to_offset / 32;
|
|
int to_offset = bf[bf_idx].to_offset % 32;
|
|
encoded_value = insn->syllables[insn_idx] >> to_offset;
|
|
encoded_value &= (1LL << bf[bf_idx].size) - 1;
|
|
value |= encoded_value << bf[bf_idx].from_offset;
|
|
}
|
|
if (flags & kvxSIGNED)
|
|
{
|
|
uint64_t signbit = 1LL << (width - 1);
|
|
value = (value ^ signbit) - signbit;
|
|
}
|
|
value = (value << shift) + bias;
|
|
|
|
#define chk_type(core_, val_) \
|
|
(env.opc_table == kvx_## core_ ##_optab && type == (val_))
|
|
|
|
if (chk_type (kv3_v1, RegClass_kv3_v1_singleReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_singleReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_singleReg))
|
|
{
|
|
if (env.kvx_regfiles[KVX_REGFILE_DEC_GPR] + value
|
|
>= env.kvx_max_dec_registers)
|
|
return -1;
|
|
if (is_add && i < 2)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
if (value == KVX_GPR_REG_SP)
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_ADD_SP;
|
|
else if (value == KVX_GPR_REG_FP)
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_ADD_FP;
|
|
else
|
|
is_add = 0;
|
|
}
|
|
else if (i == 1)
|
|
{
|
|
if (value == KVX_GPR_REG_SP)
|
|
is_a_peb_insn = 1;
|
|
else if (value == KVX_GPR_REG_FP
|
|
&& crt_peb_insn->insn_type
|
|
== KVX_PROL_EPIL_INSN_ADD_SP)
|
|
{
|
|
crt_peb_insn->insn_type
|
|
= KVX_PROL_EPIL_INSN_RESTORE_SP_FROM_FP;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
else
|
|
is_add = 0;
|
|
}
|
|
}
|
|
else if (is_copyd && i < 2)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
if (value == KVX_GPR_REG_FP)
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_ADD_FP;
|
|
crt_peb_insn->immediate = 0;
|
|
}
|
|
else if (value == KVX_GPR_REG_SP)
|
|
{
|
|
crt_peb_insn->insn_type
|
|
= KVX_PROL_EPIL_INSN_RESTORE_SP_FROM_FP;
|
|
crt_peb_insn->immediate = 0;
|
|
}
|
|
else
|
|
is_copyd = 0;
|
|
}
|
|
else if (i == 1)
|
|
{
|
|
if (value == KVX_GPR_REG_SP
|
|
&& crt_peb_insn->insn_type
|
|
== KVX_PROL_EPIL_INSN_ADD_FP)
|
|
is_a_peb_insn = 1;
|
|
else if (value == KVX_GPR_REG_FP
|
|
&& crt_peb_insn->insn_type
|
|
== KVX_PROL_EPIL_INSN_RESTORE_SP_FROM_FP)
|
|
is_a_peb_insn = 1;
|
|
else
|
|
is_copyd = 0;
|
|
}
|
|
}
|
|
else
|
|
crt_peb_insn->gpr_reg[crt_peb_insn->nb_gprs++] = value;
|
|
}
|
|
else if (chk_type (kv3_v1, RegClass_kv3_v1_pairedReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_pairedReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_pairedReg))
|
|
crt_peb_insn->gpr_reg[crt_peb_insn->nb_gprs++] = value * 2;
|
|
else if (chk_type (kv3_v1, RegClass_kv3_v1_quadReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_quadReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_quadReg))
|
|
crt_peb_insn->gpr_reg[crt_peb_insn->nb_gprs++] = value * 4;
|
|
else if (chk_type (kv3_v1, RegClass_kv3_v1_systemReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_systemReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_systemReg)
|
|
|| chk_type (kv3_v1, RegClass_kv3_v1_aloneReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_aloneReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_aloneReg)
|
|
|| chk_type (kv3_v1, RegClass_kv3_v1_onlyraReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_onlyraReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_onlygetReg)
|
|
|| chk_type (kv3_v1, RegClass_kv3_v1_onlygetReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_onlygetReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_onlygetReg)
|
|
|| chk_type (kv3_v1, RegClass_kv3_v1_onlysetReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_onlysetReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_onlysetReg)
|
|
|| chk_type (kv3_v1, RegClass_kv3_v1_onlyfxReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_onlyfxReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_onlyfxReg))
|
|
{
|
|
if (env.kvx_regfiles[KVX_REGFILE_DEC_GPR] + value
|
|
>= env.kvx_max_dec_registers)
|
|
return -1;
|
|
if (is_get && !strcmp (env.kvx_registers[env.kvx_dec_registers[env.kvx_regfiles[KVX_REGFILE_DEC_SFR] + value]].name, "$ra"))
|
|
{
|
|
crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_GET_RA;
|
|
is_a_peb_insn = 1;
|
|
}
|
|
}
|
|
else if (chk_type (kv3_v1, RegClass_kv3_v1_coproReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_coproReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_coproReg)
|
|
|| chk_type (kv3_v1, RegClass_kv3_v1_blockReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_blockReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_blockReg)
|
|
|| chk_type (kv3_v1, RegClass_kv3_v1_vectorReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_vectorReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_vectorReg)
|
|
|| chk_type (kv3_v1, RegClass_kv3_v1_tileReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_tileReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_tileReg)
|
|
|| chk_type (kv3_v1, RegClass_kv3_v1_matrixReg)
|
|
|| chk_type (kv3_v2, RegClass_kv3_v2_matrixReg)
|
|
|| chk_type (kv4_v1, RegClass_kv4_v1_matrixReg)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_scalarcond)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_column)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_comparison)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_doscale)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_exunum)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_floatcomp)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_qindex)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_rectify)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_rounding)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_roundint)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_saturate)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_scalarcond)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_silent)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_simplecond)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_speculate)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_splat32)
|
|
|| chk_type (kv3_v1, Modifier_kv3_v1_variant)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_accesses)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_boolcas)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_cachelev)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_channel)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_coherency)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_comparison)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_conjugate)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_doscale)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_exunum)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_floatcomp)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_hindex)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_lsomask)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_lsumask)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_lsupack)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_qindex)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_rounding)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_scalarcond)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_shuffleV)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_shuffleX)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_silent)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_simplecond)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_speculate)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_splat32)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_transpose)
|
|
|| chk_type (kv3_v2, Modifier_kv3_v2_variant)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_accesses)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_boolcas)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_cachelev)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_channel)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_coherency)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_comparison)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_conjugate)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_doscale)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_exunum)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_floatcomp)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_hindex)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_lsomask)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_lsumask)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_lsupack)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_qindex)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_rounding)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_scalarcond)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_shuffleV)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_shuffleX)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_silent)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_simplecond)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_speculate)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_splat32)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_transpose)
|
|
|| chk_type (kv4_v1, Modifier_kv4_v1_variant))
|
|
{
|
|
/* Do nothing. */
|
|
}
|
|
else if (chk_type (kv3_v1, Immediate_kv3_v1_sysnumber)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_sysnumber)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_sysnumber)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_wrapped8)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_wrapped8)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_signed10)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_signed10)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_signed10)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_signed16)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_signed16)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_signed16)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_signed27)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_signed27)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_signed27)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_wrapped32)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_wrapped32)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_wrapped32)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_signed37)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_signed37)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_signed37)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_signed43)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_signed43)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_signed43)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_signed54)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_signed54)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_signed54)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_wrapped64)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_wrapped64)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_wrapped64)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_unsigned6)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_unsigned6)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_unsigned6))
|
|
crt_peb_insn->immediate = value;
|
|
else if (chk_type (kv3_v1, Immediate_kv3_v1_pcrel17)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_pcrel17)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_pcrel17)
|
|
|| chk_type (kv3_v1, Immediate_kv3_v1_pcrel27)
|
|
|| chk_type (kv3_v2, Immediate_kv3_v2_pcrel27)
|
|
|| chk_type (kv4_v1, Immediate_kv4_v1_pcrel27))
|
|
crt_peb_insn->immediate = value + memaddr;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
if (is_a_peb_insn)
|
|
peb->nb_insn++;
|
|
continue;
|
|
}
|
|
|
|
return nb_syl * 4;
|
|
#undef chk_type
|
|
}
|
|
|
|
void
|
|
print_kvx_disassembler_options (FILE * stream)
|
|
{
|
|
fprintf (stream, _("\n\
|
|
The following KVX specific disassembler options are supported for use\n\
|
|
with the -M switch (multiple options should be separated by commas):\n"));
|
|
|
|
fprintf (stream, _("\n\
|
|
pretty Print 32-bit words in natural order corresponding to \
|
|
re-ordered instruction.\n"));
|
|
|
|
fprintf (stream, _("\n\
|
|
compact-assembly Do not emit a new line between bundles of instructions.\
|
|
\n"));
|
|
|
|
fprintf (stream, _("\n\
|
|
no-compact-assembly Emit a new line between bundles of instructions.\n"));
|
|
|
|
fprintf (stream, _("\n"));
|
|
}
|