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