| /* Fujitsu FRV opcode support, for GNU Binutils. -*- C -*- |
| |
| Copyright 2000, 2001, 2003, 2004, 2005, 2007, 2009 |
| Free Software Foundation, Inc. |
| |
| Contributed by Red Hat Inc; developed under contract from Fujitsu. |
| |
| This file is part of the GNU Binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| |
| /* This file is an addendum to frv.cpu. Heavy use of C code isn't |
| appropriate in .cpu files, so it resides here. This especially applies |
| to assembly/disassembly where parsing/printing can be quite involved. |
| Such things aren't really part of the specification of the cpu, per se, |
| so .cpu files provide the general framework and .opc files handle the |
| nitty-gritty details as necessary. |
| |
| Each section is delimited with start and end markers. |
| |
| <arch>-opc.h additions use: "-- opc.h" |
| <arch>-opc.c additions use: "-- opc.c" |
| <arch>-asm.c additions use: "-- asm.c" |
| <arch>-dis.c additions use: "-- dis.c" |
| <arch>-ibd.h additions use: "-- ibd.h". */ |
| |
| /* -- opc.h */ |
| |
| #undef CGEN_DIS_HASH_SIZE |
| #define CGEN_DIS_HASH_SIZE 128 |
| #undef CGEN_DIS_HASH |
| #define CGEN_DIS_HASH(buffer, value) (((value) >> 18) & 127) |
| |
| /* Allows reason codes to be output when assembler errors occur. */ |
| #define CGEN_VERBOSE_ASSEMBLER_ERRORS |
| |
| /* Vliw support. */ |
| #define FRV_VLIW_SIZE 8 /* fr550 has largest vliw size of 8. */ |
| #define PAD_VLIW_COMBO ,UNIT_NIL,UNIT_NIL,UNIT_NIL,UNIT_NIL |
| |
| typedef CGEN_ATTR_VALUE_ENUM_TYPE VLIW_COMBO[FRV_VLIW_SIZE]; |
| |
| typedef struct |
| { |
| int next_slot; |
| int constraint_violation; |
| unsigned long mach; |
| unsigned long elf_flags; |
| CGEN_ATTR_VALUE_ENUM_TYPE * unit_mapping; |
| VLIW_COMBO * current_vliw; |
| CGEN_ATTR_VALUE_ENUM_TYPE major[FRV_VLIW_SIZE]; |
| const CGEN_INSN * insn[FRV_VLIW_SIZE]; |
| } FRV_VLIW; |
| |
| bool frv_is_branch_major (CGEN_ATTR_VALUE_ENUM_TYPE, unsigned long); |
| bool frv_is_float_major (CGEN_ATTR_VALUE_ENUM_TYPE, unsigned long); |
| bool frv_is_media_major (CGEN_ATTR_VALUE_ENUM_TYPE, unsigned long); |
| bool frv_is_branch_insn (const CGEN_INSN *); |
| bool frv_is_float_insn (const CGEN_INSN *); |
| bool frv_is_media_insn (const CGEN_INSN *); |
| void frv_vliw_reset (FRV_VLIW *, unsigned long, unsigned long); |
| int frv_vliw_add_insn (FRV_VLIW *, const CGEN_INSN *); |
| bool spr_valid (long); |
| /* -- */ |
| |
| /* -- opc.c */ |
| #include "opintl.h" |
| #include "elf/frv.h" |
| #include <stdio.h> |
| |
| /* DEBUG appears below as argument of OP macro. */ |
| #undef DEBUG |
| |
| /* Returns TRUE if {MAJOR,MACH} is a major branch of the FRV |
| development tree. */ |
| |
| bool |
| frv_is_branch_major (CGEN_ATTR_VALUE_ENUM_TYPE major, unsigned long mach) |
| { |
| switch (mach) |
| { |
| case bfd_mach_fr400: |
| if (major >= FR400_MAJOR_B_1 && major <= FR400_MAJOR_B_6) |
| return true; |
| break; |
| case bfd_mach_fr450: |
| if (major >= FR450_MAJOR_B_1 && major <= FR450_MAJOR_B_6) |
| return true; |
| break; |
| default: |
| if (major >= FR500_MAJOR_B_1 && major <= FR500_MAJOR_B_6) |
| return true; |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* Returns TRUE if {MAJOR,MACH} supports floating point insns. */ |
| |
| bool |
| frv_is_float_major (CGEN_ATTR_VALUE_ENUM_TYPE major, unsigned long mach) |
| { |
| switch (mach) |
| { |
| case bfd_mach_fr400: |
| case bfd_mach_fr450: |
| return false; |
| default: |
| if (major >= FR500_MAJOR_F_1 && major <= FR500_MAJOR_F_8) |
| return true; |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* Returns TRUE if {MAJOR,MACH} supports media insns. */ |
| |
| bool |
| frv_is_media_major (CGEN_ATTR_VALUE_ENUM_TYPE major, unsigned long mach) |
| { |
| switch (mach) |
| { |
| case bfd_mach_fr400: |
| if (major >= FR400_MAJOR_M_1 && major <= FR400_MAJOR_M_2) |
| return true; |
| break; |
| case bfd_mach_fr450: |
| if (major >= FR450_MAJOR_M_1 && major <= FR450_MAJOR_M_6) |
| return true; |
| break; |
| default: |
| if (major >= FR500_MAJOR_M_1 && major <= FR500_MAJOR_M_8) |
| return true; |
| break; |
| } |
| |
| return false; |
| } |
| |
| bool |
| frv_is_branch_insn (const CGEN_INSN *insn) |
| { |
| if (frv_is_branch_major (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR400_MAJOR), |
| bfd_mach_fr400)) |
| return true; |
| if (frv_is_branch_major (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR450_MAJOR), |
| bfd_mach_fr450)) |
| return true; |
| if (frv_is_branch_major (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR500_MAJOR), |
| bfd_mach_fr500)) |
| return true; |
| |
| return false; |
| } |
| |
| bool |
| frv_is_float_insn (const CGEN_INSN *insn) |
| { |
| if (frv_is_float_major (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR400_MAJOR), |
| bfd_mach_fr400)) |
| return true; |
| if (frv_is_float_major (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR450_MAJOR), |
| bfd_mach_fr450)) |
| return true; |
| if (frv_is_float_major (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR500_MAJOR), |
| bfd_mach_fr500)) |
| return true; |
| |
| return false; |
| } |
| |
| bool |
| frv_is_media_insn (const CGEN_INSN *insn) |
| { |
| if (frv_is_media_major (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR400_MAJOR), |
| bfd_mach_fr400)) |
| return true; |
| if (frv_is_media_major (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR450_MAJOR), |
| bfd_mach_fr450)) |
| return true; |
| if (frv_is_media_major (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR500_MAJOR), |
| bfd_mach_fr500)) |
| return true; |
| |
| return false; |
| } |
| |
| /* This table represents the allowable packing for vliw insns for the fr400. |
| The fr400 has only 2 vliw slots. Represent this by not allowing any insns |
| in the extra slots. |
| Subsets of any given row are also allowed. */ |
| static VLIW_COMBO fr400_allowed_vliw[] = |
| { |
| /* slot0 slot1 slot2 slot3 */ |
| { UNIT_I0, UNIT_I1, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_I0, UNIT_FM0, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_I0, UNIT_B0, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_FM0, UNIT_FM1, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_FM0, UNIT_B0, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_B0, UNIT_NIL, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_C, UNIT_NIL, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO } |
| }; |
| |
| /* This table represents the allowable packing for vliw insns for the fr500. |
| The fr500 has only 4 vliw slots. Represent this by not allowing any insns |
| in the extra slots. |
| Subsets of any given row are also allowed. */ |
| static VLIW_COMBO fr500_allowed_vliw[] = |
| { |
| /* slot0 slot1 slot2 slot3 */ |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1 PAD_VLIW_COMBO }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_B0 PAD_VLIW_COMBO }, |
| { UNIT_I0, UNIT_FM0, UNIT_FM1, UNIT_B0 PAD_VLIW_COMBO }, |
| { UNIT_I0, UNIT_FM0, UNIT_B0, UNIT_B1 PAD_VLIW_COMBO }, |
| { UNIT_I0, UNIT_I1, UNIT_B0, UNIT_B1 PAD_VLIW_COMBO }, |
| { UNIT_I0, UNIT_B0, UNIT_B1, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_FM0, UNIT_FM1, UNIT_B0, UNIT_B1 PAD_VLIW_COMBO }, |
| { UNIT_FM0, UNIT_B0, UNIT_B1, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_C, UNIT_NIL, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO }, |
| { UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL PAD_VLIW_COMBO } |
| }; |
| |
| /* This table represents the allowable packing for vliw insns for the fr550. |
| Subsets of any given row are also allowed. */ |
| static VLIW_COMBO fr550_allowed_vliw[] = |
| { |
| /* slot0 slot1 slot2 slot3 slot4 slot5 slot6 slot7 */ |
| { UNIT_I0, UNIT_I1, UNIT_I2, UNIT_I3, UNIT_B0, UNIT_B1 , UNIT_NIL, UNIT_NIL }, |
| { UNIT_I0, UNIT_I1, UNIT_I2, UNIT_B0, UNIT_B1 , UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_I0, UNIT_I1, UNIT_B0, UNIT_B1 , UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_I0, UNIT_B0, UNIT_B1 , UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_I2, UNIT_FM2, UNIT_I3, UNIT_FM3 }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_I2, UNIT_FM2, UNIT_I3, UNIT_B0 }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_I2, UNIT_FM2, UNIT_FM3, UNIT_B0 }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_I2, UNIT_FM2, UNIT_B0, UNIT_B1 }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_I2, UNIT_I3, UNIT_B0, UNIT_B1 }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_I2, UNIT_B0, UNIT_B1, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_FM2, UNIT_FM3, UNIT_B0, UNIT_B1 }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_FM2, UNIT_FM3, UNIT_B0, UNIT_B1 }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_FM2, UNIT_B0, UNIT_B1, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_FM1, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_I2, UNIT_I3, UNIT_B0, UNIT_B1, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_I2, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_I1, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_FM1, UNIT_FM2, UNIT_FM3, UNIT_B0, UNIT_B1, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_FM1, UNIT_FM2, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_FM1, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_I0, UNIT_FM0, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_C, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_FM0, UNIT_FM1, UNIT_FM2, UNIT_FM3, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL }, |
| { UNIT_FM0, UNIT_FM1, UNIT_FM2, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_FM0, UNIT_FM1, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_FM0, UNIT_B0, UNIT_B1, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL }, |
| { UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL, UNIT_NIL } |
| }; |
| |
| /* Some insns are assigned specialized implementation units which map to |
| different actual implementation units on different machines. These |
| tables perform that mapping. */ |
| static CGEN_ATTR_VALUE_ENUM_TYPE fr400_unit_mapping[] = |
| { |
| /* unit in insn actual unit */ |
| /* NIL */ UNIT_NIL, |
| /* I0 */ UNIT_I0, |
| /* I1 */ UNIT_I1, |
| /* I01 */ UNIT_I01, |
| /* I2 */ UNIT_NIL, /* no I2 or I3 unit */ |
| /* I3 */ UNIT_NIL, |
| /* IALL */ UNIT_I01, /* only I0 and I1 units */ |
| /* FM0 */ UNIT_FM0, |
| /* FM1 */ UNIT_FM1, |
| /* FM01 */ UNIT_FM01, |
| /* FM2 */ UNIT_NIL, /* no F2 or M2 units */ |
| /* FM3 */ UNIT_NIL, /* no F3 or M3 units */ |
| /* FMALL */ UNIT_FM01,/* Only F0,F1,M0,M1 units */ |
| /* FMLOW */ UNIT_FM0, /* Only F0,M0 units */ |
| /* B0 */ UNIT_B0, /* branches only in B0 unit. */ |
| /* B1 */ UNIT_B0, |
| /* B01 */ UNIT_B0, |
| /* C */ UNIT_C, |
| /* MULT-DIV */ UNIT_I0, /* multiply and divide only in I0 unit. */ |
| /* IACC */ UNIT_I01, /* iacc multiply in I0 or I1 unit. */ |
| /* LOAD */ UNIT_I0, /* load only in I0 unit. */ |
| /* STORE */ UNIT_I0, /* store only in I0 unit. */ |
| /* SCAN */ UNIT_I0, /* scan only in I0 unit. */ |
| /* DCPL */ UNIT_C, /* dcpl only in C unit. */ |
| /* MDUALACC */ UNIT_FM0, /* media dual acc insn only in FM0 unit. */ |
| /* MDCUTSSI */ UNIT_FM0, /* mdcutssi only in FM0 unit. */ |
| /* MCLRACC-1*/ UNIT_FM0 /* mclracc,A==1 insn only in FM0 unit. */ |
| }; |
| |
| /* Some insns are assigned specialized implementation units which map to |
| different actual implementation units on different machines. These |
| tables perform that mapping. */ |
| static CGEN_ATTR_VALUE_ENUM_TYPE fr450_unit_mapping[] = |
| { |
| /* unit in insn actual unit */ |
| /* NIL */ UNIT_NIL, |
| /* I0 */ UNIT_I0, |
| /* I1 */ UNIT_I1, |
| /* I01 */ UNIT_I01, |
| /* I2 */ UNIT_NIL, /* no I2 or I3 unit */ |
| /* I3 */ UNIT_NIL, |
| /* IALL */ UNIT_I01, /* only I0 and I1 units */ |
| /* FM0 */ UNIT_FM0, |
| /* FM1 */ UNIT_FM1, |
| /* FM01 */ UNIT_FM01, |
| /* FM2 */ UNIT_NIL, /* no F2 or M2 units */ |
| /* FM3 */ UNIT_NIL, /* no F3 or M3 units */ |
| /* FMALL */ UNIT_FM01,/* Only F0,F1,M0,M1 units */ |
| /* FMLOW */ UNIT_FM0, /* Only F0,M0 units */ |
| /* B0 */ UNIT_B0, /* branches only in B0 unit. */ |
| /* B1 */ UNIT_B0, |
| /* B01 */ UNIT_B0, |
| /* C */ UNIT_C, |
| /* MULT-DIV */ UNIT_I0, /* multiply and divide only in I0 unit. */ |
| /* IACC */ UNIT_I01, /* iacc multiply in I0 or I1 unit. */ |
| /* LOAD */ UNIT_I0, /* load only in I0 unit. */ |
| /* STORE */ UNIT_I0, /* store only in I0 unit. */ |
| /* SCAN */ UNIT_I0, /* scan only in I0 unit. */ |
| /* DCPL */ UNIT_I0, /* dcpl only in I0 unit. */ |
| /* MDUALACC */ UNIT_FM0, /* media dual acc insn only in FM0 unit. */ |
| /* MDCUTSSI */ UNIT_FM01, /* mdcutssi in FM0 or FM1. */ |
| /* MCLRACC-1*/ UNIT_FM0 /* mclracc,A==1 insn only in FM0 unit. */ |
| }; |
| |
| static CGEN_ATTR_VALUE_ENUM_TYPE fr500_unit_mapping[] = |
| { |
| /* unit in insn actual unit */ |
| /* NIL */ UNIT_NIL, |
| /* I0 */ UNIT_I0, |
| /* I1 */ UNIT_I1, |
| /* I01 */ UNIT_I01, |
| /* I2 */ UNIT_NIL, /* no I2 or I3 unit */ |
| /* I3 */ UNIT_NIL, |
| /* IALL */ UNIT_I01, /* only I0 and I1 units */ |
| /* FM0 */ UNIT_FM0, |
| /* FM1 */ UNIT_FM1, |
| /* FM01 */ UNIT_FM01, |
| /* FM2 */ UNIT_NIL, /* no F2 or M2 units */ |
| /* FM3 */ UNIT_NIL, /* no F3 or M2 units */ |
| /* FMALL */ UNIT_FM01,/* Only F0,F1,M0,M1 units */ |
| /* FMLOW */ UNIT_FM0, /* Only F0,M0 units */ |
| /* B0 */ UNIT_B0, |
| /* B1 */ UNIT_B1, |
| /* B01 */ UNIT_B01, |
| /* C */ UNIT_C, |
| /* MULT-DIV */ UNIT_I01, /* multiply and divide in I0 or I1 unit. */ |
| /* IACC */ UNIT_NIL, /* iacc multiply not implemented */ |
| /* LOAD */ UNIT_I01, /* load in I0 or I1 unit. */ |
| /* STORE */ UNIT_I0, /* store only in I0 unit. */ |
| /* SCAN */ UNIT_I01, /* scan in I0 or I1 unit. */ |
| /* DCPL */ UNIT_C, /* dcpl only in C unit. */ |
| /* MDUALACC */ UNIT_FM0, /* media dual acc insn only in FM0 unit. */ |
| /* MDCUTSSI */ UNIT_FM0, /* mdcutssi only in FM0 unit. */ |
| /* MCLRACC-1*/ UNIT_FM01 /* mclracc,A==1 in FM0 or FM1 unit. */ |
| }; |
| |
| static CGEN_ATTR_VALUE_ENUM_TYPE fr550_unit_mapping[] = |
| { |
| /* unit in insn actual unit */ |
| /* NIL */ UNIT_NIL, |
| /* I0 */ UNIT_I0, |
| /* I1 */ UNIT_I1, |
| /* I01 */ UNIT_I01, |
| /* I2 */ UNIT_I2, |
| /* I3 */ UNIT_I3, |
| /* IALL */ UNIT_IALL, |
| /* FM0 */ UNIT_FM0, |
| /* FM1 */ UNIT_FM1, |
| /* FM01 */ UNIT_FM01, |
| /* FM2 */ UNIT_FM2, |
| /* FM3 */ UNIT_FM3, |
| /* FMALL */ UNIT_FMALL, |
| /* FMLOW */ UNIT_FM01, /* Only F0,F1,M0,M1 units */ |
| /* B0 */ UNIT_B0, |
| /* B1 */ UNIT_B1, |
| /* B01 */ UNIT_B01, |
| /* C */ UNIT_C, |
| /* MULT-DIV */ UNIT_I01, /* multiply and divide in I0 or I1 unit. */ |
| /* IACC */ UNIT_NIL, /* iacc multiply not implemented. */ |
| /* LOAD */ UNIT_I01, /* load in I0 or I1 unit. */ |
| /* STORE */ UNIT_I01, /* store in I0 or I1 unit. */ |
| /* SCAN */ UNIT_IALL, /* scan in any integer unit. */ |
| /* DCPL */ UNIT_I0, /* dcpl only in I0 unit. */ |
| /* MDUALACC */ UNIT_FMALL,/* media dual acc insn in all media units */ |
| /* MDCUTSSI */ UNIT_FM01, /* mdcutssi in FM0 or FM1 unit. */ |
| /* MCLRACC-1*/ UNIT_FM01 /* mclracc,A==1 in FM0 or FM1 unit. */ |
| }; |
| |
| void |
| frv_vliw_reset (FRV_VLIW *vliw, unsigned long mach, unsigned long elf_flags) |
| { |
| vliw->next_slot = 0; |
| vliw->constraint_violation = 0; |
| vliw->mach = mach; |
| vliw->elf_flags = elf_flags; |
| |
| switch (mach) |
| { |
| case bfd_mach_fr400: |
| vliw->current_vliw = fr400_allowed_vliw; |
| vliw->unit_mapping = fr400_unit_mapping; |
| break; |
| case bfd_mach_fr450: |
| vliw->current_vliw = fr400_allowed_vliw; |
| vliw->unit_mapping = fr450_unit_mapping; |
| break; |
| case bfd_mach_fr550: |
| vliw->current_vliw = fr550_allowed_vliw; |
| vliw->unit_mapping = fr550_unit_mapping; |
| break; |
| default: |
| vliw->current_vliw = fr500_allowed_vliw; |
| vliw->unit_mapping = fr500_unit_mapping; |
| break; |
| } |
| } |
| |
| /* Return TRUE if unit1 is a match for unit2. |
| Unit1 comes from the insn's UNIT attribute. unit2 comes from one of the |
| *_allowed_vliw tables above. */ |
| static bool |
| match_unit (FRV_VLIW *vliw, |
| CGEN_ATTR_VALUE_ENUM_TYPE unit1, CGEN_ATTR_VALUE_ENUM_TYPE unit2) |
| { |
| /* Map any specialized implementation units to actual ones. */ |
| unit1 = vliw->unit_mapping[unit1]; |
| |
| if (unit1 == unit2) |
| return true; |
| if (unit1 < unit2) |
| return false; |
| |
| switch (unit1) |
| { |
| case UNIT_I01: |
| case UNIT_FM01: |
| case UNIT_B01: |
| /* The 01 versions of these units are within 2 enums of the 0 or 1 |
| versions. */ |
| if (unit1 - unit2 <= 2) |
| return true; |
| break; |
| case UNIT_IALL: |
| case UNIT_FMALL: |
| /* The ALL versions of these units are within 5 enums of the 0, 1, 2 or 3 |
| versions. */ |
| if (unit1 - unit2 <= 5) |
| return true; |
| break; |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* Return TRUE if the vliws match, FALSE otherwise. */ |
| |
| static bool |
| match_vliw (VLIW_COMBO *vliw1, VLIW_COMBO *vliw2, int vliw_size) |
| { |
| int i; |
| |
| for (i = 0; i < vliw_size; ++i) |
| if ((*vliw1)[i] != (*vliw2)[i]) |
| return false; |
| |
| return true; |
| } |
| |
| /* Find the next vliw vliw in the table that can accomodate the new insn. |
| If one is found then return it. Otherwise return NULL. */ |
| |
| static VLIW_COMBO * |
| add_next_to_vliw (FRV_VLIW *vliw, CGEN_ATTR_VALUE_ENUM_TYPE unit) |
| { |
| int next = vliw->next_slot; |
| VLIW_COMBO *current = vliw->current_vliw; |
| VLIW_COMBO *potential; |
| |
| if (next <= 0) |
| { |
| /* xgettext:c-format */ |
| opcodes_error_handler (_("internal error: bad vliw->next_slot value")); |
| abort (); |
| } |
| |
| /* The table is sorted by units allowed within slots, so vliws with |
| identical starting sequences are together. */ |
| potential = current; |
| do |
| { |
| if (match_unit (vliw, unit, (*potential)[next])) |
| return potential; |
| ++potential; |
| } |
| while (match_vliw (potential, current, next)); |
| |
| return NULL; |
| } |
| |
| /* Look for the given major insn type in the given vliw. |
| Returns TRUE if found, FALSE otherwise. */ |
| |
| static bool |
| find_major_in_vliw (FRV_VLIW *vliw, CGEN_ATTR_VALUE_ENUM_TYPE major) |
| { |
| int i; |
| |
| for (i = 0; i < vliw->next_slot; ++i) |
| if (vliw->major[i] == major) |
| return true; |
| |
| return false; |
| } |
| |
| /* Check for constraints between the insns in the vliw due to major insn |
| types. */ |
| |
| static bool |
| fr400_check_insn_major_constraints (FRV_VLIW *vliw, CGEN_ATTR_VALUE_ENUM_TYPE major) |
| { |
| /* In the cpu file, all media insns are represented as being allowed in |
| both media units. This makes it easier since this is the case for fr500. |
| Catch the invalid combinations here. Insns of major class FR400_MAJOR_M_2 |
| cannot coexist with any other media insn in a vliw. */ |
| switch (major) |
| { |
| case FR400_MAJOR_M_2: |
| return ! find_major_in_vliw (vliw, FR400_MAJOR_M_1) |
| && ! find_major_in_vliw (vliw, FR400_MAJOR_M_2); |
| case FR400_MAJOR_M_1: |
| return ! find_major_in_vliw (vliw, FR400_MAJOR_M_2); |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| static bool |
| fr450_check_insn_major_constraints (FRV_VLIW *vliw, CGEN_ATTR_VALUE_ENUM_TYPE major) |
| { |
| CGEN_ATTR_VALUE_ENUM_TYPE other_major; |
| |
| /* Our caller guarantees there's at least one other instruction. */ |
| other_major = CGEN_INSN_ATTR_VALUE (vliw->insn[0], CGEN_INSN_FR450_MAJOR); |
| |
| /* (M4, M5) and (M4, M6) are allowed. */ |
| if (other_major == FR450_MAJOR_M_4) |
| if (major == FR450_MAJOR_M_5 || major == FR450_MAJOR_M_6) |
| return true; |
| |
| /* Otherwise, instructions in even-numbered media categories cannot be |
| executed in parallel with other media instructions. */ |
| switch (major) |
| { |
| case FR450_MAJOR_M_2: |
| case FR450_MAJOR_M_4: |
| case FR450_MAJOR_M_6: |
| return !(other_major >= FR450_MAJOR_M_1 |
| && other_major <= FR450_MAJOR_M_6); |
| |
| case FR450_MAJOR_M_1: |
| case FR450_MAJOR_M_3: |
| case FR450_MAJOR_M_5: |
| return !(other_major == FR450_MAJOR_M_2 |
| || other_major == FR450_MAJOR_M_4 |
| || other_major == FR450_MAJOR_M_6); |
| |
| default: |
| return true; |
| } |
| } |
| |
| static bool |
| find_unit_in_vliw (FRV_VLIW *vliw, CGEN_ATTR_VALUE_ENUM_TYPE unit) |
| { |
| int i; |
| |
| for (i = 0; i < vliw->next_slot; ++i) |
| if (CGEN_INSN_ATTR_VALUE (vliw->insn[i], CGEN_INSN_UNIT) == unit) |
| return true; |
| |
| return false; /* Not found. */ |
| } |
| |
| static bool |
| find_major_in_slot (FRV_VLIW *vliw, |
| CGEN_ATTR_VALUE_ENUM_TYPE major, |
| CGEN_ATTR_VALUE_ENUM_TYPE slot) |
| { |
| int i; |
| |
| for (i = 0; i < vliw->next_slot; ++i) |
| if (vliw->major[i] == major && (*vliw->current_vliw)[i] == slot) |
| return true; |
| |
| return false; |
| } |
| |
| static bool |
| fr550_find_media_in_vliw (FRV_VLIW *vliw) |
| { |
| int i; |
| |
| for (i = 0; i < vliw->next_slot; ++i) |
| { |
| if (vliw->major[i] < FR550_MAJOR_M_1 || vliw->major[i] > FR550_MAJOR_M_5) |
| continue; |
| |
| /* Found a media insn, however, MNOP and MCLRACC don't count. */ |
| if (CGEN_INSN_NUM (vliw->insn[i]) == FRV_INSN_MNOP |
| || CGEN_INSN_NUM (vliw->insn[i]) == FRV_INSN_MCLRACC_0 |
| || CGEN_INSN_NUM (vliw->insn[i]) == FRV_INSN_MCLRACC_1) |
| continue; |
| |
| return true; /* Found one. */ |
| } |
| |
| return false; |
| } |
| |
| static bool |
| fr550_find_float_in_vliw (FRV_VLIW *vliw) |
| { |
| int i; |
| |
| for (i = 0; i < vliw->next_slot; ++i) |
| { |
| if (vliw->major[i] < FR550_MAJOR_F_1 || vliw->major[i] > FR550_MAJOR_F_4) |
| continue; |
| |
| /* Found a floating point insn, however, FNOP doesn't count. */ |
| if (CGEN_INSN_NUM (vliw->insn[i]) == FRV_INSN_FNOP) |
| continue; |
| |
| return true; /* Found one. */ |
| } |
| |
| return false; |
| } |
| |
| static bool |
| fr550_check_insn_major_constraints (FRV_VLIW *vliw, |
| CGEN_ATTR_VALUE_ENUM_TYPE major, |
| const CGEN_INSN *insn) |
| { |
| CGEN_ATTR_VALUE_ENUM_TYPE unit; |
| CGEN_ATTR_VALUE_ENUM_TYPE slot = (*vliw->current_vliw)[vliw->next_slot]; |
| switch (slot) |
| { |
| case UNIT_I2: |
| /* If it's a store, then there must be another store in I1 */ |
| unit = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_UNIT); |
| if (unit == UNIT_STORE) |
| return find_unit_in_vliw (vliw, UNIT_STORE); |
| break; |
| case UNIT_FM2: |
| case UNIT_FM3: |
| /* Floating point insns other than FNOP in slot f2 or f3 cannot coexist |
| with media insns. */ |
| if (major >= FR550_MAJOR_F_1 && major <= FR550_MAJOR_F_4 |
| && CGEN_INSN_NUM (insn) != FRV_INSN_FNOP) |
| return ! fr550_find_media_in_vliw (vliw); |
| /* Media insns other than MNOP in slot m2 or m3 cannot coexist with |
| floating point insns. */ |
| if (major >= FR550_MAJOR_M_1 && major <= FR550_MAJOR_M_5 |
| && CGEN_INSN_NUM (insn) != FRV_INSN_MNOP) |
| return ! fr550_find_float_in_vliw (vliw); |
| /* F-2 in slot f2 or f3 cannot coexist with F-2 or F-4 in slot f1 or f2 |
| respectively. */ |
| if (major == FR550_MAJOR_F_2) |
| return ! find_major_in_slot (vliw, FR550_MAJOR_F_2, |
| slot - (UNIT_FM2 - UNIT_FM0)) |
| && ! find_major_in_slot (vliw, FR550_MAJOR_F_4, |
| slot - (UNIT_FM2 - UNIT_FM0)); |
| /* M-2 or M-5 in slot m2 or m3 cannot coexist with M-2 in slot m1 or m2 |
| respectively. */ |
| if (major == FR550_MAJOR_M_2 || major == FR550_MAJOR_M_5) |
| return ! find_major_in_slot (vliw, FR550_MAJOR_M_2, |
| slot - (UNIT_FM2 - UNIT_FM0)); |
| /* M-4 in slot m2 or m3 cannot coexist with M-4 in slot m1 or m2 |
| respectively. */ |
| if (major == FR550_MAJOR_M_4) |
| return ! find_major_in_slot (vliw, FR550_MAJOR_M_4, |
| slot - (UNIT_FM2 - UNIT_FM0)); |
| break; |
| default: |
| break; |
| } |
| return true; /* All OK. */ |
| } |
| |
| static bool |
| fr500_check_insn_major_constraints (FRV_VLIW *vliw, CGEN_ATTR_VALUE_ENUM_TYPE major) |
| { |
| /* TODO: A table might be faster for some of the more complex instances |
| here. */ |
| switch (major) |
| { |
| case FR500_MAJOR_I_1: |
| case FR500_MAJOR_I_4: |
| case FR500_MAJOR_I_5: |
| case FR500_MAJOR_I_6: |
| case FR500_MAJOR_B_1: |
| case FR500_MAJOR_B_2: |
| case FR500_MAJOR_B_3: |
| case FR500_MAJOR_B_4: |
| case FR500_MAJOR_B_5: |
| case FR500_MAJOR_B_6: |
| case FR500_MAJOR_F_4: |
| case FR500_MAJOR_F_8: |
| case FR500_MAJOR_M_8: |
| return true; /* OK */ |
| case FR500_MAJOR_I_2: |
| /* Cannot coexist with I-3 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_I_3); |
| case FR500_MAJOR_I_3: |
| /* Cannot coexist with I-2 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_I_2); |
| case FR500_MAJOR_F_1: |
| case FR500_MAJOR_F_2: |
| /* Cannot coexist with F-5, F-6, or M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_F_5) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_6) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_7); |
| case FR500_MAJOR_F_3: |
| /* Cannot coexist with F-7, or M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_F_7) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_7); |
| case FR500_MAJOR_F_5: |
| /* Cannot coexist with F-1, F-2, F-6, F-7, or M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_F_1) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_2) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_6) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_7) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_7); |
| case FR500_MAJOR_F_6: |
| /* Cannot coexist with F-1, F-2, F-5, F-6, or M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_F_1) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_2) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_5) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_6) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_7); |
| case FR500_MAJOR_F_7: |
| /* Cannot coexist with F-3, F-5, F-7, or M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_F_3) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_5) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_7) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_7); |
| case FR500_MAJOR_M_1: |
| /* Cannot coexist with M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_M_7); |
| case FR500_MAJOR_M_2: |
| case FR500_MAJOR_M_3: |
| /* Cannot coexist with M-5, M-6 or M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_M_5) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_6) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_7); |
| case FR500_MAJOR_M_4: |
| /* Cannot coexist with M-6 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_M_6); |
| case FR500_MAJOR_M_5: |
| /* Cannot coexist with M-2, M-3, M-5, M-6 or M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_M_2) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_3) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_5) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_6) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_7); |
| case FR500_MAJOR_M_6: |
| /* Cannot coexist with M-2, M-3, M-4, M-5, M-6 or M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_M_2) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_3) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_4) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_5) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_6) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_7); |
| case FR500_MAJOR_M_7: |
| /* Cannot coexist with M-1, M-2, M-3, M-5, M-6 or M-7 insn. */ |
| return ! find_major_in_vliw (vliw, FR500_MAJOR_M_1) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_2) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_3) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_5) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_6) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_M_7) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_1) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_2) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_3) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_5) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_6) |
| && ! find_major_in_vliw (vliw, FR500_MAJOR_F_7); |
| default: |
| /* xgettext:c-format */ |
| opcodes_error_handler (_("internal error: bad major code")); |
| abort (); |
| break; |
| } |
| return true; |
| } |
| |
| static bool |
| check_insn_major_constraints (FRV_VLIW *vliw, |
| CGEN_ATTR_VALUE_ENUM_TYPE major, |
| const CGEN_INSN *insn) |
| { |
| switch (vliw->mach) |
| { |
| case bfd_mach_fr400: |
| return fr400_check_insn_major_constraints (vliw, major); |
| |
| case bfd_mach_fr450: |
| return fr450_check_insn_major_constraints (vliw, major); |
| |
| case bfd_mach_fr550: |
| return fr550_check_insn_major_constraints (vliw, major, insn); |
| |
| default: |
| return fr500_check_insn_major_constraints (vliw, major); |
| } |
| } |
| |
| /* Add in insn to the VLIW vliw if possible. |
| Return 0 if successful, non-zero otherwise. */ |
| |
| int |
| frv_vliw_add_insn (FRV_VLIW *vliw, const CGEN_INSN *insn) |
| { |
| int slot_index; |
| CGEN_ATTR_VALUE_ENUM_TYPE major; |
| CGEN_ATTR_VALUE_ENUM_TYPE unit; |
| VLIW_COMBO *new_vliw; |
| |
| if (vliw->constraint_violation || CGEN_INSN_INVALID_P (insn)) |
| return 1; |
| |
| slot_index = vliw->next_slot; |
| if (slot_index >= FRV_VLIW_SIZE) |
| return 1; |
| |
| unit = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_UNIT); |
| if (unit == UNIT_NIL) |
| { |
| /* xgettext:c-format */ |
| opcodes_error_handler (_("internal error: bad insn unit")); |
| abort (); |
| } |
| |
| switch (vliw->mach) |
| { |
| case bfd_mach_fr400: |
| major = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR400_MAJOR); |
| break; |
| case bfd_mach_fr450: |
| major = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR450_MAJOR); |
| break; |
| case bfd_mach_fr550: |
| major = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR550_MAJOR); |
| break; |
| default: |
| major = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_FR500_MAJOR); |
| break; |
| } |
| |
| if (slot_index <= 0) |
| { |
| /* Any insn can be added to slot 0. */ |
| while (! match_unit (vliw, unit, (*vliw->current_vliw)[0])) |
| ++vliw->current_vliw; |
| vliw->major[0] = major; |
| vliw->insn[0] = insn; |
| vliw->next_slot = 1; |
| return 0; |
| } |
| |
| /* If there are already insns in the vliw(s) check to see that |
| this one can be added. Do this by finding an allowable vliw |
| combination that can accept the new insn. */ |
| if (! (vliw->elf_flags & EF_FRV_NOPACK)) |
| { |
| new_vliw = add_next_to_vliw (vliw, unit); |
| if (new_vliw && check_insn_major_constraints (vliw, major, insn)) |
| { |
| vliw->current_vliw = new_vliw; |
| vliw->major[slot_index] = major; |
| vliw->insn[slot_index] = insn; |
| vliw->next_slot++; |
| return 0; |
| } |
| |
| /* The frv machine supports all packing conbinations. If we fail, |
| to add the insn, then it could not be handled as if it was the fr500. |
| Just return as if it was handled ok. */ |
| if (vliw->mach == bfd_mach_frv) |
| return 0; |
| } |
| |
| vliw->constraint_violation = 1; |
| return 1; |
| } |
| |
| bool |
| spr_valid (long regno) |
| { |
| if (regno < 0) return false; |
| if (regno <= 4095) return true; |
| return false; |
| } |
| /* -- */ |
| |
| /* -- asm.c */ |
| inline static const char * |
| parse_symbolic_address (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| int opinfo, |
| enum cgen_parse_operand_result *resultp, |
| bfd_vma *valuep) |
| { |
| enum cgen_parse_operand_result result_type; |
| const char *errmsg = (* cd->parse_operand_fn) |
| (cd, CGEN_PARSE_OPERAND_SYMBOLIC, strp, opindex, opinfo, |
| &result_type, valuep); |
| |
| if (errmsg == NULL |
| && result_type != CGEN_PARSE_OPERAND_RESULT_QUEUED) |
| return "symbolic expression required"; |
| |
| if (resultp) |
| *resultp = result_type; |
| |
| return errmsg; |
| } |
| |
| static const char * |
| parse_ldd_annotation (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| unsigned long *valuep) |
| { |
| const char *errmsg; |
| enum cgen_parse_operand_result result_type; |
| bfd_vma value; |
| |
| if (**strp == '#' || **strp == '%') |
| { |
| if (strncasecmp (*strp + 1, "tlsdesc(", 8) == 0) |
| { |
| *strp += 9; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_TLSDESC_RELAX, |
| &result_type, &value); |
| if (**strp != ')') |
| return "missing ')'"; |
| if (valuep) |
| *valuep = value; |
| ++*strp; |
| if (errmsg) |
| return errmsg; |
| } |
| } |
| |
| while (**strp == ' ' || **strp == '\t') |
| ++*strp; |
| |
| if (**strp != '@') |
| return "missing `@'"; |
| |
| ++*strp; |
| |
| return NULL; |
| } |
| |
| static const char * |
| parse_call_annotation (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| unsigned long *valuep) |
| { |
| const char *errmsg; |
| enum cgen_parse_operand_result result_type; |
| bfd_vma value; |
| |
| if (**strp == '#' || **strp == '%') |
| { |
| if (strncasecmp (*strp + 1, "gettlsoff(", 10) == 0) |
| { |
| *strp += 11; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GETTLSOFF_RELAX, |
| &result_type, &value); |
| if (**strp != ')') |
| return "missing ')'"; |
| if (valuep) |
| *valuep = value; |
| ++*strp; |
| if (errmsg) |
| return errmsg; |
| } |
| } |
| |
| while (**strp == ' ' || **strp == '\t') |
| ++*strp; |
| |
| if (**strp != '@') |
| return "missing `@'"; |
| |
| ++*strp; |
| |
| return NULL; |
| } |
| |
| static const char * |
| parse_ld_annotation (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| unsigned long *valuep) |
| { |
| const char *errmsg; |
| enum cgen_parse_operand_result result_type; |
| bfd_vma value; |
| |
| if (**strp == '#' || **strp == '%') |
| { |
| if (strncasecmp (*strp + 1, "tlsoff(", 7) == 0) |
| { |
| *strp += 8; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_TLSOFF_RELAX, |
| &result_type, &value); |
| if (**strp != ')') |
| return "missing ')'"; |
| if (valuep) |
| *valuep = value; |
| ++*strp; |
| if (errmsg) |
| return errmsg; |
| } |
| } |
| |
| while (**strp == ' ' || **strp == '\t') |
| ++*strp; |
| |
| if (**strp != '@') |
| return "missing `@'"; |
| |
| ++*strp; |
| |
| return NULL; |
| } |
| |
| static const char * |
| parse_ulo16 (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| unsigned long *valuep) |
| { |
| const char *errmsg; |
| enum cgen_parse_operand_result result_type; |
| bfd_vma value; |
| |
| if (**strp == '#' || **strp == '%') |
| { |
| if (strncasecmp (*strp + 1, "lo(", 3) == 0) |
| { |
| *strp += 4; |
| errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_FRV_LO16, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing `)'"; |
| ++*strp; |
| if (errmsg == NULL |
| && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) |
| value &= 0xffff; |
| *valuep = value; |
| return errmsg; |
| } |
| if (strncasecmp (*strp + 1, "gprello(", 8) == 0) |
| { |
| *strp += 9; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GPRELLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotlo(", 6) == 0) |
| { |
| *strp += 7; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotfuncdesclo(", 14) == 0) |
| { |
| *strp += 15; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOTLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotofflo(", 9) == 0) |
| { |
| *strp += 10; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTOFFLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotofffuncdesclo(", 17) == 0) |
| { |
| *strp += 18; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOTOFFLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsdesclo(", 13) == 0) |
| { |
| *strp += 14; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSDESCLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "tlsmofflo(", 10) == 0) |
| { |
| *strp += 11; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_TLSMOFFLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsofflo(", 12) == 0) |
| { |
| *strp += 13; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSOFFLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| } |
| return cgen_parse_unsigned_integer (cd, strp, opindex, valuep); |
| } |
| |
| static const char * |
| parse_uslo16 (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| signed long *valuep) |
| { |
| const char *errmsg; |
| enum cgen_parse_operand_result result_type; |
| bfd_vma value; |
| |
| if (**strp == '#' || **strp == '%') |
| { |
| if (strncasecmp (*strp + 1, "lo(", 3) == 0) |
| { |
| *strp += 4; |
| errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_FRV_LO16, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing `)'"; |
| ++*strp; |
| if (errmsg == NULL |
| && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) |
| value &= 0xffff; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gprello(", 8) == 0) |
| { |
| *strp += 9; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GPRELLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotlo(", 6) == 0) |
| { |
| *strp += 7; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotfuncdesclo(", 14) == 0) |
| { |
| *strp += 15; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOTLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotofflo(", 9) == 0) |
| { |
| *strp += 10; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTOFFLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotofffuncdesclo(", 17) == 0) |
| { |
| *strp += 18; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOTOFFLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsdesclo(", 13) == 0) |
| { |
| *strp += 14; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSDESCLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "tlsmofflo(", 10) == 0) |
| { |
| *strp += 11; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_TLSMOFFLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsofflo(", 12) == 0) |
| { |
| *strp += 13; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSOFFLO, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| } |
| return cgen_parse_signed_integer (cd, strp, opindex, valuep); |
| } |
| |
| static const char * |
| parse_uhi16 (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| unsigned long *valuep) |
| { |
| const char *errmsg; |
| enum cgen_parse_operand_result result_type; |
| bfd_vma value; |
| |
| if (**strp == '#' || **strp == '%') |
| { |
| if (strncasecmp (*strp + 1, "hi(", 3) == 0) |
| { |
| *strp += 4; |
| errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_FRV_HI16, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing `)'"; |
| ++*strp; |
| if (errmsg == NULL |
| && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) |
| { |
| /* If value is wider than 32 bits then be |
| careful about how we extract bits 16-31. */ |
| if (sizeof (value) > 4) |
| value &= (((bfd_vma)1 << 16) << 16) - 1; |
| |
| value >>= 16; |
| } |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gprelhi(", 8) == 0) |
| { |
| *strp += 9; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GPRELHI, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gothi(", 6) == 0) |
| { |
| *strp += 7; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTHI, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotfuncdeschi(", 14) == 0) |
| { |
| *strp += 15; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOTHI, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotoffhi(", 9) == 0) |
| { |
| *strp += 10; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTOFFHI, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotofffuncdeschi(", 17) == 0) |
| { |
| *strp += 18; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOTOFFHI, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsdeschi(", 13) == 0) |
| { |
| *strp += 14; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSDESCHI, |
| &result_type, &value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "tlsmoffhi(", 10) == 0) |
| { |
| *strp += 11; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_TLSMOFFHI, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsoffhi(", 12) == 0) |
| { |
| *strp += 13; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSOFFHI, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| } |
| return cgen_parse_unsigned_integer (cd, strp, opindex, valuep); |
| } |
| |
| static long |
| parse_register_number (const char **strp) |
| { |
| int regno; |
| |
| if (**strp < '0' || **strp > '9') |
| return -1; /* error */ |
| |
| regno = **strp - '0'; |
| for (++*strp; **strp >= '0' && **strp <= '9'; ++*strp) |
| regno = regno * 10 + (**strp - '0'); |
| |
| return regno; |
| } |
| |
| static const char * |
| parse_spr (CGEN_CPU_DESC cd, |
| const char **strp, |
| CGEN_KEYWORD * table, |
| long *valuep) |
| { |
| const char *save_strp; |
| long regno; |
| |
| /* Check for spr index notation. */ |
| if (strncasecmp (*strp, "spr[", 4) == 0) |
| { |
| *strp += 4; |
| regno = parse_register_number (strp); |
| if (**strp != ']') |
| return _("missing `]'"); |
| ++*strp; |
| if (! spr_valid (regno)) |
| return _("Special purpose register number is out of range"); |
| *valuep = regno; |
| return NULL; |
| } |
| |
| save_strp = *strp; |
| regno = parse_register_number (strp); |
| if (regno != -1) |
| { |
| if (! spr_valid (regno)) |
| return _("Special purpose register number is out of range"); |
| *valuep = regno; |
| return NULL; |
| } |
| |
| *strp = save_strp; |
| return cgen_parse_keyword (cd, strp, table, valuep); |
| } |
| |
| static const char * |
| parse_d12 (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| long *valuep) |
| { |
| const char *errmsg; |
| enum cgen_parse_operand_result result_type; |
| bfd_vma value; |
| |
| /* Check for small data reference. */ |
| if (**strp == '#' || **strp == '%') |
| { |
| if (strncasecmp (*strp + 1, "gprel12(", 8) == 0) |
| { |
| *strp += 9; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GPREL12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing `)'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "got12(", 6) == 0) |
| { |
| *strp += 7; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOT12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotfuncdesc12(", 14) == 0) |
| { |
| *strp += 15; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOT12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotoff12(", 9) == 0) |
| { |
| *strp += 10; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTOFF12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotofffuncdesc12(", 17) == 0) |
| { |
| *strp += 18; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOTOFF12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsdesc12(", 13) == 0) |
| { |
| *strp += 14; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSDESC12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "tlsmoff12(", 10) == 0) |
| { |
| *strp += 11; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_TLSMOFF12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsoff12(", 12) == 0) |
| { |
| *strp += 13; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSOFF12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| } |
| return cgen_parse_signed_integer (cd, strp, opindex, valuep); |
| } |
| |
| static const char * |
| parse_s12 (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| long *valuep) |
| { |
| const char *errmsg; |
| enum cgen_parse_operand_result result_type; |
| bfd_vma value; |
| |
| /* Check for small data reference. */ |
| if (**strp == '#' || **strp == '%') |
| { |
| if (strncasecmp (*strp + 1, "gprel12(", 8) == 0) |
| { |
| *strp += 9; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GPREL12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing `)'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "got12(", 6) == 0) |
| { |
| *strp += 7; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOT12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotfuncdesc12(", 14) == 0) |
| { |
| *strp += 15; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOT12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotoff12(", 9) == 0) |
| { |
| *strp += 10; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTOFF12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gotofffuncdesc12(", 17) == 0) |
| { |
| *strp += 18; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_FUNCDESC_GOTOFF12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsdesc12(", 13) == 0) |
| { |
| *strp += 14; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSDESC12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "tlsmoff12(", 10) == 0) |
| { |
| *strp += 11; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_TLSMOFF12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else if (strncasecmp (*strp + 1, "gottlsoff12(", 12) == 0) |
| { |
| *strp += 13; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GOTTLSOFF12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing ')'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| } |
| |
| if (**strp == '#') |
| ++*strp; |
| return cgen_parse_signed_integer (cd, strp, opindex, valuep); |
| } |
| |
| static const char * |
| parse_u12 (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| long *valuep) |
| { |
| const char *errmsg; |
| enum cgen_parse_operand_result result_type; |
| bfd_vma value; |
| |
| /* Check for small data reference. */ |
| if ((**strp == '#' || **strp == '%') |
| && strncasecmp (*strp + 1, "gprel12(", 8) == 0) |
| { |
| *strp += 9; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GPRELU12, |
| & result_type, & value); |
| if (**strp != ')') |
| return "missing `)'"; |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| else |
| { |
| if (**strp == '#') |
| ++*strp; |
| return cgen_parse_signed_integer (cd, strp, opindex, valuep); |
| } |
| } |
| |
| static const char * |
| parse_A (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| unsigned long *valuep, |
| unsigned long A) |
| { |
| const char *errmsg; |
| |
| if (**strp == '#') |
| ++*strp; |
| |
| errmsg = cgen_parse_unsigned_integer (cd, strp, opindex, valuep); |
| if (errmsg) |
| return errmsg; |
| |
| if (*valuep != A) |
| return _("Value of A operand must be 0 or 1"); |
| |
| return NULL; |
| } |
| |
| static const char * |
| parse_A0 (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| unsigned long *valuep) |
| { |
| return parse_A (cd, strp, opindex, valuep, 0); |
| } |
| |
| static const char * |
| parse_A1 (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| unsigned long *valuep) |
| { |
| return parse_A (cd, strp, opindex, valuep, 1); |
| } |
| |
| static const char * |
| parse_even_register (CGEN_CPU_DESC cd, |
| const char ** strP, |
| CGEN_KEYWORD * tableP, |
| long * valueP) |
| { |
| const char * errmsg; |
| const char * saved_star_strP = * strP; |
| |
| errmsg = cgen_parse_keyword (cd, strP, tableP, valueP); |
| |
| if (errmsg == NULL && ((* valueP) & 1)) |
| { |
| errmsg = _("register number must be even"); |
| * strP = saved_star_strP; |
| } |
| |
| return errmsg; |
| } |
| |
| static const char * |
| parse_call_label (CGEN_CPU_DESC cd, |
| const char **strp, |
| int opindex, |
| int opinfo, |
| enum cgen_parse_operand_result *resultp, |
| bfd_vma *valuep) |
| { |
| const char *errmsg; |
| bfd_vma value; |
| |
| /* Check for small data reference. */ |
| if (opinfo == 0 && (**strp == '#' || **strp == '%')) |
| { |
| if (strncasecmp (*strp + 1, "gettlsoff(", 10) == 0) |
| { |
| *strp += 11; |
| errmsg = parse_symbolic_address (cd, strp, opindex, |
| BFD_RELOC_FRV_GETTLSOFF, |
| resultp, &value); |
| if (**strp != ')') |
| return _("missing `)'"); |
| ++*strp; |
| *valuep = value; |
| return errmsg; |
| } |
| } |
| |
| return cgen_parse_address (cd, strp, opindex, opinfo, resultp, valuep); |
| } |
| |
| /* -- */ |
| |
| /* -- dis.c */ |
| static void |
| print_at (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, |
| void * dis_info, |
| long reloc_ann ATTRIBUTE_UNUSED, |
| long value ATTRIBUTE_UNUSED, |
| bfd_vma pc ATTRIBUTE_UNUSED, |
| int length ATTRIBUTE_UNUSED) |
| { |
| disassemble_info *info = (disassemble_info *) dis_info; |
| |
| (*info->fprintf_func) (info->stream, "@"); |
| } |
| |
| static void |
| print_spr (CGEN_CPU_DESC cd, |
| void * dis_info, |
| CGEN_KEYWORD *names, |
| long regno, |
| unsigned int attrs) |
| { |
| /* Use the register index format for any unnamed registers. */ |
| if (cgen_keyword_lookup_value (names, regno) == NULL) |
| { |
| disassemble_info *info = (disassemble_info *) dis_info; |
| (*info->fprintf_func) (info->stream, "spr[%ld]", regno); |
| } |
| else |
| print_keyword (cd, dis_info, names, regno, attrs); |
| } |
| |
| static void |
| print_hi (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, |
| void * dis_info, |
| long value, |
| unsigned int attrs ATTRIBUTE_UNUSED, |
| bfd_vma pc ATTRIBUTE_UNUSED, |
| int length ATTRIBUTE_UNUSED) |
| { |
| disassemble_info *info = (disassemble_info *) dis_info; |
| |
| (*info->fprintf_func) (info->stream, value ? "0x%lx" : "hi(0x%lx)", value); |
| } |
| |
| static void |
| print_lo (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, |
| void * dis_info, |
| long value, |
| unsigned int attrs ATTRIBUTE_UNUSED, |
| bfd_vma pc ATTRIBUTE_UNUSED, |
| int length ATTRIBUTE_UNUSED) |
| { |
| disassemble_info *info = (disassemble_info *) dis_info; |
| if (value) |
| (*info->fprintf_func) (info->stream, "0x%lx", value); |
| else |
| (*info->fprintf_func) (info->stream, "lo(0x%lx)", value); |
| } |
| |
| /* -- */ |