| /* 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")); |
| } |